diff options
Diffstat (limited to 'engines/wintermute')
227 files changed, 63496 insertions, 0 deletions
diff --git a/engines/wintermute/ad/ad_actor.cpp b/engines/wintermute/ad/ad_actor.cpp new file mode 100644 index 0000000000..d175855d1e --- /dev/null +++ b/engines/wintermute/ad/ad_actor.cpp @@ -0,0 +1,1460 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/base_engine.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdActor, false) + + +////////////////////////////////////////////////////////////////////////// +AdActor::AdActor(BaseGame *inGame) : AdTalkHolder(inGame) { + _path = new AdPath(_gameRef); + + _type = OBJECT_ACTOR; + _dir = DI_LEFT; + + _walkSprite = NULL; + _standSprite = NULL; + _turnLeftSprite = NULL; + _turnRightSprite = NULL; + + _targetPoint = new BasePoint; + _afterWalkDir = DI_NONE; + + _animSprite2 = NULL; + + setDefaultAnimNames(); +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::setDefaultAnimNames() { + _talkAnimName = "talk"; + _idleAnimName = "idle"; + _walkAnimName = "walk"; + _turnLeftAnimName = "turnleft"; + _turnRightAnimName = "turnright"; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +AdActor::~AdActor() { + delete _path; + delete _targetPoint; + _path = NULL; + _targetPoint = NULL; + + delete _walkSprite; + delete _standSprite; + delete _turnLeftSprite; + delete _turnRightSprite; + _walkSprite = NULL; + _standSprite = NULL; + _turnLeftSprite = NULL; + _turnRightSprite = NULL; + + _animSprite2 = NULL; // ref only + + for (uint32 i = 0; i < _talkSprites.size(); i++) { + delete _talkSprites[i]; + } + _talkSprites.clear(); + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); + + for (uint32 i = 0; i < _anims.size(); i++) { + delete _anims[i]; + _anims[i] = NULL; + } + _anims.clear(); + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdActor::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ACTOR file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ACTOR) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SCALABLE) +TOKEN_DEF(REGISTRABLE) +TOKEN_DEF(INTERACTIVE) +TOKEN_DEF(SHADOWABLE) +TOKEN_DEF(COLORABLE) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(WALK) +TOKEN_DEF(STAND) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(TURN_LEFT) +TOKEN_DEF(TURN_RIGHT) +TOKEN_DEF(EVENTS) +TOKEN_DEF(FONT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(SOUND_VOLUME) +TOKEN_DEF(SOUND_PANNING) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(BLOCKED_REGION) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(IGNORE_ITEMS) +TOKEN_DEF(ROTABLE) +TOKEN_DEF(ROTATABLE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(SCALE) +TOKEN_DEF(RELATIVE_SCALE) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(ANIMATION) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdActor::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ACTOR) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCALABLE) + TOKEN_TABLE(REGISTRABLE) + TOKEN_TABLE(INTERACTIVE) + TOKEN_TABLE(SHADOWABLE) + TOKEN_TABLE(COLORABLE) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(WALK) + TOKEN_TABLE(STAND) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(TURN_LEFT) + TOKEN_TABLE(TURN_RIGHT) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(SOUND_VOLUME) + TOKEN_TABLE(SOUND_PANNING) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(BLOCKED_REGION) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(IGNORE_ITEMS) + TOKEN_TABLE(ROTABLE) + TOKEN_TABLE(ROTATABLE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(RELATIVE_SCALE) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(ANIMATION) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ACTOR) { + _gameRef->LOG(0, "'ACTOR' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + AdGame *adGame = (AdGame *)_gameRef; + AdSpriteSet *spr = NULL; + int ar = 0, ag = 0, ab = 0, alpha = 0; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_SCALABLE: + parser.scanStr((char *)params, "%b", &_zoomable); + break; + + case TOKEN_ROTABLE: + case TOKEN_ROTATABLE: + parser.scanStr((char *)params, "%b", &_rotatable); + break; + + case TOKEN_REGISTRABLE: + case TOKEN_INTERACTIVE: + parser.scanStr((char *)params, "%b", &_registrable); + break; + + case TOKEN_SHADOWABLE: + case TOKEN_COLORABLE: + parser.scanStr((char *)params, "%b", &_shadowable); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_WALK: + delete _walkSprite; + _walkSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texWalkLifeTime, CACHE_HALF))) { + cmd = PARSERR_GENERIC; + } else { + _walkSprite = spr; + } + break; + + case TOKEN_TALK: + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + break; + + case TOKEN_TALK_SPECIAL: + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + break; + + case TOKEN_STAND: + delete _standSprite; + _standSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true, adGame->_texStandLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _standSprite = spr; + } + break; + + case TOKEN_TURN_LEFT: + delete _turnLeftSprite; + _turnLeftSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true))) { + cmd = PARSERR_GENERIC; + } else { + _turnLeftSprite = spr; + } + break; + + case TOKEN_TURN_RIGHT: + delete _turnRightSprite; + _turnRightSprite = NULL; + spr = new AdSpriteSet(_gameRef, this); + if (!spr || DID_FAIL(spr->loadBuffer(params, true))) { + cmd = PARSERR_GENERIC; + } else { + _turnRightSprite = spr; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SOUND_VOLUME: + parser.scanStr((char *)params, "%d", &_sFXVolume); + break; + + case TOKEN_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _scale = (float)s; + + } + break; + + case TOKEN_RELATIVE_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _relativeScale = (float)s; + + } + break; + + case TOKEN_SOUND_PANNING: + parser.scanStr((char *)params, "%b", &_autoSoundPanning); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_BLOCKED_REGION: { + delete _blockRegion; + delete _currentBlockRegion; + _blockRegion = NULL; + _currentBlockRegion = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + BaseRegion *crgn = new BaseRegion(_gameRef); + if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) { + delete _blockRegion; + delete _currentBlockRegion; + _blockRegion = NULL; + _currentBlockRegion = NULL; + cmd = PARSERR_GENERIC; + } else { + _blockRegion = rgn; + _currentBlockRegion = crgn; + _currentBlockRegion->mimic(_blockRegion); + } + } + break; + + case TOKEN_WAYPOINTS: { + delete _wptGroup; + delete _currentWptGroup; + _wptGroup = NULL; + _currentWptGroup = NULL; + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef); + if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) { + delete _wptGroup; + delete _currentWptGroup; + _wptGroup = NULL; + _currentWptGroup = NULL; + cmd = PARSERR_GENERIC; + } else { + _wptGroup = wpt; + _currentWptGroup = cwpt; + _currentWptGroup->mimic(_wptGroup); + } + } + break; + + case TOKEN_IGNORE_ITEMS: + parser.scanStr((char *)params, "%b", &_ignoreItems); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_ANIMATION: { + AdSpriteSet *anim = new AdSpriteSet(_gameRef, this); + if (!anim || DID_FAIL(anim->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } else { + _anims.add(anim); + } + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ACTOR definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + if (spr) { + delete spr; + } + _gameRef->LOG(0, "Error loading ACTOR definition"); + return STATUS_FAILED; + } + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + _state = _nextState = STATE_READY; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::turnTo(TDirection dir) { + int delta1, delta2, delta3, delta; + + delta1 = dir - _dir; + delta2 = dir + NUM_DIRECTIONS - _dir; + delta3 = dir - NUM_DIRECTIONS - _dir; + + delta1 = (abs(delta1) <= abs(delta2)) ? delta1 : delta2; + delta = (abs(delta1) <= abs(delta3)) ? delta1 : delta3; + + // already there? + if (abs(delta) < 2) { + _dir = dir; + _state = _nextState; + _nextState = STATE_READY; + return; + } + + _targetDir = dir; + _state = delta < 0 ? STATE_TURNING_LEFT : STATE_TURNING_RIGHT; + + _tempSprite2 = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::goTo(int x, int y, TDirection afterWalkDir) { + _afterWalkDir = afterWalkDir; + if (x == _targetPoint->x && y == _targetPoint->y && _state == STATE_FOLLOWING_PATH) { + return; + } + + _path->reset(); + _path->setReady(false); + + _targetPoint->x = x; + _targetPoint->y = y; + + ((AdGame *)_gameRef)->_scene->correctTargetPoint(_posX, _posY, &_targetPoint->x, &_targetPoint->y, true, this); + + _state = STATE_SEARCHING_PATH; + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::display() { + if (_active) { + updateSounds(); + } + + uint32 alpha; + if (_alphaColor != 0) { + alpha = _alphaColor; + } else { + alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY, true) : 0xFFFFFFFF; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + + float rotate; + if (_rotatable) { + if (_rotateValid) { + rotate = _rotate; + } else { + rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate; + } + } else { + rotate = 0.0f; + } + + if (_active) { + displaySpriteAttachments(true); + } + + if (_currentSprite && _active) { + bool reg = _registrable; + if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) { + reg = false; + } + + _currentSprite->display(_posX, + _posY, + reg ? _registerAlias : NULL, + scaleX, + scaleY, + alpha, + rotate, + _blendMode); + + } + + if (_active) { + displaySpriteAttachments(false); + } + if (_active && _partEmitter) { + _partEmitter->display(); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::update() { + _currentSprite = NULL; + + if (_state == STATE_READY) { + if (_animSprite) { + delete _animSprite; + _animSprite = NULL; + } + if (_animSprite2) { + _animSprite2 = NULL; + } + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = _nextState; + _nextState = STATE_READY; + _currentSprite = _animSprite; + } + + if (_state == STATE_PLAYING_ANIM_SET && _animSprite2 != NULL && _animSprite2->isFinished()) { + _state = _nextState; + _nextState = STATE_READY; + _currentSprite = _animSprite2; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + if (_sprite) { + _currentSprite = _sprite; + } else { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + } + + bool already_moved = false; + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM_SET: + _currentSprite = _animSprite2; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TURNING_LEFT: + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { + if (_dir > 0) { + _dir = (TDirection)(_dir - 1); + } else { + _dir = (TDirection)(NUM_DIRECTIONS - 1); + } + + if (_dir == _targetDir) { + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + if (_turnLeftSprite) { + _tempSprite2 = _turnLeftSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_turnLeftAnimName); + if (anim) { + _tempSprite2 = anim->getSprite(_dir); + } + } + + if (_tempSprite2) { + _tempSprite2->reset(); + if (_tempSprite2->_looping) { + _tempSprite2->_looping = false; + } + } + _currentSprite = _tempSprite2; + } + } else { + _currentSprite = _tempSprite2; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_TURNING_RIGHT: + if (_tempSprite2 == NULL || _tempSprite2->isFinished()) { + _dir = (TDirection)(_dir + 1); + + if ((int)_dir >= (int)NUM_DIRECTIONS) { + _dir = (TDirection)(0); + } + + if (_dir == _targetDir) { + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + if (_turnRightSprite) { + _tempSprite2 = _turnRightSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_turnRightAnimName); + if (anim) { + _tempSprite2 = anim->getSprite(_dir); + } + } + + if (_tempSprite2) { + _tempSprite2->reset(); + if (_tempSprite2->_looping) { + _tempSprite2->_looping = false; + } + } + _currentSprite = _tempSprite2; + } + } else { + _currentSprite = _tempSprite2; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_SEARCHING_PATH: + // keep asking scene for the path + if (((AdGame *)_gameRef)->_scene->getPath(BasePoint(_posX, _posY), *_targetPoint, _path, this)) { + _state = STATE_WAITING_PATH; + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_WAITING_PATH: + // wait until the scene finished the path + if (_path->_ready) { + followPath(); + } + break; + + + ////////////////////////////////////////////////////////////////////////// + case STATE_FOLLOWING_PATH: + getNextStep(); + already_moved = true; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(_dir); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = _nextState; + _nextState = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite && !_animSprite2) { + if (_sprite) { + _currentSprite = _sprite; + } else { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + } + break; + default: + error("AdActor::Update - Unhandled enum"); + } + + + if (_currentSprite && !already_moved) { + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (_currentSprite->isChanged()) { + _posX += _currentSprite->_moveX; + _posY += _currentSprite->_moveY; + afterMove(); + } + } + + //_gameRef->QuickMessageForm("%s", _currentSprite->_filename); + + updateBlockRegion(); + _ready = (_state == STATE_READY); + + updatePartEmitter(); + updateSpriteAttachments(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::followPath() { + // skip current position + _path->getFirst(); + while (_path->getCurrent() != NULL) { + if (_path->getCurrent()->x != _posX || _path->getCurrent()->y != _posY) { + break; + } + _path->getNext(); + } + + // are there points to follow? + if (_path->getCurrent() != NULL) { + _state = STATE_FOLLOWING_PATH; + initLine(BasePoint(_posX, _posY), *_path->getCurrent()); + } else { + if (_afterWalkDir != DI_NONE) { + turnTo(_afterWalkDir); + } else { + _state = STATE_READY; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::getNextStep() { + if (_walkSprite) { + _currentSprite = _walkSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_walkAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + + if (!_currentSprite) { + return; + } + + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100, _zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (!_currentSprite->isChanged()) { + return; + } + + + int maxStepX, maxStepY; + maxStepX = abs(_currentSprite->_moveX); + maxStepY = abs(_currentSprite->_moveY); + + maxStepX = MAX(maxStepX, maxStepY); + maxStepX = MAX(maxStepX, 1); + + while (_pFCount > 0 && maxStepX >= 0) { + _pFX += _pFStepX; + _pFY += _pFStepY; + + _pFCount--; + maxStepX--; + } + + if (((AdGame *)_gameRef)->_scene->isBlockedAt((int)_pFX, (int) _pFY, true, this)) { + if (_pFCount == 0) { + _state = _nextState; + _nextState = STATE_READY; + return; + } + goTo(_targetPoint->x, _targetPoint->y); + return; + } + + + _posX = (int)_pFX; + _posY = (int)_pFY; + + afterMove(); + + + if (_pFCount == 0) { + if (_path->getNext() == NULL) { + _posX = _targetPoint->x; + _posY = _targetPoint->y; + + _path->reset(); + if (_afterWalkDir != DI_NONE) { + turnTo(_afterWalkDir); + } else { + _state = _nextState; + _nextState = STATE_READY; + } + } else { + initLine(BasePoint(_posX, _posY), *_path->getCurrent()); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdActor::initLine(BasePoint startPt, BasePoint endPt) { + _pFCount = MAX((abs(endPt.x - startPt.x)) , (abs(endPt.y - startPt.y))); + + _pFStepX = (double)(endPt.x - startPt.x) / _pFCount; + _pFStepY = (double)(endPt.y - startPt.y) / _pFCount; + + _pFX = startPt.x; + _pFY = startPt.y; + + int angle = (int)(atan2((double)(endPt.y - startPt.y), (double)(endPt.x - startPt.x)) * (180 / 3.14)); + + _nextState = STATE_FOLLOWING_PATH; + + turnTo(angleToDirection(angle)); +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdActor::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GoTo / GoToAsync + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GoTo") == 0 || strcmp(name, "GoToAsync") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + goTo(x, y); + if (strcmp(name, "GoToAsync") != 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoToObject / GoToObjectAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoToObject") == 0 || strcmp(name, "GoToObjectAsync") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (!val->isNative()) { + script->runtimeError("actor.%s method accepts an entity refrence only", name); + stack->pushNULL(); + return STATUS_OK; + } + AdObject *obj = (AdObject *)val->getNative(); + if (!obj || obj->_type != OBJECT_ENTITY) { + script->runtimeError("actor.%s method accepts an entity refrence only", name); + stack->pushNULL(); + return STATUS_OK; + } + AdEntity *ent = (AdEntity *)obj; + if (ent->_walkToX == 0 && ent->_walkToY == 0) { + goTo(ent->_posX, ent->_posY); + } else { + goTo(ent->_walkToX, ent->_walkToY, ent->_walkToDir); + } + if (strcmp(name, "GoToObjectAsync") != 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnTo / TurnToAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnTo") == 0 || strcmp(name, "TurnToAsync") == 0) { + stack->correctParams(1); + int dir; + ScValue *val = stack->pop(); + + // turn to object? + if (val->isNative() && _gameRef->validObject((BaseObject *)val->getNative())) { + BaseObject *obj = (BaseObject *)val->getNative(); + int angle = (int)(atan2((double)(obj->_posY - _posY), (double)(obj->_posX - _posX)) * (180 / 3.14)); + dir = (int)angleToDirection(angle); + } + // otherwise turn to direction + else { + dir = val->getInt(); + } + + if (dir >= 0 && dir < NUM_DIRECTIONS) { + turnTo((TDirection)dir); + if (strcmp(name, "TurnToAsync") != 0) { + script->waitForExclusive(this); + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsWalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsWalking") == 0) { + stack->correctParams(0); + stack->pushBool(_state == STATE_FOLLOWING_PATH); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MergeAnims + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MergeAnims") == 0) { + stack->correctParams(1); + stack->pushBool(DID_SUCCEED(mergeAnims(stack->pop()->getString()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + + bool found = false; + for (uint32 i = 0; i < _anims.size(); i++) { + if (scumm_stricmp(_anims[i]->getName(), animName) == 0) { + // invalidate sprites in use + if (_anims[i]->containsSprite(_tempSprite2)) { + _tempSprite2 = NULL; + } + if (_anims[i]->containsSprite(_currentSprite)) { + _currentSprite = NULL; + } + if (_anims[i]->containsSprite(_animSprite2)) { + _animSprite2 = NULL; + } + + delete _anims[i]; + _anims[i] = NULL; + _anims.remove_at(i); + i--; + found = true; + } + } + stack->pushBool(found); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + stack->pushBool(getAnimByName(animName) != NULL); + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdActor::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Direction + ////////////////////////////////////////////////////////////////////////// + if (name == "Direction") { + _scValue->setInt(_dir); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + else if (name == "Type") { + _scValue->setString("actor"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // TalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TalkAnimName") { + _scValue->setString(_talkAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkAnimName") { + _scValue->setString(_walkAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IdleAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "IdleAnimName") { + _scValue->setString(_idleAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnLeftAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TurnLeftAnimName") { + _scValue->setString(_turnLeftAnimName); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnRightAnimName + ////////////////////////////////////////////////////////////////////////// + else if (name == "TurnRightAnimName") { + _scValue->setString(_turnRightAnimName); + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdActor::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Direction + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Direction") == 0) { + int dir = value->getInt(); + if (dir >= 0 && dir < NUM_DIRECTIONS) { + _dir = (TDirection)dir; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TalkAnimName") == 0) { + if (value->isNULL()) { + _talkAnimName = "talk"; + } else { + _talkAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkAnimName") == 0) { + if (value->isNULL()) { + _walkAnimName = "walk"; + } else { + _walkAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IdleAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IdleAnimName") == 0) { + if (value->isNULL()) { + _idleAnimName = "idle"; + } else { + _idleAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnLeftAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnLeftAnimName") == 0) { + if (value->isNULL()) { + _turnLeftAnimName = "turnleft"; + } else { + _turnLeftAnimName = value->getString(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TurnRightAnimName + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TurnRightAnimName") == 0) { + if (value->isNULL()) { + _turnRightAnimName = "turnright"; + } else { + _turnRightAnimName = value->getString(); + } + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdActor::scToString() { + return "[actor object]"; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdActor::getTalkStance(const char *stance) { + // forced stance? + if (_forcedTalkAnimName && !_forcedTalkAnimUsed) { + _forcedTalkAnimUsed = true; + delete _animSprite; + _animSprite = new BaseSprite(_gameRef, this); + if (_animSprite) { + bool res = _animSprite->loadFile(_forcedTalkAnimName); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdActor::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName); + delete _animSprite; + _animSprite = NULL; + } else { + return _animSprite; + } + } + } + + // old way + if (_talkSprites.size() > 0 || _talkSpritesEx.size() > 0) { + return getTalkStanceOld(stance); + } + + // new way + BaseSprite *ret = NULL; + + // do we have an animation with this name? + AdSpriteSet *anim = getAnimByName(stance); + if (anim) { + ret = anim->getSprite(_dir); + } + + // not - get a random talk + if (!ret) { + BaseArray<AdSpriteSet *> talkAnims; + for (uint32 i = 0; i < _anims.size(); i++) { + if (_talkAnimName.compareToIgnoreCase(_anims[i]->getName()) == 0) { + talkAnims.add(_anims[i]); + } + } + + if (talkAnims.size() > 0) { + int rnd = BaseEngine::instance().randInt(0, talkAnims.size() - 1); + ret = talkAnims[rnd]->getSprite(_dir); + } else { + if (_standSprite) { + ret = _standSprite->getSprite(_dir); + } else { + anim = getAnimByName(_idleAnimName); + if (anim) { + ret = anim->getSprite(_dir); + } + } + } + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdActor::getTalkStanceOld(const char *stance) { + BaseSprite *ret = NULL; + + if (stance != NULL) { + // search special stances + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) { + ret = _talkSpritesEx[i]->getSprite(_dir); + break; + } + } + if (ret == NULL) { + // search generic stances + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) { + ret = _talkSprites[i]->getSprite(_dir); + break; + } + } + } + } + + // not a valid stance? get a random one + if (ret == NULL) { + if (_talkSprites.size() < 1) { + ret = _standSprite->getSprite(_dir); + } else { + // TODO: remember last + int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1); + ret = _talkSprites[rnd]->getSprite(_dir); + } + } + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::persist(BasePersistenceManager *persistMgr) { + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_dir)); + persistMgr->transfer(TMEMBER(_path)); + persistMgr->transfer(TMEMBER(_pFCount)); + persistMgr->transfer(TMEMBER(_pFStepX)); + persistMgr->transfer(TMEMBER(_pFStepY)); + persistMgr->transfer(TMEMBER(_pFX)); + persistMgr->transfer(TMEMBER(_pFY)); + persistMgr->transfer(TMEMBER(_standSprite)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + persistMgr->transfer(TMEMBER_INT(_targetDir)); + persistMgr->transfer(TMEMBER_INT(_afterWalkDir)); + persistMgr->transfer(TMEMBER(_targetPoint)); + persistMgr->transfer(TMEMBER(_turnLeftSprite)); + persistMgr->transfer(TMEMBER(_turnRightSprite)); + persistMgr->transfer(TMEMBER(_walkSprite)); + + persistMgr->transfer(TMEMBER(_animSprite2)); + persistMgr->transfer(TMEMBER(_talkAnimName)); + persistMgr->transfer(TMEMBER(_idleAnimName)); + persistMgr->transfer(TMEMBER(_walkAnimName)); + persistMgr->transfer(TMEMBER(_turnLeftAnimName)); + persistMgr->transfer(TMEMBER(_turnRightAnimName)); + + _anims.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +TDirection AdActor::angleToDirection(int angle) { + TDirection ret = DI_DOWN; + + if (angle > -112 && angle <= -67) { + ret = DI_UP; + } else if (angle > -67 && angle <= -22) { + ret = DI_UPRIGHT; + } else if (angle > -22 && angle <= 22) { + ret = DI_RIGHT; + } else if (angle > 22 && angle <= 67) { + ret = DI_DOWNRIGHT; + } else if (angle > 67 && angle <= 112) { + ret = DI_DOWN; + } else if (angle > 112 && angle <= 157) { + ret = DI_DOWNLEFT; + } else if ((angle > 157 && angle <= 180) || (angle >= -180 && angle <= -157)) { + ret = DI_LEFT; + } else if (angle > -157 && angle <= -112) { + ret = DI_UPLEFT; + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +int AdActor::getHeight() { + // if no current sprite is set, set some + if (_currentSprite == NULL) { + if (_standSprite) { + _currentSprite = _standSprite->getSprite(_dir); + } else { + AdSpriteSet *anim = getAnimByName(_idleAnimName); + if (anim) { + _currentSprite = anim->getSprite(_dir); + } + } + } + // and get height + return AdTalkHolder::getHeight(); +} + + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet *AdActor::getAnimByName(const Common::String &animName) { + for (uint32 i = 0; i < _anims.size(); i++) { + if (animName.compareToIgnoreCase(_anims[i]->getName()) == 0) { + return _anims[i]; + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::mergeAnims(const char *animsFilename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ANIMATION) + TOKEN_TABLE_END + + + byte *fileBuffer = BaseFileManager::getEngineInstance()->readWholeFile(animsFilename); + if (fileBuffer == NULL) { + _gameRef->LOG(0, "AdActor::MergeAnims failed for file '%s'", animsFilename); + return STATUS_FAILED; + } + + byte *buffer = fileBuffer; + byte *params; + int cmd; + BaseParser parser; + + bool ret = STATUS_OK; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_ANIMATION: { + AdSpriteSet *anim = new AdSpriteSet(_gameRef, this); + if (!anim || DID_FAIL(anim->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + ret = STATUS_FAILED; + } else { + _anims.add(anim); + } + } + break; + } + } + delete[] fileBuffer; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool AdActor::playAnim(const char *filename) { + // if we have an anim with this name, use it + AdSpriteSet *anim = getAnimByName(filename); + if (anim) { + _animSprite2 = anim->getSprite(_dir); + if (_animSprite2) { + _animSprite2->reset(); + _state = STATE_PLAYING_ANIM_SET; + return STATUS_OK; + } + } + // otherwise call the standard handler + return AdTalkHolder::playAnim(filename); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_actor.h b/engines/wintermute/ad/ad_actor.h new file mode 100644 index 0000000000..543c9d063a --- /dev/null +++ b/engines/wintermute/ad/ad_actor.h @@ -0,0 +1,108 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADACTOR_H +#define WINTERMUTE_ADACTOR_H + + +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/ad/ad_types.h" // Added by ClassView +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_point.h" // Added by ClassView +#include "engines/wintermute/persistent.h" +#include "common/str.h" + +namespace Wintermute { + +class AdSpriteSet; +class AdPath; +class BaseSprite; +class AdActor : public AdTalkHolder { +public: + TDirection angleToDirection(int angle); + DECLARE_PERSISTENT(AdActor, AdTalkHolder) + virtual int getHeight(); + BaseSprite *getTalkStance(const char *stance); + virtual void goTo(int x, int y, TDirection afterWalkDir = DI_NONE); + BasePoint *_targetPoint; + virtual bool update(); + virtual bool display(); + virtual void turnTo(TDirection dir); + AdActor(BaseGame *inGame/*=NULL*/); + virtual ~AdActor(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + + +private: + TDirection _targetDir; + TDirection _afterWalkDir; + + AdPath *_path; + AdSpriteSet *_walkSprite; + AdSpriteSet *_standSprite; + AdSpriteSet *_turnLeftSprite; + AdSpriteSet *_turnRightSprite; + BaseArray<AdSpriteSet *> _talkSprites; + BaseArray<AdSpriteSet *> _talkSpritesEx; + TDirection _dir; + // new anim system + Common::String _talkAnimName; + Common::String _idleAnimName; + Common::String _walkAnimName; + Common::String _turnLeftAnimName; + Common::String _turnRightAnimName; + BaseArray<AdSpriteSet *> _anims; + virtual bool playAnim(const char *filename); + AdSpriteSet *getAnimByName(const Common::String &animName); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + bool setDefaultAnimNames(); + BaseSprite *getTalkStanceOld(const char *stance); + bool mergeAnims(const char *animsFilename); + BaseSprite *_animSprite2; + + void initLine(BasePoint startPt, BasePoint endPt); + void getNextStep(); + void followPath(); + double _pFStepX; + double _pFStepY; + double _pFX; + double _pFY; + int _pFCount; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_entity.cpp b/engines/wintermute/ad/ad_entity.cpp new file mode 100644 index 0000000000..9af7e034ca --- /dev/null +++ b/engines/wintermute/ad/ad_entity.cpp @@ -0,0 +1,1122 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdEntity, false) + +////////////////////////////////////////////////////////////////////////// +AdEntity::AdEntity(BaseGame *inGame) : AdTalkHolder(inGame) { + _type = OBJECT_ENTITY; + _subtype = ENTITY_NORMAL; + _region = NULL; + _item = NULL; + + _walkToX = _walkToY = 0; + _walkToDir = DI_NONE; + + _theora = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdEntity::~AdEntity() { + _gameRef->unregisterObject(_region); + + delete _theora; + _theora = NULL; + + delete[] _item; + _item = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdEntity::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ENTITY file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ENTITY) +TOKEN_DEF(SPRITE) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SCALABLE) +TOKEN_DEF(REGISTRABLE) +TOKEN_DEF(INTERACTIVE) +TOKEN_DEF(SHADOWABLE) +TOKEN_DEF(COLORABLE) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(EVENTS) +TOKEN_DEF(FONT) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(CURSOR) +TOKEN_DEF(REGION) +TOKEN_DEF(BLOCKED_REGION) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(SOUND_START_TIME) +TOKEN_DEF(SOUND_VOLUME) +TOKEN_DEF(SOUND_PANNING) +TOKEN_DEF(SOUND) +TOKEN_DEF(SUBTYPE) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(IGNORE_ITEMS) +TOKEN_DEF(ROTABLE) +TOKEN_DEF(ROTATABLE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(SCALE) +TOKEN_DEF(RELATIVE_SCALE) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(ITEM) +TOKEN_DEF(WALK_TO_X) +TOKEN_DEF(WALK_TO_Y) +TOKEN_DEF(WALK_TO_DIR) +TOKEN_DEF(SAVE_STATE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdEntity::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCALABLE) + TOKEN_TABLE(REGISTRABLE) + TOKEN_TABLE(INTERACTIVE) + TOKEN_TABLE(SHADOWABLE) + TOKEN_TABLE(COLORABLE) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(REGION) + TOKEN_TABLE(BLOCKED_REGION) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(SOUND_START_TIME) + TOKEN_TABLE(SOUND_VOLUME) + TOKEN_TABLE(SOUND_PANNING) + TOKEN_TABLE(SOUND) + TOKEN_TABLE(SUBTYPE) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(IGNORE_ITEMS) + TOKEN_TABLE(ROTABLE) + TOKEN_TABLE(ROTATABLE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(RELATIVE_SCALE) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(ITEM) + TOKEN_TABLE(WALK_TO_X) + TOKEN_TABLE(WALK_TO_Y) + TOKEN_TABLE(WALK_TO_DIR) + TOKEN_TABLE(SAVE_STATE) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ENTITY) { + _gameRef->LOG(0, "'ENTITY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + AdGame *adGame = (AdGame *)_gameRef; + BaseSprite *spr = NULL; + int ar = 0, ag = 0, ab = 0, alpha = 0; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_SPRITE: { + delete _sprite; + _sprite = NULL; + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } else { + _sprite = spr; + } + } + break; + + case TOKEN_TALK: { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + } + break; + + case TOKEN_TALK_SPECIAL: { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, adGame->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_ITEM: + setItem((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_SCALABLE: + parser.scanStr((char *)params, "%b", &_zoomable); + break; + + case TOKEN_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _scale = (float)s; + + } + break; + + case TOKEN_RELATIVE_SCALE: { + int s; + parser.scanStr((char *)params, "%d", &s); + _relativeScale = (float)s; + + } + break; + + case TOKEN_ROTABLE: + case TOKEN_ROTATABLE: + parser.scanStr((char *)params, "%b", &_rotatable); + break; + + case TOKEN_REGISTRABLE: + case TOKEN_INTERACTIVE: + parser.scanStr((char *)params, "%b", &_registrable); + break; + + case TOKEN_SHADOWABLE: + case TOKEN_COLORABLE: + parser.scanStr((char *)params, "%b", &_shadowable); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_REGION: { + if (_region) { + _gameRef->unregisterObject(_region); + } + _region = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + if (!rgn || DID_FAIL(rgn->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } else { + _region = rgn; + _gameRef->registerObject(_region); + } + } + break; + + case TOKEN_BLOCKED_REGION: { + delete _blockRegion; + _blockRegion = NULL; + delete _currentBlockRegion; + _currentBlockRegion = NULL; + BaseRegion *rgn = new BaseRegion(_gameRef); + BaseRegion *crgn = new BaseRegion(_gameRef); + if (!rgn || !crgn || DID_FAIL(rgn->loadBuffer(params, false))) { + delete _blockRegion; + _blockRegion = NULL; + delete _currentBlockRegion; + _currentBlockRegion = NULL; + cmd = PARSERR_GENERIC; + } else { + _blockRegion = rgn; + _currentBlockRegion = crgn; + _currentBlockRegion->mimic(_blockRegion); + } + } + break; + + case TOKEN_WAYPOINTS: { + delete _wptGroup; + _wptGroup = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + AdWaypointGroup *cwpt = new AdWaypointGroup(_gameRef); + if (!wpt || !cwpt || DID_FAIL(wpt->loadBuffer(params, false))) { + delete _wptGroup; + _wptGroup = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + cmd = PARSERR_GENERIC; + } else { + _wptGroup = wpt; + _currentWptGroup = cwpt; + _currentWptGroup->mimic(_wptGroup); + } + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_SUBTYPE: { + if (scumm_stricmp((char *)params, "sound") == 0) { + delete _sprite; + _sprite = NULL; + if (_gameRef->_editorMode) { + spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile("entity_sound.sprite"))) { + cmd = PARSERR_GENERIC; + } else { + _sprite = spr; + } + } + if (_gameRef->_editorMode) { + _editorOnly = true; + } + _zoomable = false; + _rotatable = false; + _registrable = _gameRef->_editorMode; + _shadowable = false; + _subtype = ENTITY_SOUND; + } + } + break; + + case TOKEN_SOUND: + playSFX((char *)params, false, false); + break; + + case TOKEN_SOUND_START_TIME: + parser.scanStr((char *)params, "%d", &_sFXStart); + break; + + case TOKEN_SOUND_VOLUME: + parser.scanStr((char *)params, "%d", &_sFXVolume); + break; + + case TOKEN_SOUND_PANNING: + parser.scanStr((char *)params, "%b", &_autoSoundPanning); + break; + + case TOKEN_SAVE_STATE: + parser.scanStr((char *)params, "%b", &_saveState); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_IGNORE_ITEMS: + parser.scanStr((char *)params, "%b", &_ignoreItems); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_WALK_TO_X: + parser.scanStr((char *)params, "%d", &_walkToX); + break; + + case TOKEN_WALK_TO_Y: + parser.scanStr((char *)params, "%d", &_walkToY); + break; + + case TOKEN_WALK_TO_DIR: { + int i; + parser.scanStr((char *)params, "%d", &i); + if (i < 0) { + i = 0; + } + if (i >= NUM_DIRECTIONS) { + i = DI_NONE; + } + _walkToDir = (TDirection)i; + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ENTITY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ENTITY definition"); + if (spr) { + delete spr; + } + return STATUS_FAILED; + } + + if (_region && _sprite) { + _gameRef->LOG(0, "Warning: Entity '%s' has both sprite and region.", getName()); + } + + updatePosition(); + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + _state = STATE_READY; + + if (_item && ((AdGame *)_gameRef)->isItemTaken(_item)) { + _active = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::display() { + if (_active) { + updateSounds(); + + uint32 alpha; + if (_alphaColor != 0) { + alpha = _alphaColor; + } else { + alpha = _shadowable ? ((AdGame *)_gameRef)->_scene->getAlphaAt(_posX, _posY) : 0xFFFFFFFF; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + float rotate; + if (_rotatable) { + if (_rotateValid) { + rotate = _rotate; + } else { + rotate = ((AdGame *)_gameRef)->_scene->getRotationAt(_posX, _posY) + _relativeRotate; + } + } else { + rotate = 0.0f; + } + + + bool reg = _registrable; + if (_ignoreItems && ((AdGame *)_gameRef)->_selectedItem) { + reg = false; + } + + if (_region && (reg || _editorAlwaysRegister)) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, _registerAlias, _region, _gameRef->_offsetX, _gameRef->_offsetY)); + } + + displaySpriteAttachments(true); + if (_theora && (_theora->isPlaying() || _theora->isPaused())) { + _theora->display(alpha); + } else if (_currentSprite) { + _currentSprite->display(_posX, + _posY, + (reg || _editorAlwaysRegister) ? _registerAlias : NULL, + scaleX, + scaleY, + alpha, + rotate, + _blendMode); + } + displaySpriteAttachments(false); + + if (_partEmitter) { + _partEmitter->display(_region); + } + + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::update() { + _currentSprite = NULL; + + if (_state == STATE_READY && _animSprite) { + delete _animSprite; + _animSprite = NULL; + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = STATE_READY; + _currentSprite = _animSprite; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + _currentSprite = _sprite; + } + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite) { + _currentSprite = _sprite; + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + } + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + break; + default: // Silence unhandled enum-warning + break; + } + + + if (_currentSprite) { + _currentSprite->getCurrentFrame(_zoomable ? ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) : 100); + if (_currentSprite->isChanged()) { + _posX += _currentSprite->_moveX; + _posY += _currentSprite->_moveY; + } + } + + updateBlockRegion(); + _ready = (_state == STATE_READY); + + if (_theora) { + int offsetX, offsetY; + _gameRef->getOffset(&offsetX, &offsetY); + _theora->_posX = _posX - offsetX; + _theora->_posY = _posY - offsetY; + + _theora->update(); + if (_theora->isFinished()) { + _theora->stop(); + delete _theora; + _theora = NULL; + } + } + + updatePartEmitter(); + updateSpriteAttachments(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // StopSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "StopSound") == 0 && _subtype == ENTITY_SOUND) { + stack->correctParams(0); + + if (DID_FAIL(stopSFX(false))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayTheora") == 0) { + stack->correctParams(4); + const char *filename = stack->pop()->getString(); + bool looping = stack->pop()->getBool(false); + ScValue *valAlpha = stack->pop(); + int startTime = stack->pop()->getInt(); + + delete _theora; + _theora = new VideoTheoraPlayer(_gameRef); + if (_theora && DID_SUCCEED(_theora->initialize(filename))) { + if (!valAlpha->isNULL()) { + _theora->setAlphaImage(valAlpha->getString()); + } + _theora->play(VID_PLAY_POS, 0, 0, false, false, looping, startTime, _scale >= 0.0f ? _scale : -1.0f, _sFXVolume); + //if (_scale>=0) _theora->_playZoom = _scale; + stack->pushBool(true); + } else { + script->runtimeError("Entity.PlayTheora - error playing video '%s'", filename); + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopTheora") == 0) { + stack->correctParams(0); + if (_theora) { + _theora->stop(); + delete _theora; + _theora = NULL; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTheoraPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTheoraPlaying") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPlaying()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseTheora") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPlaying()) { + _theora->pause(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeTheora") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPaused()) { + _theora->resume(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTheoraPaused + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTheoraPaused") == 0) { + stack->correctParams(0); + if (_theora && _theora->isPaused()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // CreateRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateRegion") == 0) { + stack->correctParams(0); + if (!_region) { + _region = new BaseRegion(_gameRef); + _gameRef->registerObject(_region); + } + if (_region) { + stack->pushNative(_region, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteRegion") == 0) { + stack->correctParams(0); + if (_region) { + _gameRef->unregisterObject(_region); + _region = NULL; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdEntity::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("entity"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + else if (name == "Item") { + if (_item) { + _scValue->setString(_item); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtype (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Subtype") { + if (_subtype == ENTITY_SOUND) { + _scValue->setString("sound"); + } else { + _scValue->setString("normal"); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToX + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToX") { + _scValue->setInt(_walkToX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToY + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToY") { + _scValue->setInt(_walkToY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToDirection + ////////////////////////////////////////////////////////////////////////// + else if (name == "WalkToDirection") { + _scValue->setInt((int)_walkToDir); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Region (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Region") { + if (_region) { + _scValue->setNative(_region, true); + } else { + _scValue->setNULL(); + } + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Item") == 0) { + setItem(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToX") == 0) { + _walkToX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToY") == 0) { + _walkToY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WalkToDirection + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WalkToDirection") == 0) { + int dir = value->getInt(); + if (dir >= 0 && dir < NUM_DIRECTIONS) { + _walkToDir = (TDirection)dir; + } + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdEntity::scToString() { + return "[entity object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ENTITY {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + if (_subtype == ENTITY_SOUND) { + buffer->putTextIndent(indent + 2, "SUBTYPE=\"SOUND\"\n"); + } + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "SCALABLE=%s\n", _zoomable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "INTERACTIVE=%s\n", _registrable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "COLORABLE=%s\n", _shadowable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + if (_ignoreItems) { + buffer->putTextIndent(indent + 2, "IGNORE_ITEMS=%s\n", _ignoreItems ? "TRUE" : "FALSE"); + } + if (_rotatable) { + buffer->putTextIndent(indent + 2, "ROTATABLE=%s\n", _rotatable ? "TRUE" : "FALSE"); + } + + if (!_autoSoundPanning) { + buffer->putTextIndent(indent + 2, "SOUND_PANNING=%s\n", _autoSoundPanning ? "TRUE" : "FALSE"); + } + + if (!_saveState) { + buffer->putTextIndent(indent + 2, "SAVE_STATE=%s\n", _saveState ? "TRUE" : "FALSE"); + } + + if (_item && _item[0] != '\0') { + buffer->putTextIndent(indent + 2, "ITEM=\"%s\"\n", _item); + } + + buffer->putTextIndent(indent + 2, "WALK_TO_X=%d\n", _walkToX); + buffer->putTextIndent(indent + 2, "WALK_TO_Y=%d\n", _walkToY); + if (_walkToDir != DI_NONE) { + buffer->putTextIndent(indent + 2, "WALK_TO_DIR=%d\n", (int)_walkToDir); + } + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_subtype == ENTITY_NORMAL && _sprite && _sprite->getFilename()) { + buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _sprite->getFilename()); + } + + if (_subtype == ENTITY_SOUND && _sFX && _sFX->getFilename()) { + buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sFX->getFilename()); + buffer->putTextIndent(indent + 2, "SOUND_START_TIME=%d\n", _sFXStart); + buffer->putTextIndent(indent + 2, "SOUND_VOLUME=%d\n", _sFXVolume); + } + + + if (RGBCOLGetR(_alphaColor) != 0 || RGBCOLGetG(_alphaColor) != 0 || RGBCOLGetB(_alphaColor) != 0) { + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor)); + } + + if (RGBCOLGetA(_alphaColor) != 0) { + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alphaColor)); + } + + if (_scale >= 0) { + buffer->putTextIndent(indent + 2, "SCALE = %d\n", (int)_scale); + } + + if (_relativeScale != 0) { + buffer->putTextIndent(indent + 2, "RELATIVE_SCALE = %d\n", (int)_relativeScale); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + AdTalkHolder::saveAsText(buffer, indent + 2); + + if (_region) { + _region->saveAsText(buffer, indent + 2); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + AdObject::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int AdEntity::getHeight() { + if (_region && !_sprite) { + return _region->_rect.bottom - _region->_rect.top; + } else { + if (_currentSprite == NULL) { + _currentSprite = _sprite; + } + return AdObject::getHeight(); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdEntity::updatePosition() { + if (_region && !_sprite) { + _posX = _region->_rect.left + (_region->_rect.right - _region->_rect.left) / 2; + _posY = _region->_rect.bottom; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::persist(BasePersistenceManager *persistMgr) { + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_item)); + persistMgr->transfer(TMEMBER(_region)); + //persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER_INT(_subtype)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_walkToX)); + persistMgr->transfer(TMEMBER(_walkToY)); + persistMgr->transfer(TMEMBER_INT(_walkToDir)); + + persistMgr->transfer(TMEMBER(_theora)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdEntity::setItem(const char *itemName) { + BaseUtils::setString(&_item, itemName); +} + +////////////////////////////////////////////////////////////////////////// +bool AdEntity::setSprite(const char *filename) { + if (_currentSprite == _sprite) { + _currentSprite = NULL; + } + + delete _sprite; + _sprite = NULL; + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + delete _sprite; + _sprite = NULL; + return STATUS_FAILED; + } else { + _sprite = spr; + _currentSprite = _sprite; + return STATUS_OK; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_entity.h b/engines/wintermute/ad/ad_entity.h new file mode 100644 index 0000000000..415987e50a --- /dev/null +++ b/engines/wintermute/ad/ad_entity.h @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADENTITY_H +#define WINTERMUTE_ADENTITY_H + +#include "engines/wintermute/ad/ad_talk_holder.h" + +namespace Wintermute { +class VideoTheoraPlayer; +class AdEntity : public AdTalkHolder { +public: + VideoTheoraPlayer *_theora; + bool setSprite(const char *filename); + int _walkToX; + int _walkToY; + TDirection _walkToDir; + void setItem(const char *itemName); + char *_item; + DECLARE_PERSISTENT(AdEntity, AdTalkHolder) + void updatePosition(); + virtual int getHeight(); + BaseRegion *_region; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool update(); + virtual bool display(); + AdEntity(BaseGame *inGame); + virtual ~AdEntity(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + TEntityType _subtype; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_game.cpp b/engines/wintermute/ad/ad_game.cpp new file mode 100644 index 0000000000..4481b774c1 --- /dev/null +++ b/engines/wintermute/ad/ad_game.cpp @@ -0,0 +1,2281 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/video/video_player.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdGame, true) + +////////////////////////////////////////////////////////////////////////// +AdGame::AdGame(const Common::String &gameId) : BaseGame(gameId) { + _responseBox = NULL; + _inventoryBox = NULL; + + _scene = new AdScene(_gameRef); + _scene->setName(""); + registerObject(_scene); + + _prevSceneName = NULL; + _prevSceneFilename = NULL; + _scheduledScene = NULL; + _scheduledFadeIn = false; + + + _stateEx = GAME_NORMAL; + + _selectedItem = NULL; + + + _texItemLifeTime = 10000; + _texWalkLifeTime = 10000; + _texStandLifeTime = 10000; + _texTalkLifeTime = 10000; + + _talkSkipButton = TALK_SKIP_LEFT; + + _sceneViewport = NULL; + + _initialScene = true; + _debugStartupScene = NULL; + _startupScene = NULL; + + _invObject = new AdObject(this); + _inventoryOwner = _invObject; + + _tempDisableSaveState = false; + _itemsFile = NULL; + + _smartItemCursor = false; + + addSpeechDir("speech"); +} + + +////////////////////////////////////////////////////////////////////////// +AdGame::~AdGame() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::cleanup() { + for (uint32 i = 0; i < _objects.size(); i++) { + unregisterObject(_objects[i]); + _objects[i] = NULL; + } + _objects.clear(); + + + for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) { + delete[] _dlgPendingBranches[i]; + } + _dlgPendingBranches.clear(); + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + delete[] _speechDirs[i]; + } + _speechDirs.clear(); + + + unregisterObject(_scene); + _scene = NULL; + + // remove items + for (uint32 i = 0; i < _items.size(); i++) { + _gameRef->unregisterObject(_items[i]); + } + _items.clear(); + + + // clear remaining inventories + delete _invObject; + _invObject = NULL; + + for (uint32 i = 0; i < _inventories.size(); i++) { + delete _inventories[i]; + } + _inventories.clear(); + + + if (_responseBox) { + _gameRef->unregisterObject(_responseBox); + _responseBox = NULL; + } + + if (_inventoryBox) { + _gameRef->unregisterObject(_inventoryBox); + _inventoryBox = NULL; + } + + delete[] _prevSceneName; + delete[] _prevSceneFilename; + delete[] _scheduledScene; + delete[] _debugStartupScene; + delete[] _itemsFile; + _prevSceneName = NULL; + _prevSceneFilename = NULL; + _scheduledScene = NULL; + _debugStartupScene = NULL; + _startupScene = NULL; + _itemsFile = NULL; + + delete _sceneViewport; + _sceneViewport = NULL; + + for (uint32 i = 0; i < _sceneStates.size(); i++) { + delete _sceneStates[i]; + } + _sceneStates.clear(); + + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + + for (uint32 i = 0; i < _responsesGame.size(); i++) { + delete _responsesGame[i]; + } + _responsesGame.clear(); + + return BaseGame::cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::initLoop() { + if (_scheduledScene && _transMgr->isReady()) { + changeScene(_scheduledScene, _scheduledFadeIn); + delete[] _scheduledScene; + _scheduledScene = NULL; + + _gameRef->_activeObject = NULL; + } + + + bool res; + res = BaseGame::initLoop(); + if (DID_FAIL(res)) { + return res; + } + + if (_scene) { + res = _scene->initLoop(); + } + + _sentences.clear(); + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addObject(AdObject *object) { + _objects.add(object); + return registerObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::removeObject(AdObject *object) { + // in case the user called Scene.CreateXXX() and Game.DeleteXXX() + if (_scene) { + bool res = _scene->removeObject(object); + if (DID_SUCCEED(res)) { + return res; + } + } + + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] == object) { + _objects.remove_at(i); + break; + } + } + return unregisterObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::changeScene(const char *filename, bool fadeIn) { + if (_scene == NULL) { + _scene = new AdScene(_gameRef); + registerObject(_scene); + } else { + _scene->applyEvent("SceneShutdown", true); + + setPrevSceneName(_scene->getName()); + setPrevSceneFilename(_scene->getFilename()); + + if (!_tempDisableSaveState) { + _scene->saveState(); + } + _tempDisableSaveState = false; + } + + if (_scene) { + // reset objects + for (uint32 i = 0; i < _objects.size(); i++) { + _objects[i]->reset(); + } + + // reset scene properties + _scene->_sFXVolume = 100; + if (_scene->_scProp) { + _scene->_scProp->cleanup(); + } + + bool ret; + if (_initialScene && _debugDebugMode && _debugStartupScene) { + _initialScene = false; + ret = _scene->loadFile(_debugStartupScene); + } else { + ret = _scene->loadFile(filename); + } + + if (DID_SUCCEED(ret)) { + // invalidate references to the original scene + for (uint32 i = 0; i < _objects.size(); i++) { + _objects[i]->invalidateCurrRegions(); + _objects[i]->_stickRegion = NULL; + } + + _scene->loadState(); + } + if (fadeIn) { + _gameRef->_transMgr->start(TRANSITION_FADE_IN); + } + return ret; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::addSentence(AdSentence *sentence) { + _sentences.add(sentence); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displaySentences(bool frozen) { + for (uint32 i = 0; i < _sentences.size(); i++) { + if (frozen && _sentences[i]->_freezable) { + continue; + } else { + _sentences[i]->display(); + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::finishSentences() { + for (uint32 i = 0; i < _sentences.size(); i++) { + if (_sentences[i]->canSkip()) { + _sentences[i]->_duration = 0; + if (_sentences[i]->_sound) { + _sentences[i]->_sound->stop(); + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // ChangeScene + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "ChangeScene") == 0) { + stack->correctParams(3); + const char *filename = stack->pop()->getString(); + ScValue *valFadeOut = stack->pop(); + ScValue *valFadeIn = stack->pop(); + + bool transOut = valFadeOut->isNULL() ? true : valFadeOut->getBool(); + bool transIn = valFadeIn->isNULL() ? true : valFadeIn->getBool(); + + scheduleChangeScene(filename, transIn); + if (transOut) { + _transMgr->start(TRANSITION_FADE_OUT, true); + } + stack->pushNULL(); + + + //bool ret = ChangeScene(stack->pop()->getString()); + //if (DID_FAIL(ret)) stack->pushBool(false); + //else stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadActor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadActor") == 0) { + stack->correctParams(1); + AdActor *act = new AdActor(_gameRef); + if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) { + addObject(act); + stack->pushNative(act, true); + } else { + delete act; + act = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadEntity") == 0) { + stack->correctParams(1); + AdEntity *ent = new AdEntity(_gameRef); + if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) { + addObject(ent); + stack->pushNative(ent, true); + } else { + delete ent; + ent = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject / UnloadActor / UnloadEntity / DeleteEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "DeleteEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + AdObject *obj = (AdObject *)val->getNative(); + removeObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdEntity *ent = new AdEntity(_gameRef); + addObject(ent); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = new AdItem(_gameRef); + addItem(item); + if (!val->isNULL()) { + item->setName(val->getString()); + } + stack->pushNative(item, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = NULL; + if (val->isNative()) { + item = (AdItem *)val->getNative(); + } else { + item = getItemByName(val->getString()); + } + + if (item) { + deleteItem(item); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // QueryItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "QueryItem") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdItem *item = NULL; + if (val->isInt()) { + int index = val->getInt(); + if (index >= 0 && index < (int32)_items.size()) { + item = _items[index]; + } + } else { + item = getItemByName(val->getString()); + } + + if (item) { + stack->pushNative(item, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // AddResponse/AddResponseOnce/AddResponseOnceGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddResponse") == 0 || strcmp(name, "AddResponseOnce") == 0 || strcmp(name, "AddResponseOnceGame") == 0) { + stack->correctParams(6); + int id = stack->pop()->getInt(); + const char *text = stack->pop()->getString(); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + ScValue *val3 = stack->pop(); + ScValue *val4 = stack->pop(); + + if (_responseBox) { + AdResponse *res = new AdResponse(_gameRef); + if (res) { + res->_iD = id; + res->setText(text); + _stringTable->expand(&res->_text); + if (!val1->isNULL()) { + res->setIcon(val1->getString()); + } + if (!val2->isNULL()) { + res->setIconHover(val2->getString()); + } + if (!val3->isNULL()) { + res->setIconPressed(val3->getString()); + } + if (!val4->isNULL()) { + res->setFont(val4->getString()); + } + + if (strcmp(name, "AddResponseOnce") == 0) { + res->_responseType = RESPONSE_ONCE; + } else if (strcmp(name, "AddResponseOnceGame") == 0) { + res->_responseType = RESPONSE_ONCE_GAME; + } + + _responseBox->_responses.add(res); + } + } else { + script->runtimeError("Game.AddResponse: response box is not defined"); + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResetResponse + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResetResponse") == 0) { + stack->correctParams(1); + int id = stack->pop()->getInt(-1); + resetResponse(id); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClearResponses + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClearResponses") == 0) { + stack->correctParams(0); + _responseBox->clearResponses(); + _responseBox->clearButtons(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetResponse + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetResponse") == 0) { + stack->correctParams(1); + bool autoSelectLast = stack->pop()->getBool(); + + if (_responseBox) { + _responseBox->weedResponses(); + + if (_responseBox->_responses.size() == 0) { + stack->pushNULL(); + return STATUS_OK; + } + + + if (_responseBox->_responses.size() == 1 && autoSelectLast) { + stack->pushInt(_responseBox->_responses[0]->_iD); + _responseBox->handleResponse(_responseBox->_responses[0]); + _responseBox->clearResponses(); + return STATUS_OK; + } + + _responseBox->createButtons(); + _responseBox->_waitingScript = script; + script->waitForExclusive(_responseBox); + _state = GAME_SEMI_FROZEN; + _stateEx = GAME_WAITING_RESPONSE; + } else { + script->runtimeError("Game.GetResponse: response box is not defined"); + stack->pushNULL(); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // GetNumResponses + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNumResponses") == 0) { + stack->correctParams(0); + if (_responseBox) { + _responseBox->weedResponses(); + stack->pushInt(_responseBox->_responses.size()); + } else { + script->runtimeError("Game.GetNumResponses: response box is not defined"); + stack->pushNULL(); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // StartDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StartDlgBranch") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + Common::String branchName; + if (val->isNULL()) { + branchName.format("line%d", script->_currentLine); + } else { + branchName = val->getString(); + } + + startDlgBranch(branchName.c_str(), script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EndDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EndDlgBranch") == 0) { + stack->correctParams(1); + + const char *branchName = NULL; + ScValue *val = stack->pop(); + if (!val->isNULL()) { + branchName = val->getString(); + } + endDlgBranch(branchName, script->_filename == NULL ? "" : script->_filename, script->_threadEvent == NULL ? "" : script->_threadEvent); + + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCurrentDlgBranch + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCurrentDlgBranch") == 0) { + stack->correctParams(0); + + if (_dlgPendingBranches.size() > 0) { + stack->pushString(_dlgPendingBranches[_dlgPendingBranches.size() - 1]); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TakeItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TakeItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // DropItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DropItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // GetItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // HasItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasItem") == 0) { + return _invObject->scCallMethod(script, stack, thisStack, name); + } + + ////////////////////////////////////////////////////////////////////////// + // IsItemTaken + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsItemTaken") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + for (uint32 i = 0; i < _inventories.size(); i++) { + AdInventory *inv = _inventories[i]; + + for (uint32 j = 0; j < inv->_takenItems.size(); j++) { + if (val->getNative() == inv->_takenItems[j]) { + stack->pushBool(true); + return STATUS_OK; + } else if (scumm_stricmp(val->getString(), inv->_takenItems[j]->getName()) == 0) { + stack->pushBool(true); + return STATUS_OK; + } + } + } + } else { + script->runtimeError("Game.IsItemTaken: item name expected"); + } + + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInventoryWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInventoryWindow") == 0) { + stack->correctParams(0); + if (_inventoryBox && _inventoryBox->_window) { + stack->pushNative(_inventoryBox->_window, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetResponsesWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetResponsesWindow") == 0 || strcmp(name, "GetResponseWindow") == 0) { + stack->correctParams(0); + if (_responseBox && _responseBox->_window) { + stack->pushNative(_responseBox->_window, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadResponseBox + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadResponseBox") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _gameRef->unregisterObject(_responseBox); + _responseBox = new AdResponseBox(_gameRef); + if (_responseBox && !DID_FAIL(_responseBox->loadFile(filename))) { + registerObject(_responseBox); + stack->pushBool(true); + } else { + delete _responseBox; + _responseBox = NULL; + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadInventoryBox + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadInventoryBox") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _gameRef->unregisterObject(_inventoryBox); + _inventoryBox = new AdInventoryBox(_gameRef); + if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile(filename))) { + registerObject(_inventoryBox); + stack->pushBool(true); + } else { + delete _inventoryBox; + _inventoryBox = NULL; + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadItems + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadItems") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool merge = stack->pop()->getBool(false); + + bool ret = loadItemsFile(filename, merge); + stack->pushBool(DID_SUCCEED(ret)); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddSpeechDir + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSpeechDir") == 0) { + stack->correctParams(1); + const char *dir = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(addSpeechDir(dir))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveSpeechDir + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveSpeechDir") == 0) { + stack->correctParams(1); + const char *dir = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(removeSpeechDir(dir))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSceneViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSceneViewport") == 0) { + stack->correctParams(4); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + int width = stack->pop()->getInt(); + int height = stack->pop()->getInt(); + + if (width <= 0) { + width = _renderer->_width; + } + if (height <= 0) { + height = _renderer->_height; + } + + if (!_sceneViewport) { + _sceneViewport = new BaseViewport(_gameRef); + } + if (_sceneViewport) { + _sceneViewport->setRect(x, y, x + width, y + height); + } + + stack->pushBool(true); + + return STATUS_OK; + } + + + else { + return BaseGame::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdGame::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("game"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Scene + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scene") { + if (_scene) { + _scValue->setNative(_scene, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // SelectedItem + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelectedItem") { + //if (_selectedItem) _scValue->setString(_selectedItem->_name); + if (_selectedItem) { + _scValue->setNative(_selectedItem, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // NumItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumItems") { + return _invObject->scGetProperty(name); + } + + ////////////////////////////////////////////////////////////////////////// + // SmartItemCursor + ////////////////////////////////////////////////////////////////////////// + else if (name == "SmartItemCursor") { + _scValue->setBool(_smartItemCursor); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryVisible + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryVisible") { + _scValue->setBool(_inventoryBox && _inventoryBox->_visible); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryScrollOffset + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryScrollOffset") { + if (_inventoryBox) { + _scValue->setInt(_inventoryBox->_scrollOffset); + } else { + _scValue->setInt(0); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ResponsesVisible (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ResponsesVisible") { + _scValue->setBool(_stateEx == GAME_WAITING_RESPONSE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PrevScene / PreviousScene (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "PrevScene" || name == "PreviousScene") { + if (!_prevSceneName) { + _scValue->setString(""); + } else { + _scValue->setString(_prevSceneName); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PrevSceneFilename / PreviousSceneFilename (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "PrevSceneFilename" || name == "PreviousSceneFilename") { + if (!_prevSceneFilename) { + _scValue->setString(""); + } else { + _scValue->setString(_prevSceneFilename); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LastResponse (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "LastResponse") { + if (!_responseBox || !_responseBox->_lastResponseText) { + _scValue->setString(""); + } else { + _scValue->setString(_responseBox->_lastResponseText); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LastResponseOrig (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "LastResponseOrig") { + if (!_responseBox || !_responseBox->_lastResponseTextOrig) { + _scValue->setString(""); + } else { + _scValue->setString(_responseBox->_lastResponseTextOrig); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryObject + ////////////////////////////////////////////////////////////////////////// + else if (name == "InventoryObject") { + if (_inventoryOwner == _invObject) { + _scValue->setNative(this, true); + } else { + _scValue->setNative(_inventoryOwner, true); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TotalNumItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "TotalNumItems") { + _scValue->setInt(_items.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkSkipButton + ////////////////////////////////////////////////////////////////////////// + else if (name == "TalkSkipButton") { + _scValue->setInt(_talkSkipButton); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ChangingScene + ////////////////////////////////////////////////////////////////////////// + else if (name == "ChangingScene") { + _scValue->setBool(_scheduledScene != NULL); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // StartupScene + ////////////////////////////////////////////////////////////////////////// + else if (name == "StartupScene") { + if (!_startupScene) { + _scValue->setNULL(); + } else { + _scValue->setString(_startupScene); + } + return _scValue; + } + + else { + return BaseGame::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // SelectedItem + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SelectedItem") == 0) { + if (value->isNULL()) { + _selectedItem = NULL; + } else { + if (value->isNative()) { + _selectedItem = NULL; + for (uint32 i = 0; i < _items.size(); i++) { + if (_items[i] == value->getNative()) { + _selectedItem = (AdItem *)value->getNative(); + break; + } + } + } else { + // try to get by name + _selectedItem = getItemByName(value->getString()); + } + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SmartItemCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SmartItemCursor") == 0) { + _smartItemCursor = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryVisible + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryVisible") == 0) { + if (_inventoryBox) { + _inventoryBox->_visible = value->getBool(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryObject") == 0) { + if (_inventoryOwner && _inventoryBox) { + _inventoryOwner->getInventory()->_scrollOffset = _inventoryBox->_scrollOffset; + } + + if (value->isNULL()) { + _inventoryOwner = _invObject; + } else { + BaseObject *obj = (BaseObject *)value->getNative(); + if (obj == this) { + _inventoryOwner = _invObject; + } else if (_gameRef->validObject(obj)) { + _inventoryOwner = (AdObject *)obj; + } + } + + if (_inventoryOwner && _inventoryBox) { + _inventoryBox->_scrollOffset = _inventoryOwner->getInventory()->_scrollOffset; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InventoryScrollOffset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InventoryScrollOffset") == 0) { + if (_inventoryBox) { + _inventoryBox->_scrollOffset = value->getInt(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TalkSkipButton + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TalkSkipButton") == 0) { + int val = value->getInt(); + if (val < 0) { + val = 0; + } + if (val > TALK_SKIP_NONE) { + val = TALK_SKIP_NONE; + } + _talkSkipButton = (TTalkSkipButton)val; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StartupScene + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StartupScene") == 0) { + if (value == NULL) { + delete[] _startupScene; + _startupScene = NULL; + } else { + BaseUtils::setString(&_startupScene, value->getString()); + } + + return STATUS_OK; + } + + else { + return BaseGame::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) { + ScValue *thisObj; + + ////////////////////////////////////////////////////////////////////////// + // Actor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Actor") == 0) { + stack->correctParams(0); + thisObj = thisStack->getTop(); + + thisObj->setNative(new AdActor(_gameRef)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Entity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Entity") == 0) { + stack->correctParams(0); + thisObj = thisStack->getTop(); + + thisObj->setNative(new AdEntity(_gameRef)); + stack->pushNULL(); + } + + + ////////////////////////////////////////////////////////////////////////// + // call parent + else { + return BaseGame::externalCall(script, stack, thisStack, name); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::showCursor() { + if (_cursorHidden) { + return STATUS_OK; + } + + if (_selectedItem && _gameRef->_state == GAME_RUNNING && _stateEx == GAME_NORMAL && _interactive) { + if (_selectedItem->_cursorCombined) { + BaseSprite *origLastCursor = _lastCursor; + BaseGame::showCursor(); + _lastCursor = origLastCursor; + } + if (_activeObject && _selectedItem->_cursorHover && _activeObject->getExtendedFlag("usable")) { + if (!_smartItemCursor || _activeObject->canHandleEvent(_selectedItem->getName())) { + return drawCursor(_selectedItem->_cursorHover); + } else { + return drawCursor(_selectedItem->_cursorNormal); + } + } else { + return drawCursor(_selectedItem->_cursorNormal); + } + } else { + return BaseGame::showCursor(); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdGame::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing GAME file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(GAME) +TOKEN_DEF(AD_GAME) +TOKEN_DEF(RESPONSE_BOX) +TOKEN_DEF(INVENTORY_BOX) +TOKEN_DEF(ITEMS) +TOKEN_DEF(ITEM) +TOKEN_DEF(TALK_SKIP_BUTTON) +TOKEN_DEF(SCENE_VIEWPORT) +TOKEN_DEF(ENTITY_CONTAINER) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(STARTUP_SCENE) +TOKEN_DEF(DEBUG_STARTUP_SCENE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(GAME) + TOKEN_TABLE(AD_GAME) + TOKEN_TABLE(RESPONSE_BOX) + TOKEN_TABLE(INVENTORY_BOX) + TOKEN_TABLE(ITEMS) + TOKEN_TABLE(TALK_SKIP_BUTTON) + TOKEN_TABLE(SCENE_VIEWPORT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(STARTUP_SCENE) + TOKEN_TABLE(DEBUG_STARTUP_SCENE) + TOKEN_TABLE_END + + byte *params; + byte *params2; + int cmd = 1; + BaseParser parser; + + bool itemFound = false, itemsFound = false; + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_GAME: + if (DID_FAIL(BaseGame::loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AD_GAME: + while (cmd > 0 && (cmd = parser.getCommand((char **)¶ms, commands, (char **)¶ms2)) > 0) { + switch (cmd) { + case TOKEN_RESPONSE_BOX: + delete _responseBox; + _responseBox = new AdResponseBox(_gameRef); + if (_responseBox && !DID_FAIL(_responseBox->loadFile((char *)params2))) { + registerObject(_responseBox); + } else { + delete _responseBox; + _responseBox = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_INVENTORY_BOX: + delete _inventoryBox; + _inventoryBox = new AdInventoryBox(_gameRef); + if (_inventoryBox && !DID_FAIL(_inventoryBox->loadFile((char *)params2))) { + registerObject(_inventoryBox); + } else { + delete _inventoryBox; + _inventoryBox = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ITEMS: + itemsFound = true; + BaseUtils::setString(&_itemsFile, (char *)params2); + if (DID_FAIL(loadItemsFile(_itemsFile))) { + delete[] _itemsFile; + _itemsFile = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TALK_SKIP_BUTTON: + if (scumm_stricmp((char *)params2, "right") == 0) { + _talkSkipButton = TALK_SKIP_RIGHT; + } else if (scumm_stricmp((char *)params2, "both") == 0) { + _talkSkipButton = TALK_SKIP_BOTH; + } else { + _talkSkipButton = TALK_SKIP_LEFT; + } + break; + + case TOKEN_SCENE_VIEWPORT: { + Rect32 rc; + parser.scanStr((char *)params2, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom); + if (!_sceneViewport) { + _sceneViewport = new BaseViewport(_gameRef); + } + if (_sceneViewport) { + _sceneViewport->setRect(rc.left, rc.top, rc.right, rc.bottom); + } + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params2, false); + break; + + case TOKEN_STARTUP_SCENE: + BaseUtils::setString(&_startupScene, (char *)params2); + break; + + case TOKEN_DEBUG_STARTUP_SCENE: + BaseUtils::setString(&_debugStartupScene, (char *)params2); + break; + } + } + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in GAME definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading GAME definition"); + return STATUS_FAILED; + } + + if (itemFound && !itemsFound) { + _gameRef->LOG(0, "**Warning** Please put the items definition to a separate file."); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + BaseGame::persist(persistMgr); + + _dlgPendingBranches.persist(persistMgr); + + _inventories.persist(persistMgr); + persistMgr->transfer(TMEMBER(_inventoryBox)); + + _objects.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_prevSceneName)); + persistMgr->transfer(TMEMBER(_prevSceneFilename)); + + persistMgr->transfer(TMEMBER(_responseBox)); + _responsesBranch.persist(persistMgr); + _responsesGame.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scene)); + _sceneStates.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scheduledFadeIn)); + persistMgr->transfer(TMEMBER(_scheduledScene)); + persistMgr->transfer(TMEMBER(_selectedItem)); + persistMgr->transfer(TMEMBER_INT(_talkSkipButton)); + + _sentences.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_sceneViewport)); + persistMgr->transfer(TMEMBER_INT(_stateEx)); + persistMgr->transfer(TMEMBER(_initialScene)); + persistMgr->transfer(TMEMBER(_debugStartupScene)); + + persistMgr->transfer(TMEMBER(_invObject)); + persistMgr->transfer(TMEMBER(_inventoryOwner)); + persistMgr->transfer(TMEMBER(_tempDisableSaveState)); + _items.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_itemsFile)); + + _speechDirs.persist(persistMgr); + persistMgr->transfer(TMEMBER(_smartItemCursor)); + + if (!persistMgr->getIsSaving()) { + _initialScene = false; + } + + persistMgr->transfer(TMEMBER(_startupScene)); + + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void AdGame::setPrevSceneName(const char *name) { + delete[] _prevSceneName; + _prevSceneName = NULL; + if (name) { + _prevSceneName = new char[strlen(name) + 1]; + if (_prevSceneName) { + strcpy(_prevSceneName, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdGame::setPrevSceneFilename(const char *name) { + delete[] _prevSceneFilename; + _prevSceneFilename = NULL; + if (name) { + _prevSceneFilename = new char[strlen(name) + 1]; + if (_prevSceneFilename) { + strcpy(_prevSceneFilename, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::scheduleChangeScene(const char *filename, bool fadeIn) { + delete[] _scheduledScene; + _scheduledScene = NULL; + + if (_scene && !_scene->_initialized) { + return changeScene(filename, fadeIn); + } else { + _scheduledScene = new char [strlen(filename) + 1]; + strcpy(_scheduledScene, filename); + + _scheduledFadeIn = fadeIn; + + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) { + BaseGame::getVersion(verMajor, verMinor, NULL, NULL); + + if (extMajor) { + *extMajor = 0; + } + if (extMinor) { + *extMinor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadItemsFile(const char *filename, bool merge) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdGame::LoadItemsFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + //_filename = new char [strlen(filename)+1]; + //strcpy(_filename, filename); + + if (DID_FAIL(ret = loadItemsBuffer(buffer, merge))) { + _gameRef->LOG(0, "Error parsing ITEMS file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::loadItemsBuffer(byte *buffer, bool merge) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ITEM) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (!merge) { + while (_items.size() > 0) { + deleteItem(_items[0]); + } + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_ITEM: { + AdItem *item = new AdItem(_gameRef); + if (item && !DID_FAIL(item->loadBuffer(params, false))) { + // delete item with the same name, if exists + if (merge) { + AdItem *prevItem = getItemByName(item->getName()); + if (prevItem) { + deleteItem(prevItem); + } + } + addItem(item); + } else { + delete item; + item = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ITEMS definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ITEMS definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneState *AdGame::getSceneState(const char *filename, bool saving) { + char *filenameCor = new char[strlen(filename) + 1]; + strcpy(filenameCor, filename); + for (uint32 i = 0; i < strlen(filenameCor); i++) { + if (filenameCor[i] == '/') { + filenameCor[i] = '\\'; + } + } + + for (uint32 i = 0; i < _sceneStates.size(); i++) { + if (scumm_stricmp(_sceneStates[i]->_filename, filenameCor) == 0) { + delete[] filenameCor; + return _sceneStates[i]; + } + } + + if (saving) { + AdSceneState *ret = new AdSceneState(_gameRef); + ret->setFilename(filenameCor); + + _sceneStates.add(ret); + + delete[] filenameCor; + return ret; + } else { + delete[] filenameCor; + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::windowLoadHook(UIWindow *win, char **buffer, char **params) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY_CONTAINER) + TOKEN_TABLE_END + + int cmd = PARSERR_GENERIC; + BaseParser parser; + + cmd = parser.getCommand(buffer, commands, params); + switch (cmd) { + case TOKEN_ENTITY_CONTAINER: { + UIEntity *ent = new UIEntity(_gameRef); + if (!ent || DID_FAIL(ent->loadBuffer((byte *)*params, false))) { + delete ent; + ent = NULL; + cmd = PARSERR_GENERIC; + } else { + ent->_parent = win; + win->_widgets.add(ent); + } + } + break; + } + + if (cmd == PARSERR_TOKENNOTFOUND || cmd == PARSERR_GENERIC) { + return STATUS_FAILED; + } + + return STATUS_OK; + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) { + if (strcmp(name, "CreateEntityContainer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIEntity *ent = new UIEntity(_gameRef); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + + ent->_parent = win; + win->_widgets.add(ent); + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::startDlgBranch(const char *branchName, const char *scriptName, const char *eventName) { + char *name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1]; + if (name) { + sprintf(name, "%s.%s.%s", branchName, scriptName, eventName); + _dlgPendingBranches.add(name); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::endDlgBranch(const char *branchName, const char *scriptName, const char *eventName) { + char *name = NULL; + bool deleteName = false; + if (branchName == NULL && _dlgPendingBranches.size() > 0) { + name = _dlgPendingBranches[_dlgPendingBranches.size() - 1]; + } else { + if (branchName != NULL) { + name = new char[strlen(branchName) + 1 + strlen(scriptName) + 1 + strlen(eventName) + 1]; + if (name) { + sprintf(name, "%s.%s.%s", branchName, scriptName, eventName); + deleteName = true; + } + } + } + + if (name == NULL) { + return STATUS_OK; + } + + + int startIndex = -1; + for (int i = _dlgPendingBranches.size() - 1; i >= 0; i--) { + if (scumm_stricmp(name, _dlgPendingBranches[i]) == 0) { + startIndex = i; + break; + } + } + if (startIndex >= 0) { + for (uint32 i = startIndex; i < _dlgPendingBranches.size(); i++) { + //ClearBranchResponses(_dlgPendingBranches[i]); + delete[] _dlgPendingBranches[i]; + _dlgPendingBranches[i] = NULL; + } + _dlgPendingBranches.remove_at(startIndex, _dlgPendingBranches.size() - startIndex); + } + + // dialogue is over, forget selected responses + if (_dlgPendingBranches.size() == 0) { + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + } + + if (deleteName) { + delete[] name; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::clearBranchResponses(char *name) { + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (scumm_stricmp(name, _responsesBranch[i]->_context) == 0) { + delete _responsesBranch[i]; + _responsesBranch.remove_at(i); + i--; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addBranchResponse(int id) { + if (branchResponseUsed(id)) { + return STATUS_OK; + } + AdResponseContext *r = new AdResponseContext(_gameRef); + r->_id = id; + r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL); + _responsesBranch.add(r); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::branchResponseUsed(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (_responsesBranch[i]->_id == id) { + if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) { + return true; + } + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addGameResponse(int id) { + if (gameResponseUsed(id)) { + return STATUS_OK; + } + AdResponseContext *r = new AdResponseContext(_gameRef); + r->_id = id; + r->setContext(_dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL); + _responsesGame.add(r); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::gameResponseUsed(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + for (uint32 i = 0; i < _responsesGame.size(); i++) { + AdResponseContext *respContext = _responsesGame[i]; + if (respContext->_id == id) { + if ((context == NULL && respContext->_context == NULL) || ((context != NULL && respContext->_context != NULL) && scumm_stricmp(context, respContext->_context) == 0)) { + return true; + } + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::resetResponse(int id) { + char *context = _dlgPendingBranches.size() > 0 ? _dlgPendingBranches[_dlgPendingBranches.size() - 1] : NULL; + + for (uint32 i = 0; i < _responsesGame.size(); i++) { + if (_responsesGame[i]->_id == id) { + if ((context == NULL && _responsesGame[i]->_context == NULL) || scumm_stricmp(context, _responsesGame[i]->_context) == 0) { + delete _responsesGame[i]; + _responsesGame.remove_at(i); + break; + } + } + } + + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + if (_responsesBranch[i]->_id == id) { + if ((context == NULL && _responsesBranch[i]->_context == NULL) || scumm_stricmp(context, _responsesBranch[i]->_context) == 0) { + delete _responsesBranch[i]; + _responsesBranch.remove_at(i); + break; + } + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displayContent(bool doUpdate, bool displayAll) { + // init + if (doUpdate) { + initLoop(); + } + + // fill black + _renderer->fill(0, 0, 0); + if (!_editorMode) { + _renderer->setScreenViewport(); + } + + // playing exclusive video? + if (_videoPlayer->isPlaying()) { + if (doUpdate) { + _videoPlayer->update(); + } + _videoPlayer->display(); + } else if (_theoraPlayer) { + if (_theoraPlayer->isPlaying()) { + if (doUpdate) { + _theoraPlayer->update(); + } + _theoraPlayer->display(); + } + if (_theoraPlayer->isFinished()) { + delete _theoraPlayer; + _theoraPlayer = NULL; + } + } else { + + // process scripts + if (doUpdate) { + _scEngine->tick(); + } + + Point32 p; + getMousePos(&p); + + _scene->update(); + _scene->display(); + + + // display in-game windows + displayWindows(true); + if (_inventoryBox) { + _inventoryBox->display(); + } + if (_stateEx == GAME_WAITING_RESPONSE) { + _responseBox->display(); + } + _renderer->displayIndicator(); + + + if (doUpdate || displayAll) { + // display normal windows + displayWindows(false); + + setActiveObject(_gameRef->_renderer->getObjectAt(p.x, p.y)); + + // textual info + displaySentences(_state == GAME_FROZEN); + + showCursor(); + + if (_fader) { + _fader->display(); + } + _transMgr->update(); + } + + } + if (_loadingIcon) { + _loadingIcon->display(_loadingIconX, _loadingIconY); + if (!_loadingIconPersistent) { + delete _loadingIcon; + _loadingIcon = NULL; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::registerInventory(AdInventory *inv) { + for (uint32 i = 0; i < _inventories.size(); i++) { + if (_inventories[i] == inv) { + return STATUS_OK; + } + } + registerObject(inv); + _inventories.add(inv); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::unregisterInventory(AdInventory *inv) { + for (uint32 i = 0; i < _inventories.size(); i++) { + if (_inventories[i] == inv) { + unregisterObject(_inventories[i]); + _inventories.remove_at(i); + return STATUS_OK; + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::isItemTaken(char *itemName) { + for (uint32 i = 0; i < _inventories.size(); i++) { + AdInventory *inv = _inventories[i]; + + for (uint32 j = 0; j < inv->_takenItems.size(); j++) { + if (scumm_stricmp(itemName, inv->_takenItems[j]->getName()) == 0) { + return true; + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +AdItem *AdGame::getItemByName(const char *name) { + for (uint32 i = 0; i < _items.size(); i++) { + if (scumm_stricmp(_items[i]->getName(), name) == 0) { + return _items[i]; + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addItem(AdItem *item) { + _items.add(item); + return _gameRef->registerObject(item); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::resetContent() { + // clear pending dialogs + for (uint32 i = 0; i < _dlgPendingBranches.size(); i++) { + delete[] _dlgPendingBranches[i]; + } + _dlgPendingBranches.clear(); + + + // clear inventories + for (uint32 i = 0; i < _inventories.size(); i++) { + _inventories[i]->_takenItems.clear(); + } + + // clear scene states + for (uint32 i = 0; i < _sceneStates.size(); i++) { + delete _sceneStates[i]; + } + _sceneStates.clear(); + + // clear once responses + for (uint32 i = 0; i < _responsesBranch.size(); i++) { + delete _responsesBranch[i]; + } + _responsesBranch.clear(); + + // clear once game responses + for (uint32 i = 0; i < _responsesGame.size(); i++) { + delete _responsesGame[i]; + } + _responsesGame.clear(); + + // reload inventory items + if (_itemsFile) { + loadItemsFile(_itemsFile); + } + + _tempDisableSaveState = true; + + return BaseGame::resetContent(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::deleteItem(AdItem *item) { + if (!item) { + return STATUS_FAILED; + } + + if (_selectedItem == item) { + _selectedItem = NULL; + } + _scene->handleItemAssociations(item->getName(), false); + + // remove from all inventories + for (uint32 i = 0; i < _inventories.size(); i++) { + _inventories[i]->removeItem(item); + } + + // remove object + for (uint32 i = 0; i < _items.size(); i++) { + if (_items[i] == item) { + unregisterObject(_items[i]); + _items.remove_at(i); + break; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::addSpeechDir(const char *dir) { + if (!dir || dir[0] == '\0') { + return STATUS_FAILED; + } + + char *temp = new char[strlen(dir) + 2]; + strcpy(temp, dir); + if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') { + strcat(temp, "\\"); + } + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + if (scumm_stricmp(_speechDirs[i], temp) == 0) { + delete[] temp; + return STATUS_OK; + } + } + _speechDirs.add(temp); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::removeSpeechDir(const char *dir) { + if (!dir || dir[0] == '\0') { + return STATUS_FAILED; + } + + char *temp = new char[strlen(dir) + 2]; + strcpy(temp, dir); + if (temp[strlen(temp) - 1] != '\\' && temp[strlen(temp) - 1] != '/') { + strcat(temp, "\\"); + } + + bool found = false; + for (uint32 i = 0; i < _speechDirs.size(); i++) { + if (scumm_stricmp(_speechDirs[i], temp) == 0) { + delete[] _speechDirs[i]; + _speechDirs.remove_at(i); + found = true; + break; + } + } + delete[] temp; + + return found; +} + + +////////////////////////////////////////////////////////////////////////// +char *AdGame::findSpeechFile(char *stringID) { + char *ret = new char[MAX_PATH_LENGTH]; + + for (uint32 i = 0; i < _speechDirs.size(); i++) { + sprintf(ret, "%s%s.ogg", _speechDirs[i], stringID); + if (BaseFileManager::getEngineInstance()->hasFile(ret)) { + return ret; + } + + sprintf(ret, "%s%s.wav", _speechDirs[i], stringID); + if (BaseFileManager::getEngineInstance()->hasFile(ret)) { + return ret; + } + } + delete[] ret; + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::validMouse() { + Point32 pos; + BasePlatform::getCursorPos(&pos); + + return _renderer->pointInViewport(&pos); +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftDown() { + if (!validMouse()) { + return STATUS_OK; + } + if (_state == GAME_RUNNING && !_interactive) { + if (_talkSkipButton == TALK_SKIP_LEFT || _talkSkipButton == TALK_SKIP_BOTH) { + finishSentences(); + } + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftClick"); + } + } + + if (_activeObject != NULL) { + _gameRef->_capturedObject = _gameRef->_activeObject; + } + _mouseLeftDown = true; + BasePlatform::setCapture(/*_renderer->_window*/); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT); + } + + BasePlatform::releaseCapture(); + _capturedObject = NULL; + _mouseLeftDown = false; + + bool handled = /*_state==GAME_RUNNING &&*/ DID_SUCCEED(applyEvent("LeftRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftRelease"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseLeftDblClick() { + if (!validMouse()) { + return STATUS_OK; + } + + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftDoubleClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("LeftDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseRightDown() { + if (!validMouse()) { + return STATUS_OK; + } + if (_state == GAME_RUNNING && !_interactive) { + if (_talkSkipButton == TALK_SKIP_RIGHT || _talkSkipButton == TALK_SKIP_BOTH) { + finishSentences(); + } + return STATUS_OK; + } + + if ((_state == GAME_RUNNING && !_interactive) || _stateEx == GAME_WAITING_RESPONSE) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightClick"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("RightClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onMouseRightUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightRelease"); + } else if (_state == GAME_RUNNING && _scene && _scene->pointInViewport(_mousePos.x, _mousePos.y)) { + _scene->applyEvent("RightRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdGame::displayDebugInfo() { + char str[100]; + if (_gameRef->_debugDebugMode) { + sprintf(str, "Mouse: %d, %d (scene: %d, %d)", _mousePos.x, _mousePos.y, _mousePos.x + _scene->getOffsetLeft(), _mousePos.y + _scene->getOffsetTop()); + _systemFont->drawText((byte *)str, 0, 90, _renderer->_width, TAL_RIGHT); + + sprintf(str, "Scene: %s (prev: %s)", (_scene && _scene->getName()) ? _scene->getName() : "???", _prevSceneName ? _prevSceneName : "???"); + _systemFont->drawText((byte *)str, 0, 110, _renderer->_width, TAL_RIGHT); + } + return BaseGame::displayDebugInfo(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdGame::onScriptShutdown(ScScript *script) { + if (_responseBox && _responseBox->_waitingScript == script) { + _responseBox->_waitingScript = NULL; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_game.h b/engines/wintermute/ad/ad_game.h new file mode 100644 index 0000000000..81c79a3da8 --- /dev/null +++ b/engines/wintermute/ad/ad_game.h @@ -0,0 +1,163 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ +#ifndef WINTERMUTE_ADGAME_H +#define WINTERMUTE_ADGAME_H + +#include "engines/wintermute/ad/ad_types.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { +class AdItem; +class AdInventory; +class AdSceneState; +class AdScene; +class AdItem; +class AdObject; +class AdSentence; +class AdInventoryBox; +class AdResponseContext; +class AdResponseBox; +class AdGame : public BaseGame { +public: + virtual bool onScriptShutdown(ScScript *script); + + virtual bool onMouseLeftDown(); + virtual bool onMouseLeftUp(); + virtual bool onMouseLeftDblClick(); + virtual bool onMouseRightDown(); + virtual bool onMouseRightUp(); + + virtual bool displayDebugInfo(); + + bool addSpeechDir(const char *dir); + bool removeSpeechDir(const char *dir); + char *findSpeechFile(char *StringID); + + bool deleteItem(AdItem *Item); + char *_itemsFile; + bool _tempDisableSaveState; + virtual bool resetContent(); + bool addItem(AdItem *item); + AdItem *getItemByName(const char *name); + + AdObject *_inventoryOwner; + bool isItemTaken(char *itemName); + bool registerInventory(AdInventory *inv); + bool unregisterInventory(AdInventory *inv); + virtual bool displayContent(bool update = true, bool displayAll = false); + + bool gameResponseUsed(int ID); + bool addGameResponse(int ID); + bool resetResponse(int ID); + + bool branchResponseUsed(int ID); + bool addBranchResponse(int ID); + bool clearBranchResponses(char *name); + bool startDlgBranch(const char *branchName, const char *scriptName, const char *eventName); + bool endDlgBranch(const char *branchName, const char *scriptName, const char *eventName); + virtual bool windowLoadHook(UIWindow *win, char **buf, char **params); + virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name); + + AdSceneState *getSceneState(const char *filename, bool saving); + BaseViewport *_sceneViewport; + + int _texItemLifeTime; + int _texWalkLifeTime; + int _texStandLifeTime; + int _texTalkLifeTime; + + TTalkSkipButton _talkSkipButton; + + virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor); + bool scheduleChangeScene(const char *filename, bool fadeIn); + void setPrevSceneName(const char *name); + void setPrevSceneFilename(const char *name); + + AdItem *_selectedItem; + bool cleanup(); + DECLARE_PERSISTENT(AdGame, BaseGame) + + void finishSentences(); + bool showCursor(); + + TGameStateEx _stateEx; + + bool displaySentences(bool frozen); + void addSentence(AdSentence *sentence); + bool changeScene(const char *filename, bool fadeIn); + bool removeObject(AdObject *object); + bool addObject(AdObject *object); + AdScene *_scene; + bool initLoop(); + AdGame(const Common::String &gameId); + virtual ~AdGame(); + + BaseArray<AdObject *> _objects; + + virtual bool loadFile(const char *filename); + virtual bool loadBuffer(byte *buffer, bool complete = true); + + bool loadItemsFile(const char *filename, bool merge = false); + bool loadItemsBuffer(byte *buffer, bool merge = false); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + bool validMouse(); +private: + virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name); + + AdObject *_invObject; + BaseArray<AdInventory *> _inventories; + char *_scheduledScene; + bool _scheduledFadeIn; + char *_prevSceneName; + char *_prevSceneFilename; + char *_debugStartupScene; + char *_startupScene; + bool _initialScene; + bool _smartItemCursor; + BaseArray<char *> _speechDirs; + BaseArray<AdItem *> _items; + + BaseArray<AdSentence *> _sentences; + + BaseArray<AdSceneState *> _sceneStates; + BaseArray<char *> _dlgPendingBranches; + + BaseArray<AdResponseContext *> _responsesBranch; + BaseArray<AdResponseContext *> _responsesGame; + + AdResponseBox *_responseBox; + AdInventoryBox *_inventoryBox; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_inventory.cpp b/engines/wintermute/ad/ad_inventory.cpp new file mode 100644 index 0000000000..72f8fa0fb4 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory.cpp @@ -0,0 +1,136 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdInventory, false) + +////////////////////////////////////////////////////////////////////////// +AdInventory::AdInventory(BaseGame *inGame) : BaseObject(inGame) { + _scrollOffset = 0; +} + + +////////////////////////////////////////////////////////////////////////// +AdInventory::~AdInventory() { + _takenItems.clear(); // ref only +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::insertItem(const char *name, const char *insertAfter) { + if (name == NULL) { + return STATUS_FAILED; + } + + AdItem *item = ((AdGame *)_gameRef)->getItemByName(name); + if (item == NULL) { + return STATUS_FAILED; + } + + int insertIndex = -1; + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) { + _takenItems.remove_at(i); + i--; + continue; + } + if (insertAfter && scumm_stricmp(_takenItems[i]->getName(), insertAfter) == 0) { + insertIndex = i + 1; + } + } + + + if (insertIndex == -1) { + _takenItems.add(item); + } else { + _takenItems.insert_at(insertIndex, item); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::removeItem(const char *name) { + if (name == NULL) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (scumm_stricmp(_takenItems[i]->getName(), name) == 0) { + if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) { + ((AdGame *)_gameRef)->_selectedItem = NULL; + } + _takenItems.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::removeItem(AdItem *item) { + if (item == NULL) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _takenItems.size(); i++) { + if (_takenItems[i] == item) { + if (((AdGame *)_gameRef)->_selectedItem == _takenItems[i]) { + ((AdGame *)_gameRef)->_selectedItem = NULL; + } + _takenItems.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool AdInventory::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + _takenItems.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollOffset)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_inventory.h b/engines/wintermute/ad/ad_inventory.h new file mode 100644 index 0000000000..4017d914bc --- /dev/null +++ b/engines/wintermute/ad/ad_inventory.h @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADINVENTORY_H +#define WINTERMUTE_ADINVENTORY_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdItem; + +class AdInventory : public BaseObject { +public: + DECLARE_PERSISTENT(AdInventory, BaseObject) + bool removeItem(const char *name); + bool removeItem(AdItem *Item); + bool insertItem(const char *name, const char *insertAfter = NULL); + AdInventory(BaseGame *inGame); + virtual ~AdInventory(); + BaseArray<AdItem *> _takenItems; + int _scrollOffset; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_inventory_box.cpp b/engines/wintermute/ad/ad_inventory_box.cpp new file mode 100644 index 0000000000..7ae8ff8d69 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory_box.cpp @@ -0,0 +1,389 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/rect.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdInventoryBox, false) + +////////////////////////////////////////////////////////////////////////// +AdInventoryBox::AdInventoryBox(BaseGame *inGame) : BaseObject(inGame) { + _itemsArea.setEmpty(); + _scrollOffset = 0; + _spacing = 0; + _itemWidth = _itemHeight = 50; + _scrollBy = 1; + + _window = NULL; + _closeButton = NULL; + + _hideSelected = false; + + _visible = false; + _exclusive = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdInventoryBox::~AdInventoryBox() { + _gameRef->unregisterObject(_window); + _window = NULL; + + delete _closeButton; + _closeButton = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "close") == 0) { + _visible = false; + } else if (scumm_stricmp(obj->getName(), "prev") == 0) { + _scrollOffset -= _scrollBy; + _scrollOffset = MAX(_scrollOffset, 0); + } else if (scumm_stricmp(obj->getName(), "next") == 0) { + _scrollOffset += _scrollBy; + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + error("AdInventoryBox::Listen - Unhandled enum"); + break; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::display() { + AdGame *adGame = (AdGame *)_gameRef; + + if (!_visible) { + return STATUS_OK; + } + + int itemsX, itemsY; + itemsX = (int)floor((float)((_itemsArea.right - _itemsArea.left + _spacing) / (_itemWidth + _spacing))); + itemsY = (int)floor((float)((_itemsArea.bottom - _itemsArea.top + _spacing) / (_itemHeight + _spacing))); + + if (_window) { + _window->enableWidget("prev", _scrollOffset > 0); + _window->enableWidget("next", _scrollOffset + itemsX * itemsY < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size()); + } + + + if (_closeButton) { + _closeButton->_posX = _closeButton->_posY = 0; + _closeButton->_width = _gameRef->_renderer->_width; + _closeButton->_height = _gameRef->_renderer->_height; + + _closeButton->display(); + } + + + // display window + Rect32 rect = _itemsArea; + if (_window) { + rect.offsetRect(_window->_posX, _window->_posY); + _window->display(); + } + + // display items + if (_window && _window->_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = _window->_alphaColor; + } + int yyy = rect.top; + for (int j = 0; j < itemsY; j++) { + int xxx = rect.left; + for (int i = 0; i < itemsX; i++) { + int itemIndex = _scrollOffset + j * itemsX + i; + if (itemIndex >= 0 && itemIndex < (int32)adGame->_inventoryOwner->getInventory()->_takenItems.size()) { + AdItem *item = adGame->_inventoryOwner->getInventory()->_takenItems[itemIndex]; + if (item != ((AdGame *)_gameRef)->_selectedItem || !_hideSelected) { + item->update(); + item->display(xxx, yyy); + } + } + + xxx += (_itemWidth + _spacing); + } + yyy += (_itemHeight + _spacing); + } + if (_window && _window->_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdInventoryBox::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing INVENTORY_BOX file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(INVENTORY_BOX) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(WINDOW) +TOKEN_DEF(EXCLUSIVE) +TOKEN_DEF(ALWAYS_VISIBLE) +TOKEN_DEF(AREA) +TOKEN_DEF(SPACING) +TOKEN_DEF(ITEM_WIDTH) +TOKEN_DEF(ITEM_HEIGHT) +TOKEN_DEF(SCROLL_BY) +TOKEN_DEF(NAME) +TOKEN_DEF(CAPTION) +TOKEN_DEF(HIDE_SELECTED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(INVENTORY_BOX) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(EXCLUSIVE) + TOKEN_TABLE(ALWAYS_VISIBLE) + TOKEN_TABLE(AREA) + TOKEN_TABLE(SPACING) + TOKEN_TABLE(ITEM_WIDTH) + TOKEN_TABLE(ITEM_HEIGHT) + TOKEN_TABLE(SCROLL_BY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(HIDE_SELECTED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + bool alwaysVisible = false; + + _exclusive = false; + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_INVENTORY_BOX) { + _gameRef->LOG(0, "'INVENTORY_BOX' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_WINDOW: + delete _window; + _window = new UIWindow(_gameRef); + if (!_window || DID_FAIL(_window->loadBuffer(params, false))) { + delete _window; + _window = NULL; + cmd = PARSERR_GENERIC; + } else { + _gameRef->registerObject(_window); + } + break; + + case TOKEN_AREA: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_itemsArea.left, &_itemsArea.top, &_itemsArea.right, &_itemsArea.bottom); + break; + + case TOKEN_EXCLUSIVE: + parser.scanStr((char *)params, "%b", &_exclusive); + break; + + case TOKEN_HIDE_SELECTED: + parser.scanStr((char *)params, "%b", &_hideSelected); + break; + + case TOKEN_ALWAYS_VISIBLE: + parser.scanStr((char *)params, "%b", &alwaysVisible); + break; + + case TOKEN_SPACING: + parser.scanStr((char *)params, "%d", &_spacing); + break; + + case TOKEN_ITEM_WIDTH: + parser.scanStr((char *)params, "%d", &_itemWidth); + break; + + case TOKEN_ITEM_HEIGHT: + parser.scanStr((char *)params, "%d", &_itemHeight); + break; + + case TOKEN_SCROLL_BY: + parser.scanStr((char *)params, "%d", &_scrollBy); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in INVENTORY_BOX definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading INVENTORY_BOX definition"); + return STATUS_FAILED; + } + + if (_exclusive) { + delete _closeButton; + _closeButton = new UIButton(_gameRef); + if (_closeButton) { + _closeButton->setName("close"); + _closeButton->setListener(this, _closeButton, 0); + _closeButton->_parent = _window; + } + } + + _visible = alwaysVisible; + + if (_window) { + for (uint32 i = 0; i < _window->_widgets.size(); i++) { + if (!_window->_widgets[i]->_listenerObject) { + _window->_widgets[i]->setListener(this, _window->_widgets[i], 0); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "INVENTORY_BOX\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _itemsArea.left, _itemsArea.top, _itemsArea.right, _itemsArea.bottom); + + buffer->putTextIndent(indent + 2, "EXCLUSIVE=%s\n", _exclusive ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "HIDE_SELECTED=%s\n", _hideSelected ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "ALWAYS_VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing); + buffer->putTextIndent(indent + 2, "ITEM_WIDTH=%d\n", _itemWidth); + buffer->putTextIndent(indent + 2, "ITEM_HEIGHT=%d\n", _itemHeight); + buffer->putTextIndent(indent + 2, "SCROLL_BY=%d\n", _scrollBy); + + buffer->putTextIndent(indent + 2, "\n"); + + // window + if (_window) { + _window->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdInventoryBox::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_closeButton)); + persistMgr->transfer(TMEMBER(_hideSelected)); + persistMgr->transfer(TMEMBER(_itemHeight)); + persistMgr->transfer(TMEMBER(_itemsArea)); + persistMgr->transfer(TMEMBER(_itemWidth)); + persistMgr->transfer(TMEMBER(_scrollBy)); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_spacing)); + persistMgr->transfer(TMEMBER(_visible)); + persistMgr->transfer(TMEMBER(_window)); + persistMgr->transfer(TMEMBER(_exclusive)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_inventory_box.h b/engines/wintermute/ad/ad_inventory_box.h new file mode 100644 index 0000000000..cb6d084562 --- /dev/null +++ b/engines/wintermute/ad/ad_inventory_box.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADINVENTORYBOX_H +#define WINTERMUTE_ADINVENTORYBOX_H + +#include "engines/wintermute/base/base_object.h" +#include "common/rect.h" + +namespace Wintermute { +class UIButton; +class UIWindow; + +class AdInventoryBox : public BaseObject { +public: + bool _hideSelected; + DECLARE_PERSISTENT(AdInventoryBox, BaseObject) + bool _visible; + virtual bool display(); + UIButton *_closeButton; + int _spacing; + int _scrollOffset; + Rect32 _itemsArea; + bool listen(BaseScriptHolder *param1, uint32 param2); + UIWindow *_window; + AdInventoryBox(BaseGame *inGame); + virtual ~AdInventoryBox(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); +private: + bool _exclusive; + int _scrollBy; + int _itemHeight; + int _itemWidth; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_item.cpp b/engines/wintermute/ad/ad_item.cpp new file mode 100644 index 0000000000..427b1c7db4 --- /dev/null +++ b/engines/wintermute/ad/ad_item.cpp @@ -0,0 +1,813 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdItem, false) + +////////////////////////////////////////////////////////////////////////// +AdItem::AdItem(BaseGame *inGame) : AdTalkHolder(inGame) { + _spriteHover = NULL; + _cursorNormal = _cursorHover = NULL; + + _cursorCombined = true; + _inInventory = false; + + _displayAmount = false; + _amount = 0; + _amountOffsetX = 0; + _amountOffsetY = 0; + _amountAlign = TAL_RIGHT; + _amountString = NULL; + + _state = STATE_READY; + + _movable = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdItem::~AdItem() { + delete _spriteHover; + delete _cursorNormal; + delete _cursorHover; + _spriteHover = NULL; + _cursorNormal = NULL; + _cursorHover = NULL; + + delete[] _amountString; + _amountString = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdItem::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ITEM file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ITEM) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(CURSOR_HOVER) +TOKEN_DEF(CURSOR_COMBINED) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(IMAGE_HOVER) +TOKEN_DEF(IMAGE) +TOKEN_DEF(EVENTS) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(FONT) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(TALK_SPECIAL) +TOKEN_DEF(TALK) +TOKEN_DEF(SPRITE_HOVER) +TOKEN_DEF(SPRITE) +TOKEN_DEF(DISPLAY_AMOUNT) +TOKEN_DEF(AMOUNT_OFFSET_X) +TOKEN_DEF(AMOUNT_OFFSET_Y) +TOKEN_DEF(AMOUNT_ALIGN) +TOKEN_DEF(AMOUNT_STRING) +TOKEN_DEF(AMOUNT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdItem::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ITEM) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(CURSOR_HOVER) + TOKEN_TABLE(CURSOR_COMBINED) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(IMAGE_HOVER) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(FONT) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(TALK_SPECIAL) + TOKEN_TABLE(TALK) + TOKEN_TABLE(SPRITE_HOVER) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(DISPLAY_AMOUNT) + TOKEN_TABLE(AMOUNT_OFFSET_X) + TOKEN_TABLE(AMOUNT_OFFSET_Y) + TOKEN_TABLE(AMOUNT_ALIGN) + TOKEN_TABLE(AMOUNT_STRING) + TOKEN_TABLE(AMOUNT) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ITEM) { + _gameRef->LOG(0, "'ITEM' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int ar = 0, ag = 0, ab = 0, alpha = 255; + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_FONT: + setFont((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_IMAGE: + case TOKEN_SPRITE: + delete _sprite; + _sprite = new BaseSprite(_gameRef, this); + if (!_sprite || DID_FAIL(_sprite->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _sprite; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_HOVER: + case TOKEN_SPRITE_HOVER: + delete _spriteHover; + _spriteHover = new BaseSprite(_gameRef, this); + if (!_spriteHover || DID_FAIL(_spriteHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _spriteHover; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AMOUNT: + parser.scanStr((char *)params, "%d", &_amount); + break; + + case TOKEN_DISPLAY_AMOUNT: + parser.scanStr((char *)params, "%b", &_displayAmount); + break; + + case TOKEN_AMOUNT_OFFSET_X: + parser.scanStr((char *)params, "%d", &_amountOffsetX); + break; + + case TOKEN_AMOUNT_OFFSET_Y: + parser.scanStr((char *)params, "%d", &_amountOffsetY); + break; + + case TOKEN_AMOUNT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _amountAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _amountAlign = TAL_RIGHT; + } else { + _amountAlign = TAL_CENTER; + } + break; + + case TOKEN_AMOUNT_STRING: + BaseUtils::setString(&_amountString, (char *)params); + break; + + case TOKEN_TALK: { + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSprites.add(spr); + } + } + break; + + case TOKEN_TALK_SPECIAL: { + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile((char *)params, ((AdGame *)_gameRef)->_texTalkLifeTime))) { + cmd = PARSERR_GENERIC; + } else { + _talkSpritesEx.add(spr); + } + } + break; + + case TOKEN_CURSOR: + delete _cursorNormal; + _cursorNormal = new BaseSprite(_gameRef); + if (!_cursorNormal || DID_FAIL(_cursorNormal->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _cursorNormal; + _cursorNormal = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_HOVER: + delete _cursorHover; + _cursorHover = new BaseSprite(_gameRef); + if (!_cursorHover || DID_FAIL(_cursorHover->loadFile((char *)params, ((AdGame *)_gameRef)->_texItemLifeTime))) { + delete _cursorHover; + _cursorHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_COMBINED: + parser.scanStr((char *)params, "%b", &_cursorCombined); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ITEM definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ITEM definition"); + return STATUS_FAILED; + } + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::update() { + _currentSprite = NULL; + + if (_state == STATE_READY && _animSprite) { + delete _animSprite; + _animSprite = NULL; + } + + // finished playing animation? + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL && _animSprite->isFinished()) { + _state = STATE_READY; + _currentSprite = _animSprite; + } + + if (_sentence && _state != STATE_TALKING) { + _sentence->finish(); + } + + // default: stand animation + if (!_currentSprite) { + _currentSprite = _sprite; + } + + switch (_state) { + ////////////////////////////////////////////////////////////////////////// + case STATE_PLAYING_ANIM: + _currentSprite = _animSprite; + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_READY: + if (!_animSprite) { + if (_gameRef->_activeObject == this && _spriteHover) { + _currentSprite = _spriteHover; + } else { + _currentSprite = _sprite; + } + } + break; + + ////////////////////////////////////////////////////////////////////////// + case STATE_TALKING: { + _sentence->update(); + if (_sentence->_currentSprite) { + _tempSprite2 = _sentence->_currentSprite; + } + + bool timeIsUp = (_sentence->_sound && _sentence->_soundStarted && (!_sentence->_sound->isPlaying() && !_sentence->_sound->isPaused())) || (!_sentence->_sound && _sentence->_duration <= _gameRef->_timer - _sentence->_startTime); + if (_tempSprite2 == NULL || _tempSprite2->isFinished() || (/*_tempSprite2->_looping &&*/ timeIsUp)) { + if (timeIsUp) { + _sentence->finish(); + _tempSprite2 = NULL; + _state = STATE_READY; + } else { + _tempSprite2 = getTalkStance(_sentence->getNextStance()); + if (_tempSprite2) { + _tempSprite2->reset(); + _currentSprite = _tempSprite2; + } + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } else { + _currentSprite = _tempSprite2; + ((AdGame *)_gameRef)->addSentence(_sentence); + } + } + default: + break; + } + _ready = (_state == STATE_READY); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::display(int x, int y) { + int width = 0; + if (_currentSprite) { + Rect32 rc; + _currentSprite->getBoundingRect(&rc, 0, 0); + width = rc.width(); + } + + _posX = x + width / 2; + _posY = y; + + bool ret; + if (_currentSprite) { + ret = _currentSprite->draw(x, y, this, 100, 100, _alphaColor); + } else { + ret = STATUS_OK; + } + + if (_displayAmount) { + int amountX = x; + int amountY = y + _amountOffsetY; + + if (_amountAlign == TAL_RIGHT) { + width -= _amountOffsetX; + amountX -= _amountOffsetX; + } + amountX += _amountOffsetX; + + BaseFont *font = _font ? _font : _gameRef->_systemFont; + if (font) { + if (_amountString) { + font->drawText((byte *)_amountString, amountX, amountY, width, _amountAlign); + } else { + char str[256]; + sprintf(str, "%d", _amount); + font->drawText((byte *)str, amountX, amountY, width, _amountAlign); + } + } + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdItem::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetHoverSprite + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetHoverSprite") == 0) { + stack->correctParams(1); + + bool setCurrent = false; + if (_currentSprite && _currentSprite == _spriteHover) { + setCurrent = true; + } + + const char *filename = stack->pop()->getString(); + + delete _spriteHover; + _spriteHover = NULL; + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetHoverSprite failed for file '%s'", filename); + } else { + _spriteHover = spr; + if (setCurrent) { + _currentSprite = _spriteHover; + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverSprite") == 0) { + stack->correctParams(0); + + if (!_spriteHover || !_spriteHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_spriteHover->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverSpriteObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverSpriteObject") == 0) { + stack->correctParams(0); + if (!_spriteHover) { + stack->pushNULL(); + } else { + stack->pushNative(_spriteHover, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetNormalCursor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetNormalCursor") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + delete _cursorNormal; + _cursorNormal = NULL; + BaseSprite *spr = new BaseSprite(_gameRef); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetNormalCursor failed for file '%s'", filename); + } else { + _cursorNormal = spr; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNormalCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNormalCursor") == 0) { + stack->correctParams(0); + + if (!_cursorNormal || !_cursorNormal->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorNormal->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNormalCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNormalCursorObject") == 0) { + stack->correctParams(0); + + if (!_cursorNormal) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorNormal, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetHoverCursor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetHoverCursor") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + delete _cursorHover; + _cursorHover = NULL; + BaseSprite *spr = new BaseSprite(_gameRef); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("Item.SetHoverCursor failed for file '%s'", filename); + } else { + _cursorHover = spr; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverCursor") == 0) { + stack->correctParams(0); + + if (!_cursorHover || !_cursorHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorHover->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverCursorObject") == 0) { + stack->correctParams(0); + + if (!_cursorHover) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorHover, true); + } + return STATUS_OK; + } else { + return AdTalkHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdItem::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("item"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayAmount + ////////////////////////////////////////////////////////////////////////// + else if (name == "DisplayAmount") { + _scValue->setBool(_displayAmount); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Amount + ////////////////////////////////////////////////////////////////////////// + else if (name == "Amount") { + _scValue->setInt(_amount); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetX + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountOffsetX") { + _scValue->setInt(_amountOffsetX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetY + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountOffsetY") { + _scValue->setInt(_amountOffsetY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountAlign") { + _scValue->setInt(_amountAlign); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountString + ////////////////////////////////////////////////////////////////////////// + else if (name == "AmountString") { + if (!_amountString) { + _scValue->setNULL(); + } else { + _scValue->setString(_amountString); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorCombined + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorCombined") { + _scValue->setBool(_cursorCombined); + return _scValue; + } else { + return AdTalkHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayAmount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisplayAmount") == 0) { + _displayAmount = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Amount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Amount") == 0) { + _amount = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountOffsetX") == 0) { + _amountOffsetX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountOffsetY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountOffsetY") == 0) { + _amountOffsetY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountAlign + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountAlign") == 0) { + _amountAlign = (TTextAlign)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AmountString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AmountString") == 0) { + if (value->isNULL()) { + delete[] _amountString; + _amountString = NULL; + } else { + BaseUtils::setString(&_amountString, value->getString()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorCombined + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorCombined") == 0) { + _cursorCombined = value->getBool(); + return STATUS_OK; + } else { + return AdTalkHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdItem::scToString() { + return "[item]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::persist(BasePersistenceManager *persistMgr) { + + AdTalkHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_cursorCombined)); + persistMgr->transfer(TMEMBER(_cursorHover)); + persistMgr->transfer(TMEMBER(_cursorNormal)); + persistMgr->transfer(TMEMBER(_spriteHover)); + persistMgr->transfer(TMEMBER(_inInventory)); + persistMgr->transfer(TMEMBER(_displayAmount)); + persistMgr->transfer(TMEMBER(_amount)); + persistMgr->transfer(TMEMBER(_amountOffsetX)); + persistMgr->transfer(TMEMBER(_amountOffsetY)); + persistMgr->transfer(TMEMBER_INT(_amountAlign)); + persistMgr->transfer(TMEMBER(_amountString)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdItem::getExtendedFlag(const char *flagName) { + if (!flagName) { + return false; + } else if (strcmp(flagName, "usable") == 0) { + return true; + } else { + return AdObject::getExtendedFlag(flagName); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_item.h b/engines/wintermute/ad/ad_item.h new file mode 100644 index 0000000000..79978f9f72 --- /dev/null +++ b/engines/wintermute/ad/ad_item.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADITEM_H +#define WINTERMUTE_ADITEM_H + + +#include "engines/wintermute/ad/ad_talk_holder.h" + +namespace Wintermute { + +class AdItem : public AdTalkHolder { +public: + bool update(); + DECLARE_PERSISTENT(AdItem, AdTalkHolder) + bool display(int x, int y); + bool getExtendedFlag(const char *flagName); + bool _inInventory; + bool _cursorCombined; + BaseSprite *_spriteHover; + BaseSprite *_cursorNormal; + BaseSprite *_cursorHover; + AdItem(BaseGame *inGame); + virtual ~AdItem(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + bool _displayAmount; + int _amount; + int _amountOffsetX; + int _amountOffsetY; + TTextAlign _amountAlign; + char *_amountString; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_layer.cpp b/engines/wintermute/ad/ad_layer.cpp new file mode 100644 index 0000000000..209c12b7a2 --- /dev/null +++ b/engines/wintermute/ad/ad_layer.cpp @@ -0,0 +1,564 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdLayer, false) + +////////////////////////////////////////////////////////////////////////// +AdLayer::AdLayer(BaseGame *inGame) : BaseObject(inGame) { + _main = false; + _width = _height = 0; + _active = true; + _closeUp = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdLayer::~AdLayer() { + for (uint32 i = 0; i < _nodes.size(); i++) { + delete _nodes[i]; + } + _nodes.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdLayer::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing LAYER file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(LAYER) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(MAIN) +TOKEN_DEF(ENTITY) +TOKEN_DEF(REGION) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(CLOSE_UP) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdLayer::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(LAYER) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(MAIN) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(REGION) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(CLOSE_UP) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_LAYER) { + _gameRef->LOG(0, "'LAYER' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_MAIN: + parser.scanStr((char *)params, "%b", &_main); + break; + + case TOKEN_CLOSE_UP: + parser.scanStr((char *)params, "%b", &_closeUp); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_REGION: { + AdRegion *region = new AdRegion(_gameRef); + AdSceneNode *node = new AdSceneNode(_gameRef); + if (!region || !node || DID_FAIL(region->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete region; + delete node; + region = NULL; + node = NULL; + } else { + node->setRegion(region); + _nodes.add(node); + } + } + break; + + case TOKEN_ENTITY: { + AdEntity *entity = new AdEntity(_gameRef); + AdSceneNode *node = new AdSceneNode(_gameRef); + if (entity) { + entity->_zoomable = false; // scene entites default to NOT zoom + } + if (!entity || !node || DID_FAIL(entity->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete entity; + delete node; + entity = NULL; + node = NULL; + } else { + node->setEntity(entity); + _nodes.add(node); + } + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in LAYER definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdLayer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetNode + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + int node = -1; + + if (val->_type == VAL_INT) { + node = val->getInt(); + } else { // get by name + for (uint32 i = 0; i < _nodes.size(); i++) { + if ((_nodes[i]->_type == OBJECT_ENTITY && scumm_stricmp(_nodes[i]->_entity->getName(), val->getString()) == 0) || + (_nodes[i]->_type == OBJECT_REGION && scumm_stricmp(_nodes[i]->_region->getName(), val->getString()) == 0)) { + node = i; + break; + } + } + } + + if (node < 0 || node >= (int32)_nodes.size()) { + stack->pushNULL(); + } else { + switch (_nodes[node]->_type) { + case OBJECT_ENTITY: + stack->pushNative(_nodes[node]->_entity, true); + break; + case OBJECT_REGION: + stack->pushNative(_nodes[node]->_region, true); + break; + default: + stack->pushNULL(); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddRegion / AddEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddRegion") == 0 || strcmp(name, "AddEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdSceneNode *node = new AdSceneNode(_gameRef); + if (strcmp(name, "AddRegion") == 0) { + AdRegion *region = new AdRegion(_gameRef); + if (!val->isNULL()) { + region->setName(val->getString()); + } + node->setRegion(region); + stack->pushNative(region, true); + } else { + AdEntity *entity = new AdEntity(_gameRef); + if (!val->isNULL()) { + entity->setName(val->getString()); + } + node->setEntity(entity); + stack->pushNative(entity, true); + } + _nodes.add(node); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertRegion / InsertEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertRegion") == 0 || strcmp(name, "InsertEntity") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + AdSceneNode *node = new AdSceneNode(_gameRef); + if (strcmp(name, "InsertRegion") == 0) { + AdRegion *region = new AdRegion(_gameRef); + if (!val->isNULL()) { + region->setName(val->getString()); + } + node->setRegion(region); + stack->pushNative(region, true); + } else { + AdEntity *entity = new AdEntity(_gameRef); + if (!val->isNULL()) { + entity->setName(val->getString()); + } + node->setEntity(entity); + stack->pushNative(entity, true); + } + if (index < 0) { + index = 0; + } + if (index <= (int32)_nodes.size() - 1) { + _nodes.insert_at(index, node); + } else { + _nodes.add(node); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdSceneNode *toDelete = NULL; + if (val->isNative()) { + BaseScriptable *temp = val->getNative(); + for (uint32 i = 0; i < _nodes.size(); i++) { + if (_nodes[i]->_region == temp || _nodes[i]->_entity == temp) { + toDelete = _nodes[i]; + break; + } + } + } else { + int index = val->getInt(); + if (index >= 0 && index < (int32)_nodes.size()) { + toDelete = _nodes[index]; + } + } + if (toDelete == NULL) { + stack->pushBool(false); + return STATUS_OK; + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + if (_nodes[i] == toDelete) { + delete _nodes[i]; + _nodes[i] = NULL; + _nodes.remove_at(i); + break; + } + } + stack->pushBool(true); + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdLayer::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("layer"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumNodes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumNodes") { + _scValue->setInt(_nodes.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Main (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Main") { + _scValue->setBool(_main); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CloseUp + ////////////////////////////////////////////////////////////////////////// + else if (name == "CloseUp") { + _scValue->setBool(_closeUp); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CloseUp + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CloseUp") == 0) { + _closeUp = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + if (_width < 0) { + _width = 0; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + if (_height < 0) { + _height = 0; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Active") == 0) { + bool b = value->getBool(); + if (b == false && _main) { + _gameRef->LOG(0, "Warning: cannot deactivate scene's main layer"); + } else { + _active = b; + } + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdLayer::scToString() { + return "[layer]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "LAYER {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "MAIN=%s\n", _main ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + if (_closeUp) { + buffer->putTextIndent(indent + 2, "CLOSE_UP=%s\n", _closeUp ? "TRUE" : "FALSE"); + } + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + switch (_nodes[i]->_type) { + case OBJECT_ENTITY: + _nodes[i]->_entity->saveAsText(buffer, indent + 2); + break; + case OBJECT_REGION: + _nodes[i]->_region->saveAsText(buffer, indent + 2); + break; + default: + error("AdLayer::SaveAsText - Unhandled enum"); + break; + } + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdLayer::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_closeUp)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_main)); + _nodes.persist(persistMgr); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_layer.h b/engines/wintermute/ad/ad_layer.h new file mode 100644 index 0000000000..de65e2822f --- /dev/null +++ b/engines/wintermute/ad/ad_layer.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADLAYER_H +#define WINTERMUTE_ADLAYER_H + +namespace Wintermute { +class AdSceneNode; +class AdLayer : public BaseObject { +public: + bool _closeUp; + DECLARE_PERSISTENT(AdLayer, BaseObject) + bool _active; + int _height; + int _width; + bool _main; + AdLayer(BaseGame *inGame); + virtual ~AdLayer(); + BaseArray<AdSceneNode *> _nodes; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_node_state.cpp b/engines/wintermute/ad/ad_node_state.cpp new file mode 100644 index 0000000000..493156c750 --- /dev/null +++ b/engines/wintermute/ad/ad_node_state.cpp @@ -0,0 +1,196 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdNodeState, false) + + +////////////////////////////////////////////////////////////////////////// +AdNodeState::AdNodeState(BaseGame *inGame) : BaseClass(inGame) { + _name = NULL; + _active = false; + for (int i = 0; i < 7; i++) { + _caption[i] = NULL; + } + _alphaColor = 0; + _filename = NULL; + _cursor = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdNodeState::~AdNodeState() { + delete[] _name; + delete[] _filename; + delete[] _cursor; + _name = NULL; + _filename = NULL; + _cursor = NULL; + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setName(const char *name) { + delete[] _name; + _name = NULL; + BaseUtils::setString(&_name, name); +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setFilename(const char *filename) { + delete[] _filename; + _filename = NULL; + BaseUtils::setString(&_filename, filename); +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setCursor(const char *filename) { + delete[] _cursor; + _cursor = NULL; + BaseUtils::setString(&_cursor, filename); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdNodeState::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_name)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_cursor)); + persistMgr->transfer(TMEMBER(_alphaColor)); + for (int i = 0; i < 7; i++) { + persistMgr->transfer(TMEMBER(_caption[i])); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdNodeState::setCaption(const char *caption, int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7) { + return; + } + + delete[] _caption[caseVal - 1]; + _caption[caseVal - 1] = new char[strlen(caption) + 1]; + if (_caption[caseVal - 1]) { + strcpy(_caption[caseVal - 1], caption); + _gameRef->_stringTable->expand(&_caption[caseVal - 1]); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdNodeState::getCaption(int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) { + return ""; + } else { + return _caption[caseVal - 1]; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdNodeState::transferEntity(AdEntity *entity, bool includingSprites, bool saving) { + if (!entity) { + return STATUS_FAILED; + } + + // HACK! + if (this->_gameRef != entity->_gameRef) { + this->_gameRef = entity->_gameRef; + } + + if (saving) { + for (int i = 0; i < 7; i++) { + if (entity->_caption[i]) { + setCaption(entity->_caption[i], i); + } + } + if (!entity->_region && entity->_sprite && entity->_sprite->getFilename()) { + if (includingSprites) { + setFilename(entity->_sprite->getFilename()); + } else { + setFilename(""); + } + } + if (entity->_cursor && entity->_cursor->getFilename()) { + setCursor(entity->_cursor->getFilename()); + } + _alphaColor = entity->_alphaColor; + _active = entity->_active; + } else { + for (int i = 0; i < 7; i++) { + if (_caption[i]) { + entity->setCaption(_caption[i], i); + } + } + if (_filename && !entity->_region && includingSprites && strcmp(_filename, "") != 0) { + if (!entity->_sprite || !entity->_sprite->getFilename() || scumm_stricmp(entity->_sprite->getFilename(), _filename) != 0) { + entity->setSprite(_filename); + } + } + if (_cursor) { + if (!entity->_cursor || !entity->_cursor->getFilename() || scumm_stricmp(entity->_cursor->getFilename(), _cursor) != 0) { + entity->setCursor(_cursor); + } + } + + entity->_active = _active; + entity->_alphaColor = _alphaColor; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_node_state.h b/engines/wintermute/ad/ad_node_state.h new file mode 100644 index 0000000000..e2050815a7 --- /dev/null +++ b/engines/wintermute/ad/ad_node_state.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADNODESTATE_H +#define WINTERMUTE_ADNODESTATE_H + +namespace Wintermute { + +class AdEntity; + +class AdNodeState : public BaseClass { +public: + bool _active; + bool transferEntity(AdEntity *entity, bool includingSprites, bool saving); + void setName(const char *name); + void setFilename(const char *filename); + void setCursor(const char *filename); + DECLARE_PERSISTENT(AdNodeState, BaseClass) + AdNodeState(BaseGame *inGame); + virtual ~AdNodeState(); + const char *getName() const { return _name; } +private: + char *_name; + char *_caption[7]; + void setCaption(const char *caption, int caseVal); + const char *getCaption(int caseVal); + uint32 _alphaColor; + char *_filename; + char *_cursor; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_object.cpp b/engines/wintermute/ad/ad_object.cpp new file mode 100644 index 0000000000..7b91daab2e --- /dev/null +++ b/engines/wintermute/ad/ad_object.cpp @@ -0,0 +1,1300 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "common/str.h" +#include "common/util.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdObject, false) + +////////////////////////////////////////////////////////////////////////// +AdObject::AdObject(BaseGame *inGame) : BaseObject(inGame) { + _type = OBJECT_NONE; + _state = _nextState = STATE_NONE; + + _active = true; + _drawn = false; + + _currentSprite = NULL; + _animSprite = NULL; + _tempSprite2 = NULL; + + _font = NULL; + + _sentence = NULL; + + _forcedTalkAnimName = NULL; + _forcedTalkAnimUsed = false; + + _blockRegion = NULL; + _wptGroup = NULL; + + _currentBlockRegion = NULL; + _currentWptGroup = NULL; + + _ignoreItems = false; + _sceneIndependent = false; + + _stickRegion = NULL; + + _subtitlesModRelative = true; + _subtitlesModX = 0; + _subtitlesModY = 0; + _subtitlesWidth = 0; + _subtitlesModXCenter = true; + + _inventory = NULL; + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + _currentRegions[i] = NULL; + } + + _partEmitter = NULL; + _partFollowParent = false; + _partOffsetX = _partOffsetY = 0; + + _registerAlias = this; +} + + +////////////////////////////////////////////////////////////////////////// +AdObject::~AdObject() { + _currentSprite = NULL; // reference only, don't delete + delete _animSprite; + _animSprite = NULL; + delete _sentence; + _sentence = NULL; + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = NULL; + + delete _blockRegion; + _blockRegion = NULL; + delete _wptGroup; + _wptGroup = NULL; + + delete _currentBlockRegion; + _currentBlockRegion = NULL; + delete _currentWptGroup; + _currentWptGroup = NULL; + + _tempSprite2 = NULL; // reference only + _stickRegion = NULL; + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + + if (_inventory) { + ((AdGame *)_gameRef)->unregisterInventory(_inventory); + _inventory = NULL; + } + + if (_partEmitter) { + _gameRef->unregisterObject(_partEmitter); + } + + + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + _gameRef->unregisterObject(_attachmentsPre[i]); + } + _attachmentsPre.clear(); + + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + _gameRef->unregisterObject(_attachmentsPost[i]); + } + _attachmentsPost.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::playAnim(const char *filename) { + delete _animSprite; + _animSprite = NULL; + _animSprite = new BaseSprite(_gameRef, this); + if (!_animSprite) { + _gameRef->LOG(0, "AdObject::PlayAnim: error creating temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename); + return STATUS_FAILED; + } + bool res = _animSprite->loadFile(filename); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdObject::PlayAnim: error loading temp sprite (object:\"%s\" sprite:\"%s\")", getName(), filename); + delete _animSprite; + _animSprite = NULL; + return res; + } + _state = STATE_PLAYING_ANIM; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::display() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::update() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // PlayAnim / PlayAnimAsync + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "PlayAnim") == 0 || strcmp(name, "PlayAnimAsync") == 0) { + stack->correctParams(1); + if (DID_FAIL(playAnim(stack->pop()->getString()))) { + stack->pushBool(false); + } else { + if (strcmp(name, "PlayAnimAsync") != 0) { + script->waitFor(this); + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + reset(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsTalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsTalking") == 0) { + stack->correctParams(0); + stack->pushBool(_state == STATE_TALKING); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopTalk / StopTalking + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopTalk") == 0 || strcmp(name, "StopTalking") == 0) { + stack->correctParams(0); + if (_sentence) { + _sentence->finish(); + } + if (_state == STATE_TALKING) { + _state = _nextState; + _nextState = STATE_READY; + stack->pushBool(true); + } else { + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ForceTalkAnim + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ForceTalkAnim") == 0) { + stack->correctParams(1); + const char *animName = stack->pop()->getString(); + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = new char[strlen(animName) + 1]; + strcpy(_forcedTalkAnimName, animName); + _forcedTalkAnimUsed = false; + stack->pushBool(true); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // Talk / TalkAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Talk") == 0 || strcmp(name, "TalkAsync") == 0) { + stack->correctParams(5); + + const char *text = stack->pop()->getString(); + ScValue *soundVal = stack->pop(); + int duration = stack->pop()->getInt(); + ScValue *valStances = stack->pop(); + + const char *stances = valStances->isNULL() ? NULL : valStances->getString(); + + int align = 0; + ScValue *val = stack->pop(); + if (val->isNULL()) { + align = TAL_CENTER; + } else { + align = val->getInt(); + } + + align = MIN(MAX(0, align), NUM_TEXT_ALIGN - 1); + + const char *sound = soundVal->isNULL() ? NULL : soundVal->getString(); + + talk(text, sound, duration, stances, (TTextAlign)align); + if (strcmp(name, "TalkAsync") != 0) { + script->waitForExclusive(this); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StickToRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StickToRegion") == 0) { + stack->correctParams(1); + + AdLayer *main = ((AdGame *)_gameRef)->_scene->_mainLayer; + bool regFound = false; + + uint32 i; + ScValue *val = stack->pop(); + if (val->isNULL() || !main) { + _stickRegion = NULL; + regFound = true; + } else if (val->isString()) { + const char *regionName = val->getString(); + for (i = 0; i < main->_nodes.size(); i++) { + if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region->getName() && scumm_stricmp(main->_nodes[i]->_region->getName(), regionName) == 0) { + _stickRegion = main->_nodes[i]->_region; + regFound = true; + break; + } + } + } else if (val->isNative()) { + BaseScriptable *obj = val->getNative(); + + for (i = 0; i < main->_nodes.size(); i++) { + if (main->_nodes[i]->_type == OBJECT_REGION && main->_nodes[i]->_region == obj) { + _stickRegion = main->_nodes[i]->_region; + regFound = true; + break; + } + } + + } + + if (!regFound) { + _stickRegion = NULL; + } + stack->pushBool(regFound); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isNULL()) { + setFont(NULL); + } else { + setFont(val->getString()); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFont") == 0) { + stack->correctParams(0); + if (_font && _font->getFilename()) { + stack->pushString(_font->getFilename()); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TakeItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TakeItem") == 0) { + stack->correctParams(2); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + const char *itemName = val->getString(); + val = stack->pop(); + const char *insertAfter = val->isNULL() ? NULL : val->getString(); + if (DID_FAIL(_inventory->insertItem(itemName, insertAfter))) { + script->runtimeError("Cannot add item '%s' to inventory", itemName); + } else { + // hide associated entities + ((AdGame *)_gameRef)->_scene->handleItemAssociations(itemName, false); + } + + } else { + script->runtimeError("TakeItem: item name expected"); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DropItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DropItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + if (DID_FAIL(_inventory->removeItem(val->getString()))) { + script->runtimeError("Cannot remove item '%s' from inventory", val->getString()); + } else { + // show associated entities + ((AdGame *)_gameRef)->_scene->handleItemAssociations(val->getString(), true); + } + } else { + script->runtimeError("DropItem: item name expected"); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (val->_type == VAL_STRING) { + AdItem *item = ((AdGame *)_gameRef)->getItemByName(val->getString()); + if (item) { + stack->pushNative(item, true); + } else { + stack->pushNULL(); + } + } else if (val->isNULL() || val->getInt() < 0 || val->getInt() >= (int32)_inventory->_takenItems.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_inventory->_takenItems[val->getInt()], true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasItem + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasItem") == 0) { + stack->correctParams(1); + + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + + ScValue *val = stack->pop(); + if (!val->isNULL()) { + for (uint32 i = 0; i < _inventory->_takenItems.size(); i++) { + if (val->getNative() == _inventory->_takenItems[i]) { + stack->pushBool(true); + return STATUS_OK; + } else if (scumm_stricmp(val->getString(), _inventory->_takenItems[i]->getName()) == 0) { + stack->pushBool(true); + return STATUS_OK; + } + } + } else { + script->runtimeError("HasItem: item name expected"); + } + + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateParticleEmitter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateParticleEmitter") == 0) { + stack->correctParams(3); + bool followParent = stack->pop()->getBool(); + int offsetX = stack->pop()->getInt(); + int offsetY = stack->pop()->getInt(); + + PartEmitter *emitter = createParticleEmitter(followParent, offsetX, offsetY); + if (emitter) { + stack->pushNative(_partEmitter, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteParticleEmitter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteParticleEmitter") == 0) { + stack->correctParams(0); + if (_partEmitter) { + _gameRef->unregisterObject(_partEmitter); + _partEmitter = NULL; + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddAttachment") == 0) { + stack->correctParams(4); + const char *filename = stack->pop()->getString(); + bool preDisplay = stack->pop()->getBool(true); + int offsetX = stack->pop()->getInt(); + int offsetY = stack->pop()->getInt(); + + bool res; + AdEntity *ent = new AdEntity(_gameRef); + if (DID_FAIL(res = ent->loadFile(filename))) { + delete ent; + ent = NULL; + script->runtimeError("AddAttachment() failed loading entity '%s'", filename); + stack->pushBool(false); + } else { + _gameRef->registerObject(ent); + + ent->_posX = offsetX; + ent->_posY = offsetY; + ent->_active = true; + + if (preDisplay) { + _attachmentsPre.add(ent); + } else { + _attachmentsPost.add(ent); + } + + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveAttachment") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + bool found = false; + if (val->isNative()) { + BaseScriptable *obj = val->getNative(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i] == obj) { + found = true; + _gameRef->unregisterObject(_attachmentsPre[i]); + _attachmentsPre.remove_at(i); + i--; + } + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i] == obj) { + found = true; + _gameRef->unregisterObject(_attachmentsPost[i]); + _attachmentsPost.remove_at(i); + i--; + } + } + } else { + const char *attachmentName = val->getString(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) { + found = true; + _gameRef->unregisterObject(_attachmentsPre[i]); + _attachmentsPre.remove_at(i); + i--; + } + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) { + found = true; + _gameRef->unregisterObject(_attachmentsPost[i]); + _attachmentsPost.remove_at(i); + i--; + } + } + } + stack->pushBool(found); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetAttachment + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetAttachment") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdObject *ret = NULL; + if (val->isInt()) { + int index = val->getInt(); + int currIndex = 0; + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (currIndex == index) { + ret = _attachmentsPre[i]; + } + currIndex++; + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (currIndex == index) { + ret = _attachmentsPost[i]; + } + currIndex++; + } + } else { + const char *attachmentName = val->getString(); + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + if (_attachmentsPre[i]->getName() && scumm_stricmp(_attachmentsPre[i]->getName(), attachmentName) == 0) { + ret = _attachmentsPre[i]; + break; + } + } + if (!ret) { + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + if (_attachmentsPost[i]->getName() && scumm_stricmp(_attachmentsPost[i]->getName(), attachmentName) == 0) { + ret = _attachmentsPre[i]; + break; + } + } + } + } + + if (ret != NULL) { + stack->pushNative(ret, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IgnoreItems + ////////////////////////////////////////////////////////////////////////// + else if (name == "IgnoreItems") { + _scValue->setBool(_ignoreItems); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SceneIndependent + ////////////////////////////////////////////////////////////////////////// + else if (name == "SceneIndependent") { + _scValue->setBool(_sceneIndependent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesWidth + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesWidth") { + _scValue->setInt(_subtitlesWidth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosRelative + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosRelative") { + _scValue->setBool(_subtitlesModRelative); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosX + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosX") { + _scValue->setInt(_subtitlesModX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosY + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosY") { + _scValue->setInt(_subtitlesModY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosXCenter + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesPosXCenter") { + _scValue->setBool(_subtitlesModXCenter); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumItems (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumItems") { + _scValue->setInt(getInventory()->_takenItems.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ParticleEmitter (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ParticleEmitter") { + if (_partEmitter) { + _scValue->setNative(_partEmitter, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumAttachments (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumAttachments") { + _scValue->setInt(_attachmentsPre.size() + _attachmentsPost.size()); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::scSetProperty(const char *name, ScValue *value) { + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IgnoreItems + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IgnoreItems") == 0) { + _ignoreItems = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SceneIndependent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SceneIndependent") == 0) { + _sceneIndependent = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesWidth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesWidth") == 0) { + _subtitlesWidth = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosRelative + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosRelative") == 0) { + _subtitlesModRelative = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosX") == 0) { + _subtitlesModX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosY") == 0) { + _subtitlesModY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesPosXCenter + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesPosXCenter") == 0) { + _subtitlesModXCenter = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdObject::scToString() { + return "[ad object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::setFont(const char *filename) { + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (filename) { + _font = _gameRef->_fontStorage->addFont(filename); + return _font == NULL ? STATUS_FAILED : STATUS_OK; + } else { + _font = NULL; + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +int AdObject::getHeight() { + if (!_currentSprite) { + return 0; + } else { + BaseFrame *frame = _currentSprite->_frames[_currentSprite->_currentFrame]; + int ret = 0; + for (uint32 i = 0; i < frame->_subframes.size(); i++) { + ret = MAX(ret, frame->_subframes[i]->_hotspotY); + } + + if (_zoomable) { + float zoom = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY); + ret = (int)(ret * zoom / 100); + } + return ret; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdObject::talk(const char *text, const char *sound, uint32 duration, const char *stances, TTextAlign Align) { + if (!_sentence) { + _sentence = new AdSentence(_gameRef); + } + if (!_sentence) { + return; + } + + if (_forcedTalkAnimName && _forcedTalkAnimUsed) { + delete[] _forcedTalkAnimName; + _forcedTalkAnimName = NULL; + _forcedTalkAnimUsed = false; + } + + delete(_sentence->_sound); + _sentence->_sound = NULL; + + _sentence->setText(text); + _gameRef->_stringTable->expand(&_sentence->_text); + _sentence->setStances(stances); + _sentence->_duration = duration; + _sentence->_align = Align; + _sentence->_startTime = _gameRef->_timer; + _sentence->_currentStance = -1; + _sentence->_font = _font == NULL ? _gameRef->_systemFont : _font; + _sentence->_freezable = _freezable; + + // try to locate speech file automatically + bool deleteSound = false; + if (!sound) { + char *key = _gameRef->_stringTable->getKey(text); + if (key) { + sound = ((AdGame *)_gameRef)->findSpeechFile(key); + delete[] key; + + if (sound) { + deleteSound = true; + } + } + } + + // load sound and set duration appropriately + if (sound) { + BaseSound *snd = new BaseSound(_gameRef); + if (snd && DID_SUCCEED(snd->setSound(sound, Audio::Mixer::kSpeechSoundType, true))) { + _sentence->setSound(snd); + if (_sentence->_duration <= 0) { + uint32 length = snd->getLength(); + if (length != 0) { + _sentence->_duration = length; + } + } + } else { + delete snd; + } + } + + // set duration by text length + if (_sentence->_duration <= 0) {// TODO: Avoid longs. + _sentence->_duration = MAX((size_t)1000, _gameRef->_subtitlesSpeed * strlen(_sentence->_text)); + } + + + int x, y, width, height; + + x = _posX; + y = _posY; + + if (!_sceneIndependent && _subtitlesModRelative) { + x -= ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + y -= ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + + if (_subtitlesWidth > 0) { + width = _subtitlesWidth; + } else { + if ((x < _gameRef->_renderer->_width / 4 || x > _gameRef->_renderer->_width * 0.75) && !_gameRef->_touchInterface) { + width = MAX(_gameRef->_renderer->_width / 4, MIN(x * 2, (_gameRef->_renderer->_width - x) * 2)); + } else { + width = _gameRef->_renderer->_width / 2; + } + } + + height = _sentence->_font->getTextHeight((byte *)_sentence->_text, width); + + y = y - height - getHeight() - 5; + if (_subtitlesModRelative) { + x += _subtitlesModX; + y += _subtitlesModY; + } else { + x = _subtitlesModX; + y = _subtitlesModY; + } + if (_subtitlesModXCenter) { + x = x - width / 2; + } + + + x = MIN(MAX(0, x), _gameRef->_renderer->_width - width); + y = MIN(MAX(0, y), _gameRef->_renderer->_height - height); + + _sentence->_width = width; + + + _sentence->_pos.x = x; + _sentence->_pos.y = y; + + + if (_subtitlesModRelative) { + _sentence->_pos.x += ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + _sentence->_pos.y += ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + _sentence->_fixedPos = !_subtitlesModRelative; + + + _sentence->setupTalkFile(sound); + + _state = STATE_TALKING; + + if (deleteSound) { + delete[] sound; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::reset() { + if (_state == STATE_PLAYING_ANIM && _animSprite != NULL) { + delete _animSprite; + _animSprite = NULL; + } else if (_state == STATE_TALKING && _sentence) { + _sentence->finish(); + } + + _state = _nextState = STATE_READY; + + _gameRef->_scEngine->resetObject(this); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_blockRegion)); + persistMgr->transfer(TMEMBER(_currentBlockRegion)); + persistMgr->transfer(TMEMBER(_currentWptGroup)); + persistMgr->transfer(TMEMBER(_currentSprite)); + persistMgr->transfer(TMEMBER(_drawn)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_ignoreItems)); + persistMgr->transfer(TMEMBER_INT(_nextState)); + persistMgr->transfer(TMEMBER(_sentence)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_animSprite)); + persistMgr->transfer(TMEMBER(_sceneIndependent)); + persistMgr->transfer(TMEMBER(_forcedTalkAnimName)); + persistMgr->transfer(TMEMBER(_forcedTalkAnimUsed)); + persistMgr->transfer(TMEMBER(_tempSprite2)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_wptGroup)); + persistMgr->transfer(TMEMBER(_stickRegion)); + persistMgr->transfer(TMEMBER(_subtitlesModRelative)); + persistMgr->transfer(TMEMBER(_subtitlesModX)); + persistMgr->transfer(TMEMBER(_subtitlesModY)); + persistMgr->transfer(TMEMBER(_subtitlesModXCenter)); + persistMgr->transfer(TMEMBER(_subtitlesWidth)); + persistMgr->transfer(TMEMBER(_inventory)); + persistMgr->transfer(TMEMBER(_partEmitter)); + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + persistMgr->transfer(TMEMBER(_currentRegions[i])); + } + + _attachmentsPre.persist(persistMgr); + _attachmentsPost.persist(persistMgr); + persistMgr->transfer(TMEMBER(_registerAlias)); + + persistMgr->transfer(TMEMBER(_partFollowParent)); + persistMgr->transfer(TMEMBER(_partOffsetX)); + persistMgr->transfer(TMEMBER(_partOffsetY)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateSounds() { + if (_sentence && _sentence->_sound) { + updateOneSound(_sentence->_sound); + } + + return BaseObject::updateSounds(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::resetSoundPan() { + if (_sentence && _sentence->_sound) { + _sentence->_sound->setPan(0.0f); + } + return BaseObject::resetSoundPan(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::getExtendedFlag(const char *flagName) { + if (!flagName) { + return false; + } else if (strcmp(flagName, "usable") == 0) { + return true; + } else { + return BaseObject::getExtendedFlag(flagName); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + if (_blockRegion) { + _blockRegion->saveAsText(buffer, indent + 2, "BLOCKED_REGION"); + } + if (_wptGroup) { + _wptGroup->saveAsText(buffer, indent + 2); + } + + BaseClass::saveAsText(buffer, indent + 2); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateBlockRegion() { + AdGame *adGame = (AdGame *)_gameRef; + if (adGame->_scene) { + if (_blockRegion && _currentBlockRegion) { + _currentBlockRegion->mimic(_blockRegion, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY); + } + + if (_wptGroup && _currentWptGroup) { + _currentWptGroup->mimic(_wptGroup, _zoomable ? adGame->_scene->getScaleAt(_posY) : 100.0f, _posX, _posY); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +AdInventory *AdObject::getInventory() { + if (!_inventory) { + _inventory = new AdInventory(_gameRef); + ((AdGame *)_gameRef)->registerInventory(_inventory); + } + return _inventory; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::afterMove() { + AdRegion *newRegions[MAX_NUM_REGIONS]; + + ((AdGame *)_gameRef)->_scene->getRegionsAt(_posX, _posY, newRegions, MAX_NUM_REGIONS); + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + if (!newRegions[i]) { + break; + } + bool regFound = false; + for (int j = 0; j < MAX_NUM_REGIONS; j++) { + if (_currentRegions[j] == newRegions[i]) { + _currentRegions[j] = NULL; + regFound = true; + break; + } + } + if (!regFound) { + newRegions[i]->applyEvent("ActorEntry"); + } + } + + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + if (_currentRegions[i] && _gameRef->validObject(_currentRegions[i])) { + _currentRegions[i]->applyEvent("ActorLeave"); + } + _currentRegions[i] = newRegions[i]; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::invalidateCurrRegions() { + for (int i = 0; i < MAX_NUM_REGIONS; i++) { + _currentRegions[i] = NULL; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdObject::getScale(float *scaleX, float *scaleY) { + if (_zoomable) { + if (_scaleX >= 0 || _scaleY >= 0) { + *scaleX = _scaleX < 0 ? 100 : _scaleX; + *scaleY = _scaleY < 0 ? 100 : _scaleY; + } else if (_scale >= 0) { + *scaleX = *scaleY = _scale; + } else { + *scaleX = *scaleY = ((AdGame *)_gameRef)->_scene->getZoomAt(_posX, _posY) + _relativeScale; + } + } else { + *scaleX = *scaleY = 100; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updateSpriteAttachments() { + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + _attachmentsPre[i]->update(); + } + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + _attachmentsPost[i]->update(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::displaySpriteAttachments(bool preDisplay) { + if (preDisplay) { + for (uint32 i = 0; i < _attachmentsPre.size(); i++) { + displaySpriteAttachment(_attachmentsPre[i]); + } + } else { + for (uint32 i = 0; i < _attachmentsPost.size(); i++) { + displaySpriteAttachment(_attachmentsPost[i]); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::displaySpriteAttachment(AdObject *attachment) { + if (!attachment->_active) { + return STATUS_OK; + } + + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + int origX = attachment->_posX; + int origY = attachment->_posY; + + // inherit position from owner + attachment->_posX = (int)(this->_posX + attachment->_posX * scaleX / 100.0f); + attachment->_posY = (int)(this->_posY + attachment->_posY * scaleY / 100.0f); + + // inherit other props + attachment->_alphaColor = this->_alphaColor; + attachment->_blendMode = this->_blendMode; + + attachment->_scale = this->_scale; + attachment->_relativeScale = this->_relativeScale; + attachment->_scaleX = this->_scaleX; + attachment->_scaleY = this->_scaleY; + + attachment->_rotate = this->_rotate; + attachment->_relativeRotate = this->_relativeRotate; + attachment->_rotateValid = this->_rotateValid; + + attachment->_registerAlias = this; + attachment->_registrable = this->_registrable; + + bool ret = attachment->display(); + + attachment->_posX = origX; + attachment->_posY = origY; + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +PartEmitter *AdObject::createParticleEmitter(bool followParent, int offsetX, int offsetY) { + _partFollowParent = followParent; + _partOffsetX = offsetX; + _partOffsetY = offsetY; + + if (!_partEmitter) { + _partEmitter = new PartEmitter(_gameRef, this); + if (_partEmitter) { + _gameRef->registerObject(_partEmitter); + } + } + updatePartEmitter(); + return _partEmitter; +} + +////////////////////////////////////////////////////////////////////////// +bool AdObject::updatePartEmitter() { + if (!_partEmitter) { + return STATUS_FAILED; + } + + if (_partFollowParent) { + float scaleX, scaleY; + getScale(&scaleX, &scaleY); + + _partEmitter->_posX = (int)(_posX + (scaleX / 100.0f) * _partOffsetX); + _partEmitter->_posY = (int)(_posY + (scaleY / 100.0f) * _partOffsetY); + } + return _partEmitter->update(); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_object.h b/engines/wintermute/ad/ad_object.h new file mode 100644 index 0000000000..d1a20908e1 --- /dev/null +++ b/engines/wintermute/ad/ad_object.h @@ -0,0 +1,124 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADOBJECT_H +#define WINTERMUTE_ADOBJECT_H + +#include "engines/wintermute/ad/ad_types.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdWaypointGroup; +class AdRegion; +class AdSentence; +class BaseFont; +class BaseRegion; +class AdInventory; +class PartEmitter; + +#define MAX_NUM_REGIONS 10 + +class AdObject : public BaseObject { +public: + PartEmitter *_partEmitter; + virtual PartEmitter *createParticleEmitter(bool followParent = false, int offsetX = 0, int offsetY = 0); + virtual bool updatePartEmitter(); + bool _partFollowParent; + int _partOffsetX; + int _partOffsetY; + + bool invalidateCurrRegions(); + bool _subtitlesModRelative; + bool _subtitlesModXCenter; + int _subtitlesModX; + int _subtitlesModY; + int _subtitlesWidth; + AdRegion *_stickRegion; + bool _sceneIndependent; + bool _ignoreItems; + bool updateBlockRegion(); + bool _forcedTalkAnimUsed; + char *_forcedTalkAnimName; + virtual bool getExtendedFlag(const char *flagName); + virtual bool resetSoundPan(); + virtual bool updateSounds(); + bool reset(); + DECLARE_PERSISTENT(AdObject, BaseObject) + virtual void talk(const char *text, const char *sound = NULL, uint32 duration = 0, const char *stances = NULL, TTextAlign align = TAL_CENTER); + virtual int getHeight(); + AdSentence *_sentence; + bool setFont(const char *filename); + virtual bool update(); + virtual bool display(); + bool _drawn; + bool _active; + virtual bool playAnim(const char *filename); + BaseSprite *_animSprite; + BaseSprite *_currentSprite; + TObjectState _state; + TObjectState _nextState; + TObjectType _type; + AdObject(BaseGame *inGame); + virtual ~AdObject(); + BaseFont *_font; + BaseSprite *_tempSprite2; + BaseRegion *_blockRegion; + AdWaypointGroup *_wptGroup; + BaseRegion *_currentBlockRegion; + AdWaypointGroup *_currentWptGroup; + AdInventory *getInventory(); + + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + virtual bool afterMove(); + AdRegion *_currentRegions[MAX_NUM_REGIONS]; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + BaseArray<AdObject *> _attachmentsPre; + BaseArray<AdObject *> _attachmentsPost; + + bool updateSpriteAttachments(); + bool displaySpriteAttachments(bool preDisplay); + AdObject *_registerAlias; +private: + bool displaySpriteAttachment(AdObject *attachment); + AdInventory *_inventory; + +protected: + bool getScale(float *scaleX, float *scaleY); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_path.cpp b/engines/wintermute/ad/ad_path.cpp new file mode 100644 index 0000000000..c931213456 --- /dev/null +++ b/engines/wintermute/ad/ad_path.cpp @@ -0,0 +1,120 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/base/base_point.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdPath, false) + +////////////////////////////////////////////////////////////////////////// +AdPath::AdPath(BaseGame *inGame) : BaseClass(inGame) { + _currIndex = -1; + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdPath::~AdPath() { + reset(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdPath::reset() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + + _points.clear(); + _currIndex = -1; + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getFirst() { + if (_points.size() > 0) { + _currIndex = 0; + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getNext() { + _currIndex++; + if (_currIndex < (int32)_points.size()) { + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint *AdPath::getCurrent() { + if (_currIndex >= 0 && _currIndex < (int32)_points.size()) { + return _points[_currIndex]; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdPath::addPoint(BasePoint *point) { + _points.add(point); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPath::setReady(bool ready) { + bool orig = _ready; + _ready = ready; + + return orig; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPath::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_currIndex)); + _points.persist(persistMgr); + persistMgr->transfer(TMEMBER(_ready)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_path.h b/engines/wintermute/ad/ad_path.h new file mode 100644 index 0000000000..6b043197aa --- /dev/null +++ b/engines/wintermute/ad/ad_path.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADPATH_H +#define WINTERMUTE_ADPATH_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BasePoint; +class AdPath : public BaseClass { +public: + DECLARE_PERSISTENT(AdPath, BaseClass) + BasePoint *getCurrent(); + bool setReady(bool ready = true); + void addPoint(BasePoint *point); + BasePoint *getNext(); + BasePoint *getFirst(); + void reset(); + AdPath(BaseGame *inGame); + virtual ~AdPath(); + BaseArray<BasePoint *> _points; + int _currIndex; + bool _ready; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_path_point.cpp b/engines/wintermute/ad/ad_path_point.cpp new file mode 100644 index 0000000000..a36648eb69 --- /dev/null +++ b/engines/wintermute/ad/ad_path_point.cpp @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdPathPoint, false) + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::AdPathPoint() { + x = y = 0; + _distance = 0; + + _marked = false; + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::AdPathPoint(int initX, int initY, int initDistance) { + x = initX; + y = initY; + _distance = initDistance; + + _marked = false; + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdPathPoint::~AdPathPoint() { + _origin = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdPathPoint::persist(BasePersistenceManager *persistMgr) { + + BasePoint::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_distance)); + persistMgr->transfer(TMEMBER(_marked)); + persistMgr->transfer(TMEMBER(_origin)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_path_point.h b/engines/wintermute/ad/ad_path_point.h new file mode 100644 index 0000000000..58457976c8 --- /dev/null +++ b/engines/wintermute/ad/ad_path_point.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADPATHPOINT_H +#define WINTERMUTE_ADPATHPOINT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base_point.h" + +namespace Wintermute { + +class AdPathPoint : public BasePoint { +public: + DECLARE_PERSISTENT(AdPathPoint, BasePoint) + AdPathPoint(int initX, int initY, int initDistance); + AdPathPoint(); + virtual ~AdPathPoint(); + AdPathPoint *_origin; + bool _marked; + int _distance; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_region.cpp b/engines/wintermute/ad/ad_region.cpp new file mode 100644 index 0000000000..c9f1553c9a --- /dev/null +++ b/engines/wintermute/ad/ad_region.cpp @@ -0,0 +1,397 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_region.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdRegion, false) + +////////////////////////////////////////////////////////////////////////// +AdRegion::AdRegion(BaseGame *inGame) : BaseRegion(inGame) { + _blocked = false; + _decoration = false; + _zoom = 0; + _alpha = 0xFFFFFFFF; +} + + +////////////////////////////////////////////////////////////////////////// +AdRegion::~AdRegion() { +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdRegion::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing REGION file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(REGION) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(ZOOM) +TOKEN_DEF(SCALE) +TOKEN_DEF(BLOCKED) +TOKEN_DEF(DECORATION) +TOKEN_DEF(POINT) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdRegion::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(REGION) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(ZOOM) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(BLOCKED) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(POINT) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_REGION) { + _gameRef->LOG(0, "'REGION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + int ar = 255, ag = 255, ab = 255, alpha = 255; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_BLOCKED: + parser.scanStr((char *)params, "%b", &_blocked); + break; + + case TOKEN_DECORATION: + parser.scanStr((char *)params, "%b", &_decoration); + break; + + case TOKEN_ZOOM: + case TOKEN_SCALE: { + int j; + parser.scanStr((char *)params, "%d", &j); + _zoom = (float)j; + } + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in REGION definition"); + return STATUS_FAILED; + } + + createRegion(); + + _alpha = BYTETORGBA(ar, ag, ab, alpha); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + /* + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SkipTo")==0) { + stack->correctParams(2); + _posX = stack->pop()->getInt(); + _posY = stack->pop()->getInt(); + stack->pushNULL(); + + return STATUS_OK; + } + + else*/ return BaseRegion::scCallMethod(script, stack, thisStack, name); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdRegion::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("ad region"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Blocked + ////////////////////////////////////////////////////////////////////////// + else if (name == "Blocked") { + _scValue->setBool(_blocked); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (name == "Decoration") { + _scValue->setBool(_decoration); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale") { + _scValue->setFloat(_zoom); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + _scValue->setInt((int)_alpha); + return _scValue; + } else { + return BaseRegion::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Blocked + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Blocked") == 0) { + _blocked = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Decoration") == 0) { + _decoration = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale") == 0) { + _zoom = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaColor") == 0) { + _alpha = (uint32)value->getInt(); + return STATUS_OK; + } else { + return BaseRegion::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdRegion::scToString() { + return "[ad region]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "REGION {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "BLOCKED=%s\n", _blocked ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_zoom); + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha)); + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha)); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRegion::persist(BasePersistenceManager *persistMgr) { + BaseRegion::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_alpha)); + persistMgr->transfer(TMEMBER(_blocked)); + persistMgr->transfer(TMEMBER(_decoration)); + persistMgr->transfer(TMEMBER(_zoom)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_region.h b/engines/wintermute/ad/ad_region.h new file mode 100644 index 0000000000..6112900361 --- /dev/null +++ b/engines/wintermute/ad/ad_region.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADREGION_H +#define WINTERMUTE_ADREGION_H + +#include "engines/wintermute/base/base_region.h" + +namespace Wintermute { + +class AdRegion : public BaseRegion { +public: + DECLARE_PERSISTENT(AdRegion, BaseRegion) + uint32 _alpha; + float _zoom; + bool _blocked; + bool _decoration; + AdRegion(BaseGame *inGame); + virtual ~AdRegion(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response.cpp b/engines/wintermute/ad/ad_response.cpp new file mode 100644 index 0000000000..a2225f2632 --- /dev/null +++ b/engines/wintermute/ad/ad_response.cpp @@ -0,0 +1,146 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponse, false) + +////////////////////////////////////////////////////////////////////////// +AdResponse::AdResponse(BaseGame *inGame) : BaseObject(inGame) { + _text = NULL; + _textOrig = NULL; + _icon = _iconHover = _iconPressed = NULL; + _font = NULL; + _iD = 0; + _responseType = RESPONSE_ALWAYS; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponse::~AdResponse() { + delete[] _text; + delete[] _textOrig; + delete _icon; + delete _iconHover; + delete _iconPressed; + _text = NULL; + _textOrig = NULL; + _icon = NULL; + _iconHover = NULL; + _iconPressed = NULL; + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponse::setText(const char *text) { + BaseUtils::setString(&_text, text); + BaseUtils::setString(&_textOrig, text); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIcon(const char *filename) { + delete _icon; + _icon = new BaseSprite(_gameRef); + if (!_icon || DID_FAIL(_icon->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIcon failed for file '%s'", filename); + delete _icon; + _icon = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setFont(const char *filename) { + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont(filename); + if (!_font) { + _gameRef->LOG(0, "AdResponse::setFont failed for file '%s'", filename); + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIconHover(const char *filename) { + delete _iconHover; + _iconHover = new BaseSprite(_gameRef); + if (!_iconHover || DID_FAIL(_iconHover->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIconHover failed for file '%s'", filename); + delete _iconHover; + _iconHover = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::setIconPressed(const char *filename) { + delete _iconPressed; + _iconPressed = new BaseSprite(_gameRef); + if (!_iconPressed || DID_FAIL(_iconPressed->loadFile(filename))) { + _gameRef->LOG(0, "AdResponse::setIconPressed failed for file '%s'", filename); + delete _iconPressed; + _iconPressed = NULL; + return STATUS_FAILED; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponse::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_icon)); + persistMgr->transfer(TMEMBER(_iconHover)); + persistMgr->transfer(TMEMBER(_iconPressed)); + persistMgr->transfer(TMEMBER(_iD)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER(_textOrig)); + persistMgr->transfer(TMEMBER_INT(_responseType)); + persistMgr->transfer(TMEMBER(_font)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response.h b/engines/wintermute/ad/ad_response.h new file mode 100644 index 0000000000..0ba88cf2e8 --- /dev/null +++ b/engines/wintermute/ad/ad_response.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSE_H +#define WINTERMUTE_ADRESPONSE_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/ad/ad_types.h" + +namespace Wintermute { +class BaseFont; +class AdResponse : public BaseObject { +public: + DECLARE_PERSISTENT(AdResponse, BaseObject) + bool setIcon(const char *filename); + bool setFont(const char *filename); + bool setIconHover(const char *filename); + bool setIconPressed(const char *filename); + void setText(const char *text); + int _iD; + BaseSprite *_icon; + BaseSprite *_iconHover; + BaseSprite *_iconPressed; + BaseFont *_font; + char *_text; + char *_textOrig; + AdResponse(BaseGame *inGame); + virtual ~AdResponse(); + TResponseType _responseType; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response_box.cpp b/engines/wintermute/ad/ad_response_box.cpp new file mode 100644 index 0000000000..fb31aa0bb8 --- /dev/null +++ b/engines/wintermute/ad/ad_response_box.cpp @@ -0,0 +1,713 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/wintermute.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponseBox, false) + +////////////////////////////////////////////////////////////////////////// +AdResponseBox::AdResponseBox(BaseGame *inGame) : BaseObject(inGame) { + _font = _fontHover = NULL; + + _window = NULL; + _shieldWindow = new UIWindow(_gameRef); + + _horizontal = false; + BasePlatform::setRectEmpty(&_responseArea); + _scrollOffset = 0; + _spacing = 0; + + _waitingScript = NULL; + _lastResponseText = NULL; + _lastResponseTextOrig = NULL; + + _verticalAlign = VAL_BOTTOM; + _align = TAL_LEFT; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponseBox::~AdResponseBox() { + + delete _window; + _window = NULL; + delete _shieldWindow; + _shieldWindow = NULL; + delete[] _lastResponseText; + _lastResponseText = NULL; + delete[] _lastResponseTextOrig; + _lastResponseTextOrig = NULL; + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + + clearResponses(); + clearButtons(); + + _waitingScript = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::clearResponses() { + for (uint32 i = 0; i < _responses.size(); i++) { + delete _responses[i]; + } + _responses.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::clearButtons() { + for (uint32 i = 0; i < _respButtons.size(); i++) { + delete _respButtons[i]; + } + _respButtons.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::invalidateButtons() { + for (uint32 i = 0; i < _respButtons.size(); i++) { + _respButtons[i]->_image = NULL; + _respButtons[i]->_cursor = NULL; + _respButtons[i]->_font = NULL; + _respButtons[i]->_fontHover = NULL; + _respButtons[i]->_fontPress = NULL; + _respButtons[i]->setText(""); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::createButtons() { + clearButtons(); + + _scrollOffset = 0; + for (uint32 i = 0; i < _responses.size(); i++) { + UIButton *btn = new UIButton(_gameRef); + if (btn) { + btn->_parent = _window; + btn->_sharedFonts = btn->_sharedImages = true; + btn->_sharedCursors = true; + // iconic + if (_responses[i]->_icon) { + btn->_image = _responses[i]->_icon; + if (_responses[i]->_iconHover) { + btn->_imageHover = _responses[i]->_iconHover; + } + if (_responses[i]->_iconPressed) { + btn->_imagePress = _responses[i]->_iconPressed; + } + + btn->setCaption(_responses[i]->_text); + if (_cursor) { + btn->_cursor = _cursor; + } else if (_gameRef->_activeCursor) { + btn->_cursor = _gameRef->_activeCursor; + } + } + // textual + else { + btn->setText(_responses[i]->_text); + btn->_font = (_font == NULL) ? _gameRef->_systemFont : _font; + btn->_fontHover = (_fontHover == NULL) ? _gameRef->_systemFont : _fontHover; + btn->_fontPress = btn->_fontHover; + btn->_align = _align; + + if (_gameRef->_touchInterface) { + btn->_fontHover = btn->_font; + } + + + if (_responses[i]->_font) { + btn->_font = _responses[i]->_font; + } + + btn->_width = _responseArea.right - _responseArea.left; + if (btn->_width <= 0) { + btn->_width = _gameRef->_renderer->_width; + } + } + btn->setName("response"); + btn->correctSize(); + + // make the responses touchable + if (_gameRef->_touchInterface) { + btn->_height = MAX(btn->_height, 50); + } + + //btn->SetListener(this, btn, _responses[i]->_iD); + btn->setListener(this, btn, i); + btn->_visible = false; + _respButtons.add(btn); + + if (_responseArea.bottom - _responseArea.top < btn->_height) { + _gameRef->LOG(0, "Warning: Response '%s' is too high to be displayed within response box. Correcting.", _responses[i]->_text); + _responseArea.bottom += (btn->_height - (_responseArea.bottom - _responseArea.top)); + } + } + } + _ready = false; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdResponseBox::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing RESPONSE_BOX file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(RESPONSE_BOX) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(FONT_HOVER) +TOKEN_DEF(FONT) +TOKEN_DEF(AREA) +TOKEN_DEF(HORIZONTAL) +TOKEN_DEF(SPACING) +TOKEN_DEF(WINDOW) +TOKEN_DEF(CURSOR) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(VERTICAL_ALIGN) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(RESPONSE_BOX) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(FONT_HOVER) + TOKEN_TABLE(FONT) + TOKEN_TABLE(AREA) + TOKEN_TABLE(HORIZONTAL) + TOKEN_TABLE(SPACING) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(VERTICAL_ALIGN) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_RESPONSE_BOX) { + _gameRef->LOG(0, "'RESPONSE_BOX' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_WINDOW: + delete _window; + _window = new UIWindow(_gameRef); + if (!_window || DID_FAIL(_window->loadBuffer(params, false))) { + delete _window; + _window = NULL; + cmd = PARSERR_GENERIC; + } else if (_shieldWindow) { + _shieldWindow->_parent = _window; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_HOVER: + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + _fontHover = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontHover) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_AREA: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_responseArea.left, &_responseArea.top, &_responseArea.right, &_responseArea.bottom); + break; + + case TOKEN_HORIZONTAL: + parser.scanStr((char *)params, "%b", &_horizontal); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "center") == 0) { + _align = TAL_CENTER; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _align = TAL_RIGHT; + } else { + _align = TAL_LEFT; + } + break; + + case TOKEN_VERTICAL_ALIGN: + if (scumm_stricmp((char *)params, "top") == 0) { + _verticalAlign = VAL_TOP; + } else if (scumm_stricmp((char *)params, "center") == 0) { + _verticalAlign = VAL_CENTER; + } else { + _verticalAlign = VAL_BOTTOM; + } + break; + + case TOKEN_SPACING: + parser.scanStr((char *)params, "%d", &_spacing); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in RESPONSE_BOX definition"); + return STATUS_FAILED; + } + + if (_window) { + for (uint32 i = 0; i < _window->_widgets.size(); i++) { + if (!_window->_widgets[i]->_listenerObject) { + _window->_widgets[i]->setListener(this, _window->_widgets[i], 0); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "RESPONSE_BOX\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "AREA { %d, %d, %d, %d }\n", _responseArea.left, _responseArea.top, _responseArea.right, _responseArea.bottom); + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontHover && _fontHover->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "HORIZONTAL=%s\n", _horizontal ? "TRUE" : "FALSE"); + + switch (_align) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + error("AdResponseBox::SaveAsText - Unhandled enum"); + break; + } + + switch (_verticalAlign) { + case VAL_TOP: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top"); + break; + case VAL_BOTTOM: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom"); + break; + case VAL_CENTER: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center"); + break; + } + + buffer->putTextIndent(indent + 2, "SPACING=%d\n", _spacing); + + buffer->putTextIndent(indent + 2, "\n"); + + // window + if (_window) { + _window->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::display() { + Rect32 rect = _responseArea; + if (_window) { + rect.offsetRect(_window->_posX, _window->_posY); + //_window->display(); + } + + int xxx, yyy; + uint32 i; + + xxx = rect.left; + yyy = rect.top; + + // shift down if needed + if (!_horizontal) { + int totalHeight = 0; + for (i = 0; i < _respButtons.size(); i++) { + totalHeight += (_respButtons[i]->_height + _spacing); + } + totalHeight -= _spacing; + + switch (_verticalAlign) { + case VAL_BOTTOM: + if (yyy + totalHeight < rect.bottom) { + yyy = rect.bottom - totalHeight; + } + break; + + case VAL_CENTER: + if (yyy + totalHeight < rect.bottom) { + yyy += ((rect.bottom - rect.top) - totalHeight) / 2; + } + break; + + case VAL_TOP: + // do nothing + break; + } + } + + // prepare response buttons + bool scrollNeeded = false; + for (i = _scrollOffset; i < _respButtons.size(); i++) { + if ((_horizontal && xxx + _respButtons[i]->_width > rect.right) + || (!_horizontal && yyy + _respButtons[i]->_height > rect.bottom)) { + + scrollNeeded = true; + _respButtons[i]->_visible = false; + break; + } + + _respButtons[i]->_visible = true; + _respButtons[i]->_posX = xxx; + _respButtons[i]->_posY = yyy; + + if (_horizontal) { + xxx += (_respButtons[i]->_width + _spacing); + } else { + yyy += (_respButtons[i]->_height + _spacing); + } + } + + // show appropriate scroll buttons + if (_window) { + _window->showWidget("prev", _scrollOffset > 0); + _window->showWidget("next", scrollNeeded); + } + + // go exclusive + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + + _shieldWindow->display(); + } + + // display window + if (_window) { + _window->display(); + } + + + // display response buttons + for (i = _scrollOffset; i < _respButtons.size(); i++) { + _respButtons[i]->display(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "prev") == 0) { + _scrollOffset--; + } else if (scumm_stricmp(obj->getName(), "next") == 0) { + _scrollOffset++; + } else if (scumm_stricmp(obj->getName(), "response") == 0) { + if (_waitingScript) { + _waitingScript->_stack->pushInt(_responses[param2]->_iD); + } + handleResponse(_responses[param2]); + _waitingScript = NULL; + _gameRef->_state = GAME_RUNNING; + ((AdGame *)_gameRef)->_stateEx = GAME_NORMAL; + _ready = true; + invalidateButtons(); + clearResponses(); + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + error("AdResponseBox::Listen - Unhandled enum"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_fontHover)); + persistMgr->transfer(TMEMBER(_horizontal)); + persistMgr->transfer(TMEMBER(_lastResponseText)); + persistMgr->transfer(TMEMBER(_lastResponseTextOrig)); + _respButtons.persist(persistMgr); + persistMgr->transfer(TMEMBER(_responseArea)); + _responses.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER(_spacing)); + persistMgr->transfer(TMEMBER(_waitingScript)); + persistMgr->transfer(TMEMBER(_window)); + + persistMgr->transfer(TMEMBER_INT(_verticalAlign)); + persistMgr->transfer(TMEMBER_INT(_align)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::weedResponses() { + AdGame *adGame = (AdGame *)_gameRef; + + for (uint32 i = 0; i < _responses.size(); i++) { + switch (_responses[i]->_responseType) { + case RESPONSE_ONCE: + if (adGame->branchResponseUsed(_responses[i]->_iD)) { + delete _responses[i]; + _responses.remove_at(i); + i--; + } + break; + + case RESPONSE_ONCE_GAME: + if (adGame->gameResponseUsed(_responses[i]->_iD)) { + delete _responses[i]; + _responses.remove_at(i); + i--; + } + break; + default: + debugC(kWintermuteDebugGeneral, "AdResponseBox::WeedResponses - Unhandled enum"); + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdResponseBox::setLastResponseText(const char *text, const char *textOrig) { + BaseUtils::setString(&_lastResponseText, text); + BaseUtils::setString(&_lastResponseTextOrig, textOrig); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::handleResponse(AdResponse *response) { + setLastResponseText(response->_text, response->_textOrig); + + AdGame *adGame = (AdGame *)_gameRef; + + switch (response->_responseType) { + case RESPONSE_ONCE: + adGame->addBranchResponse(response->_iD); + break; + + case RESPONSE_ONCE_GAME: + adGame->addGameResponse(response->_iD); + break; + default: + debugC(kWintermuteDebugGeneral, "AdResponseBox::HandleResponse - Unhandled enum"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdResponseBox::getNextAccessObject(BaseObject *currObject) { + BaseArray<UIObject *> objects; + getObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (uint32 i = 0; i < objects.size(); i++) { + if (objects[i] == currObject) { + if (i < objects.size() - 1) { + return objects[i + 1]; + } else { + break; + } + } + } + } + return objects[0]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdResponseBox::getPrevAccessObject(BaseObject *currObject) { + BaseArray<UIObject *> objects; + getObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (int i = objects.size() - 1; i >= 0; i--) { + if (objects[i] == currObject) { + if (i > 0) { + return objects[i - 1]; + } else { + break; + } + } + } + } + return objects[objects.size() - 1]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool AdResponseBox::getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _respButtons.size(); i++) { + objects.add(_respButtons[i]); + } + if (_window) { + _window->getWindowObjects(objects, interactiveOnly); + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response_box.h b/engines/wintermute/ad/ad_response_box.h new file mode 100644 index 0000000000..35f8cb6347 --- /dev/null +++ b/engines/wintermute/ad/ad_response_box.h @@ -0,0 +1,87 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSEBOX_H +#define WINTERMUTE_ADRESPONSEBOX_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class UIButton; +class UIWindow; +class UIObject; +class AdResponse; +class AdResponseBox : public BaseObject { +public: + BaseObject *getNextAccessObject(BaseObject *CurrObject); + BaseObject *getPrevAccessObject(BaseObject *CurrObject); + bool getObjects(BaseArray<UIObject *> &objects, bool interactiveOnly); + + bool handleResponse(AdResponse *response); + void setLastResponseText(const char *text, const char *textOrig); + char *_lastResponseText; + char *_lastResponseTextOrig; + DECLARE_PERSISTENT(AdResponseBox, BaseObject) + ScScript *_waitingScript; + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + typedef enum { + EVENT_PREV, + EVENT_NEXT, + EVENT_RESPONSE + } TResponseEvent; + + bool weedResponses(); + bool display(); + int _spacing; + int _scrollOffset; + BaseFont *_fontHover; + BaseFont *_font; + bool createButtons(); + bool invalidateButtons(); + void clearButtons(); + void clearResponses(); + AdResponseBox(BaseGame *inGame); + virtual ~AdResponseBox(); + BaseArray<AdResponse *> _responses; + BaseArray<UIButton *> _respButtons; + UIWindow *_window; + UIWindow *_shieldWindow; + bool _horizontal; + Rect32 _responseArea; + int _verticalAlign; + TTextAlign _align; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_response_context.cpp b/engines/wintermute/ad/ad_response_context.cpp new file mode 100644 index 0000000000..ebfa03feea --- /dev/null +++ b/engines/wintermute/ad/ad_response_context.cpp @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdResponseContext, false) + +////////////////////////////////////////////////////////////////////////// +AdResponseContext::AdResponseContext(BaseGame *inGame) : BaseClass(inGame) { + _id = 0; + _context = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdResponseContext::~AdResponseContext() { + delete[] _context; + _context = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdResponseContext::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_context)); + persistMgr->transfer(TMEMBER(_id)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void AdResponseContext::setContext(const char *context) { + delete[] _context; + _context = NULL; + if (context) { + _context = new char [strlen(context) + 1]; + if (_context) { + strcpy(_context, context); + } + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_response_context.h b/engines/wintermute/ad/ad_response_context.h new file mode 100644 index 0000000000..14bc1abd93 --- /dev/null +++ b/engines/wintermute/ad/ad_response_context.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADRESPONSECONTEXT_H +#define WINTERMUTE_ADRESPONSECONTEXT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class AdResponseContext : public BaseClass { +public: + void setContext(const char *context); + int _id; + char *_context; + DECLARE_PERSISTENT(AdResponseContext, BaseClass) + AdResponseContext(BaseGame *inGame); + virtual ~AdResponseContext(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_rot_level.cpp b/engines/wintermute/ad/ad_rot_level.cpp new file mode 100644 index 0000000000..fb9a4a47b9 --- /dev/null +++ b/engines/wintermute/ad/ad_rot_level.cpp @@ -0,0 +1,161 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdRotLevel, false) + + +////////////////////////////////////////////////////////////////////////// +AdRotLevel::AdRotLevel(BaseGame *inGame) : BaseObject(inGame) { + _posX = 0; + _rotation = 0.0f; +} + + +////////////////////////////////////////////////////////////////////////// +AdRotLevel::~AdRotLevel() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdRotLevel::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ROTATION_LEVEL file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ROTATION_LEVEL) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(X) +TOKEN_DEF(ROTATION) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ROTATION_LEVEL) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(X) + TOKEN_TABLE(ROTATION) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ROTATION_LEVEL) { + _gameRef->LOG(0, "'ROTATION_LEVEL' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_ROTATION: { + int i; + parser.scanStr((char *)params, "%d", &i); + _rotation = (float)i; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ROTATION_LEVEL definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ROTATION_LEVEL {\n"); + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "ROTATION=%d\n", (int)_rotation); + BaseClass::saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdRotLevel::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_rotation)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_rot_level.h b/engines/wintermute/ad/ad_rot_level.h new file mode 100644 index 0000000000..d7f5f8edf0 --- /dev/null +++ b/engines/wintermute/ad/ad_rot_level.h @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADROTLEVEL_H +#define WINTERMUTE_ADROTLEVEL_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdRotLevel : public BaseObject { +public: + DECLARE_PERSISTENT(AdRotLevel, BaseObject) + AdRotLevel(BaseGame *inGame); + virtual ~AdRotLevel(); + float _rotation; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scale_level.cpp b/engines/wintermute/ad/ad_scale_level.cpp new file mode 100644 index 0000000000..4e9293d875 --- /dev/null +++ b/engines/wintermute/ad/ad_scale_level.cpp @@ -0,0 +1,159 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdScaleLevel, false) + +////////////////////////////////////////////////////////////////////////// +AdScaleLevel::AdScaleLevel(BaseGame *inGame) : BaseObject(inGame) { + _posY = 0; + _scale = 100; +} + + +////////////////////////////////////////////////////////////////////////// +AdScaleLevel::~AdScaleLevel() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdScaleLevel::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SCALE_LEVEL file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SCALE_LEVEL) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(Y) +TOKEN_DEF(SCALE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SCALE_LEVEL) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(Y) + TOKEN_TABLE(SCALE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SCALE_LEVEL) { + _gameRef->LOG(0, "'SCALE_LEVEL' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_SCALE: { + int i; + parser.scanStr((char *)params, "%d", &i); + _scale = (float)i; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SCALE_LEVEL definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SCALE_LEVEL {\n"); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "SCALE=%d\n", (int)_scale); + BaseClass::saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScaleLevel::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_scale)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scale_level.h b/engines/wintermute/ad/ad_scale_level.h new file mode 100644 index 0000000000..628a385eb4 --- /dev/null +++ b/engines/wintermute/ad/ad_scale_level.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCALELEVEL_H +#define WINTERMUTE_ADSCALELEVEL_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class AdScaleLevel : public BaseObject { +public: + DECLARE_PERSISTENT(AdScaleLevel, BaseObject) + float _scale; + AdScaleLevel(BaseGame *inGame); + virtual ~AdScaleLevel(); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scene.cpp b/engines/wintermute/ad/ad_scene.cpp new file mode 100644 index 0000000000..8e9beca0c0 --- /dev/null +++ b/engines/wintermute/ad/ad_scene.cpp @@ -0,0 +1,2987 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/wintermute.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdScene, false) + +////////////////////////////////////////////////////////////////////////// +AdScene::AdScene(BaseGame *inGame) : BaseObject(inGame) { + _pfTarget = new BasePoint; + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +AdScene::~AdScene() { + cleanup(); + _gameRef->unregisterObject(_fader); + delete _pfTarget; + _pfTarget = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::setDefaults() { + _initialized = false; + _pfReady = true; + _pfTargetPath = NULL; + _pfRequester = NULL; + _mainLayer = NULL; + + _pfPointsNum = 0; + _persistentState = false; + _persistentStateSprites = true; + + _autoScroll = true; + _offsetLeft = _offsetTop = 0; + _targetOffsetLeft = _targetOffsetTop = 0; + + _lastTimeH = _lastTimeV = 0; + _scrollTimeH = _scrollTimeV = 10; + _scrollPixelsH = _scrollPixelsV = 1; + + _pfMaxTime = 15; + + _paralaxScrolling = true; + + // editor settings + _editorMarginH = _editorMarginV = 100; + + _editorColFrame = 0xE0888888; + _editorColEntity = 0xFF008000; + _editorColRegion = 0xFF0000FF; + _editorColBlocked = 0xFF800080; + _editorColWaypoints = 0xFF0000FF; + _editorColEntitySel = 0xFFFF0000; + _editorColRegionSel = 0xFFFF0000; + _editorColBlockedSel = 0xFFFF0000; + _editorColWaypointsSel = 0xFFFF0000; + _editorColScale = 0xFF00FF00; + _editorColDecor = 0xFF00FFFF; + _editorColDecorSel = 0xFFFF0000; + + _editorShowRegions = true; + _editorShowBlocked = true; + _editorShowDecor = true; + _editorShowEntities = true; + _editorShowScale = true; + + _shieldWindow = NULL; + + _fader = new BaseFader(_gameRef); + _gameRef->registerObject(_fader); + + _viewport = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::cleanup() { + BaseObject::cleanup(); + + _mainLayer = NULL; // reference only + + delete _shieldWindow; + _shieldWindow = NULL; + + _gameRef->unregisterObject(_fader); + _fader = NULL; + + for (uint32 i = 0; i < _layers.size(); i++) { + _gameRef->unregisterObject(_layers[i]); + } + _layers.clear(); + + + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + _gameRef->unregisterObject(_waypointGroups[i]); + } + _waypointGroups.clear(); + + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + _gameRef->unregisterObject(_scaleLevels[i]); + } + _scaleLevels.clear(); + + for (uint32 i = 0; i < _rotLevels.size(); i++) { + _gameRef->unregisterObject(_rotLevels[i]); + } + _rotLevels.clear(); + + + for (uint32 i = 0; i < _pfPath.size(); i++) { + delete _pfPath[i]; + } + _pfPath.clear(); + _pfPointsNum = 0; + + for (uint32 i = 0; i < _objects.size(); i++) { + _gameRef->unregisterObject(_objects[i]); + } + _objects.clear(); + + delete _viewport; + _viewport = NULL; + + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester) { + if (!_pfReady) { + return false; + } else { + _pfReady = false; + *_pfTarget = target; + _pfTargetPath = path; + _pfRequester = requester; + + _pfTargetPath->reset(); + _pfTargetPath->setReady(false); + + // prepare working path + pfPointsStart(); + + // first point + //_pfPath.add(new AdPathPoint(source.x, source.y, 0)); + + // if we're one pixel stuck, get unstuck + int startX = source.x; + int startY = source.y; + int bestDistance = 1000; + if (isBlockedAt(startX, startY, true, requester)) { + int tolerance = 2; + for (int xxx = startX - tolerance; xxx <= startX + tolerance; xxx++) { + for (int yyy = startY - tolerance; yyy <= startY + tolerance; yyy++) { + if (isWalkableAt(xxx, yyy, true, requester)) { + int distance = abs(xxx - source.x) + abs(yyy - source.y); + if (distance < bestDistance) { + startX = xxx; + startY = yyy; + + bestDistance = distance; + } + } + } + } + } + + pfPointsAdd(startX, startY, 0); + + //CorrectTargetPoint(&target.x, &target.y); + + // last point + //_pfPath.add(new AdPathPoint(target.x, target.y, INT_MAX)); + pfPointsAdd(target.x, target.y, INT_MAX); + + // active waypoints + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + if (_waypointGroups[i]->_active) { + pfAddWaypointGroup(_waypointGroups[i], requester); + } + } + + + // free waypoints + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentWptGroup) { + pfAddWaypointGroup(_objects[i]->_currentWptGroup, requester); + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentWptGroup) { + pfAddWaypointGroup(adGame->_objects[i]->_currentWptGroup, requester); + } + } + + return true; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfAddWaypointGroup(AdWaypointGroup *wpt, BaseObject *requester) { + if (!wpt->_active) { + return; + } + + for (uint32 i = 0; i < wpt->_points.size(); i++) { + if (isBlockedAt(wpt->_points[i]->x, wpt->_points[i]->y, true, requester)) { + continue; + } + + //_pfPath.add(new AdPathPoint(Wpt->_points[i]->x, Wpt->_points[i]->y, INT_MAX)); + pfPointsAdd(wpt->_points[i]->x, wpt->_points[i]->y, INT_MAX); + } +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getZoomAt(int x, int y) { + float ret = 100; + + bool found = false; + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_blocked && node->_region->pointInRegion(x, y)) { + if (node->_region->_zoom != 0) { + ret = node->_region->_zoom; + found = true; + break; + } + } + } + } + if (!found) { + ret = getScaleAt(y); + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 AdScene::getAlphaAt(int x, int y, bool colorCheck) { + if (!_gameRef->_debugDebugMode) { + colorCheck = false; + } + + uint32 ret; + if (colorCheck) { + ret = 0xFFFF0000; + } else { + ret = 0xFFFFFFFF; + } + + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && (colorCheck || !node->_region->_blocked) && node->_region->pointInRegion(x, y)) { + if (!node->_region->_blocked) { + ret = node->_region->_alpha; + } + break; + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::isBlockedAt(int x, int y, bool checkFreeObjects, BaseObject *requester) { + bool ret = true; + + if (checkFreeObjects) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) { + if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return true; + } + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) { + if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return true; + } + } + } + } + + + if (_mainLayer) { + for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) { + AdSceneNode *node = _mainLayer->_nodes[i]; + /* + if (Node->_type == OBJECT_REGION && Node->_region->_active && Node->_region->_blocked && Node->_region->PointInRegion(X, Y)) + { + ret = true; + break; + } + */ + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_decoration && node->_region->pointInRegion(x, y)) { + if (node->_region->_blocked) { + ret = true; + break; + } else { + ret = false; + } + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::isWalkableAt(int x, int y, bool checkFreeObjects, BaseObject *requester) { + bool ret = false; + + if (checkFreeObjects) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i] != requester && _objects[i]->_currentBlockRegion) { + if (_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return false; + } + } + } + AdGame *adGame = (AdGame *)_gameRef; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && adGame->_objects[i] != requester && adGame->_objects[i]->_currentBlockRegion) { + if (adGame->_objects[i]->_currentBlockRegion->pointInRegion(x, y)) { + return false; + } + } + } + } + + + if (_mainLayer) { + for (uint32 i = 0; i < _mainLayer->_nodes.size(); i++) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && !node->_region->_decoration && node->_region->pointInRegion(x, y)) { + if (node->_region->_blocked) { + ret = false; + break; + } else { + ret = true; + } + } + } + } + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester) { + double xStep, yStep, x, y; + int xLength, yLength, xCount, yCount; + int x1, y1, x2, y2; + + x1 = p1.x; + y1 = p1.y; + x2 = p2.x; + y2 = p2.y; + + xLength = abs(x2 - x1); + yLength = abs(y2 - y1); + + if (xLength > yLength) { + if (x1 > x2) { + BaseUtils::swap(&x1, &x2); + BaseUtils::swap(&y1, &y2); + } + + yStep = (double)(y2 - y1) / (double)(x2 - x1); + y = y1; + + for (xCount = x1; xCount < x2; xCount++) { + if (isBlockedAt(xCount, (int)y, true, requester)) { + return -1; + } + y += yStep; + } + } else { + if (y1 > y2) { + BaseUtils::swap(&x1, &x2); + BaseUtils::swap(&y1, &y2); + } + + xStep = (double)(x2 - x1) / (double)(y2 - y1); + x = x1; + + for (yCount = y1; yCount < y2; yCount++) { + if (isBlockedAt((int)x, yCount, true, requester)) { + return -1; + } + x += xStep; + } + } + return MAX(xLength, yLength); +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pathFinderStep() { + int i; + // get lowest unmarked + int lowestDist = INT_MAX; + AdPathPoint *lowestPt = NULL; + + for (i = 0; i < _pfPointsNum; i++) + if (!_pfPath[i]->_marked && _pfPath[i]->_distance < lowestDist) { + lowestDist = _pfPath[i]->_distance; + lowestPt = _pfPath[i]; + } + + if (lowestPt == NULL) { // no path -> terminate PathFinder + _pfReady = true; + _pfTargetPath->setReady(true); + return; + } + + lowestPt->_marked = true; + + // target point marked, generate path and terminate + if (lowestPt->x == _pfTarget->x && lowestPt->y == _pfTarget->y) { + while (lowestPt != NULL) { + _pfTargetPath->_points.insert_at(0, new BasePoint(lowestPt->x, lowestPt->y)); + lowestPt = lowestPt->_origin; + } + + _pfReady = true; + _pfTargetPath->setReady(true); + return; + } + + // otherwise keep on searching + for (i = 0; i < _pfPointsNum; i++) + if (!_pfPath[i]->_marked) { + int j = getPointsDist(*lowestPt, *_pfPath[i], _pfRequester); + if (j != -1 && lowestPt->_distance + j < _pfPath[i]->_distance) { + _pfPath[i]->_distance = lowestPt->_distance + j; + _pfPath[i]->_origin = lowestPt; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::initLoop() { +#ifdef _DEBUGxxxx + int nu_steps = 0; + uint32 start = _gameRef->_currentTime; + while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) { + PathFinderStep(); + nu_steps++; + } + if (nu_steps > 0) { + _gameRef->LOG(0, "STAT: PathFinder iterations in one loop: %d (%s) _pfMaxTime=%d", nu_steps, _pfReady ? "finished" : "not yet done", _pfMaxTime); + } +#else + uint32 start = _gameRef->_currentTime; + while (!_pfReady && g_system->getMillis() - start <= _pfMaxTime) { + pathFinderStep(); + } +#endif + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdScene::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SCENE file '%s'", filename); + } + + setFilename(filename); + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SCENE) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(LAYER) +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(EVENTS) +TOKEN_DEF(CURSOR) +TOKEN_DEF(CAMERA) +TOKEN_DEF(ENTITY) +TOKEN_DEF(SCALE_LEVEL) +TOKEN_DEF(ROTATION_LEVEL) +TOKEN_DEF(EDITOR_MARGIN_H) +TOKEN_DEF(EDITOR_MARGIN_V) +TOKEN_DEF(EDITOR_COLOR_FRAME) +TOKEN_DEF(EDITOR_COLOR_ENTITY_SEL) +TOKEN_DEF(EDITOR_COLOR_REGION_SEL) +TOKEN_DEF(EDITOR_COLOR_DECORATION_SEL) +TOKEN_DEF(EDITOR_COLOR_BLOCKED_SEL) +TOKEN_DEF(EDITOR_COLOR_WAYPOINTS_SEL) +TOKEN_DEF(EDITOR_COLOR_REGION) +TOKEN_DEF(EDITOR_COLOR_DECORATION) +TOKEN_DEF(EDITOR_COLOR_BLOCKED) +TOKEN_DEF(EDITOR_COLOR_ENTITY) +TOKEN_DEF(EDITOR_COLOR_WAYPOINTS) +TOKEN_DEF(EDITOR_COLOR_SCALE) +TOKEN_DEF(EDITOR_SHOW_REGIONS) +TOKEN_DEF(EDITOR_SHOW_BLOCKED) +TOKEN_DEF(EDITOR_SHOW_DECORATION) +TOKEN_DEF(EDITOR_SHOW_ENTITIES) +TOKEN_DEF(EDITOR_SHOW_SCALE) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(VIEWPORT) +TOKEN_DEF(PERSISTENT_STATE_SPRITES) +TOKEN_DEF(PERSISTENT_STATE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SCENE) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(LAYER) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(CAMERA) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SCALE_LEVEL) + TOKEN_TABLE(ROTATION_LEVEL) + TOKEN_TABLE(EDITOR_MARGIN_H) + TOKEN_TABLE(EDITOR_MARGIN_V) + TOKEN_TABLE(EDITOR_COLOR_FRAME) + TOKEN_TABLE(EDITOR_COLOR_ENTITY_SEL) + TOKEN_TABLE(EDITOR_COLOR_REGION_SEL) + TOKEN_TABLE(EDITOR_COLOR_DECORATION_SEL) + TOKEN_TABLE(EDITOR_COLOR_BLOCKED_SEL) + TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS_SEL) + TOKEN_TABLE(EDITOR_COLOR_REGION) + TOKEN_TABLE(EDITOR_COLOR_DECORATION) + TOKEN_TABLE(EDITOR_COLOR_BLOCKED) + TOKEN_TABLE(EDITOR_COLOR_ENTITY) + TOKEN_TABLE(EDITOR_COLOR_WAYPOINTS) + TOKEN_TABLE(EDITOR_COLOR_SCALE) + TOKEN_TABLE(EDITOR_SHOW_REGIONS) + TOKEN_TABLE(EDITOR_SHOW_DECORATION) + TOKEN_TABLE(EDITOR_SHOW_BLOCKED) + TOKEN_TABLE(EDITOR_SHOW_ENTITIES) + TOKEN_TABLE(EDITOR_SHOW_SCALE) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(VIEWPORT) + TOKEN_TABLE(PERSISTENT_STATE_SPRITES) + TOKEN_TABLE(PERSISTENT_STATE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + cleanup(); + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SCENE) { + _gameRef->LOG(0, "'SCENE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int ar, ag, ab, aa; + char camera[MAX_PATH_LENGTH] = ""; + /* float waypointHeight = -1.0f; */ + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_LAYER: { + AdLayer *layer = new AdLayer(_gameRef); + if (!layer || DID_FAIL(layer->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete layer; + layer = NULL; + } else { + _gameRef->registerObject(layer); + _layers.add(layer); + if (layer->_main) { + _mainLayer = layer; + _width = layer->_width; + _height = layer->_height; + } + } + } + break; + + case TOKEN_WAYPOINTS: { + AdWaypointGroup *wpt = new AdWaypointGroup(_gameRef); + if (!wpt || DID_FAIL(wpt->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete wpt; + wpt = NULL; + } else { + _gameRef->registerObject(wpt); + _waypointGroups.add(wpt); + } + } + break; + + case TOKEN_SCALE_LEVEL: { + AdScaleLevel *sl = new AdScaleLevel(_gameRef); + if (!sl || DID_FAIL(sl->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete sl; + sl = NULL; + } else { + _gameRef->registerObject(sl); + _scaleLevels.add(sl); + } + } + break; + + case TOKEN_ROTATION_LEVEL: { + AdRotLevel *rl = new AdRotLevel(_gameRef); + if (!rl || DID_FAIL(rl->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete rl; + rl = NULL; + } else { + _gameRef->registerObject(rl); + _rotLevels.add(rl); + } + } + break; + + case TOKEN_ENTITY: { + AdEntity *entity = new AdEntity(_gameRef); + if (!entity || DID_FAIL(entity->loadBuffer(params, false))) { + cmd = PARSERR_GENERIC; + delete entity; + entity = NULL; + } else { + addObject(entity); + } + } + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CAMERA: + strcpy(camera, (char *)params); + break; + + case TOKEN_EDITOR_MARGIN_H: + parser.scanStr((char *)params, "%d", &_editorMarginH); + break; + + case TOKEN_EDITOR_MARGIN_V: + parser.scanStr((char *)params, "%d", &_editorMarginV); + break; + + case TOKEN_EDITOR_COLOR_FRAME: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColFrame = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_ENTITY: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColEntity = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_ENTITY_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColEntitySel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_REGION_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColRegionSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_DECORATION_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColDecorSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_BLOCKED_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColBlockedSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_WAYPOINTS_SEL: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColWaypointsSel = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_REGION: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColRegion = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_DECORATION: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColDecor = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_BLOCKED: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColBlocked = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_WAYPOINTS: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColWaypoints = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_COLOR_SCALE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &ar, &ag, &ab, &aa); + _editorColScale = BYTETORGBA(ar, ag, ab, aa); + break; + + case TOKEN_EDITOR_SHOW_REGIONS: + parser.scanStr((char *)params, "%b", &_editorShowRegions); + break; + + case TOKEN_EDITOR_SHOW_BLOCKED: + parser.scanStr((char *)params, "%b", &_editorShowBlocked); + break; + + case TOKEN_EDITOR_SHOW_DECORATION: + parser.scanStr((char *)params, "%b", &_editorShowDecor); + break; + + case TOKEN_EDITOR_SHOW_ENTITIES: + parser.scanStr((char *)params, "%b", &_editorShowEntities); + break; + + case TOKEN_EDITOR_SHOW_SCALE: + parser.scanStr((char *)params, "%b", &_editorShowScale); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_VIEWPORT: { + Rect32 rc; + parser.scanStr((char *)params, "%d,%d,%d,%d", &rc.left, &rc.top, &rc.right, &rc.bottom); + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(rc.left, rc.top, rc.right, rc.bottom, true); + } + } + + case TOKEN_PERSISTENT_STATE: + parser.scanStr((char *)params, "%b", &_persistentState); + break; + + case TOKEN_PERSISTENT_STATE_SPRITES: + parser.scanStr((char *)params, "%b", &_persistentStateSprites); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SCENE definition"); + return STATUS_FAILED; + } + + if (_mainLayer == NULL) { + _gameRef->LOG(0, "Warning: scene '%s' has no main layer.", getFilename()); + } + + + sortScaleLevels(); + sortRotLevels(); + + _initialized = true; + + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::traverseNodes(bool doUpdate) { + if (!_initialized) { + return STATUS_OK; + } + + AdGame *adGame = (AdGame *)_gameRef; + + + ////////////////////////////////////////////////////////////////////////// + // prepare viewport + bool popViewport = false; + if (_viewport && !_gameRef->_editorMode) { + _gameRef->pushViewport(_viewport); + popViewport = true; + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + _gameRef->pushViewport(adGame->_sceneViewport); + popViewport = true; + } + + + ////////////////////////////////////////////////////////////////////////// + // *** adjust scroll offset + if (doUpdate) { + /* + if (_autoScroll && _gameRef->_mainObject != NULL) + { + ScrollToObject(_gameRef->_mainObject); + } + */ + + if (_autoScroll) { + // adjust horizontal scroll + if (_gameRef->_timer - _lastTimeH >= _scrollTimeH) { + _lastTimeH = _gameRef->_timer; + if (_offsetLeft < _targetOffsetLeft) { + _offsetLeft += _scrollPixelsH; + _offsetLeft = MIN(_offsetLeft, _targetOffsetLeft); + } else if (_offsetLeft > _targetOffsetLeft) { + _offsetLeft -= _scrollPixelsH; + _offsetLeft = MAX(_offsetLeft, _targetOffsetLeft); + } + } + + // adjust vertical scroll + if (_gameRef->_timer - _lastTimeV >= _scrollTimeV) { + _lastTimeV = _gameRef->_timer; + if (_offsetTop < _targetOffsetTop) { + _offsetTop += _scrollPixelsV; + _offsetTop = MIN(_offsetTop, _targetOffsetTop); + } else if (_offsetTop > _targetOffsetTop) { + _offsetTop -= _scrollPixelsV; + _offsetTop = MAX(_offsetTop, _targetOffsetTop); + } + } + + if (_offsetTop == _targetOffsetTop && _offsetLeft == _targetOffsetLeft) { + _ready = true; + } + } else { + _ready = true; // not scrolling, i.e. always ready + } + } + + + + + ////////////////////////////////////////////////////////////////////////// + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + int viewportX, viewportY; + getViewportOffset(&viewportX, &viewportY); + + int scrollableX = _width - viewportWidth; + int scrollableY = _height - viewportHeight; + + double widthRatio = scrollableX <= 0 ? 0 : ((double)(_offsetLeft) / (double)scrollableX); + double heightRatio = scrollableY <= 0 ? 0 : ((double)(_offsetTop) / (double)scrollableY); + + int origX, origY; + _gameRef->getOffset(&origX, &origY); + + + + ////////////////////////////////////////////////////////////////////////// + // *** display/update everything + _gameRef->_renderer->setup2D(); + + // for each layer + /* int mainOffsetX = 0; */ + /* int mainOffsetY = 0; */ + + for (uint32 j = 0; j < _layers.size(); j++) { + if (!_layers[j]->_active) { + continue; + } + + // make layer exclusive + if (!doUpdate) { + if (_layers[j]->_closeUp && !_gameRef->_editorMode) { + if (!_shieldWindow) { + _shieldWindow = new UIWindow(_gameRef); + } + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + _shieldWindow->display(); + } + } + } + + if (_paralaxScrolling) { + int offsetX = (int)(widthRatio * (_layers[j]->_width - viewportWidth) - viewportX); + int offsetY = (int)(heightRatio * (_layers[j]->_height - viewportHeight) - viewportY); + _gameRef->setOffset(offsetX, offsetY); + + _gameRef->_offsetPercentX = (float)offsetX / ((float)_layers[j]->_width - viewportWidth) * 100.0f; + _gameRef->_offsetPercentY = (float)offsetY / ((float)_layers[j]->_height - viewportHeight) * 100.0f; + + //_gameRef->QuickMessageForm("%d %f", OffsetX+ViewportX, _gameRef->_offsetPercentX); + } else { + _gameRef->setOffset(_offsetLeft - viewportX, _offsetTop - viewportY); + + _gameRef->_offsetPercentX = (float)(_offsetLeft - viewportX) / ((float)_layers[j]->_width - viewportWidth) * 100.0f; + _gameRef->_offsetPercentY = (float)(_offsetTop - viewportY) / ((float)_layers[j]->_height - viewportHeight) * 100.0f; + } + + + // for each node + for (uint32 k = 0; k < _layers[j]->_nodes.size(); k++) { + AdSceneNode *node = _layers[j]->_nodes[k]; + switch (node->_type) { + case OBJECT_ENTITY: + if (node->_entity->_active && (_gameRef->_editorMode || !node->_entity->_editorOnly)) { + _gameRef->_renderer->setup2D(); + + if (doUpdate) { + node->_entity->update(); + } else { + node->_entity->display(); + } + } + break; + + case OBJECT_REGION: { + if (node->_region->_blocked) { + break; + } + if (node->_region->_decoration) { + break; + } + + if (!doUpdate) { + displayRegionContent(node->_region); + } + } + break; + default: + error("AdScene::TraverseNodes - Unhandled enum"); + break; + } // switch + } // each node + + // display/update all objects which are off-regions + if (_layers[j]->_main) { + if (doUpdate) { + updateFreeObjects(); + } else { + displayRegionContent(NULL); + } + } + } // each layer + + + // restore state + _gameRef->setOffset(origX, origY); + _gameRef->_renderer->setup2D(); + + // display/update fader + if (_fader) { + if (doUpdate) { + _fader->update(); + } else { + _fader->display(); + } + } + + if (popViewport) { + _gameRef->popViewport(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::display() { + return traverseNodes(false); +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::updateFreeObjects() { + AdGame *adGame = (AdGame *)_gameRef; + // 3D-code removed + // bool is3DSet; + + // *** update all active objects + // is3DSet = false; + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (!adGame->_objects[i]->_active) { + continue; + } + // 3D-code removed + adGame->_objects[i]->update(); + adGame->_objects[i]->_drawn = false; + } + + + for (uint32 i = 0; i < _objects.size(); i++) { + if (!_objects[i]->_active) { + continue; + } + + _objects[i]->update(); + _objects[i]->_drawn = false; + } + + + if (_autoScroll && _gameRef->_mainObject != NULL) { + scrollToObject(_gameRef->_mainObject); + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::displayRegionContent(AdRegion *region, bool display3DOnly) { + AdGame *adGame = (AdGame *)_gameRef; + BaseArray<AdObject *> objects; + AdObject *obj; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + obj = adGame->_objects[i]; + if (obj->_active && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + objects.add(obj); + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + obj = _objects[i]; + if (obj->_active && !obj->_editorOnly && !obj->_drawn && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + objects.add(obj); + } + } + + // sort by _posY + Common::sort(objects.begin(), objects.end(), AdScene::compareObjs); + + // display them + for (uint32 i = 0; i < objects.size(); i++) { + obj = objects[i]; + + if (display3DOnly && !obj->_is3D) { + continue; + } + + _gameRef->_renderer->setup2D(); + + if (_gameRef->_editorMode || !obj->_editorOnly) { + obj->display(); + } + obj->_drawn = true; + } + + + // display design only objects + if (!display3DOnly) { + if (_gameRef->_editorMode && region == NULL) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i]->_editorOnly) { + _objects[i]->display(); + _objects[i]->_drawn = true; + } + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int AdScene::compareObjs(const void *obj1, const void *obj2) { + const AdObject *object1 = *(const AdObject *const *)obj1; + const AdObject *object2 = *(const AdObject *const *)obj2; + + if (object1->_posY < object2->_posY) { + return -1; + } else if (object1->_posY > object2->_posY) { + return 1; + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::displayRegionContentOld(AdRegion *region) { + AdGame *adGame = (AdGame *)_gameRef; + AdObject *obj; + + // display all objects in region sorted by _posY + do { + obj = NULL; + int minY = INT_MAX; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + if (adGame->_objects[i]->_active && !adGame->_objects[i]->_drawn && adGame->_objects[i]->_posY < minY && (adGame->_objects[i]->_stickRegion == region || region == NULL || (adGame->_objects[i]->_stickRegion == NULL && region->pointInRegion(adGame->_objects[i]->_posX, adGame->_objects[i]->_posY)))) { + obj = adGame->_objects[i]; + minY = adGame->_objects[i]->_posY; + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && !_objects[i]->_editorOnly && !_objects[i]->_drawn && _objects[i]->_posY < minY && (_objects[i]->_stickRegion == region || region == NULL || (_objects[i]->_stickRegion == NULL && region->pointInRegion(_objects[i]->_posX, _objects[i]->_posY)))) { + obj = _objects[i]; + minY = _objects[i]->_posY; + } + } + + + if (obj != NULL) { + _gameRef->_renderer->setup2D(); + + if (_gameRef->_editorMode || !obj->_editorOnly) { + obj->display(); + } + obj->_drawn = true; + } + } while (obj != NULL); + + + // design only objects + if (_gameRef->_editorMode && region == NULL) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_active && _objects[i]->_editorOnly) { + _objects[i]->display(); + _objects[i]->_drawn = true; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::update() { + return traverseNodes(true); +} + +////////////////////////////////////////////////////////////////////////// +void AdScene::scrollTo(int offsetX, int offsetY) { + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + int origOffsetLeft = _targetOffsetLeft; + int origOffsetTop = _targetOffsetTop; + + _targetOffsetLeft = MAX(0, offsetX - viewportWidth / 2); + _targetOffsetLeft = MIN(_targetOffsetLeft, _width - viewportWidth); + + _targetOffsetTop = MAX(0, offsetY - viewportHeight / 2); + _targetOffsetTop = MIN(_targetOffsetTop, _height - viewportHeight); + + + if (_gameRef->_mainObject && _gameRef->_mainObject->_is3D) { + if (abs(origOffsetLeft - _targetOffsetLeft) < 5) { + _targetOffsetLeft = origOffsetLeft; + } + if (abs(origOffsetTop - _targetOffsetTop) < 5) { + _targetOffsetTop = origOffsetTop; + } + //_targetOffsetTop = 0; + } + + _ready = false; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::scrollToObject(BaseObject *object) { + if (object) { + scrollTo(object->_posX, object->_posY - object->getHeight() / 2); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::skipToObject(BaseObject *object) { + if (object) { + skipTo(object->_posX, object->_posY - object->getHeight() / 2); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::skipTo(int offsetX, int offsetY) { + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetLeft = MAX(0, offsetX - viewportWidth / 2); + _offsetLeft = MIN(_offsetLeft, _width - viewportWidth); + + _offsetTop = MAX(0, offsetY - viewportHeight / 2); + _offsetTop = MIN(_offsetTop, _height - viewportHeight); + + _targetOffsetLeft = _offsetLeft; + _targetOffsetTop = _offsetTop; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdScene::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // LoadActor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LoadActor") == 0) { + stack->correctParams(1); + AdActor *act = new AdActor(_gameRef); + if (act && DID_SUCCEED(act->loadFile(stack->pop()->getString()))) { + addObject(act); + stack->pushNative(act, true); + } else { + delete act; + act = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadEntity") == 0) { + stack->correctParams(1); + AdEntity *ent = new AdEntity(_gameRef); + if (ent && DID_SUCCEED(ent->loadFile(stack->pop()->getString()))) { + addObject(ent); + stack->pushNative(ent, true); + } else { + delete ent; + ent = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdEntity *ent = new AdEntity(_gameRef); + addObject(ent); + if (!val->isNULL()) { + ent->setName(val->getString()); + } + stack->pushNative(ent, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject / UnloadActor / UnloadEntity / UnloadActor3D / DeleteEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0 || strcmp(name, "UnloadActor") == 0 || strcmp(name, "UnloadEntity") == 0 || strcmp(name, "UnloadActor3D") == 0 || strcmp(name, "DeleteEntity") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + AdObject *obj = (AdObject *)val->getNative(); + removeObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SkipTo") == 0) { + stack->correctParams(2); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + if (val1->isNative()) { + skipToObject((BaseObject *)val1->getNative()); + } else { + skipTo(val1->getInt(), val2->getInt()); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollTo / ScrollToAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollTo") == 0 || strcmp(name, "ScrollToAsync") == 0) { + stack->correctParams(2); + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + if (val1->isNative()) { + scrollToObject((BaseObject *)val1->getNative()); + } else { + scrollTo(val1->getInt(), val2->getInt()); + } + if (strcmp(name, "ScrollTo") == 0) { + script->waitForExclusive(this); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int layer = val->getInt(); + if (layer < 0 || layer >= (int32)_layers.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_layers[layer], true); + } + } else { + const char *layerName = val->getString(); + bool layerFound = false; + for (uint32 i = 0; i < _layers.size(); i++) { + if (scumm_stricmp(layerName, _layers[i]->getName()) == 0) { + stack->pushNative(_layers[i], true); + layerFound = true; + break; + } + } + if (!layerFound) { + stack->pushNULL(); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaypointGroup + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaypointGroup") == 0) { + stack->correctParams(1); + int group = stack->pop()->getInt(); + if (group < 0 || group >= (int32)_waypointGroups.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_waypointGroups[group], true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetNode") == 0) { + stack->correctParams(1); + const char *nodeName = stack->pop()->getString(); + + BaseObject *node = getNodeByName(nodeName); + if (node) { + stack->pushNative((BaseScriptable *)node, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFreeNode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFreeNode") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdObject *ret = NULL; + if (val->isInt()) { + int index = val->getInt(); + if (index >= 0 && index < (int32)_objects.size()) { + ret = _objects[index]; + } + } else { + const char *nodeName = val->getString(); + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] && _objects[i]->getName() && scumm_stricmp(_objects[i]->getName(), nodeName) == 0) { + ret = _objects[i]; + break; + } + } + } + if (ret) { + stack->pushNative(ret, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetRegionAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRegionAt") == 0) { + stack->correctParams(3); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + bool includeDecors = false; + if (!val->isNULL()) { + includeDecors = val->getBool(); + } + + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) { + if (node->_region->_decoration && !includeDecors) { + continue; + } + + stack->pushNative(node->_region, true); + return STATUS_OK; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsBlockedAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsBlockedAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushBool(isBlockedAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsWalkableAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsWalkableAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushBool(isWalkableAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetScaleAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetScaleAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushFloat(getZoomAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetRotationAt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRotationAt") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + stack->pushFloat(getRotationAt(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsScrolling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsScrolling") == 0) { + stack->correctParams(0); + bool ret = false; + if (_autoScroll) { + if (_targetOffsetLeft != _offsetLeft || _targetOffsetTop != _offsetTop) { + ret = true; + } + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeOut / FadeOutAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration); + if (strcmp(name, "FadeOutAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeIn / FadeInAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration); + if (strcmp(name, "FadeInAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFadeColor") == 0) { + stack->correctParams(0); + stack->pushInt(_fader->getCurrentColor()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsPointInViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsPointInViewport") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + stack->pushBool(pointInViewport(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetViewport + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetViewport") == 0) { + stack->correctParams(4); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + int width = stack->pop()->getInt(); + int height = stack->pop()->getInt(); + + if (width <= 0) { + width = _gameRef->_renderer->_width; + } + if (height <= 0) { + height = _gameRef->_renderer->_height; + } + + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(x, y, x + width, y + height); + } + + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdLayer *layer = new AdLayer(_gameRef); + if (!val->isNULL()) { + layer->setName(val->getString()); + } + if (_mainLayer) { + layer->_width = _mainLayer->_width; + layer->_height = _mainLayer->_height; + } + _layers.add(layer); + _gameRef->registerObject(layer); + + stack->pushNative(layer, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertLayer") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + ScValue *val = stack->pop(); + + AdLayer *layer = new AdLayer(_gameRef); + if (!val->isNULL()) { + layer->setName(val->getString()); + } + if (_mainLayer) { + layer->_width = _mainLayer->_width; + layer->_height = _mainLayer->_height; + } + if (index < 0) { + index = 0; + } + if (index <= (int32)_layers.size() - 1) { + _layers.insert_at(index, layer); + } else { + _layers.add(layer); + } + + _gameRef->registerObject(layer); + + stack->pushNative(layer, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteLayer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteLayer") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + AdLayer *toDelete = NULL; + if (val->isNative()) { + BaseScriptable *temp = val->getNative(); + for (uint32 i = 0; i < _layers.size(); i++) { + if (_layers[i] == temp) { + toDelete = _layers[i]; + break; + } + } + } else { + int index = val->getInt(); + if (index >= 0 && index < (int32)_layers.size()) { + toDelete = _layers[index]; + } + } + if (toDelete == NULL) { + stack->pushBool(false); + return STATUS_OK; + } + + if (toDelete->_main) { + script->runtimeError("Scene.DeleteLayer - cannot delete main scene layer"); + stack->pushBool(false); + return STATUS_OK; + } + + for (uint32 i = 0; i < _layers.size(); i++) { + if (_layers[i] == toDelete) { + _layers.remove_at(i); + _gameRef->unregisterObject(toDelete); + break; + } + } + stack->pushBool(true); + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdScene::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("scene"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumLayers (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumLayers") { + _scValue->setInt(_layers.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumWaypointGroups (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumWaypointGroups") { + _scValue->setInt(_waypointGroups.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MainLayer (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MainLayer") { + if (_mainLayer) { + _scValue->setNative(_mainLayer, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumFreeNodes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumFreeNodes") { + _scValue->setInt(_objects.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseX") { + int viewportX; + getViewportOffset(&viewportX); + + _scValue->setInt(_gameRef->_mousePos.x + _offsetLeft - viewportX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseY") { + int viewportY; + getViewportOffset(NULL, &viewportY); + + _scValue->setInt(_gameRef->_mousePos.y + _offsetTop - viewportY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoScroll + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoScroll") { + _scValue->setBool(_autoScroll); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentState + ////////////////////////////////////////////////////////////////////////// + else if (name == "PersistentState") { + _scValue->setBool(_persistentState); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentStateSprites + ////////////////////////////////////////////////////////////////////////// + else if (name == "PersistentStateSprites") { + _scValue->setBool(_persistentStateSprites); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollPixelsX") { + _scValue->setInt(_scrollPixelsH); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollPixelsY") { + _scValue->setInt(_scrollPixelsV); + return _scValue; + } + + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollSpeedX") { + _scValue->setInt(_scrollTimeH); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScrollSpeedY") { + _scValue->setInt(_scrollTimeV); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetX + ////////////////////////////////////////////////////////////////////////// + else if (name == "OffsetX") { + _scValue->setInt(_offsetLeft); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetY + ////////////////////////////////////////////////////////////////////////// + else if (name == "OffsetY") { + _scValue->setInt(_offsetTop); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + if (_mainLayer) { + _scValue->setInt(_mainLayer->_width); + } else { + _scValue->setInt(0); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + if (_mainLayer) { + _scValue->setInt(_mainLayer->_height); + } else { + _scValue->setInt(0); + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoScroll + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoScroll") == 0) { + _autoScroll = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentState + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PersistentState") == 0) { + _persistentState = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PersistentStateSprites + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PersistentStateSprites") == 0) { + _persistentStateSprites = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollPixelsX") == 0) { + _scrollPixelsH = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollPixelsY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollPixelsY") == 0) { + _scrollPixelsV = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollSpeedX") == 0) { + _scrollTimeH = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScrollSpeedY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScrollSpeedY") == 0) { + _scrollTimeV = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OffsetX") == 0) { + _offsetLeft = value->getInt(); + + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetLeft = MAX(0, _offsetLeft - viewportWidth / 2); + _offsetLeft = MIN(_offsetLeft, _width - viewportWidth); + _targetOffsetLeft = _offsetLeft; + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OffsetY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OffsetY") == 0) { + _offsetTop = value->getInt(); + + int viewportWidth, viewportHeight; + getViewportSize(&viewportWidth, &viewportHeight); + + _offsetTop = MAX(0, _offsetTop - viewportHeight / 2); + _offsetTop = MIN(_offsetTop, _height - viewportHeight); + _targetOffsetTop = _offsetTop; + + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdScene::scToString() { + return "[scene object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::addObject(AdObject *object) { + _objects.add(object); + return _gameRef->registerObject(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::removeObject(AdObject *object) { + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i] == object) { + _objects.remove_at(i); + return _gameRef->unregisterObject(object); + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SCENE {\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + if (_persistentState) { + buffer->putTextIndent(indent + 2, "PERSISTENT_STATE=%s\n", _persistentState ? "TRUE" : "FALSE"); + } + + if (!_persistentStateSprites) { + buffer->putTextIndent(indent + 2, "PERSISTENT_STATE_SPRITES=%s\n", _persistentStateSprites ? "TRUE" : "FALSE"); + } + + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // properties + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + // viewport + if (_viewport) { + Rect32 *rc = _viewport->getRect(); + buffer->putTextIndent(indent + 2, "VIEWPORT { %d, %d, %d, %d }\n", rc->left, rc->top, rc->right, rc->bottom); + } + + + + // editor settings + buffer->putTextIndent(indent + 2, "; ----- editor settings\n"); + buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_H=%d\n", _editorMarginH); + buffer->putTextIndent(indent + 2, "EDITOR_MARGIN_V=%d\n", _editorMarginV); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_FRAME { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColFrame), RGBCOLGetG(_editorColFrame), RGBCOLGetB(_editorColFrame), RGBCOLGetA(_editorColFrame)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntitySel), RGBCOLGetG(_editorColEntitySel), RGBCOLGetB(_editorColEntitySel), RGBCOLGetA(_editorColEntitySel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegionSel), RGBCOLGetG(_editorColRegionSel), RGBCOLGetB(_editorColRegionSel), RGBCOLGetA(_editorColRegionSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlockedSel), RGBCOLGetG(_editorColBlockedSel), RGBCOLGetB(_editorColBlockedSel), RGBCOLGetA(_editorColBlockedSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecorSel), RGBCOLGetG(_editorColDecorSel), RGBCOLGetB(_editorColDecorSel), RGBCOLGetA(_editorColDecorSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS_SEL { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypointsSel), RGBCOLGetG(_editorColWaypointsSel), RGBCOLGetB(_editorColWaypointsSel), RGBCOLGetA(_editorColWaypointsSel)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_ENTITY { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColEntity), RGBCOLGetG(_editorColEntity), RGBCOLGetB(_editorColEntity), RGBCOLGetA(_editorColEntity)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_REGION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColRegion), RGBCOLGetG(_editorColRegion), RGBCOLGetB(_editorColRegion), RGBCOLGetA(_editorColRegion)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_DECORATION { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColDecor), RGBCOLGetG(_editorColDecor), RGBCOLGetB(_editorColDecor), RGBCOLGetA(_editorColDecor)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_BLOCKED { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColBlocked), RGBCOLGetG(_editorColBlocked), RGBCOLGetB(_editorColBlocked), RGBCOLGetA(_editorColBlocked)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_WAYPOINTS { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColWaypoints), RGBCOLGetG(_editorColWaypoints), RGBCOLGetB(_editorColWaypoints), RGBCOLGetA(_editorColWaypoints)); + buffer->putTextIndent(indent + 2, "EDITOR_COLOR_SCALE { %d,%d,%d,%d }\n", RGBCOLGetR(_editorColScale), RGBCOLGetG(_editorColScale), RGBCOLGetB(_editorColScale), RGBCOLGetA(_editorColScale)); + + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_REGIONS=%s\n", _editorShowRegions ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_BLOCKED=%s\n", _editorShowBlocked ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_DECORATION=%s\n", _editorShowDecor ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_ENTITIES=%s\n", _editorShowEntities ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SHOW_SCALE=%s\n", _editorShowScale ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + BaseClass::saveAsText(buffer, indent + 2); + + // waypoints + buffer->putTextIndent(indent + 2, "; ----- waypoints\n"); + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + _waypointGroups[i]->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // layers + buffer->putTextIndent(indent + 2, "; ----- layers\n"); + for (uint32 i = 0; i < _layers.size(); i++) { + _layers[i]->saveAsText(buffer, indent + 2); + } + + // scale levels + buffer->putTextIndent(indent + 2, "; ----- scale levels\n"); + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + _scaleLevels[i]->saveAsText(buffer, indent + 2); + } + + // rotation levels + buffer->putTextIndent(indent + 2, "; ----- rotation levels\n"); + for (uint32 i = 0; i < _rotLevels.size(); i++) { + _rotLevels[i]->saveAsText(buffer, indent + 2); + } + + + buffer->putTextIndent(indent + 2, "\n"); + + // free entities + buffer->putTextIndent(indent + 2, "; ----- free entities\n"); + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_type == OBJECT_ENTITY) { + _objects[i]->saveAsText(buffer, indent + 2); + + } + } + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::sortScaleLevels() { + if (_scaleLevels.size() == 0) { + return STATUS_OK; + } + bool changed; + do { + changed = false; + for (uint32 i = 0; i < _scaleLevels.size() - 1; i++) { + if (_scaleLevels[i]->_posY > _scaleLevels[i + 1]->_posY) { + AdScaleLevel *sl = _scaleLevels[i]; + _scaleLevels[i] = _scaleLevels[i + 1]; + _scaleLevels[i + 1] = sl; + + changed = true; + } + } + + } while (changed); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::sortRotLevels() { + if (_rotLevels.size() == 0) { + return STATUS_OK; + } + bool changed; + do { + changed = false; + for (uint32 i = 0; i < _rotLevels.size() - 1; i++) { + if (_rotLevels[i]->_posX > _rotLevels[i + 1]->_posX) { + AdRotLevel *rl = _rotLevels[i]; + _rotLevels[i] = _rotLevels[i + 1]; + _rotLevels[i + 1] = rl; + + changed = true; + } + } + + } while (changed); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getScaleAt(int Y) { + AdScaleLevel *prev = NULL; + AdScaleLevel *next = NULL; + + for (uint32 i = 0; i < _scaleLevels.size(); i++) { + /* AdScaleLevel *xxx = _scaleLevels[i];*/ + /* int j = _scaleLevels.size(); */ + if (_scaleLevels[i]->_posY < Y) { + prev = _scaleLevels[i]; + } else { + next = _scaleLevels[i]; + break; + } + } + + if (prev == NULL || next == NULL) { + return 100; + } + + int delta_y = next->_posY - prev->_posY; + float delta_scale = next->_scale - prev->_scale; + Y -= prev->_posY; + + float percent = (float)Y / ((float)delta_y / 100.0f); + return prev->_scale + delta_scale / 100 * percent; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_autoScroll)); + persistMgr->transfer(TMEMBER(_editorColBlocked)); + persistMgr->transfer(TMEMBER(_editorColBlockedSel)); + persistMgr->transfer(TMEMBER(_editorColDecor)); + persistMgr->transfer(TMEMBER(_editorColDecorSel)); + persistMgr->transfer(TMEMBER(_editorColEntity)); + persistMgr->transfer(TMEMBER(_editorColEntitySel)); + persistMgr->transfer(TMEMBER(_editorColFrame)); + persistMgr->transfer(TMEMBER(_editorColRegion)); + persistMgr->transfer(TMEMBER(_editorColRegionSel)); + persistMgr->transfer(TMEMBER(_editorColScale)); + persistMgr->transfer(TMEMBER(_editorColWaypoints)); + persistMgr->transfer(TMEMBER(_editorColWaypointsSel)); + persistMgr->transfer(TMEMBER(_editorMarginH)); + persistMgr->transfer(TMEMBER(_editorMarginV)); + persistMgr->transfer(TMEMBER(_editorShowBlocked)); + persistMgr->transfer(TMEMBER(_editorShowDecor)); + persistMgr->transfer(TMEMBER(_editorShowEntities)); + persistMgr->transfer(TMEMBER(_editorShowRegions)); + persistMgr->transfer(TMEMBER(_editorShowScale)); + persistMgr->transfer(TMEMBER(_fader)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_initialized)); + persistMgr->transfer(TMEMBER(_lastTimeH)); + persistMgr->transfer(TMEMBER(_lastTimeV)); + _layers.persist(persistMgr); + persistMgr->transfer(TMEMBER(_mainLayer)); + _objects.persist(persistMgr); + persistMgr->transfer(TMEMBER(_offsetLeft)); + persistMgr->transfer(TMEMBER(_offsetTop)); + persistMgr->transfer(TMEMBER(_paralaxScrolling)); + persistMgr->transfer(TMEMBER(_persistentState)); + persistMgr->transfer(TMEMBER(_persistentStateSprites)); + persistMgr->transfer(TMEMBER(_pfMaxTime)); + _pfPath.persist(persistMgr); + persistMgr->transfer(TMEMBER(_pfPointsNum)); + persistMgr->transfer(TMEMBER(_pfReady)); + persistMgr->transfer(TMEMBER(_pfRequester)); + persistMgr->transfer(TMEMBER(_pfTarget)); + persistMgr->transfer(TMEMBER(_pfTargetPath)); + _rotLevels.persist(persistMgr); + _scaleLevels.persist(persistMgr); + persistMgr->transfer(TMEMBER(_scrollPixelsH)); + persistMgr->transfer(TMEMBER(_scrollPixelsV)); + persistMgr->transfer(TMEMBER(_scrollTimeH)); + persistMgr->transfer(TMEMBER(_scrollTimeV)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER(_targetOffsetLeft)); + persistMgr->transfer(TMEMBER(_targetOffsetTop)); + _waypointGroups.persist(persistMgr); + persistMgr->transfer(TMEMBER(_viewport)); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::afterLoad() { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester) { + double xStep, yStep, x, y; + int xLength, yLength, xCount, yCount; + int x1, y1, x2, y2; + + x1 = *targetX; + y1 = *targetY; + x2 = startX; + y2 = startY; + + + xLength = abs(x2 - x1); + yLength = abs(y2 - y1); + + if (xLength > yLength) { + + yStep = fabs((double)(y2 - y1) / (double)(x2 - x1)); + y = y1; + + for (xCount = x1; xCount < x2; xCount++) { + if (isWalkableAt(xCount, (int)y, checkFreeObjects, requester)) { + *targetX = xCount; + *targetY = (int)y; + return STATUS_OK; + } + y += yStep; + } + } else { + + xStep = fabs((double)(x2 - x1) / (double)(y2 - y1)); + x = x1; + + for (yCount = y1; yCount < y2; yCount++) { + if (isWalkableAt((int)x, yCount, checkFreeObjects, requester)) { + *targetX = (int)x; + *targetY = yCount; + return STATUS_OK; + } + x += xStep; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::correctTargetPoint(int startX, int startY, int *argX, int *argY, bool checkFreeObjects, BaseObject *requester) { + int x = *argX; + int y = *argY; + + if (isWalkableAt(x, y, checkFreeObjects, requester) || !_mainLayer) { + return STATUS_OK; + } + + // right + int lengthRight = 0; + bool foundRight = false; + for (x = *argX, y = *argY; x < _mainLayer->_width; x++, lengthRight++) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x - 5, y, checkFreeObjects, requester)) { + foundRight = true; + break; + } + } + + // left + int lengthLeft = 0; + bool foundLeft = false; + for (x = *argX, y = *argY; x >= 0; x--, lengthLeft--) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x + 5, y, checkFreeObjects, requester)) { + foundLeft = true; + break; + } + } + + // up + int lengthUp = 0; + bool foundUp = false; + for (x = *argX, y = *argY; y >= 0; y--, lengthUp--) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y + 5, checkFreeObjects, requester)) { + foundUp = true; + break; + } + } + + // down + int lengthDown = 0; + bool foundDown = false; + for (x = *argX, y = *argY; y < _mainLayer->_height; y++, lengthDown++) { + if (isWalkableAt(x, y, checkFreeObjects, requester) && isWalkableAt(x, y - 5, checkFreeObjects, requester)) { + foundDown = true; + break; + } + } + + if (!foundLeft && !foundRight && !foundUp && !foundDown) { + return STATUS_OK; + } + + int offsetX = INT_MAX, offsetY = INT_MAX; + + if (foundLeft && foundRight) { + if (abs(lengthLeft) < abs(lengthRight)) { + offsetX = lengthLeft; + } else { + offsetX = lengthRight; + } + } else if (foundLeft) { + offsetX = lengthLeft; + } else if (foundRight) { + offsetX = lengthRight; + } + + if (foundUp && foundDown) { + if (abs(lengthUp) < abs(lengthDown)) { + offsetY = lengthUp; + } else { + offsetY = lengthDown; + } + } else if (foundUp) { + offsetY = lengthUp; + } else if (foundDown) { + offsetY = lengthDown; + } + + if (abs(offsetX) < abs(offsetY)) { + *argX = *argX + offsetX; + } else { + *argY = *argY + offsetY; + } + + if (!isWalkableAt(*argX, *argY)) { + return correctTargetPoint2(startX, startY, argX, argY, checkFreeObjects, requester); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfPointsStart() { + _pfPointsNum = 0; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::pfPointsAdd(int x, int y, int distance) { + if (_pfPointsNum >= (int32)_pfPath.size()) { + _pfPath.add(new AdPathPoint(x, y, distance)); + } else { + _pfPath[_pfPointsNum]->x = x; + _pfPath[_pfPointsNum]->y = y; + _pfPath[_pfPointsNum]->_distance = distance; + _pfPath[_pfPointsNum]->_marked = false; + _pfPath[_pfPointsNum]->_origin = NULL; + } + + _pfPointsNum++; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getViewportOffset(int *offsetX, int *offsetY) { + AdGame *adGame = (AdGame *)_gameRef; + if (_viewport && !_gameRef->_editorMode) { + if (offsetX) { + *offsetX = _viewport->_offsetX; + } + if (offsetY) { + *offsetY = _viewport->_offsetY; + } + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + if (offsetX) { + *offsetX = adGame->_sceneViewport->_offsetX; + } + if (offsetY) { + *offsetY = adGame->_sceneViewport->_offsetY; + } + } else { + if (offsetX) { + *offsetX = 0; + } + if (offsetY) { + *offsetY = 0; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getViewportSize(int *width, int *height) { + AdGame *adGame = (AdGame *)_gameRef; + if (_viewport && !_gameRef->_editorMode) { + if (width) { + *width = _viewport->getWidth(); + } + if (height) { + *height = _viewport->getHeight(); + } + } else if (adGame->_sceneViewport && !_gameRef->_editorMode) { + if (width) { + *width = adGame->_sceneViewport->getWidth(); + } + if (height) { + *height = adGame->_sceneViewport->getHeight(); + } + } else { + if (width) { + *width = _gameRef->_renderer->_width; + } + if (height) { + *height = _gameRef->_renderer->_height; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getOffsetLeft() { + int viewportX; + getViewportOffset(&viewportX); + + return _offsetLeft - viewportX; +} + + +////////////////////////////////////////////////////////////////////////// +int AdScene::getOffsetTop() { + int viewportY; + getViewportOffset(NULL, &viewportY); + + return _offsetTop - viewportY; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::pointInViewport(int x, int y) { + int left, top, width, height; + + getViewportOffset(&left, &top); + getViewportSize(&width, &height); + + return x >= left && x <= left + width && y >= top && y <= top + height; +} + + +////////////////////////////////////////////////////////////////////////// +void AdScene::setOffset(int offsetLeft, int offsetTop) { + _offsetLeft = offsetLeft; + _offsetTop = offsetTop; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getNodeByName(const char *name) { + BaseObject *ret = NULL; + + // dependent objects + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + AdSceneNode *node = layer->_nodes[j]; + if ((node->_type == OBJECT_ENTITY && !scumm_stricmp(name, node->_entity->getName())) || + (node->_type == OBJECT_REGION && !scumm_stricmp(name, node->_region->getName()))) { + switch (node->_type) { + case OBJECT_ENTITY: + ret = node->_entity; + break; + case OBJECT_REGION: + ret = node->_region; + break; + default: + ret = NULL; + } + return ret; + } + } + } + + // free entities + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_type == OBJECT_ENTITY && !scumm_stricmp(name, _objects[i]->getName())) { + return _objects[i]; + } + } + + // waypoint groups + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + if (!scumm_stricmp(name, _waypointGroups[i]->getName())) { + return _waypointGroups[i]; + } + } + + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::saveState() { + return persistState(true); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::loadState() { + return persistState(false); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::persistState(bool saving) { + if (!_persistentState) { + return STATUS_OK; + } + + AdGame *adGame = (AdGame *)_gameRef; + AdSceneState *state = adGame->getSceneState(getFilename(), saving); + if (!state) { + return STATUS_OK; + } + + AdNodeState *nodeState; + + // dependent objects + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + AdSceneNode *node = layer->_nodes[j]; + switch (node->_type) { + case OBJECT_ENTITY: + if (!node->_entity->_saveState) { + continue; + } + nodeState = state->getNodeState(node->_entity->getName(), saving); + if (nodeState) { + nodeState->transferEntity(node->_entity, _persistentStateSprites, saving); + //if (Saving) NodeState->_active = node->_entity->_active; + //else node->_entity->_active = NodeState->_active; + } + break; + case OBJECT_REGION: + if (!node->_region->_saveState) { + continue; + } + nodeState = state->getNodeState(node->_region->getName(), saving); + if (nodeState) { + if (saving) { + nodeState->_active = node->_region->_active; + } else { + node->_region->_active = nodeState->_active; + } + } + break; + default: + warning("AdScene::PersistState - unhandled enum"); + break; + } + } + } + + // free entities + for (uint32 i = 0; i < _objects.size(); i++) { + if (!_objects[i]->_saveState) { + continue; + } + if (_objects[i]->_type == OBJECT_ENTITY) { + nodeState = state->getNodeState(_objects[i]->getName(), saving); + if (nodeState) { + nodeState->transferEntity((AdEntity *)_objects[i], _persistentStateSprites, saving); + //if (Saving) NodeState->_active = _objects[i]->_active; + //else _objects[i]->_active = NodeState->_active; + } + } + } + + // waypoint groups + for (uint32 i = 0; i < _waypointGroups.size(); i++) { + nodeState = state->getNodeState(_waypointGroups[i]->getName(), saving); + if (nodeState) { + if (saving) { + nodeState->_active = _waypointGroups[i]->_active; + } else { + _waypointGroups[i]->_active = nodeState->_active; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float AdScene::getRotationAt(int x, int y) { + AdRotLevel *prev = NULL; + AdRotLevel *next = NULL; + + for (uint32 i = 0; i < _rotLevels.size(); i++) { + /* AdRotLevel *xxx = _rotLevels[i]; + int j = _rotLevels.size();*/ + if (_rotLevels[i]->_posX < x) { + prev = _rotLevels[i]; + } else { + next = _rotLevels[i]; + break; + } + } + + if (prev == NULL || next == NULL) { + return 0; + } + + int delta_x = next->_posX - prev->_posX; + float delta_rot = next->_rotation - prev->_rotation; + x -= prev->_posX; + + float percent = (float)x / ((float)delta_x / 100.0f); + return prev->_rotation + delta_rot / 100 * percent; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::handleItemAssociations(const char *itemName, bool show) { + for (uint32 i = 0; i < _layers.size(); i++) { + AdLayer *layer = _layers[i]; + for (uint32 j = 0; j < layer->_nodes.size(); j++) { + if (layer->_nodes[j]->_type == OBJECT_ENTITY) { + AdEntity *ent = layer->_nodes[j]->_entity; + + if (ent->_item && strcmp(ent->_item, itemName) == 0) { + ent->_active = show; + } + } + } + } + + for (uint32 i = 0; i < _objects.size(); i++) { + if (_objects[i]->_type == OBJECT_ENTITY) { + AdEntity *ent = (AdEntity *)_objects[i]; + if (ent->_item && strcmp(ent->_item, itemName) == 0) { + ent->_active = show; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getRegionsAt(int x, int y, AdRegion **regionList, int numRegions) { + int numUsed = 0; + if (_mainLayer) { + for (int i = _mainLayer->_nodes.size() - 1; i >= 0; i--) { + AdSceneNode *node = _mainLayer->_nodes[i]; + if (node->_type == OBJECT_REGION && node->_region->_active && node->_region->pointInRegion(x, y)) { + if (numUsed < numRegions - 1) { + regionList[numUsed] = node->_region; + numUsed++; + } else { + break; + } + } + } + } + for (int i = numUsed; i < numRegions; i++) { + regionList[i] = NULL; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdScene::restoreDeviceObjects() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getNextAccessObject(BaseObject *currObject) { + BaseArray<AdObject *> objects; + getSceneObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (uint32 i = 0; i < objects.size(); i++) { + if (objects[i] == currObject) { + if (i < objects.size() - 1) { + return objects[i + 1]; + } else { + break; + } + } + } + } + return objects[0]; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseObject *AdScene::getPrevAccessObject(BaseObject *currObject) { + BaseArray<AdObject *> objects; + getSceneObjects(objects, true); + + if (objects.size() == 0) { + return NULL; + } else { + if (currObject != NULL) { + for (int i = objects.size() - 1; i >= 0; i--) { + if (objects[i] == currObject) { + if (i > 0) { + return objects[i - 1]; + } else { + break; + } + } + } + } + return objects[objects.size() - 1]; + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _layers.size(); i++) { + // close-up layer -> remove everything below it + if (interactiveOnly && _layers[i]->_closeUp) { + objects.clear(); + } + + + for (uint32 j = 0; j < _layers[i]->_nodes.size(); j++) { + AdSceneNode *node = _layers[i]->_nodes[j]; + switch (node->_type) { + case OBJECT_ENTITY: { + AdEntity *ent = node->_entity; + if (ent->_active && (ent->_registrable || !interactiveOnly)) { + objects.add(ent); + } + } + break; + + case OBJECT_REGION: { + BaseArray<AdObject *> regionObj; + getRegionObjects(node->_region, regionObj, interactiveOnly); + for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) { + bool found = false; + for (uint32 old = 0; old < objects.size(); old++) { + if (objects[old] == regionObj[newIndex]) { + found = true; + break; + } + } + if (!found) { + objects.add(regionObj[newIndex]); + } + } + //if (regionObj.size() > 0) Objects.Append(RegionObj); + } + break; + default: + debugC(kWintermuteDebugGeneral, "AdScene::GetSceneObjects - Unhandled enum"); + break; + } + } + } + + // objects outside any region + BaseArray<AdObject *> regionObj; + getRegionObjects(NULL, regionObj, interactiveOnly); + for (uint32 newIndex = 0; newIndex < regionObj.size(); newIndex++) { + bool found = false; + for (uint32 old = 0; old < objects.size(); old++) { + if (objects[old] == regionObj[newIndex]) { + found = true; + break; + } + } + if (!found) { + objects.add(regionObj[newIndex]); + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdScene::getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly) { + AdGame *adGame = (AdGame *)_gameRef; + AdObject *obj; + + // global objects + for (uint32 i = 0; i < adGame->_objects.size(); i++) { + obj = adGame->_objects[i]; + if (obj->_active && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + if (interactiveOnly && !obj->_registrable) { + continue; + } + + objects.add(obj); + } + } + + // scene objects + for (uint32 i = 0; i < _objects.size(); i++) { + obj = _objects[i]; + if (obj->_active && !obj->_editorOnly && (obj->_stickRegion == region || region == NULL || (obj->_stickRegion == NULL && region->pointInRegion(obj->_posX, obj->_posY)))) { + if (interactiveOnly && !obj->_registrable) { + continue; + } + + objects.add(obj); + } + } + + // sort by _posY + Common::sort(objects.begin(), objects.end(), AdScene::compareObjs); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene.h b/engines/wintermute/ad/ad_scene.h new file mode 100644 index 0000000000..3b482403b5 --- /dev/null +++ b/engines/wintermute/ad/ad_scene.h @@ -0,0 +1,181 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENE_H +#define WINTERMUTE_ADSCENE_H + +#include "engines/wintermute/base/base_fader.h" + +namespace Wintermute { + +class UIWindow; +class AdObject; +class AdRegion; +class BaseViewport; +class AdLayer; +class BasePoint; +class AdWaypointGroup; +class AdPath; +class AdScaleLevel; +class AdRotLevel; +class AdPathPoint; +class AdScene : public BaseObject { +public: + + BaseObject *getNextAccessObject(BaseObject *currObject); + BaseObject *getPrevAccessObject(BaseObject *currObject); + bool getSceneObjects(BaseArray<AdObject *> &objects, bool interactiveOnly); + bool getRegionObjects(AdRegion *region, BaseArray<AdObject *> &objects, bool interactiveOnly); + + bool afterLoad(); + + bool getRegionsAt(int x, int y, AdRegion **regionList, int numRegions); + bool handleItemAssociations(const char *itemName, bool show); + UIWindow *_shieldWindow; + float getRotationAt(int x, int y); + bool loadState(); + bool saveState(); + bool _persistentState; + bool _persistentStateSprites; + BaseObject *getNodeByName(const char *name); + void setOffset(int offsetLeft, int offsetTop); + bool pointInViewport(int x, int y); + int getOffsetTop(); + int getOffsetLeft(); + bool getViewportSize(int *width = NULL, int *height = NULL); + bool getViewportOffset(int *offsetX = NULL, int *offsetY = NULL); + BaseViewport *_viewport; + BaseFader *_fader; + int _pfPointsNum; + void pfPointsAdd(int x, int y, int distance); + void pfPointsStart(); + bool _initialized; + bool correctTargetPoint(int startX, int startY, int *x, int *y, bool checkFreeObjects = false, BaseObject *requester = NULL); + bool correctTargetPoint2(int startX, int startY, int *targetX, int *targetY, bool checkFreeObjects, BaseObject *requester); + DECLARE_PERSISTENT(AdScene, BaseObject) + bool displayRegionContent(AdRegion *region = NULL, bool display3DOnly = false); + bool displayRegionContentOld(AdRegion *region = NULL); + static int compareObjs(const void *obj1, const void *obj2); + + bool updateFreeObjects(); + bool traverseNodes(bool update = false); + float getScaleAt(int y); + bool sortScaleLevels(); + bool sortRotLevels(); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + uint32 getAlphaAt(int x, int y, bool colorCheck = false); + bool _paralaxScrolling; + void skipTo(int offsetX, int offsetY); + void setDefaults(); + void cleanup(); + void skipToObject(BaseObject *object); + void scrollToObject(BaseObject *object); + void scrollTo(int offsetX, int offsetY); + virtual bool update(); + bool _autoScroll; + int _targetOffsetTop; + int _targetOffsetLeft; + + int _scrollPixelsV; + uint32 _scrollTimeV; + uint32 _lastTimeV; + + int _scrollPixelsH; + uint32 _scrollTimeH; + uint32 _lastTimeH; + + virtual bool display(); + uint32 _pfMaxTime; + bool initLoop(); + void pathFinderStep(); + bool isBlockedAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL); + bool isWalkableAt(int x, int y, bool checkFreeObjects = false, BaseObject *requester = NULL); + AdLayer *_mainLayer; + float getZoomAt(int x, int y); + bool getPath(BasePoint source, BasePoint target, AdPath *path, BaseObject *requester = NULL); + AdScene(BaseGame *inGame); + virtual ~AdScene(); + BaseArray<AdLayer *> _layers; + BaseArray<AdObject *> _objects; + BaseArray<AdWaypointGroup *> _waypointGroups; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + int _width; + int _height; + bool addObject(AdObject *Object); + bool removeObject(AdObject *Object); + int _editorMarginH; + int _editorMarginV; + uint32 _editorColFrame; + uint32 _editorColEntity; + uint32 _editorColRegion; + uint32 _editorColBlocked; + uint32 _editorColWaypoints; + uint32 _editorColEntitySel; + uint32 _editorColRegionSel; + uint32 _editorColBlockedSel; + uint32 _editorColWaypointsSel; + uint32 _editorColScale; + uint32 _editorColDecor; + uint32 _editorColDecorSel; + + bool _editorShowRegions; + bool _editorShowBlocked; + bool _editorShowDecor; + bool _editorShowEntities; + bool _editorShowScale; + BaseArray<AdScaleLevel *> _scaleLevels; + BaseArray<AdRotLevel *> _rotLevels; + + virtual bool restoreDeviceObjects(); + int getPointsDist(BasePoint p1, BasePoint p2, BaseObject *requester = NULL); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + +private: + bool persistState(bool saving = true); + void pfAddWaypointGroup(AdWaypointGroup *Wpt, BaseObject *requester = NULL); + bool _pfReady; + BasePoint *_pfTarget; + AdPath *_pfTargetPath; + BaseObject *_pfRequester; + BaseArray<AdPathPoint *> _pfPath; + + int _offsetTop; + int _offsetLeft; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_scene_node.cpp b/engines/wintermute/ad/ad_scene_node.cpp new file mode 100644 index 0000000000..d0202236fd --- /dev/null +++ b/engines/wintermute/ad/ad_scene_node.cpp @@ -0,0 +1,82 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSceneNode, false) + +////////////////////////////////////////////////////////////////////////// +AdSceneNode::AdSceneNode(BaseGame *inGame) : BaseObject(inGame) { + _type = OBJECT_NONE; + _region = NULL; + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneNode::~AdSceneNode() { + _gameRef->unregisterObject(_region); + _region = NULL; + + _gameRef->unregisterObject(_entity); + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::setEntity(AdEntity *entity) { + _type = OBJECT_ENTITY; + _entity = entity; + return _gameRef->registerObject(entity); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::setRegion(AdRegion *region) { + _type = OBJECT_REGION; + _region = region; + return _gameRef->registerObject(region); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneNode::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_entity)); + persistMgr->transfer(TMEMBER(_region)); + persistMgr->transfer(TMEMBER_INT(_type)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene_node.h b/engines/wintermute/ad/ad_scene_node.h new file mode 100644 index 0000000000..5bb1606d0e --- /dev/null +++ b/engines/wintermute/ad/ad_scene_node.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENENODE_H +#define WINTERMUTE_ADSCENENODE_H + + +#include "engines/wintermute/ad/ad_types.h" // Added by ClassView +#include "engines/wintermute/ad/ad_region.h" // Added by ClassView +#include "engines/wintermute/ad/ad_entity.h" + +namespace Wintermute { + +class AdSceneNode : public BaseObject { +public: + DECLARE_PERSISTENT(AdSceneNode, BaseObject) + bool setRegion(AdRegion *region); + bool setEntity(AdEntity *entity); + AdEntity *_entity; + AdRegion *_region; + TObjectType _type; + AdSceneNode(BaseGame *inGame); + virtual ~AdSceneNode(); + +}; + +} + +#endif diff --git a/engines/wintermute/ad/ad_scene_state.cpp b/engines/wintermute/ad/ad_scene_state.cpp new file mode 100644 index 0000000000..6b34f1af53 --- /dev/null +++ b/engines/wintermute/ad/ad_scene_state.cpp @@ -0,0 +1,95 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSceneState, false) + +////////////////////////////////////////////////////////////////////////// +AdSceneState::AdSceneState(BaseGame *inGame) : BaseClass(inGame) { + _filename = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdSceneState::~AdSceneState() { + delete[] _filename; + _filename = NULL; + + for (uint32 i = 0; i < _nodeStates.size(); i++) { + delete _nodeStates[i]; + } + _nodeStates.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSceneState::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_filename)); + _nodeStates.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdSceneState::setFilename(const char *filename) { + delete[] _filename; + _filename = new char [strlen(filename) + 1]; + if (_filename) { + strcpy(_filename, filename); + } +} + + +////////////////////////////////////////////////////////////////////////// +AdNodeState *AdSceneState::getNodeState(const char *name, bool saving) { + for (uint32 i = 0; i < _nodeStates.size(); i++) { + if (scumm_stricmp(_nodeStates[i]->getName(), name) == 0) { + return _nodeStates[i]; + } + } + + if (saving) { + AdNodeState *ret = new AdNodeState(_gameRef); + ret->setName(name); + _nodeStates.add(ret); + + return ret; + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_scene_state.h b/engines/wintermute/ad/ad_scene_state.h new file mode 100644 index 0000000000..2b25393c5a --- /dev/null +++ b/engines/wintermute/ad/ad_scene_state.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSCENESTATE_H +#define WINTERMUTE_ADSCENESTATE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class AdNodeState; +class AdSceneState : public BaseClass { +public: + AdNodeState *getNodeState(const char *name, bool saving); + void setFilename(const char *filename); + DECLARE_PERSISTENT(AdSceneState, BaseClass) + AdSceneState(BaseGame *inGame); + virtual ~AdSceneState(); + char *_filename; + BaseArray<AdNodeState *> _nodeStates; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_sentence.cpp b/engines/wintermute/ad/ad_sentence.cpp new file mode 100644 index 0000000000..cfe4191b07 --- /dev/null +++ b/engines/wintermute/ad/ad_sentence.cpp @@ -0,0 +1,361 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSentence, false) + +////////////////////////////////////////////////////////////////////////// +AdSentence::AdSentence(BaseGame *inGame) : BaseClass(inGame) { + _text = NULL; + _stances = NULL; + _tempStance = NULL; + + _duration = 0; + _startTime = 0; + _currentStance = 0; + + _font = NULL; + + _pos.x = _pos.y = 0; + _width = _gameRef->_renderer->_width; + + _align = (TTextAlign)TAL_CENTER; + + _sound = NULL; + _soundStarted = false; + + _talkDef = NULL; + _currentSprite = NULL; + _currentSkelAnim = NULL; + _fixedPos = false; + _freezable = true; +} + + +////////////////////////////////////////////////////////////////////////// +AdSentence::~AdSentence() { + delete _sound; + delete[] _text; + delete[] _stances; + delete[] _tempStance; + delete _talkDef; + _sound = NULL; + _text = NULL; + _stances = NULL; + _tempStance = NULL; + _talkDef = NULL; + + _currentSprite = NULL; // ref only + _currentSkelAnim = NULL; + _font = NULL; // ref only +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setText(const char *text) { + if (_text) { + delete[] _text; + } + _text = new char[strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + } +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setStances(const char *stances) { + if (_stances) { + delete[] _stances; + } + if (stances) { + _stances = new char[strlen(stances) + 1]; + if (_stances) { + strcpy(_stances, stances); + } + } else { + _stances = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getCurrentStance() { + return getStance(_currentStance); +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getNextStance() { + _currentStance++; + return getStance(_currentStance); +} + + +////////////////////////////////////////////////////////////////////////// +char *AdSentence::getStance(int stance) { + if (_stances == NULL) { + return NULL; + } + + if (_tempStance) { + delete[] _tempStance; + } + _tempStance = NULL; + + char *start; + char *curr; + int pos; + + if (stance == 0) { + start = _stances; + } else { + pos = 0; + start = NULL; + curr = _stances; + while (pos < stance) { + if (*curr == '\0') { + break; + } + if (*curr == ',') { + pos++; + } + curr++; + } + if (pos == stance) { + start = curr; + } + } + + if (start == NULL) { + return NULL; + } + + while (*start == ' ' && *start != ',' && *start != '\0') { + start++; + } + + curr = start; + while (*curr != '\0' && *curr != ',') { + curr++; + } + + while (curr > start && *(curr - 1) == ' ') { + curr--; + } + + _tempStance = new char [curr - start + 1]; + if (_tempStance) { + Common::strlcpy(_tempStance, start, curr - start + 1); + } + + return _tempStance; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::display() { + if (!_font || !_text) { + return STATUS_FAILED; + } + + if (_sound && !_soundStarted) { + _sound->play(); + _soundStarted = true; + } + + if (_gameRef->_subtitles) { + int x = _pos.x; + int y = _pos.y; + + if (!_fixedPos) { + x = x - ((AdGame *)_gameRef)->_scene->getOffsetLeft(); + y = y - ((AdGame *)_gameRef)->_scene->getOffsetTop(); + } + + + x = MAX(x, 0); + x = MIN(x, _gameRef->_renderer->_width - _width); + y = MAX(y, 0); + + _font->drawText((byte *)_text, x, y, _width, _align); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void AdSentence::setSound(BaseSound *sound) { + if (!sound) { + return; + } + delete _sound; + _sound = sound; + _soundStarted = false; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::finish() { + if (_sound) { + _sound->stop(); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER_INT(_align)); + persistMgr->transfer(TMEMBER(_currentStance)); + persistMgr->transfer(TMEMBER(_currentSprite)); + persistMgr->transfer(TMEMBER(_currentSkelAnim)); + persistMgr->transfer(TMEMBER(_duration)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_sound)); + persistMgr->transfer(TMEMBER(_soundStarted)); + persistMgr->transfer(TMEMBER(_stances)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_talkDef)); + persistMgr->transfer(TMEMBER(_tempStance)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER(_width)); + persistMgr->transfer(TMEMBER(_fixedPos)); + persistMgr->transfer(TMEMBER(_freezable)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::setupTalkFile(const char *soundFilename) { + delete _talkDef; + _talkDef = NULL; + _currentSprite = NULL; + + if (!soundFilename) { + return STATUS_OK; + } + + + AnsiString path = PathUtil::getDirectoryName(soundFilename); + AnsiString name = PathUtil::getFileNameWithoutExtension(soundFilename); + + AnsiString talkDefFileName = PathUtil::combine(path, name + ".talk"); + + if (!BaseFileManager::getEngineInstance()->hasFile(talkDefFileName)) { + return STATUS_OK; // no talk def file found + } + + _talkDef = new AdTalkDef(_gameRef); + if (!_talkDef || DID_FAIL(_talkDef->loadFile(talkDefFileName.c_str()))) { + delete _talkDef; + _talkDef = NULL; + return STATUS_FAILED; + } + //_gameRef->LOG(0, "Using .talk file: %s", TalkDefFile); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::update(TDirection dir) { + if (!_talkDef) { + return STATUS_OK; + } + + uint32 currentTime; + // if sound is available, synchronize with sound, otherwise use timer + + /* + if (_sound) CurrentTime = _sound->GetPositionTime(); + else CurrentTime = _gameRef->_timer - _startTime; + */ + currentTime = _gameRef->_timer - _startTime; + + bool talkNodeFound = false; + for (uint32 i = 0; i < _talkDef->_nodes.size(); i++) { + if (_talkDef->_nodes[i]->isInTimeInterval(currentTime, dir)) { + talkNodeFound = true; + + BaseSprite *newSprite = _talkDef->_nodes[i]->getSprite(dir); + if (newSprite != _currentSprite) { + newSprite->reset(); + } + _currentSprite = newSprite; + + if (!_talkDef->_nodes[i]->_playToEnd) { + break; + } + } + } + + + // no talk node, try to use default sprite instead (if any) + if (!talkNodeFound) { + BaseSprite *newSprite = _talkDef->getDefaultSprite(dir); + if (newSprite) { + if (newSprite != _currentSprite) { + newSprite->reset(); + } + _currentSprite = newSprite; + } else { + _currentSprite = NULL; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdSentence::canSkip() { + // prevent accidental sentence skipping (TODO make configurable) + return (_gameRef->_timer - _startTime) > 300; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_sentence.h b/engines/wintermute/ad/ad_sentence.h new file mode 100644 index 0000000000..e7c94030b9 --- /dev/null +++ b/engines/wintermute/ad/ad_sentence.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSENTENCE_H +#define WINTERMUTE_ADSENTENCE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "common/rect.h" + +namespace Wintermute { +class AdTalkDef; +class BaseFont; +class BaseSprite; +class BaseSound; +class AdSentence : public BaseClass { +public: + bool _freezable; + bool _fixedPos; + BaseSprite *_currentSprite; + char *_currentSkelAnim; + bool update(TDirection dir = DI_DOWN); + bool setupTalkFile(const char *soundFilename); + DECLARE_PERSISTENT(AdSentence, BaseClass) + bool finish(); + void setSound(BaseSound *Sound); + bool _soundStarted; + BaseSound *_sound; + TTextAlign _align; + bool display(); + int _width; + Point32 _pos; + BaseFont *_font; + char *getNextStance(); + char *getCurrentStance(); + void setStances(const char *stances); + void setText(const char *text); + int _currentStance; + uint32 _startTime; + char *_stances; + char *_text; + uint32 _duration; + AdSentence(BaseGame *inGame); + virtual ~AdSentence(); + AdTalkDef *_talkDef; + + bool canSkip(); + +private: + char *_tempStance; + char *getStance(int stance); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_sprite_set.cpp b/engines/wintermute/ad/ad_sprite_set.cpp new file mode 100644 index 0000000000..345b483a8f --- /dev/null +++ b/engines/wintermute/ad/ad_sprite_set.cpp @@ -0,0 +1,356 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdSpriteSet, false) + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet::AdSpriteSet(BaseGame *inGame, BaseObject *owner) : BaseObject(inGame) { + _owner = owner; + + for (int i = 0; i < NUM_DIRECTIONS; i++) { + _sprites[i] = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +AdSpriteSet::~AdSpriteSet() { + for (int i = 0; i < NUM_DIRECTIONS; i++) { + delete _sprites[i]; + _sprites[i] = NULL; + } + + _owner = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::loadFile(const char *filename, int lifeTime, TSpriteCacheType cacheType) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdSpriteSet::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing SPRITESET file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(SPRITESET) +TOKEN_DEF(NAME) +TOKEN_DEF(UP_LEFT) +TOKEN_DEF(DOWN_LEFT) +TOKEN_DEF(LEFT) +TOKEN_DEF(UP_RIGHT) +TOKEN_DEF(DOWN_RIGHT) +TOKEN_DEF(RIGHT) +TOKEN_DEF(UP) +TOKEN_DEF(DOWN) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SPRITESET) + TOKEN_TABLE(NAME) + TOKEN_TABLE(UP_LEFT) + TOKEN_TABLE(DOWN_LEFT) + TOKEN_TABLE(LEFT) + TOKEN_TABLE(UP_RIGHT) + TOKEN_TABLE(DOWN_RIGHT) + TOKEN_TABLE(RIGHT) + TOKEN_TABLE(UP) + TOKEN_TABLE(DOWN) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SPRITESET) { + _gameRef->LOG(0, "'SPRITESET' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + BaseSprite *spr = NULL; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_LEFT: + delete _sprites[DI_LEFT]; + _sprites[DI_LEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_LEFT] = spr; + } + break; + + case TOKEN_RIGHT: + delete _sprites[DI_RIGHT]; + _sprites[DI_RIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_RIGHT] = spr; + } + break; + + case TOKEN_UP: + delete _sprites[DI_UP]; + _sprites[DI_UP] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UP] = spr; + } + break; + + case TOKEN_DOWN: + delete _sprites[DI_DOWN]; + _sprites[DI_DOWN] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWN] = spr; + } + break; + + case TOKEN_UP_LEFT: + delete _sprites[DI_UPLEFT]; + _sprites[DI_UPLEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UPLEFT] = spr; + } + break; + + case TOKEN_UP_RIGHT: + delete _sprites[DI_UPRIGHT]; + _sprites[DI_UPRIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_UPRIGHT] = spr; + } + break; + + case TOKEN_DOWN_LEFT: + delete _sprites[DI_DOWNLEFT]; + _sprites[DI_DOWNLEFT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWNLEFT] = spr; + } + break; + + case TOKEN_DOWN_RIGHT: + delete _sprites[DI_DOWNRIGHT]; + _sprites[DI_DOWNRIGHT] = NULL; + spr = new BaseSprite(_gameRef, _owner); + if (!spr || DID_FAIL(spr->loadFile((char *)params, lifeTime, cacheType))) { + cmd = PARSERR_GENERIC; + } else { + _sprites[DI_DOWNRIGHT] = spr; + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SPRITESET definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading SPRITESET definition"); + if (spr) { + delete spr; + } + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_owner)); + for (int i = 0; i < NUM_DIRECTIONS; i++) { + persistMgr->transfer("", &_sprites[i]); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdSpriteSet::getSprite(TDirection direction) { + int dir = (int)direction; + if (dir < 0) { + dir = 0; + } + if (dir >= NUM_DIRECTIONS) { + dir = NUM_DIRECTIONS - 1; + } + + BaseSprite *ret = NULL; + + // find nearest set sprite + int numSteps = 0; + for (int i = dir; i >= 0; i--) { + if (_sprites[i] != NULL) { + ret = _sprites[i]; + numSteps = dir - i; + break; + } + } + + for (int i = dir; i < NUM_DIRECTIONS; i++) { + if (_sprites[i] != NULL) { + if (ret == NULL || numSteps > i - dir) { + return _sprites[i]; + } else { + return ret; + } + } + } + + return ret; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SPRITESET {\n"); + if (getName()) { + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + } + for (int i = 0; i < NUM_DIRECTIONS; i++) { + if (_sprites[i]) { + switch (i) { + case DI_UP: + buffer->putTextIndent(indent + 2, "UP=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_UPRIGHT: + buffer->putTextIndent(indent + 2, "UP_RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_RIGHT: + buffer->putTextIndent(indent + 2, "RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWNRIGHT: + buffer->putTextIndent(indent + 2, "DOWN_RIGHT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWN: + buffer->putTextIndent(indent + 2, "DOWN=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_DOWNLEFT: + buffer->putTextIndent(indent + 2, "DOWN_LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_LEFT: + buffer->putTextIndent(indent + 2, "LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + case DI_UPLEFT: + buffer->putTextIndent(indent + 2, "UP_LEFT=\"%s\"\n", _sprites[i]->getFilename()); + break; + } + } + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool AdSpriteSet::containsSprite(BaseSprite *sprite) { + if (!sprite) { + return false; + } + + for (int i = 0; i < NUM_DIRECTIONS; i++) { + if (_sprites[i] == sprite) { + return true; + } + } + return false; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_sprite_set.h b/engines/wintermute/ad/ad_sprite_set.h new file mode 100644 index 0000000000..ba5da0ff2e --- /dev/null +++ b/engines/wintermute/ad/ad_sprite_set.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADSPRITESET_H +#define WINTERMUTE_ADSPRITESET_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class BaseSprite; +class AdSpriteSet : public BaseObject { +public: + bool containsSprite(BaseSprite *sprite); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + BaseSprite *getSprite(TDirection direction); + DECLARE_PERSISTENT(AdSpriteSet, BaseObject) + BaseObject *_owner; + AdSpriteSet(BaseGame *inGame, BaseObject *owner = NULL); + virtual ~AdSpriteSet(); + bool loadFile(const char *filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool loadBuffer(byte *buffer, bool complete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + BaseSprite *_sprites[NUM_DIRECTIONS]; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_def.cpp b/engines/wintermute/ad/ad_talk_def.cpp new file mode 100644 index 0000000000..a85cd7f986 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_def.cpp @@ -0,0 +1,285 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkDef, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkDef::AdTalkDef(BaseGame *inGame) : BaseObject(inGame) { + _defaultSpriteFilename = NULL; + _defaultSprite = NULL; + + _defaultSpriteSetFilename = NULL; + _defaultSpriteSet = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkDef::~AdTalkDef() { + for (uint32 i = 0; i < _nodes.size(); i++) { + delete _nodes[i]; + } + _nodes.clear(); + + delete[] _defaultSpriteFilename; + delete _defaultSprite; + _defaultSpriteFilename = NULL; + _defaultSprite = NULL; + + delete[] _defaultSpriteSetFilename; + delete _defaultSpriteSet; + _defaultSpriteSetFilename = NULL; + _defaultSpriteSet = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdTalkDef::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing TALK file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TALK) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(ACTION) +TOKEN_DEF(DEFAULT_SPRITESET_FILE) +TOKEN_DEF(DEFAULT_SPRITESET) +TOKEN_DEF(DEFAULT_SPRITE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TALK) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(ACTION) + TOKEN_TABLE(DEFAULT_SPRITESET_FILE) + TOKEN_TABLE(DEFAULT_SPRITESET) + TOKEN_TABLE(DEFAULT_SPRITE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TALK) { + _gameRef->LOG(0, "'TALK' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ACTION: { + AdTalkNode *node = new AdTalkNode(_gameRef); + if (node && DID_SUCCEED(node->loadBuffer(params, false))) { + _nodes.add(node); + } else { + delete node; + node = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + case TOKEN_DEFAULT_SPRITE: + BaseUtils::setString(&_defaultSpriteFilename, (char *)params); + break; + + case TOKEN_DEFAULT_SPRITESET_FILE: + BaseUtils::setString(&_defaultSpriteSetFilename, (char *)params); + break; + + case TOKEN_DEFAULT_SPRITESET: { + delete _defaultSpriteSet; + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadBuffer(params, false))) { + delete _defaultSpriteSet; + _defaultSpriteSet = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TALK definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading TALK definition"); + return STATUS_FAILED; + } + + delete _defaultSprite; + delete _defaultSpriteSet; + _defaultSprite = NULL; + _defaultSpriteSet = NULL; + + if (_defaultSpriteFilename) { + _defaultSprite = new BaseSprite(_gameRef); + if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) { + return STATUS_FAILED; + } + } + + if (_defaultSpriteSetFilename) { + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) { + return STATUS_FAILED; + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_defaultSprite)); + persistMgr->transfer(TMEMBER(_defaultSpriteFilename)); + persistMgr->transfer(TMEMBER(_defaultSpriteSet)); + persistMgr->transfer(TMEMBER(_defaultSpriteSetFilename)); + + _nodes.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "TALK {\n"); + if (_defaultSpriteFilename) { + buffer->putTextIndent(indent + 2, "DEFAULT_SPRITE=\"%s\"\n", _defaultSpriteFilename); + } + + if (_defaultSpriteSetFilename) { + buffer->putTextIndent(indent + 2, "DEFAULT_SPRITESET_FILE=\"%s\"\n", _defaultSpriteSetFilename); + } else if (_defaultSpriteSet) { + _defaultSpriteSet->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _nodes.size(); i++) { + _nodes[i]->saveAsText(buffer, indent + 2); + buffer->putTextIndent(indent, "\n"); + } + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkDef::loadDefaultSprite() { + if (_defaultSpriteFilename && !_defaultSprite) { + _defaultSprite = new BaseSprite(_gameRef); + if (!_defaultSprite || DID_FAIL(_defaultSprite->loadFile(_defaultSpriteFilename))) { + delete _defaultSprite; + _defaultSprite = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else if (_defaultSpriteSetFilename && !_defaultSpriteSet) { + _defaultSpriteSet = new AdSpriteSet(_gameRef); + if (!_defaultSpriteSet || DID_FAIL(_defaultSpriteSet->loadFile(_defaultSpriteSetFilename))) { + delete _defaultSpriteSet; + _defaultSpriteSet = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkDef::getDefaultSprite(TDirection dir) { + loadDefaultSprite(); + if (_defaultSprite) { + return _defaultSprite; + } else if (_defaultSpriteSet) { + return _defaultSpriteSet->getSprite(dir); + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_def.h b/engines/wintermute/ad/ad_talk_def.h new file mode 100644 index 0000000000..d147212775 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_def.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKDEF_H +#define WINTERMUTE_ADTALKDEF_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class AdTalkNode; +class AdSpriteSet; +class AdTalkDef : public BaseObject { +public: + char *_defaultSpriteSetFilename; + AdSpriteSet *_defaultSpriteSet; + BaseSprite *getDefaultSprite(TDirection Dir); + bool loadDefaultSprite(); + DECLARE_PERSISTENT(AdTalkDef, BaseObject) + + AdTalkDef(BaseGame *inGame); + virtual ~AdTalkDef(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + BaseArray<AdTalkNode *> _nodes; + char *_defaultSpriteFilename; + BaseSprite *_defaultSprite; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_holder.cpp b/engines/wintermute/ad/ad_talk_holder.cpp new file mode 100644 index 0000000000..cca4fdc2cb --- /dev/null +++ b/engines/wintermute/ad/ad_talk_holder.cpp @@ -0,0 +1,402 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkHolder, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkHolder::AdTalkHolder(BaseGame *inGame) : AdObject(inGame) { + _sprite = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkHolder::~AdTalkHolder() { + delete _sprite; + _sprite = NULL; + + for (uint32 i = 0; i < _talkSprites.size(); i++) { + delete _talkSprites[i]; + } + _talkSprites.clear(); + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); +} + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkHolder::getTalkStance(const char *stance) { + BaseSprite *ret = NULL; + + + // forced stance? + if (_forcedTalkAnimName && !_forcedTalkAnimUsed) { + _forcedTalkAnimUsed = true; + delete _animSprite; + _animSprite = new BaseSprite(_gameRef, this); + if (_animSprite) { + bool res = _animSprite->loadFile(_forcedTalkAnimName); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "AdTalkHolder::GetTalkStance: error loading talk sprite (object:\"%s\" sprite:\"%s\")", getName(), _forcedTalkAnimName); + delete _animSprite; + _animSprite = NULL; + } else { + return _animSprite; + } + } + } + + + if (stance != NULL) { + // search special talk stances + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getName(), stance) == 0) { + ret = _talkSpritesEx[i]; + break; + } + } + if (ret == NULL) { + // serach generic talk stances + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getName(), stance) == 0) { + ret = _talkSprites[i]; + break; + } + } + } + } + + // not a valid stance? get a random one + if (ret == NULL) { + if (_talkSprites.size() < 1) { + ret = _sprite; + } else { + // TODO: remember last + int rnd = BaseEngine::instance().randInt(0, _talkSprites.size() - 1); + ret = _talkSprites[rnd]; + } + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSprite + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSprite") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + + bool setCurrent = false; + if (_currentSprite && _currentSprite == _sprite) { + setCurrent = true; + } + + delete _sprite; + _sprite = NULL; + + if (val->isNULL()) { + _sprite = NULL; + if (setCurrent) { + _currentSprite = NULL; + } + stack->pushBool(true); + } else { + const char *filename = val->getString(); + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + script->runtimeError("SetSprite method failed for file '%s'", filename); + stack->pushBool(false); + } else { + _sprite = spr; + if (setCurrent) { + _currentSprite = _sprite; + } + stack->pushBool(true); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSprite") == 0) { + stack->correctParams(0); + + if (!_sprite || !_sprite->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_sprite->getFilename()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSpriteObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSpriteObject") == 0) { + stack->correctParams(0); + + if (!_sprite) { + stack->pushNULL(); + } else { + stack->pushNative(_sprite, true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("AddTalkSprite method failed for file '%s'", filename); + } else { + if (ex) { + _talkSpritesEx.add(spr); + } else { + _talkSprites.add(spr); + } + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + + bool setCurrent = false; + bool setTemp2 = false; + + if (ex) { + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (scumm_stricmp(_talkSpritesEx[i]->getFilename(), filename) == 0) { + if (_currentSprite == _talkSpritesEx[i]) { + setCurrent = true; + } + if (_tempSprite2 == _talkSpritesEx[i]) { + setTemp2 = true; + } + delete _talkSpritesEx[i]; + _talkSpritesEx.remove_at(i); + break; + } + } + } else { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (scumm_stricmp(_talkSprites[i]->getFilename(), filename) == 0) { + if (_currentSprite == _talkSprites[i]) { + setCurrent = true; + } + if (_tempSprite2 == _talkSprites[i]) { + setTemp2 = true; + } + delete _talkSprites[i]; + _talkSprites.remove_at(i); + break; + } + } + + } + + stack->pushBool(true); + if (setCurrent) { + _currentSprite = _sprite; + } + if (setTemp2) { + _tempSprite2 = _sprite; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetTalkSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetTalkSprite") == 0) { + stack->correctParams(2); + + const char *filename = stack->pop()->getString(); + bool ex = stack->pop()->getBool(); + bool setCurrent = false; + bool setTemp2 = false; + + BaseSprite *spr = new BaseSprite(_gameRef, this); + if (!spr || DID_FAIL(spr->loadFile(filename))) { + stack->pushBool(false); + script->runtimeError("SetTalkSprite method failed for file '%s'", filename); + } else { + + // delete current + if (ex) { + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (_talkSpritesEx[i] == _currentSprite) { + setCurrent = true; + } + if (_talkSpritesEx[i] == _tempSprite2) { + setTemp2 = true; + } + delete _talkSpritesEx[i]; + } + _talkSpritesEx.clear(); + } else { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (_talkSprites[i] == _currentSprite) { + setCurrent = true; + } + if (_talkSprites[i] == _tempSprite2) { + setTemp2 = true; + } + delete _talkSprites[i]; + } + _talkSprites.clear(); + } + + // set new + if (ex) { + _talkSpritesEx.add(spr); + } else { + _talkSprites.add(spr); + } + stack->pushBool(true); + + if (setCurrent) { + _currentSprite = spr; + } + if (setTemp2) { + _tempSprite2 = spr; + } + } + return STATUS_OK; + } else { + return AdObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdTalkHolder::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("talk-holder"); + return _scValue; + } else { + return AdObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Item + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Item")==0){ + SetItem(value->getString()); + return STATUS_OK; + } + + else*/ return AdObject::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +const char *AdTalkHolder::scToString() { + return "[talk-holder object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) { + for (uint32 i = 0; i < _talkSprites.size(); i++) { + if (_talkSprites[i]->getFilename()) { + buffer->putTextIndent(indent + 2, "TALK=\"%s\"\n", _talkSprites[i]->getFilename()); + } + } + + for (uint32 i = 0; i < _talkSpritesEx.size(); i++) { + if (_talkSpritesEx[i]->getFilename()) { + buffer->putTextIndent(indent + 2, "TALK_SPECIAL=\"%s\"\n", _talkSpritesEx[i]->getFilename()); + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkHolder::persist(BasePersistenceManager *persistMgr) { + AdObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_sprite)); + _talkSprites.persist(persistMgr); + _talkSpritesEx.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_holder.h b/engines/wintermute/ad/ad_talk_holder.h new file mode 100644 index 0000000000..d52ebf63c0 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_holder.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKHOLDER_H +#define WINTERMUTE_ADTALKHOLDER_H + +#include "engines/wintermute/ad/ad_object.h" + +namespace Wintermute { + +class AdTalkHolder : public AdObject { +public: + DECLARE_PERSISTENT(AdTalkHolder, AdObject) + virtual BaseSprite *getTalkStance(const char *stance); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + BaseSprite *_sprite; + BaseArray<BaseSprite *> _talkSprites; + BaseArray<BaseSprite *> _talkSpritesEx; + AdTalkHolder(BaseGame *inGame); + virtual ~AdTalkHolder(); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_talk_node.cpp b/engines/wintermute/ad/ad_talk_node.cpp new file mode 100644 index 0000000000..c909ee27ff --- /dev/null +++ b/engines/wintermute/ad/ad_talk_node.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdTalkNode, false) + +////////////////////////////////////////////////////////////////////////// +AdTalkNode::AdTalkNode(BaseGame *inGame) : BaseClass(inGame) { + _sprite = NULL; + _spriteFilename = NULL; + _spriteSet = NULL; + _spriteSetFilename = NULL; + _comment = NULL; + + _startTime = _endTime = 0; + _playToEnd = false; + _preCache = false; +} + + +////////////////////////////////////////////////////////////////////////// +AdTalkNode::~AdTalkNode() { + delete[] _spriteFilename; + delete _sprite; + delete[] _spriteSetFilename; + delete _spriteSet; + delete _comment; + _spriteFilename = NULL; + _sprite = NULL; + _spriteSetFilename = NULL; + _spriteSet = NULL; + _comment = NULL; +} + + + +TOKEN_DEF_START +TOKEN_DEF(ACTION) +TOKEN_DEF(SPRITESET_FILE) +TOKEN_DEF(SPRITESET) +TOKEN_DEF(SPRITE) +TOKEN_DEF(START_TIME) +TOKEN_DEF(END_TIME) +TOKEN_DEF(COMMENT) +TOKEN_DEF(PRECACHE) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ACTION) + TOKEN_TABLE(SPRITESET_FILE) + TOKEN_TABLE(SPRITESET) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(START_TIME) + TOKEN_TABLE(END_TIME) + TOKEN_TABLE(COMMENT) + TOKEN_TABLE(PRECACHE) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ACTION) { + _gameRef->LOG(0, "'ACTION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + _endTime = 0; + _playToEnd = false; + _preCache = false; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_SPRITE: + BaseUtils::setString(&_spriteFilename, (char *)params); + break; + + case TOKEN_SPRITESET_FILE: + BaseUtils::setString(&_spriteSetFilename, (char *)params); + break; + + case TOKEN_SPRITESET: { + delete _spriteSet; + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadBuffer(params, false))) { + delete _spriteSet; + _spriteSet = NULL; + cmd = PARSERR_GENERIC; + } + } + break; + + case TOKEN_START_TIME: + parser.scanStr((char *)params, "%d", &_startTime); + break; + + case TOKEN_END_TIME: + parser.scanStr((char *)params, "%d", &_endTime); + break; + + case TOKEN_PRECACHE: + parser.scanStr((char *)params, "%b", &_preCache); + break; + + case TOKEN_COMMENT: + if (_gameRef->_editorMode) { + BaseUtils::setString(&_comment, (char *)params); + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ACTION definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ACTION definition"); + return STATUS_FAILED; + } + + if (_endTime == 0) { + _playToEnd = true; + } else { + _playToEnd = false; + } + + if (_preCache && _spriteFilename) { + delete _sprite; + _sprite = new BaseSprite(_gameRef); + if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) { + return STATUS_FAILED; + } + } + + if (_preCache && _spriteSetFilename) { + delete _spriteSet; + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) { + return STATUS_FAILED; + } + } + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_comment)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_endTime)); + persistMgr->transfer(TMEMBER(_playToEnd)); + persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER(_spriteFilename)); + persistMgr->transfer(TMEMBER(_spriteSet)); + persistMgr->transfer(TMEMBER(_spriteSetFilename)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ACTION {\n"); + if (_comment) { + buffer->putTextIndent(indent + 2, "COMMENT=\"%s\"\n", _comment); + } + buffer->putTextIndent(indent + 2, "START_TIME=%d\n", _startTime); + if (!_playToEnd) { + buffer->putTextIndent(indent + 2, "END_TIME=%d\n", _endTime); + } + if (_spriteFilename) { + buffer->putTextIndent(indent + 2, "SPRITE=\"%s\"\n", _spriteFilename); + } + if (_spriteSetFilename) { + buffer->putTextIndent(indent + 2, "SPRITESET_FILE=\"%s\"\n", _spriteSetFilename); + } else if (_spriteSet) { + _spriteSet->saveAsText(buffer, indent + 2); + } + if (_preCache) { + buffer->putTextIndent(indent + 2, "PRECACHE=\"%s\"\n", _preCache ? "TRUE" : "FALSE"); + } + + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::loadSprite() { + if (_spriteFilename && !_sprite) { + _sprite = new BaseSprite(_gameRef); + if (!_sprite || DID_FAIL(_sprite->loadFile(_spriteFilename))) { + delete _sprite; + _sprite = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else if (_spriteSetFilename && !_spriteSet) { + _spriteSet = new AdSpriteSet(_gameRef); + if (!_spriteSet || DID_FAIL(_spriteSet->loadFile(_spriteSetFilename))) { + delete _spriteSet; + _spriteSet = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdTalkNode::isInTimeInterval(uint32 time, TDirection dir) { + if (time >= _startTime) { + if (_playToEnd) { + if ((_spriteFilename && _sprite == NULL) || (_sprite && _sprite->isFinished() == false)) { + return true; + } else if ((_spriteSetFilename && _spriteSet == NULL) || (_spriteSet && _spriteSet->getSprite(dir) && _spriteSet->getSprite(dir)->isFinished() == false)) { + return true; + } else { + return false; + } + } else { + return _endTime >= time; + } + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +BaseSprite *AdTalkNode::getSprite(TDirection dir) { + loadSprite(); + if (_sprite) { + return _sprite; + } else if (_spriteSet) { + return _spriteSet->getSprite(dir); + } else { + return NULL; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_talk_node.h b/engines/wintermute/ad/ad_talk_node.h new file mode 100644 index 0000000000..7dfd861f85 --- /dev/null +++ b/engines/wintermute/ad/ad_talk_node.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTALKNODE_H +#define WINTERMUTE_ADTALKNODE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class AdSpriteSet; +class BaseSprite; +class AdTalkNode : public BaseClass { +public: + char *_spriteSetFilename; + AdSpriteSet *_spriteSet; + BaseSprite *getSprite(TDirection dir); + bool isInTimeInterval(uint32 time, TDirection dir); + bool loadSprite(); + DECLARE_PERSISTENT(AdTalkNode, BaseClass) + + AdTalkNode(BaseGame *inGame); + virtual ~AdTalkNode(); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + char *_spriteFilename; + BaseSprite *_sprite; + uint32 _startTime; + uint32 _endTime; + bool _playToEnd; + bool _preCache; + char *_comment; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_types.h b/engines/wintermute/ad/ad_types.h new file mode 100644 index 0000000000..ae5882f4ee --- /dev/null +++ b/engines/wintermute/ad/ad_types.h @@ -0,0 +1,107 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADTYPES_H +#define WINTERMUTE_ADTYPES_H + +namespace Wintermute { + +typedef enum { + GAME_NORMAL, + GAME_WAITING_RESPONSE +} TGameStateEx; + +typedef enum { + OBJECT_ENTITY, + OBJECT_REGION, + OBJECT_ACTOR, + OBJECT_NONE +} TObjectType; + +typedef enum { + ENTITY_NORMAL, + ENTITY_SOUND +} TEntityType; + +typedef enum { + STATE_NONE, + STATE_IDLE, + STATE_PLAYING_ANIM, + STATE_READY, + STATE_FOLLOWING_PATH, + STATE_SEARCHING_PATH, + STATE_WAITING_PATH, + STATE_TURNING_LEFT, + STATE_TURNING_RIGHT, + STATE_TURNING, + STATE_TALKING, + STATE_DIRECT_CONTROL, + STATE_PLAYING_ANIM_SET +} TObjectState; + +typedef enum { + DIRECT_WALK_NONE, + DIRECT_WALK_FW, + DIRECT_WALK_BK +} TDirectWalkMode; + +typedef enum { + DIRECT_TURN_NONE, + DIRECT_TURN_CW, + DIRECT_TURN_CCW +} TDirectTurnMode; + +typedef enum { + RESPONSE_TEXT, + RESPONSE_ICON +} TResponseStyle; + +typedef enum { + RESPONSE_ALWAYS, + RESPONSE_ONCE, + RESPONSE_ONCE_GAME +} TResponseType; + + +typedef enum { + TALK_SKIP_LEFT = 0, + TALK_SKIP_RIGHT = 1, + TALK_SKIP_BOTH = 2, + TALK_SKIP_NONE = 3 +} TTalkSkipButton; + +typedef enum { + GEOM_WAYPOINT, + GEOM_WALKPLANE, + GEOM_BLOCKED, + GEOM_GENERIC +} TGeomNodeType; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ad/ad_waypoint_group.cpp b/engines/wintermute/ad/ad_waypoint_group.cpp new file mode 100644 index 0000000000..81493ce769 --- /dev/null +++ b/engines/wintermute/ad/ad_waypoint_group.cpp @@ -0,0 +1,270 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(AdWaypointGroup, false) + +////////////////////////////////////////////////////////////////////////// +AdWaypointGroup::AdWaypointGroup(BaseGame *inGame) : BaseObject(inGame) { + _active = true; + _editorSelectedPoint = -1; + _lastMimicScale = -1; + _lastMimicX = _lastMimicY = INT_MIN; +} + + +////////////////////////////////////////////////////////////////////////// +AdWaypointGroup::~AdWaypointGroup() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void AdWaypointGroup::cleanup() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + _editorSelectedPoint = -1; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "AdWaypointGroup::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing WAYPOINTS file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(WAYPOINTS) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(POINT) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(WAYPOINTS) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(POINT) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_WAYPOINTS) { + _gameRef->LOG(0, "'WAYPOINTS' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr((char *)params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in WAYPOINTS definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "WAYPOINTS {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint); + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + BaseClass::saveAsText(buffer, indent + 2); + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + buffer->putTextIndent(indent, "}\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_editorSelectedPoint)); + persistMgr->transfer(TMEMBER(_lastMimicScale)); + persistMgr->transfer(TMEMBER(_lastMimicX)); + persistMgr->transfer(TMEMBER(_lastMimicY)); + _points.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *AdWaypointGroup::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("waypoint-group"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } + + else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool AdWaypointGroup::mimic(AdWaypointGroup *wpt, float scale, int argX, int argY) { + if (scale == _lastMimicScale && argX == _lastMimicX && argY == _lastMimicY) { + return STATUS_OK; + } + + cleanup(); + + for (uint32 i = 0; i < wpt->_points.size(); i++) { + int x = (int)((float)wpt->_points[i]->x * scale / 100.0f); + int y = (int)((float)wpt->_points[i]->y * scale / 100.0f); + + _points.add(new BasePoint(x + argX, y + argY)); + } + + _lastMimicScale = scale; + _lastMimicX = argX; + _lastMimicY = argY; + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ad/ad_waypoint_group.h b/engines/wintermute/ad/ad_waypoint_group.h new file mode 100644 index 0000000000..13d6bbadd7 --- /dev/null +++ b/engines/wintermute/ad/ad_waypoint_group.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_ADWAYPOINTGROUP_H +#define WINTERMUTE_ADWAYPOINTGROUP_H + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { +class BasePoint; +class AdWaypointGroup : public BaseObject { +public: + float _lastMimicScale; + int _lastMimicX; + int _lastMimicY; + void cleanup(); + bool mimic(AdWaypointGroup *wpt, float scale = 100.0f, int x = 0, int y = 0); + DECLARE_PERSISTENT(AdWaypointGroup, BaseObject) + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + bool _active; + AdWaypointGroup(BaseGame *inGame); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual ~AdWaypointGroup(); + BaseArray<BasePoint *> _points; + int _editorSelectedPoint; + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base.cpp b/engines/wintermute/base/base.cpp new file mode 100644 index 0000000000..e351792e61 --- /dev/null +++ b/engines/wintermute/base/base.cpp @@ -0,0 +1,185 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseClass::BaseClass(BaseGame *gameOwner) { + _gameRef = gameOwner; + _persistable = true; +} + + +////////////////////////////////////////////////////////////////////////// +BaseClass::BaseClass() { + _gameRef = NULL; + _persistable = true; +} + + +////////////////////////////////////////////////////////////////////// +BaseClass::~BaseClass() { + _editorProps.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +Common::String BaseClass::getEditorProp(const Common::String &propName, const Common::String &initVal) { + _editorPropsIter = _editorProps.find(propName); + if (_editorPropsIter != _editorProps.end()) { + return _editorPropsIter->_value.c_str(); + } else { + return initVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseClass::setEditorProp(const Common::String &propName, const Common::String &propValue) { + if (propName.size() == 0) { + return STATUS_FAILED; + } + + if (propValue.size() == 0) { + _editorProps.erase(propName); + } else { + _editorProps[propName] = propValue; + } + return STATUS_OK; +} + + + +TOKEN_DEF_START +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(NAME) +TOKEN_DEF(VALUE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseClass::parseEditorProperty(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(VALUE) + TOKEN_TABLE_END + + + if (!_gameRef->_editorMode) { + return STATUS_OK; + } + + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_EDITOR_PROPERTY) { + _gameRef->LOG(0, "'EDITOR_PROPERTY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + char *propName = NULL; + char *propValue = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_NAME: + delete[] propName; + propName = new char[strlen((char *)params) + 1]; + if (propName) { + strcpy(propName, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_VALUE: + delete[] propValue; + propValue = new char[strlen((char *)params) + 1]; + if (propValue) { + strcpy(propValue, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Syntax error in EDITOR_PROPERTY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Error loading EDITOR_PROPERTY definition"); + return STATUS_FAILED; + } + + + setEditorProp(propName, propValue); + + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseClass::saveAsText(BaseDynamicBuffer *buffer, int indent) { + _editorPropsIter = _editorProps.begin(); + while (_editorPropsIter != _editorProps.end()) { + buffer->putTextIndent(indent, "EDITOR_PROPERTY\n"); + buffer->putTextIndent(indent, "{\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _editorPropsIter->_key.c_str()); + buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _editorPropsIter->_value.c_str()); + buffer->putTextIndent(indent, "}\n\n"); + + _editorPropsIter++; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base.h b/engines/wintermute/base/base.h new file mode 100644 index 0000000000..24820c748a --- /dev/null +++ b/engines/wintermute/base/base.h @@ -0,0 +1,62 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_BASE_H +#define WINTERMUTE_BASE_BASE_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/dctypes.h" +#include "common/str.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + +namespace Wintermute { + +class BaseGame; +class BaseDynamicBuffer; + +class BaseClass { +public: + bool _persistable; + bool setEditorProp(const Common::String &propName, const Common::String &propValue); + Common::String getEditorProp(const Common::String &propName, const Common::String &initVal = NULL); + BaseClass(TDynamicConstructor, TDynamicConstructor) {} + bool parseEditorProperty(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent = 0); + BaseClass(); + BaseClass(BaseGame *GameOwner); + virtual ~BaseClass(); + BaseGame *_gameRef; +protected: + Common::HashMap<Common::String, Common::String> _editorProps; + Common::HashMap<Common::String, Common::String>::iterator _editorPropsIter; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_active_rect.cpp b/engines/wintermute/base/base_active_rect.cpp new file mode 100644 index 0000000000..4addf15be8 --- /dev/null +++ b/engines/wintermute/base/base_active_rect.cpp @@ -0,0 +1,111 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame) : BaseClass(inGame) { + BasePlatform::setRectEmpty(&_rect); + _owner = NULL; + _frame = NULL; + _region = NULL; + _zoomX = 100; + _zoomY = 100; + _offsetX = _offsetY = 0; + clipRect(); +} + + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX, float zoomY, bool precise) : BaseClass(inGame) { + _owner = owner; + _frame = frame; + BasePlatform::setRect(&_rect, x, y, x + width, y + height); + _zoomX = zoomX; + _zoomY = zoomY; + _precise = precise; + _region = NULL; + _offsetX = _offsetY = 0; + clipRect(); +} + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY) : BaseClass(inGame) { + _owner = owner; + _region = region; + BasePlatform::copyRect(&_rect, ®ion->_rect); + _rect.offsetRect(-offsetX, -offsetY); + _zoomX = 100; + _zoomY = 100; + _precise = true; + _frame = NULL; + clipRect(); + _offsetX = offsetX; + _offsetY = offsetY; +} + + +////////////////////////////////////////////////////////////////////// +BaseActiveRect::~BaseActiveRect() { + _owner = NULL; + _frame = NULL; + _region = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseActiveRect::clipRect() { + Rect32 rc; + bool customViewport; + _gameRef->getCurrentViewportRect(&rc, &customViewport); + BaseRenderer *Rend = _gameRef->_renderer; + + if (!customViewport) { + rc.left -= Rend->_drawOffsetX; + rc.right -= Rend->_drawOffsetX; + rc.top -= Rend->_drawOffsetY; + rc.bottom -= Rend->_drawOffsetY; + } + + if (rc.left > _rect.left) { + _offsetX = rc.left - _rect.left; + } + if (rc.top > _rect.top) { + _offsetY = rc.top - _rect.top; + } + + BasePlatform::intersectRect(&_rect, &_rect, &rc); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_active_rect.h b/engines/wintermute/base/base_active_rect.h new file mode 100644 index 0000000000..fcd2619b03 --- /dev/null +++ b/engines/wintermute/base/base_active_rect.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_ACTIVE_RECT_H +#define WINTERMUTE_BASE_ACTIVE_RECT_H + +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BaseRegion; +class BaseSubFrame; +class BaseObject; +class BaseActiveRect: BaseClass { +public: + void clipRect(); + bool _precise; + float _zoomX; + float _zoomY; + BaseSubFrame *_frame; + BaseObject *_owner; + BaseRegion *_region; + int _offsetX; + int _offsetY; + Rect32 _rect; + BaseActiveRect(BaseGame *inGameOwner = NULL); + BaseActiveRect(BaseGame *inGameOwner, BaseObject *owner, BaseSubFrame *frame, int x, int y, int width, int height, float zoomX = 100, float zoomY = 100, bool precise = true); + BaseActiveRect(BaseGame *inGame, BaseObject *owner, BaseRegion *region, int offsetX, int offsetY); + virtual ~BaseActiveRect(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_dynamic_buffer.cpp b/engines/wintermute/base/base_dynamic_buffer.cpp new file mode 100644 index 0000000000..fc48e93c2b --- /dev/null +++ b/engines/wintermute/base/base_dynamic_buffer.cpp @@ -0,0 +1,204 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseDynamicBuffer::BaseDynamicBuffer(BaseGame *inGame, uint32 initSize, uint32 growBy) { + _buffer = NULL; + _size = 0; + _realSize = 0; + + _offset = 0; + _initSize = initSize; + _growBy = growBy; + + _initialized = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseDynamicBuffer::~BaseDynamicBuffer() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::cleanup() { + if (_buffer) { + free(_buffer); + } + _buffer = NULL; + _size = 0; + _realSize = 0; + _offset = 0; + _initialized = false; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseDynamicBuffer::getSize() { + return _size; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::init(uint32 initSize) { + cleanup(); + + if (initSize == 0) { + initSize = _initSize; + } + + _buffer = (byte *)malloc(initSize); + if (!_buffer) { + BaseEngine::LOG(0, "BaseDynamicBuffer::Init - Error allocating %d bytes", initSize); + return STATUS_FAILED; + } + + _realSize = initSize; + _initialized = true; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::putBytes(const byte *buffer, uint32 size) { + if (!_initialized) { + init(); + } + + while (_offset + size > _realSize) { + _realSize += _growBy; + _buffer = (byte *)realloc(_buffer, _realSize); + if (!_buffer) { + BaseEngine::LOG(0, "BaseDynamicBuffer::PutBytes - Error reallocating buffer to %d bytes", _realSize); + return STATUS_FAILED; + } + } + + memcpy(_buffer + _offset, buffer, size); + _offset += size; + _size += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseDynamicBuffer::getBytes(byte *buffer, uint32 size) { + if (!_initialized) { + init(); + } + + if (_offset + size > _size) { + BaseEngine::LOG(0, "BaseDynamicBuffer::GetBytes - Buffer underflow"); + return STATUS_FAILED; + } + + memcpy(buffer, _buffer + _offset, size); + _offset += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putDWORD(uint32 val) { + putBytes((byte *)&val, sizeof(uint32)); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseDynamicBuffer::getDWORD() { + uint32 ret; + getBytes((byte *)&ret, sizeof(uint32)); + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putString(const char *val) { + if (!val) { + putString("(null)"); + } else { + putDWORD(strlen(val) + 1); + putBytes((const byte *)val, strlen(val) + 1); + } +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseDynamicBuffer::getString() { + uint32 len = getDWORD(); + char *ret = (char *)(_buffer + _offset); + _offset += len; + + if (!strcmp(ret, "(null)")) { + return NULL; + } else { + return ret; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putText(const char *fmt, ...) { + va_list va; + + va_start(va, fmt); + putTextForm(fmt, va); + va_end(va); + +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putTextIndent(int indent, const char *fmt, ...) { + va_list va; + + putText("%*s", indent, ""); + + va_start(va, fmt); + putTextForm(fmt, va); + va_end(va); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseDynamicBuffer::putTextForm(const char *format, va_list argptr) { + char buff[32768]; + vsprintf(buff, format, argptr); + putBytes((byte *)buff, strlen(buff)); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_dynamic_buffer.h b/engines/wintermute/base/base_dynamic_buffer.h new file mode 100644 index 0000000000..2d1a7fbe48 --- /dev/null +++ b/engines/wintermute/base/base_dynamic_buffer.h @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_DYNAMIC_BUFFER_H +#define WINTERMUTE_BASE_DYNAMIC_BUFFER_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseDynamicBuffer { +public: + void putText(const char *fmt, ...); + void putTextIndent(int indent, const char *fmt, ...); + uint32 getDWORD(); + void putDWORD(uint32 val); + char *getString(); + void putString(const char *val); + bool getBytes(byte *buffer, uint32 size); + bool putBytes(const byte *buffer, uint32 size); + uint32 getSize(); + bool init(uint32 initSize = 0); + void cleanup(); + BaseDynamicBuffer(BaseGame *inGame, uint32 initSize = 1000, uint32 growBy = 1000); + virtual ~BaseDynamicBuffer(); + +private: + uint32 _size; + byte *_buffer; + bool _initialized; + uint32 _realSize; + uint32 _growBy; + uint32 _initSize; + uint32 _offset; + void putTextForm(const char *format, va_list argptr); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp new file mode 100644 index 0000000000..8146d14beb --- /dev/null +++ b/engines/wintermute/base/base_engine.cpp @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "common/system.h" +namespace Common { +DECLARE_SINGLETON(Wintermute::BaseEngine); +} + +namespace Wintermute { + +BaseEngine::BaseEngine() { + _fileManager = NULL; + _gameRef = NULL; + _classReg = NULL; + _rnd = NULL; + _gameId = ""; +} + +void BaseEngine::init(Common::Language lang) { + _fileManager = new BaseFileManager(lang); + // Don't forget to register your random source + _rnd = new Common::RandomSource("Wintermute"); + _classReg = new SystemClassRegistry(); + _classReg->registerClasses(); +} + +BaseEngine::~BaseEngine() { + delete _fileManager; + delete _rnd; + delete _classReg; +} + +void BaseEngine::createInstance(const Common::String &gameid, Common::Language lang) { + instance()._gameId = gameid; + instance().init(lang); +} + +void BaseEngine::LOG(bool res, const char *fmt, ...) { + uint32 secs = g_system->getMillis() / 1000; + uint32 hours = secs / 3600; + secs = secs % 3600; + uint32 mins = secs / 60; + secs = secs % 60; + + char buff[512]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + if (instance()._gameRef) { + instance()._gameRef->LOG("%s", buff); + } else { + debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + } +} + +uint32 BaseEngine::randInt(int from, int to) { + return _rnd->getRandomNumberRng(from, to); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h new file mode 100644 index 0000000000..1ed0e3ab01 --- /dev/null +++ b/engines/wintermute/base/base_engine.h @@ -0,0 +1,69 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_ENGINE_H +#define WINTERMUTE_BASE_ENGINE_H + +#include "common/str.h" +#include "common/singleton.h" +#include "common/random.h" +#include "common/language.h" + +namespace Wintermute { + +class BaseFileManager; +class BaseRegistry; +class BaseGame; +class SystemClassRegistry; +class BaseEngine : public Common::Singleton<Wintermute::BaseEngine> { + void init(Common::Language lang); + BaseFileManager *_fileManager; + Common::String _gameId; + BaseGame *_gameRef; + // We need random numbers + Common::RandomSource *_rnd; + SystemClassRegistry *_classReg; +public: + BaseEngine(); + ~BaseEngine(); + static void createInstance(const Common::String &gameid, Common::Language lang); + void setGameRef(BaseGame *gameRef) { _gameRef = gameRef; } + + Common::RandomSource *getRandomSource() { return _rnd; } + uint32 randInt(int from, int to); + + SystemClassRegistry *getClassRegistry(){ return _classReg; } + BaseGame *getGameRef() { return _gameRef; } + BaseFileManager *getFileManager() { return _fileManager; } + static void LOG(bool res, const char *fmt, ...); + const char *getGameId() { return _gameId.c_str(); } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_fader.cpp b/engines/wintermute/base/base_fader.cpp new file mode 100644 index 0000000000..985718fcab --- /dev/null +++ b/engines/wintermute/base/base_fader.cpp @@ -0,0 +1,195 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "common/util.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFader, false) + +////////////////////////////////////////////////////////////////////////// +BaseFader::BaseFader(BaseGame *inGame) : BaseObject(inGame) { + _active = false; + _red = _green = _blue = 0; + _currentAlpha = 0x00; + _sourceAlpha = 0; + _targetAlpha = 0; + _duration = 1000; + _startTime = 0; + _system = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFader::~BaseFader() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::update() { + if (!_active) { + return STATUS_OK; + } + + int alphaDelta = _targetAlpha - _sourceAlpha; + + uint32 time; + + if (_system) { + time = g_system->getMillis() - _startTime; + } else { + time = _gameRef->_timer - _startTime; + } + + if (time >= _duration) { + _currentAlpha = _targetAlpha; + } else { + _currentAlpha = (byte)(_sourceAlpha + (float)time / (float)_duration * alphaDelta); + } + _currentAlpha = MIN((unsigned char)255, MAX(_currentAlpha, (byte)0)); // TODO: clean + + _ready = time >= _duration; + if (_ready && _currentAlpha == 0x00) { + _active = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::display() { + if (!_active) { + return STATUS_OK; + } + + if (_currentAlpha > 0x00) { + _gameRef->_renderer->fadeToColor(_red, _green, _blue, _currentAlpha); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::deactivate() { + _active = false; + _ready = true; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::fadeIn(uint32 sourceColor, uint32 duration, bool system) { + _ready = false; + _active = true; + + _red = RGBCOLGetR(sourceColor); + _green = RGBCOLGetG(sourceColor); + _blue = RGBCOLGetB(sourceColor); + + _sourceAlpha = RGBCOLGetA(sourceColor); + _targetAlpha = 0; + + _duration = duration; + _system = system; + + if (_system) { + _startTime = g_system->getMillis(); + } else { + _startTime = _gameRef->_timer; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::fadeOut(uint32 targetColor, uint32 duration, bool system) { + _ready = false; + _active = true; + + _red = RGBCOLGetR(targetColor); + _green = RGBCOLGetG(targetColor); + _blue = RGBCOLGetB(targetColor); + + //_sourceAlpha = 0; + _sourceAlpha = _currentAlpha; + _targetAlpha = RGBCOLGetA(targetColor); + + _duration = duration; + _system = system; + + if (_system) { + _startTime = g_system->getMillis(); + } else { + _startTime = _gameRef->_timer; + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseFader::getCurrentColor() { + return BYTETORGBA(_red, _green, _blue, _currentAlpha); +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseFader::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_blue)); + persistMgr->transfer(TMEMBER(_currentAlpha)); + persistMgr->transfer(TMEMBER(_duration)); + persistMgr->transfer(TMEMBER(_green)); + persistMgr->transfer(TMEMBER(_red)); + persistMgr->transfer(TMEMBER(_sourceAlpha)); + persistMgr->transfer(TMEMBER(_startTime)); + persistMgr->transfer(TMEMBER(_targetAlpha)); + persistMgr->transfer(TMEMBER(_system)); + + if (_system && !persistMgr->getIsSaving()) { + _startTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_fader.h b/engines/wintermute/base/base_fader.h new file mode 100644 index 0000000000..116c8c963d --- /dev/null +++ b/engines/wintermute/base/base_fader.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FADER_H +#define WINTERMUTE_BASE_FADER_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class BaseFader : public BaseObject { +public: + uint32 getCurrentColor(); + bool fadeOut(uint32 targetColor, uint32 duration, bool system = false); + bool fadeIn(uint32 sourceColor, uint32 duration, bool system = false); + bool deactivate(); + bool display(); + bool update(); + DECLARE_PERSISTENT(BaseFader, BaseObject) + BaseFader(BaseGame *inGame); + virtual ~BaseFader(); +private: + bool _system; + bool _active; + byte _red; + byte _green; + byte _blue; + byte _currentAlpha; + byte _targetAlpha; + byte _sourceAlpha; + uint32 _duration; + uint32 _startTime; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_file_manager.cpp b/engines/wintermute/base/base_file_manager.cpp new file mode 100644 index 0000000000..b726c0c66f --- /dev/null +++ b/engines/wintermute/base/base_file_manager.cpp @@ -0,0 +1,340 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/file/base_disk_file.h" +#include "engines/wintermute/base/file/base_save_thumb_file.h" +#include "engines/wintermute/base/file/base_package.h" +#include "engines/wintermute/base/file/base_resources.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/wintermute.h" +#include "common/debug.h" +#include "common/str.h" +#include "common/tokenizer.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "common/config-manager.h" +#include "common/system.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/savefile.h" +#include "common/fs.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// +BaseFileManager::BaseFileManager(Common::Language lang) { + _language = lang; + initPaths(); + registerPackages(); +} + +////////////////////////////////////////////////////////////////////// +BaseFileManager::~BaseFileManager() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::cleanup() { + // delete registered paths + _packagePaths.clear(); + + // close open files + for (uint32 i = 0; i < _openFiles.size(); i++) { + delete _openFiles[i]; + } + _openFiles.clear(); + + // delete packages + _packages.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////// +byte *BaseFileManager::readWholeFile(const Common::String &filename, uint32 *size, bool mustExist) { + byte *buffer = NULL; + + Common::SeekableReadStream *file = openFile(filename); + if (!file) { + if (mustExist) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error opening file '%s'", filename.c_str()); + } + return NULL; + } + + buffer = new byte[file->size() + 1]; + if (buffer == NULL) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error allocating buffer for file '%s' (%d bytes)", filename.c_str(), file->size() + 1); + closeFile(file); + return NULL; + } + + if (file->read(buffer, (uint32)file->size()) != (uint32)file->size()) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Error reading file '%s'", filename.c_str()); + closeFile(file); + delete[] buffer; + return NULL; + }; + + buffer[file->size()] = '\0'; + if (size != NULL) { + *size = file->size(); + } + closeFile(file); + + return buffer; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::addPath(TPathType type, const Common::FSNode &path) { + if (!path.exists()) { + return STATUS_FAILED; + } + + switch (type) { + case PATH_SINGLE: + // _singlePaths.push_back(path); + error("TODO: Allow adding single-paths"); + break; + case PATH_PACKAGE: + _packagePaths.push_back(path); + break; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::reloadPaths() { + // delete registered paths + //_singlePaths.clear(); + _packagePaths.clear(); + + return initPaths(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::initPaths() { + // Removed: Config-based file-path choice. + + // package files paths + const Common::FSNode gameData(ConfMan.get("path")); + addPath(PATH_PACKAGE, gameData); + + Common::FSNode dataSubFolder = gameData.getChild("data"); + if (dataSubFolder.exists()) { + addPath(PATH_PACKAGE, dataSubFolder); + } + Common::FSNode languageSubFolder = gameData.getChild("language"); + if (languageSubFolder.exists()) { + addPath(PATH_PACKAGE, languageSubFolder); + } + return STATUS_OK; +} + +bool BaseFileManager::registerPackages(const Common::FSList &fslist) { + for (Common::FSList::const_iterator it = fslist.begin(); it != fslist.end(); ++it) { + debugC(kWintermuteDebugFileAccess, "Adding %s", (*it).getName().c_str()); + if ((*it).getName().contains(".dcp")) { + if (registerPackage((*it))) { + addPath(PATH_PACKAGE, (*it)); + } + } + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::registerPackages() { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, "Scanning packages"); + + // Register without using SearchMan, as otherwise the FSNode-based lookup in openPackage will fail + // and that has to be like that to support the detection-scheme. + Common::FSList files; + for (Common::FSList::iterator it = _packagePaths.begin(); it != _packagePaths.end(); ++it) { + debugC(kWintermuteDebugFileAccess, "Should register folder: %s %s", (*it).getPath().c_str(), (*it).getName().c_str()); + (*it).getChildren(files, Common::FSNode::kListFilesOnly); + for (Common::FSList::iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt) { + if (!fileIt->getName().hasSuffix(".dcp")) { + continue; + } + // Avoid registering all the language files + // TODO: Select based on the gameDesc. + if (_language != Common::UNK_LANG && fileIt->getParent().getName() == "language") { + Common::String parentName = fileIt->getParent().getName(); + Common::String dcpName = fileIt->getName(); + if (_language == Common::EN_ANY && fileIt->getName() != "english.dcp") { + continue; + } else if (_language == Common::CZ_CZE && fileIt->getName() != "czech.dcp") { + continue; + } else if (_language == Common::IT_ITA && fileIt->getName() != "italian.dcp") { + continue; + } else if (_language == Common::PL_POL && fileIt->getName() != "polish.dcp") { + continue; + } else if (_language == Common::RU_RUS && fileIt->getName() != "russian.dcp") { + continue; + } + } + debugC(kWintermuteDebugFileAccess, "Registering %s %s", (*fileIt).getPath().c_str(), (*fileIt).getName().c_str()); + registerPackage((*fileIt)); + } + } + +// debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Registered %d files in %d package(s)", _files.size(), _packages.size()); + + return STATUS_OK; +} + +bool BaseFileManager::registerPackage(Common::FSNode file, const Common::String &filename, bool searchSignature) { + PackageSet *pack = new PackageSet(file, filename, searchSignature); + _packages.add(file.getName(), pack, pack->getPriority() , true); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openPkgFile(const Common::String &filename) { + Common::String upcName = filename; + upcName.toUppercase(); + Common::SeekableReadStream *file = NULL; + char fileName[MAX_PATH_LENGTH]; + strcpy(fileName, upcName.c_str()); + + // correct slashes + for (uint32 i = 0; i < upcName.size(); i++) { + if (upcName[(int32)i] == '/') { + upcName.setChar('\\', (uint32)i); + } + } + Common::ArchiveMemberPtr entry = _packages.getMember(upcName); + if (!entry) { + return NULL; + } + file = entry->createReadStream(); + return file; +} + +bool BaseFileManager::hasFile(const Common::String &filename) { + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) { + BasePersistenceManager pm(BaseEngine::instance().getGameId()); + if (filename.size() <= 9) { + return false; + } + int slot = atoi(filename.c_str() + 9); + return pm.getSaveExists(slot); + } + if (diskFileExists(filename)) { + return true; + } + if (_packages.hasFile(filename)) { + return true; // We don't bother checking if the file can actually be opened, something bigger is wrong if that is the case. + } + if (BaseResources::hasFile(filename)) { + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openFile(const Common::String &filename, bool absPathWarning, bool keepTrackOf) { + if (strcmp(filename.c_str(), "") == 0) { + return NULL; + } + debugC(kWintermuteDebugFileAccess, "Open file %s", filename.c_str()); + + Common::SeekableReadStream *file = openFileRaw(filename); + if (file && keepTrackOf) { + _openFiles.push_back(file); + } + return file; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFileManager::closeFile(Common::SeekableReadStream *File) { + for (uint32 i = 0; i < _openFiles.size(); i++) { + if (_openFiles[i] == File) { + delete _openFiles[i]; + _openFiles.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +Common::SeekableReadStream *BaseFileManager::openFileRaw(const Common::String &filename) { + Common::SeekableReadStream *ret = NULL; + + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) == 0) { + if (!BaseEngine::instance().getGameRef()) { + error("Attempt to load filename: %s without BaseEngine-object, this is unsupported", filename.c_str()); + } + BaseSaveThumbFile *saveThumbFile = new BaseSaveThumbFile(); + if (DID_SUCCEED(saveThumbFile->open(filename))) { + ret = saveThumbFile->getMemStream(); + } + delete saveThumbFile; + return ret; + } + + ret = openDiskFile(filename); + if (ret) { + return ret; + } + + ret = openPkgFile(filename); + if (ret) { + return ret; + } + + ret = BaseResources::getFile(filename); + if (ret) { + return ret; + } + + debugC(kWintermuteDebugFileAccess ,"BFileManager::OpenFileRaw - Failed to open %s", filename.c_str()); + return NULL; +} + +BaseFileManager *BaseFileManager::getEngineInstance() { + if (BaseEngine::instance().getFileManager()) { + return BaseEngine::instance().getFileManager(); + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_file_manager.h b/engines/wintermute/base/base_file_manager.h new file mode 100644 index 0000000000..70aff49bbb --- /dev/null +++ b/engines/wintermute/base/base_file_manager.h @@ -0,0 +1,76 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILE_MANAGER_H +#define WINTERMUTE_BASE_FILE_MANAGER_H + +#include "common/archive.h" +#include "common/str.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/language.h" + +namespace Wintermute { +class BaseFileManager { +public: + bool cleanup(); + + bool closeFile(Common::SeekableReadStream *File); + bool hasFile(const Common::String &filename); + Common::SeekableReadStream *openFile(const Common::String &filename, bool absPathWarning = true, bool keepTrackOf = true); + byte *readWholeFile(const Common::String &filename, uint32 *size = NULL, bool mustExist = true); + + BaseFileManager(Common::Language lang); + virtual ~BaseFileManager(); + // Used only for detection + bool registerPackages(const Common::FSList &fslist); + static BaseFileManager *getEngineInstance(); +private: + typedef enum { + PATH_PACKAGE, + PATH_SINGLE + } TPathType; + bool reloadPaths(); + bool initPaths(); + bool addPath(TPathType type, const Common::FSNode &path); + bool registerPackages(); + Common::SeekableReadStream *openFileRaw(const Common::String &filename); + Common::SeekableReadStream *openPkgFile(const Common::String &filename); + Common::FSList _packagePaths; + bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset); + bool registerPackage(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); + Common::SearchSet _packages; + Common::Array<Common::SeekableReadStream *> _openFiles; + Common::Language _language; + // This class is intentionally not a subclass of Base, as it needs to be used by + // the detector too, without launching the entire engine: +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_frame.cpp b/engines/wintermute/base/base_frame.cpp new file mode 100644 index 0000000000..7c64144480 --- /dev/null +++ b/engines/wintermute/base/base_frame.cpp @@ -0,0 +1,766 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/str.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseFrame, false) + +////////////////////////////////////////////////////////////////////// +BaseFrame::BaseFrame(BaseGame *inGame) : BaseScriptable(inGame, true) { + _delay = 0; + _moveX = _moveY = 0; + + _sound = NULL; + _killSound = false; + + _editorExpanded = false; + _keyframe = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseFrame::~BaseFrame() { + delete _sound; + _sound = NULL; + + for (uint32 i = 0; i < _subframes.size(); i++) { + delete _subframes[i]; + } + _subframes.clear(); + + for (uint32 i = 0; i < _applyEvent.size(); i++) { + delete[] _applyEvent[i]; + _applyEvent[i] = NULL; + } + _applyEvent.clear(); +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, bool allFrames, float rotate, TSpriteBlendMode blendMode) { + bool res; + + for (uint32 i = 0; i < _subframes.size(); i++) { + res = _subframes[i]->draw(x, y, registerOwner, zoomX, zoomY, precise, alpha, rotate, blendMode); + if (DID_FAIL(res)) { + return res; + } + } + return STATUS_OK; +} + +void BaseFrame::stopSound() { + if (_sound) { + _sound->stop(); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::oneTimeDisplay(BaseObject *owner, bool muted) { + if (_sound && !muted) { + if (owner) { + owner->updateOneSound(_sound); + } + _sound->play(); + /* + if (_gameRef->_state == GAME_FROZEN) { + _sound->Pause(true); + } + */ + } + if (owner) { + for (uint32 i = 0; i < _applyEvent.size(); i++) { + owner->applyEvent(_applyEvent[i]); + } + } + return STATUS_OK; +} + + + +TOKEN_DEF_START +TOKEN_DEF(DELAY) +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(RECT) +TOKEN_DEF(HOTSPOT) +TOKEN_DEF(2D_ONLY) +TOKEN_DEF(3D_ONLY) +TOKEN_DEF(MIRROR_X) +TOKEN_DEF(MIRROR_Y) +TOKEN_DEF(MOVE) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(SUBFRAME) +TOKEN_DEF(SOUND) +TOKEN_DEF(KEYFRAME) +TOKEN_DEF(DECORATION) +TOKEN_DEF(APPLY_EVENT) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(EDITOR_EXPANDED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(KILL_SOUND) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(DELAY) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(RECT) + TOKEN_TABLE(HOTSPOT) + TOKEN_TABLE(2D_ONLY) + TOKEN_TABLE(3D_ONLY) + TOKEN_TABLE(MIRROR_X) + TOKEN_TABLE(MIRROR_Y) + TOKEN_TABLE(MOVE) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(SUBFRAME) + TOKEN_TABLE(SOUND) + TOKEN_TABLE(KEYFRAME) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(APPLY_EVENT) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(EDITOR_EXPANDED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(KILL_SOUND) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + Rect32 rect; + int r = 255, g = 255, b = 255; + int ar = 255, ag = 255, ab = 255, alpha = 255; + int hotspotX = 0, hotspotY = 0; + bool custoTrans = false; + bool editorSelected = false; + bool is2DOnly = false; + bool is3DOnly = false; + bool decoration = false; + bool mirrorX = false; + bool mirrorY = false; + BasePlatform::setRectEmpty(&rect); + char *surface_file = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, ¶ms)) > 0) { + switch (cmd) { + case TOKEN_DELAY: + parser.scanStr(params, "%d", &_delay); + break; + + case TOKEN_IMAGE: + surface_file = params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_RECT: + parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom); + break; + + case TOKEN_HOTSPOT: + parser.scanStr(params, "%d,%d", &hotspotX, &hotspotY); + break; + + case TOKEN_MOVE: + parser.scanStr(params, "%d,%d", &_moveX, &_moveY); + break; + + case TOKEN_2D_ONLY: + parser.scanStr(params, "%b", &is2DOnly); + break; + + case TOKEN_3D_ONLY: + parser.scanStr(params, "%b", &is3DOnly); + break; + + case TOKEN_MIRROR_X: + parser.scanStr(params, "%b", &mirrorX); + break; + + case TOKEN_MIRROR_Y: + parser.scanStr(params, "%b", &mirrorY); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr(params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr(params, "%b", &editorSelected); + break; + + case TOKEN_EDITOR_EXPANDED: + parser.scanStr(params, "%b", &_editorExpanded); + break; + + case TOKEN_KILL_SOUND: + parser.scanStr(params, "%b", &_killSound); + break; + + case TOKEN_SUBFRAME: { + BaseSubFrame *subframe = new BaseSubFrame(_gameRef); + if (!subframe || DID_FAIL(subframe->loadBuffer((byte *)params, lifeTime, keepLoaded))) { + delete subframe; + cmd = PARSERR_GENERIC; + } else { + _subframes.add(subframe); + } + } + break; + + case TOKEN_SOUND: { + if (_sound) { + delete _sound; + _sound = NULL; + } + _sound = new BaseSound(_gameRef); + if (!_sound || DID_FAIL(_sound->setSound(params, Audio::Mixer::kSFXSoundType, false))) { + if (_gameRef->_soundMgr->_soundAvailable) { + _gameRef->LOG(0, "Error loading sound '%s'.", params); + } + delete _sound; + _sound = NULL; + } + } + break; + + case TOKEN_APPLY_EVENT: { + char *event = new char[strlen(params) + 1]; + strcpy(event, params); + _applyEvent.add(event); + } + break; + + case TOKEN_KEYFRAME: + parser.scanStr(params, "%b", &_keyframe); + break; + + case TOKEN_DECORATION: + parser.scanStr(params, "%b", &decoration); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in FRAME definition"); + return STATUS_FAILED; + } + + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading FRAME definition"); + return STATUS_FAILED; + } + + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (surface_file != NULL) { + if (custoTrans) { + sub->setSurface(surface_file, false, r, g, b, lifeTime, keepLoaded); + } else { + sub->setSurface(surface_file, true, 0, 0, 0, lifeTime, keepLoaded); + } + + if (!sub->_surface) { + delete sub; + _gameRef->LOG(0, "Error loading SUBFRAME"); + return STATUS_FAILED; + } + + sub->_alpha = BYTETORGBA(ar, ag, ab, alpha); + if (custoTrans) { + sub->_transparent = BYTETORGBA(r, g, b, 0xFF); + } + } + + if (BasePlatform::isRectEmpty(&rect)) { + sub->setDefaultRect(); + } else { + sub->setRect(rect); + } + + sub->_hotspotX = hotspotX; + sub->_hotspotY = hotspotY; + sub->_2DOnly = is2DOnly; + sub->_3DOnly = is3DOnly; + sub->_decoration = decoration; + sub->_mirrorX = mirrorX; + sub->_mirrorY = mirrorY; + + + sub->_editorSelected = editorSelected; + _subframes.insert_at(0, sub); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + BasePlatform::setRectEmpty(rect); + + Rect32 subRect; + + for (uint32 i = 0; i < _subframes.size(); i++) { + _subframes[i]->getBoundingRect(&subRect, x, y, scaleX, scaleY); + BasePlatform::unionRect(rect, rect, &subRect); + } + return true; +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "FRAME {\n"); + buffer->putTextIndent(indent + 2, "DELAY = %d\n", _delay); + + if (_moveX != 0 || _moveY != 0) { + buffer->putTextIndent(indent + 2, "MOVE {%d, %d}\n", _moveX, _moveY); + } + + if (_sound && _sound->getFilename()) { + buffer->putTextIndent(indent + 2, "SOUND=\"%s\"\n", _sound->getFilename()); + } + + buffer->putTextIndent(indent + 2, "KEYFRAME=%s\n", _keyframe ? "TRUE" : "FALSE"); + + if (_killSound) { + buffer->putTextIndent(indent + 2, "KILL_SOUND=%s\n", _killSound ? "TRUE" : "FALSE"); + } + + if (_editorExpanded) { + buffer->putTextIndent(indent + 2, "EDITOR_EXPANDED=%s\n", _editorExpanded ? "TRUE" : "FALSE"); + } + + if (_subframes.size() > 0) { + _subframes[0]->saveAsText(buffer, indent, false); + } + + for (uint32 i = 1; i < _subframes.size(); i++) { + _subframes[i]->saveAsText(buffer, indent + 2); + } + + for (uint32 i = 0; i < _applyEvent.size(); i++) { + buffer->putTextIndent(indent + 2, "APPLY_EVENT=\"%s\"\n", _applyEvent[i]); + } + + BaseClass::saveAsText(buffer, indent + 2); + + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + _applyEvent.persist(persistMgr); + persistMgr->transfer(TMEMBER(_delay)); + persistMgr->transfer(TMEMBER(_editorExpanded)); + persistMgr->transfer(TMEMBER(_keyframe)); + persistMgr->transfer(TMEMBER(_killSound)); + persistMgr->transfer(TMEMBER(_moveX)); + persistMgr->transfer(TMEMBER(_moveY)); + persistMgr->transfer(TMEMBER(_sound)); + _subframes.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // GetSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetSound") == 0) { + stack->correctParams(0); + + if (_sound && _sound->getFilename()) { + stack->pushString(_sound->getFilename()); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSound + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSound") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + delete _sound; + _sound = NULL; + + if (!val->isNULL()) { + _sound = new BaseSound(_gameRef); + if (!_sound || DID_FAIL(_sound->setSound(val->getString(), Audio::Mixer::kSFXSoundType, false))) { + stack->pushBool(false); + delete _sound; + _sound = NULL; + } else { + stack->pushBool(true); + } + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSubframe + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetSubframe") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_subframes.size()) { + script->runtimeError("Frame.GetSubframe: Subframe index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushNative(_subframes[index], true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteSubframe") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int index = val->getInt(-1); + if (index < 0 || index >= (int32)_subframes.size()) { + script->runtimeError("Frame.DeleteSubframe: Subframe index %d is out of range.", index); + } + } else { + BaseSubFrame *sub = (BaseSubFrame *)val->getNative(); + for (uint32 i = 0; i < _subframes.size(); i++) { + if (_subframes[i] == sub) { + delete _subframes[i]; + _subframes.remove_at(i); + break; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSubframe") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (filename != NULL) { + sub->setSurface(filename); + sub->setDefaultRect(); + } + _subframes.add(sub); + + stack->pushNative(sub, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertSubframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertSubframe") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + if (index < 0) { + index = 0; + } + + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (filename != NULL) { + sub->setSurface(filename); + } + + if (index >= (int32)_subframes.size()) { + _subframes.add(sub); + } else { + _subframes.insert_at(index, sub); + } + + stack->pushNative(sub, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSubframe") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_applyEvent.size()) { + script->runtimeError("Frame.GetEvent: Event index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushString(_applyEvent[index]); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddEvent") == 0) { + stack->correctParams(1); + const char *event = stack->pop()->getString(); + for (uint32 i = 0; i < _applyEvent.size(); i++) { + if (scumm_stricmp(_applyEvent[i], event) == 0) { + stack->pushNULL(); + return STATUS_OK; + } + } + _applyEvent.add(event); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteEvent") == 0) { + stack->correctParams(1); + const char *event = stack->pop()->getString(); + for (uint32 i = 0; i < _applyEvent.size(); i++) { + if (scumm_stricmp(_applyEvent[i], event) == 0) { + delete[] _applyEvent[i]; + _applyEvent.remove_at(i); + break; + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scCallMethod(script, stack, thisStack, name); + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseFrame::scGetProperty(const Common::String &name) { + if (!_scValue) { + _scValue = new ScValue(_gameRef); + } + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("frame"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Delay + ////////////////////////////////////////////////////////////////////////// + else if (name == "Delay") { + _scValue->setInt(_delay); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyframe + ////////////////////////////////////////////////////////////////////////// + else if (name == "Keyframe") { + _scValue->setBool(_keyframe); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // KillSounds + ////////////////////////////////////////////////////////////////////////// + else if (name == "KillSounds") { + _scValue->setBool(_killSound); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MoveX") { + _scValue->setInt(_moveX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MoveY") { + _scValue->setInt(_moveY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumSubframes (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumSubframes") { + _scValue->setInt(_subframes.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumEvents (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumEvents") { + _scValue->setInt(_applyEvent.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scGetProperty(name); + } else { + return BaseScriptable::scGetProperty(name); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFrame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Delay + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Delay") == 0) { + _delay = MAX(0, value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyframe + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Keyframe") == 0) { + _keyframe = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // KillSounds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "KillSounds") == 0) { + _killSound = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveX") == 0) { + _moveX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveY") == 0) { + _moveY = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + else { + if (_subframes.size() == 1) { + return _subframes[0]->scSetProperty(name, value); + } else { + return BaseScriptable::scSetProperty(name, value); + } + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseFrame::scToString() { + return "[frame]"; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_frame.h b/engines/wintermute/base/base_frame.h new file mode 100644 index 0000000000..7c5d893e70 --- /dev/null +++ b/engines/wintermute/base/base_frame.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FRAME_H +#define WINTERMUTE_BASE_FRAME_H + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class BaseSound; +class BaseSubFrame; +class BaseObject; +class ScScript; +class ScStack; +class BaseFrame: public BaseScriptable { +public: + bool _killSound; + void stopSound(); + bool oneTimeDisplay(BaseObject *owner, bool muted = false); + DECLARE_PERSISTENT(BaseFrame, BaseScriptable) + + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + int _moveY; + int _moveX; + uint32 _delay; + BaseArray<BaseSubFrame *> _subframes; + bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, bool allFrames = false, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded); + + BaseFrame(BaseGame *inGame); + virtual ~BaseFrame(); + + BaseArray<const char *> _applyEvent; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + bool _keyframe; + bool _editorExpanded; + BaseSound *_sound; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_game.cpp b/engines/wintermute/base/base_game.cpp new file mode 100644 index 0000000000..f0b1171ca4 --- /dev/null +++ b/engines/wintermute/base/base_game.cpp @@ -0,0 +1,4483 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_quick_msg.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/video/video_player.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/utils/crc.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/config-manager.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/util.h" +#include "common/keyboard.h" +#include "common/system.h" +#include "common/file.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseGame, true) + + +////////////////////////////////////////////////////////////////////// +BaseGame::BaseGame(const Common::String &gameId) : BaseObject(this), _gameId(gameId) { + _shuttingDown = false; + + _state = GAME_RUNNING; + _origState = GAME_RUNNING; + _freezeLevel = 0; + + _interactive = true; + _origInteractive = false; + + _surfaceStorage = NULL; + _fontStorage = NULL; + _renderer = NULL; + _soundMgr = NULL; + _transMgr = NULL; + _scEngine = NULL; + _keyboardState = NULL; + + _mathClass = NULL; + + _debugLogFile = NULL; + _debugDebugMode = false; + _debugShowFPS = false; + + _systemFont = NULL; + _videoFont = NULL; + + _videoPlayer = NULL; + _theoraPlayer = NULL; + + _mainObject = NULL; + _activeObject = NULL; + + _fader = NULL; + + _offsetX = _offsetY = 0; + _offsetPercentX = _offsetPercentY = 0.0f; + + _subtitles = true; + _videoSubtitles = true; + + _timer = 0; + _timerDelta = 0; + _timerLast = 0; + + _liveTimer = 0; + _liveTimerDelta = 0; + _liveTimerLast = 0; + + _sequence = 0; + + _mousePos.x = _mousePos.y = 0; + _mouseLeftDown = _mouseRightDown = _mouseMidlleDown = false; + _capturedObject = NULL; + + // FPS counters + _lastTime = _fpsTime = _deltaTime = _framesRendered = _fps = 0; + + _cursorNoninteractive = NULL; + + _useD3D = false; + + _stringTable = new BaseStringTable(this); + + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + _music[i] = NULL; + _musicStartTime[i] = 0; + } + + _settingsResWidth = 800; + _settingsResHeight = 600; + _settingsRequireAcceleration = false; + _settingsRequireSound = false; + _settingsTLMode = 0; + _settingsAllowWindowed = true; + _settingsGameFile = NULL; + _settingsAllowAdvanced = false; + _settingsAllowAccessTab = true; + _settingsAllowAboutTab = true; + _settingsAllowDesktopRes = false; + + _editorForceScripts = false; + _editorAlwaysRegister = false; + + _focusedWindow = NULL; + + _loadInProgress = false; + + _quitting = false; + _loading = false; + _scheduledLoadSlot = -1; + + _personalizedSave = false; + _compressedSavegames = true; + + _editorMode = false; + //_doNotExpandStrings = false; + + _engineLogCallback = NULL; + _engineLogCallbackData = NULL; + + _smartCache = false; + _surfaceGCCycleTime = 10000; + + _reportTextureFormat = false; + + _viewportSP = -1; + + _subtitlesSpeed = 70; + + _forceNonStreamedSounds = false; + + _thumbnailWidth = _thumbnailHeight = 0; + + _richSavedGames = false; + _savedGameExt = NULL; + BaseUtils::setString(&_savedGameExt, "dsv"); + + _musicCrossfadeRunning = false; + _musicCrossfadeStartTime = 0; + _musicCrossfadeLength = 0; + _musicCrossfadeChannel1 = -1; + _musicCrossfadeChannel2 = -1; + _musicCrossfadeSwap = false; + + _localSaveDir = NULL; + BaseUtils::setString(&_localSaveDir, "saves"); + _saveDirChecked = false; + + _loadingIcon = NULL; + _loadingIconX = _loadingIconY = 0; + _loadingIconPersistent = false; + + _textEncoding = TEXT_ANSI; + _textRTL = false; + + _soundBufferSizeSec = 3; + _suspendedRendering = false; + + _lastCursor = NULL; + + + BasePlatform::setRectEmpty(&_mouseLockRect); + + _suppressScriptErrors = false; + _lastMiniUpdate = 0; + _miniUpdateEnabled = false; + + _cachedThumbnail = NULL; + + _autorunDisabled = false; + + // compatibility bits + _compatKillMethodThreads = false; + + _usedMem = 0; + + + _autoSaveOnExit = true; + _autoSaveSlot = 999; + _cursorHidden = false; + + // Block kept as a reminder that the engine CAN run in constrained/touch-mode + /*#ifdef __IPHONEOS__ + _touchInterface = true; + _constrainedMemory = true; // TODO differentiate old and new iOS devices + #else*/ + _touchInterface = false; + _constrainedMemory = false; +//#endif + +} + + +////////////////////////////////////////////////////////////////////// +BaseGame::~BaseGame() { + _shuttingDown = true; + + LOG(0, ""); + LOG(0, "Shutting down..."); + + ConfMan.setBool("last_run", true); + + cleanup(); + + delete[] _localSaveDir; + delete[] _settingsGameFile; + delete[] _savedGameExt; + + delete _cachedThumbnail; + + delete _mathClass; + + delete _transMgr; + delete _scEngine; + delete _fontStorage; + delete _surfaceStorage; + delete _videoPlayer; + delete _theoraPlayer; + delete _soundMgr; + //SAFE_DELETE(_keyboardState); + + delete _renderer; + delete _stringTable; + + _localSaveDir = NULL; + _settingsGameFile = NULL; + _savedGameExt = NULL; + + _cachedThumbnail = NULL; + + _mathClass = NULL; + + _transMgr = NULL; + _scEngine = NULL; + _fontStorage = NULL; + _surfaceStorage = NULL; + _videoPlayer = NULL; + _theoraPlayer = NULL; + _soundMgr = NULL; + + _renderer = NULL; + _stringTable = NULL; + + DEBUG_DebugDisable(); + debugC(kWintermuteDebugLog, "--- shutting down normally ---\n"); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::cleanup() { + delete _loadingIcon; + _loadingIcon = NULL; + + _engineLogCallback = NULL; + _engineLogCallbackData = NULL; + + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + delete _music[i]; + _music[i] = NULL; + _musicStartTime[i] = 0; + } + + unregisterObject(_fader); + _fader = NULL; + + for (uint32 i = 0; i < _regObjects.size(); i++) { + delete _regObjects[i]; + _regObjects[i] = NULL; + } + _regObjects.clear(); + + _windows.clear(); // refs only + _focusedWindow = NULL; // ref only + + delete _cursorNoninteractive; + delete _cursor; + delete _activeCursor; + _cursorNoninteractive = NULL; + _cursor = NULL; + _activeCursor = NULL; + + delete _scValue; + delete _sFX; + _scValue = NULL; + _sFX = NULL; + + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->_owner = NULL; + _scripts[i]->finish(); + } + _scripts.clear(); + + _fontStorage->removeFont(_systemFont); + _systemFont = NULL; + + _fontStorage->removeFont(_videoFont); + _videoFont = NULL; + + for (uint32 i = 0; i < _quickMessages.size(); i++) { + delete _quickMessages[i]; + } + _quickMessages.clear(); + + _viewportStack.clear(); + _viewportSP = -1; + + setName(NULL); + setFilename(NULL); + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } + + _lastCursor = NULL; + + delete _keyboardState; + _keyboardState = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize1() { + bool loaded = false; // Not really a loop, but a goto-replacement. + while (!loaded) { + _surfaceStorage = new BaseSurfaceStorage(this); + if (_surfaceStorage == NULL) { + break; + } + + _fontStorage = new BaseFontStorage(this); + if (_fontStorage == NULL) { + break; + } + + _soundMgr = new BaseSoundMgr(this); + if (_soundMgr == NULL) { + break; + } + + _mathClass = new SXMath(this); + if (_mathClass == NULL) { + break; + } + + _scEngine = new ScEngine(this); + if (_scEngine == NULL) { + break; + } + + _videoPlayer = new VideoPlayer(this); + if (_videoPlayer == NULL) { + break; + } + + _transMgr = new BaseTransitionMgr(this); + if (_transMgr == NULL) { + break; + } + + _keyboardState = new BaseKeyboardState(this); + if (_keyboardState == NULL) { + break; + } + + _fader = new BaseFader(this); + if (_fader == NULL) { + break; + } + registerObject(_fader); + + loaded = true; + } + if (loaded == true) { + return STATUS_OK; + } else { + delete _mathClass; + delete _keyboardState; + delete _transMgr; + delete _surfaceStorage; + delete _fontStorage; + delete _soundMgr; + delete _scEngine; + delete _videoPlayer; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize2() { // we know whether we are going to be accelerated + _renderer = makeOSystemRenderer(this); + if (_renderer == NULL) { + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initialize3() { // renderer is initialized + _posX = _renderer->_width / 2; + _posY = _renderer->_height / 2; + _renderer->initIndicator(); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DebugEnable(const char *filename) { + _debugDebugMode = true; + + int secs = g_system->getMillis() / 1000; + int hours = secs / 3600; + secs = secs % 3600; + int mins = secs / 60; + secs = secs % 60; + +#ifdef _DEBUG + LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Debug Build) *******************", hours, mins, secs); +#else + LOG(0, "********** DEBUG LOG OPENED %02d-%02d-%02d (Release Build) *****************", hours, mins, secs); +#endif + + LOG(0, "%s ver %d.%d.%d%s, Compiled on " __DATE__ ", " __TIME__, DCGF_NAME, DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, DCGF_VER_SUFFIX); + + AnsiString platform = BasePlatform::getPlatformName(); + LOG(0, "Platform: %s", platform.c_str()); + LOG(0, ""); +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DebugDisable() { + if (_debugLogFile != NULL) { + LOG(0, "********** DEBUG LOG CLOSED ********************************************"); + //fclose((FILE *)_debugLogFile); + _debugLogFile = NULL; + } + _debugDebugMode = false; +} + + +////////////////////////////////////////////////////////////////////// +void BaseGame::LOG(bool res, const char *fmt, ...) { + uint32 secs = g_system->getMillis() / 1000; + uint32 hours = secs / 3600; + secs = secs % 3600; + uint32 mins = secs / 60; + secs = secs % 60; + + char buff[512]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + // redirect to an engine's own callback + if (_engineLogCallback) { + _engineLogCallback(buff, res, _engineLogCallbackData); + } + + debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + + //fprintf((FILE *)_debugLogFile, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); + //fflush((FILE *)_debugLogFile); + + //QuickMessage(buff); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setEngineLogCallback(ENGINE_LOG_CALLBACK callback, void *data) { + _engineLogCallback = callback; + _engineLogCallbackData = data; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initLoop() { + _viewportSP = -1; + + _currentTime = g_system->getMillis(); + + _renderer->initLoop(); + updateMusicCrossfade(); + + _surfaceStorage->initLoop(); + _fontStorage->initLoop(); + + + //_activeObject = NULL; + + // count FPS + _deltaTime = _currentTime - _lastTime; + _lastTime = _currentTime; + _fpsTime += _deltaTime; + + _liveTimerDelta = _liveTimer - _liveTimerLast; + _liveTimerLast = _liveTimer; + _liveTimer += MIN((uint32)1000, _deltaTime); + + if (_state != GAME_FROZEN) { + _timerDelta = _timer - _timerLast; + _timerLast = _timer; + _timer += MIN((uint32)1000, _deltaTime); + } else { + _timerDelta = 0; + } + + _framesRendered++; + if (_fpsTime > 1000) { + _fps = _framesRendered; + _framesRendered = 0; + _fpsTime = 0; + } + //_gameRef->LOG(0, "%d", _fps); + + getMousePos(&_mousePos); + + _focusedWindow = NULL; + for (int i = _windows.size() - 1; i >= 0; i--) { + if (_windows[i]->_visible) { + _focusedWindow = _windows[i]; + break; + } + } + + updateSounds(); + + if (_fader) { + _fader->update(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseGame::initInput() { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseGame::getSequence() { + return ++_sequence; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setOffset(int offsetX, int offsetY) { + _offsetX = offsetX; + _offsetY = offsetY; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::getOffset(int *offsetX, int *offsetY) { + if (offsetX != NULL) { + *offsetX = _offsetX; + } + if (offsetY != NULL) { + *offsetY = _offsetY; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseGame::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing GAME file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(GAME) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(SYSTEM_FONT) +TOKEN_DEF(VIDEO_FONT) +TOKEN_DEF(EVENTS) +TOKEN_DEF(CURSOR) +TOKEN_DEF(ACTIVE_CURSOR) +TOKEN_DEF(NONINTERACTIVE_CURSOR) +TOKEN_DEF(STRING_TABLE) +TOKEN_DEF(RESOLUTION) +TOKEN_DEF(SETTINGS) +TOKEN_DEF(REQUIRE_3D_ACCELERATION) +TOKEN_DEF(REQUIRE_SOUND) +TOKEN_DEF(HWTL_MODE) +TOKEN_DEF(ALLOW_WINDOWED_MODE) +TOKEN_DEF(ALLOW_ACCESSIBILITY_TAB) +TOKEN_DEF(ALLOW_ABOUT_TAB) +TOKEN_DEF(ALLOW_ADVANCED) +TOKEN_DEF(ALLOW_DESKTOP_RES) +TOKEN_DEF(REGISTRY_PATH) +TOKEN_DEF(PERSONAL_SAVEGAMES) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PROPERTY) +TOKEN_DEF(SUBTITLES_SPEED) +TOKEN_DEF(SUBTITLES) +TOKEN_DEF(VIDEO_SUBTITLES) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(THUMBNAIL_WIDTH) +TOKEN_DEF(THUMBNAIL_HEIGHT) +TOKEN_DEF(INDICATOR_X) +TOKEN_DEF(INDICATOR_Y) +TOKEN_DEF(INDICATOR_WIDTH) +TOKEN_DEF(INDICATOR_HEIGHT) +TOKEN_DEF(INDICATOR_COLOR) +TOKEN_DEF(SAVE_IMAGE_X) +TOKEN_DEF(SAVE_IMAGE_Y) +TOKEN_DEF(SAVE_IMAGE) +TOKEN_DEF(LOAD_IMAGE_X) +TOKEN_DEF(LOAD_IMAGE_Y) +TOKEN_DEF(LOAD_IMAGE) +TOKEN_DEF(LOCAL_SAVE_DIR) +TOKEN_DEF(RICH_SAVED_GAMES) +TOKEN_DEF(SAVED_GAME_EXT) +TOKEN_DEF(GUID) +TOKEN_DEF(COMPAT_KILL_METHOD_THREADS) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(GAME) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SYSTEM_FONT) + TOKEN_TABLE(VIDEO_FONT) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(ACTIVE_CURSOR) + TOKEN_TABLE(NONINTERACTIVE_CURSOR) + TOKEN_TABLE(PERSONAL_SAVEGAMES) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(SUBTITLES_SPEED) + TOKEN_TABLE(SUBTITLES) + TOKEN_TABLE(VIDEO_SUBTITLES) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(THUMBNAIL_WIDTH) + TOKEN_TABLE(THUMBNAIL_HEIGHT) + TOKEN_TABLE(INDICATOR_X) + TOKEN_TABLE(INDICATOR_Y) + TOKEN_TABLE(INDICATOR_WIDTH) + TOKEN_TABLE(INDICATOR_HEIGHT) + TOKEN_TABLE(INDICATOR_COLOR) + TOKEN_TABLE(SAVE_IMAGE_X) + TOKEN_TABLE(SAVE_IMAGE_Y) + TOKEN_TABLE(SAVE_IMAGE) + TOKEN_TABLE(LOAD_IMAGE_X) + TOKEN_TABLE(LOAD_IMAGE_Y) + TOKEN_TABLE(LOAD_IMAGE) + TOKEN_TABLE(LOCAL_SAVE_DIR) + TOKEN_TABLE(COMPAT_KILL_METHOD_THREADS) + TOKEN_TABLE_END + + // Declare a few variables necessary for moving data from these settings over to the renderer: + // The values are the same as the defaults set in BaseRenderer. + int loadImageX = 0; + int loadImageY = 0; + int saveImageX = 0; + int saveImageY = 0; + int indicatorX = -1; + int indicatorY = -1; + int indicatorWidth = -1; + int indicatorHeight = 8; + uint32 indicatorColor = BYTETORGBA(255, 0, 0, 128); + Common::String loadImageName = ""; + Common::String saveImageName = ""; + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_GAME) { + _gameRef->LOG(0, "'GAME' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_SYSTEM_FONT: + if (_systemFont) { + _fontStorage->removeFont(_systemFont); + } + _systemFont = NULL; + + _systemFont = _gameRef->_fontStorage->addFont((char *)params); + break; + + case TOKEN_VIDEO_FONT: + if (_videoFont) { + _fontStorage->removeFont(_videoFont); + } + _videoFont = NULL; + + _videoFont = _gameRef->_fontStorage->addFont((char *)params); + break; + + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_ACTIVE_CURSOR: + delete _activeCursor; + _activeCursor = NULL; + _activeCursor = new BaseSprite(_gameRef); + if (!_activeCursor || DID_FAIL(_activeCursor->loadFile((char *)params))) { + delete _activeCursor; + _activeCursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NONINTERACTIVE_CURSOR: + delete _cursorNoninteractive; + _cursorNoninteractive = new BaseSprite(_gameRef); + if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile((char *)params))) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PERSONAL_SAVEGAMES: + parser.scanStr((char *)params, "%b", &_personalizedSave); + break; + + case TOKEN_SUBTITLES: + parser.scanStr((char *)params, "%b", &_subtitles); + break; + + case TOKEN_SUBTITLES_SPEED: + parser.scanStr((char *)params, "%d", &_subtitlesSpeed); + break; + + case TOKEN_VIDEO_SUBTITLES: + parser.scanStr((char *)params, "%b", &_videoSubtitles); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_THUMBNAIL_WIDTH: + parser.scanStr((char *)params, "%d", &_thumbnailWidth); + break; + + case TOKEN_THUMBNAIL_HEIGHT: + parser.scanStr((char *)params, "%d", &_thumbnailHeight); + break; + + case TOKEN_INDICATOR_X: + parser.scanStr((char *)params, "%d", &indicatorX); + break; + + case TOKEN_INDICATOR_Y: + parser.scanStr((char *)params, "%d", &indicatorY); + break; + + case TOKEN_INDICATOR_COLOR: { + int r, g, b, a; + parser.scanStr((char *)params, "%d,%d,%d,%d", &r, &g, &b, &a); + indicatorColor = BYTETORGBA(r, g, b, a); + } + break; + + case TOKEN_INDICATOR_WIDTH: + parser.scanStr((char *)params, "%d", &indicatorWidth); + break; + + case TOKEN_INDICATOR_HEIGHT: + parser.scanStr((char *)params, "%d", &indicatorHeight); + break; + + case TOKEN_SAVE_IMAGE: + saveImageName = (char *) params; + break; + + case TOKEN_SAVE_IMAGE_X: + parser.scanStr((char *)params, "%d", &saveImageX); + break; + + case TOKEN_SAVE_IMAGE_Y: + parser.scanStr((char *)params, "%d", &saveImageY); + break; + + case TOKEN_LOAD_IMAGE: + loadImageName = (char *) params; + break; + + case TOKEN_LOAD_IMAGE_X: + parser.scanStr((char *)params, "%d", &loadImageX); + break; + + case TOKEN_LOAD_IMAGE_Y: + parser.scanStr((char *)params, "%d", &loadImageY); + break; + + case TOKEN_LOCAL_SAVE_DIR: + BaseUtils::setString(&_localSaveDir, (char *)params); + break; + + case TOKEN_COMPAT_KILL_METHOD_THREADS: + parser.scanStr((char *)params, "%b", &_compatKillMethodThreads); + break; + } + } + + _renderer->setIndicator(indicatorWidth, indicatorHeight, indicatorX, indicatorY, indicatorColor); + _renderer->initIndicator(); // In case we just reset the values. + _renderer->setSaveImage(saveImageName.c_str(), saveImageX, saveImageY); + _renderer->setLoadingScreen(loadImageName.c_str(), loadImageX, loadImageY); + + if (!_systemFont) { + _systemFont = _gameRef->_fontStorage->addFont("system_font.fnt"); + } + + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in GAME definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading GAME definition"); + return STATUS_FAILED; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseGame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // LOG + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LOG") == 0) { + stack->correctParams(1); + LOG(0, stack->pop()->getString()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Caption") == 0) { + bool res = BaseObject::scCallMethod(script, stack, thisStack, name); + setWindowTitle(); + return res; + } + + ////////////////////////////////////////////////////////////////////////// + // Msg + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Msg") == 0) { + stack->correctParams(1); + quickMessage(stack->pop()->getString()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RunScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RunScript") == 0) { + _gameRef->LOG(0, "**Warning** The 'RunScript' method is now obsolete. Use 'AttachScript' instead (same syntax)"); + stack->correctParams(1); + if (DID_FAIL(addScript(stack->pop()->getString()))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadStringTable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadStringTable") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + ScValue *val = stack->pop(); + + bool clearOld; + if (val->isNULL()) { + clearOld = true; + } else { + clearOld = val->getBool(); + } + + if (DID_FAIL(_stringTable->loadFile(filename, clearOld))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ValidObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ValidObject") == 0) { + stack->correctParams(1); + BaseScriptable *obj = stack->pop()->getNative(); + if (validObject((BaseObject *) obj)) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + resetContent(); + stack->pushNULL(); + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // UnloadObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UnloadObject") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + BaseObject *obj = (BaseObject *)val->getNative(); + unregisterObject(obj); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadWindow") == 0) { + stack->correctParams(1); + UIWindow *win = new UIWindow(_gameRef); + if (win && DID_SUCCEED(win->loadFile(stack->pop()->getString()))) { + _windows.add(win); + registerObject(win); + stack->pushNative(win, true); + } else { + delete win; + win = NULL; + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ExpandString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ExpandString") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + char *str = new char[strlen(val->getString()) + 1]; + strcpy(str, val->getString()); + _stringTable->expand(&str); + stack->pushString(str); + delete[] str; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayMusic / PlayMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayMusic") == 0 || strcmp(name, "PlayMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "PlayMusic") == 0) { + stack->correctParams(3); + } else { + stack->correctParams(4); + channel = stack->pop()->getInt(); + } + + const char *filename = stack->pop()->getString(); + ScValue *valLooping = stack->pop(); + bool looping = valLooping->isNULL() ? true : valLooping->getBool(); + + ScValue *valLoopStart = stack->pop(); + uint32 loopStart = (uint32)(valLoopStart->isNULL() ? 0 : valLoopStart->getInt()); + + + if (DID_FAIL(playMusic(channel, filename, looping, loopStart))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopMusic / StopMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopMusic") == 0 || strcmp(name, "StopMusicChannel") == 0) { + int channel = 0; + + if (strcmp(name, "StopMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(stopMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic / PauseMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseMusic") == 0 || strcmp(name, "PauseMusicChannel") == 0) { + int channel = 0; + + if (strcmp(name, "PauseMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(pauseMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeMusic / ResumeMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeMusic") == 0 || strcmp(name, "ResumeMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "ResumeMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (DID_FAIL(resumeMusic(channel))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusic / GetMusicChannel + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusic") == 0 || strcmp(name, "GetMusicChannel") == 0) { + int channel = 0; + if (strcmp(name, "GetMusic") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS) { + stack->pushNULL(); + } else { + if (!_music[channel] || !_music[channel]->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_music[channel]->getFilename()); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMusicPosition / SetMusicChannelPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMusicPosition") == 0 || strcmp(name, "SetMusicChannelPosition") == 0 || strcmp(name, "SetMusicPositionChannel") == 0) { + int channel = 0; + if (strcmp(name, "SetMusicPosition") == 0) { + stack->correctParams(1); + } else { + stack->correctParams(2); + channel = stack->pop()->getInt(); + } + + uint32 time = stack->pop()->getInt(); + + if (DID_FAIL(setMusicStartTime(channel, time))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusicPosition / GetMusicChannelPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusicPosition") == 0 || strcmp(name, "GetMusicChannelPosition") == 0) { + int channel = 0; + if (strcmp(name, "GetMusicPosition") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushInt(0); + } else { + stack->pushInt(_music[channel]->getPositionTime()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsMusicPlaying / IsMusicChannelPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsMusicPlaying") == 0 || strcmp(name, "IsMusicChannelPlaying") == 0) { + int channel = 0; + if (strcmp(name, "IsMusicPlaying") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushBool(false); + } else { + stack->pushBool(_music[channel]->isPlaying()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMusicVolume / SetMusicChannelVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMusicVolume") == 0 || strcmp(name, "SetMusicChannelVolume") == 0) { + int channel = 0; + if (strcmp(name, "SetMusicVolume") == 0) { + stack->correctParams(1); + } else { + stack->correctParams(2); + channel = stack->pop()->getInt(); + } + + int volume = stack->pop()->getInt(); + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushBool(false); + } else { + if (DID_FAIL(_music[channel]->setVolumePercent(volume))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetMusicVolume / GetMusicChannelVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMusicVolume") == 0 || strcmp(name, "GetMusicChannelVolume") == 0) { + int channel = 0; + if (strcmp(name, "GetMusicVolume") == 0) { + stack->correctParams(0); + } else { + stack->correctParams(1); + channel = stack->pop()->getInt(); + } + + if (channel < 0 || channel >= NUM_MUSIC_CHANNELS || !_music[channel]) { + stack->pushInt(0); + } else { + stack->pushInt(_music[channel]->getVolumePercent()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicCrossfade + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MusicCrossfade") == 0) { + stack->correctParams(4); + int channel1 = stack->pop()->getInt(0); + int channel2 = stack->pop()->getInt(0); + uint32 fadeLength = (uint32)stack->pop()->getInt(0); + bool swap = stack->pop()->getBool(true); + + if (_musicCrossfadeRunning) { + script->runtimeError("Game.MusicCrossfade: Music crossfade is already in progress."); + stack->pushBool(false); + return STATUS_OK; + } + + _musicCrossfadeStartTime = _liveTimer; + _musicCrossfadeChannel1 = channel1; + _musicCrossfadeChannel2 = channel2; + _musicCrossfadeLength = fadeLength; + _musicCrossfadeSwap = swap; + + _musicCrossfadeRunning = true; + + stack->pushBool(true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundLength + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundLength") == 0) { + stack->correctParams(1); + + int length = 0; + const char *filename = stack->pop()->getString(); + + BaseSound *sound = new BaseSound(_gameRef); + if (sound && DID_SUCCEED(sound->setSound(filename, Audio::Mixer::kMusicSoundType, true))) { + length = sound->getLength(); + delete sound; + sound = NULL; + } + stack->pushInt(length); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetMousePos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMousePos") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + x = MAX(x, 0); + x = MIN(x, _renderer->_width); + y = MAX(y, 0); + y = MIN(y, _renderer->_height); + Point32 p; + p.x = x + _renderer->_drawOffsetX; + p.y = y + _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(p.x, p.y); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LockMouseRect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LockMouseRect") == 0) { + stack->correctParams(4); + int left = stack->pop()->getInt(); + int top = stack->pop()->getInt(); + int right = stack->pop()->getInt(); + int bottom = stack->pop()->getInt(); + + if (right < left) { + BaseUtils::swap(&left, &right); + } + if (bottom < top) { + BaseUtils::swap(&top, &bottom); + } + + BasePlatform::setRect(&_mouseLockRect, left, top, right, bottom); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayVideo + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayVideo") == 0) { + _gameRef->LOG(0, "Warning: Game.PlayVideo() is now deprecated. Use Game.PlayTheora() instead."); + + stack->correctParams(6); + const char *filename = stack->pop()->getString(); + warning("PlayVideo: %s - not implemented yet", filename); + ScValue *valType = stack->pop(); + int type; + if (valType->isNULL()) { + type = (int)VID_PLAY_STRETCH; + } else { + type = valType->getInt(); + } + + int xVal = stack->pop()->getInt(); + int yVal = stack->pop()->getInt(); + bool freezeMusic = stack->pop()->getBool(true); + + ScValue *valSub = stack->pop(); + const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString(); + + if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) { + type = (int)VID_PLAY_STRETCH; + } + + if (DID_SUCCEED(_gameRef->_videoPlayer->initialize(filename, subtitleFile))) { + if (DID_SUCCEED(_gameRef->_videoPlayer->play((TVideoPlayback)type, xVal, yVal, freezeMusic))) { + stack->pushBool(true); + script->sleep(0); + } else { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlayTheora + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlayTheora") == 0) { + stack->correctParams(7); + const char *filename = stack->pop()->getString(); + ScValue *valType = stack->pop(); + int type; + if (valType->isNULL()) { + type = (int)VID_PLAY_STRETCH; + } else { + type = valType->getInt(); + } + + int xVal = stack->pop()->getInt(); + int yVal = stack->pop()->getInt(); + bool freezeMusic = stack->pop()->getBool(true); + bool dropFrames = stack->pop()->getBool(true); + + ScValue *valSub = stack->pop(); + const char *subtitleFile = valSub->isNULL() ? NULL : valSub->getString(); + + if (type < (int)VID_PLAY_POS || type > (int)VID_PLAY_CENTER) { + type = (int)VID_PLAY_STRETCH; + } + + delete _theoraPlayer; + _theoraPlayer = new VideoTheoraPlayer(this); + if (_theoraPlayer && DID_SUCCEED(_theoraPlayer->initialize(filename, subtitleFile))) { + _theoraPlayer->_dontDropFrames = !dropFrames; + if (DID_SUCCEED(_theoraPlayer->play((TVideoPlayback)type, xVal, yVal, true, freezeMusic))) { + stack->pushBool(true); + script->sleep(0); + } else { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + delete _theoraPlayer; + _theoraPlayer = NULL; + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // QuitGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "QuitGame") == 0) { + stack->correctParams(0); + stack->pushNULL(); + _quitting = true; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegWriteNumber + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegWriteNumber") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + int val = stack->pop()->getInt(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + ConfMan.setInt(privKey, val); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegReadNumber + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegReadNumber") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + int initVal = stack->pop()->getInt(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + int result = initVal; + if (ConfMan.hasKey(privKey)) { + result = ConfMan.getInt(privKey); + } + stack->pushInt(result); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegWriteString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegWriteString") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + const char *val = stack->pop()->getString(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + Common::String privVal = StringUtil::encodeSetting(val); + ConfMan.set(privKey, privVal); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RegReadString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RegReadString") == 0) { + stack->correctParams(2); + const char *key = stack->pop()->getString(); + const char *initVal = stack->pop()->getString(); + Common::String privKey = "priv_" + StringUtil::encodeSetting(key); + Common::String result = initVal; + if (ConfMan.hasKey(privKey)) { + result = StringUtil::decodeSetting(ConfMan.get(key)); + } + stack->pushString(result.c_str()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SaveGame") == 0) { + stack->correctParams(3); + int slot = stack->pop()->getInt(); + const char *xdesc = stack->pop()->getString(); + bool quick = stack->pop()->getBool(false); + + char *desc = new char[strlen(xdesc) + 1]; + strcpy(desc, xdesc); + stack->pushBool(true); + if (DID_FAIL(saveGame(slot, desc, quick))) { + stack->pop(); + stack->pushBool(false); + } + delete[] desc; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadGame") == 0) { + stack->correctParams(1); + _scheduledLoadSlot = stack->pop()->getInt(); + _loading = true; + stack->pushBool(false); + script->sleep(0); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsSaveSlotUsed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsSaveSlotUsed") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + stack->pushBool(SaveLoad::isSaveSlotUsed(slot)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSaveSlotDescription + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSaveSlotDescription") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + char desc[512]; + desc[0] = '\0'; + SaveLoad::getSaveSlotDescription(slot, desc); + stack->pushString(desc); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EmptySaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EmptySaveSlot") == 0) { + stack->correctParams(1); + int slot = stack->pop()->getInt(); + SaveLoad::emptySaveSlot(slot); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalSFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalSFXVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalSpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalSpeechVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalMusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalMusicVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetGlobalMasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetGlobalMasterVolume") == 0) { + stack->correctParams(1); + _gameRef->_soundMgr->setMasterVolumePercent((byte)stack->pop()->getInt()); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalSFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalSFXVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalSpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalSpeechVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalMusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalMusicVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetGlobalMasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGlobalMasterVolume") == 0) { + stack->correctParams(0); + stack->pushInt(_soundMgr->getMasterVolumePercent()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetActiveCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setActiveCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetActiveCursor") == 0) { + stack->correctParams(0); + if (!_activeCursor || !_activeCursor->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_activeCursor->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetActiveCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetActiveCursorObject") == 0) { + stack->correctParams(0); + if (!_activeCursor) { + stack->pushNULL(); + } else { + stack->pushNative(_activeCursor, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveActiveCursor") == 0) { + stack->correctParams(0); + delete _activeCursor; + _activeCursor = NULL; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasActiveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasActiveCursor") == 0) { + stack->correctParams(0); + + if (_activeCursor) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FileExists + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FileExists") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + bool exists = BaseFileManager::getEngineInstance()->hasFile(filename); // Had absPathWarning = false + stack->pushBool(exists); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeOut / FadeOutAsync / SystemFadeOut / SystemFadeOutAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOut") == 0 || strcmp(name, "FadeOutAsync") == 0 || strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + bool system = (strcmp(name, "SystemFadeOut") == 0 || strcmp(name, "SystemFadeOutAsync") == 0); + + _fader->fadeOut(BYTETORGBA(red, green, blue, alpha), duration, system); + if (strcmp(name, "FadeOutAsync") != 0 && strcmp(name, "SystemFadeOutAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeIn / FadeInAsync / SystemFadeIn / SystemFadeInAsync + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeIn") == 0 || strcmp(name, "FadeInAsync") == 0 || strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0) { + stack->correctParams(5); + uint32 duration = stack->pop()->getInt(500); + byte red = stack->pop()->getInt(0); + byte green = stack->pop()->getInt(0); + byte blue = stack->pop()->getInt(0); + byte alpha = stack->pop()->getInt(0xFF); + + bool system = (strcmp(name, "SystemFadeIn") == 0 || strcmp(name, "SystemFadeInAsync") == 0); + + _fader->fadeIn(BYTETORGBA(red, green, blue, alpha), duration, system); + if (strcmp(name, "FadeInAsync") != 0 && strcmp(name, "SystemFadeInAsync") != 0) { + script->waitFor(_fader); + } + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFadeColor") == 0) { + stack->correctParams(0); + stack->pushInt(_fader->getCurrentColor()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Screenshot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Screenshot") == 0) { + stack->correctParams(1); + char filename[MAX_PATH_LENGTH]; + + ScValue *val = stack->pop(); + + warning("BGame::ScCallMethod - Screenshot not reimplemented"); //TODO + int fileNum = 0; + + while (true) { + sprintf(filename, "%s%03d.bmp", val->isNULL() ? getName() : val->getString(), fileNum); + if (!Common::File::exists(filename)) { + break; + } + fileNum++; + } + + bool ret = false; + BaseImage *image = _gameRef->_renderer->takeScreenshot(); + if (image) { + ret = DID_SUCCEED(image->saveBMPFile(filename)); + delete image; + } else { + ret = false; + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenshotEx + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScreenshotEx") == 0) { + stack->correctParams(3); + const char *filename = stack->pop()->getString(); + int sizeX = stack->pop()->getInt(_renderer->_width); + int sizeY = stack->pop()->getInt(_renderer->_height); + + bool ret = false; + BaseImage *image = _gameRef->_renderer->takeScreenshot(); + if (image) { + ret = DID_SUCCEED(image->resize(sizeX, sizeY)); + if (ret) { + ret = DID_SUCCEED(image->saveBMPFile(filename)); + } + delete image; + } else { + ret = false; + } + + stack->pushBool(ret); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIWindow *win = new UIWindow(_gameRef); + _windows.add(win); + registerObject(win); + if (!val->isNULL()) { + win->setName(val->getString()); + } + stack->pushNative(win, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteWindow") == 0) { + stack->correctParams(1); + BaseObject *obj = (BaseObject *)stack->pop()->getNative(); + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i] == obj) { + unregisterObject(_windows[i]); + stack->pushBool(true); + return STATUS_OK; + } + } + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OpenDocument + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OpenDocument") == 0) { + stack->correctParams(0); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DEBUG_DumpClassRegistry + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DEBUG_DumpClassRegistry") == 0) { + stack->correctParams(0); + DEBUG_DumpClassRegistry(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetLoadingScreen + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetLoadingScreen") == 0) { + stack->correctParams(3); + ScValue *val = stack->pop(); + int loadImageX = stack->pop()->getInt(); + int loadImageY = stack->pop()->getInt(); + + if (val->isNULL()) { + _renderer->setLoadingScreen(NULL, loadImageX, loadImageY); + } else { + _renderer->setLoadingScreen(val->getString(), loadImageX, loadImageY); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSavingScreen + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSavingScreen") == 0) { + stack->correctParams(3); + ScValue *val = stack->pop(); + int saveImageX = stack->pop()->getInt(); + int saveImageY = stack->pop()->getInt(); + + if (val->isNULL()) { + _renderer->setSaveImage(NULL, saveImageX, saveImageY); + } else { + _renderer->setSaveImage(NULL, saveImageX, saveImageY); + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetWaitCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setWaitCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveWaitCursor") == 0) { + stack->correctParams(0); + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaitCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaitCursor") == 0) { + stack->correctParams(0); + if (!_cursorNoninteractive || !_cursorNoninteractive->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursorNoninteractive->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetWaitCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWaitCursorObject") == 0) { + stack->correctParams(0); + if (!_cursorNoninteractive) { + stack->pushNULL(); + } else { + stack->pushNative(_cursorNoninteractive, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClearScriptCache + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClearScriptCache") == 0) { + stack->correctParams(0); + stack->pushBool(DID_SUCCEED(_scEngine->emptyScriptCache())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisplayLoadingIcon + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisplayLoadingIcon") == 0) { + stack->correctParams(4); + + const char *filename = stack->pop()->getString(); + _loadingIconX = stack->pop()->getInt(); + _loadingIconY = stack->pop()->getInt(); + _loadingIconPersistent = stack->pop()->getBool(); + + delete _loadingIcon; + _loadingIcon = new BaseSprite(this); + if (!_loadingIcon || DID_FAIL(_loadingIcon->loadFile(filename))) { + delete _loadingIcon; + _loadingIcon = NULL; + } else { + displayContent(false, true); + _gameRef->_renderer->flip(); + _gameRef->_renderer->initLoop(); + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HideLoadingIcon + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HideLoadingIcon") == 0) { + stack->correctParams(0); + delete _loadingIcon; + _loadingIcon = NULL; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DumpTextureStats + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DumpTextureStats") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + + _renderer->dumpData(filename); + + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AccOutputText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AccOutputText") == 0) { + stack->correctParams(2); + /* const char *str = */ stack->pop()->getString(); + /* int type = */ stack->pop()->getInt(); + // do nothing + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StoreSaveThumbnail + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StoreSaveThumbnail") == 0) { + stack->correctParams(0); + delete _cachedThumbnail; + _cachedThumbnail = new BaseSaveThumbHelper(this); + if (DID_FAIL(_cachedThumbnail->storeThumbnail())) { + delete _cachedThumbnail; + _cachedThumbnail = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteSaveThumbnail + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteSaveThumbnail") == 0) { + stack->correctParams(0); + delete _cachedThumbnail; + _cachedThumbnail = NULL; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFileChecksum + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFileChecksum") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool asHex = stack->pop()->getBool(false); + + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename, false); + if (file) { + crc remainder = crc_initialize(); + byte buf[1024]; + int bytesRead = 0; + + while (bytesRead < file->size()) { + int bufSize = MIN((uint32)1024, (uint32)(file->size() - bytesRead)); + bytesRead += file->read(buf, bufSize); + + for (int i = 0; i < bufSize; i++) { + remainder = crc_process_byte(buf[i], remainder); + } + } + crc checksum = crc_finalize(remainder); + + if (asHex) { + char hex[100]; + sprintf(hex, "%x", checksum); + stack->pushString(hex); + } else { + stack->pushInt(checksum); + } + + BaseFileManager::getEngineInstance()->closeFile(file); + file = NULL; + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EnableScriptProfiling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EnableScriptProfiling") == 0) { + stack->correctParams(0); + _scEngine->enableProfiling(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DisableScriptProfiling + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DisableScriptProfiling") == 0) { + stack->correctParams(0); + _scEngine->disableProfiling(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ShowStatusLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ShowStatusLine") == 0) { + stack->correctParams(0); + // Block kept to show intention of opcode. + /*#ifdef __IPHONEOS__ + IOS_ShowStatusLine(TRUE); + #endif*/ + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HideStatusLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HideStatusLine") == 0) { + stack->correctParams(0); + // Block kept to show intention of opcode. + /*#ifdef __IPHONEOS__ + IOS_ShowStatusLine(FALSE); + #endif*/ + stack->pushNULL(); + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseGame::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("game"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Hwnd (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Hwnd") { + _scValue->setInt((int)_renderer->_window); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CurrentTime (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "CurrentTime") { + _scValue->setInt((int)_timer); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WindowsTime (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "WindowsTime") { + _scValue->setInt((int)g_system->getMillis()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // WindowedMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "WindowedMode") { + _scValue->setBool(_renderer->_windowed); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseX") { + _scValue->setInt(_mousePos.x); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MouseY") { + _scValue->setInt(_mousePos.y); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MainObject + ////////////////////////////////////////////////////////////////////////// + else if (name == "MainObject") { + _scValue->setNative(_mainObject, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ActiveObject (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ActiveObject") { + _scValue->setNative(_activeObject, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenWidth (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScreenWidth") { + _scValue->setInt(_renderer->_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScreenHeight (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScreenHeight") { + _scValue->setInt(_renderer->_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Interactive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Interactive") { + _scValue->setBool(_interactive); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DebugMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "DebugMode") { + _scValue->setBool(_debugDebugMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundAvailable (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundAvailable") { + _scValue->setBool(_soundMgr->_soundAvailable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "SFXVolume") { + _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "SpeechVolume") { + _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kSpeechSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "MusicVolume") { + _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getVolumePercent(Audio::Mixer::kMusicSoundType)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (name == "MasterVolume") { + _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete"); + _scValue->setInt(_soundMgr->getMasterVolumePercent()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Keyboard (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Keyboard") { + if (_keyboardState) { + _scValue->setNative(_keyboardState, true); + } else { + _scValue->setNULL(); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtitles + ////////////////////////////////////////////////////////////////////////// + else if (name == "Subtitles") { + _scValue->setBool(_subtitles); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesSpeed + ////////////////////////////////////////////////////////////////////////// + else if (name == "SubtitlesSpeed") { + _scValue->setInt(_subtitlesSpeed); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // VideoSubtitles + ////////////////////////////////////////////////////////////////////////// + else if (name == "VideoSubtitles") { + _scValue->setBool(_videoSubtitles); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FPS (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "FPS") { + _scValue->setInt(_fps); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AcceleratedMode / Accelerated (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "AcceleratedMode" || name == "Accelerated") { + _scValue->setBool(_useD3D); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextEncoding + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextEncoding") { + _scValue->setInt(_textEncoding); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextRTL + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextRTL") { + _scValue->setBool(_textRTL); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundBufferSize + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundBufferSize") { + _scValue->setInt(_soundBufferSizeSec); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SuspendedRendering + ////////////////////////////////////////////////////////////////////////// + else if (name == "SuspendedRendering") { + _scValue->setBool(_suspendedRendering); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SuppressScriptErrors + ////////////////////////////////////////////////////////////////////////// + else if (name == "SuppressScriptErrors") { + _scValue->setBool(_suppressScriptErrors); + return _scValue; + } + + + ////////////////////////////////////////////////////////////////////////// + // Frozen + ////////////////////////////////////////////////////////////////////////// + else if (name == "Frozen") { + _scValue->setBool(_state == GAME_FROZEN); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSEnabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSEnabled") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSTalk + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSTalk") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSCaptions + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSCaptions") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccTTSKeypress + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccTTSKeypress") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardEnabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardEnabled") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardCursorSkip + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardCursorSkip") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccKeyboardPause + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccKeyboardPause") { + _scValue->setBool(false); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutorunDisabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutorunDisabled") { + _scValue->setBool(_autorunDisabled); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveDirectory (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "SaveDirectory") { + AnsiString dataDir = "saves/"; // TODO: This is just to avoid telling the engine actual paths. + _scValue->setString(dataDir.c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveOnExit + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoSaveOnExit") { + _scValue->setBool(_autoSaveOnExit); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (name == "AutoSaveSlot") { + _scValue->setInt(_autoSaveSlot); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorHidden + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorHidden") { + _scValue->setBool(_cursorHidden); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Platform (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Platform") { + _scValue->setString(BasePlatform::getPlatformName().c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // DeviceType (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "DeviceType") { + _scValue->setString(getDeviceType().c_str()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MostRecentSaveSlot (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "MostRecentSaveSlot") { + if (!ConfMan.hasKey("most_recent_saveslot")) { + _scValue->setInt(-1); + } else { + _scValue->setInt(ConfMan.getInt("most_recent_saveslot")); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Store (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Store") { + _scValue->setNULL(); + error("Request for a SXStore-object, which is not supported by ScummVM"); + + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MouseX") == 0) { + _mousePos.x = value->getInt(); + resetMousePos(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MouseY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MouseY") == 0) { + _mousePos.y = value->getInt(); + resetMousePos(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Name") == 0) { + bool res = BaseObject::scSetProperty(name, value); + setWindowTitle(); + return res; + } + + ////////////////////////////////////////////////////////////////////////// + // MainObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MainObject") == 0) { + BaseScriptable *obj = value->getNative(); + if (obj == NULL || validObject((BaseObject *)obj)) { + _mainObject = (BaseObject *)obj; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Interactive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Interactive") == 0) { + setInteractive(value->getBool()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SFXVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SFXVolume") == 0) { + _gameRef->LOG(0, "**Warning** The SFXVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSFXSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SpeechVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SpeechVolume") == 0) { + _gameRef->LOG(0, "**Warning** The SpeechVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kSpeechSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MusicVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MusicVolume") == 0) { + _gameRef->LOG(0, "**Warning** The MusicVolume attribute is obsolete"); + _gameRef->_soundMgr->setVolumePercent(Audio::Mixer::kMusicSoundType, (byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MasterVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MasterVolume") == 0) { + _gameRef->LOG(0, "**Warning** The MasterVolume attribute is obsolete"); + _gameRef->_soundMgr->setMasterVolumePercent((byte)value->getInt()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Subtitles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Subtitles") == 0) { + _subtitles = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SubtitlesSpeed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SubtitlesSpeed") == 0) { + _subtitlesSpeed = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // VideoSubtitles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VideoSubtitles") == 0) { + _videoSubtitles = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TextEncoding + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TextEncoding") == 0) { + int enc = value->getInt(); + if (enc < 0) { + enc = 0; + } + if (enc >= NUM_TEXT_ENCODINGS) { + enc = NUM_TEXT_ENCODINGS - 1; + } + _textEncoding = (TTextEncoding)enc; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // TextRTL + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "TextRTL") == 0) { + _textRTL = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundBufferSize + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundBufferSize") == 0) { + _soundBufferSizeSec = value->getInt(); + _soundBufferSizeSec = MAX(3, _soundBufferSizeSec); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SuspendedRendering + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SuspendedRendering") == 0) { + _suspendedRendering = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SuppressScriptErrors + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SuppressScriptErrors") == 0) { + _suppressScriptErrors = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutorunDisabled + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutorunDisabled") == 0) { + _autorunDisabled = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveOnExit + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoSaveOnExit") == 0) { + _autoSaveOnExit = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AutoSaveSlot + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AutoSaveSlot") == 0) { + _autoSaveSlot = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorHidden + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorHidden") == 0) { + _cursorHidden = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseGame::scToString() { + return "[game object]"; +} + + + +#define QUICK_MSG_DURATION 3000 +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayQuickMsg() { + if (_quickMessages.size() == 0 || !_systemFont) { + return STATUS_OK; + } + + // update + for (uint32 i = 0; i < _quickMessages.size(); i++) { + if (_currentTime - _quickMessages[i]->_startTime >= QUICK_MSG_DURATION) { + delete _quickMessages[i]; + _quickMessages.remove_at(i); + i--; + } + } + + int posY = 20; + + // display + for (uint32 i = 0; i < _quickMessages.size(); i++) { + _systemFont->drawText((byte *)_quickMessages[i]->getText(), 0, posY, _renderer->_width); + posY += _systemFont->getTextHeight((byte *)_quickMessages[i]->getText(), _renderer->_width); + } + return STATUS_OK; +} + + +#define MAX_QUICK_MSG 5 +////////////////////////////////////////////////////////////////////////// +void BaseGame::quickMessage(const char *text) { + if (_quickMessages.size() >= MAX_QUICK_MSG) { + delete _quickMessages[0]; + _quickMessages.remove_at(0); + } + _quickMessages.add(new BaseQuickMsg(_gameRef, text)); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::quickMessageForm(char *fmt, ...) { + char buff[256]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + quickMessage(buff); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::registerObject(BaseObject *object) { + _regObjects.add(object); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::unregisterObject(BaseObject *object) { + if (!object) { + return STATUS_OK; + } + + // is it a window? + for (uint32 i = 0; i < _windows.size(); i++) { + if ((BaseObject *)_windows[i] == object) { + _windows.remove_at(i); + + // get new focused window + if (_focusedWindow == object) { + _focusedWindow = NULL; + } + + break; + } + } + + // is it active object? + if (_activeObject == object) { + _activeObject = NULL; + } + + // is it main object? + if (_mainObject == object) { + _mainObject = NULL; + } + + // destroy object + for (uint32 i = 0; i < _regObjects.size(); i++) { + if (_regObjects[i] == object) { + _regObjects.remove_at(i); + if (!_loadInProgress) { + SystemClassRegistry::getInstance()->enumInstances(invalidateValues, "ScValue", (void *)object); + } + delete object; + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::invalidateValues(void *value, void *data) { + ScValue *val = (ScValue *)value; + if (val->isNative() && val->getNative() == data) { + if (!val->_persistent && ((BaseScriptable *)data)->_refCount == 1) { + ((BaseScriptable *)data)->_refCount++; + } + val->setNative(NULL); + val->setNULL(); + } +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::validObject(BaseObject *object) { + if (!object) { + return false; + } + if (object == this) { + return true; + } + + for (uint32 i = 0; i < _regObjects.size(); i++) { + if (_regObjects[i] == object) { + return true; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name) { + ScValue *thisObj; + + ////////////////////////////////////////////////////////////////////////// + // LOG + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "LOG") == 0) { + stack->correctParams(1); + _gameRef->LOG(0, "sc: %s", stack->pop()->getString()); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // String + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "String") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXString(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // MemBuffer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MemBuffer") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXMemBuffer(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // File + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "File") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXFile(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Date + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Date") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXDate(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Array + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Array") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXArray(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Object + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Object") == 0) { + thisObj = thisStack->getTop(); + + thisObj->setNative(makeSXObject(_gameRef, stack)); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Sleep + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sleep") == 0) { + stack->correctParams(1); + + script->sleep((uint32)stack->pop()->getInt()); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // WaitFor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WaitFor") == 0) { + stack->correctParams(1); + + BaseScriptable *obj = stack->pop()->getNative(); + if (validObject((BaseObject *)obj)) { + script->waitForExclusive((BaseObject *)obj); + } + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // Random + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Random") == 0) { + stack->correctParams(2); + + int from = stack->pop()->getInt(); + int to = stack->pop()->getInt(); + + stack->pushInt(BaseUtils::randomInt(from, to)); + } + + ////////////////////////////////////////////////////////////////////////// + // SetScriptTimeSlice + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetScriptTimeSlice") == 0) { + stack->correctParams(1); + + script->_timeSlice = (uint32)stack->pop()->getInt(); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // MakeRGBA / MakeRGB / RGB + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MakeRGBA") == 0 || strcmp(name, "MakeRGB") == 0 || strcmp(name, "RGB") == 0) { + stack->correctParams(4); + int r = stack->pop()->getInt(); + int g = stack->pop()->getInt(); + int b = stack->pop()->getInt(); + int a; + ScValue *val = stack->pop(); + if (val->isNULL()) { + a = 255; + } else { + a = val->getInt(); + } + + stack->pushInt(BYTETORGBA(r, g, b, a)); + } + + ////////////////////////////////////////////////////////////////////////// + // MakeHSL + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MakeHSL") == 0) { + stack->correctParams(3); + int h = stack->pop()->getInt(); + int s = stack->pop()->getInt(); + int l = stack->pop()->getInt(); + + stack->pushInt(BaseUtils::HSLtoRGB(h, s, l)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetRValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetRValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetR(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetGValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetGValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetG(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetBValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetBValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetB(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetAValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetAValue") == 0) { + stack->correctParams(1); + + uint32 rgba = (uint32)stack->pop()->getInt(); + stack->pushInt(RGBCOLGetA(rgba)); + } + + ////////////////////////////////////////////////////////////////////////// + // GetHValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(H); + } + + ////////////////////////////////////////////////////////////////////////// + // GetSValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(S); + } + + ////////////////////////////////////////////////////////////////////////// + // GetLValue + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetLValue") == 0) { + stack->correctParams(1); + uint32 rgb = (uint32)stack->pop()->getInt(); + + byte H, S, L; + BaseUtils::RGBtoHSL(rgb, &H, &S, &L); + stack->pushInt(L); + } + + ////////////////////////////////////////////////////////////////////////// + // Debug + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Debug") == 0) { + stack->correctParams(0); + stack->pushNULL(); + } + + ////////////////////////////////////////////////////////////////////////// + // ToString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToString") == 0) { + stack->correctParams(1); + const char *str = stack->pop()->getString(); + char *str2 = new char[strlen(str) + 1]; + strcpy(str2, str); + stack->pushString(str2); + delete[] str2; + } + + ////////////////////////////////////////////////////////////////////////// + // ToInt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToInt") == 0) { + stack->correctParams(1); + int val = stack->pop()->getInt(); + stack->pushInt(val); + } + + ////////////////////////////////////////////////////////////////////////// + // ToFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToFloat") == 0) { + stack->correctParams(1); + double val = stack->pop()->getFloat(); + stack->pushFloat(val); + } + + ////////////////////////////////////////////////////////////////////////// + // ToBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToBool") == 0) { + stack->correctParams(1); + bool val = stack->pop()->getBool(); + stack->pushBool(val); + } + + ////////////////////////////////////////////////////////////////////////// + // failure + else { + script->runtimeError("Call to undefined function '%s'. Ignored.", name); + stack->correctParams(0); + stack->pushNULL(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::showCursor() { + if (_cursorHidden) { + return STATUS_OK; + } + + if (!_interactive && _gameRef->_state == GAME_RUNNING) { + if (_cursorNoninteractive) { + return drawCursor(_cursorNoninteractive); + } + } else { + if (_activeObject && !DID_FAIL(_activeObject->showCursor())) { + return STATUS_OK; + } else { + if (_activeObject && _activeCursor && _activeObject->getExtendedFlag("usable")) { + return drawCursor(_activeCursor); + } else if (_cursor) { + return drawCursor(_cursor); + } + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::saveGame(int slot, const char *desc, bool quickSave) { + return SaveLoad::saveGame(slot, desc, quickSave, _gameRef); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadGame(int slot) { + //_gameRef->LOG(0, "Load start %d", BaseUtils::GetUsedMemMB()); + + _loading = false; + _scheduledLoadSlot = -1; + + Common::String filename = SaveLoad::getSaveSlotFilename(slot); + + return loadGame(filename.c_str()); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadGame(const char *filename) { + return SaveLoad::loadGame(filename, _gameRef); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayWindows(bool inGame) { + bool res; + + // did we lose focus? focus topmost window + if (_focusedWindow == NULL || !_focusedWindow->_visible || _focusedWindow->_disable) { + _focusedWindow = NULL; + for (int i = _windows.size() - 1; i >= 0; i--) { + if (_windows[i]->_visible && !_windows[i]->_disable) { + _focusedWindow = _windows[i]; + break; + } + } + } + + // display all windows + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i]->_visible && _windows[i]->_inGame == inGame) { + + res = _windows[i]->display(); + if (DID_FAIL(res)) { + return res; + } + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::playMusic(int channel, const char *filename, bool looping, uint32 loopStart) { + if (channel >= NUM_MUSIC_CHANNELS) { + _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + delete _music[channel]; + _music[channel] = NULL; + + _music[channel] = new BaseSound(_gameRef); + if (_music[channel] && DID_SUCCEED(_music[channel]->setSound(filename, Audio::Mixer::kMusicSoundType, true))) { + if (_musicStartTime[channel]) { + _music[channel]->setPositionTime(_musicStartTime[channel]); + _musicStartTime[channel] = 0; + } + if (loopStart) { + _music[channel]->setLoopStart(loopStart); + } + return _music[channel]->play(looping); + } else { + delete _music[channel]; + _music[channel] = NULL; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::stopMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + _music[channel]->stop(); + delete _music[channel]; + _music[channel] = NULL; + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::pauseMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + return _music[channel]->pause(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::resumeMusic(int channel) { + if (channel >= NUM_MUSIC_CHANNELS) { + _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + if (_music[channel]) { + return _music[channel]->resume(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::setMusicStartTime(int channel, uint32 time) { + if (channel >= NUM_MUSIC_CHANNELS) { + _gameRef->LOG(0, "**Error** Attempting to use music channel %d (max num channels: %d)", channel, NUM_MUSIC_CHANNELS); + return STATUS_FAILED; + } + + _musicStartTime[channel] = time; + if (_music[channel] && _music[channel]->isPlaying()) { + return _music[channel]->setPositionTime(time); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::loadSettings(const char *filename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(SETTINGS) + TOKEN_TABLE(GAME) + TOKEN_TABLE(STRING_TABLE) + TOKEN_TABLE(RESOLUTION) + TOKEN_TABLE(REQUIRE_3D_ACCELERATION) + TOKEN_TABLE(REQUIRE_SOUND) + TOKEN_TABLE(HWTL_MODE) + TOKEN_TABLE(ALLOW_WINDOWED_MODE) + TOKEN_TABLE(ALLOW_ACCESSIBILITY_TAB) + TOKEN_TABLE(ALLOW_ABOUT_TAB) + TOKEN_TABLE(ALLOW_ADVANCED) + TOKEN_TABLE(ALLOW_DESKTOP_RES) + TOKEN_TABLE(REGISTRY_PATH) + TOKEN_TABLE(RICH_SAVED_GAMES) + TOKEN_TABLE(SAVED_GAME_EXT) + TOKEN_TABLE(GUID) + TOKEN_TABLE_END + + + byte *origBuffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (origBuffer == NULL) { + _gameRef->LOG(0, "BaseGame::LoadSettings failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret = STATUS_OK; + + byte *buffer = origBuffer; + byte *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SETTINGS) { + _gameRef->LOG(0, "'SETTINGS' keyword expected in game settings file."); + return STATUS_FAILED; + } + buffer = params; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_GAME: + delete[] _settingsGameFile; + _settingsGameFile = new char[strlen((char *)params) + 1]; + if (_settingsGameFile) { + strcpy(_settingsGameFile, (char *)params); + } + break; + + case TOKEN_STRING_TABLE: + if (DID_FAIL(_stringTable->loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_RESOLUTION: + parser.scanStr((char *)params, "%d,%d", &_settingsResWidth, &_settingsResHeight); + break; + + case TOKEN_REQUIRE_3D_ACCELERATION: + parser.scanStr((char *)params, "%b", &_settingsRequireAcceleration); + break; + + case TOKEN_REQUIRE_SOUND: + parser.scanStr((char *)params, "%b", &_settingsRequireSound); + break; + + case TOKEN_HWTL_MODE: + parser.scanStr((char *)params, "%d", &_settingsTLMode); + break; + + case TOKEN_ALLOW_WINDOWED_MODE: + parser.scanStr((char *)params, "%b", &_settingsAllowWindowed); + break; + + case TOKEN_ALLOW_DESKTOP_RES: + parser.scanStr((char *)params, "%b", &_settingsAllowDesktopRes); + break; + + case TOKEN_ALLOW_ADVANCED: + parser.scanStr((char *)params, "%b", &_settingsAllowAdvanced); + break; + + case TOKEN_ALLOW_ACCESSIBILITY_TAB: + parser.scanStr((char *)params, "%b", &_settingsAllowAccessTab); + break; + + case TOKEN_ALLOW_ABOUT_TAB: + parser.scanStr((char *)params, "%b", &_settingsAllowAboutTab); + break; + + case TOKEN_REGISTRY_PATH: + //BaseEngine::instance().getRegistry()->setBasePath((char *)params); + break; + + case TOKEN_RICH_SAVED_GAMES: + parser.scanStr((char *)params, "%b", &_richSavedGames); + break; + + case TOKEN_SAVED_GAME_EXT: + BaseUtils::setString(&_savedGameExt, (char *)params); + break; + + case TOKEN_GUID: + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in game settings '%s'", filename); + ret = STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading game settings '%s'", filename); + ret = STATUS_FAILED; + } + + _settingsAllowWindowed = true; // TODO: These two settings should probably be cleaned out altogether. + _compressedSavegames = true; + + delete[] origBuffer; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_activeObject)); + persistMgr->transfer(TMEMBER(_capturedObject)); + persistMgr->transfer(TMEMBER(_cursorNoninteractive)); + persistMgr->transfer(TMEMBER(_editorMode)); + persistMgr->transfer(TMEMBER(_fader)); + persistMgr->transfer(TMEMBER(_freezeLevel)); + persistMgr->transfer(TMEMBER(_focusedWindow)); + persistMgr->transfer(TMEMBER(_fontStorage)); + persistMgr->transfer(TMEMBER(_interactive)); + persistMgr->transfer(TMEMBER(_keyboardState)); + persistMgr->transfer(TMEMBER(_lastTime)); + persistMgr->transfer(TMEMBER(_mainObject)); + for (int i = 0; i < NUM_MUSIC_CHANNELS; i++) { + persistMgr->transfer(TMEMBER(_music[i])); + persistMgr->transfer(TMEMBER(_musicStartTime[i])); + } + + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_offsetPercentX)); + persistMgr->transfer(TMEMBER(_offsetPercentY)); + + persistMgr->transfer(TMEMBER(_origInteractive)); + persistMgr->transfer(TMEMBER_INT(_origState)); + persistMgr->transfer(TMEMBER(_personalizedSave)); + persistMgr->transfer(TMEMBER(_quitting)); + + _regObjects.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_scEngine)); + //persistMgr->transfer(TMEMBER(_soundMgr)); + persistMgr->transfer(TMEMBER_INT(_state)); + //persistMgr->transfer(TMEMBER(_surfaceStorage)); + persistMgr->transfer(TMEMBER(_subtitles)); + persistMgr->transfer(TMEMBER(_subtitlesSpeed)); + persistMgr->transfer(TMEMBER(_systemFont)); + persistMgr->transfer(TMEMBER(_videoFont)); + persistMgr->transfer(TMEMBER(_videoSubtitles)); + + persistMgr->transfer(TMEMBER(_timer)); + persistMgr->transfer(TMEMBER(_timerDelta)); + persistMgr->transfer(TMEMBER(_timerLast)); + + persistMgr->transfer(TMEMBER(_liveTimer)); + persistMgr->transfer(TMEMBER(_liveTimerDelta)); + persistMgr->transfer(TMEMBER(_liveTimerLast)); + + persistMgr->transfer(TMEMBER(_musicCrossfadeRunning)); + persistMgr->transfer(TMEMBER(_musicCrossfadeStartTime)); + persistMgr->transfer(TMEMBER(_musicCrossfadeLength)); + persistMgr->transfer(TMEMBER(_musicCrossfadeChannel1)); + persistMgr->transfer(TMEMBER(_musicCrossfadeChannel2)); + persistMgr->transfer(TMEMBER(_musicCrossfadeSwap)); + + _renderer->persistSaveLoadImages(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_textEncoding)); + persistMgr->transfer(TMEMBER(_textRTL)); + + persistMgr->transfer(TMEMBER(_soundBufferSizeSec)); + persistMgr->transfer(TMEMBER(_suspendedRendering)); + + persistMgr->transfer(TMEMBER(_mouseLockRect)); + + _windows.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_suppressScriptErrors)); + persistMgr->transfer(TMEMBER(_autorunDisabled)); + + persistMgr->transfer(TMEMBER(_autoSaveOnExit)); + persistMgr->transfer(TMEMBER(_autoSaveSlot)); + persistMgr->transfer(TMEMBER(_cursorHidden)); + + if (!persistMgr->getIsSaving()) { + _quitting = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::focusWindow(UIWindow *window) { + UIWindow *prev = _focusedWindow; + + for (uint32 i = 0; i < _windows.size(); i++) { + if (_windows[i] == window) { + if (i < _windows.size() - 1) { + _windows.remove_at(i); + _windows.add(window); + + _gameRef->_focusedWindow = window; + } + + if (window->_mode == WINDOW_NORMAL && prev != window && _gameRef->validObject(prev) && (prev->_mode == WINDOW_EXCLUSIVE || prev->_mode == WINDOW_SYSTEM_EXCLUSIVE)) { + return focusWindow(prev); + } else { + return STATUS_OK; + } + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::freeze(bool includingMusic) { + if (_freezeLevel == 0) { + _scEngine->pauseAll(); + _soundMgr->pauseAll(includingMusic); + _origState = _state; + _origInteractive = _interactive; + _interactive = true; + } + _state = GAME_FROZEN; + _freezeLevel++; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::unfreeze() { + if (_freezeLevel == 0) { + return STATUS_OK; + } + + _freezeLevel--; + if (_freezeLevel == 0) { + _state = _origState; + _interactive = _origInteractive; + _scEngine->resumeAll(); + _soundMgr->resumeAll(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::handleKeypress(Common::Event *event, bool printable) { + if (isVideoPlaying()) { + if (event->kbd.keycode == Common::KEYCODE_ESCAPE) { + stopVideo(); + } + return true; + } + + if (event->type == Common::EVENT_QUIT) { + onWindowClose(); + return true; + } + + _keyboardState->handleKeyPress(event); + _keyboardState->readKey(event); +// TODO + + if (_focusedWindow) { + if (!_gameRef->_focusedWindow->handleKeypress(event, _keyboardState->_currentPrintable)) { + /*if (event->type != SDL_TEXTINPUT) {*/ + if (_gameRef->_focusedWindow->canHandleEvent("Keypress")) { + _gameRef->_focusedWindow->applyEvent("Keypress"); + } else { + applyEvent("Keypress"); + } + /*}*/ + } + return true; + } else { /*if (event->type != SDL_TEXTINPUT)*/ + applyEvent("Keypress"); + return true; + } + + return false; +} + +void BaseGame::handleKeyRelease(Common::Event *event) { + _keyboardState->handleKeyRelease(event); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::handleMouseWheel(int delta) { + bool handled = false; + if (_focusedWindow) { + handled = _gameRef->_focusedWindow->handleMouseWheel(delta); + + if (!handled) { + if (delta < 0 && _gameRef->_focusedWindow->canHandleEvent("MouseWheelDown")) { + _gameRef->_focusedWindow->applyEvent("MouseWheelDown"); + handled = true; + } else if (_gameRef->_focusedWindow->canHandleEvent("MouseWheelUp")) { + _gameRef->_focusedWindow->applyEvent("MouseWheelUp"); + handled = true; + } + + } + } + + if (!handled) { + if (delta < 0) { + applyEvent("MouseWheelDown"); + } else { + applyEvent("MouseWheelUp"); + } + } + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor) { + if (verMajor) { + *verMajor = DCGF_VER_MAJOR; + } + if (verMinor) { + *verMinor = DCGF_VER_MINOR; + } + + if (extMajor) { + *extMajor = 0; + } + if (extMinor) { + *extMinor = 0; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setWindowTitle() { + if (_renderer) { + char title[512]; + strcpy(title, _caption[0]); + if (title[0] != '\0') { + strcat(title, " - "); + } + strcat(title, "WME Lite"); + + + Utf8String utf8Title; + if (_textEncoding == TEXT_UTF8) { + utf8Title = Utf8String(title); + } else { + warning("BaseGame::SetWindowTitle - Ignoring textencoding"); + utf8Title = Utf8String(title); + /* WideString wstr = StringUtil::AnsiToWide(Title); + title = StringUtil::WideToUtf8(wstr);*/ + } + warning("BaseGame::SetWindowTitle: Ignoring value: %s", utf8Title.c_str()); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::setActiveObject(BaseObject *obj) { + // not-active when game is frozen + if (obj && !_gameRef->_interactive && !obj->_nonIntMouseEvents) { + obj = NULL; + } + + if (obj == _activeObject) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->applyEvent("MouseLeave"); + } + //if (ValidObject(_activeObject)) _activeObject->applyEvent("MouseLeave"); + _activeObject = obj; + if (_activeObject) { + _activeObject->applyEvent("MouseEntry"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::pushViewport(BaseViewport *viewport) { + _viewportSP++; + if (_viewportSP >= (int32)_viewportStack.size()) { + _viewportStack.add(viewport); + } else { + _viewportStack[_viewportSP] = viewport; + } + + _renderer->setViewport(viewport->getRect()); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::popViewport() { + _viewportSP--; + if (_viewportSP < -1) { + _gameRef->LOG(0, "Fatal: Viewport stack underflow!"); + } + + if (_viewportSP >= 0 && _viewportSP < (int32)_viewportStack.size()) { + _renderer->setViewport(_viewportStack[_viewportSP]->getRect()); + } else _renderer->setViewport(_renderer->_drawOffsetX, + _renderer->_drawOffsetY, + _renderer->_width + _renderer->_drawOffsetX, + _renderer->_height + _renderer->_drawOffsetY); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getCurrentViewportRect(Rect32 *rect, bool *custom) { + if (rect == NULL) { + return STATUS_FAILED; + } else { + if (_viewportSP >= 0) { + BasePlatform::copyRect(rect, _viewportStack[_viewportSP]->getRect()); + if (custom) { + *custom = true; + } + } else { + BasePlatform::setRect(rect, _renderer->_drawOffsetX, + _renderer->_drawOffsetY, + _renderer->_width + _renderer->_drawOffsetX, + _renderer->_height + _renderer->_drawOffsetY); + if (custom) { + *custom = false; + } + } + + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::getCurrentViewportOffset(int *offsetX, int *offsetY) { + if (_viewportSP >= 0) { + if (offsetX) { + *offsetX = _viewportStack[_viewportSP]->_offsetX; + } + if (offsetY) { + *offsetY = _viewportStack[_viewportSP]->_offsetY; + } + } else { + if (offsetX) { + *offsetX = 0; + } + if (offsetY) { + *offsetY = 0; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::windowLoadHook(UIWindow *win, char **buf, char **params) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::setInteractive(bool state) { + _interactive = state; + if (_transMgr) { + _transMgr->_origInteractive = state; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseGame::resetMousePos() { + Common::Point p; + p.x = _mousePos.x + _renderer->_drawOffsetX; + p.y = _mousePos.y + _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(p.x, p.y); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayContent(bool doUpdate, bool displayAll) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayContentSimple() { + // fill black + _renderer->fill(0, 0, 0); + _renderer->displayIndicator(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::updateMusicCrossfade() { + /* byte globMusicVol = _soundMgr->getVolumePercent(SOUND_MUSIC); */ + + if (!_musicCrossfadeRunning) { + return STATUS_OK; + } + if (_state == GAME_FROZEN) { + return STATUS_OK; + } + + if (_musicCrossfadeChannel1 < 0 || _musicCrossfadeChannel1 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel1]) { + _musicCrossfadeRunning = false; + return STATUS_OK; + } + if (_musicCrossfadeChannel2 < 0 || _musicCrossfadeChannel2 >= NUM_MUSIC_CHANNELS || !_music[_musicCrossfadeChannel2]) { + _musicCrossfadeRunning = false; + return STATUS_OK; + } + + if (!_music[_musicCrossfadeChannel1]->isPlaying()) { + _music[_musicCrossfadeChannel1]->play(); + } + if (!_music[_musicCrossfadeChannel2]->isPlaying()) { + _music[_musicCrossfadeChannel2]->play(); + } + + uint32 currentTime = _gameRef->_liveTimer - _musicCrossfadeStartTime; + + if (currentTime >= _musicCrossfadeLength) { + _musicCrossfadeRunning = false; + //_music[_musicCrossfadeChannel2]->setVolume(GlobMusicVol); + _music[_musicCrossfadeChannel2]->setVolumePercent(100); + + _music[_musicCrossfadeChannel1]->stop(); + //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol); + _music[_musicCrossfadeChannel1]->setVolumePercent(100); + + + if (_musicCrossfadeSwap) { + // swap channels + BaseSound *dummy = _music[_musicCrossfadeChannel1]; + int dummyInt = _musicStartTime[_musicCrossfadeChannel1]; + + _music[_musicCrossfadeChannel1] = _music[_musicCrossfadeChannel2]; + _musicStartTime[_musicCrossfadeChannel1] = _musicStartTime[_musicCrossfadeChannel2]; + + _music[_musicCrossfadeChannel2] = dummy; + _musicStartTime[_musicCrossfadeChannel2] = dummyInt; + } + } else { + //_music[_musicCrossfadeChannel1]->setVolume(GlobMusicVol - (float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol); + //_music[_musicCrossfadeChannel2]->setVolume((float)CurrentTime / (float)_musicCrossfadeLength * GlobMusicVol); + _music[_musicCrossfadeChannel1]->setVolumePercent((int)(100.0f - (float)currentTime / (float)_musicCrossfadeLength * 100.0f)); + _music[_musicCrossfadeChannel2]->setVolumePercent((int)((float)currentTime / (float)_musicCrossfadeLength * 100.0f)); + + //_gameRef->QuickMessageForm("%d %d", _music[_musicCrossfadeChannel1]->GetVolume(), _music[_musicCrossfadeChannel2]->GetVolume()); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::resetContent() { + _scEngine->clearGlobals(); + //_timer = 0; + //_liveTimer = 0; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::DEBUG_DumpClassRegistry() { + warning("DEBUG_DumpClassRegistry - untested"); + Common::DumpFile *f = new Common::DumpFile; + f->open("zz_class_reg_dump.log"); + + SystemClassRegistry::getInstance()->dumpClasses(f); + + f->close(); + delete f; + _gameRef->quickMessage("Classes dump completed."); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::invalidateDeviceObjects() { + for (uint32 i = 0; i < _regObjects.size(); i++) { + _regObjects[i]->invalidateDeviceObjects(); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::restoreDeviceObjects() { + for (uint32 i = 0; i < _regObjects.size(); i++) { + _regObjects[i]->restoreDeviceObjects(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::setWaitCursor(const char *filename) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + + _cursorNoninteractive = new BaseSprite(_gameRef); + if (!_cursorNoninteractive || DID_FAIL(_cursorNoninteractive->loadFile(filename))) { + delete _cursorNoninteractive; + _cursorNoninteractive = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isVideoPlaying() { + if (_videoPlayer->isPlaying()) { + return true; + } + if (_theoraPlayer && _theoraPlayer->isPlaying()) { + return true; + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::stopVideo() { + if (_videoPlayer->isPlaying()) { + _videoPlayer->stop(); + } + if (_theoraPlayer && _theoraPlayer->isPlaying()) { + _theoraPlayer->stop(); + delete _theoraPlayer; + _theoraPlayer = NULL; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::drawCursor(BaseSprite *cursor) { + if (!cursor) { + return STATUS_FAILED; + } + if (cursor != _lastCursor) { + cursor->reset(); + _lastCursor = cursor; + } + return cursor->draw(_mousePos.x, _mousePos.y); +} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onActivate(bool activate, bool refreshMouse) { + if (_shuttingDown || !_renderer) { + return STATUS_OK; + } + + _renderer->_active = activate; + + if (refreshMouse) { + Point32 p; + getMousePos(&p); + setActiveObject(_renderer->getObjectAt(p.x, p.y)); + } + + if (activate) { + _soundMgr->resumeAll(); + } else { + _soundMgr->pauseAll(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftDown() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftClick"); + } + } + + if (_activeObject != NULL) { + _capturedObject = _activeObject; + } + _mouseLeftDown = true; + BasePlatform::setCapture(/*_renderer->_window*/); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_LEFT); + } + + BasePlatform::releaseCapture(); + _capturedObject = NULL; + _mouseLeftDown = false; + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseLeftDblClick() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_LEFT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("LeftDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("LeftDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightDblClick() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_DBLCLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightDoubleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightDoubleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightDown() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseRightUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_RIGHT); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("RightRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("RightRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseMiddleDown() { + if (_state == GAME_RUNNING && !_interactive) { + return STATUS_OK; + } + + if (_activeObject) { + _activeObject->handleMouse(MOUSE_CLICK, MOUSE_BUTTON_MIDDLE); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleClick")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("MiddleClick"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onMouseMiddleUp() { + if (_activeObject) { + _activeObject->handleMouse(MOUSE_RELEASE, MOUSE_BUTTON_MIDDLE); + } + + bool handled = _state == GAME_RUNNING && DID_SUCCEED(applyEvent("MiddleRelease")); + if (!handled) { + if (_activeObject != NULL) { + _activeObject->applyEvent("MiddleRelease"); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onPaint() { + if (_renderer && _renderer->_windowed && _renderer->_ready) { + _renderer->initLoop(); + displayContent(false, true); + displayDebugInfo(); + _renderer->windowedBlt(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onWindowClose() { + if (canHandleEvent("QuitGame")) { + if (_state != GAME_FROZEN) { + _gameRef->applyEvent("QuitGame"); + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::displayDebugInfo() { + char str[100]; + + if (_debugShowFPS) { + sprintf(str, "FPS: %d", _gameRef->_fps); + _systemFont->drawText((byte *)str, 0, 0, 100, TAL_LEFT); + } + + if (_gameRef->_debugDebugMode) { + if (!_gameRef->_renderer->_windowed) { + sprintf(str, "Mode: %dx%dx%d", _renderer->_width, _renderer->_height, _renderer->_bPP); + } else { + sprintf(str, "Mode: %dx%d windowed", _renderer->_width, _renderer->_height); + } + + strcat(str, " ("); + strcat(str, _renderer->getName().c_str()); + strcat(str, ")"); + _systemFont->drawText((byte *)str, 0, 0, _renderer->_width, TAL_RIGHT); + + _renderer->displayDebugInfo(); + + int scrTotal, scrRunning, scrWaiting, scrPersistent; + scrTotal = _scEngine->getNumScripts(&scrRunning, &scrWaiting, &scrPersistent); + sprintf(str, "Running scripts: %d (r:%d w:%d p:%d)", scrTotal, scrRunning, scrWaiting, scrPersistent); + _systemFont->drawText((byte *)str, 0, 70, _renderer->_width, TAL_RIGHT); + + + sprintf(str, "Timer: %d", _timer); + _gameRef->_systemFont->drawText((byte *)str, 0, 130, _renderer->_width, TAL_RIGHT); + + if (_activeObject != NULL) { + _systemFont->drawText((const byte *)_activeObject->getName(), 0, 150, _renderer->_width, TAL_RIGHT); + } + + sprintf(str, "GfxMem: %dMB", _usedMem / (1024 * 1024)); + _systemFont->drawText((byte *)str, 0, 170, _renderer->_width, TAL_RIGHT); + + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::getMousePos(Point32 *pos) { + BasePlatform::getCursorPos(pos); + + pos->x -= _renderer->_drawOffsetX; + pos->y -= _renderer->_drawOffsetY; + + /* + // Windows can squish maximized window if it's larger than desktop + // so we need to modify mouse position appropriately (tnx mRax) + if (_renderer->_windowed && ::IsZoomed(_renderer->_window)) { + Common::Rect rc; + ::GetClientRect(_renderer->_window, &rc); + Pos->x *= _gameRef->_renderer->_realWidth; + Pos->x /= (rc.right - rc.left); + Pos->y *= _gameRef->_renderer->_realHeight; + Pos->y /= (rc.bottom - rc.top); + } + */ + + if (_mouseLockRect.left != 0 && _mouseLockRect.right != 0 && _mouseLockRect.top != 0 && _mouseLockRect.bottom != 0) { + if (!BasePlatform::ptInRect(&_mouseLockRect, *pos)) { + pos->x = MAX(_mouseLockRect.left, pos->x); + pos->y = MAX(_mouseLockRect.top, pos->y); + + pos->x = MIN(_mouseLockRect.right, pos->x); + pos->y = MIN(_mouseLockRect.bottom, pos->y); + + Point32 newPos = *pos; + + newPos.x += _renderer->_drawOffsetX; + newPos.y += _renderer->_drawOffsetY; + + BasePlatform::setCursorPos(newPos.x, newPos.y); + } + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::miniUpdate() { // TODO: Is this really necessary, it used to update sound, but the mixer does that now. + if (!_miniUpdateEnabled) { + return; + } + + if (g_system->getMillis() - _lastMiniUpdate > 200) { + _lastMiniUpdate = g_system->getMillis(); + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::onScriptShutdown(ScScript *script) { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isLeftDoubleClick() { + return isDoubleClick(0); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isRightDoubleClick() { + return isDoubleClick(1); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseGame::isDoubleClick(int buttonIndex) { + uint32 maxDoubleCLickTime = 500; + int maxMoveX = 4; + int maxMoveY = 4; + + Point32 pos; + BasePlatform::getCursorPos(&pos); + + int moveX = abs(pos.x - _lastClick[buttonIndex].posX); + int moveY = abs(pos.y - _lastClick[buttonIndex].posY); + + + if (_lastClick[buttonIndex].time == 0 || g_system->getMillis() - _lastClick[buttonIndex].time > maxDoubleCLickTime || moveX > maxMoveX || moveY > maxMoveY) { + _lastClick[buttonIndex].time = g_system->getMillis(); + _lastClick[buttonIndex].posX = pos.x; + _lastClick[buttonIndex].posY = pos.y; + return false; + } else { + _lastClick[buttonIndex].time = 0; + return true; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::autoSaveOnExit() { + _soundMgr->saveSettings(); + ConfMan.flushToDisk(); + + if (!_autoSaveOnExit) { + return; + } + if (_state == GAME_FROZEN) { + return; + } + + saveGame(_autoSaveSlot, "autosave", true); +} + +////////////////////////////////////////////////////////////////////////// +void BaseGame::addMem(int bytes) { + _usedMem += bytes; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString BaseGame::getDeviceType() const { + return "computer"; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_game.h b/engines/wintermute/base/base_game.h new file mode 100644 index 0000000000..0f764b3d03 --- /dev/null +++ b/engines/wintermute/base/base_game.h @@ -0,0 +1,361 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_GAME_H +#define WINTERMUTE_BASE_GAME_H + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/math/rect32.h" +#include "common/events.h" + +namespace Wintermute { + +typedef void (*ENGINE_LOG_CALLBACK)(char *text, bool result, void *data); + +class BaseSoundMgr; +class BaseFader; +class BaseFont; +class BaseFileManager; +class BaseTransitionMgr; +class ScEngine; +class BaseFontStorage; +class BaseStringTable; +class BaseQuickMsg; +class UIWindow; +class BaseViewport; +class BaseRenderer; +class BaseRegistry; +class BaseSaveThumbHelper; +class BaseSurfaceStorage; +class SXMath; +class BaseKeyboardState; +class VideoPlayer; +class VideoTheoraPlayer; + +#define NUM_MUSIC_CHANNELS 5 + +class BaseGame: public BaseObject { +public: + DECLARE_PERSISTENT(BaseGame, BaseObject) + + virtual bool onScriptShutdown(ScScript *script); + + virtual bool onActivate(bool activate, bool refreshMouse); + virtual bool onMouseLeftDown(); + virtual bool onMouseLeftUp(); + virtual bool onMouseLeftDblClick(); + virtual bool onMouseRightDblClick(); + virtual bool onMouseRightDown(); + virtual bool onMouseRightUp(); + virtual bool onMouseMiddleDown(); + virtual bool onMouseMiddleUp(); + virtual bool onPaint(); + virtual bool onWindowClose(); + + bool isLeftDoubleClick(); + bool isRightDoubleClick(); + + bool _autorunDisabled; + + uint32 _lastMiniUpdate; + bool _miniUpdateEnabled; + + virtual void miniUpdate(); + + void getMousePos(Point32 *Pos); + Rect32 _mouseLockRect; + + bool _shuttingDown; + + virtual bool displayDebugInfo(); + bool _debugShowFPS; + + bool _suspendedRendering; + int _soundBufferSizeSec; + + TTextEncoding _textEncoding; + bool _textRTL; + + virtual bool resetContent(); + + void DEBUG_DumpClassRegistry(); + bool setWaitCursor(const char *filename); + + int _thumbnailWidth; + int _thumbnailHeight; + + bool _editorMode; + void getOffset(int *offsetX, int *offsetY); + void setOffset(int offsetX, int offsetY); + int getSequence(); + + int _offsetY; + int _offsetX; + float _offsetPercentX; + float _offsetPercentY; + BaseObject *_mainObject; + + bool initInput(); + bool initLoop(); + uint32 _currentTime; + uint32 _deltaTime; + BaseFont *_systemFont; + BaseFont *_videoFont; + bool initialize1(); + bool initialize2(); + bool initialize3(); + BaseTransitionMgr *_transMgr; + + void LOG(bool res, const char *fmt, ...); + + BaseRenderer *_renderer; + BaseSoundMgr *_soundMgr; + ScEngine *_scEngine; + SXMath *_mathClass; + BaseSurfaceStorage *_surfaceStorage; + BaseFontStorage *_fontStorage; + BaseGame(const Common::String &gameId); + virtual ~BaseGame(); + + void DEBUG_DebugDisable(); + void DEBUG_DebugEnable(const char *filename = NULL); + bool _debugDebugMode; + + void *_debugLogFile; + int _sequence; + virtual bool loadFile(const char *filename); + virtual bool loadBuffer(byte *buffer, bool complete = true); + + int _viewportSP; + + BaseStringTable *_stringTable; + int _settingsResWidth; + int _settingsResHeight; + char *_settingsGameFile; + bool _suppressScriptErrors; + bool _mouseLeftDown; // TODO: Hide + + virtual bool externalCall(ScScript *script, ScStack *stack, ScStack *thisStack, char *name); + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + // compatibility bits + bool _compatKillMethodThreads; + + const char* getGameId() { return _gameId.c_str(); } + void setGameId(const Common::String& gameId) { _gameId = gameId; } + uint32 _surfaceGCCycleTime; + bool _smartCache; // RO + bool _subtitles; // RO + + int _scheduledLoadSlot; + bool _loading; + + virtual bool handleMouseWheel(int delta); + bool _quitting; + virtual bool getVersion(byte *verMajor, byte *verMinor, byte *extMajor, byte *extMinor); + + virtual bool handleKeypress(Common::Event *event, bool printable = false); + virtual void handleKeyRelease(Common::Event *event); + + bool unfreeze(); + bool freeze(bool includingMusic = true); + bool focusWindow(UIWindow *window); + bool _loadInProgress; + UIWindow *_focusedWindow; + bool _editorForceScripts; + + static void invalidateValues(void *value, void *data); + + bool loadSettings(const char *filename); + + bool displayWindows(bool inGame = false); + bool _useD3D; + virtual bool cleanup(); + bool loadGame(int slot); + bool loadGame(const char *filename); + bool saveGame(int slot, const char *desc, bool quickSave = false); + virtual bool showCursor(); + + BaseObject *_activeObject; + + bool _interactive; + TGameState _state; + TGameState _origState; + bool _origInteractive; + uint32 _timer; + uint32 _timerDelta; + uint32 _timerLast; + + uint32 _liveTimer; + uint32 _liveTimerDelta; + uint32 _liveTimerLast; + + BaseObject *_capturedObject; + Point32 _mousePos; + bool validObject(BaseObject *object); + bool unregisterObject(BaseObject *object); + bool registerObject(BaseObject *object); + void quickMessage(const char *text); + void quickMessageForm(char *fmt, ...); + bool displayQuickMsg(); + + virtual bool displayContent(bool update = true, bool displayAll = false); + virtual bool displayContentSimple(); + bool _forceNonStreamedSounds; + void resetMousePos(); + int _subtitlesSpeed; + void setInteractive(bool state); + virtual bool windowLoadHook(UIWindow *win, char **buf, char **params); + virtual bool windowScriptMethodHook(UIWindow *win, ScScript *script, ScStack *stack, const char *name); + bool getCurrentViewportOffset(int *offsetX = NULL, int *offsetY = NULL); + bool getCurrentViewportRect(Rect32 *rect, bool *custom = NULL); + bool popViewport(); + bool pushViewport(BaseViewport *Viewport); + bool setActiveObject(BaseObject *Obj); + BaseSprite *_lastCursor; + bool drawCursor(BaseSprite *Cursor); + + BaseSaveThumbHelper *_cachedThumbnail; + void addMem(int bytes); + bool _touchInterface; + bool _constrainedMemory; +protected: + BaseSprite *_loadingIcon; + int _loadingIconX; + int _loadingIconY; + int _loadingIconPersistent; + + BaseFader *_fader; + + int _freezeLevel; + VideoPlayer *_videoPlayer; + VideoTheoraPlayer *_theoraPlayer; +private: + bool _mouseRightDown; + bool _mouseMidlleDown; + bool _settingsRequireAcceleration; + bool _settingsAllowWindowed; + bool _settingsAllowAdvanced; + bool _settingsAllowAccessTab; + bool _settingsAllowAboutTab; + bool _settingsRequireSound; + bool _settingsAllowDesktopRes; + int _settingsTLMode; + virtual bool invalidateDeviceObjects(); + virtual bool restoreDeviceObjects(); + + char *_localSaveDir; + bool _saveDirChecked; + bool _richSavedGames; + char *_savedGameExt; + + bool _reportTextureFormat; + + // FPS stuff + uint32 _lastTime; + uint32 _fpsTime; + uint32 _framesRendered; + Common::String _gameId; + + void setEngineLogCallback(ENGINE_LOG_CALLBACK callback = NULL, void *data = NULL); + ENGINE_LOG_CALLBACK _engineLogCallback; + void *_engineLogCallbackData; + + bool _videoSubtitles; + uint32 _musicStartTime[NUM_MUSIC_CHANNELS]; + bool _compressedSavegames; + + bool _personalizedSave; + + void setWindowTitle(); + + bool resumeMusic(int channel); + bool setMusicStartTime(int channel, uint32 time); + bool pauseMusic(int channel); + bool stopMusic(int channel); + bool playMusic(int channel, const char *filename, bool looping = true, uint32 loopStart = 0); + BaseSound *_music[NUM_MUSIC_CHANNELS]; + bool _musicCrossfadeRunning; + bool _musicCrossfadeSwap; + uint32 _musicCrossfadeStartTime; + uint32 _musicCrossfadeLength; + int _musicCrossfadeChannel1; + int _musicCrossfadeChannel2; + + BaseSprite *_cursorNoninteractive; + BaseKeyboardState *_keyboardState; + + uint32 _fps; + bool updateMusicCrossfade(); + + bool isVideoPlaying(); + bool stopVideo(); + + BaseArray<BaseQuickMsg *> _quickMessages; + BaseArray<UIWindow *> _windows; + BaseArray<BaseViewport *> _viewportStack; + BaseArray<BaseObject *> _regObjects; + + AnsiString getDeviceType() const; + + struct LastClickInfo { + LastClickInfo() { + posX = posY = 0; + time = 0; + } + + int posX; + int posY; + uint32 time; + }; + + LastClickInfo _lastClick[2]; + bool isDoubleClick(int buttonIndex); + uint32 _usedMem; + + + +protected: + // WME Lite specific + bool _autoSaveOnExit; + int _autoSaveSlot; + bool _cursorHidden; + +public: + void autoSaveOnExit(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_keyboard_state.cpp b/engines/wintermute/base/base_keyboard_state.cpp new file mode 100644 index 0000000000..da7baafd2d --- /dev/null +++ b/engines/wintermute/base/base_keyboard_state.cpp @@ -0,0 +1,309 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/system.h" +#include "common/keyboard.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseKeyboardState, false) + +////////////////////////////////////////////////////////////////////////// +BaseKeyboardState::BaseKeyboardState(BaseGame *inGame) : BaseScriptable(inGame) { + _currentPrintable = false; + _currentCharCode = 0; + _currentKeyData = 0; + + _currentShift = false; + _currentAlt = false; + _currentControl = false; + + _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum + for (int i = 0; i < 323; i++) { + _keyStates[i] = false; + } +} + +////////////////////////////////////////////////////////////////////////// +BaseKeyboardState::~BaseKeyboardState() { + delete[] _keyStates; +} + +void BaseKeyboardState::handleKeyPress(Common::Event *event) { + if (event->type == Common::EVENT_KEYDOWN) { + _keyStates[event->kbd.keycode] = true; + } +} + +void BaseKeyboardState::handleKeyRelease(Common::Event *event) { + if (event->type == Common::EVENT_KEYUP) { + _keyStates[event->kbd.keycode] = false; + } +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // IsKeyDown + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "IsKeyDown") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + int vKey; + + if (val->_type == VAL_STRING && strlen(val->getString()) > 0) { + const char *str = val->getString(); + char temp = str[0]; + if (temp >= 'A' && temp <= 'Z') { + temp += ('a' - 'A'); + } + vKey = (int)temp; + } else { + vKey = val->getInt(); + } + + bool isDown = _keyStates[vKeyToKeyCode(vKey)]; + + stack->pushBool(isDown); + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseKeyboardState::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("keyboard"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Key + ////////////////////////////////////////////////////////////////////////// + else if (name == "Key") { + if (_currentPrintable) { + char key[2]; + key[0] = (char)_currentCharCode; + key[1] = '\0'; + _scValue->setString(key); + } else { + _scValue->setString(""); + } + + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Printable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Printable") { + _scValue->setBool(_currentPrintable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // KeyCode + ////////////////////////////////////////////////////////////////////////// + else if (name == "KeyCode") { + _scValue->setInt(_currentCharCode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsShift + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsShift") { + _scValue->setBool(_currentShift); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsAlt + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsAlt") { + _scValue->setBool(_currentAlt); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // IsControl + ////////////////////////////////////////////////////////////////////////// + else if (name == "IsControl") { + _scValue->setBool(_currentControl); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + if (_renderer) SetWindowText(_renderer->_window, _name); + return STATUS_OK; + } + + else*/ return BaseScriptable::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseKeyboardState::scToString() { + return "[keyboard state]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::readKey(Common::Event *event) { + //_currentPrintable = (event->type == SDL_TEXTINPUT); // TODO + _currentCharCode = keyCodeToVKey(event); + if ((_currentCharCode <= Common::KEYCODE_z && _currentCharCode >= Common::KEYCODE_a) || + (_currentCharCode <= Common::KEYCODE_9 && _currentCharCode >= Common::KEYCODE_0) || + (_currentCharCode == Common::KEYCODE_SPACE)) { + _currentPrintable = true; + } else { + _currentPrintable = false; + } + //_currentKeyData = KeyData; + + _currentControl = isControlDown(); + _currentAlt = isAltDown(); + _currentShift = isShiftDown(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::persist(BasePersistenceManager *persistMgr) { + //if (!persistMgr->getIsSaving()) cleanup(); + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_currentAlt)); + persistMgr->transfer(TMEMBER(_currentCharCode)); + persistMgr->transfer(TMEMBER(_currentControl)); + persistMgr->transfer(TMEMBER(_currentKeyData)); + persistMgr->transfer(TMEMBER(_currentPrintable)); + persistMgr->transfer(TMEMBER(_currentShift)); + + if (!persistMgr->getIsSaving()) { + _keyStates = new uint8[323]; // Hardcoded size for the common/keyboard.h enum + for (int i = 0; i < 323; i++) { + _keyStates[i] = false; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isShiftDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_SHIFT); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isControlDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_CTRL); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseKeyboardState::isAltDown() { + int mod = g_system->getEventManager()->getModifierState(); + return (mod & Common::KBD_ALT); +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseKeyboardState::keyCodeToVKey(Common::Event *event) { + if (event->type != Common::EVENT_KEYDOWN) { + return 0; + } + + switch (event->kbd.keycode) { + case Common::KEYCODE_KP_ENTER: + return Common::KEYCODE_RETURN; + default: + return (uint32)event->kbd.keycode; + } +} + +enum VKeyCodes { + kVkSpace = 32, + kVkLeft = 37, + kVkUp = 38, + kVkRight = 39, + kVkDown = 40 +}; + +////////////////////////////////////////////////////////////////////////// +Common::KeyCode BaseKeyboardState::vKeyToKeyCode(uint32 vkey) { + // todo + switch (vkey) { + case kVkSpace: + return Common::KEYCODE_SPACE; + break; + case kVkLeft: + return Common::KEYCODE_LEFT; + break; + case kVkRight: + return Common::KEYCODE_RIGHT; + break; + case kVkUp: + return Common::KEYCODE_UP; + break; + case kVkDown: + return Common::KEYCODE_DOWN; + break; + default: + warning("Unknown VKEY: %d", vkey); + return (Common::KeyCode)vkey; + break; + } + +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_keyboard_state.h b/engines/wintermute/base/base_keyboard_state.h new file mode 100644 index 0000000000..dfd0efdec0 --- /dev/null +++ b/engines/wintermute/base/base_keyboard_state.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_KEYBOARD_STATE_H +#define WINTERMUTE_BASE_KEYBOARD_STATE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "common/keyboard.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseKeyboardState : public BaseScriptable { +public: + uint32 _currentKeyData; + uint32 _currentCharCode; + bool _currentPrintable; + + bool _currentShift; + bool _currentAlt; + bool _currentControl; + + DECLARE_PERSISTENT(BaseKeyboardState, BaseScriptable) + BaseKeyboardState(BaseGame *inGame); + virtual ~BaseKeyboardState(); + bool readKey(Common::Event *event); + + void handleKeyPress(Common::Event *event); + void handleKeyRelease(Common::Event *event); + static bool isShiftDown(); + static bool isControlDown(); + static bool isAltDown(); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +private: + uint8 *_keyStates; + uint32 keyCodeToVKey(Common::Event *event); + Common::KeyCode vKeyToKeyCode(uint32 vkey); //TODO, reimplement using ScummVM-backend +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_named_object.cpp b/engines/wintermute/base/base_named_object.cpp new file mode 100644 index 0000000000..915bf24d7f --- /dev/null +++ b/engines/wintermute/base/base_named_object.cpp @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_named_object.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject(BaseGame *inGame) : BaseClass(inGame) { + _name = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject() : BaseClass() { + _name = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::BaseNamedObject(TDynamicConstructor, TDynamicConstructor) { + _name = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseNamedObject::~BaseNamedObject(void) { + delete[] _name; + _name = NULL; +} + + +////////////////////////////////////////////////////////////////////// +void BaseNamedObject::setName(const char *name) { + delete[] _name; + _name = NULL; + + if (name == NULL) { + return; + } + + _name = new char [strlen(name) + 1]; + if (_name != NULL) { + strcpy(_name, name); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_named_object.h b/engines/wintermute/base/base_named_object.h new file mode 100644 index 0000000000..77a00cee45 --- /dev/null +++ b/engines/wintermute/base/base_named_object.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_NAMED_OBJECT_H +#define WINTERMUTE_BASE_NAMED_OBJECT_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseNamedObject : public BaseClass { + char *_name; +public: + BaseNamedObject(BaseGame *inGame); + BaseNamedObject(); + virtual ~BaseNamedObject(void); + BaseNamedObject(TDynamicConstructor, TDynamicConstructor); + + const char *getName() { return _name; } + void setName(const char *name); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_object.cpp b/engines/wintermute/base/base_object.cpp new file mode 100644 index 0000000000..eba8416485 --- /dev/null +++ b/engines/wintermute/base/base_object.cpp @@ -0,0 +1,1246 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseObject, false) + +////////////////////////////////////////////////////////////////////// +BaseObject::BaseObject(BaseGame *inGame) : BaseScriptHolder(inGame) { + _posX = _posY = 0; + _movable = true; + _zoomable = true; + _registrable = true; + _shadowable = true; + _rotatable = false; + _is3D = false; + + _alphaColor = 0; + _scale = -1; + _relativeScale = 0; + + _scaleX = -1; + _scaleY = -1; + + _ready = true; + + _soundEvent = NULL; + + _iD = _gameRef->getSequence(); + + BasePlatform::setRectEmpty(&_rect); + _rectSet = false; + + _cursor = NULL; + _activeCursor = NULL; + _sharedCursors = false; + + _sFX = NULL; + _sFXStart = 0; + _sFXVolume = 100; + _autoSoundPanning = true; + + _editorAlwaysRegister = false; + _editorSelected = false; + + _editorOnly = false; + + _rotate = 0.0f; + _rotateValid = false; + _relativeRotate = 0.0f; + + for (int i = 0; i < 7; i++) { + _caption[i] = NULL; + } + _saveState = true; + + _nonIntMouseEvents = false; + + // sound FX + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + + _blendMode = BLEND_NORMAL; +} + + +////////////////////////////////////////////////////////////////////// +BaseObject::~BaseObject() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::cleanup() { + if (_gameRef && _gameRef->_activeObject == this) { + _gameRef->_activeObject = NULL; + } + + BaseScriptHolder::cleanup(); + delete[] _soundEvent; + _soundEvent = NULL; + + if (!_sharedCursors) { + delete _cursor; + delete _activeCursor; + _cursor = NULL; + _activeCursor = NULL; + } + delete _sFX; + _sFX = NULL; + + for (int i = 0; i < 7; i++) { + delete[] _caption[i]; + _caption[i] = NULL; + } + + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseObject::setCaption(const char *caption, int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7) { + return; + } + + delete[] _caption[caseVal - 1]; + _caption[caseVal - 1] = new char[strlen(caption) + 1]; + if (_caption[caseVal - 1]) { + strcpy(_caption[caseVal - 1], caption); + _gameRef->_stringTable->expand(&_caption[caseVal - 1]); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseObject::getCaption(int caseVal) { + if (caseVal == 0) { + caseVal = 1; + } + if (caseVal < 1 || caseVal > 7 || _caption[caseVal - 1] == NULL) { + return ""; + } else { + return _caption[caseVal - 1]; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::listen(BaseScriptHolder *param1, uint32 param2) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // SkipTo + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SkipTo") == 0) { + stack->correctParams(2); + _posX = stack->pop()->getInt(); + _posY = stack->pop()->getInt(); + afterMove(); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Caption") == 0) { + stack->correctParams(1); + stack->pushString(getCaption(stack->pop()->getInt())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCursor") == 0) { + stack->correctParams(1); + if (DID_SUCCEED(setCursor(stack->pop()->getString()))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveCursor") == 0) { + stack->correctParams(0); + if (!_sharedCursors) { + delete _cursor; + _cursor = NULL; + } else { + _cursor = NULL; + + } + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCursor") == 0) { + stack->correctParams(0); + if (!_cursor || !_cursor->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_cursor->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetCursorObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetCursorObject") == 0) { + stack->correctParams(0); + if (!_cursor) { + stack->pushNULL(); + } else { + stack->pushNative(_cursor, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HasCursor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HasCursor") == 0) { + stack->correctParams(0); + + if (_cursor) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetCaption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCaption") == 0) { + stack->correctParams(2); + setCaption(stack->pop()->getString(), stack->pop()->getInt()); + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadSound") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + if (DID_SUCCEED(playSFX(filename, false, false))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlaySound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlaySound") == 0) { + stack->correctParams(3); + + const char *filename; + bool looping; + uint32 loopStart; + + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + ScValue *val3 = stack->pop(); + + if (val1->_type == VAL_BOOL) { + filename = NULL; + looping = val1->getBool(); + loopStart = val2->getInt(); + } else { + if (val1->isNULL()) { + filename = NULL; + } else { + filename = val1->getString(); + } + looping = val2->isNULL() ? false : val2->getBool(); + loopStart = val3->getInt(); + } + + if (DID_FAIL(playSFX(filename, looping, true, NULL, loopStart))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PlaySoundEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PlaySoundEvent") == 0) { + stack->correctParams(2); + + const char *filename; + const char *eventName; + + ScValue *val1 = stack->pop(); + ScValue *val2 = stack->pop(); + + if (val2->isNULL()) { + filename = NULL; + eventName = val1->getString(); + } else { + filename = val1->getString(); + eventName = val2->getString(); + } + + if (DID_FAIL(playSFX(filename, false, true, eventName))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // StopSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "StopSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(stopSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(pauseSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ResumeSound + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ResumeSound") == 0) { + stack->correctParams(0); + + if (DID_FAIL(resumeSFX())) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsSoundPlaying + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsSoundPlaying") == 0) { + stack->correctParams(0); + + if (_sFX && _sFX->isPlaying()) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSoundPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSoundPosition") == 0) { + stack->correctParams(1); + + uint32 time = stack->pop()->getInt(); + if (DID_FAIL(setSFXTime(time))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundPosition") == 0) { + stack->correctParams(0); + + if (!_sFX) { + stack->pushInt(0); + } else { + stack->pushInt(_sFX->getPositionTime()); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetSoundVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSoundVolume") == 0) { + stack->correctParams(1); + + int volume = stack->pop()->getInt(); + if (DID_FAIL(setSFXVolume(volume))) { + stack->pushBool(false); + } else { + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetSoundVolume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSoundVolume") == 0) { + stack->correctParams(0); + + if (!_sFX) { + stack->pushInt(_sFXVolume); + } else { + stack->pushInt(_sFX->getVolumePercent()); + } + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SoundFXNone + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXNone") == 0) { + stack->correctParams(0); + _sFXType = SFX_NONE; + _sFXParam1 = 0; + _sFXParam2 = 0; + _sFXParam3 = 0; + _sFXParam4 = 0; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundFXEcho + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXEcho") == 0) { + stack->correctParams(4); + _sFXType = SFX_ECHO; + _sFXParam1 = (float)stack->pop()->getFloat(0); // Wet/Dry Mix [%] (0-100) + _sFXParam2 = (float)stack->pop()->getFloat(0); // Feedback [%] (0-100) + _sFXParam3 = (float)stack->pop()->getFloat(333.0f); // Left Delay [ms] (1-2000) + _sFXParam4 = (float)stack->pop()->getFloat(333.0f); // Right Delay [ms] (1-2000) + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundFXReverb + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundFXReverb") == 0) { + stack->correctParams(4); + _sFXType = SFX_REVERB; + _sFXParam1 = (float)stack->pop()->getFloat(0); // In Gain [dB] (-96 - 0) + _sFXParam2 = (float)stack->pop()->getFloat(0); // Reverb Mix [dB] (-96 - 0) + _sFXParam3 = (float)stack->pop()->getFloat(1000.0f); // Reverb Time [ms] (0.001 - 3000) + _sFXParam4 = (float)stack->pop()->getFloat(0.001f); // HighFreq RT Ratio (0.001 - 0.999) + stack->pushNULL(); + + return STATUS_OK; + } else { + return BaseScriptHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + else if (name == "Caption") { + _scValue->setString(getCaption(1)); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (name == "X") { + _scValue->setInt(_posX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (name == "Y") { + _scValue->setInt(_posY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(getHeight()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Ready (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Ready") { + _scValue->setBool(_ready); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Movable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Movable") { + _scValue->setBool(_movable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Registrable/Interactive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Registrable" || name == "Interactive") { + _scValue->setBool(_registrable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Zoomable/Scalable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Zoomable" || name == "Scalable") { + _scValue->setBool(_zoomable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Rotatable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotatable") { + _scValue->setBool(_rotatable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + _scValue->setInt((int)_alphaColor); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // BlendMode + ////////////////////////////////////////////////////////////////////////// + else if (name == "BlendMode") { + _scValue->setInt((int)_blendMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale") { + if (_scale < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scale); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleX + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleX") { + if (_scaleX < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scaleX); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleY + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleY") { + if (_scaleY < 0) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_scaleY); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeScale + ////////////////////////////////////////////////////////////////////////// + else if (name == "RelativeScale") { + _scValue->setFloat((double)_relativeScale); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotate + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotate") { + if (!_rotateValid) { + _scValue->setNULL(); + } else { + _scValue->setFloat((double)_rotate); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeRotate + ////////////////////////////////////////////////////////////////////////// + else if (name == "RelativeRotate") { + _scValue->setFloat((double)_relativeRotate); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Colorable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Colorable") { + _scValue->setBool(_shadowable); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // SoundPanning + ////////////////////////////////////////////////////////////////////////// + else if (name == "SoundPanning") { + _scValue->setBool(_autoSoundPanning); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveState + ////////////////////////////////////////////////////////////////////////// + else if (name == "SaveState") { + _scValue->setBool(_saveState); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NonIntMouseEvents + ////////////////////////////////////////////////////////////////////////// + else if (name == "NonIntMouseEvents") { + _scValue->setBool(_nonIntMouseEvents); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccCaption + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccCaption") { + _scValue->setNULL(); + return _scValue; + } else { + return BaseScriptHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Caption + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Caption") == 0) { + setCaption(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "X") == 0) { + _posX = value->getInt(); + afterMove(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Y") == 0) { + _posY = value->getInt(); + afterMove(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Movable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Movable") == 0) { + _movable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Registrable/Interactive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Registrable") == 0 || strcmp(name, "Interactive") == 0) { + _registrable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Zoomable/Scalable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Zoomable") == 0 || strcmp(name, "Scalable") == 0) { + _zoomable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotatable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotatable") == 0) { + _rotatable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaColor") == 0) { + _alphaColor = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // BlendMode + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "BlendMode") == 0) { + int i = value->getInt(); + if (i < BLEND_NORMAL || i >= NUM_BLEND_MODES) { + i = BLEND_NORMAL; + } + _blendMode = (TSpriteBlendMode)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale") == 0) { + if (value->isNULL()) { + _scale = -1; + } else { + _scale = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleX") == 0) { + if (value->isNULL()) { + _scaleX = -1; + } else { + _scaleX = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ScaleY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleY") == 0) { + if (value->isNULL()) { + _scaleY = -1; + } else { + _scaleY = (float)value->getFloat(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeScale + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RelativeScale") == 0) { + _relativeScale = (float)value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotate") == 0) { + if (value->isNULL()) { + _rotate = 0.0f; + _rotateValid = false; + } else { + _rotate = (float)value->getFloat(); + _rotateValid = true; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RelativeRotate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RelativeRotate") == 0) { + _relativeRotate = (float)value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Colorable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Colorable") == 0) { + _shadowable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SoundPanning + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SoundPanning") == 0) { + _autoSoundPanning = value->getBool(); + if (!_autoSoundPanning) { + resetSoundPan(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SaveState + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SaveState") == 0) { + _saveState = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // NonIntMouseEvents + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "NonIntMouseEvents") == 0) { + _nonIntMouseEvents = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AccCaption + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AccCaption") == 0) { + return STATUS_OK; + } else { + return BaseScriptHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseObject::scToString() { + return "[object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::showCursor() { + if (_cursor) { + return _gameRef->drawCursor(_cursor); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::persist(BasePersistenceManager *persistMgr) { + BaseScriptHolder::persist(persistMgr); + + for (int i = 0; i < 7; i++) { + persistMgr->transfer(TMEMBER(_caption[i])); + } + persistMgr->transfer(TMEMBER(_activeCursor)); + persistMgr->transfer(TMEMBER(_alphaColor)); + persistMgr->transfer(TMEMBER(_autoSoundPanning)); + persistMgr->transfer(TMEMBER(_cursor)); + persistMgr->transfer(TMEMBER(_sharedCursors)); + persistMgr->transfer(TMEMBER(_editorAlwaysRegister)); + persistMgr->transfer(TMEMBER(_editorOnly)); + persistMgr->transfer(TMEMBER(_editorSelected)); + persistMgr->transfer(TMEMBER(_iD)); + persistMgr->transfer(TMEMBER(_is3D)); + persistMgr->transfer(TMEMBER(_movable)); + persistMgr->transfer(TMEMBER(_posX)); + persistMgr->transfer(TMEMBER(_posY)); + persistMgr->transfer(TMEMBER(_relativeScale)); + persistMgr->transfer(TMEMBER(_rotatable)); + persistMgr->transfer(TMEMBER(_scale)); + persistMgr->transfer(TMEMBER(_sFX)); + persistMgr->transfer(TMEMBER(_sFXStart)); + persistMgr->transfer(TMEMBER(_sFXVolume)); + persistMgr->transfer(TMEMBER(_ready)); + persistMgr->transfer(TMEMBER(_rect)); + persistMgr->transfer(TMEMBER(_rectSet)); + persistMgr->transfer(TMEMBER(_registrable)); + persistMgr->transfer(TMEMBER(_shadowable)); + persistMgr->transfer(TMEMBER(_soundEvent)); + persistMgr->transfer(TMEMBER(_zoomable)); + + persistMgr->transfer(TMEMBER(_scaleX)); + persistMgr->transfer(TMEMBER(_scaleY)); + + persistMgr->transfer(TMEMBER(_rotate)); + persistMgr->transfer(TMEMBER(_rotateValid)); + persistMgr->transfer(TMEMBER(_relativeRotate)); + + persistMgr->transfer(TMEMBER(_saveState)); + persistMgr->transfer(TMEMBER(_nonIntMouseEvents)); + + persistMgr->transfer(TMEMBER_INT(_sFXType)); + persistMgr->transfer(TMEMBER(_sFXParam1)); + persistMgr->transfer(TMEMBER(_sFXParam2)); + persistMgr->transfer(TMEMBER(_sFXParam3)); + persistMgr->transfer(TMEMBER(_sFXParam4)); + + + persistMgr->transfer(TMEMBER_INT(_blendMode)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setCursor(const char *filename) { + if (!_sharedCursors) { + delete _cursor; + _cursor = NULL; + } + + _sharedCursors = false; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile(filename))) { + delete _cursor; + _cursor = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setActiveCursor(const char *filename) { + delete _activeCursor; + _activeCursor = new BaseSprite(_gameRef); + if (!_activeCursor || DID_FAIL(_activeCursor->loadFile(filename))) { + delete _activeCursor; + _activeCursor = NULL; + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +int BaseObject::getHeight() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleMouse(TMouseEvent event, TMouseButton button) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleKeypress(Common::Event *event, bool printable) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::handleMouseWheel(int delta) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::playSFX(const char *filename, bool looping, bool playNow, const char *eventName, uint32 loopStart) { + // just play loaded sound + if (filename == NULL && _sFX) { + if (_gameRef->_editorMode || _sFXStart) { + _sFX->setVolumePercent(_sFXVolume); + _sFX->setPositionTime(_sFXStart); + if (!_gameRef->_editorMode) { + _sFXStart = 0; + } + } + if (playNow) { + setSoundEvent(eventName); + if (loopStart) { + _sFX->setLoopStart(loopStart); + } + return _sFX->play(looping); + } else { + return STATUS_OK; + } + } + + if (filename == NULL) { + return STATUS_FAILED; + } + + // create new sound + delete _sFX; + + _sFX = new BaseSound(_gameRef); + if (_sFX && DID_SUCCEED(_sFX->setSound(filename, Audio::Mixer::kSFXSoundType, true))) { + _sFX->setVolumePercent(_sFXVolume); + if (_sFXStart) { + _sFX->setPositionTime(_sFXStart); + _sFXStart = 0; + } + _sFX->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4); + if (playNow) { + setSoundEvent(eventName); + if (loopStart) { + _sFX->setLoopStart(loopStart); + } + return _sFX->play(looping); + } else { + return STATUS_OK; + } + } else { + delete _sFX; + _sFX = NULL; + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::stopSFX(bool deleteSound) { + if (_sFX) { + _sFX->stop(); + if (deleteSound) { + delete _sFX; + _sFX = NULL; + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::pauseSFX() { + if (_sFX) { + return _sFX->pause(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::resumeSFX() { + if (_sFX) { + return _sFX->resume(); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setSFXTime(uint32 time) { + _sFXStart = time; + if (_sFX && _sFX->isPlaying()) { + return _sFX->setPositionTime(time); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::setSFXVolume(int volume) { + _sFXVolume = volume; + if (_sFX) { + return _sFX->setVolumePercent(volume); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::updateSounds() { + if (_soundEvent) { + if (_sFX && !_sFX->isPlaying()) { + applyEvent(_soundEvent); + setSoundEvent(NULL); + } + } + + if (_sFX) { + updateOneSound(_sFX); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::updateOneSound(BaseSound *sound) { + bool ret = STATUS_OK; + + if (sound) { + if (_autoSoundPanning) { + ret = sound->setPan(_gameRef->_soundMgr->posToPan(_posX - _gameRef->_offsetX, _posY - _gameRef->_offsetY)); + } + + ret = sound->applyFX(_sFXType, _sFXParam1, _sFXParam2, _sFXParam3, _sFXParam4); + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::resetSoundPan() { + if (!_sFX) { + return STATUS_OK; + } else { + return _sFX->setPan(0.0f); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::getExtendedFlag(const char *flagName) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::isReady() { + return _ready; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseObject::setSoundEvent(const char *eventName) { + delete[] _soundEvent; + _soundEvent = NULL; + if (eventName) { + _soundEvent = new char[strlen(eventName) + 1]; + if (_soundEvent) { + strcpy(_soundEvent, eventName); + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseObject::afterMove() { + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_object.h b/engines/wintermute/base/base_object.h new file mode 100644 index 0000000000..d7d91a25f6 --- /dev/null +++ b/engines/wintermute/base/base_object.h @@ -0,0 +1,147 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_OBJECT_H +#define WINTERMUTE_BASE_OBJECT_H + + +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/persistent.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseSprite; +class BaseSound; +class BaseSurface; +class BaseScriptHolder; +class ScValue; +class ScStack; +class ScScript; +class BaseObject : public BaseScriptHolder { +protected: + bool _autoSoundPanning; + uint32 _sFXStart; + bool setSFXTime(uint32 time); + bool setSFXVolume(int volume); + bool resumeSFX(); + bool pauseSFX(); + bool stopSFX(bool deleteSound = true); + bool playSFX(const char *filename, bool looping = false, bool playNow = true, const char *eventName = NULL, uint32 loopStart = 0); + BaseSound *_sFX; + TSFXType _sFXType; + float _sFXParam1; + float _sFXParam2; + float _sFXParam3; + float _sFXParam4; + float _relativeRotate; + bool _rotateValid; + float _rotate; + void setSoundEvent(const char *eventName); + bool _rotatable; + float _scaleX; + float _scaleY; + float _relativeScale; + bool _editorSelected; + bool _editorAlwaysRegister; + bool _ready; + Rect32 _rect; + bool _rectSet; + int _iD; + char *_soundEvent; +public: + TSpriteBlendMode _blendMode; + virtual bool afterMove(); + float _scale; + uint32 _alphaColor; + virtual bool isReady(); + virtual bool getExtendedFlag(const char *flagName); + virtual bool resetSoundPan(); + virtual bool updateSounds(); + bool updateOneSound(BaseSound *sound); + int _sFXVolume; + + virtual bool handleMouseWheel(int delta); + virtual bool handleMouse(TMouseEvent event, TMouseButton button); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + virtual int getHeight(); + bool setCursor(const char *filename); + bool setActiveCursor(const char *filename); + bool cleanup(); + const char *getCaption(int caseVal = 1); + void setCaption(const char *caption, int caseVal = 1); + + bool _editorOnly; + bool _is3D; + + DECLARE_PERSISTENT(BaseObject, BaseScriptHolder) + virtual bool showCursor(); + BaseSprite *_cursor; + bool _sharedCursors; + BaseSprite *_activeCursor; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + + bool _movable; + bool _zoomable; + bool _shadowable; + int _posY; + int _posX; + bool _registrable; + char *_caption[7]; + bool _saveState; + + BaseObject(BaseGame *inGame); + virtual ~BaseObject(); + // base + virtual bool update() { + return STATUS_FAILED; + }; + virtual bool display() { + return STATUS_FAILED; + }; + virtual bool invalidateDeviceObjects() { + return STATUS_OK; + }; + virtual bool restoreDeviceObjects() { + return STATUS_OK; + }; + bool _nonIntMouseEvents; + + +public: + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_parser.cpp b/engines/wintermute/base/base_parser.cpp new file mode 100644 index 0000000000..9a0e9e3ad9 --- /dev/null +++ b/engines/wintermute/base/base_parser.cpp @@ -0,0 +1,467 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/util.h" + +#define WHITESPACE " \t\n\r" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////// +BaseParser::BaseParser() { + _whiteSpace = new char [strlen(WHITESPACE) + 1]; + strcpy(_whiteSpace, WHITESPACE); +} + + +////////////////////////////////////////////////////////////////////// +BaseParser::~BaseParser() { + if (_whiteSpace != NULL) { + delete[] _whiteSpace; + } +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getLastOffender() { + return _lastOffender; +} + + +////////////////////////////////////////////////////////////////////// +int32 BaseParser::getObject(char **buf, const TokenDesc *tokens, char **name, char **data) { + skipCharacters(buf, _whiteSpace); + + // skip comment lines. + while (**buf == ';') { + *buf = strchr(*buf, '\n'); + _parserLine++; + skipCharacters(buf, _whiteSpace); + } + + if (! **buf) { // at end of file + return PARSERR_EOF; + } + + // find the token. + // TODO: for now just use brute force. Improve later. + while (tokens->id != 0) { + if (!scumm_strnicmp(tokens->token, *buf, strlen(tokens->token))) { + // here we could be matching PART of a string + // we could detect this here or the token list + // could just have the longer tokens first in the list + break; + } + ++tokens; + } + if (tokens->id == 0) { + char *p = strchr(*buf, '\n'); + if (p && p > *buf) { + strncpy(_lastOffender, *buf, MIN((uint32)255, (uint32)(p - *buf))); // TODO, clean + } else { + strcpy(_lastOffender, ""); + } + + return PARSERR_TOKENNOTFOUND; + } + // skip the token + *buf += strlen(tokens->token); + skipCharacters(buf, _whiteSpace); + + // get optional name + *name = getSubText(buf, '\'', '\''); // single quotes + skipCharacters(buf, _whiteSpace); + + // get optional data + if (**buf == '=') { // An assignment rather than a command/object. + *data = getAssignmentText(buf); + } else { + *data = getSubText(buf, '{', '}'); + } + + return tokens->id; +} + + +////////////////////////////////////////////////////////////////////// +int32 BaseParser::getCommand(char **buf, const TokenDesc *tokens, char **params) { + if (!*buf) { + return PARSERR_TOKENNOTFOUND; + } + BaseEngine::instance().getGameRef()->miniUpdate(); + char *name; + return getObject(buf, tokens, &name, params); +} + + +////////////////////////////////////////////////////////////////////// +void BaseParser::skipCharacters(char **buf, const char *toSkip) { + char ch; + while ((ch = **buf) != 0) { + if (ch == '\n') { + _parserLine++; + } + if (strchr(toSkip, ch) == NULL) { + return; + } + ++*buf; // skip this character + } + // we must be at the end of the buffer if we get here +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getSubText(char **buf, char open, char close) { + if (**buf == 0 || **buf != open) { + return 0; + } + ++*buf; // skip opening delimiter + char *result = *buf; + + // now find the closing delimiter + char theChar; + long skip = 1; + + if (open == close) { // we cant nest identical delimiters + open = 0; + } + while ((theChar = **buf) != 0) { + if (theChar == open) { + ++skip; + } + if (theChar == close) { + if (--skip == 0) { + **buf = 0; // null terminate the result string + ++*buf; // move past the closing delimiter + break; + } + } + ++*buf; // try next character + } + return result; +} + + +////////////////////////////////////////////////////////////////////// +char *BaseParser::getAssignmentText(char **buf) { + ++*buf; // skip the '=' + skipCharacters(buf, _whiteSpace); + char *result = *buf; + + + if (*result == '"') { + result = getSubText(buf, '"', '"'); + } else { + // now, we need to find the next whitespace to end the data + char theChar; + + while ((theChar = **buf) != 0) { + if (theChar <= 0x20) { // space and control chars + break; + } + ++*buf; + } + **buf = 0; // null terminate it + if (theChar) { // skip the terminator + ++*buf; + } + } + + return result; +} + +////////////////////////////////////////////////////////////////////// +Common::String BaseParser::getToken(char **buf) { + char token[100]; // TODO: Remove static + char *b = *buf, *t = token; + while (true) { + while (*b && (*b == ' ' || *b == '\n' || *b == 13 || *b == 10 || *b == '\t')) { + b++; + } + if (*b == ';') + while (*b && *b != '\n' && *b != 13 && *b != 10) { + b++; + } + else { + break; + } + } + + if (*b == '\'') { + b++; + while (*b && *b != '\'') { + *t++ = *b++; + } + *t++ = 0; + if (*b == '\'') { + b++; + } + } else if (*b == '(' || *b == ')' || *b == '=' || *b == ',' || *b == '[' || *b == ']' || + *b == '%' || *b == ':' || *b == '{' || *b == '}') { + *t++ = *b++; + *t++ = 0; + } else if (*b == '.' && (*(b + 1) < '0' || *(b + 1) > '9')) { + *t++ = *b++; + *t++ = 0; + } else if ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-') { + while (*b && ((*b >= '0' && *b <= '9') || *b == '.' || *b == '-')) { + *t++ = *b++; + } + *t++ = 0; + } else if ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_') { + while (*b && ((*b >= 'A' && *b <= 'Z') || (*b >= 'a' && *b <= 'z') || *b == '_')) { + *t++ = *b++; + } + *t++ = 0; + } else if (*b == 0) { + *buf = b; + return NULL; + } else { + // Error. + return NULL; + } + + *buf = b; + return token; +} + + +////////////////////////////////////////////////////////////////////// +float BaseParser::getTokenFloat(char **buf) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (!((*t >= '0' && *t <= '9') || *t == '-' || *t == '.')) { + // Error situation. We handle this by return 0. + return 0.; + } + float rc = (float)atof(t); + return rc; +} + + +////////////////////////////////////////////////////////////////////// +int BaseParser::getTokenInt(char **buf) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (!((*t >= '0' && *t <= '9') || *t == '-')) { + // Error situation. We handle this by return 0. + return 0; + } + int rc = atoi(t); + return rc; +} + + +////////////////////////////////////////////////////////////////////// +void BaseParser::skipToken(char **buf, char *tok, char * /*msg*/) { + Common::String token = getToken(buf); + const char *t = token.c_str(); + if (strcmp(t, tok)) { + return; // Error + } +} + + +////////////////////////////////////////////////////////////////////// +int BaseParser::scanStr(const char *in, const char *format, ...) { + va_list arg; + va_start(arg, format); + + int num = 0; + in += strspn(in, " \t\n\f"); + + while (*format && *in) { + if (*format == '%') { + format++; + switch (*format) { + case 'd': { + int *a = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + *a = atoi(in); + in += strspn(in, "0123456789+- \t\n\f"); + num++; + break; + } + case 'D': { + int i; + int *list = va_arg(arg, int *); + int *nr = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + i = 0; + while ((*in >= '0' && *in <= '9') || *in == '+' || *in == '-') { + list[i++] = atoi(in); + in += strspn(in, "0123456789+-"); + in += strspn(in, " \t\n\f"); + if (*in != ',') { + break; + } + in++; + in += strspn(in, " \t\n\f"); + } + *nr = i; + num++; + break; + } + case 'b': { + bool *a = va_arg(arg, bool *); + in += strspn(in, " \t\n\f"); + const char *in2 = in + strspn(in, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + int l = (int)(in2 - in); + + *a = (bool)(!scumm_strnicmp(in, "yes", l) || !scumm_strnicmp(in, "true", l) || !scumm_strnicmp(in, "on", l) || + !scumm_strnicmp(in, "1", l)); + + + in = in2 + strspn(in2, " \t\n\f"); + num++; + break; + } + case 'f': { + float *a = va_arg(arg, float *); + in += strspn(in, " \t\n\f"); + *a = (float)atof(in); + in += strspn(in, "0123456789.eE+- \t\n\f"); + num++; + break; + } + case 'F': { + int i; + float *list = va_arg(arg, float *); + int *nr = va_arg(arg, int *); + in += strspn(in, " \t\n\f"); + i = 0; + while ((*in >= '0' && *in <= '9') || *in == '.' || *in == '+' || *in == '-' || *in == 'e' || *in == 'E') { + list[i++] = (float)atof(in); + in += strspn(in, "0123456789.eE+-"); + in += strspn(in, " \t\n\f"); + if (*in != ',') { + break; + } + in++; + in += strspn(in, " \t\n\f"); + } + *nr = i; + num++; + break; + } + case 's': { + char *a = va_arg(arg, char *); + in += strspn(in, " \t\n\f"); + if (*in == '\'') { + in++; + const char *in2 = strchr(in, '\''); + if (in2) { + Common::strlcpy(a, in, (int)(in2 - in) + 1); + in = in2 + 1; + } else { + strcpy(a, in); + in = strchr(in, 0); + } + } else { + const char *in2 = in + strspn(in, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789."); + Common::strlcpy(a, in, (int)(in2 - in) + 1); + in = in2; + } + in += strspn(in, " \t\n\f"); + num++; + break; + } + case 'S': { + char *a = va_arg(arg, char *); + in += strspn(in, " \t\n\f"); + if (*in == '\"') { + in++; + while (*in != '\"') { + if (*in == '\\') { + in++; + switch (*in) { + case '\\': + *a++ = '\\'; + break; + case 'n': + *a++ = '\n'; + break; + case 'r': + *a++ = '\r'; + break; + case 't': + *a++ = '\t'; + break; + case '"': + *a++ = '"'; + break; + default: + *a++ = '\\'; + *a++ = *in; + break; + } //switch + in++; + } else { + *a++ = *in++; + } + } //while in string + in++; + num++; + } //if string started + + //terminate string + *a = '\0'; + break; + } + } + if (*format) { + format++; + } + } else if (*format == ' ') { + format++; + in += strspn(in, " \t\n\f"); + } else if (*in == *format) { + in++; + format++; + } else { + num = -1; + break; + } + } + + va_end(arg); + + return num; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_parser.h b/engines/wintermute/base/base_parser.h new file mode 100644 index 0000000000..76ca8ea856 --- /dev/null +++ b/engines/wintermute/base/base_parser.h @@ -0,0 +1,88 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PARSER_H +#define WINTERMUTE_BASE_PARSER_H + + +#define TOKEN_DEF_START \ + enum \ + { \ + TOKEN_NONE = 0, +#define TOKEN_DEF(name) \ + TOKEN_ ## name, +#define TOKEN_DEF_END \ + TOKEN_TOTAL_COUNT \ + }; +#define TOKEN_TABLE_START(name) \ + static const BaseParser::TokenDesc name [] = \ + { +#define TOKEN_TABLE(name) \ + { TOKEN_ ## name, #name }, +#define TOKEN_TABLE_END \ + { 0, 0 } \ + }; + +#define PARSERR_GENERIC -3 +#define PARSERR_EOF -2 +#define PARSERR_TOKENNOTFOUND -1 + +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { + +class BaseParser { +public: + struct TokenDesc { + int32 id; + const char *token; + }; + +public: + int scanStr(const char *in, const char *format, ...); + int32 getCommand(char **buf, const TokenDesc *tokens, char **params); + BaseParser(); + virtual ~BaseParser(); +private: + char *getLastOffender(); + void skipToken(char **buf, char *tok, char *msg = NULL); + int getTokenInt(char **buf); + float getTokenFloat(char **buf); + Common::String getToken(char **buf); + char *getAssignmentText(char **buf); + char *getSubText(char **buf, char open, char close); + void skipCharacters(char **buf, const char *toSkip); + int32 getObject(char **buf, const TokenDesc *tokens, char **name, char **data); + int _parserLine; + char _lastOffender[255]; + char *_whiteSpace; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp new file mode 100644 index 0000000000..4cb67b87e1 --- /dev/null +++ b/engines/wintermute/base/base_persistence_manager.cpp @@ -0,0 +1,828 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/math/vector2.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/wintermute.h" +#include "graphics/decoders/bmp.h" +#include "common/memstream.h" +#include "common/str.h" +#include "common/system.h" +#include "common/savefile.h" + +namespace Wintermute { + +#define SAVE_BUFFER_INIT_SIZE 100000 +#define SAVE_BUFFER_GROW_BY 50000 + +#define SAVE_MAGIC 0x45564153 +#define SAVE_MAGIC_2 0x32564153 + +////////////////////////////////////////////////////////////////////////// +BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) { + _saving = false; +// _buffer = NULL; +// _bufferSize = 0; + _offset = 0; + _saveStream = NULL; + _loadStream = NULL; + _deleteSingleton = deleteSingleton; + if (BaseEngine::instance().getGameRef()) { + _gameRef = BaseEngine::instance().getGameRef(); + } else { + _gameRef = NULL; + } + + _richBuffer = NULL; + _richBufferSize = 0; + + _savedDescription = NULL; +// _savedTimestamp = 0; + _savedVerMajor = _savedVerMinor = _savedVerBuild = 0; + _savedExtMajor = _savedExtMinor = 0; + + _thumbnailDataSize = 0; + _thumbnailData = NULL; + if (savePrefix) { + _savePrefix = savePrefix; + } else if (_gameRef) { + _savePrefix = _gameRef->getGameId(); + } else { + _savePrefix = "wmesav"; + } +} + + +////////////////////////////////////////////////////////////////////////// +BasePersistenceManager::~BasePersistenceManager() { + cleanup(); + if (_deleteSingleton && BaseEngine::instance().getGameRef() == NULL) + BaseEngine::destroy(); +} + + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::cleanup() { + /* if (_buffer) { + if (_saving) free(_buffer); + else delete[] _buffer; // allocated by file manager + } + _buffer = NULL; + + _bufferSize = 0;*/ + _offset = 0; + + delete[] _richBuffer; + _richBuffer = NULL; + _richBufferSize = 0; + + delete[] _savedDescription; + _savedDescription = NULL; // ref to buffer +// _savedTimestamp = 0; + _savedVerMajor = _savedVerMinor = _savedVerBuild = 0; + _savedExtMajor = _savedExtMinor = 0; + + _thumbnailDataSize = 0; + if (_thumbnailData) { + delete[] _thumbnailData; + _thumbnailData = NULL; + } + + delete _loadStream; + delete _saveStream; + _loadStream = NULL; + _saveStream = NULL; +} + +Common::String BasePersistenceManager::getFilenameForSlot(int slot) const { + // 3 Digits, to allow for one save-slot for autosave + slot 1 - 100 (which will be numbered 0-99 filename-wise) + return Common::String::format("%s-save%03d.wsv", _savePrefix.c_str(), slot); +} + +void BasePersistenceManager::getSaveStateDesc(int slot, SaveStateDescriptor &desc) { + Common::String filename = getFilenameForSlot(slot); + debugC(kWintermuteDebugSaveGame, "Trying to list savegame %s in slot %d", filename.c_str(), slot); + if (DID_FAIL(readHeader(filename))) { + warning("getSavedDesc(%d) - Failed for %s", slot, filename.c_str()); + return; + } + desc.setSaveSlot(slot); + desc.setDescription(_savedDescription); + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + + if (_thumbnailDataSize > 0) { + Common::MemoryReadStream thumbStream(_thumbnailData, _thumbnailDataSize); + Graphics::BitmapDecoder bmpDecoder; + if (bmpDecoder.loadStream(thumbStream)) { + Graphics::Surface *surf = new Graphics::Surface; + surf = bmpDecoder.getSurface()->convertTo(g_system->getOverlayFormat()); + desc.setThumbnail(surf); + } + } + + desc.setSaveDate(_savedTimestamp.tm_year, _savedTimestamp.tm_mon, _savedTimestamp.tm_mday); + desc.setSaveTime(_savedTimestamp.tm_hour, _savedTimestamp.tm_min); + desc.setPlayTime(0); +} + +void BasePersistenceManager::deleteSaveSlot(int slot) { + Common::String filename = getFilenameForSlot(slot); + g_system->getSavefileManager()->removeSavefile(filename); +} + +uint32 BasePersistenceManager::getMaxUsedSlot() { + Common::String saveMask = Common::String::format("%s-save???.wsv", _savePrefix.c_str()); + Common::StringArray saves = g_system->getSavefileManager()->listSavefiles(saveMask); + Common::StringArray::iterator it = saves.begin(); + int ret = -1; + for (; it != saves.end(); ++it) { + int num = -1; + sscanf(it->c_str(), "save%d", &num); + ret = MAX(ret, num); + } + return ret; +} + +bool BasePersistenceManager::getSaveExists(int slot) { + Common::String filename = getFilenameForSlot(slot); + if (DID_FAIL(readHeader(filename))) { + return false; + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::initSave(const char *desc) { + if (!desc) { + return STATUS_FAILED; + } + + cleanup(); + _saving = true; + + _saveStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES); + + if (_saveStream) { + // get thumbnails + if (!_gameRef->_cachedThumbnail) { + _gameRef->_cachedThumbnail = new BaseSaveThumbHelper(_gameRef); + if (DID_FAIL(_gameRef->_cachedThumbnail->storeThumbnail(true))) { + delete _gameRef->_cachedThumbnail; + _gameRef->_cachedThumbnail = NULL; + } + } + + uint32 magic = DCGF_MAGIC; + putDWORD(magic); + + magic = SAVE_MAGIC_2; + putDWORD(magic); + + byte verMajor, verMinor, extMajor, extMinor; + _gameRef->getVersion(&verMajor, &verMinor, &extMajor, &extMinor); + //uint32 version = MAKELONG(MAKEWORD(VerMajor, VerMinor), MAKEWORD(ExtMajor, ExtMinor)); + _saveStream->writeByte(verMajor); + _saveStream->writeByte(verMinor); + _saveStream->writeByte(extMajor); + _saveStream->writeByte(extMinor); + + // new in ver 2 + putDWORD((uint32)DCGF_VER_BUILD); + putString(_gameRef->getName()); + + // thumbnail data size + bool thumbnailOK = false; + + if (_gameRef->_cachedThumbnail) { + if (_gameRef->_cachedThumbnail->_thumbnail) { + Common::MemoryWriteStreamDynamic thumbStream(DisposeAfterUse::YES); + if (_gameRef->_cachedThumbnail->_thumbnail->writeBMPToStream(&thumbStream)) { + _saveStream->writeUint32LE(thumbStream.size()); + _saveStream->write(thumbStream.getData(), thumbStream.size()); + } else { + _saveStream->writeUint32LE(0); + } + + thumbnailOK = true; + } + } + if (!thumbnailOK) { + putDWORD(0); + } + + // in any case, destroy the cached thumbnail once used + delete _gameRef->_cachedThumbnail; + _gameRef->_cachedThumbnail = NULL; + + uint32 dataOffset = _offset + + sizeof(uint32) + // data offset + sizeof(uint32) + strlen(desc) + 1 + // description + sizeof(uint32); // timestamp + + putDWORD(dataOffset); + putString(desc); + + g_system->getTimeAndDate(_savedTimestamp); + putTimeDate(_savedTimestamp); + _savedPlayTime = g_system->getMillis(); + _saveStream->writeUint32LE(_savedPlayTime); + } + return STATUS_OK; +} + +bool BasePersistenceManager::readHeader(const Common::String &filename) { + cleanup(); + + _saving = false; + + _loadStream = g_system->getSavefileManager()->openForLoading(filename); + //_buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &_bufferSize); + if (_loadStream) { + uint32 magic; + magic = getDWORD(); + + if (magic != DCGF_MAGIC) { + cleanup(); + return STATUS_FAILED; + } + + magic = getDWORD(); + + if (magic == SAVE_MAGIC || magic == SAVE_MAGIC_2) { + _savedVerMajor = _loadStream->readByte(); + _savedVerMinor = _loadStream->readByte(); + _savedExtMajor = _loadStream->readByte(); + _savedExtMinor = _loadStream->readByte(); + + if (magic == SAVE_MAGIC_2) { + _savedVerBuild = (byte)getDWORD(); + _savedName = getStringObj(); + + // load thumbnail + _thumbnailDataSize = getDWORD(); + if (_thumbnailDataSize > 0) { + _thumbnailData = new byte[_thumbnailDataSize]; + if (_thumbnailData) { + getBytes(_thumbnailData, _thumbnailDataSize); + } else { + _thumbnailDataSize = 0; + } + } + } else { + _savedVerBuild = 35; // last build with ver1 savegames + } + + uint32 dataOffset = getDWORD(); + + _savedDescription = getString(); + _savedTimestamp = getTimeDate(); + _savedPlayTime = _loadStream->readUint32LE(); + + _offset = dataOffset; + + return STATUS_OK; + } + } + + cleanup(); + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::initLoad(const Common::String &filename) { + if (DID_FAIL(readHeader(filename))) { + cleanup(); + return STATUS_FAILED; + } + _saving = false; + + if (_savedName == "" || scumm_stricmp(_savedName.c_str(), _gameRef->getName()) != 0) { + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game name doesn't match current game"); + cleanup(); + return STATUS_FAILED; + } + + // if save is newer version than we are, fail + if (_savedVerMajor > DCGF_VER_MAJOR || + (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor > DCGF_VER_MINOR) || + (_savedVerMajor == DCGF_VER_MAJOR && _savedVerMinor == DCGF_VER_MINOR && _savedVerBuild > DCGF_VER_BUILD) + ) { + + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game version is newer than current game"); + debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild); + cleanup(); + return STATUS_FAILED; + } + + // if save is older than the minimal version we support + if (_savedVerMajor < SAVEGAME_VER_MAJOR || + (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor < SAVEGAME_VER_MINOR) || + (_savedVerMajor == SAVEGAME_VER_MAJOR && _savedVerMinor == SAVEGAME_VER_MINOR && _savedVerBuild < SAVEGAME_VER_BUILD) + ) { + debugC(kWintermuteDebugSaveGame, "ERROR: Saved game is too old and cannot be used by this version of game engine"); + debugC(kWintermuteDebugSaveGame, "ERROR: Expected %d.%d.%d got %d.%d.%d", DCGF_VER_MAJOR, DCGF_VER_MINOR, DCGF_VER_BUILD, _savedVerMajor, _savedVerMinor, _savedVerBuild); + cleanup(); + return STATUS_FAILED; + + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::saveFile(const Common::String &filename) { + byte *prefixBuffer = _richBuffer; + uint32 prefixSize = _richBufferSize; + byte *buffer = ((Common::MemoryWriteStreamDynamic *)_saveStream)->getData(); + uint32 bufferSize = ((Common::MemoryWriteStreamDynamic *)_saveStream)->size(); + + Common::SaveFileManager *saveMan = ((WintermuteEngine *)g_engine)->getSaveFileMan(); + Common::OutSaveFile *file = saveMan->openForSaving(filename); + file->write(prefixBuffer, prefixSize); + file->write(buffer, bufferSize); + bool retVal = !file->err(); + file->finalize(); + delete file; + return retVal; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::putBytes(byte *buffer, uint32 size) { + _saveStream->write(buffer, size); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::getBytes(byte *buffer, uint32 size) { + _loadStream->read(buffer, size); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::putDWORD(uint32 val) { + _saveStream->writeUint32LE(val); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BasePersistenceManager::getDWORD() { + uint32 ret = _loadStream->readUint32LE(); + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +void BasePersistenceManager::putString(const Common::String &val) { + if (!val.size()) { + putString("(null)"); + } else { + _saveStream->writeUint32LE(val.size()); + _saveStream->writeString(val); + } +} + +Common::String BasePersistenceManager::getStringObj() { + uint32 len = _loadStream->readUint32LE(); + char *ret = new char[len + 1]; + _loadStream->read(ret, len); + ret[len] = '\0'; + + Common::String retString = ret; + delete[] ret; + + if (retString == "(null)") { + retString = ""; + } + + return retString; +} + +////////////////////////////////////////////////////////////////////////// +char *BasePersistenceManager::getString() { + uint32 len = _loadStream->readUint32LE(); + char *ret = new char[len + 1]; + _loadStream->read(ret, len); + ret[len] = '\0'; + + if (!strcmp(ret, "(null)")) { + delete[] ret; + return NULL; + } else { + return ret; + } +} + +bool BasePersistenceManager::putTimeDate(const TimeDate &t) { + _saveStream->writeSint32LE(t.tm_sec); + _saveStream->writeSint32LE(t.tm_min); + _saveStream->writeSint32LE(t.tm_hour); + _saveStream->writeSint32LE(t.tm_mday); + _saveStream->writeSint32LE(t.tm_mon); + _saveStream->writeSint32LE(t.tm_year); + // _saveStream->writeSint32LE(t.tm_wday); //TODO: Add this in when merging next + + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; +} + +TimeDate BasePersistenceManager::getTimeDate() { + TimeDate t; + t.tm_sec = _loadStream->readSint32LE(); + t.tm_min = _loadStream->readSint32LE(); + t.tm_hour = _loadStream->readSint32LE(); + t.tm_mday = _loadStream->readSint32LE(); + t.tm_mon = _loadStream->readSint32LE(); + t.tm_year = _loadStream->readSint32LE(); + // t.tm_wday = _loadStream->readSint32LE(); //TODO: Add this in when merging next + return t; +} + +void BasePersistenceManager::putFloat(float val) { + Common::String str = Common::String::format("F%f", val); + _saveStream->writeUint32LE(str.size()); + _saveStream->writeString(str); +} + +float BasePersistenceManager::getFloat() { + char *str = getString(); + float value = 0.0f; + int ret = sscanf(str, "F%f", &value); + if (ret != 1) { + warning("%s not parsed as float", str); + } + delete[] str; + return value; +} + +void BasePersistenceManager::putDouble(double val) { + Common::String str = Common::String::format("D%f", val); + str.format("D%f", val); + _saveStream->writeUint32LE(str.size()); + _saveStream->writeString(str); +} + +double BasePersistenceManager::getDouble() { + char *str = getString(); + float value = 0.0f; // TODO: Do we ever really need to carry a full double-precision number? + int ret = sscanf(str, "D%f", &value); + if (ret != 1) { + warning("%s not parsed as double", str); + } + delete[] str; + return value; +} + +////////////////////////////////////////////////////////////////////////// +// bool +bool BasePersistenceManager::transfer(const char *name, bool *val) { + if (_saving) { + _saveStream->writeByte(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readByte(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// int +bool BasePersistenceManager::transfer(const char *name, int *val) { + if (_saving) { + _saveStream->writeSint32LE(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// DWORD +bool BasePersistenceManager::transfer(const char *name, uint32 *val) { + if (_saving) { + _saveStream->writeUint32LE(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readUint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// float +bool BasePersistenceManager::transfer(const char *name, float *val) { + if (_saving) { + putFloat(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = getFloat(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// double +bool BasePersistenceManager::transfer(const char *name, double *val) { + if (_saving) { + putDouble(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = getDouble(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// char* +bool BasePersistenceManager::transfer(const char *name, char **val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + *val = str; + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +// const char* +bool BasePersistenceManager::transfer(const char *name, const char **val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + *val = str; + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +// Common::String +bool BasePersistenceManager::transfer(const char *name, Common::String *val) { + if (_saving) { + putString(*val); + return STATUS_OK; + } else { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + if (str) { + *val = str; + delete[] str; + } else { + *val = ""; + } + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::transfer(const char *name, AnsiStringArray &val) { + size_t size; + + if (_saving) { + size = val.size(); + _saveStream->writeUint32LE(size); + + for (AnsiStringArray::iterator it = val.begin(); it != val.end(); ++it) { + putString((*it).c_str()); + } + } else { + val.clear(); + size = _loadStream->readUint32LE(); + + for (size_t i = 0; i < size; i++) { + char *str = getString(); + if (_loadStream->err()) { + delete[] str; + return STATUS_FAILED; + } + if (str) { + val.push_back(str); + } + delete[] str; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// BYTE +bool BasePersistenceManager::transfer(const char *name, byte *val) { + if (_saving) { + _saveStream->writeByte(*val); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + *val = _loadStream->readByte(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// RECT +bool BasePersistenceManager::transfer(const char *name, Rect32 *val) { + if (_saving) { + _saveStream->writeSint32LE(val->left); + _saveStream->writeSint32LE(val->top); + _saveStream->writeSint32LE(val->right); + _saveStream->writeSint32LE(val->bottom); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->left = _loadStream->readSint32LE(); + val->top = _loadStream->readSint32LE(); + val->right = _loadStream->readSint32LE(); + val->bottom = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// POINT +bool BasePersistenceManager::transfer(const char *name, Point32 *val) { + if (_saving) { + _saveStream->writeSint32LE(val->x); + _saveStream->writeSint32LE(val->y); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->x = _loadStream->readSint32LE(); + val->y = _loadStream->readSint32LE(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// Vector2 +bool BasePersistenceManager::transfer(const char *name, Vector2 *val) { + if (_saving) { + putFloat(val->x); + putFloat(val->y); + if (_saveStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } else { + val->x = getFloat(); + val->y = getFloat(); + if (_loadStream->err()) { + return STATUS_FAILED; + } + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +// generic pointer +bool BasePersistenceManager::transfer(const char *name, void *val) { + int classID = -1, instanceID = -1; + + if (_saving) { + SystemClassRegistry::getInstance()->getPointerID(*(void **)val, &classID, &instanceID); + if (*(void **)val != NULL && (classID == -1 || instanceID == -1)) { + debugC(kWintermuteDebugSaveGame, "Warning: invalid instance '%s'", name); + } + + _saveStream->writeUint32LE(classID); + _saveStream->writeUint32LE(instanceID); + } else { + classID = _loadStream->readUint32LE(); + instanceID = _loadStream->readUint32LE(); + + *(void **)val = SystemClassRegistry::getInstance()->idToPointer(classID, instanceID); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BasePersistenceManager::checkVersion(byte verMajor, byte verMinor, byte verBuild) { + if (_saving) { + return true; + } + + // it's ok if we are same or newer than the saved game + if (verMajor > _savedVerMajor || + (verMajor == _savedVerMajor && verMinor > _savedVerMinor) || + (verMajor == _savedVerMajor && verMinor == _savedVerMinor && verBuild > _savedVerBuild) + ) { + return false; + } + + return true; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h new file mode 100644 index 0000000000..a262c92a0b --- /dev/null +++ b/engines/wintermute/base/base_persistence_manager.h @@ -0,0 +1,118 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PERSISTENCE_MANAGER_H +#define WINTERMUTE_BASE_PERSISTENCE_MANAGER_H + + +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/savestate.h" +#include "common/stream.h" +#include "common/str.h" +#include "common/system.h" +#include "common/rect.h" + +namespace Wintermute { + +class Vector2; +class BaseGame; +class BasePersistenceManager { +public: + char *_savedDescription; + Common::String _savePrefix; + Common::String _savedName; + bool saveFile(const Common::String &filename); + uint32 getDWORD(); + void putDWORD(uint32 val); + char *getString(); + Common::String getStringObj(); + void putString(const Common::String &val); + float getFloat(); + void putFloat(float val); + double getDouble(); + void putDouble(double val); + void cleanup(); + void getSaveStateDesc(int slot, SaveStateDescriptor &desc); + void deleteSaveSlot(int slot); + uint32 getMaxUsedSlot(); + bool getSaveExists(int slot); + bool initLoad(const Common::String &filename); + bool initSave(const char *desc); + bool getBytes(byte *buffer, uint32 size); + bool putBytes(byte *buffer, uint32 size); + uint32 _offset; + + bool getIsSaving() { return _saving; } + + uint32 _richBufferSize; + byte *_richBuffer; + + bool transfer(const char *name, void *val); + bool transfer(const char *name, int *val); + bool transfer(const char *name, uint32 *val); + bool transfer(const char *name, float *val); + bool transfer(const char *name, double *val); + bool transfer(const char *name, bool *val); + bool transfer(const char *name, byte *val); + bool transfer(const char *name, Rect32 *val); + bool transfer(const char *name, Point32 *val); + bool transfer(const char *name, const char **val); + bool transfer(const char *name, char **val); + bool transfer(const char *name, Common::String *val); + bool transfer(const char *name, Vector2 *val); + bool transfer(const char *name, AnsiStringArray &Val); + BasePersistenceManager(const char *savePrefix = NULL, bool deleteSingleton = false); + virtual ~BasePersistenceManager(); + bool checkVersion(byte verMajor, byte verMinor, byte verBuild); + + uint32 _thumbnailDataSize; + byte *_thumbnailData; + Common::String getFilenameForSlot(int slot) const; +private: + bool _deleteSingleton; + bool readHeader(const Common::String &filename); + TimeDate getTimeDate(); + bool putTimeDate(const TimeDate &t); + Common::WriteStream *_saveStream; + Common::SeekableReadStream *_loadStream; + TimeDate _savedTimestamp; + uint32 _savedPlayTime; + byte _savedVerMajor; + byte _savedVerMinor; + byte _savedVerBuild; + byte _savedExtMajor; + byte _savedExtMinor; + bool _saving; + // Separate from Base, as this class can do SOME operations without a _gameRef. + BaseGame *_gameRef; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_point.cpp b/engines/wintermute/base/base_point.cpp new file mode 100644 index 0000000000..fbd8960894 --- /dev/null +++ b/engines/wintermute/base/base_point.cpp @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BasePoint, false) + +////////////////////////////////////////////////////////////////////////// +BasePoint::BasePoint() { + x = y = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint::~BasePoint() { + +} + + +////////////////////////////////////////////////////////////////////////// +BasePoint::BasePoint(int initX, int initY) { + x = initX; + y = initY; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePoint::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(x)); + persistMgr->transfer(TMEMBER(y)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_point.h b/engines/wintermute/base/base_point.h new file mode 100644 index 0000000000..c0bbd3102e --- /dev/null +++ b/engines/wintermute/base/base_point.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_POINT_H +#define WINTERMUTE_BASE_POINT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BasePoint: public BaseClass { +public: + DECLARE_PERSISTENT(BasePoint, BaseClass) + BasePoint(); + BasePoint(int initX, int initY); + int y; + int x; + virtual ~BasePoint(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_quick_msg.cpp b/engines/wintermute/base/base_quick_msg.cpp new file mode 100644 index 0000000000..0a9907ac6b --- /dev/null +++ b/engines/wintermute/base/base_quick_msg.cpp @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_quick_msg.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseQuickMsg::BaseQuickMsg(BaseGame *inGame, const char *text) : BaseClass(inGame) { + _text = new char [strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + } + _startTime = _gameRef->_currentTime; +} + + +////////////////////////////////////////////////////////////////////////// +BaseQuickMsg::~BaseQuickMsg() { + if (_text) { + delete[] _text; + } +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseQuickMsg::getText() { + return _text; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_quick_msg.h b/engines/wintermute/base/base_quick_msg.h new file mode 100644 index 0000000000..67f9613461 --- /dev/null +++ b/engines/wintermute/base/base_quick_msg.h @@ -0,0 +1,48 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_QUICKMSG_H +#define WINTERMUTE_BASE_QUICKMSG_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseQuickMsg : public BaseClass { +public: + char *getText(); + uint32 _startTime; + BaseQuickMsg(BaseGame *inGame, const char *text); + virtual ~BaseQuickMsg(); +private: + char *_text; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_region.cpp b/engines/wintermute/base/base_region.cpp new file mode 100644 index 0000000000..0bc5975e51 --- /dev/null +++ b/engines/wintermute/base/base_region.cpp @@ -0,0 +1,535 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseRegion, false) + +////////////////////////////////////////////////////////////////////////// +BaseRegion::BaseRegion(BaseGame *inGame) : BaseObject(inGame) { + _active = true; + _editorSelectedPoint = -1; + _lastMimicScale = -1; + _lastMimicX = _lastMimicY = INT_MIN; + + BasePlatform::setRectEmpty(&_rect); +} + + +////////////////////////////////////////////////////////////////////////// +BaseRegion::~BaseRegion() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRegion::cleanup() { + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + BasePlatform::setRectEmpty(&_rect); + _editorSelectedPoint = -1; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::createRegion() { + return DID_SUCCEED(getBoundingRect(&_rect)); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::pointInRegion(int x, int y) { + if (_points.size() < 3) { + return false; + } + + Point32 pt; + pt.x = x; + pt.y = y; + + Rect32 rect; + rect.left = x - 1; + rect.right = x + 2; + rect.top = y - 1; + rect.bottom = y + 2; + + if (BasePlatform::ptInRect(&_rect, pt)) { + return ptInPolygon(x, y); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseRegion::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing REGION file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(REGION) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(NAME) +TOKEN_DEF(ACTIVE) +TOKEN_DEF(POINT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_SELECTED_POINT) +TOKEN_DEF(PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(REGION) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ACTIVE) + TOKEN_TABLE(POINT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_SELECTED_POINT) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_REGION) { + _gameRef->LOG(0, "'REGION' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + for (uint32 i = 0; i < _points.size(); i++) { + delete _points[i]; + } + _points.clear(); + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_ACTIVE: + parser.scanStr((char *)params, "%b", &_active); + break; + + case TOKEN_POINT: { + int x, y; + parser.scanStr((char *)params, "%d,%d", &x, &y); + _points.add(new BasePoint(x, y)); + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_EDITOR_SELECTED_POINT: + parser.scanStr((char *)params, "%d", &_editorSelectedPoint); + break; + + case TOKEN_PROPERTY: + parseProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in REGION definition"); + return STATUS_FAILED; + } + + createRegion(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // AddPoint + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "AddPoint") == 0) { + stack->correctParams(2); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + _points.add(new BasePoint(x, y)); + createRegion(); + + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertPoint") == 0) { + stack->correctParams(3); + int index = stack->pop()->getInt(); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + _points.insert_at(index, new BasePoint(x, y)); + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPoint") == 0) { + stack->correctParams(3); + int index = stack->pop()->getInt(); + int x = stack->pop()->getInt(); + int y = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + _points[index]->x = x; + _points[index]->y = y; + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemovePoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemovePoint") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + delete _points[index]; + _points[index] = NULL; + + _points.remove_at(index); + createRegion(); + + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPoint + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPoint") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(); + + if (index >= 0 && index < (int32)_points.size()) { + ScValue *val = stack->getPushValue(); + if (val) { + val->setProperty("X", _points[index]->x); + val->setProperty("Y", _points[index]->y); + } + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseRegion::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("region"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (name == "Active") { + _scValue->setBool(_active); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumPoints + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumPoints") { + _scValue->setInt(_points.size()); + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Active + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Active") == 0) { + _active = value->getBool(); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseRegion::scToString() { + return "[region]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride) { + if (!nameOverride) { + buffer->putTextIndent(indent, "REGION {\n"); + } else { + buffer->putTextIndent(indent, "%s {\n", nameOverride); + } + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + buffer->putTextIndent(indent + 2, "ACTIVE=%s\n", _active ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED_POINT=%d\n", _editorSelectedPoint); + + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + for (uint32 i = 0; i < _points.size(); i++) { + buffer->putTextIndent(indent + 2, "POINT {%d,%d}\n", _points[i]->x, _points[i]->y); + } + + if (_scProp) { + _scProp->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_active)); + persistMgr->transfer(TMEMBER(_editorSelectedPoint)); + persistMgr->transfer(TMEMBER(_lastMimicScale)); + persistMgr->transfer(TMEMBER(_lastMimicX)); + persistMgr->transfer(TMEMBER(_lastMimicY)); + _points.persist(persistMgr); + + return STATUS_OK; +} + + +typedef struct { + double x, y; +} dPoint; + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::ptInPolygon(int x, int y) { + if (_points.size() < 3) { + return false; + } + + int counter = 0; + double xinters; + dPoint p, p1, p2; + + p.x = (double)x; + p.y = (double)y; + + p1.x = (double)_points[0]->x; + p1.y = (double)_points[0]->y; + + for (uint32 i = 1; i <= _points.size(); i++) { + p2.x = (double)_points[i % _points.size()]->x; + p2.y = (double)_points[i % _points.size()]->y; + + if (p.y > MIN(p1.y, p2.y)) { + if (p.y <= MAX(p1.y, p2.y)) { + if (p.x <= MAX(p1.x, p2.x)) { + if (p1.y != p2.y) { + xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; + if (p1.x == p2.x || p.x <= xinters) { + counter++; + } + } + } + } + } + p1 = p2; + } + + if (counter % 2 == 0) { + return false; + } else { + return true; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::getBoundingRect(Rect32 *rect) { + if (_points.size() == 0) { + BasePlatform::setRectEmpty(rect); + } else { + int minX = INT_MAX, minY = INT_MAX, maxX = INT_MIN, maxY = INT_MIN; + + for (uint32 i = 0; i < _points.size(); i++) { + minX = MIN(minX, _points[i]->x); + minY = MIN(minY, _points[i]->y); + + maxX = MAX(maxX, _points[i]->x); + maxY = MAX(maxY, _points[i]->y); + } + BasePlatform::setRect(rect, minX, minY, maxX, maxY); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRegion::mimic(BaseRegion *region, float scale, int x, int y) { + if (scale == _lastMimicScale && x == _lastMimicX && y == _lastMimicY) { + return STATUS_OK; + } + + cleanup(); + + for (uint32 i = 0; i < region->_points.size(); i++) { + int xVal, yVal; + + xVal = (int)((float)region->_points[i]->x * scale / 100.0f); + yVal = (int)((float)region->_points[i]->y * scale / 100.0f); + + _points.add(new BasePoint(xVal + x, yVal + y)); + } + + _lastMimicScale = scale; + _lastMimicX = x; + _lastMimicY = y; + + return createRegion() ? STATUS_OK : STATUS_FAILED; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_region.h b/engines/wintermute/base/base_region.h new file mode 100644 index 0000000000..464f25be2f --- /dev/null +++ b/engines/wintermute/base/base_region.h @@ -0,0 +1,70 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_REGION_H +#define WINTERMUTE_BASE_REGION_H + +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class BaseRegion : public BaseObject { +public: + void cleanup(); + bool mimic(BaseRegion *region, float scale = 100.0f, int x = 0, int y = 0); + bool getBoundingRect(Rect32 *rect); + bool ptInPolygon(int x, int y); + DECLARE_PERSISTENT(BaseRegion, BaseObject) + bool _active; + int _editorSelectedPoint; + BaseRegion(BaseGame *inGame); + virtual ~BaseRegion(); + bool pointInRegion(int x, int y); + bool createRegion(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + Rect32 _rect; + BaseArray<BasePoint *> _points; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, NULL); } + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent, const char *nameOverride); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + float _lastMimicScale; + int _lastMimicX; + int _lastMimicY; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_save_thumb_helper.cpp b/engines/wintermute/base/base_save_thumb_helper.cpp new file mode 100644 index 0000000000..b4205c21c4 --- /dev/null +++ b/engines/wintermute/base/base_save_thumb_helper.cpp @@ -0,0 +1,81 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_save_thumb_helper.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbHelper::BaseSaveThumbHelper(BaseGame *inGame) : BaseClass(inGame) { + _thumbnail = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbHelper::~BaseSaveThumbHelper(void) { + delete _thumbnail; + _thumbnail = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbHelper::storeThumbnail(bool doFlip) { + delete _thumbnail; + _thumbnail = NULL; + + if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) { + if (doFlip) { + // when using opengl on windows it seems to be necessary to do this twice + // works normally for direct3d + _gameRef->displayContent(false); + _gameRef->_renderer->flip(); + + _gameRef->displayContent(false); + _gameRef->_renderer->flip(); + } + + BaseImage *screenshot = _gameRef->_renderer->takeScreenshot(); + if (!screenshot) { + return STATUS_FAILED; + } + + // normal thumbnail + if (_gameRef->_thumbnailWidth > 0 && _gameRef->_thumbnailHeight > 0) { + _thumbnail = new BaseImage(); + _thumbnail->copyFrom(screenshot, _gameRef->_thumbnailWidth, _gameRef->_thumbnailHeight); + } + + + delete screenshot; + screenshot = NULL; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_save_thumb_helper.h b/engines/wintermute/base/base_save_thumb_helper.h new file mode 100644 index 0000000000..8863508ac9 --- /dev/null +++ b/engines/wintermute/base/base_save_thumb_helper.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ +#ifndef WINTERMUTE_BASE_SAVE_THUMB_HELPER_H +#define WINTERMUTE_BASE_SAVE_THUMB_HELPER_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseImage; + +class BaseSaveThumbHelper : public BaseClass { +public: + BaseSaveThumbHelper(BaseGame *inGame); + virtual ~BaseSaveThumbHelper(void); + bool storeThumbnail(bool doFlip = false); + + BaseImage *_thumbnail; +private: + BaseImage *_richThumbnail; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_script_holder.cpp b/engines/wintermute/base/base_script_holder.cpp new file mode 100644 index 0000000000..c5d5e82f76 --- /dev/null +++ b/engines/wintermute/base/base_script_holder.cpp @@ -0,0 +1,502 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseScriptHolder, false) + +////////////////////////////////////////////////////////////////////// +BaseScriptHolder::BaseScriptHolder(BaseGame *inGame) : BaseScriptable(inGame) { + setName("<unnamed>"); + + _freezable = true; + _filename = NULL; +} + + +////////////////////////////////////////////////////////////////////// +BaseScriptHolder::~BaseScriptHolder() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::cleanup() { + delete[] _filename; + _filename = NULL; + + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->finish(true); + _scripts[i]->_owner = NULL; + } + _scripts.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::setFilename(const char *filename) { + if (_filename != NULL) { + delete[] _filename; + _filename = NULL; + } + if (filename == NULL) { + return; + } + _filename = new char [strlen(filename) + 1]; + if (_filename != NULL) { + strcpy(_filename, filename); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::applyEvent(const char *eventName, bool unbreakable) { + int numHandlers = 0; + + bool ret = STATUS_FAILED; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread) { + ScScript *handler = _scripts[i]->invokeEventHandler(eventName, unbreakable); + if (handler) { + //_scripts.add(handler); + numHandlers++; + ret = STATUS_OK; + } + } + } + if (numHandlers > 0 && unbreakable) { + _gameRef->_scEngine->tickUnbreakable(); + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::listen(BaseScriptHolder *param1, uint32 param2) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // DEBUG_CrashMe + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "DEBUG_CrashMe") == 0) { + stack->correctParams(0); + byte *p = 0; + *p = 10; + stack->pushNULL(); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ApplyEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ApplyEvent") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + bool ret; + ret = applyEvent(val->getString()); + + if (DID_SUCCEED(ret)) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CanHandleEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CanHandleEvent") == 0) { + stack->correctParams(1); + stack->pushBool(canHandleEvent(stack->pop()->getString())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CanHandleMethod + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CanHandleMethod") == 0) { + stack->correctParams(1); + stack->pushBool(canHandleMethod(stack->pop()->getString())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AttachScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AttachScript") == 0) { + stack->correctParams(1); + stack->pushBool(DID_SUCCEED(addScript(stack->pop()->getString()))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DetachScript + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DetachScript") == 0) { + stack->correctParams(2); + const char *filename = stack->pop()->getString(); + bool killThreads = stack->pop()->getBool(false); + bool ret = false; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) { + _scripts[i]->finish(killThreads); + ret = true; + break; + } + } + stack->pushBool(ret); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IsScriptRunning + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IsScriptRunning") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + bool ret = false; + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0 && _scripts[i]->_state != SCRIPT_FINISHED && _scripts[i]->_state != SCRIPT_ERROR) { + ret = true; + break; + } + } + stack->pushBool(ret); + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseScriptHolder::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("script_holder"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Filename (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Filename") { + _scValue->setString(_filename); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } else { + return BaseScriptable::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseScriptHolder::scToString() { + return "[script_holder]"; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return BaseClass::saveAsText(buffer, indent); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_freezable)); + if (persistMgr->getIsSaving()) { + const char *name = getName(); + persistMgr->transfer(TMEMBER(name)); + } else { + char *name; + persistMgr->transfer(TMEMBER(name)); + setName(name); + delete[] name; + } + _scripts.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::addScript(const char *filename) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (scumm_stricmp(_scripts[i]->_filename, filename) == 0) { + if (_scripts[i]->_state != SCRIPT_FINISHED) { + _gameRef->LOG(0, "BaseScriptHolder::AddScript - trying to add script '%s' mutiple times (obj: '%s')", filename, getName()); + return STATUS_OK; + } + } + } + + ScScript *scr = _gameRef->_scEngine->runScript(filename, this); + if (!scr) { + if (_gameRef->_editorForceScripts) { + // editor hack + scr = new ScScript(_gameRef, _gameRef->_scEngine); + scr->_filename = new char[strlen(filename) + 1]; + strcpy(scr->_filename, filename); + scr->_state = SCRIPT_ERROR; + scr->_owner = this; + _scripts.add(scr); + _gameRef->_scEngine->_scripts.add(scr); + + return STATUS_OK; + } + return STATUS_FAILED; + } else { + scr->_freezable = _freezable; + _scripts.add(scr); + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::removeScript(ScScript *script) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] == script) { + _scripts.remove_at(i); + break; + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::canHandleEvent(const char *EventName) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->canHandleEvent(EventName)) { + return true; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::canHandleMethod(const char *MethodName) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->canHandleMethod(MethodName)) { + return true; + } + } + return false; +} + + +TOKEN_DEF_START +TOKEN_DEF(PROPERTY) +TOKEN_DEF(NAME) +TOKEN_DEF(VALUE) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::parseProperty(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(PROPERTY) + TOKEN_TABLE(NAME) + TOKEN_TABLE(VALUE) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_PROPERTY) { + _gameRef->LOG(0, "'PROPERTY' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + char *propName = NULL; + char *propValue = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_NAME: + delete[] propName; + propName = new char[strlen((char *)params) + 1]; + if (propName) { + strcpy(propName, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_VALUE: + delete[] propValue; + propValue = new char[strlen((char *)params) + 1]; + if (propValue) { + strcpy(propValue, (char *)params); + } else { + cmd = PARSERR_GENERIC; + } + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Syntax error in PROPERTY definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC || propName == NULL || propValue == NULL) { + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + _gameRef->LOG(0, "Error loading PROPERTY definition"); + return STATUS_FAILED; + } + + + ScValue *val = new ScValue(_gameRef); + val->setString(propValue); + scSetProperty(propName, val); + + delete val; + delete[] propName; + delete[] propValue; + propName = NULL; + propValue = NULL; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::makeFreezable(bool freezable) { + _freezable = freezable; + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->_freezable = freezable; + } + +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *BaseScriptHolder::invokeMethodThread(const char *methodName) { + for (int i = _scripts.size() - 1; i >= 0; i--) { + if (_scripts[i]->canHandleMethod(methodName)) { + + ScScript *thread = new ScScript(_gameRef, _scripts[i]->_engine); + if (thread) { + bool ret = thread->createMethodThread(_scripts[i], methodName); + if (DID_SUCCEED(ret)) { + _scripts[i]->_engine->_scripts.add(thread); + return thread; + } else { + delete thread; + } + } + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptHolder::scDebuggerDesc(char *buf, int bufSize) { + strcpy(buf, scToString()); + if (getName() && strcmp(getName(), "<unnamed>") != 0) { + strcat(buf, " Name: "); + strcat(buf, getName()); + } + if (_filename) { + strcat(buf, " File: "); + strcat(buf, _filename); + } +} + + +////////////////////////////////////////////////////////////////////////// +// IWmeObject +////////////////////////////////////////////////////////////////////////// +bool BaseScriptHolder::sendEvent(const char *eventName) { + return DID_SUCCEED(applyEvent(eventName)); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_script_holder.h b/engines/wintermute/base/base_script_holder.h new file mode 100644 index 0000000000..5fd0dbec9c --- /dev/null +++ b/engines/wintermute/base/base_script_holder.h @@ -0,0 +1,76 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SCRIPTHOLDER_H +#define WINTERMUTE_BASE_SCRIPTHOLDER_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class BaseScriptHolder : public BaseScriptable { +public: + DECLARE_PERSISTENT(BaseScriptHolder, BaseScriptable) + + BaseScriptHolder(BaseGame *inGame); + virtual ~BaseScriptHolder(); + virtual ScScript *invokeMethodThread(const char *methodName); + virtual void makeFreezable(bool freezable); + bool canHandleEvent(const char *eventName); + virtual bool canHandleMethod(const char *eventMethod); + bool cleanup(); + bool removeScript(ScScript *script); + bool addScript(const char *filename); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + bool applyEvent(const char *eventName, bool unbreakable = false); + void setFilename(const char *filename); + const char *getFilename() { return _filename; } + bool parseProperty(byte *buffer, bool complete = true); + bool _freezable; + bool _ready; + + BaseArray<ScScript *> _scripts; + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + virtual void scDebuggerDesc(char *buf, int bufSize); + // IWmeObject +private: + char *_filename; +public: + virtual bool sendEvent(const char *eventName); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_scriptable.cpp b/engines/wintermute/base/base_scriptable.cpp new file mode 100644 index 0000000000..a2dd8b00e7 --- /dev/null +++ b/engines/wintermute/base/base_scriptable.cpp @@ -0,0 +1,191 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseScriptable, false) + +////////////////////////////////////////////////////////////////////////// +BaseScriptable::BaseScriptable(BaseGame *inGame, bool noValue, bool persistable) : BaseNamedObject(inGame) { + _refCount = 0; + + if (noValue) { + _scValue = NULL; + } else { + _scValue = new ScValue(_gameRef); + } + + _persistable = persistable; + + _scProp = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseScriptable::~BaseScriptable() { + //if (_refCount>0) _gameRef->LOG(0, "Warning: Destroying object, _refCount=%d", _refCount); + delete _scValue; + delete _scProp; + _scValue = NULL; + _scProp = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + /* + stack->correctParams(0); + stack->pushNULL(); + script->runtimeError("Call to undefined method '%s'.", name); + + return STATUS_OK; + */ + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseScriptable::scGetProperty(const Common::String &name) { + if (!_scProp) { + _scProp = new ScValue(_gameRef); + } + if (_scProp) { + return _scProp->getProp(name.c_str()); // TODO: Change to Common::String + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scSetProperty(const char *name, ScValue *value) { + if (!_scProp) { + _scProp = new ScValue(_gameRef); + } + if (_scProp) { + return _scProp->setProp(name, value); + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseScriptable::scToString() { + return "[native object]"; +} + +////////////////////////////////////////////////////////////////////////// +void *BaseScriptable::scToMemBuffer() { + return (void *)NULL; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseScriptable::scToInt() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +double BaseScriptable::scToFloat() { + return 0.0f; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::scToBool() { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetString(const char *val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetInt(int val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetFloat(double val) { +} + + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scSetBool(bool val) { +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_refCount)); + persistMgr->transfer(TMEMBER(_scProp)); + persistMgr->transfer(TMEMBER(_scValue)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseScriptable::scCompare(BaseScriptable *val) { + if (this < val) { + return -1; + } else if (this > val) { + return 1; + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseScriptable::scDebuggerDesc(char *buf, int bufSize) { + strcpy(buf, scToString()); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseScriptable::canHandleMethod(const char *eventMethod) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *BaseScriptable::invokeMethodThread(const char *methodName) { + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_scriptable.h b/engines/wintermute/base/base_scriptable.h new file mode 100644 index 0000000000..fbe14fc299 --- /dev/null +++ b/engines/wintermute/base/base_scriptable.h @@ -0,0 +1,83 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SCRIPTABLE_H +#define WINTERMUTE_BASE_SCRIPTABLE_H + + +#include "engines/wintermute/base/base_named_object.h" +#include "engines/wintermute/persistent.h" + +namespace Wintermute { + +class ScValue; +class ScStack; +class ScScript; + +class BaseScriptable : public BaseNamedObject { +public: + virtual ScScript *invokeMethodThread(const char *methodName); + DECLARE_PERSISTENT(BaseScriptable, BaseNamedObject) + + BaseScriptable(BaseGame *inGame, bool noValue = false, bool persistable = true); + virtual ~BaseScriptable(); + + // high level scripting interface + virtual bool canHandleMethod(const char *eventMethod); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + virtual void *scToMemBuffer(); + virtual int scToInt(); + virtual double scToFloat(); + virtual bool scToBool(); + virtual void scSetString(const char *val); + virtual void scSetInt(int val); + virtual void scSetFloat(double val); + virtual void scSetBool(bool val); + virtual int scCompare(BaseScriptable *val); + virtual void scDebuggerDesc(char *buf, int bufSize); + int _refCount; + ScValue *_scValue; + ScValue *_scProp; +}; + +// Implemented in their respective .cpp-files +BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXMath(BaseGame *inGame); +BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack); +BaseScriptable *makeSXStore(BaseGame *inGame); +BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack); + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_sprite.cpp b/engines/wintermute/base/base_sprite.cpp new file mode 100644 index 0000000000..468af1bd75 --- /dev/null +++ b/engines/wintermute/base/base_sprite.cpp @@ -0,0 +1,819 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSprite, false) + +////////////////////////////////////////////////////////////////////// +BaseSprite::BaseSprite(BaseGame *inGame, BaseObject *Owner) : BaseScriptHolder(inGame) { + _editorAllFrames = false; + _owner = Owner; + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////// +BaseSprite::~BaseSprite() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSprite::setDefaults() { + _currentFrame = -1; + _looping = false; + _lastFrameTime = 0; + setFilename(NULL); + _finished = false; + _changed = false; + _paused = false; + _continuous = false; + _moveX = _moveY = 0; + + _editorMuted = false; + _editorBgFile = NULL; + _editorBgOffsetX = _editorBgOffsetY = 0; + _editorBgAlpha = 0xFF; + _streamed = false; + _streamedKeepLoaded = false; + + setName(""); + + _precise = true; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSprite::cleanup() { + BaseScriptHolder::cleanup(); + + for (uint32 i = 0; i < _frames.size(); i++) { + delete _frames[i]; + } + _frames.clear(); + + delete[] _editorBgFile; + _editorBgFile = NULL; + + setDefaults(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, uint32 alpha) { + getCurrentFrame(zoomX, zoomY); + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return STATUS_OK; + } + + // move owner if allowed to + if (_changed && _owner && _owner->_movable) { + _owner->_posX += _moveX; + _owner->_posY += _moveY; + _owner->afterMove(); + + x = _owner->_posX; + y = _owner->_posY; + } + + // draw frame + return display(x, y, registerOwner, zoomX, zoomY, alpha); +} + +bool BaseSprite::isChanged() { + return _changed; +} + +bool BaseSprite::isFinished() { + return _finished; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::loadFile(const Common::String &filename, int lifeTime, TSpriteCacheType cacheType) { + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(filename); + if (!file) { + _gameRef->LOG(0, "BaseSprite::LoadFile failed for file '%s'", filename.c_str()); + if (_gameRef->_debugDebugMode) { + return loadFile("invalid_debug.bmp", lifeTime, cacheType); + } else { + return loadFile("invalid.bmp", lifeTime, cacheType); + } + } else { + BaseFileManager::getEngineInstance()->closeFile(file); + file = NULL; + } + + bool ret = STATUS_FAILED; + + AnsiString filePrefix = filename; + AnsiString ext = PathUtil::getExtension(filename); + ext.toLowercase(); + filePrefix.toLowercase(); + if (filePrefix.hasPrefix("savegame:") || (ext == "bmp") || (ext == "tga") || (ext == "png") || (ext == "jpg")) { + BaseFrame *frame = new BaseFrame(_gameRef); + BaseSubFrame *subframe = new BaseSubFrame(_gameRef); + subframe->setSurface(filename, true, 0, 0, 0, lifeTime, true); + if (subframe->_surface == NULL) { + _gameRef->LOG(0, "Error loading simple sprite '%s'", filename.c_str()); + ret = STATUS_FAILED; + delete frame; + delete subframe; + } else { + subframe->setDefaultRect(); + frame->_subframes.add(subframe); + _frames.add(frame); + _currentFrame = 0; + ret = STATUS_OK; + } + } else { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer) { + if (DID_FAIL(ret = loadBuffer(buffer, true, lifeTime, cacheType))) { + _gameRef->LOG(0, "Error parsing SPRITE file '%s'", filename.c_str()); + } else { + ret = STATUS_OK; + } + delete[] buffer; + } + } + + setFilename(filename.c_str()); + + return ret; +} + + + +TOKEN_DEF_START +TOKEN_DEF(CONTINUOUS) +TOKEN_DEF(SPRITE) +TOKEN_DEF(LOOPING) +TOKEN_DEF(FRAME) +TOKEN_DEF(NAME) +TOKEN_DEF(PRECISE) +TOKEN_DEF(EDITOR_MUTED) +TOKEN_DEF(STREAMED_KEEP_LOADED) +TOKEN_DEF(STREAMED) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_BG_FILE) +TOKEN_DEF(EDITOR_BG_OFFSET_X) +TOKEN_DEF(EDITOR_BG_OFFSET_Y) +TOKEN_DEF(EDITOR_BG_ALPHA) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseSprite::loadBuffer(byte *buffer, bool complete, int lifeTime, TSpriteCacheType cacheType) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(CONTINUOUS) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(LOOPING) + TOKEN_TABLE(FRAME) + TOKEN_TABLE(NAME) + TOKEN_TABLE(PRECISE) + TOKEN_TABLE(EDITOR_MUTED) + TOKEN_TABLE(STREAMED_KEEP_LOADED) + TOKEN_TABLE(STREAMED) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_BG_FILE) + TOKEN_TABLE(EDITOR_BG_OFFSET_X) + TOKEN_TABLE(EDITOR_BG_OFFSET_Y) + TOKEN_TABLE(EDITOR_BG_ALPHA) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + + cleanup(); + + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_SPRITE) { + _gameRef->LOG(0, "'SPRITE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + int frameCount = 1; + BaseFrame *frame; + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_CONTINUOUS: + parser.scanStr((char *)params, "%b", &_continuous); + break; + + case TOKEN_EDITOR_MUTED: + parser.scanStr((char *)params, "%b", &_editorMuted); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_LOOPING: + parser.scanStr((char *)params, "%b", &_looping); + break; + + case TOKEN_PRECISE: + parser.scanStr((char *)params, "%b", &_precise); + break; + + case TOKEN_STREAMED: + parser.scanStr((char *)params, "%b", &_streamed); + if (_streamed && lifeTime == -1) { + lifeTime = 500; + cacheType = CACHE_ALL; + } + break; + + case TOKEN_STREAMED_KEEP_LOADED: + parser.scanStr((char *)params, "%b", &_streamedKeepLoaded); + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_EDITOR_BG_FILE: + if (_gameRef->_editorMode) { + delete[] _editorBgFile; + _editorBgFile = new char[strlen((char *)params) + 1]; + if (_editorBgFile) { + strcpy(_editorBgFile, (char *)params); + } + } + break; + + case TOKEN_EDITOR_BG_OFFSET_X: + parser.scanStr((char *)params, "%d", &_editorBgOffsetX); + break; + + case TOKEN_EDITOR_BG_OFFSET_Y: + parser.scanStr((char *)params, "%d", &_editorBgOffsetY); + break; + + case TOKEN_EDITOR_BG_ALPHA: + parser.scanStr((char *)params, "%d", &_editorBgAlpha); + _editorBgAlpha = MIN(_editorBgAlpha, 255); + _editorBgAlpha = MAX(_editorBgAlpha, 0); + break; + + case TOKEN_FRAME: { + int frameLifeTime = lifeTime; + if (cacheType == CACHE_HALF && frameCount % 2 != 1) { + frameLifeTime = -1; + } + + frame = new BaseFrame(_gameRef); + + if (DID_FAIL(frame->loadBuffer(params, frameLifeTime, _streamedKeepLoaded))) { + delete frame; + _gameRef->LOG(0, "Error parsing frame %d", frameCount); + return STATUS_FAILED; + } + + _frames.add(frame); + frameCount++; + if (_currentFrame == -1) { + _currentFrame = 0; + } + } + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SPRITE definition"); + return STATUS_FAILED; + } + _canBreak = !_continuous; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void BaseSprite::reset() { + if (_frames.size() > 0) { + _currentFrame = 0; + } else { + _currentFrame = -1; + } + + killAllSounds(); + + _lastFrameTime = 0; + _finished = false; + _moveX = _moveY = 0; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::getCurrentFrame(float zoomX, float zoomY) { + //if (_owner && _owner->_freezable && _gameRef->_state == GAME_FROZEN) return true; + + if (_currentFrame == -1) { + return false; + } + + uint32 timer; + if (_owner && _owner->_freezable) { + timer = _gameRef->_timer; + } else { + timer = _gameRef->_liveTimer; + } + + int lastFrame = _currentFrame; + + // get current frame + if (!_paused && !_finished && timer >= _lastFrameTime + _frames[_currentFrame]->_delay && _lastFrameTime != 0) { + if (_currentFrame < (int32)_frames.size() - 1) { + _currentFrame++; + if (_continuous) { + _canBreak = (_currentFrame == (int32)_frames.size() - 1); + } + } else { + if (_looping) { + _currentFrame = 0; + _canBreak = true; + } else { + _finished = true; + _canBreak = true; + } + } + + _lastFrameTime = timer; + } + + _changed = (lastFrame != _currentFrame || (_looping && (int32)_frames.size() == 1)); + + if (_lastFrameTime == 0) { + _lastFrameTime = timer; + _changed = true; + if (_continuous) { + _canBreak = (_currentFrame == (int32)_frames.size() - 1); + } + } + + if (_changed) { + _moveX = _frames[_currentFrame]->_moveX; + _moveY = _frames[_currentFrame]->_moveY; + + if (zoomX != 100 || zoomY != 100) { + _moveX = (int)((float)_moveX * (float)(zoomX / 100.0f)); + _moveY = (int)((float)_moveY * (float)(zoomY / 100.0f)); + } + } + + return _changed; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSprite::display(int x, int y, BaseObject *registerVal, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode) { + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return STATUS_OK; + } + + // on change... + if (_changed) { + if (_frames[_currentFrame]->_killSound) { + killAllSounds(); + } + applyEvent("FrameChanged"); + _frames[_currentFrame]->oneTimeDisplay(_owner, _gameRef->_editorMode && _editorMuted); + } + + // draw frame + return _frames[_currentFrame]->draw(x - _gameRef->_offsetX, y - _gameRef->_offsetY, registerVal, zoomX, zoomY, _precise, alpha, _editorAllFrames, rotate, blendMode); +} + + +////////////////////////////////////////////////////////////////////////// +BaseSurface *BaseSprite::getSurface() { + // only used for animated textures for 3D models + if (_currentFrame < 0 || _currentFrame >= (int32)_frames.size()) { + return NULL; + } + BaseFrame *frame = _frames[_currentFrame]; + if (frame && frame->_subframes.size() > 0) { + BaseSubFrame *subframe = frame->_subframes[0]; + if (subframe) { + return subframe->_surface; + } else { + return NULL; + } + } else { + return NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + + BasePlatform::setRectEmpty(rect); + for (uint32 i = 0; i < _frames.size(); i++) { + Rect32 frame; + Rect32 temp; + BasePlatform::copyRect(&temp, rect); + _frames[i]->getBoundingRect(&frame, x, y, scaleX, scaleY); + BasePlatform::unionRect(rect, &temp, &frame); + } + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "SPRITE {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "LOOPING=%s\n", _looping ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CONTINUOUS=%s\n", _continuous ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PRECISE=%s\n", _precise ? "TRUE" : "FALSE"); + if (_streamed) { + buffer->putTextIndent(indent + 2, "STREAMED=%s\n", _streamed ? "TRUE" : "FALSE"); + + if (_streamedKeepLoaded) { + buffer->putTextIndent(indent + 2, "STREAMED_KEEP_LOADED=%s\n", _streamedKeepLoaded ? "TRUE" : "FALSE"); + } + } + + if (_editorMuted) { + buffer->putTextIndent(indent + 2, "EDITOR_MUTED=%s\n", _editorMuted ? "TRUE" : "FALSE"); + } + + if (_editorBgFile) { + buffer->putTextIndent(indent + 2, "EDITOR_BG_FILE=\"%s\"\n", _editorBgFile); + buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_X=%d\n", _editorBgOffsetX); + buffer->putTextIndent(indent + 2, "EDITOR_BG_OFFSET_Y=%d\n", _editorBgOffsetY); + buffer->putTextIndent(indent + 2, "EDITOR_BG_ALPHA=%d\n", _editorBgAlpha); + } + + BaseScriptHolder::saveAsText(buffer, indent + 2); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + for (uint32 i = 0; i < _frames.size(); i++) { + _frames[i]->saveAsText(buffer, indent + 2); + } + + buffer->putTextIndent(indent, "}\n\n"); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::persist(BasePersistenceManager *persistMgr) { + BaseScriptHolder::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_canBreak)); + persistMgr->transfer(TMEMBER(_changed)); + persistMgr->transfer(TMEMBER(_paused)); + persistMgr->transfer(TMEMBER(_continuous)); + persistMgr->transfer(TMEMBER(_currentFrame)); + persistMgr->transfer(TMEMBER(_editorAllFrames)); + persistMgr->transfer(TMEMBER(_editorBgAlpha)); + persistMgr->transfer(TMEMBER(_editorBgFile)); + persistMgr->transfer(TMEMBER(_editorBgOffsetX)); + persistMgr->transfer(TMEMBER(_editorBgOffsetY)); + persistMgr->transfer(TMEMBER(_editorMuted)); + persistMgr->transfer(TMEMBER(_finished)); + + _frames.persist(persistMgr); + + persistMgr->transfer(TMEMBER(_lastFrameTime)); + persistMgr->transfer(TMEMBER(_looping)); + persistMgr->transfer(TMEMBER(_moveX)); + persistMgr->transfer(TMEMBER(_moveY)); + persistMgr->transfer(TMEMBER(_owner)); + persistMgr->transfer(TMEMBER(_precise)); + persistMgr->transfer(TMEMBER(_streamed)); + persistMgr->transfer(TMEMBER(_streamedKeepLoaded)); + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetFrame + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetFrame") == 0) { + stack->correctParams(1); + int index = stack->pop()->getInt(-1); + if (index < 0 || index >= (int32)_frames.size()) { + script->runtimeError("Sprite.GetFrame: Frame index %d is out of range.", index); + stack->pushNULL(); + } else { + stack->pushNative(_frames[index], true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteFrame") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->isInt()) { + int index = val->getInt(-1); + if (index < 0 || index >= (int32)_frames.size()) { + script->runtimeError("Sprite.DeleteFrame: Frame index %d is out of range.", index); + } + } else { + BaseFrame *frame = (BaseFrame *)val->getNative(); + for (uint32 i = 0; i < _frames.size(); i++) { + if (_frames[i] == frame) { + if (i == (uint32)_currentFrame) { + _lastFrameTime = 0; + } + delete _frames[i]; + _frames.remove_at(i); + break; + } + } + } + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Reset + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Reset") == 0) { + stack->correctParams(0); + reset(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddFrame") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseFrame *frame = new BaseFrame(_gameRef); + if (filename != NULL) { + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (DID_SUCCEED(sub->setSurface(filename))) { + sub->setDefaultRect(); + frame->_subframes.add(sub); + } else { + delete sub; + } + } + _frames.add(frame); + + stack->pushNative(frame, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InsertFrame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InsertFrame") == 0) { + stack->correctParams(2); + int index = stack->pop()->getInt(); + if (index < 0) { + index = 0; + } + + ScValue *val = stack->pop(); + const char *filename = NULL; + if (!val->isNULL()) { + filename = val->getString(); + } + + BaseFrame *frame = new BaseFrame(_gameRef); + if (filename != NULL) { + BaseSubFrame *sub = new BaseSubFrame(_gameRef); + if (DID_SUCCEED(sub->setSurface(filename))) { + frame->_subframes.add(sub); + } else { + delete sub; + } + } + + if (index >= (int32)_frames.size()) { + _frames.add(frame); + } else { + _frames.insert_at(index, frame); + } + + stack->pushNative(frame, true); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pause + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pause") == 0) { + stack->correctParams(0); + _paused = true; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Play + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Play") == 0) { + stack->correctParams(0); + _paused = false; + stack->pushNULL(); + return STATUS_OK; + } else { + return BaseScriptHolder::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseSprite::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("sprite"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumFrames (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumFrames") { + _scValue->setInt(_frames.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CurrentFrame + ////////////////////////////////////////////////////////////////////////// + else if (name == "CurrentFrame") { + _scValue->setInt(_currentFrame); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (name == "PixelPerfect") { + _scValue->setBool(_precise); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Looping + ////////////////////////////////////////////////////////////////////////// + else if (name == "Looping") { + _scValue->setBool(_looping); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Owner (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Owner") { + if (_owner == NULL) { + _scValue->setNULL(); + } else { + _scValue->setNative(_owner, true); + } + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Finished (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Finished") { + _scValue->setBool(_finished); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Paused (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Paused") { + _scValue->setBool(_paused); + return _scValue; + } else { + return BaseScriptHolder::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // CurrentFrame + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "CurrentFrame") == 0) { + _currentFrame = value->getInt(0); + if (_currentFrame >= (int32)_frames.size() || _currentFrame < 0) { + _currentFrame = -1; + } + _lastFrameTime = 0; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PixelPerfect") == 0) { + _precise = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Looping + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Looping") == 0) { + _looping = value->getBool(); + return STATUS_OK; + } else { + return BaseScriptHolder::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseSprite::scToString() { + return "[sprite]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSprite::killAllSounds() { + for (uint32 i = 0; i < _frames.size(); i++) { + _frames[i]->stopSound(); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_sprite.h b/engines/wintermute/base/base_sprite.h new file mode 100644 index 0000000000..1d244c3a52 --- /dev/null +++ b/engines/wintermute/base/base_sprite.h @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SPRITE_H +#define WINTERMUTE_BASE_SPRITE_H + + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base_script_holder.h" + +namespace Wintermute { +class BaseFrame; +class BaseSurface; +class BaseObject; +class BaseSprite: public BaseScriptHolder { +public: + BaseSurface *getSurface(); + void cleanup(); + void setDefaults(); + DECLARE_PERSISTENT(BaseSprite, BaseScriptHolder) + + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + int _moveY; + int _moveX; + bool display(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool getCurrentFrame(float zoomX = 100, float zoomY = 100); + void reset(); + bool isChanged(); + bool isFinished(); + bool loadBuffer(byte *buffer, bool compete = true, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool loadFile(const Common::String &filename, int lifeTime = -1, TSpriteCacheType cacheType = CACHE_ALL); + bool draw(int x, int y, BaseObject *Register = NULL, float zoomX = 100, float zoomY = 100, uint32 alpha = 0xFFFFFFFF); + bool _looping; + int _currentFrame; + bool addFrame(const char *filename, uint32 delay = 0, int hotspotX = 0, int hotspotY = 0, Rect32 *rect = NULL); + BaseSprite(BaseGame *inGame, BaseObject *owner = NULL); + virtual ~BaseSprite(); + BaseArray<BaseFrame *> _frames; + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +private: + BaseObject *_owner; + bool _canBreak; + bool _changed; + bool _editorAllFrames; + char *_editorBgFile; + int _editorBgOffsetX; + int _editorBgOffsetY; + int _editorBgAlpha; + bool _editorMuted; + bool _finished; + bool _continuous; + uint32 _lastFrameTime; + bool _precise; + bool _paused; + bool _streamed; + bool _streamedKeepLoaded; + bool killAllSounds(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_string_table.cpp b/engines/wintermute/base/base_string_table.cpp new file mode 100644 index 0000000000..2f890beea1 --- /dev/null +++ b/engines/wintermute/base/base_string_table.cpp @@ -0,0 +1,255 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_string_table.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseStringTable::BaseStringTable(BaseGame *inGame) : BaseClass(inGame) { + +} + + +////////////////////////////////////////////////////////////////////////// +BaseStringTable::~BaseStringTable() { + // delete strings + _strings.clear(); + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseStringTable::addString(const char *key, const char *val, bool reportDuplicities) { + if (key == NULL || val == NULL) { + return STATUS_FAILED; + } + + if (scumm_stricmp(key, "@right-to-left") == 0) { + _gameRef->_textRTL = true; + return STATUS_OK; + } + + Common::String finalKey = key; + finalKey.toLowercase(); + + StringsIter it = _strings.find(finalKey); + if (it != _strings.end() && reportDuplicities) { + _gameRef->LOG(0, " Warning: Duplicate definition of string '%s'.", finalKey.c_str()); + } + + _strings[finalKey] = val; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +char *BaseStringTable::getKey(const char *str) const { + if (str == NULL || str[0] != '/') { + return NULL; + } + + const char *value = strchr(str + 1, '/'); + if (value == NULL) { + return NULL; + } + + char *key = new char[value - str]; + Common::strlcpy(key, str + 1, (size_t)(value - str)); + + BasePlatform::strlwr(key); + + char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = new char[it->_value.size() + 1]; + strcpy(newStr, it->_value.c_str()); + if (strlen(newStr) > 0 && newStr[0] == '/' && strchr(newStr + 1, '/')) { + delete[] key; + char *ret = getKey(newStr); + delete[] newStr; + return ret; + } else { + delete[] newStr; + return key; + } + } else { + return key; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseStringTable::expand(char **str) const { + if (str == NULL || *str == NULL || *str[0] != '/') { + return; + } + + char *value = strchr(*str + 1, '/'); + if (value == NULL) { + return; + } + + char *key = new char[value - *str]; + Common::strlcpy(key, *str + 1, (size_t)(value - *str)); + + BasePlatform::strlwr(key); + + value++; + + char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = new char[it->_value.size() + 1]; + strcpy(newStr, it->_value.c_str()); + } else { + newStr = new char[strlen(value) + 1]; + strcpy(newStr, value); + } + + delete[] key; + delete[] *str; + *str = newStr; + + if (strlen(*str) > 0 && *str[0] == '/') { + expand(str); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseStringTable::expandStatic(const char *string) const { + if (string == NULL || string[0] == '\0' || string[0] != '/') { + return string; + } + + const char *value = strchr(string + 1, '/'); + if (value == NULL) { + return string; + } + + char *key = new char[value - string]; + Common::strlcpy(key, string + 1, (size_t)(value - string - 1)); + BasePlatform::strlwr(key); + + value++; + + const char *newStr; + + StringsIter it = _strings.find(key); + if (it != _strings.end()) { + newStr = it->_value.c_str(); + } else { + newStr = value; + } + + delete[] key; + + if (strlen(newStr) > 0 && newStr[0] == '/') { + return expandStatic(newStr); + } else { + return newStr; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseStringTable::loadFile(const char *filename, bool clearOld) { + _gameRef->LOG(0, "Loading string table..."); + + if (clearOld) { + _strings.clear(); + } + + uint32 size; + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename, &size); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseStringTable::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + uint32 pos = 0; + + if (size > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { + pos += 3; + if (_gameRef->_textEncoding != TEXT_UTF8) { + _gameRef->_textEncoding = TEXT_UTF8; + //_gameRef->_textEncoding = TEXT_ANSI; + _gameRef->LOG(0, " UTF8 file detected, switching to UTF8 text encoding"); + } + } else { + _gameRef->_textEncoding = TEXT_ANSI; + } + + uint32 lineLength = 0; + while (pos < size) { + lineLength = 0; + while (pos + lineLength < size && buffer[pos + lineLength] != '\n' && buffer[pos + lineLength] != '\0') { + lineLength++; + } + + uint32 realLength = lineLength - (pos + lineLength >= size ? 0 : 1); + char *line = new char[realLength + 1]; + Common::strlcpy(line, (char *)&buffer[pos], realLength + 1); + char *value = strchr(line, '\t'); + if (value == NULL) { + value = strchr(line, ' '); + } + + if (line[0] != ';') { + if (value != NULL) { + value[0] = '\0'; + value++; + for (uint32 i = 0; i < strlen(value); i++) { + if (value[i] == '|') { + value[i] = '\n'; + } + } + addString(line, value, clearOld); + } else if (line[0] != '\0') { + addString(line, "", clearOld); + } + } + + delete[] line; + pos += lineLength + 1; + } + + delete[] buffer; + + _gameRef->LOG(0, " %d strings loaded", _strings.size()); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_string_table.h b/engines/wintermute/base/base_string_table.h new file mode 100644 index 0000000000..128807bd1a --- /dev/null +++ b/engines/wintermute/base/base_string_table.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_STRING_TABLE_H +#define WINTERMUTE_BASE_STRING_TABLE_H + + +#include "common/hashmap.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseStringTable : public BaseClass { +public: + bool loadFile(const char *filename, bool deleteAll = true); + void expand(char **str) const; + const char *expandStatic(const char *string) const; + bool addString(const char *key, const char *val, bool reportDuplicities = true); + BaseStringTable(BaseGame *inGame); + virtual ~BaseStringTable(); + char *getKey(const char *str) const; +private: + Common::HashMap<Common::String, Common::String> _strings; + typedef Common::HashMap<Common::String, Common::String>::const_iterator StringsIter; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_sub_frame.cpp b/engines/wintermute/base/base_sub_frame.cpp new file mode 100644 index 0000000000..77cc522ae7 --- /dev/null +++ b/engines/wintermute/base/base_sub_frame.cpp @@ -0,0 +1,659 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSubFrame, false) + +////////////////////////////////////////////////////////////////////////// +BaseSubFrame::BaseSubFrame(BaseGame *inGame) : BaseScriptable(inGame, true) { + _surface = NULL; + _hotspotX = _hotspotY = 0; + _alpha = 0xFFFFFFFF; + _transparent = 0xFFFF00FF; + + _wantsDefaultRect = false; + BasePlatform::setRectEmpty(&_rect); + + _editorSelected = false; + + _surfaceFilename = NULL; + _cKDefault = true; + _cKRed = _cKBlue = _cKGreen = 0; + _lifeTime = -1; + _keepLoaded = false; + + _2DOnly = _3DOnly = false; + _decoration = false; + + _mirrorX = _mirrorY = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSubFrame::~BaseSubFrame() { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + } + delete[] _surfaceFilename; + _surfaceFilename = NULL; +} + + +TOKEN_DEF_START +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(RECT) +TOKEN_DEF(HOTSPOT) +TOKEN_DEF(2D_ONLY) +TOKEN_DEF(3D_ONLY) +TOKEN_DEF(DECORATION) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(MIRROR_X) +TOKEN_DEF(MIRROR_Y) +TOKEN_DEF(EDITOR_SELECTED) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::loadBuffer(byte *buffer, int lifeTime, bool keepLoaded) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(RECT) + TOKEN_TABLE(HOTSPOT) + TOKEN_TABLE(2D_ONLY) + TOKEN_TABLE(3D_ONLY) + TOKEN_TABLE(DECORATION) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(MIRROR_X) + TOKEN_TABLE(MIRROR_Y) + TOKEN_TABLE(EDITOR_SELECTED) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + Rect32 rect; + int r = 255, g = 255, b = 255; + int ar = 255, ag = 255, ab = 255, alpha = 255; + bool custoTrans = false; + BasePlatform::setRectEmpty(&rect); + char *surfaceFile = NULL; + + delete _surface; + _surface = NULL; + + while ((cmd = parser.getCommand((char **)&buffer, commands, ¶ms)) > 0) { + switch (cmd) { + case TOKEN_IMAGE: + surfaceFile = params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_RECT: + parser.scanStr(params, "%d,%d,%d,%d", &rect.left, &rect.top, &rect.right, &rect.bottom); + break; + + case TOKEN_HOTSPOT: + parser.scanStr(params, "%d,%d", &_hotspotX, &_hotspotY); + break; + + case TOKEN_2D_ONLY: + parser.scanStr(params, "%b", &_2DOnly); + break; + + case TOKEN_3D_ONLY: + parser.scanStr(params, "%b", &_3DOnly); + break; + + case TOKEN_MIRROR_X: + parser.scanStr(params, "%b", &_mirrorX); + break; + + case TOKEN_MIRROR_Y: + parser.scanStr(params, "%b", &_mirrorY); + break; + + case TOKEN_DECORATION: + parser.scanStr(params, "%b", &_decoration); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr(params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr(params, "%d", &alpha); + break; + + case TOKEN_EDITOR_SELECTED: + parser.scanStr(params, "%b", &_editorSelected); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in SUBFRAME definition"); + return STATUS_FAILED; + } + + if (surfaceFile != NULL) { + if (custoTrans) { + setSurface(surfaceFile, false, r, g, b, lifeTime, keepLoaded); + } else { + setSurface(surfaceFile, true, 0, 0, 0, lifeTime, keepLoaded); + } + } + + _alpha = BYTETORGBA(ar, ag, ab, alpha); + if (custoTrans) { + _transparent = BYTETORGBA(r, g, b, 0xFF); + } + + /* + if (_surface == NULL) + { + _gameRef->LOG(0, "Error parsing sub-frame. Image not set."); + return STATUS_FAILED; + } + */ + if (BasePlatform::isRectEmpty(&rect)) { + setDefaultRect(); + } else { + setRect(rect); + } + + return STATUS_OK; +} + +Rect32 BaseSubFrame::getRect() { + if (_wantsDefaultRect && _surface) { + BasePlatform::setRect(&_rect, 0, 0, _surface->getWidth(), _surface->getHeight()); + _wantsDefaultRect = false; + } + return _rect; +} + +void BaseSubFrame::setRect(Rect32 rect) { + _wantsDefaultRect = false; + _rect = rect; +} + +const char* BaseSubFrame::getSurfaceFilename() { + return _surfaceFilename; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::draw(int x, int y, BaseObject *registerOwner, float zoomX, float zoomY, bool precise, uint32 alpha, float rotate, TSpriteBlendMode blendMode) { + if (!_surface) { + return STATUS_OK; + } + + if (registerOwner != NULL && !_decoration) { + if (zoomX == 100 && zoomY == 100) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, x - _hotspotX + getRect().left, y - _hotspotY + getRect().top, getRect().right - getRect().left, getRect().bottom - getRect().top, zoomX, zoomY, precise)); + } else { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, registerOwner, this, (int)(x - (_hotspotX + getRect().left) * (zoomX / 100)), (int)(y - (_hotspotY + getRect().top) * (zoomY / 100)), (int)((getRect().right - getRect().left) * (zoomX / 100)), (int)((getRect().bottom - getRect().top) * (zoomY / 100)), zoomX, zoomY, precise)); + } + } + if (_gameRef->_suspendedRendering) { + return STATUS_OK; + } + + bool res; + + //if (Alpha==0xFFFFFFFF) Alpha = _alpha; // TODO: better (combine owner's and self alpha) + if (_alpha != 0xFFFFFFFF) { + alpha = _alpha; + } + + if (rotate != 0.0f) { + res = _surface->displayTransform((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), _hotspotX, _hotspotY, getRect(), zoomX, zoomY, alpha, rotate, blendMode, _mirrorX, _mirrorY); + } else { + if (zoomX == 100 && zoomY == 100) { + res = _surface->displayTrans(x - _hotspotX, y - _hotspotY, getRect(), alpha, blendMode, _mirrorX, _mirrorY); + } else { + res = _surface->displayTransZoom((int)(x - _hotspotX * (zoomX / 100)), (int)(y - _hotspotY * (zoomY / 100)), getRect(), zoomX, zoomY, alpha, blendMode, _mirrorX, _mirrorY); + } + } + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::getBoundingRect(Rect32 *rect, int x, int y, float scaleX, float scaleY) { + if (!rect) { + return false; + } + + float ratioX = scaleX / 100.0f; + float ratioY = scaleY / 100.0f; + + BasePlatform::setRect(rect, + (int)(x - _hotspotX * ratioX), + (int)(y - _hotspotY * ratioY), + (int)(x - _hotspotX * ratioX + (getRect().right - getRect().left) * ratioX), + (int)(y - _hotspotY * ratioY + (getRect().bottom - getRect().top) * ratioY)); + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete) { + if (complete) { + buffer->putTextIndent(indent, "SUBFRAME {\n"); + } + + if (_surface && _surface->getFileNameStr() != "") { + buffer->putTextIndent(indent + 2, "IMAGE = \"%s\"\n", _surface->getFileName()); + } + + if (_transparent != 0xFFFF00FF) { + buffer->putTextIndent(indent + 2, "TRANSPARENT { %d,%d,%d }\n", RGBCOLGetR(_transparent), RGBCOLGetG(_transparent), RGBCOLGetB(_transparent)); + } + + Rect32 rect; + BasePlatform::setRectEmpty(&rect); + if (_surface) { + BasePlatform::setRect(&rect, 0, 0, _surface->getWidth(), _surface->getHeight()); + } + if (!(rect == getRect())) { + buffer->putTextIndent(indent + 2, "RECT { %d,%d,%d,%d }\n", getRect().left, getRect().top, getRect().right, getRect().bottom); + } + + if (_hotspotX != 0 || _hotspotY != 0) { + buffer->putTextIndent(indent + 2, "HOTSPOT {%d, %d}\n", _hotspotX, _hotspotY); + } + + if (_alpha != 0xFFFFFFFF) { + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d,%d,%d }\n", RGBCOLGetR(_alpha), RGBCOLGetG(_alpha), RGBCOLGetB(_alpha)); + buffer->putTextIndent(indent + 2, "ALPHA = %d\n", RGBCOLGetA(_alpha)); + } + + if (_mirrorX) { + buffer->putTextIndent(indent + 2, "MIRROR_X=%s\n", _mirrorX ? "TRUE" : "FALSE"); + } + + if (_mirrorY) { + buffer->putTextIndent(indent + 2, "MIRROR_Y=%s\n", _mirrorY ? "TRUE" : "FALSE"); + } + + if (_2DOnly) { + buffer->putTextIndent(indent + 2, "2D_ONLY=%s\n", _2DOnly ? "TRUE" : "FALSE"); + } + + if (_3DOnly) { + buffer->putTextIndent(indent + 2, "3D_ONLY=%s\n", _3DOnly ? "TRUE" : "FALSE"); + } + + if (_decoration) { + buffer->putTextIndent(indent + 2, "DECORATION=%s\n", _decoration ? "TRUE" : "FALSE"); + } + + if (_editorSelected) { + buffer->putTextIndent(indent + 2, "EDITOR_SELECTED=%s\n", _editorSelected ? "TRUE" : "FALSE"); + } + + BaseClass::saveAsText(buffer, indent + 2); + + + if (complete) { + buffer->putTextIndent(indent, "}\n\n"); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSubFrame::setDefaultRect() { + if (_surface) { + _wantsDefaultRect = true; + } else { + _wantsDefaultRect = false; + BasePlatform::setRectEmpty(&_rect); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_2DOnly)); + persistMgr->transfer(TMEMBER(_3DOnly)); + persistMgr->transfer(TMEMBER(_alpha)); + persistMgr->transfer(TMEMBER(_decoration)); + persistMgr->transfer(TMEMBER(_editorSelected)); + persistMgr->transfer(TMEMBER(_hotspotX)); + persistMgr->transfer(TMEMBER(_hotspotY)); + persistMgr->transfer(TMEMBER(_rect)); + persistMgr->transfer(TMEMBER(_wantsDefaultRect)); + + persistMgr->transfer(TMEMBER(_surfaceFilename)); + persistMgr->transfer(TMEMBER(_cKDefault)); + persistMgr->transfer(TMEMBER(_cKRed)); + persistMgr->transfer(TMEMBER(_cKGreen)); + persistMgr->transfer(TMEMBER(_cKBlue)); + persistMgr->transfer(TMEMBER(_lifeTime)); + + persistMgr->transfer(TMEMBER(_keepLoaded)); + persistMgr->transfer(TMEMBER(_mirrorX)); + persistMgr->transfer(TMEMBER(_mirrorY)); + persistMgr->transfer(TMEMBER(_transparent)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + + ////////////////////////////////////////////////////////////////////////// + // GetImage + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetImage") == 0) { + stack->correctParams(0); + + if (!_surfaceFilename) { + stack->pushNULL(); + } else { + stack->pushString(_surfaceFilename); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetImage") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isNULL()) { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + } + delete[] _surfaceFilename; + _surfaceFilename = NULL; + stack->pushBool(true); + } else { + const char *filename = val->getString(); + if (DID_SUCCEED(setSurface(filename))) { + setDefaultRect(); + stack->pushBool(true); + } else { + stack->pushBool(false); + } + } + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *BaseSubFrame::scGetProperty(const Common::String &name) { + if (!_scValue) { + _scValue = new ScValue(_gameRef); + } + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("subframe"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaColor") { + + _scValue->setInt((int)_alpha); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TransparentColor (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "TransparentColor") { + _scValue->setInt((int)_transparent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Is2DOnly + ////////////////////////////////////////////////////////////////////////// + else if (name == "Is2DOnly") { + _scValue->setBool(_2DOnly); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Is3DOnly + ////////////////////////////////////////////////////////////////////////// + else if (name == "Is3DOnly") { + _scValue->setBool(_3DOnly); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorX + ////////////////////////////////////////////////////////////////////////// + else if (name == "MirrorX") { + _scValue->setBool(_mirrorX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorY + ////////////////////////////////////////////////////////////////////////// + else if (name == "MirrorY") { + _scValue->setBool(_mirrorY); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (name == "Decoration") { + _scValue->setBool(_decoration); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotX + ////////////////////////////////////////////////////////////////////////// + else if (name == "HotspotX") { + _scValue->setInt(_hotspotX); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotY + ////////////////////////////////////////////////////////////////////////// + else if (name == "HotspotY") { + _scValue->setInt(_hotspotY); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // AlphaColor + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "AlphaColor") == 0) { + _alpha = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Is2DOnly + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Is2DOnly") == 0) { + _2DOnly = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Is3DOnly + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Is3DOnly") == 0) { + _3DOnly = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MirrorX") == 0) { + _mirrorX = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MirrorY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MirrorY") == 0) { + _mirrorY = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Decoration + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Decoration") == 0) { + _decoration = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotX + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HotspotX") == 0) { + _hotspotX = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HotspotY + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HotspotY") == 0) { + _hotspotY = value->getInt(); + return STATUS_OK; + } else { + return BaseScriptable::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *BaseSubFrame::scToString() { + return "[subframe]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::setSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + if (_surface) { + _gameRef->_surfaceStorage->removeSurface(_surface); + _surface = NULL; + } + + delete[] _surfaceFilename; + _surfaceFilename = NULL; + + _surface = _gameRef->_surfaceStorage->addSurface(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + if (_surface) { + _surfaceFilename = new char[filename.size() + 1]; + strcpy(_surfaceFilename, filename.c_str()); + + _cKDefault = defaultCK; + _cKRed = ckRed; + _cKGreen = ckGreen; + _cKBlue = ckBlue; + _lifeTime = lifeTime; + _keepLoaded = keepLoaded; + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSubFrame::setSurfaceSimple() { + if (!_surfaceFilename) { + _surface = NULL; + return STATUS_OK; + } + _surface = _gameRef->_surfaceStorage->addSurface(_surfaceFilename, _cKDefault, _cKRed, _cKGreen, _cKBlue, _lifeTime, _keepLoaded); + if (_surface) { + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_sub_frame.h b/engines/wintermute/base/base_sub_frame.h new file mode 100644 index 0000000000..c173ae69d1 --- /dev/null +++ b/engines/wintermute/base/base_sub_frame.h @@ -0,0 +1,93 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SUBFRAME_H +#define WINTERMUTE_BASE_SUBFRAME_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { +class BaseObject; +class BaseSurface; +class BaseSubFrame : public BaseScriptable { +public: + bool _mirrorX; + bool _mirrorY; + bool _decoration; + bool setSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false); + bool setSurfaceSimple(); + DECLARE_PERSISTENT(BaseSubFrame, BaseScriptable) + void setDefaultRect(); + uint32 _transparent; + bool saveAsText(BaseDynamicBuffer *buffer, int indent) { return saveAsText(buffer, indent, true); } + bool saveAsText(BaseDynamicBuffer *buffer, int indent, bool complete); + bool _editorSelected; + BaseSubFrame(BaseGame *inGame); + virtual ~BaseSubFrame(); + bool loadBuffer(byte *buffer, int lifeTime, bool keepLoaded); + bool draw(int x, int y, BaseObject *registerOwner = NULL, float zoomX = 100, float zoomY = 100, bool precise = true, uint32 alpha = 0xFFFFFFFF, float rotate = 0.0f, TSpriteBlendMode blendMode = BLEND_NORMAL); + bool getBoundingRect(Rect32 *rect, int x, int y, float scaleX = 100, float scaleY = 100); + const char* getSurfaceFilename(); + + int _hotspotX; + int _hotspotY; + uint32 _alpha; + // These two setters and getters are rather usefull, as they allow _rect to be lazily defined + // Thus we don't need to load the actual graphics before the rect is actually needed. + Rect32 getRect(); + void setRect(Rect32 rect); +private: + bool _wantsDefaultRect; + Rect32 _rect; + char *_surfaceFilename; +public: + bool _cKDefault; + byte _cKRed; + byte _cKGreen; + byte _cKBlue; + int _lifeTime; + bool _keepLoaded; + + bool _2DOnly; + bool _3DOnly; + + BaseSurface *_surface; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_surface_storage.cpp b/engines/wintermute/base/base_surface_storage.cpp new file mode 100644 index 0000000000..4e795ca813 --- /dev/null +++ b/engines/wintermute/base/base_surface_storage.cpp @@ -0,0 +1,208 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +//IMPLEMENT_PERSISTENT(BaseSurfaceStorage, true); + +////////////////////////////////////////////////////////////////////// +BaseSurfaceStorage::BaseSurfaceStorage(BaseGame *inGame) : BaseClass(inGame) { + _lastCleanupTime = 0; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurfaceStorage::~BaseSurfaceStorage() { + cleanup(true); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::cleanup(bool warn) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (warn) { + _gameRef->LOG(0, "BaseSurfaceStorage warning: purging surface '%s', usage:%d", _surfaces[i]->getFileName(), _surfaces[i]->_referenceCount); + } + delete _surfaces[i]; + } + _surfaces.clear(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::initLoop() { + if (_gameRef->_smartCache && _gameRef->_liveTimer - _lastCleanupTime >= _gameRef->_surfaceGCCycleTime) { + _lastCleanupTime = _gameRef->_liveTimer; + sortSurfaces(); + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (_surfaces[i]->_lifeTime <= 0) { + break; + } + + if (_surfaces[i]->_lifeTime > 0 && _surfaces[i]->_valid && (int)(_gameRef->_liveTimer - _surfaces[i]->_lastUsedTime) >= _surfaces[i]->_lifeTime) { + //_gameRef->QuickMessageForm("Invalidating: %s", _surfaces[i]->_filename); + _surfaces[i]->invalidate(); + } + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::removeSurface(BaseSurface *surface) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (_surfaces[i] == surface) { + _surfaces[i]->_referenceCount--; + if (_surfaces[i]->_referenceCount <= 0) { + delete _surfaces[i]; + _surfaces.remove_at(i); + } + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurface *BaseSurfaceStorage::addSurface(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + for (uint32 i = 0; i < _surfaces.size(); i++) { + if (scumm_stricmp(_surfaces[i]->getFileName(), filename.c_str()) == 0) { + _surfaces[i]->_referenceCount++; + return _surfaces[i]; + } + } + + if (!BaseFileManager::getEngineInstance()->hasFile(filename)) { + if (filename.size()) { + _gameRef->LOG(0, "Missing image: '%s'", filename.c_str()); + } + if (_gameRef->_debugDebugMode) { + return addSurface("invalid_debug.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + } else { + return addSurface("invalid.bmp", defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded); + } + } + + BaseSurface *surface; + surface = _gameRef->_renderer->createSurface(); + + if (!surface) { + return NULL; + } + + if (DID_FAIL(surface->create(filename, defaultCK, ckRed, ckGreen, ckBlue, lifeTime, keepLoaded))) { + delete surface; + return NULL; + } else { + surface->_referenceCount = 1; + _surfaces.push_back(surface); + return surface; + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::restoreAll() { + bool ret; + for (uint32 i = 0; i < _surfaces.size(); i++) { + ret = _surfaces[i]->restore(); + if (ret != STATUS_OK) { + _gameRef->LOG(0, "BaseSurfaceStorage::RestoreAll failed"); + return ret; + } + } + return STATUS_OK; +} + + +/* +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::persist(BasePersistenceManager *persistMgr) +{ + + if (!persistMgr->getIsSaving()) cleanup(false); + + persistMgr->transfer(TMEMBER(_gameRef)); + + //_surfaces.persist(persistMgr); + + return STATUS_OK; +} +*/ + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceStorage::sortSurfaces() { + Common::sort(_surfaces.begin(), _surfaces.end(), surfaceSortCB); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseSurfaceStorage::surfaceSortCB(const void *arg1, const void *arg2) { + const BaseSurface *s1 = *((const BaseSurface *const *)arg1); + const BaseSurface *s2 = *((const BaseSurface *const *)arg2); + + // sort by life time + if (s1->_lifeTime <= 0 && s2->_lifeTime > 0) { + return 1; + } else if (s1->_lifeTime > 0 && s2->_lifeTime <= 0) { + return -1; + } + + + // sort by validity + if (s1->_valid && !s2->_valid) { + return -1; + } else if (!s1->_valid && s2->_valid) { + return 1; + } + + // sort by time + else if (s1->_lastUsedTime > s2->_lastUsedTime) { + return 1; + } else if (s1->_lastUsedTime < s2->_lastUsedTime) { + return -1; + } else { + return 0; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_surface_storage.h b/engines/wintermute/base/base_surface_storage.h new file mode 100644 index 0000000000..aef8ad23f9 --- /dev/null +++ b/engines/wintermute/base/base_surface_storage.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACE_STORAGE_H +#define WINTERMUTE_BASE_SURFACE_STORAGE_H + +#include "engines/wintermute/base/base.h" +#include "common/array.h" + +namespace Wintermute { +class BaseSurface; +class BaseSurfaceStorage : public BaseClass { +public: + uint32 _lastCleanupTime; + bool initLoop(); + bool sortSurfaces(); + static int surfaceSortCB(const void *arg1, const void *arg2); + bool cleanup(bool warn = false); + //DECLARE_PERSISTENT(BaseSurfaceStorage, BaseClass); + + bool restoreAll(); + BaseSurface *addSurface(const Common::String &filename, bool defaultCK = true, byte ckRed = 0, byte ckGreen = 0, byte ckBlue = 0, int lifeTime = -1, bool keepLoaded = false); + bool removeSurface(BaseSurface *surface); + BaseSurfaceStorage(BaseGame *inGame); + virtual ~BaseSurfaceStorage(); + + Common::Array<BaseSurface *> _surfaces; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_transition_manager.cpp b/engines/wintermute/base/base_transition_manager.cpp new file mode 100644 index 0000000000..7785f3d5af --- /dev/null +++ b/engines/wintermute/base/base_transition_manager.cpp @@ -0,0 +1,137 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_transition_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseTransitionMgr::BaseTransitionMgr(BaseGame *inGame) : BaseClass(inGame) { + _state = TRANS_MGR_READY; + _type = TRANSITION_NONE; + _origInteractive = false; + _preserveInteractive = false; + _lastTime = 0; + _started = false; +} + + + +////////////////////////////////////////////////////////////////////////// +BaseTransitionMgr::~BaseTransitionMgr() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::isReady() { + return (_state == TRANS_MGR_READY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::start(TTransitionType type, bool nonInteractive) { + if (_state != TRANS_MGR_READY) { + return STATUS_OK; + } + + if (type == TRANSITION_NONE || type >= NUM_TRANSITION_TYPES) { + _state = TRANS_MGR_READY; + return STATUS_OK; + } + + if (nonInteractive) { + _preserveInteractive = true; + _origInteractive = _gameRef->_interactive; + _gameRef->_interactive = false; + } /*else _preserveInteractive */; + + + _type = type; + _state = TRANS_MGR_RUNNING; + _started = false; + + return STATUS_OK; +} + +#define FADE_DURATION 200 + +////////////////////////////////////////////////////////////////////////// +bool BaseTransitionMgr::update() { + if (isReady()) { + return STATUS_OK; + } + + if (!_started) { + _started = true; + _lastTime = g_system->getMillis(); + } + + switch (_type) { + case TRANSITION_NONE: + _state = TRANS_MGR_READY; + break; + + case TRANSITION_FADE_OUT: { + uint32 time = g_system->getMillis() - _lastTime; + int alpha = (int)(255 - (float)time / (float)FADE_DURATION * 255); + alpha = MIN(255, MAX(alpha, 0)); + _gameRef->_renderer->fade((uint16)alpha); + + if (time > FADE_DURATION) { + _state = TRANS_MGR_READY; + } + } + break; + + case TRANSITION_FADE_IN: { + uint32 time = g_system->getMillis() - _lastTime; + int alpha = (int)((float)time / (float)FADE_DURATION * 255); + alpha = MIN(255, MAX(alpha, 0)); + _gameRef->_renderer->fade((uint16)alpha); + + if (time > FADE_DURATION) { + _state = TRANS_MGR_READY; + } + } + break; + default: + error("BaseTransitionMgr::Update - unhandled enum NUM_TRANSITION_TYPES"); + } + + if (isReady()) { + if (_preserveInteractive) { + _gameRef->_interactive = _origInteractive; + } + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_transition_manager.h b/engines/wintermute/base/base_transition_manager.h new file mode 100644 index 0000000000..d16a44c88e --- /dev/null +++ b/engines/wintermute/base/base_transition_manager.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_TRANSITION_MANAGER_H +#define WINTERMUTE_BASE_TRANSITION_MANAGER_H + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +class BaseTransitionMgr : public BaseClass { +public: + bool _started; + uint32 _lastTime; + bool _origInteractive; + bool _preserveInteractive; + bool update(); + bool start(TTransitionType type, bool nonInteractive = false); + bool isReady(); + TTransMgrState _state; + BaseTransitionMgr(BaseGame *inGame); + virtual ~BaseTransitionMgr(); + TTransitionType _type; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/base_viewport.cpp b/engines/wintermute/base/base_viewport.cpp new file mode 100644 index 0000000000..7ec995449f --- /dev/null +++ b/engines/wintermute/base/base_viewport.cpp @@ -0,0 +1,99 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/gfx/base_renderer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseViewport, false) + +////////////////////////////////////////////////////////////////////////// +BaseViewport::BaseViewport(BaseGame *inGame) : BaseClass(inGame) { + BasePlatform::setRectEmpty(&_rect); + _mainObject = NULL; + _offsetX = _offsetY = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseViewport::~BaseViewport() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseViewport::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_mainObject)); + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_rect)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseViewport::setRect(int left, int top, int right, int bottom, bool noCheck) { + if (!noCheck) { + left = MAX(left, 0); + top = MAX(top, 0); + right = MIN(right, _gameRef->_renderer->_width); + bottom = MIN(bottom, _gameRef->_renderer->_height); + } + + BasePlatform::setRect(&_rect, left, top, right, bottom); + _offsetX = left; + _offsetY = top; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +Rect32 *BaseViewport::getRect() { + return &_rect; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseViewport::getWidth() { + return _rect.right - _rect.left; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseViewport::getHeight() { + return _rect.bottom - _rect.top; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/base_viewport.h b/engines/wintermute/base/base_viewport.h new file mode 100644 index 0000000000..98ad1c1e14 --- /dev/null +++ b/engines/wintermute/base/base_viewport.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_VIEWPORT_H +#define WINTERMUTE_BASE_VIEWPORT_H + + +#include "engines/wintermute/base/base.h" + +namespace Wintermute { +class BaseObject; +class BaseViewport : public BaseClass { +public: + int getHeight(); + int getWidth(); + Rect32 *getRect(); + bool setRect(int left, int top, int right, int bottom, bool noCheck = false); + DECLARE_PERSISTENT(BaseViewport, BaseClass) + int _offsetY; + int _offsetX; + BaseObject *_mainObject; + BaseViewport(BaseGame *inGame = NULL); + virtual ~BaseViewport(); +private: + Rect32 _rect; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp new file mode 100644 index 0000000000..25be3dad2d --- /dev/null +++ b/engines/wintermute/base/file/base_disk_file.cpp @@ -0,0 +1,194 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/dcgf.h" +#include "engines/wintermute/base/file/base_disk_file.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "common/stream.h" +#include "common/memstream.h" +#include "common/file.h" +#include "common/zlib.h" +#include "common/archive.h" +#include "common/tokenizer.h" +#include "common/config-manager.h" + +namespace Wintermute { + +void correctSlashes(char *fileName) { + for (size_t i = 0; i < strlen(fileName); i++) { + if (fileName[i] == '\\') { + fileName[i] = '/'; + } + } +} + +// Parse a relative path in the game-folder, and if it exists, return a FSNode to it. +static Common::FSNode getNodeForRelativePath(const Common::String &filename) { + // The filename can be an explicit path, thus we need to chop it up, expecting the path the game + // specifies to follow the Windows-convention of folder\subfolder\file (absolute paths should not happen) + + // Absolute path: These should have been handled in openDiskFile. + if (filename.contains(':')) { + // So just return an invalid node. + return Common::FSNode(); + } + + // Relative path: + if (filename.contains('\\')) { + Common::StringTokenizer path(filename, "\\"); + + // Start traversing relative to the game-data-dir + const Common::FSNode gameDataDir(ConfMan.get("path")); + Common::FSNode curNode = gameDataDir; + + // Parse all path-elements + while (!path.empty()) { + // Get the next path-component by slicing on '\\' + Common::String pathPart = path.nextToken(); + // Get the next FSNode in the chain, if it exists as a child from the previous. + curNode = curNode.getChild(pathPart); + if (!curNode.isReadable()) { + // Return an invalid FSNode. + return Common::FSNode(); + } + // Following the comments in common/fs.h, anything not a directory is a file. + if (!curNode.isDirectory()) { + if (!path.empty()) { + error("Relative path %s reached a file before the end of the path", filename.c_str()); + } + return curNode; + } + } + } + // Return an invalid FSNode to mark that we didn't find the requested file. + return Common::FSNode(); +} + +bool diskFileExists(const Common::String &filename) { + // Try directly from SearchMan first + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, filename); + + for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { + if ((*it)->getName() == filename) { + return true; + } + } + // File wasn't found in SearchMan, try to parse the path as a relative path. + Common::FSNode searchNode = getNodeForRelativePath(filename); + if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { + return true; + } + return false; +} + +Common::SeekableReadStream *openDiskFile(const Common::String &filename) { + uint32 prefixSize = 0; + Common::SeekableReadStream *file = NULL; + Common::String fixedFilename = filename; + + // Absolute path: TODO: Add specific fallbacks here. + if (filename.contains(':')) { + if (filename.hasPrefix("c:\\windows\\fonts\\")) { // East Side Story refers to "c:\windows\fonts\framd.ttf" + fixedFilename = filename.c_str() + 17; + } else { + error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str()); + } + } + // Try directly from SearchMan first + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, fixedFilename); + + for (Common::ArchiveMemberList::iterator it = files.begin(); it != files.end(); ++it) { + if ((*it)->getName() == filename) { + file = (*it)->createReadStream(); + break; + } + } + // File wasn't found in SearchMan, try to parse the path as a relative path. + if (!file) { + Common::FSNode searchNode = getNodeForRelativePath(filename); + if (searchNode.exists() && !searchNode.isDirectory() && searchNode.isReadable()) { + file = searchNode.createReadStream(); + } + } + if (file) { + uint32 magic1, magic2; + magic1 = file->readUint32LE(); + magic2 = file->readUint32LE(); + + bool compressed = false; + if (magic1 == DCGF_MAGIC && magic2 == COMPRESSED_FILE_MAGIC) { + compressed = true; + } + + if (compressed) { + uint32 dataOffset, compSize, uncompSize; + dataOffset = file->readUint32LE(); + compSize = file->readUint32LE(); + uncompSize = file->readUint32LE(); + + byte *compBuffer = new byte[compSize]; + if (!compBuffer) { + error("Error allocating memory for compressed file '%s'", filename.c_str()); + delete file; + return NULL; + } + + byte *data = new byte[uncompSize]; + if (!data) { + error("Error allocating buffer for file '%s'", filename.c_str()); + delete[] compBuffer; + delete file; + return NULL; + } + file->seek(dataOffset + prefixSize, SEEK_SET); + file->read(compBuffer, compSize); + + if (Common::uncompress(data, (unsigned long *)&uncompSize, compBuffer, compSize) != true) { + error("Error uncompressing file '%s'", filename.c_str()); + delete[] compBuffer; + delete file; + return NULL; + } + + delete[] compBuffer; + delete file; + return new Common::MemoryReadStream(data, uncompSize, DisposeAfterUse::YES); + } else { + file->seek(0, SEEK_SET); + return file; + } + + return file; + + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_disk_file.h b/engines/wintermute/base/file/base_disk_file.h new file mode 100644 index 0000000000..c9f93b80d9 --- /dev/null +++ b/engines/wintermute/base/file/base_disk_file.h @@ -0,0 +1,41 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_DISKFILE_H +#define WINTERMUTE_BASE_DISKFILE_H + +#include "common/stream.h" + +namespace Wintermute { + +Common::SeekableReadStream *openDiskFile(const Common::String &filename); +bool diskFileExists(const Common::String &filename); + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_file.cpp b/engines/wintermute/base/file/base_file.cpp new file mode 100644 index 0000000000..f52a13211e --- /dev/null +++ b/engines/wintermute/base/file/base_file.cpp @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_file.h" +#include "common/memstream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +BaseFile::BaseFile() { + _pos = 0; + _size = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFile::~BaseFile() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFile::isEOF() { + return _pos == _size; +} + +Common::SeekableReadStream *BaseFile::getMemStream() { + uint32 oldPos = getPos(); + seek(0); + byte *data = new byte[getSize()]; + read(data, getSize()); + seek(oldPos); + Common::MemoryReadStream *memStream = new Common::MemoryReadStream(data, getSize(), DisposeAfterUse::YES); + return memStream; +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_file.h b/engines/wintermute/base/file/base_file.h new file mode 100644 index 0000000000..82f6ce3554 --- /dev/null +++ b/engines/wintermute/base/file/base_file.h @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILE_H +#define WINTERMUTE_BASE_FILE_H + + +#include "engines/wintermute/base/base.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Wintermute { + +class BaseFile { +protected: + uint32 _pos; + uint32 _size; +public: + virtual uint32 getSize() { + return _size; + }; + virtual uint32 getPos() { + return _pos; + }; + virtual bool seek(uint32 pos, int whence = SEEK_SET) = 0; + virtual bool read(void *buffer, uint32 size) = 0; + virtual bool close() = 0; + virtual bool open(const Common::String &filename) = 0; + virtual bool isEOF(); + BaseFile(); + virtual ~BaseFile(); + // Temporary solution to allow usage in ScummVM-code: + virtual Common::SeekableReadStream *getMemStream(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_file_entry.cpp b/engines/wintermute/base/file/base_file_entry.cpp new file mode 100644 index 0000000000..b9805d78dd --- /dev/null +++ b/engines/wintermute/base/file/base_file_entry.cpp @@ -0,0 +1,73 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/base_package.h" +#include "common/stream.h" +#include "common/substream.h" +#include "common/zlib.h" + +namespace Wintermute { + +Common::SeekableReadStream *BaseFileEntry::createReadStream() const { + Common::SeekableReadStream *file = _package->getFilePointer(); + if (!file) { + return NULL; + } + + bool compressed = (_compressedLength != 0); + + if (compressed) { + file = Common::wrapCompressedReadStream(new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES), _length); // + } else { + file = new Common::SeekableSubReadStream(file, _offset, _offset + _length, DisposeAfterUse::YES); + } + + file->seek(0); + + return file; +} + +////////////////////////////////////////////////////////////////////////// +BaseFileEntry::BaseFileEntry() { + _package = NULL; + _length = _compressedLength = _offset = _flags = 0; + _filename = ""; + + _timeDate1 = _timeDate2 = 0; + + _journalTime = 0; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFileEntry::~BaseFileEntry() { + _package = NULL; // ref only +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_file_entry.h b/engines/wintermute/base/file/base_file_entry.h new file mode 100644 index 0000000000..6e4823d994 --- /dev/null +++ b/engines/wintermute/base/file/base_file_entry.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FILEENTRY_H +#define WINTERMUTE_BASE_FILEENTRY_H + +#include "common/archive.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Wintermute { + +class BasePackage; + +class BaseFileEntry : public Common::ArchiveMember { +public: + virtual Common::SeekableReadStream *createReadStream() const; + virtual Common::String getName() const { return _filename; } + uint32 _timeDate2; + uint32 _timeDate1; + uint32 _flags; + uint32 _journalTime; + Common::String _filename; + uint32 _compressedLength; + uint32 _length; + uint32 _offset; + BasePackage *_package; + BaseFileEntry(); + virtual ~BaseFileEntry(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_package.cpp b/engines/wintermute/base/file/base_package.cpp new file mode 100644 index 0000000000..51a1558a7c --- /dev/null +++ b/engines/wintermute/base/file/base_package.cpp @@ -0,0 +1,276 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_package.h" +#include "engines/wintermute/base/file/base_file_entry.h" +#include "engines/wintermute/base/file/dcpackage.h" +#include "engines/wintermute/wintermute.h" +#include "common/file.h" +#include "common/stream.h" +#include "common/debug.h" + +namespace Wintermute { + +BasePackage::BasePackage() { + _name = ""; + _cd = 0; + _priority = 0; + _boundToExe = false; +} + +Common::SeekableReadStream *BasePackage::getFilePointer() { + Common::SeekableReadStream *stream = _fsnode.createReadStream(); + + return stream; +} + +static bool findPackageSignature(Common::SeekableReadStream *f, uint32 *offset) { + byte buf[32768]; + + byte signature[8]; + ((uint32 *)signature)[0] = PACKAGE_MAGIC_1; + ((uint32 *)signature)[1] = PACKAGE_MAGIC_2; + + uint32 fileSize = (uint32)f->size(); + uint32 startPos = 1024 * 1024; + uint32 bytesRead = startPos; + + while (bytesRead < fileSize - 16) { + uint32 toRead = MIN((unsigned int)32768, fileSize - bytesRead); + f->seek((int32)startPos, SEEK_SET); + uint32 actuallyRead = f->read(buf, toRead); + if (actuallyRead != toRead) { + return false; + } + + for (uint32 i = 0; i < toRead - 8; i++) + if (!memcmp(buf + i, signature, 8)) { + *offset = startPos + i; + return true; + } + + bytesRead = bytesRead + toRead - 16; + startPos = startPos + toRead - 16; + + } + return false; + +} + +void TPackageHeader::readFromStream(Common::ReadStream *stream) { + _magic1 = stream->readUint32LE(); + _magic2 = stream->readUint32LE(); + _packageVersion = stream->readUint32LE(); + + _gameVersion = stream->readUint32LE(); + + _priority = stream->readByte(); + _cd = stream->readByte(); + _masterIndex = stream->readByte(); + stream->readByte(); // To align the next byte... + + _creationTime = stream->readUint32LE(); + + stream->read(_desc, 100); + _numDirs = stream->readUint32LE(); +} + +PackageSet::PackageSet(Common::FSNode file, const Common::String &filename, bool searchSignature) { + uint32 absoluteOffset = 0; + _priority = 0; + bool boundToExe = false; + Common::SeekableReadStream *stream = file.createReadStream(); + if (!stream) { + return; + } + if (searchSignature) { + uint32 offset; + if (!findPackageSignature(stream, &offset)) { + delete stream; + return; + } else { + stream->seek(offset, SEEK_SET); + absoluteOffset = offset; + boundToExe = true; + } + } + + TPackageHeader hdr; + hdr.readFromStream(stream); + if (hdr._magic1 != PACKAGE_MAGIC_1 || hdr._magic2 != PACKAGE_MAGIC_2 || hdr._packageVersion > PACKAGE_VERSION) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Invalid header in package file '%s'. Ignoring.", filename.c_str()); + delete stream; + return; + } + + if (hdr._packageVersion != PACKAGE_VERSION) { + debugC(kWintermuteDebugFileAccess | kWintermuteDebugLog, " Warning: package file '%s' is outdated.", filename.c_str()); + } + _priority = hdr._priority; + // new in v2 + if (hdr._packageVersion == PACKAGE_VERSION) { + uint32 dirOffset; + dirOffset = stream->readUint32LE(); + dirOffset += absoluteOffset; + stream->seek(dirOffset, SEEK_SET); + } + assert(hdr._numDirs == 1); + for (uint32 i = 0; i < hdr._numDirs; i++) { + BasePackage *pkg = new BasePackage(); + if (!pkg) { + return; + } + pkg->_fsnode = file; + + pkg->_boundToExe = boundToExe; + + // read package info + byte nameLength = stream->readByte(); + char *pkgName = new char[nameLength]; + stream->read(pkgName, nameLength); + pkg->_name = pkgName; + pkg->_cd = stream->readByte(); + pkg->_priority = hdr._priority; + delete[] pkgName; + pkgName = NULL; + + if (!hdr._masterIndex) { + pkg->_cd = 0; // override CD to fixed disk + } + _packages.push_back(pkg); + + // read file entries + uint32 numFiles = stream->readUint32LE(); + + for (uint32 j = 0; j < numFiles; j++) { + char *name; + uint32 offset, length, compLength, flags;/*, timeDate1, timeDate2;*/ + + nameLength = stream->readByte(); + name = new char[nameLength]; + stream->read(name, nameLength); + + // v2 - xor name + if (hdr._packageVersion == PACKAGE_VERSION) { + for (int k = 0; k < nameLength; k++) { + ((byte *)name)[k] ^= 'D'; + } + } + debugC(kWintermuteDebugFileAccess, "Package contains %s", name); + + Common::String upcName = name; + upcName.toUppercase(); + delete[] name; + name = NULL; + + offset = stream->readUint32LE(); + offset += absoluteOffset; + length = stream->readUint32LE(); + compLength = stream->readUint32LE(); + flags = stream->readUint32LE(); + + if (hdr._packageVersion == PACKAGE_VERSION) { + /* timeDate1 = */ stream->readUint32LE(); + /* timeDate2 = */ stream->readUint32LE(); + } + _filesIter = _files.find(upcName); + if (_filesIter == _files.end()) { + BaseFileEntry *fileEntry = new BaseFileEntry(); + fileEntry->_package = pkg; + fileEntry->_offset = offset; + fileEntry->_length = length; + fileEntry->_compressedLength = compLength; + fileEntry->_flags = flags; + + _files[upcName] = Common::ArchiveMemberPtr(fileEntry); + } else { + // current package has higher priority than the registered + // TODO: This cast might be a bit ugly. + BaseFileEntry *filePtr = (BaseFileEntry *) &*(_filesIter->_value); + if (pkg->_priority > filePtr->_package->_priority) { + filePtr->_package = pkg; + filePtr->_offset = offset; + filePtr->_length = length; + filePtr->_compressedLength = compLength; + filePtr->_flags = flags; + } + } + } + } + debugC(kWintermuteDebugFileAccess, " Registered %d files in %d package(s)", _files.size(), _packages.size()); + + delete stream; +} + +PackageSet::~PackageSet() { + for (Common::Array<BasePackage *>::iterator it = _packages.begin(); it != _packages.end(); ++it) { + delete *it; + } + _packages.clear(); +} + +bool PackageSet::hasFile(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + return (it != _files.end()); +} + +int PackageSet::listMembers(Common::ArchiveMemberList &list) const { + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it = _files.begin(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator end = _files.end(); + int count = 0; + for (; it != end; ++it) { + const Common::ArchiveMemberPtr ptr(it->_value); + list.push_back(ptr); + count++; + } + return count; +} + +const Common::ArchiveMemberPtr PackageSet::getMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + return Common::ArchiveMemberPtr(it->_value); +} + +Common::SeekableReadStream *PackageSet::createReadStreamForMember(const Common::String &name) const { + Common::String upcName = name; + upcName.toUppercase(); + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::const_iterator it; + it = _files.find(upcName.c_str()); + if (it != _files.end()) { + return it->_value->createReadStream(); + } + return NULL; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_package.h b/engines/wintermute/base/file/base_package.h new file mode 100644 index 0000000000..2882eb03b7 --- /dev/null +++ b/engines/wintermute/base/file/base_package.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_PACKAGE_H +#define WINTERMUTE_BASE_PACKAGE_H + +#include "common/archive.h" +#include "common/stream.h" +#include "common/fs.h" + +namespace Wintermute { +class BasePackage { +public: + Common::SeekableReadStream *getFilePointer(); + Common::FSNode _fsnode; + bool _boundToExe; + byte _priority; + Common::String _name; + int _cd; + BasePackage(); +}; + +class PackageSet : public Common::Archive { +public: + virtual ~PackageSet(); + + PackageSet(Common::FSNode package, const Common::String &filename = "", bool searchSignature = false); + /** + * Check if a member with the given name is present in the Archive. + * Patterns are not allowed, as this is meant to be a quick File::exists() + * replacement. + */ + virtual bool hasFile(const Common::String &name) const; + + /** + * Add all members of the Archive to list. + * Must only append to list, and not remove elements from it. + * + * @return the number of names added to list + */ + virtual int listMembers(Common::ArchiveMemberList &list) const; + + /** + * Returns a ArchiveMember representation of the given file. + */ + virtual const Common::ArchiveMemberPtr getMember(const Common::String &name) const; + + /** + * Create a stream bound to a member with the specified name in the + * archive. If no member with this name exists, 0 is returned. + * @return the newly created input stream + */ + virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const; + + int getPriority() const { return _priority; } +private: + byte _priority; + Common::Array<BasePackage *> _packages; + Common::HashMap<Common::String, Common::ArchiveMemberPtr> _files; + Common::HashMap<Common::String, Common::ArchiveMemberPtr>::iterator _filesIter; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_resources.cpp b/engines/wintermute/base/file/base_resources.cpp new file mode 100644 index 0000000000..0b32cb0c4f --- /dev/null +++ b/engines/wintermute/base/file/base_resources.cpp @@ -0,0 +1,2830 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/file/base_resources.h" +#include "common/str.h" +#include "common/memstream.h" + +namespace Wintermute { + +unsigned char invalid[] = { + 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x20, 0x4e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +} ; + +unsigned char invaliddebug[] = { + 0x42, 0x4d, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, + 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0x00, 0xc0, 0xdc, 0xc0, 0x00, 0xf0, 0xca, 0xa6, 0x00, 0x00, 0x20, + 0x40, 0x00, 0x00, 0x20, 0x60, 0x00, 0x00, 0x20, 0x80, 0x00, 0x00, 0x20, 0xa0, 0x00, 0x00, 0x20, + 0xc0, 0x00, 0x00, 0x20, 0xe0, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, + 0x40, 0x00, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, + 0xc0, 0x00, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x60, + 0x40, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x80, 0x00, 0x00, 0x60, 0xa0, 0x00, 0x00, 0x60, + 0xc0, 0x00, 0x00, 0x60, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, + 0x40, 0x00, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, + 0xc0, 0x00, 0x00, 0x80, 0xe0, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0xa0, 0x20, 0x00, 0x00, 0xa0, + 0x40, 0x00, 0x00, 0xa0, 0x60, 0x00, 0x00, 0xa0, 0x80, 0x00, 0x00, 0xa0, 0xa0, 0x00, 0x00, 0xa0, + 0xc0, 0x00, 0x00, 0xa0, 0xe0, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, + 0x40, 0x00, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, + 0xc0, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x20, 0x00, 0x00, 0xe0, + 0x40, 0x00, 0x00, 0xe0, 0x60, 0x00, 0x00, 0xe0, 0x80, 0x00, 0x00, 0xe0, 0xa0, 0x00, 0x00, 0xe0, + 0xc0, 0x00, 0x00, 0xe0, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x20, 0x00, 0x40, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x60, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0xa0, 0x00, 0x40, 0x00, + 0xc0, 0x00, 0x40, 0x00, 0xe0, 0x00, 0x40, 0x20, 0x00, 0x00, 0x40, 0x20, 0x20, 0x00, 0x40, 0x20, + 0x40, 0x00, 0x40, 0x20, 0x60, 0x00, 0x40, 0x20, 0x80, 0x00, 0x40, 0x20, 0xa0, 0x00, 0x40, 0x20, + 0xc0, 0x00, 0x40, 0x20, 0xe0, 0x00, 0x40, 0x40, 0x00, 0x00, 0x40, 0x40, 0x20, 0x00, 0x40, 0x40, + 0x40, 0x00, 0x40, 0x40, 0x60, 0x00, 0x40, 0x40, 0x80, 0x00, 0x40, 0x40, 0xa0, 0x00, 0x40, 0x40, + 0xc0, 0x00, 0x40, 0x40, 0xe0, 0x00, 0x40, 0x60, 0x00, 0x00, 0x40, 0x60, 0x20, 0x00, 0x40, 0x60, + 0x40, 0x00, 0x40, 0x60, 0x60, 0x00, 0x40, 0x60, 0x80, 0x00, 0x40, 0x60, 0xa0, 0x00, 0x40, 0x60, + 0xc0, 0x00, 0x40, 0x60, 0xe0, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0x80, 0x20, 0x00, 0x40, 0x80, + 0x40, 0x00, 0x40, 0x80, 0x60, 0x00, 0x40, 0x80, 0x80, 0x00, 0x40, 0x80, 0xa0, 0x00, 0x40, 0x80, + 0xc0, 0x00, 0x40, 0x80, 0xe0, 0x00, 0x40, 0xa0, 0x00, 0x00, 0x40, 0xa0, 0x20, 0x00, 0x40, 0xa0, + 0x40, 0x00, 0x40, 0xa0, 0x60, 0x00, 0x40, 0xa0, 0x80, 0x00, 0x40, 0xa0, 0xa0, 0x00, 0x40, 0xa0, + 0xc0, 0x00, 0x40, 0xa0, 0xe0, 0x00, 0x40, 0xc0, 0x00, 0x00, 0x40, 0xc0, 0x20, 0x00, 0x40, 0xc0, + 0x40, 0x00, 0x40, 0xc0, 0x60, 0x00, 0x40, 0xc0, 0x80, 0x00, 0x40, 0xc0, 0xa0, 0x00, 0x40, 0xc0, + 0xc0, 0x00, 0x40, 0xc0, 0xe0, 0x00, 0x40, 0xe0, 0x00, 0x00, 0x40, 0xe0, 0x20, 0x00, 0x40, 0xe0, + 0x40, 0x00, 0x40, 0xe0, 0x60, 0x00, 0x40, 0xe0, 0x80, 0x00, 0x40, 0xe0, 0xa0, 0x00, 0x40, 0xe0, + 0xc0, 0x00, 0x40, 0xe0, 0xe0, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, + 0x40, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xa0, 0x00, 0x80, 0x00, + 0xc0, 0x00, 0x80, 0x00, 0xe0, 0x00, 0x80, 0x20, 0x00, 0x00, 0x80, 0x20, 0x20, 0x00, 0x80, 0x20, + 0x40, 0x00, 0x80, 0x20, 0x60, 0x00, 0x80, 0x20, 0x80, 0x00, 0x80, 0x20, 0xa0, 0x00, 0x80, 0x20, + 0xc0, 0x00, 0x80, 0x20, 0xe0, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x80, 0x40, + 0x40, 0x00, 0x80, 0x40, 0x60, 0x00, 0x80, 0x40, 0x80, 0x00, 0x80, 0x40, 0xa0, 0x00, 0x80, 0x40, + 0xc0, 0x00, 0x80, 0x40, 0xe0, 0x00, 0x80, 0x60, 0x00, 0x00, 0x80, 0x60, 0x20, 0x00, 0x80, 0x60, + 0x40, 0x00, 0x80, 0x60, 0x60, 0x00, 0x80, 0x60, 0x80, 0x00, 0x80, 0x60, 0xa0, 0x00, 0x80, 0x60, + 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x20, 0x00, 0x80, 0x80, + 0x40, 0x00, 0x80, 0x80, 0x60, 0x00, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0xa0, 0x00, 0x80, 0x80, + 0xc0, 0x00, 0x80, 0x80, 0xe0, 0x00, 0x80, 0xa0, 0x00, 0x00, 0x80, 0xa0, 0x20, 0x00, 0x80, 0xa0, + 0x40, 0x00, 0x80, 0xa0, 0x60, 0x00, 0x80, 0xa0, 0x80, 0x00, 0x80, 0xa0, 0xa0, 0x00, 0x80, 0xa0, + 0xc0, 0x00, 0x80, 0xa0, 0xe0, 0x00, 0x80, 0xc0, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x00, 0x80, 0xc0, + 0x40, 0x00, 0x80, 0xc0, 0x60, 0x00, 0x80, 0xc0, 0x80, 0x00, 0x80, 0xc0, 0xa0, 0x00, 0x80, 0xc0, + 0xc0, 0x00, 0x80, 0xc0, 0xe0, 0x00, 0x80, 0xe0, 0x00, 0x00, 0x80, 0xe0, 0x20, 0x00, 0x80, 0xe0, + 0x40, 0x00, 0x80, 0xe0, 0x60, 0x00, 0x80, 0xe0, 0x80, 0x00, 0x80, 0xe0, 0xa0, 0x00, 0x80, 0xe0, + 0xc0, 0x00, 0x80, 0xe0, 0xe0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x20, 0x00, 0xc0, 0x00, + 0x40, 0x00, 0xc0, 0x00, 0x60, 0x00, 0xc0, 0x00, 0x80, 0x00, 0xc0, 0x00, 0xa0, 0x00, 0xc0, 0x00, + 0xc0, 0x00, 0xc0, 0x00, 0xe0, 0x00, 0xc0, 0x20, 0x00, 0x00, 0xc0, 0x20, 0x20, 0x00, 0xc0, 0x20, + 0x40, 0x00, 0xc0, 0x20, 0x60, 0x00, 0xc0, 0x20, 0x80, 0x00, 0xc0, 0x20, 0xa0, 0x00, 0xc0, 0x20, + 0xc0, 0x00, 0xc0, 0x20, 0xe0, 0x00, 0xc0, 0x40, 0x00, 0x00, 0xc0, 0x40, 0x20, 0x00, 0xc0, 0x40, + 0x40, 0x00, 0xc0, 0x40, 0x60, 0x00, 0xc0, 0x40, 0x80, 0x00, 0xc0, 0x40, 0xa0, 0x00, 0xc0, 0x40, + 0xc0, 0x00, 0xc0, 0x40, 0xe0, 0x00, 0xc0, 0x60, 0x00, 0x00, 0xc0, 0x60, 0x20, 0x00, 0xc0, 0x60, + 0x40, 0x00, 0xc0, 0x60, 0x60, 0x00, 0xc0, 0x60, 0x80, 0x00, 0xc0, 0x60, 0xa0, 0x00, 0xc0, 0x60, + 0xc0, 0x00, 0xc0, 0x60, 0xe0, 0x00, 0xc0, 0x80, 0x00, 0x00, 0xc0, 0x80, 0x20, 0x00, 0xc0, 0x80, + 0x40, 0x00, 0xc0, 0x80, 0x60, 0x00, 0xc0, 0x80, 0x80, 0x00, 0xc0, 0x80, 0xa0, 0x00, 0xc0, 0x80, + 0xc0, 0x00, 0xc0, 0x80, 0xe0, 0x00, 0xc0, 0xa0, 0x00, 0x00, 0xc0, 0xa0, 0x20, 0x00, 0xc0, 0xa0, + 0x40, 0x00, 0xc0, 0xa0, 0x60, 0x00, 0xc0, 0xa0, 0x80, 0x00, 0xc0, 0xa0, 0xa0, 0x00, 0xc0, 0xa0, + 0xc0, 0x00, 0xc0, 0xa0, 0xe0, 0x00, 0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0x20, 0x00, 0xc0, 0xc0, + 0x40, 0x00, 0xc0, 0xc0, 0x60, 0x00, 0xc0, 0xc0, 0x80, 0x00, 0xc0, 0xc0, 0xa0, 0x00, 0xf0, 0xfb, + 0xff, 0x00, 0xa4, 0xa0, 0xa0, 0x00, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, + 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, + 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0xf9, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, + 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9 +} ; + +unsigned char systemfont[] = { + 0x42, 0x4d, 0x36, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x04, 0x00, 0x00, 0x28, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x70, 0x0b, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x80, 0x80, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x01, 0x02, 0x02, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x00, 0x01, 0x02, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00, 0x02, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x02, 0x01, 0x00, 0x02, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, + 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x02, 0x01, 0x02, 0x01, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x01, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x01, 0x02, 0x00, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x01, + 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x00, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x00, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x02, 0x00, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, + 0x00, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x00, 0x01, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +} ; + +Common::SeekableReadStream *BaseResources::getFile(const Common::String &filename) { + if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) { + return new Common::MemoryReadStream(invalid, sizeof(invalid), DisposeAfterUse::NO); + } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) { + return new Common::MemoryReadStream(invaliddebug, sizeof(invalid), DisposeAfterUse::NO); + } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) { + return new Common::MemoryReadStream(systemfont, sizeof(invalid), DisposeAfterUse::NO); + } + return NULL; +} + +bool BaseResources::hasFile(const Common::String &filename) { + if (scumm_stricmp(filename.c_str(), "invalid.bmp") == 0) { + return true; + } else if (scumm_stricmp(filename.c_str(), "invalid_debug.bmp") == 0) { + return true; + } else if (scumm_stricmp(filename.c_str(), "syste_font.bmp") == 0) { + return true; + } + return false; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_resources.h b/engines/wintermute/base/file/base_resources.h new file mode 100644 index 0000000000..91c30bcfa7 --- /dev/null +++ b/engines/wintermute/base/file/base_resources.h @@ -0,0 +1,45 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RESOURCES_H +#define WINTERMUTE_BASE_RESOURCES_H + +#include "common/stream.h" +#include "common/str.h" + +namespace Wintermute { + +class BaseResources { +public: + static Common::SeekableReadStream *getFile(const Common::String &filename); + static bool hasFile(const Common::String &filename); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/base_save_thumb_file.cpp b/engines/wintermute/base/file/base_save_thumb_file.cpp new file mode 100644 index 0000000000..94d3e5a94e --- /dev/null +++ b/engines/wintermute/base/file/base_save_thumb_file.cpp @@ -0,0 +1,153 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/base/file/base_save_thumb_file.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbFile::BaseSaveThumbFile() { + _data = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSaveThumbFile::~BaseSaveThumbFile() { + close(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::open(const Common::String &filename) { + close(); + + if (scumm_strnicmp(filename.c_str(), "savegame:", 9) != 0) { + return STATUS_FAILED; + } + + char *tempFilename = new char[strlen(filename.c_str()) - 8]; + strcpy(tempFilename, filename.c_str() + 9); + for (uint32 i = 0; i < strlen(tempFilename); i++) { + if (tempFilename[i] < '0' || tempFilename[i] > '9') { + tempFilename[i] = '\0'; + break; + } + } + + // get slot number from name + int slot = atoi(tempFilename); + delete[] tempFilename; + + BasePersistenceManager *pm = new BasePersistenceManager(); + Common::String slotFilename = pm->getFilenameForSlot(slot); + if (!pm) { + return STATUS_FAILED; + } + + if (DID_FAIL(pm->initLoad(slotFilename))) { + delete pm; + return STATUS_FAILED; + } + + bool res; + + if (pm->_thumbnailDataSize != 0) { + _data = new byte[pm->_thumbnailDataSize]; + memcpy(_data, pm->_thumbnailData, pm->_thumbnailDataSize); + _size = pm->_thumbnailDataSize; + res = STATUS_OK; + } else { + res = STATUS_FAILED; + } + delete pm; + + return res; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::close() { + delete[] _data; + _data = NULL; + + _pos = 0; + _size = 0; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::read(void *buffer, uint32 size) { + if (!_data || _pos + size > _size) { + return STATUS_FAILED; + } + + memcpy(buffer, (byte *)_data + _pos, size); + _pos += size; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSaveThumbFile::seek(uint32 pos, int whence) { + if (!_data) { + return STATUS_FAILED; + } + + uint32 newPos = 0; + + switch (whence) { + case SEEK_SET: + newPos = pos; + break; + case SEEK_END: + newPos = _size + pos; + break; + case SEEK_CUR: + newPos = _pos + pos; + break; + } + + if (newPos > _size) { + return STATUS_FAILED; + } else { + _pos = newPos; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/file/base_save_thumb_file.h b/engines/wintermute/base/file/base_save_thumb_file.h new file mode 100644 index 0000000000..3b217525fd --- /dev/null +++ b/engines/wintermute/base/file/base_save_thumb_file.h @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SAVETHUMBFILE_H +#define WINTERMUTE_BASE_SAVETHUMBFILE_H + + +#include "engines/wintermute/base/file/base_file.h" + +namespace Wintermute { + +//TODO: Get rid of this +class BaseSaveThumbFile : public BaseFile { +public: + BaseSaveThumbFile(); + virtual ~BaseSaveThumbFile(); + virtual bool seek(uint32 pos, int whence = SEEK_SET); + virtual bool read(void *buffer, uint32 size); + virtual bool close(); + virtual bool open(const Common::String &filename); +private: + byte *_data; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/file/dcpackage.h b/engines/wintermute/base/file/dcpackage.h new file mode 100644 index 0000000000..2234139408 --- /dev/null +++ b/engines/wintermute/base/file/dcpackage.h @@ -0,0 +1,80 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef _DCPACKAGE_H_ +#define _DCPACKAGE_H_ + + +#define PACKAGE_MAGIC_1 0xDEC0ADDE +#define PACKAGE_MAGIC_2 0x4B4E554A // "JUNK" +#define PACKAGE_VERSION 0x00000200 +#define PACKAGE_EXTENSION "dcp" + +#include "common/stream.h" + +namespace Wintermute { + +struct TPackageHeader { + uint32 _magic1; + uint32 _magic2; + uint32 _packageVersion; + uint32 _gameVersion; + byte _priority; + byte _cd; + bool _masterIndex; + uint32 _creationTime; + char _desc[100]; + uint32 _numDirs; + // base_package.cpp: + void readFromStream(Common::ReadStream *stream); +}; + +/* +v2: uint32 DirOffset + + +Dir: byte NameLength + char Name [NameLength] + byte CD; + uint32 NumEntries + + +Entry: byte NameLength + char Name [NameLength] + uint32 Offset + uint32 Length + uint32 CompLength + uint32 Flags +v2: uint32 TimeDate1 + uint32 TimeDate2 // not used + +*/ + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font.cpp b/engines/wintermute/base/font/base_font.cpp new file mode 100644 index 0000000000..87dd3da5a3 --- /dev/null +++ b/engines/wintermute/base/font/base_font.cpp @@ -0,0 +1,140 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_file_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFont, false) + +////////////////////////////////////////////////////////////////////// +BaseFont::BaseFont(BaseGame *inGame) : BaseObject(inGame) { + +} + + +////////////////////////////////////////////////////////////////////// +BaseFont::~BaseFont() { +} + + +////////////////////////////////////////////////////////////////////// +void BaseFont::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { +} + + +////////////////////////////////////////////////////////////////////// +int BaseFont::getTextHeight(byte *text, int width) { + return 0; +} + + +////////////////////////////////////////////////////////////////////// +int BaseFont::getTextWidth(byte *text, int maxLength) { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFont::getLetterHeight() { + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFont::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +BaseFont *BaseFont::createFromFile(BaseGame *gameRef, const Common::String &filename) { + if (isTrueType(gameRef, filename)) { + BaseFontTT *font = new BaseFontTT(gameRef); + if (font) { + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + } + return font; + } else { + BaseFontBitmap *font = new BaseFontBitmap(gameRef); + if (font) { + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + } + return font; + } +} + + +TOKEN_DEF_START +TOKEN_DEF(FONT) +TOKEN_DEF(TTFONT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool BaseFont::isTrueType(BaseGame *gameRef, const Common::String &filename) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TTFONT) + TOKEN_TABLE_END + + + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + return false; + } + + byte *workBuffer = buffer; + + char *params; + BaseParser parser; + + bool ret = false; + if (parser.getCommand((char **)&workBuffer, commands, (char **)¶ms) == TOKEN_TTFONT) { + ret = true; + } + + delete[] buffer; + return ret; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font.h b/engines/wintermute/base/font/base_font.h new file mode 100644 index 0000000000..0abe62ab98 --- /dev/null +++ b/engines/wintermute/base/font/base_font.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONT_H +#define WINTERMUTE_BASE_FONT_H + +#include "engines/wintermute/base/base_object.h" + +#define NUM_CHARACTERS 256 + +namespace Wintermute { + +class BaseFont: public BaseObject { +public: + DECLARE_PERSISTENT(BaseFont, BaseObject) + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + virtual void initLoop() {} + virtual void afterLoad() {} + BaseFont(BaseGame *inGame); + virtual ~BaseFont(); + + static BaseFont *createFromFile(BaseGame *game, const Common::String &filename); + +private: + //bool loadBuffer(byte * Buffer); + //bool loadFile(const char* Filename); + static bool isTrueType(BaseGame *game, const Common::String &filename); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_bitmap.cpp b/engines/wintermute/base/font/base_font_bitmap.cpp new file mode 100644 index 0000000000..55f46c476b --- /dev/null +++ b/engines/wintermute/base/font/base_font_bitmap.cpp @@ -0,0 +1,590 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFontBitmap, false) + +////////////////////////////////////////////////////////////////////// +BaseFontBitmap::BaseFontBitmap(BaseGame *inGame) : BaseFont(inGame) { + _subframe = NULL; + _sprite = NULL; + _widthsFrame = 0; + memset(_widths, 0, NUM_CHARACTERS); + _tileWidth = _tileHeight = _numColumns = 0; + _fontextFix = false; + _freezable = false; + _wholeCell = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseFontBitmap::~BaseFontBitmap() { + delete _subframe; + delete _sprite; + _subframe = NULL; + _sprite = NULL; +} + + +////////////////////////////////////////////////////////////////////// +void BaseFontBitmap::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { + textHeightDraw(text, x, y, width, align, true, maxHeight, maxLength); +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getTextHeight(byte *text, int width) { + return textHeightDraw(text, 0, 0, width, TAL_LEFT, false); +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getTextWidth(byte *text, int maxLength) { + AnsiString str; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(Utf8String((char *)text)); + str = StringUtil::wideToAnsi(wstr); + } else { + str = AnsiString((char *)text); + } + + if (maxLength >= 0 && str.size() > (uint32)maxLength) { + str = Common::String(str.c_str(), (uint32)maxLength); + } + //str.substr(0, maxLength); // TODO: Remove + + int textWidth = 0; + for (int i = 0; (uint32)i < str.size(); i++) { + textWidth += getCharWidth((byte)str[i]); + } + + return textWidth; +} + + +////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int maxHeight, int maxLength) { + if (maxLength == 0) { + return 0; + } + + if (text == NULL || text[0] == '\0') { + return _tileHeight; + } + + AnsiString str; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(Utf8String((const char *)text)); + str = StringUtil::wideToAnsi(wstr); + } else { + str = AnsiString((const char *)text); + } + if (str.empty()) { + return 0; + } + + int lineLength = 0; + int realLength = 0; + int numLines = 0; + + int i; + + int index = -1; + int start = 0; + int end = 0; + int last_end = 0; + + bool done = false; + bool newLine = false; + bool longLine = false; + + if (draw) { + _gameRef->_renderer->startSpriteBatch(); + } + + while (!done) { + if (maxHeight > 0 && (numLines + 1)*_tileHeight > maxHeight) { + if (draw) { + _gameRef->_renderer->endSpriteBatch(); + } + return numLines * _tileHeight; + } + + index++; + + if (str[index] == ' ' && (maxHeight < 0 || maxHeight / _tileHeight > 1)) { + end = index - 1; + realLength = lineLength; + } + + if (str[index] == '\n') { + end = index - 1; + realLength = lineLength; + newLine = true; + } + + if (lineLength + getCharWidth(str[index]) > width && last_end == end) { + end = index - 1; + realLength = lineLength; + newLine = true; + longLine = true; + } + + if ((int)str.size() == (index + 1) || (maxLength >= 0 && index == maxLength - 1)) { + done = true; + if (!newLine) { + end = index; + lineLength += getCharWidth(str[index]); + realLength = lineLength; + } + } else { + lineLength += getCharWidth(str[index]); + } + + if ((lineLength > width) || done || newLine) { + if (end < 0) { + done = true; + } + int startX; + switch (align) { + case TAL_CENTER: + startX = x + (width - realLength) / 2; + break; + case TAL_RIGHT: + startX = x + width - realLength; + break; + case TAL_LEFT: + startX = x; + break; + default: + error("BaseFontBitmap::TextHeightDraw - Unhandled enum"); + break; + } + for (i = start; i < end + 1; i++) { + if (draw) { + drawChar(str[i], startX, y); + } + startX += getCharWidth(str[i]); + } + y += _tileHeight; + last_end = end; + if (longLine) { + end--; + } + start = end + 2; + index = end + 1; + lineLength = 0; + newLine = false; + longLine = false; + numLines++; + } + } + + if (draw) { + _gameRef->_renderer->endSpriteBatch(); + } + + return numLines * _tileHeight; +} + + +////////////////////////////////////////////////////////////////////// +void BaseFontBitmap::drawChar(byte c, int x, int y) { + if (_fontextFix) { + c--; + } + + int row, col; + + row = c / _numColumns; + col = c % _numColumns; + + Rect32 rect; + /* l t r b */ + int tileWidth; + if (_wholeCell) { + tileWidth = _tileWidth; + } else { + tileWidth = _widths[c]; + } + + BasePlatform::setRect(&rect, col * _tileWidth, row * _tileHeight, col * _tileWidth + tileWidth, (row + 1)*_tileHeight); + bool handled = false; + if (_sprite) { + _sprite->getCurrentFrame(); + if (_sprite->_currentFrame >= 0 && _sprite->_currentFrame < (int32)_sprite->_frames.size() && _sprite->_frames[_sprite->_currentFrame]) { + if (_sprite->_frames[_sprite->_currentFrame]->_subframes.size() > 0) { + _sprite->_frames[_sprite->_currentFrame]->_subframes[0]->_surface->displayTrans(x, y, rect); + } + handled = true; + } + } + if (!handled && _subframe) { + _subframe->_surface->displayTrans(x, y, rect); + } +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::loadFile(const Common::String &filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseFontBitmap::LoadFile failed for file '%s'", filename.c_str()); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename.c_str()); + + if (DID_FAIL(ret = loadBuffer(buffer))) { + _gameRef->LOG(0, "Error parsing FONT file '%s'", filename.c_str()); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(FONTEXT_FIX) +TOKEN_DEF(FONT) +TOKEN_DEF(IMAGE) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(COLUMNS) +TOKEN_DEF(TILE_WIDTH) +TOKEN_DEF(TILE_HEIGHT) +TOKEN_DEF(DEFAULT_WIDTH) +TOKEN_DEF(WIDTHS) +TOKEN_DEF(AUTO_WIDTH) +TOKEN_DEF(SPACE_WIDTH) +TOKEN_DEF(EXPAND_WIDTH) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(SPRITE) +TOKEN_DEF(WIDTHS_FRAME) +TOKEN_DEF(PAINT_WHOLE_CELL) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::loadBuffer(byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(FONTEXT_FIX) + TOKEN_TABLE(FONT) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(COLUMNS) + TOKEN_TABLE(TILE_WIDTH) + TOKEN_TABLE(TILE_HEIGHT) + TOKEN_TABLE(DEFAULT_WIDTH) + TOKEN_TABLE(WIDTHS) + TOKEN_TABLE(AUTO_WIDTH) + TOKEN_TABLE(SPACE_WIDTH) + TOKEN_TABLE(EXPAND_WIDTH) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(SPRITE) + TOKEN_TABLE(WIDTHS_FRAME) + TOKEN_TABLE(PAINT_WHOLE_CELL) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_FONT) { + _gameRef->LOG(0, "'FONT' keyword expected."); + return STATUS_FAILED; + } + buffer = (byte *)params; + + int widths[300]; + int num = 0, defaultWidth = 8; + int lastWidth = 0; + int i; + int r = 255, g = 255, b = 255; + bool custoTrans = false; + char *surfaceFile = NULL; + char *spriteFile = NULL; + + bool autoWidth = false; + int spaceWidth = 0; + int expandWidth = 0; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + + switch (cmd) { + case TOKEN_IMAGE: + surfaceFile = (char *)params; + break; + + case TOKEN_SPRITE: + spriteFile = (char *)params; + break; + + case TOKEN_TRANSPARENT: + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + custoTrans = true; + break; + + case TOKEN_WIDTHS: + parser.scanStr(params, "%D", widths, &num); + for (i = 0; lastWidth < NUM_CHARACTERS && num > 0; lastWidth++, num--, i++) { + _widths[lastWidth] = (byte)widths[i]; + } + break; + + case TOKEN_DEFAULT_WIDTH: + parser.scanStr(params, "%d", &defaultWidth); + break; + + case TOKEN_WIDTHS_FRAME: + parser.scanStr(params, "%d", &_widthsFrame); + break; + + case TOKEN_COLUMNS: + parser.scanStr(params, "%d", &_numColumns); + break; + + case TOKEN_TILE_WIDTH: + parser.scanStr(params, "%d", &_tileWidth); + break; + + case TOKEN_TILE_HEIGHT: + parser.scanStr(params, "%d", &_tileHeight); + break; + + case TOKEN_AUTO_WIDTH: + parser.scanStr(params, "%b", &autoWidth); + break; + + case TOKEN_FONTEXT_FIX: + parser.scanStr(params, "%b", &_fontextFix); + break; + + case TOKEN_PAINT_WHOLE_CELL: + parser.scanStr(params, "%b", &_wholeCell); + break; + + case TOKEN_SPACE_WIDTH: + parser.scanStr(params, "%d", &spaceWidth); + break; + + case TOKEN_EXPAND_WIDTH: + parser.scanStr(params, "%d", &expandWidth); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty((byte *)params, false); + break; + } + + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in FONT definition"); + return STATUS_FAILED; + } + + if (spriteFile != NULL) { + delete _sprite; + _sprite = new BaseSprite(_gameRef, this); + if (!_sprite || DID_FAIL(_sprite->loadFile(spriteFile))) { + delete _sprite; + _sprite = NULL; + } + } + + if (surfaceFile != NULL && !_sprite) { + _subframe = new BaseSubFrame(_gameRef); + if (custoTrans) { + _subframe->setSurface(surfaceFile, false, r, g, b); + } else { + _subframe->setSurface(surfaceFile); + } + } + + + if (((_subframe == NULL || _subframe->_surface == NULL) && _sprite == NULL) || _numColumns == 0 || _tileWidth == 0 || _tileHeight == 0) { + _gameRef->LOG(0, "Incomplete font definition"); + return STATUS_FAILED; + } + + if (autoWidth) { + // calculate characters width + getWidths(); + + // do we need to modify widths? + if (expandWidth != 0) { + for (i = 0; i < NUM_CHARACTERS; i++) { + int newWidth = (int)_widths[i] + expandWidth; + if (newWidth < 0) { + newWidth = 0; + } + + _widths[i] = (byte)newWidth; + } + } + + // handle space character + uint32 spaceChar = ' '; + if (_fontextFix) { + spaceChar--; + } + + if (spaceWidth != 0) { + _widths[spaceChar] = spaceWidth; + } else { + if (_widths[spaceChar] == expandWidth || _widths[spaceChar] == 0) { + _widths[spaceChar] = (_widths['m'] + _widths['i']) / 2; + } + } + } else { + for (i = lastWidth; i < NUM_CHARACTERS; i++) { + _widths[i] = defaultWidth; + } + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::persist(BasePersistenceManager *persistMgr) { + + BaseFont::persist(persistMgr); + persistMgr->transfer(TMEMBER(_numColumns)); + + persistMgr->transfer(TMEMBER(_subframe)); + persistMgr->transfer(TMEMBER(_tileHeight)); + persistMgr->transfer(TMEMBER(_tileWidth)); + persistMgr->transfer(TMEMBER(_sprite)); + persistMgr->transfer(TMEMBER(_widthsFrame)); + + if (persistMgr->getIsSaving()) { + persistMgr->putBytes(_widths, sizeof(_widths)); + } else { + persistMgr->getBytes(_widths, sizeof(_widths)); + } + + + persistMgr->transfer(TMEMBER(_fontextFix)); + persistMgr->transfer(TMEMBER(_wholeCell)); + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getCharWidth(byte index) { + if (_fontextFix) { + index--; + } + return _widths[index]; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontBitmap::getWidths() { + BaseSurface *surf = NULL; + + if (_sprite) { + if (_widthsFrame >= 0 && _widthsFrame < (int32)_sprite->_frames.size()) { + if (_sprite->_frames[_widthsFrame] && (int32)_sprite->_frames[_widthsFrame]->_subframes.size() > 0) { + surf = _sprite->_frames[_widthsFrame]->_subframes[0]->_surface; + } + } + } + if (surf == NULL && _subframe) { + surf = _subframe->_surface; + } + if (!surf || DID_FAIL(surf->startPixelOp())) { + return STATUS_FAILED; + } + + + for (int i = 0; i < NUM_CHARACTERS; i++) { + int xxx = (i % _numColumns) * _tileWidth; + int yyy = (i / _numColumns) * _tileHeight; + + + int minCol = -1; + for (int row = 0; row < _tileHeight; row++) { + for (int col = _tileWidth - 1; col >= minCol + 1; col--) { + if (xxx + col < 0 || xxx + col >= surf->getWidth() || yyy + row < 0 || yyy + row >= surf->getHeight()) { + continue; + } + if (!surf->isTransparentAtLite(xxx + col, yyy + row)) { + //min_col = col; + minCol = MAX(col, minCol); + break; + } + } + if (minCol == _tileWidth - 1) { + break; + } + } + + _widths[i] = minCol + 1; + } + surf->endPixelOp(); + /* + _gameRef->LOG(0, "----- %s ------", _filename); + for(int j=0; j<16; j++) + { + _gameRef->LOG(0, "%02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d %02d", _widths[j*16+0], _widths[j*16+1], _widths[j*16+2], _widths[j*16+3], _widths[j*16+4], _widths[j*16+5], _widths[j*16+6], _widths[j*16+7], _widths[j*16+8], _widths[j*16+9], _widths[j*16+10], _widths[j*16+11], _widths[j*16+12], _widths[j*16+13], _widths[j*16+14], _widths[j*16+15]); + } + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontBitmap::getLetterHeight() { + return _tileHeight; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_bitmap.h b/engines/wintermute/base/font/base_font_bitmap.h new file mode 100644 index 0000000000..2f3a69d097 --- /dev/null +++ b/engines/wintermute/base/font/base_font_bitmap.h @@ -0,0 +1,71 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTBITMAP_H +#define WINTERMUTE_BASE_FONTBITMAP_H + + +#include "engines/wintermute/base/font/base_font.h" + +namespace Wintermute { +class BaseSubFrame; +class BaseFontBitmap : public BaseFont { +public: + DECLARE_PERSISTENT(BaseFontBitmap, BaseFont) + bool loadBuffer(byte *Buffer); + bool loadFile(const Common::String &filename); + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + BaseFontBitmap(BaseGame *inGame); + virtual ~BaseFontBitmap(); + +private: + bool getWidths(); + BaseSprite *_sprite; + int _widthsFrame; + bool _fontextFix; + int _numColumns; + int _tileHeight; + int _tileWidth; + byte _widths[NUM_CHARACTERS]; + BaseSubFrame *_subframe; + bool _wholeCell; + + int getCharWidth(byte index); + void drawChar(byte c, int x, int y); + + int textHeightDraw(const byte *text, int x, int y, int width, TTextAlign align, bool draw, int max_height = -1, int maxLength = -1); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_storage.cpp b/engines/wintermute/base/font/base_font_storage.cpp new file mode 100644 index 0000000000..8128ffe897 --- /dev/null +++ b/engines/wintermute/base/font/base_font_storage.cpp @@ -0,0 +1,141 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_game.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(BaseFontStorage, true) + +////////////////////////////////////////////////////////////////////////// +BaseFontStorage::BaseFontStorage(BaseGame *inGame) : BaseClass(inGame) { +} + +////////////////////////////////////////////////////////////////////////// +BaseFontStorage::~BaseFontStorage() { + cleanup(true); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::cleanup(bool warn) { + for (uint32 i = 0; i < _fonts.size(); i++) { + if (warn) { + _gameRef->LOG(0, "Removing orphan font '%s'", _fonts[i]->getFilename()); + } + delete _fonts[i]; + } + _fonts.clear(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::initLoop() { + for (uint32 i = 0; i < _fonts.size(); i++) { + _fonts[i]->initLoop(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseFont *BaseFontStorage::addFont(const Common::String &filename) { + if (!filename.size()) { + return NULL; + } + + for (uint32 i = 0; i < _fonts.size(); i++) { + if (scumm_stricmp(_fonts[i]->getFilename(), filename.c_str()) == 0) { + _fonts[i]->_refCount++; + return _fonts[i]; + } + } + + /* + BaseFont* font = new BaseFont(_gameRef); + if (!font) return NULL; + + if (DID_FAIL(font->loadFile(filename))) { + delete font; + return NULL; + } + else { + font->_refCount = 1; + _fonts.add(font); + return font; + } + */ + BaseFont *font = BaseFont::createFromFile(_gameRef, filename); + if (font) { + font->_refCount = 1; + _fonts.add(font); + } + return font; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::removeFont(BaseFont *font) { + if (!font) { + return STATUS_FAILED; + } + + for (uint32 i = 0; i < _fonts.size(); i++) { + if (_fonts[i] == font) { + _fonts[i]->_refCount--; + if (_fonts[i]->_refCount <= 0) { + delete _fonts[i]; + _fonts.remove_at(i); + } + break; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontStorage::persist(BasePersistenceManager *persistMgr) { + + if (!persistMgr->getIsSaving()) { + cleanup(false); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + _fonts.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_storage.h b/engines/wintermute/base/font/base_font_storage.h new file mode 100644 index 0000000000..60874167e7 --- /dev/null +++ b/engines/wintermute/base/font/base_font_storage.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTSTORAGE_H +#define WINTERMUTE_BASE_FONTSTORAGE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { + +class BaseFont; + +class BaseFontStorage : public BaseClass { +public: + DECLARE_PERSISTENT(BaseFontStorage, BaseClass) + bool cleanup(bool warn = false); + bool removeFont(BaseFont *font); + BaseFont *addFont(const Common::String &filename); + BaseFontStorage(BaseGame *inGame); + virtual ~BaseFontStorage(); + BaseArray<BaseFont *> _fonts; + bool initLoop(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/font/base_font_truetype.cpp b/engines/wintermute/base/font/base_font_truetype.cpp new file mode 100644 index 0000000000..3219918e6d --- /dev/null +++ b/engines/wintermute/base/font/base_font_truetype.cpp @@ -0,0 +1,602 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/wintermute.h" +#include "graphics/fonts/ttf.h" +#include "graphics/fontman.h" +#include <limits.h> + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseFontTT, false) + +////////////////////////////////////////////////////////////////////////// +BaseFontTT::BaseFontTT(BaseGame *inGame) : BaseFont(inGame) { + _fontHeight = 12; + _isBold = _isItalic = _isUnderline = _isStriked = false; + + _fontFile = NULL; + _font = NULL; + _fallbackFont = NULL; + _deletableFont = NULL; + + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + _cachedTexts[i] = NULL; + } + + _lineHeight = 0; + _maxCharWidth = _maxCharHeight = 0; +} + +////////////////////////////////////////////////////////////////////////// +BaseFontTT::~BaseFontTT(void) { + clearCache(); + + for (uint32 i = 0; i < _layers.size(); i++) { + delete _layers[i]; + } + _layers.clear(); + + delete[] _fontFile; + _fontFile = NULL; + + delete _deletableFont; + _font = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::clearCache() { + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i]) { + delete _cachedTexts[i]; + } + _cachedTexts[i] = NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::initLoop() { + // we need more aggressive cache management on iOS not to waste too much memory on fonts + if (_gameRef->_constrainedMemory) { + // purge all cached images not used in the last frame + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i] == NULL) { + continue; + } + + if (!_cachedTexts[i]->_marked) { + delete _cachedTexts[i]; + _cachedTexts[i] = NULL; + } else { + _cachedTexts[i]->_marked = false; + } + } + } +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getTextWidth(byte *text, int maxLength) { + WideString textStr; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + textStr = StringUtil::utf8ToWide((char *)text); + } else { + textStr = StringUtil::ansiToWide((char *)text); + } + + if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { + textStr = Common::String(textStr.c_str(), (uint32)maxLength); + } + //text = text.substr(0, MaxLength); // TODO: Remove + + int textWidth, textHeight; + measureText(textStr, -1, -1, textWidth, textHeight); + + return textWidth; +} + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getTextHeight(byte *text, int width) { + WideString textStr; + + if (_gameRef->_textEncoding == TEXT_UTF8) { + textStr = StringUtil::utf8ToWide((char *)text); + } else { + textStr = StringUtil::ansiToWide((char *)text); + } + + + int textWidth, textHeight; + measureText(textStr, width, -1, textWidth, textHeight); + + return textHeight; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::drawText(const byte *text, int x, int y, int width, TTextAlign align, int maxHeight, int maxLength) { + if (text == NULL || strcmp((const char *)text, "") == 0) { + return; + } + + WideString textStr = (const char *)text; + + // TODO: Why do we still insist on Widestrings everywhere? + /* if (_gameRef->_textEncoding == TEXT_UTF8) text = StringUtil::Utf8ToWide((char *)Text); + else text = StringUtil::AnsiToWide((char *)Text);*/ + + if (maxLength >= 0 && textStr.size() > (uint32)maxLength) { + textStr = Common::String(textStr.c_str(), (uint32)maxLength); + } + //text = text.substr(0, MaxLength); // TODO: Remove + + BaseRenderer *renderer = _gameRef->_renderer; + + // find cached surface, if exists + int minPriority = INT_MAX; + int minIndex = -1; + BaseSurface *surface = NULL; + int textOffset = 0; + + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + if (_cachedTexts[i] == NULL) { + minPriority = 0; + minIndex = i; + } else { + if (_cachedTexts[i]->_text == textStr && _cachedTexts[i]->_align == align && _cachedTexts[i]->_width == width && _cachedTexts[i]->_maxHeight == maxHeight && _cachedTexts[i]->_maxLength == maxLength) { + surface = _cachedTexts[i]->_surface; + textOffset = _cachedTexts[i]->_textOffset; + _cachedTexts[i]->_priority++; + _cachedTexts[i]->_marked = true; + break; + } else { + if (_cachedTexts[i]->_priority < minPriority) { + minPriority = _cachedTexts[i]->_priority; + minIndex = i; + } + } + } + } + + // not found, create one + if (!surface) { + debugC(kWintermuteDebugFont, "Draw text: %s", text); + surface = renderTextToTexture(textStr, width, align, maxHeight, textOffset); + if (surface) { + // write surface to cache + if (_cachedTexts[minIndex] != NULL) { + delete _cachedTexts[minIndex]; + } + _cachedTexts[minIndex] = new BaseCachedTTFontText; + + _cachedTexts[minIndex]->_surface = surface; + _cachedTexts[minIndex]->_align = align; + _cachedTexts[minIndex]->_width = width; + _cachedTexts[minIndex]->_maxHeight = maxHeight; + _cachedTexts[minIndex]->_maxLength = maxLength; + _cachedTexts[minIndex]->_priority = 1; + _cachedTexts[minIndex]->_text = textStr; + _cachedTexts[minIndex]->_textOffset = textOffset; + _cachedTexts[minIndex]->_marked = true; + } + } + + + // and paint it + if (surface) { + Rect32 rc; + BasePlatform::setRect(&rc, 0, 0, surface->getWidth(), surface->getHeight()); + for (uint32 i = 0; i < _layers.size(); i++) { + uint32 color = _layers[i]->_color; + uint32 origForceAlpha = renderer->_forceAlphaColor; + if (renderer->_forceAlphaColor != 0) { + color = BYTETORGBA(RGBCOLGetR(color), RGBCOLGetG(color), RGBCOLGetB(color), RGBCOLGetA(renderer->_forceAlphaColor)); + renderer->_forceAlphaColor = 0; + } + surface->displayTransOffset(x, y - textOffset, rc, color, BLEND_NORMAL, false, false, _layers[i]->_offsetX, _layers[i]->_offsetY); + + renderer->_forceAlphaColor = origForceAlpha; + } + } + + +} + +////////////////////////////////////////////////////////////////////////// +BaseSurface *BaseFontTT::renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset) { + //TextLineList lines; + // TODO: Use WideString-conversion here. + //WrapText(text, width, maxHeight, lines); + Common::Array<Common::String> lines; + _font->wordWrapText(text, width, lines); + + while (maxHeight > 0 && lines.size() * _lineHeight > maxHeight) { + lines.pop_back(); + } + if (lines.size() == 0) { + return NULL; + } + + Graphics::TextAlign alignment = Graphics::kTextAlignInvalid; + if (align == TAL_LEFT) { + alignment = Graphics::kTextAlignLeft; + } else if (align == TAL_CENTER) { + alignment = Graphics::kTextAlignCenter; + } else if (align == TAL_RIGHT) { + alignment = Graphics::kTextAlignRight; + } + + debugC(kWintermuteDebugFont, "%s %d %d %d %d", text.c_str(), RGBCOLGetR(_layers[0]->_color), RGBCOLGetG(_layers[0]->_color), RGBCOLGetB(_layers[0]->_color), RGBCOLGetA(_layers[0]->_color)); +// void drawString(Surface *dst, const Common::String &str, int x, int y, int w, uint32 color, TextAlign align = kTextAlignLeft, int deltax = 0, bool useEllipsis = true) const; + Graphics::Surface *surface = new Graphics::Surface(); + if (_deletableFont) { // We actually have a TTF + surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + } else { // We are using a fallback, they can't do 32bpp + surface->create((uint16)width, (uint16)(_lineHeight * lines.size()), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0)); + } + uint32 useColor = 0xffffffff; + Common::Array<Common::String>::iterator it; + int heightOffset = 0; + for (it = lines.begin(); it != lines.end(); ++it) { + _font->drawString(surface, *it, 0, heightOffset, width, useColor, alignment); + heightOffset += (int)_lineHeight; + } + + BaseSurface *retSurface = _gameRef->_renderer->createSurface(); + Graphics::Surface *convertedSurface = surface->convertTo(Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24)); + retSurface->putSurface(*convertedSurface, true); + convertedSurface->free(); + surface->free(); + delete surface; + delete convertedSurface; + return retSurface; + // TODO: _isUnderline, _isBold, _isItalic, _isStriked +} + + +////////////////////////////////////////////////////////////////////////// +int BaseFontTT::getLetterHeight() { + return (int)getLineHeight(); +} + + +////////////////////////////////////////////////////////////////////// +bool BaseFontTT::loadFile(const Common::String &filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "BaseFontTT::LoadFile failed for file '%s'", filename.c_str()); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename.c_str()); + + if (DID_FAIL(ret = loadBuffer(buffer))) { + _gameRef->LOG(0, "Error parsing TTFONT file '%s'", filename.c_str()); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TTFONT) +TOKEN_DEF(SIZE) +TOKEN_DEF(FACE) +TOKEN_DEF(FILENAME) +TOKEN_DEF(BOLD) +TOKEN_DEF(ITALIC) +TOKEN_DEF(UNDERLINE) +TOKEN_DEF(STRIKE) +TOKEN_DEF(CHARSET) +TOKEN_DEF(COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(LAYER) +TOKEN_DEF(OFFSET_X) +TOKEN_DEF(OFFSET_Y) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////// +bool BaseFontTT::loadBuffer(byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TTFONT) + TOKEN_TABLE(SIZE) + TOKEN_TABLE(FACE) + TOKEN_TABLE(FILENAME) + TOKEN_TABLE(BOLD) + TOKEN_TABLE(ITALIC) + TOKEN_TABLE(UNDERLINE) + TOKEN_TABLE(STRIKE) + TOKEN_TABLE(CHARSET) + TOKEN_TABLE(COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(LAYER) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TTFONT) { + _gameRef->LOG(0, "'TTFONT' keyword expected."); + return STATUS_FAILED; + } + buffer = (byte *)params; + + uint32 baseColor = 0x00000000; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_SIZE: + parser.scanStr(params, "%d", &_fontHeight); + break; + + case TOKEN_FACE: + // we don't need this anymore + break; + + case TOKEN_FILENAME: + BaseUtils::setString(&_fontFile, params); + break; + + case TOKEN_BOLD: + parser.scanStr(params, "%b", &_isBold); + break; + + case TOKEN_ITALIC: + parser.scanStr(params, "%b", &_isItalic); + break; + + case TOKEN_UNDERLINE: + parser.scanStr(params, "%b", &_isUnderline); + break; + + case TOKEN_STRIKE: + parser.scanStr(params, "%b", &_isStriked); + break; + + case TOKEN_CHARSET: + // we don't need this anymore + break; + + case TOKEN_COLOR: { + int r, g, b; + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + baseColor = BYTETORGBA(r, g, b, RGBCOLGetA(baseColor)); + } + break; + + case TOKEN_ALPHA: { + int a; + parser.scanStr(params, "%d", &a); + baseColor = BYTETORGBA(RGBCOLGetR(baseColor), RGBCOLGetG(baseColor), RGBCOLGetB(baseColor), a); + } + break; + + case TOKEN_LAYER: { + BaseTTFontLayer *layer = new BaseTTFontLayer; + if (layer && DID_SUCCEED(parseLayer(layer, (byte *)params))) { + _layers.add(layer); + } else { + delete layer; + layer = NULL; + cmd = PARSERR_TOKENNOTFOUND; + } + } + break; + + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TTFONT definition"); + return STATUS_FAILED; + } + + // create at least one layer + if (_layers.size() == 0) { + BaseTTFontLayer *layer = new BaseTTFontLayer; + layer->_color = baseColor; + _layers.add(layer); + } + + if (!_fontFile) { + BaseUtils::setString(&_fontFile, "arial.ttf"); + } + + return initFont(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::parseLayer(BaseTTFontLayer *layer, byte *buffer) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(OFFSET_X) + TOKEN_TABLE(OFFSET_Y) + TOKEN_TABLE(COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE_END + + char *params; + int cmd; + BaseParser parser; + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_OFFSET_X: + parser.scanStr(params, "%d", &layer->_offsetX); + break; + + case TOKEN_OFFSET_Y: + parser.scanStr(params, "%d", &layer->_offsetY); + break; + + case TOKEN_COLOR: { + int r, g, b; + parser.scanStr(params, "%d,%d,%d", &r, &g, &b); + layer->_color = BYTETORGBA(r, g, b, RGBCOLGetA(layer->_color)); + } + break; + + case TOKEN_ALPHA: { + int a; + parser.scanStr(params, "%d", &a); + layer->_color = BYTETORGBA(RGBCOLGetR(layer->_color), RGBCOLGetG(layer->_color), RGBCOLGetB(layer->_color), a); + } + break; + } + } + if (cmd != PARSERR_EOF) { + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::persist(BasePersistenceManager *persistMgr) { + BaseFont::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_isBold)); + persistMgr->transfer(TMEMBER(_isItalic)); + persistMgr->transfer(TMEMBER(_isUnderline)); + persistMgr->transfer(TMEMBER(_isStriked)); + persistMgr->transfer(TMEMBER(_fontHeight)); + persistMgr->transfer(TMEMBER(_fontFile)); + + + // persist layers + int numLayers; + if (persistMgr->getIsSaving()) { + numLayers = _layers.size(); + persistMgr->transfer(TMEMBER(numLayers)); + for (int i = 0; i < numLayers; i++) { + _layers[i]->persist(persistMgr); + } + } else { + numLayers = _layers.size(); + persistMgr->transfer(TMEMBER(numLayers)); + for (int i = 0; i < numLayers; i++) { + BaseTTFontLayer *layer = new BaseTTFontLayer; + layer->persist(persistMgr); + _layers.add(layer); + } + } + + if (!persistMgr->getIsSaving()) { + for (int i = 0; i < NUM_CACHED_TEXTS; i++) { + _cachedTexts[i] = NULL; + } + _fallbackFont = _font = _deletableFont = NULL; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::afterLoad() { + initFont(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseFontTT::initFont() { + if (!_fontFile) { + return STATUS_FAILED; + } + + Common::SeekableReadStream *file = BaseFileManager::getEngineInstance()->openFile(_fontFile); + if (!file) { + //TODO: Try to fallback from Arial to FreeSans + /* + // the requested font file is not in wme file space; try loading a system font + AnsiString fontFileName = PathUtil::combine(BasePlatform::getSystemFontPath(), PathUtil::getFileName(_fontFile)); + file = BaseFileManager::getEngineInstance()->openFile(fontFileName.c_str(), false); + if (!file) { + _gameRef->LOG(0, "Error loading TrueType font '%s'", _fontFile); + //return STATUS_FAILED; + }*/ + } + + if (file) { +#ifdef USE_FREETYPE2 + _deletableFont = Graphics::loadTTFFont(*file, 96, _fontHeight); // Use the same dpi as WME (96 vs 72). + _font = _deletableFont; +#endif + } + if (!_font) { + _font = _fallbackFont = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); + warning("BaseFontTT::InitFont - Couldn't load font: %s", _fontFile); + } + _lineHeight = _font->getFontHeight(); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseFontTT::measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight) { + //TextLineList lines; + + if (maxWidth >= 0) { + Common::Array<Common::String> lines; + _font->wordWrapText(text, maxWidth, lines); + Common::Array<Common::String>::iterator it; + textWidth = 0; + for (it = lines.begin(); it != lines.end(); ++it) { + textWidth = MAX(textWidth, _font->getStringWidth(*it)); + } + + //WrapText(text, maxWidth, maxHeight, lines); + + textHeight = (int)(lines.size() * getLineHeight()); + } else { + textWidth = _font->getStringWidth(text); + textHeight = _fontHeight; + } + /* + TextLineList::iterator it; + for (it = lines.begin(); it != lines.end(); ++it) { + TextLine *line = (*it); + textWidth = MAX(textWidth, line->GetWidth()); + delete line; + line = NULL; + }*/ +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/font/base_font_truetype.h b/engines/wintermute/base/font/base_font_truetype.h new file mode 100644 index 0000000000..02dca7439f --- /dev/null +++ b/engines/wintermute/base/font/base_font_truetype.h @@ -0,0 +1,152 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_FONTTT_H +#define WINTERMUTE_BASE_FONTTT_H + +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "graphics/font.h" + +#define NUM_CACHED_TEXTS 30 + +namespace Wintermute { + +class BaseFontTT : public BaseFont { +private: + ////////////////////////////////////////////////////////////////////////// + class BaseCachedTTFontText { + public: + WideString _text; + int _width; + TTextAlign _align; + int _maxHeight; + int _maxLength; + BaseSurface *_surface; + int _priority; + int _textOffset; + bool _marked; + + BaseCachedTTFontText() { + //_text = L""; + _text = ""; + _width = _maxHeight = _maxLength = -1; + _align = TAL_LEFT; + _surface = NULL; + _priority = -1; + _textOffset = 0; + _marked = false; + } + + virtual ~BaseCachedTTFontText() { + if (_surface) { + delete _surface; + } + } + }; + +public: + ////////////////////////////////////////////////////////////////////////// + class BaseTTFontLayer { + public: + BaseTTFontLayer() { + _offsetX = _offsetY = 0; + _color = 0x00000000; + } + + bool persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_offsetX)); + persistMgr->transfer(TMEMBER(_offsetY)); + persistMgr->transfer(TMEMBER(_color)); + return STATUS_OK; + } + + int _offsetX; + int _offsetY; + uint32 _color; + }; + +public: + DECLARE_PERSISTENT(BaseFontTT, BaseFont) + BaseFontTT(BaseGame *inGame); + virtual ~BaseFontTT(void); + + virtual int getTextWidth(byte *text, int maxLength = -1); + virtual int getTextHeight(byte *text, int width); + virtual void drawText(const byte *text, int x, int y, int width, TTextAlign align = TAL_LEFT, int max_height = -1, int maxLength = -1); + virtual int getLetterHeight(); + + bool loadBuffer(byte *buffer); + bool loadFile(const Common::String &filename); + + float getLineHeight() const { + return _lineHeight; + } + + void afterLoad(); + void initLoop(); + +private: + bool parseLayer(BaseTTFontLayer *layer, byte *buffer); + + void measureText(const WideString &text, int maxWidth, int maxHeight, int &textWidth, int &textHeight); + + BaseSurface *renderTextToTexture(const WideString &text, int width, TTextAlign align, int maxHeight, int &textOffset); + + BaseCachedTTFontText *_cachedTexts[NUM_CACHED_TEXTS]; + + bool initFont(); + + Graphics::Font *_deletableFont; + const Graphics::Font *_font; + const Graphics::Font *_fallbackFont; + + float _lineHeight; + + size_t _maxCharWidth; + size_t _maxCharHeight; + +public: + bool _isBold; + bool _isItalic; + bool _isUnderline; + bool _isStriked; + int _fontHeight; + char *_fontFile; + + BaseArray<BaseTTFontLayer *> _layers; + void clearCache(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_image.cpp b/engines/wintermute/base/gfx/base_image.cpp new file mode 100644 index 0000000000..4b15d563ed --- /dev/null +++ b/engines/wintermute/base/gfx/base_image.cpp @@ -0,0 +1,231 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "graphics/decoders/png.h" +#include "graphics/decoders/jpeg.h" +#include "graphics/decoders/bmp.h" +#include "graphics/decoders/tga.h" +#include "graphics/surface.h" +#include "common/textconsole.h" +#include "common/stream.h" +#include "common/system.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseImage::BaseImage() { + _fileManager = BaseFileManager::getEngineInstance(); + _palette = NULL; + _surface = NULL; + _decoder = NULL; + _deletableSurface = NULL; +} + + +////////////////////////////////////////////////////////////////////// +BaseImage::~BaseImage() { + delete _decoder; + if (_deletableSurface) { + _deletableSurface->free(); + } + delete _deletableSurface; +} + +bool BaseImage::loadFile(const Common::String &filename) { + _filename = filename; + _filename.toLowercase(); + if (filename.hasPrefix("savegame:")) { + _decoder = new Graphics::BitmapDecoder(); + } else if (_filename.hasSuffix(".png")) { + _decoder = new Graphics::PNGDecoder(); + } else if (_filename.hasSuffix(".bmp")) { + _decoder = new Graphics::BitmapDecoder(); + } else if (_filename.hasSuffix(".tga")) { + _decoder = new Graphics::TGADecoder(); + } else if (_filename.hasSuffix(".jpg")) { + _decoder = new Graphics::JPEGDecoder(); + } else { + error("BaseImage::loadFile : Unsupported fileformat %s", filename.c_str()); + } + _filename = filename; + Common::SeekableReadStream *file = _fileManager->openFile(filename.c_str()); + if (!file) { + return false; + } + + _decoder->loadStream(*file); + _surface = _decoder->getSurface(); + _palette = _decoder->getPalette(); + _fileManager->closeFile(file); + + return true; +} + +byte BaseImage::getAlphaAt(int x, int y) const { + if (!_surface) { + return 0xFF; + } + uint32 color = *(const uint32 *)_surface->getBasePtr(x, y); + byte r, g, b, a; + _surface->format.colorToARGB(color, a, r, g, b); + return a; +} + +void BaseImage::copyFrom(const Graphics::Surface *surface) { + _surface = _deletableSurface = new Graphics::Surface(); + _deletableSurface->copyFrom(*surface); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::saveBMPFile(const char *filename) const { + warning("BaseImage::saveBMPFile - stubbed"); // TODO + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::resize(int newWidth, int newHeight) { + // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here. + TransparentSurface temp(*_surface, true); + if (_deletableSurface) { + _deletableSurface->free(); + delete _deletableSurface; + _deletableSurface = NULL; + } + _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight); + temp.free(); + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::writeBMPToStream(Common::WriteStream *stream) const { + if (!_surface) { + return false; + } + + /* The following is just copied over and inverted to write-ops from the BMP-decoder */ + stream->writeByte('B'); + stream->writeByte('M'); + + /* Since we don't care during reads, we don't care during writes: */ + /* uint32 fileSize = */ + stream->writeUint32LE(54 + _surface->h * _surface->pitch); + /* uint16 res1 = */ + stream->writeUint16LE(0); + /* uint16 res2 = */ + stream->writeUint16LE(0); + const uint32 imageOffset = 54; + stream->writeUint32LE(imageOffset); + + const uint32 infoSize = 40; /* Windows v3 BMP */ + stream->writeUint32LE(infoSize); + + uint32 width = _surface->w; + int32 height = _surface->h; + stream->writeUint32LE(width); + stream->writeUint32LE((uint32)height); + + if (width == 0 || height == 0) { + return false; + } + + if (height < 0) { + warning("Right-side up bitmaps not supported"); + return false; + } + + /* uint16 planes = */ stream->writeUint16LE(1); + const uint16 bitsPerPixel = 24; + stream->writeUint16LE(bitsPerPixel); + + const uint32 compression = 0; + stream->writeUint32LE(compression); + + /* uint32 imageSize = */ + stream->writeUint32LE(_surface->h * _surface->pitch); + /* uint32 pixelsPerMeterX = */ + stream->writeUint32LE(0); + /* uint32 pixelsPerMeterY = */ + stream->writeUint32LE(0); + const uint32 paletteColorCount = 0; + stream->writeUint32LE(paletteColorCount); + /* uint32 colorsImportant = */ + stream->writeUint32LE(0); + + // Start us at the beginning of the image (54 bytes in) + Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + + // BGRA for 24bpp + if (bitsPerPixel == 24) { + format = Graphics::PixelFormat(4, 8, 8, 8, 8, 8, 16, 24, 0); + } + + Graphics::Surface *surface = _surface->convertTo(format); + + int srcPitch = width * (bitsPerPixel >> 3); + const int extraDataLength = (srcPitch % 4) ? 4 - (srcPitch % 4) : 0; + + for (int32 i = height - 1; i >= 0; i--) { + for (uint32 j = 0; j < width; j++) { + byte b, g, r; + uint32 color = *(uint32 *)surface->getBasePtr(j, i); + surface->format.colorToRGB(color, r, g, b); + stream->writeByte(b); + stream->writeByte(g); + stream->writeByte(r); + } + + for (int k = 0; k < extraDataLength; k++) { + stream->writeByte(0); + } + } + surface->free(); + delete surface; + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseImage::copyFrom(BaseImage *origImage, int newWidth, int newHeight) { + // WME Lite used FILTER_BILINEAR with FreeImage_Rescale here. + + TransparentSurface temp(*origImage->_surface, false); + if (_deletableSurface) { + _deletableSurface->free(); + delete _deletableSurface; + _deletableSurface = NULL; + } + _surface = _deletableSurface = temp.scale((uint16)newWidth, (uint16)newHeight); + return true; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_image.h b/engines/wintermute/base/gfx/base_image.h new file mode 100644 index 0000000000..6d01b84184 --- /dev/null +++ b/engines/wintermute/base/gfx/base_image.h @@ -0,0 +1,72 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_IMAGE_H +#define WINTERMUTE_BASE_IMAGE_H + +#include "graphics/surface.h" +#include "graphics/pixelformat.h" +#include "graphics/decoders/image_decoder.h" +#include "common/endian.h" +#include "common/str.h" +#include "common/stream.h" + +namespace Wintermute { +class BaseSurface; +class BaseFileManager; +class BaseImage { + +public: + BaseImage(); + ~BaseImage(); + + bool loadFile(const Common::String &filename); + const Graphics::Surface *getSurface() const { + return _surface; + }; + const byte *getPalette() const { + return _palette; + } + byte getAlphaAt(int x, int y) const; + bool writeBMPToStream(Common::WriteStream *stream) const; + bool resize(int newWidth, int newHeight); + bool saveBMPFile(const char *filename) const; + bool copyFrom(BaseImage *origImage, int newWidth = 0, int newHeight = 0); + void copyFrom(const Graphics::Surface *surface); +private: + Common::String _filename; + Graphics::ImageDecoder *_decoder; + const Graphics::Surface *_surface; + Graphics::Surface *_deletableSurface; + const byte *_palette; + BaseFileManager *_fileManager; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_renderer.cpp b/engines/wintermute/base/gfx/base_renderer.cpp new file mode 100644 index 0000000000..e7ffc14c25 --- /dev/null +++ b/engines/wintermute/base/gfx/base_renderer.cpp @@ -0,0 +1,381 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseRenderer::BaseRenderer(BaseGame *inGame) : BaseClass(inGame) { + _window = 0; + _clipperWindow = 0; + _active = false; + _ready = false; + _windowed = true; + _forceAlphaColor = 0x00; + + _indicatorDisplay = false; + _indicatorColor = BYTETORGBA(255, 0, 0, 128); + _indicatorProgress = 0; + _indicatorX = -1; + _indicatorY = -1; + _indicatorWidth = -1; + _indicatorHeight = 8; + _indicatorWidthDrawn = 0; + + _loadImageName = ""; + _saveImageName = ""; + _saveLoadImage = NULL; + _loadInProgress = false; + _hasDrawnSaveLoadImage = false; + + _saveImageX = _saveImageY = 0; + _loadImageX = _loadImageY = 0; + + _width = _height = _bPP = 0; + BasePlatform::setRectEmpty(&_monitorRect); + + _realWidth = _realHeight = 0; + _drawOffsetX = _drawOffsetY = 0; +} + + +////////////////////////////////////////////////////////////////////// +BaseRenderer::~BaseRenderer() { + deleteRectList(); + unclipCursor(); + delete _saveLoadImage; +} + + +////////////////////////////////////////////////////////////////////// +void BaseRenderer::initLoop() { + deleteRectList(); +} + +void BaseRenderer::initIndicator() { + if (_indicatorY == -1) { + _indicatorY = _height - _indicatorHeight; + } + if (_indicatorX == -1) { + _indicatorX = 0; + } + if (_indicatorWidth == -1) { + _indicatorWidth = _width; + } +} + +void BaseRenderer::setIndicator(int width, int height, int x, int y, uint32 color) { + _indicatorWidth = width; + _indicatorHeight = height; + _indicatorX = x; + _indicatorY = y; + _indicatorColor = color; +} + +void BaseRenderer::setIndicatorVal(int value) { + bool redisplay = (_indicatorProgress != value); + _indicatorProgress = value; + if (redisplay) + displayIndicator(); +} + +void BaseRenderer::setLoadingScreen(const char *filename, int x, int y) { + // TODO: Handle NULL + _loadImageName = filename; + _loadImageX = x; + _loadImageY = y; +} + +void BaseRenderer::setSaveImage(const char *filename, int x, int y) { + // TODO: Handle NULL + _saveImageName = filename; + _saveImageX = x; + _saveImageY = y; +} + +void BaseRenderer::initSaveLoad(bool isSaving, bool quickSave) { + _indicatorDisplay = true; + _indicatorProgress = 0; + _hasDrawnSaveLoadImage = false; + + if (isSaving && !quickSave) { + delete _saveLoadImage; + _saveLoadImage = NULL; + if (_saveImageName.size()) { + _saveLoadImage = createSurface(); + + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_saveImageName, true, 0, 0, 0))) { + delete _saveLoadImage; + _saveLoadImage = NULL; + } + } + } else { + delete _saveLoadImage; + _saveLoadImage = NULL; + if (_loadImageName.size()) { + _saveLoadImage = createSurface(); + + if (!_saveLoadImage || DID_FAIL(_saveLoadImage->create(_loadImageName, true, 0, 0, 0))) { + delete _saveLoadImage; + _saveLoadImage = NULL; + } + } + _loadInProgress = true; + } +} + +void BaseRenderer::endSaveLoad() { + _loadInProgress = false; + _indicatorDisplay = false; + _indicatorWidthDrawn = 0; + + delete _saveLoadImage; + _saveLoadImage = NULL; +} + +void BaseRenderer::persistSaveLoadImages(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_loadImageName)); + persistMgr->transfer(TMEMBER(_saveImageName)); + persistMgr->transfer(TMEMBER(_saveImageX)); + persistMgr->transfer(TMEMBER(_saveImageY)); + persistMgr->transfer(TMEMBER(_loadImageX)); + persistMgr->transfer(TMEMBER(_loadImageY)); +} + +////////////////////////////////////////////////////////////////////// +BaseObject *BaseRenderer::getObjectAt(int x, int y) { + Point32 point; + point.x = x; + point.y = y; + + for (int i = _rectList.size() - 1; i >= 0; i--) { + if (BasePlatform::ptInRect(&_rectList[i]->_rect, point)) { + if (_rectList[i]->_precise) { + // frame + if (_rectList[i]->_frame) { + int xx = (int)((_rectList[i]->_frame->getRect().left + x - _rectList[i]->_rect.left + _rectList[i]->_offsetX) / (float)((float)_rectList[i]->_zoomX / (float)100)); + int yy = (int)((_rectList[i]->_frame->getRect().top + y - _rectList[i]->_rect.top + _rectList[i]->_offsetY) / (float)((float)_rectList[i]->_zoomY / (float)100)); + + if (_rectList[i]->_frame->_mirrorX) { + int width = _rectList[i]->_frame->getRect().right - _rectList[i]->_frame->getRect().left; + xx = width - xx; + } + + if (_rectList[i]->_frame->_mirrorY) { + int height = _rectList[i]->_frame->getRect().bottom - _rectList[i]->_frame->getRect().top; + yy = height - yy; + } + + if (!_rectList[i]->_frame->_surface->isTransparentAt(xx, yy)) { + return _rectList[i]->_owner; + } + } + // region + else if (_rectList[i]->_region) { + if (_rectList[i]->_region->pointInRegion(x + _rectList[i]->_offsetX, y + _rectList[i]->_offsetY)) { + return _rectList[i]->_owner; + } + } + } else { + return _rectList[i]->_owner; + } + } + } + + return (BaseObject *)NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderer::deleteRectList() { + for (uint32 i = 0; i < _rectList.size(); i++) { + delete _rectList[i]; + } + _rectList.clear(); +} + +////////////////////////////////////////////////////////////////////// +bool BaseRenderer::initRenderer(int width, int height, bool windowed) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////// +void BaseRenderer::onWindowChange() { +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::windowedBlt() { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setup2D(bool Force) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setupLines() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::drawLine(int x1, int y1, int x2, int y2, uint32 color) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::drawRect(int x1, int y1, int x2, int y2, uint32 color, int width) { + for (int i = 0; i < width; i++) { + drawLine(x1 + i, y1 + i, x2 - i, y1 + i, color); // up + drawLine(x1 + i, y2 - i, x2 - i + 1, y2 - i, color); // down + + drawLine(x1 + i, y1 + i, x1 + i, y2 - i, color); // left + drawLine(x2 - i, y1 + i, x2 - i, y2 - i + 1, color); // right + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setViewport(int left, int top, int right, int bottom) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setScreenViewport() { + return setViewport(_drawOffsetX, _drawOffsetY, _width + _drawOffsetX, _height + _drawOffsetY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::setViewport(Rect32 *rect) { + return setViewport(rect->left + _drawOffsetX, + rect->top + _drawOffsetY, + rect->right + _drawOffsetX, + rect->bottom + _drawOffsetY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::clipCursor() { + // TODO: Reimplement this. (Currently aspect-indpendence isn't quite finished) + /* + if (!_windowed) { + Rect32 rc; + GetWindowRect(_window, &rc); + + // if "maintain aspect ratio" is in effect, lock mouse to visible area + rc.left = _drawOffsetX; + rc.top = _drawOffsetY; + rc.right = rc.left + _width; + rc.bottom = rc.top + _height; + + ::ClipCursor(&rc); + } + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::unclipCursor() { + /* + if (!_windowed) ::ClipCursor(NULL); + */ + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::pointInViewport(Point32 *p) { + if (p->x < _drawOffsetX) { + return false; + } + if (p->y < _drawOffsetY) { + return false; + } + if (p->x > _drawOffsetX + _width) { + return false; + } + if (p->y > _drawOffsetY + _height) { + return false; + } + + return true; +} + +void BaseRenderer::addRectToList(BaseActiveRect *rect) { + _rectList.push_back(rect); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderer::displayIndicator() { + if (!_indicatorDisplay || !_indicatorProgress) { + return STATUS_OK; + } + if (_saveLoadImage && !_hasDrawnSaveLoadImage) { + Rect32 rc; + BasePlatform::setRect(&rc, 0, 0, _saveLoadImage->getWidth(), _saveLoadImage->getHeight()); + if (_loadInProgress) { + _saveLoadImage->displayTrans(_loadImageX, _loadImageY, rc); + } else { + _saveLoadImage->displayTrans(_saveImageX, _saveImageY, rc); + } + flip(); + _hasDrawnSaveLoadImage = true; + } + + if ((!_indicatorDisplay && _indicatorWidth <= 0) || _indicatorHeight <= 0) { + return STATUS_OK; + } + setupLines(); + int curWidth = (int)(_indicatorWidth * (float)((float)_indicatorProgress / 100.0f)); + for (int i = 0; i < _indicatorHeight; i++) { + drawLine(_indicatorX, _indicatorY + i, _indicatorX + curWidth, _indicatorY + i, _indicatorColor); + } + + setup2D(); + _indicatorWidthDrawn = curWidth; + if (_indicatorWidthDrawn) { + indicatorFlip(); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_renderer.h b/engines/wintermute/base/gfx/base_renderer.h new file mode 100644 index 0000000000..0475824464 --- /dev/null +++ b/engines/wintermute/base/gfx/base_renderer.h @@ -0,0 +1,221 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RENDERER_H +#define WINTERMUTE_BASE_RENDERER_H + +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/base/base.h" +#include "common/rect.h" +#include "common/array.h" + +namespace Wintermute { + +class BaseImage; +class BaseActiveRect; +class BaseObject; +class BaseSurface; +class BasePersistenceManager; + +/** + * @class BaseRenderer a common interface for the rendering portion of WME + * this interface is mainly intended to wrap away any differencies between + * software-rendering/hardware-rendering. + */ +class BaseRenderer: public BaseClass { +public: + int _realWidth; + int _realHeight; + int _drawOffsetX; + int _drawOffsetY; + + virtual void dumpData(const char *filename) {} + /** + * Take a screenshot of the current screenstate + * + * @return a BaseImage containing the current screen-buffer. + */ + virtual BaseImage *takeScreenshot() = 0; + virtual bool setViewport(int left, int top, int right, int bottom); + virtual bool setViewport(Rect32 *rect); + virtual Rect32 getViewPort() = 0; + virtual bool setScreenViewport(); + + virtual Graphics::PixelFormat getPixelFormat() const = 0; + /** + * Fade the screen to black + * + * @param alpha amount to fade by (alpha value of black) + * @return + */ + virtual void fade(uint16 alpha) = 0; + /** + * Fade a portion of the screen to a specific color + * + * @param r the red component to fade too. + * @param g the green component to fade too. + * @param b the blue component to fade too. + * @param a the alpha component to fade too. + * @param rect the portion of the screen to fade (if NULL, the entire screen will be faded). + */ + virtual void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL) = 0; + virtual bool drawLine(int x1, int y1, int x2, int y2, uint32 color); + virtual bool drawRect(int x1, int y1, int x2, int y2, uint32 color, int width = 1); + BaseRenderer(BaseGame *inGame = NULL); + virtual ~BaseRenderer(); + virtual bool setProjection() { + return STATUS_OK; + }; + + virtual bool windowedBlt(); + /** + * Fill a portion of the screen with a specified color + * + * @param r the red component to fill with. + * @param g the green component to fill with. + * @param b the blue component to fill with. + */ + virtual bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL) = 0; + virtual void onWindowChange(); + virtual bool initRenderer(int width, int height, bool windowed); + /** + * Flip the backbuffer onto the screen-buffer + * The screen will NOT be updated before calling this function. + * + * @return true if successfull, false on error. + */ + virtual bool flip() = 0; + /** + * Special flip for the indicator drawn during save/load + * essentially, just copies the region defined by the _indicator-variables. + */ + virtual bool indicatorFlip() = 0; + virtual void initLoop(); + virtual bool setup2D(bool force = false); + virtual bool setupLines(); + + /** + * Get the name of the current renderer + * + * @return the name of the renderer. + */ + virtual Common::String getName() const = 0; + virtual bool displayDebugInfo() { + return STATUS_FAILED; + }; + virtual bool drawShaderQuad() { + return STATUS_FAILED; + } + + virtual float getScaleRatioX() const { + return 1.0f; + } + virtual float getScaleRatioY() const { + return 1.0f; + } + + /** + * Create a Surface fit for use with the renderer. + * As diverse implementations of BaseRenderer might have different solutions for storing surfaces + * this allows for a common interface for creating surface-handles. (Mostly usefull to ease future + * implementation of hw-accelerated rendering, or readding 3D-support at some point). + * + * @return a surface that can be used with this renderer + */ + virtual BaseSurface *createSurface() = 0; + + bool clipCursor(); + bool unclipCursor(); + + BaseObject *getObjectAt(int x, int y); + void deleteRectList(); + + virtual bool startSpriteBatch() { + return STATUS_OK; + }; + virtual bool endSpriteBatch() { + return STATUS_OK; + }; + bool pointInViewport(Point32 *P); + bool _active; + bool _ready; + bool _windowed; + int _bPP; + int _height; + int _width; + uint32 _window; + uint32 _forceAlphaColor; + + void addRectToList(BaseActiveRect *rect); + + // Indicator & Save/Load-related functions + void initIndicator(); + void setIndicatorVal(int value); + void setIndicator(int width, int height, int x, int y, uint32 color); + void persistSaveLoadImages(BasePersistenceManager *persistMgr); + void initSaveLoad(bool isSaving, bool quickSave = false); + void endSaveLoad(); + void setLoadingScreen(const char *filename, int x, int y); + void setSaveImage(const char *filename, int x, int y); + + bool displayIndicator(); +protected: + Common::String _loadImageName; + Common::String _saveImageName; + int _saveImageX; + int _saveImageY; + int _loadImageX; + int _loadImageY; + BaseSurface *_saveLoadImage; + bool _hasDrawnSaveLoadImage; + + int _indicatorWidthDrawn; + uint32 _indicatorColor; + int _indicatorX; + int _indicatorY; + int _indicatorWidth; + int _indicatorHeight; + bool _loadInProgress; + bool _indicatorDisplay; + int _indicatorProgress; +protected: + uint32 _clipperWindow; + + Rect32 _windowRect; + Rect32 _viewportRect; + Rect32 _screenRect; + Rect32 _monitorRect; +private: + Common::Array<BaseActiveRect *> _rectList; +}; + +BaseRenderer *makeOSystemRenderer(BaseGame *inGame); // Implemented in BRenderSDL.cpp + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/base_surface.cpp b/engines/wintermute/base/gfx/base_surface.cpp new file mode 100644 index 0000000000..d882adf5ad --- /dev/null +++ b/engines/wintermute/base/gfx/base_surface.cpp @@ -0,0 +1,149 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_surface.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +BaseSurface::BaseSurface(BaseGame *inGame) : BaseClass(inGame) { + _referenceCount = 0; + + _width = _height = 0; + + _filename = ""; + + _pixelOpReady = false; + + _ckDefault = true; + _ckRed = _ckGreen = _ckBlue = 0; + _lifeTime = 0; + _keepLoaded = false; + + _lastUsedTime = 0; + _valid = false; +} + + +////////////////////////////////////////////////////////////////////// +BaseSurface::~BaseSurface() { + if (_pixelOpReady) { + endPixelOp(); + } +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::restore() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::isTransparentAt(int x, int y) { + return false; +} + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::displayHalfTrans(int x, int y, Rect32 rect) { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return displayTransZoom(x, y, rect, zoomX, zoomY, alpha, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::create(int width, int height) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::startPixelOp() { + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::endPixelOp() { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::putPixel(int x, int y, byte r, byte g, byte b, int a) { + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::comparePixel(int x, int y, byte r, byte g, byte b, int a) { + return false; +} + + +////////////////////////////////////////////////////////////////////// +bool BaseSurface::isTransparentAtLite(int x, int y) { + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::invalidate() { + return STATUS_FAILED; +} + + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurface::prepareToDraw() { + _lastUsedTime = _gameRef->_liveTimer; + + if (!_valid) { + //_gameRef->LOG(0, "Reviving: %s", _filename); + return create(_filename.c_str(), _ckDefault, _ckRed, _ckGreen, _ckBlue, _lifeTime, _keepLoaded); + } else { + return STATUS_OK; + } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSurface::setSize(int width, int height) { + _width = width; + _height = height; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/base_surface.h b/engines/wintermute/base/gfx/base_surface.h new file mode 100644 index 0000000000..ee53c03e77 --- /dev/null +++ b/engines/wintermute/base/gfx/base_surface.h @@ -0,0 +1,99 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACE_H +#define WINTERMUTE_BASE_SURFACE_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/math/rect32.h" +#include "graphics/surface.h" + +namespace Wintermute { + +class BaseSurface: public BaseClass { +public: + virtual bool invalidate(); + virtual bool prepareToDraw(); + uint32 _lastUsedTime; + bool _valid; + int _lifeTime; + + bool _pixelOpReady; + BaseSurface(BaseGame *inGame); + virtual ~BaseSurface(); + + virtual bool displayHalfTrans(int x, int y, Rect32 rect); + virtual bool isTransparentAt(int x, int y); + virtual bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0) = 0; + virtual bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false) = 0; + virtual bool restore(); + virtual bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false) = 0; + virtual bool create(int width, int height); + virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false) { + return STATUS_FAILED; + } + virtual bool putPixel(int x, int y, byte r, byte g, byte b, int a = -1); + virtual bool getPixel(int x, int y, byte *r, byte *g, byte *b, byte *a = NULL); + virtual bool comparePixel(int x, int y, byte r, byte g, byte b, int a = -1); + virtual bool startPixelOp(); + virtual bool endPixelOp(); + virtual bool isTransparentAtLite(int x, int y); + void setSize(int width, int height); + + int _referenceCount; + + virtual int getWidth() { + return _width; + } + virtual int getHeight() { + return _height; + } + Common::String getFileNameStr() { return _filename; } + const char* getFileName() { return _filename.c_str(); } + //void SetWidth(int Width){ _width = Width; } + //void SetHeight(int Height){ _height = Height; } +protected: + bool _ckDefault; + byte _ckRed; + byte _ckGreen; + byte _ckBlue; + + bool _keepLoaded; + Common::String _filename; + int _height; + int _width; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp new file mode 100644 index 0000000000..7970a25300 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.cpp @@ -0,0 +1,609 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/base_surface_storage.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/math/math_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "common/system.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "common/queue.h" +#include "common/config-manager.h" + +namespace Wintermute { + +RenderTicket::RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) : _owner(owner), + _srcRect(*srcRect), _dstRect(*dstRect), _drawNum(0), _isValid(true), _wantsDraw(true), _hasAlpha(!disableAlpha) { + _colorMod = 0; + _mirror = TransparentSurface::FLIP_NONE; + if (mirrorX) { + _mirror |= TransparentSurface::FLIP_V; + } + if (mirrorY) { + _mirror |= TransparentSurface::FLIP_H; + } + if (surf) { + _surface = new Graphics::Surface(); + _surface->create((uint16)srcRect->width(), (uint16)srcRect->height(), surf->format); + assert(_surface->format.bytesPerPixel == 4); + // Get a clipped copy of the surface + for (int i = 0; i < _surface->h; i++) { + memcpy(_surface->getBasePtr(0, i), surf->getBasePtr(srcRect->left, srcRect->top + i), srcRect->width() * _surface->format.bytesPerPixel); + } + // Then scale it if necessary + if (dstRect->width() != srcRect->width() || dstRect->height() != srcRect->height()) { + TransparentSurface src(*_surface, false); + Graphics::Surface *temp = src.scale(dstRect->width(), dstRect->height()); + _surface->free(); + delete _surface; + _surface = temp; + } + } else { + _surface = NULL; + } +} + +RenderTicket::~RenderTicket() { + if (_surface) { + _surface->free(); + delete _surface; + } +} + +bool RenderTicket::operator==(RenderTicket &t) { + if ((t._srcRect != _srcRect) || + (t._dstRect != _dstRect) || + (t._mirror != _mirror) || + (t._owner != _owner) || + (t._hasAlpha != _hasAlpha) || + (t._colorMod != _colorMod)) { + return false; + } + return true; +} + +BaseRenderer *makeOSystemRenderer(BaseGame *inGame) { + return new BaseRenderOSystem(inGame); +} + +////////////////////////////////////////////////////////////////////////// +BaseRenderOSystem::BaseRenderOSystem(BaseGame *inGame) : BaseRenderer(inGame) { + _renderSurface = new Graphics::Surface(); + _blankSurface = new Graphics::Surface(); + _drawNum = 1; + _needsFlip = true; + + _borderLeft = _borderRight = _borderTop = _borderBottom = 0; + _ratioX = _ratioY = 1.0f; + setAlphaMod(255); + setColorMod(255, 255, 255); + _dirtyRect = NULL; +} + +////////////////////////////////////////////////////////////////////////// +BaseRenderOSystem::~BaseRenderOSystem() { + _renderSurface->free(); + delete _renderSurface; + _blankSurface->free(); + delete _blankSurface; + TransparentSurface::destroyLookup(); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::initRenderer(int width, int height, bool windowed) { + _width = width; + _height = height; + _renderRect.setWidth(_width); + _renderRect.setHeight(_height); + + _realWidth = width; + _realHeight = height; + + //TODO: Tiny resolution-displays might want to do some resolution-selection logic here + + //_realWidth = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResWidth", _width); + //_realHeight = BaseEngine::instance().getRegistry()->readInt("Debug", "ForceResHeight", _height); + + float origAspect = (float)_width / (float)_height; + float realAspect = (float)_realWidth / (float)_realHeight; + + float ratio; + if (origAspect < realAspect) { + // normal to wide + ratio = (float)_realHeight / (float)_height; + } else { + // wide to normal + ratio = (float)_realWidth / (float)_width; + } + + _borderLeft = (int)((_realWidth - (_width * ratio)) / 2); + _borderRight = (int)(_realWidth - (_width * ratio) - _borderLeft); + + _borderTop = (int)((_realHeight - (_height * ratio)) / 2); + _borderBottom = (int)(_realHeight - (_height * ratio) - _borderTop); + + + + _ratioX = (float)(_realWidth - _borderLeft - _borderRight) / (float)_width; + _ratioY = (float)(_realHeight - _borderTop - _borderBottom) / (float)_height; + + _windowed = !ConfMan.getBool("fullscreen"); + + Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + g_system->beginGFXTransaction(); + g_system->initSize(_width, _height, &format); + OSystem::TransactionError gfxError = g_system->endGFXTransaction(); + + if (gfxError != OSystem::kTransactionSuccess) { + warning("Couldn't setup GFX-backend for %dx%dx%d", _width, _height, format.bytesPerPixel * 8); + return STATUS_FAILED; + } + + g_system->showMouse(false); + + _renderSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat()); + _blankSurface->create(g_system->getWidth(), g_system->getHeight(), g_system->getScreenFormat()); + _blankSurface->fillRect(Common::Rect(0, 0, _blankSurface->h, _blankSurface->w), _blankSurface->format.ARGBToColor(255, 0, 0, 0)); + _active = true; + + _clearColor = _renderSurface->format.ARGBToColor(255, 0, 0, 0); + + return STATUS_OK; +} + +void BaseRenderOSystem::setAlphaMod(byte alpha) { + byte r = RGBCOLGetR(_colorMod); + byte g = RGBCOLGetB(_colorMod); + byte b = RGBCOLGetB(_colorMod); + _colorMod = BS_ARGB(alpha, r, g, b); +} + +void BaseRenderOSystem::setColorMod(byte r, byte g, byte b) { + byte alpha = RGBCOLGetA(_colorMod); + _colorMod = BS_ARGB(alpha, r, g, b); +} + +bool BaseRenderOSystem::indicatorFlip() { + g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_indicatorX, _indicatorY), _renderSurface->pitch, _indicatorX, _indicatorY, _indicatorWidthDrawn, _indicatorHeight); + g_system->updateScreen(); + return STATUS_OK; +} + +bool BaseRenderOSystem::flip() { + if (!_disableDirtyRects) { + drawTickets(); + } else { + // Clear the scale-buffered tickets that wasn't reused. + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + if ((*it)->_wantsDraw == false) { + RenderTicket *ticket = *it; + it = _renderQueue.erase(it); + delete ticket; + } else { + (*it)->_wantsDraw = false; + ++it; + } + } + } + if (_needsFlip || _disableDirtyRects) { + if (_disableDirtyRects) { + g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, 0, 0, _renderSurface->w, _renderSurface->h); + } + // g_system->copyRectToScreen((byte *)_renderSurface->pixels, _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height()); + delete _dirtyRect; + _dirtyRect = NULL; + g_system->updateScreen(); + _needsFlip = false; + } + _drawNum = 1; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::fill(byte r, byte g, byte b, Common::Rect *rect) { + _clearColor = _renderSurface->format.ARGBToColor(0xFF, r, g, b); + if (!_disableDirtyRects) { + return STATUS_OK; + } + if (!rect) { +// TODO: This should speed things up, but for some reason it misses the size by quite a bit. +/* if (r == 0 && g == 0 && b == 0) { + // Simply memcpy from the buffered black-surface, way faster than Surface::fillRect. + memcpy(_renderSurface->pixels, _blankSurface->pixels, _renderSurface->pitch * _renderSurface->h); + return STATUS_OK; + }*/ + rect = &_renderRect; + } + // TODO: This doesn't work with dirty rects + _renderSurface->fillRect(*rect, _clearColor); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::fade(uint16 alpha) { + byte dwAlpha = (byte)(255 - alpha); + return fadeToColor(0, 0, 0, dwAlpha); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect) { + // This particular warning is rather messy, as this function is called a ton, + // thus we avoid printing it more than once. + + // TODO: Add fading with dirty rects. + if (!_disableDirtyRects) { + warning("BaseRenderOSystem::FadeToColor - Breaks when using dirty rects"); + } + + Common::Rect fillRect; + + if (rect) { + fillRect.left = rect->left; + fillRect.top = rect->top; + fillRect.setWidth(rect->width()); + fillRect.setHeight(rect->height()); + } else { + Rect32 rc; + _gameRef->getCurrentViewportRect(&rc); + fillRect.left = (int16)rc.left; + fillRect.top = (int16)rc.top; + fillRect.setWidth((int16)(rc.right - rc.left)); + fillRect.setHeight((int16)(rc.bottom - rc.top)); + } + modTargetRect(&fillRect); + + //TODO: This is only here until I'm sure about the final pixelformat + uint32 col = _renderSurface->format.ARGBToColor(a, r, g, b); + + setAlphaMod(255); + setColorMod(255, 255, 255); + Graphics::Surface surf; + surf.create((uint16)fillRect.width(), (uint16)fillRect.height(), _renderSurface->format); + Common::Rect sizeRect(fillRect); + sizeRect.translate(-fillRect.top, -fillRect.left); + surf.fillRect(fillRect, col); + drawSurface(NULL, &surf, &sizeRect, &fillRect, false, false); + surf.free(); + + //SDL_SetRenderDrawColor(_renderer, r, g, b, a); + //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + //SDL_RenderFillRect(_renderer, &fillRect); +} + +Graphics::PixelFormat BaseRenderOSystem::getPixelFormat() const { + return _renderSurface->format; +} + +void BaseRenderOSystem::drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha) { + // Skip rects that are completely outside the screen: + if ((dstRect->left < 0 && dstRect->right < 0) || (dstRect->top < 0 && dstRect->bottom < 0)) { + return; + } + + if (owner) { // Fade-tickets are owner-less + RenderTicket compare(owner, NULL, srcRect, dstRect, mirrorX, mirrorY, disableAlpha); + compare._colorMod = _colorMod; + RenderQueueIterator it; + for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) { + if ((*it)->_owner == owner && *(*it) == compare && (*it)->_isValid) { + (*it)->_colorMod = _colorMod; + if (_disableDirtyRects) { + drawFromSurface(*it, NULL); + } else { + drawFromTicket(*it); + } + return; + } + } + } + RenderTicket *ticket = new RenderTicket(owner, surf, srcRect, dstRect, mirrorX, mirrorY, disableAlpha); + ticket->_colorMod = _colorMod; + if (!_disableDirtyRects) { + drawFromTicket(ticket); + } else { + ticket->_wantsDraw = true; + _renderQueue.push_back(ticket); + drawFromSurface(ticket, NULL); + } +} + +void BaseRenderOSystem::invalidateTicket(RenderTicket *renderTicket) { + addDirtyRect(renderTicket->_dstRect); + renderTicket->_isValid = false; +// renderTicket->_canDelete = true; // TODO: Maybe readd this, to avoid even more duplicates. +} + +void BaseRenderOSystem::invalidateTicketsFromSurface(BaseSurfaceOSystem *surf) { + RenderQueueIterator it; + for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) { + if ((*it)->_owner == surf) { + invalidateTicket(*it); + } + } +} + +void BaseRenderOSystem::drawFromTicket(RenderTicket *renderTicket) { + renderTicket->_wantsDraw = true; + // A new item always has _drawNum == 0 + if (renderTicket->_drawNum == 0) { + // In-order + if (_renderQueue.empty() || _drawNum > (_renderQueue.back())->_drawNum) { + renderTicket->_drawNum = _drawNum++; + _renderQueue.push_back(renderTicket); + addDirtyRect(renderTicket->_dstRect); + } else { + // Before something + Common::List<RenderTicket *>::iterator pos; + for (pos = _renderQueue.begin(); pos != _renderQueue.end(); pos++) { + if ((*pos)->_drawNum >= _drawNum) { + break; + } + } + _renderQueue.insert(pos, renderTicket); + Common::List<RenderTicket *>::iterator it; + renderTicket->_drawNum = _drawNum++; + // Increment the following tickets, so they still are in line + for (it = pos; it != _renderQueue.end(); ++it) { + (*it)->_drawNum++; + (*it)->_wantsDraw = false; + } + addDirtyRect(renderTicket->_dstRect); + } + } else { + // Was drawn last round, still in the same order + if (_drawNum == renderTicket->_drawNum) { + _drawNum++; + } else { + // Remove the ticket from the list + RenderQueueIterator it = _renderQueue.begin(); + while (it != _renderQueue.end()) { + if ((*it) == renderTicket) { + it = _renderQueue.erase(it); + break; + } else { + ++it; + } + } + if (it != _renderQueue.end()) { + // Decreement the following tickets. + for (; it != _renderQueue.end(); ++it) { + (*it)->_drawNum--; + } + } + // Is not in order, so readd it as if it was a new ticket + renderTicket->_drawNum = 0; + drawFromTicket(renderTicket); + } + } +} + +void BaseRenderOSystem::addDirtyRect(const Common::Rect &rect) { + if (!_dirtyRect) { + _dirtyRect = new Common::Rect(rect); + } else { + _dirtyRect->extend(rect); + } + _dirtyRect->clip(_renderRect); +} + +void BaseRenderOSystem::drawTickets() { + RenderQueueIterator it = _renderQueue.begin(); + // Clean out the old tickets + int decrement = 0; + while (it != _renderQueue.end()) { + if ((*it)->_wantsDraw == false || (*it)->_isValid == false) { + RenderTicket *ticket = *it; + addDirtyRect((*it)->_dstRect); + it = _renderQueue.erase(it); + delete ticket; + decrement++; + } else { + (*it)->_drawNum -= decrement; + ++it; + } + } + if (!_dirtyRect || _dirtyRect->width() == 0 || _dirtyRect->height() == 0) { + return; + } + // The color-mods are stored in the RenderTickets on add, since we set that state again during + // draw, we need to keep track of what it was prior to draw. + uint32 oldColorMod = _colorMod; + + // Apply the clear-color to the dirty rect. + _renderSurface->fillRect(*_dirtyRect, _clearColor); + _drawNum = 1; + for (it = _renderQueue.begin(); it != _renderQueue.end(); ++it) { + RenderTicket *ticket = *it; + assert(ticket->_drawNum == _drawNum++); + if (ticket->_isValid && ticket->_dstRect.intersects(*_dirtyRect)) { + // dstClip is the area we want redrawn. + Common::Rect dstClip(ticket->_dstRect); + // reduce it to the dirty rect + dstClip.clip(*_dirtyRect); + // we need to keep track of the position to redraw the dirty rect + Common::Rect pos(dstClip); + int16 offsetX = ticket->_dstRect.left; + int16 offsetY = ticket->_dstRect.top; + // convert from screen-coords to surface-coords. + dstClip.translate(-offsetX, -offsetY); + + _colorMod = ticket->_colorMod; + drawFromSurface(ticket->getSurface(), &ticket->_srcRect, &pos, &dstClip, ticket->_mirror); + _needsFlip = true; + } + // Some tickets want redraw but don't actually clip the dirty area (typically the ones that shouldnt become clear-color) + ticket->_wantsDraw = false; + } + g_system->copyRectToScreen((byte *)_renderSurface->getBasePtr(_dirtyRect->left, _dirtyRect->top), _renderSurface->pitch, _dirtyRect->left, _dirtyRect->top, _dirtyRect->width(), _dirtyRect->height()); + + // Revert the colorMod-state. + _colorMod = oldColorMod; +} + +// Replacement for SDL2's SDL_RenderCopy +void BaseRenderOSystem::drawFromSurface(RenderTicket *ticket, Common::Rect *clipRect) { + TransparentSurface src(*ticket->getSurface(), false); + bool doDelete = false; + if (!clipRect) { + doDelete = true; + clipRect = new Common::Rect(); + clipRect->setWidth(ticket->getSurface()->w); + clipRect->setHeight(ticket->getSurface()->h); + } + + src._enableAlphaBlit = ticket->_hasAlpha; + src.blit(*_renderSurface, ticket->_dstRect.left, ticket->_dstRect.top, ticket->_mirror, clipRect, _colorMod, clipRect->width(), clipRect->height()); + if (doDelete) { + delete clipRect; + } +} +void BaseRenderOSystem::drawFromSurface(const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, Common::Rect *clipRect, uint32 mirror) { + TransparentSurface src(*surf, false); + bool doDelete = false; + if (!clipRect) { + doDelete = true; + clipRect = new Common::Rect(); + clipRect->setWidth(surf->w); + clipRect->setHeight(surf->h); + } + + src.blit(*_renderSurface, dstRect->left, dstRect->top, mirror, clipRect, _colorMod, clipRect->width(), clipRect->height()); + if (doDelete) { + delete clipRect; + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::drawLine(int x1, int y1, int x2, int y2, uint32 color) { + + if (!_disableDirtyRects) { + warning("BaseRenderOSystem::DrawLine - doesn't work for dirty rects yet"); + } + + byte r = RGBCOLGetR(color); + byte g = RGBCOLGetG(color); + byte b = RGBCOLGetB(color); + byte a = RGBCOLGetA(color); + + //SDL_SetRenderDrawColor(_renderer, r, g, b, a); + //SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + + Point32 point1, point2; + point1.x = x1; + point1.y = y1; + pointToScreen(&point1); + + point2.x = x2; + point2.y = y2; + pointToScreen(&point2); + + // TODO: This thing is mostly here until I'm sure about the final color-format. + uint32 colorVal = _renderSurface->format.ARGBToColor(a, r, g, b); + _renderSurface->drawLine(point1.x, point1.y, point2.x, point2.y, colorVal); + //SDL_RenderDrawLine(_renderer, point1.x, point1.y, point2.x, point2.y); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseImage *BaseRenderOSystem::takeScreenshot() { +// TODO: Clip by viewport. + BaseImage *screenshot = new BaseImage(); + screenshot->copyFrom(_renderSurface); + return screenshot; +} + +////////////////////////////////////////////////////////////////////////// +Common::String BaseRenderOSystem::getName() const { + return "ScummVM-OSystem-renderer"; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseRenderOSystem::setViewport(int left, int top, int right, int bottom) { + Common::Rect rect; + // TODO: Hopefully this is the same logic that ScummVM uses. + rect.left = (int16)(left + _borderLeft); + rect.top = (int16)(top + _borderTop); + rect.right = (int16)((right - left) * _ratioX); + rect.bottom = (int16)((bottom - top) * _ratioY); + + _renderRect = rect; + return STATUS_OK; +} + +Rect32 BaseRenderOSystem::getViewPort() { + Rect32 ret; + ret.top = _renderRect.top; + ret.bottom = _renderRect.bottom; + ret.left = _renderRect.left; + ret.right = _renderRect.right; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::modTargetRect(Common::Rect *rect) { + // FIXME: This is wrong in quite a few ways right now, and ends up + // breaking the notebook in Dirty Split, so we disable the correction + // for now, this will need fixing when a game with odd aspect-ratios + // show up. + return; + rect->left = (int16)MathUtil::round(rect->left * _ratioX + _borderLeft - _renderRect.left); + rect->top = (int16)MathUtil::round(rect->top * _ratioY + _borderTop - _renderRect.top); + rect->setWidth((int16)MathUtil::roundUp(rect->width() * _ratioX)); + rect->setHeight((int16)MathUtil::roundUp(rect->height() * _ratioY)); +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::pointFromScreen(Point32 *point) { + point->x = (int16)(point->x / _ratioX - _borderLeft / _ratioX + _renderRect.left); + point->y = (int16)(point->y / _ratioY - _borderTop / _ratioY + _renderRect.top); +} + + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::pointToScreen(Point32 *point) { + point->x = (int16)MathUtil::roundUp(point->x * _ratioX) + _borderLeft - _renderRect.left; + point->y = (int16)MathUtil::roundUp(point->y * _ratioY) + _borderTop - _renderRect.top; +} + +////////////////////////////////////////////////////////////////////////// +void BaseRenderOSystem::dumpData(const char *filename) { + warning("BaseRenderOSystem::DumpData(%s) - stubbed", filename); // TODO +} + +BaseSurface *BaseRenderOSystem::createSurface() { + return new BaseSurfaceOSystem(_gameRef); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/osystem/base_render_osystem.h b/engines/wintermute/base/gfx/osystem/base_render_osystem.h new file mode 100644 index 0000000000..1e72508cd0 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_render_osystem.h @@ -0,0 +1,131 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_RENDERER_SDL_H +#define WINTERMUTE_BASE_RENDERER_SDL_H + +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "common/list.h" + +namespace Wintermute { +class BaseSurfaceOSystem; +class RenderTicket { + Graphics::Surface *_surface; +public: + RenderTicket(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRest, bool mirrorX = false, bool mirrorY = false, bool disableAlpha = false); + RenderTicket() : _isValid(true), _wantsDraw(false), _drawNum(0) {} + ~RenderTicket(); + const Graphics::Surface *getSurface() { return _surface; } + Common::Rect _srcRect; + Common::Rect _dstRect; + uint32 _mirror; + bool _hasAlpha; + + bool _isValid; + bool _wantsDraw; + uint32 _drawNum; + uint32 _colorMod; + + BaseSurfaceOSystem *_owner; + bool operator==(RenderTicket &a); +}; + +class BaseRenderOSystem : public BaseRenderer { +public: + BaseRenderOSystem(BaseGame *inGame); + ~BaseRenderOSystem(); + + Common::String getName() const; + + bool initRenderer(int width, int height, bool windowed); + bool flip(); + virtual bool indicatorFlip(); + bool fill(byte r, byte g, byte b, Common::Rect *rect = NULL); + Graphics::PixelFormat getPixelFormat() const; + void fade(uint16 alpha); + void fadeToColor(byte r, byte g, byte b, byte a, Common::Rect *rect = NULL); + + bool drawLine(int x1, int y1, int x2, int y2, uint32 color); + + BaseImage *takeScreenshot(); + + void setAlphaMod(byte alpha); + void setColorMod(byte r, byte g, byte b); + void invalidateTicket(RenderTicket *renderTicket); + void invalidateTicketsFromSurface(BaseSurfaceOSystem *surf); + void drawFromTicket(RenderTicket *renderTicket); + + bool setViewport(int left, int top, int right, int bottom); + bool setViewport(Rect32 *rect) { return BaseRenderer::setViewport(rect); } + Rect32 getViewPort(); + void modTargetRect(Common::Rect *rect); + void pointFromScreen(Point32 *point); + void pointToScreen(Point32 *point); + + void dumpData(const char *filename); + + float getScaleRatioX() const { + return _ratioX; + } + float getScaleRatioY() const { + return _ratioY; + } + + void drawSurface(BaseSurfaceOSystem *owner, const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, bool mirrorX, bool mirrorY, bool disableAlpha = false); + BaseSurface *createSurface(); +private: + void addDirtyRect(const Common::Rect &rect); + void drawTickets(); + void drawFromSurface(RenderTicket *ticket, Common::Rect *clipRect); + void drawFromSurface(const Graphics::Surface *surf, Common::Rect *srcRect, Common::Rect *dstRect, Common::Rect *clipRect, uint32 mirror); + typedef Common::List<RenderTicket *>::iterator RenderQueueIterator; + Common::Rect *_dirtyRect; + Common::List<RenderTicket *> _renderQueue; + bool _needsFlip; + uint32 _drawNum; + Common::Rect _renderRect; + Graphics::Surface *_renderSurface; + Graphics::Surface *_blankSurface; + + int _borderLeft; + int _borderTop; + int _borderRight; + int _borderBottom; + + static const bool _disableDirtyRects = true; + float _ratioX; + float _ratioY; + uint32 _colorMod; + uint32 _clearColor; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp new file mode 100644 index 0000000000..bee876bb65 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.cpp @@ -0,0 +1,428 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/platform_osystem.h" +#include "graphics/decoders/png.h" +#include "graphics/decoders/bmp.h" +#include "graphics/decoders/jpeg.h" +#include "graphics/decoders/tga.h" +#include "engines/wintermute/graphics/transparent_surface.h" +#include "graphics/pixelformat.h" +#include "graphics/surface.h" +#include "common/stream.h" +#include "common/system.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +BaseSurfaceOSystem::BaseSurfaceOSystem(BaseGame *inGame) : BaseSurface(inGame) { + _surface = new Graphics::Surface(); + _alphaMask = NULL; + _hasAlpha = true; + _lockPixels = NULL; + _lockPitch = 0; + _loaded = false; +} + +////////////////////////////////////////////////////////////////////////// +BaseSurfaceOSystem::~BaseSurfaceOSystem() { + if (_surface) { + _surface->free(); + delete _surface; + _surface = NULL; + } + + delete[] _alphaMask; + _alphaMask = NULL; + + _gameRef->addMem(-_width * _height * 4); + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); +} + +bool hasTransparency(Graphics::Surface *surf) { + if (surf->format.bytesPerPixel != 4) { + warning("hasTransparency:: non 32 bpp surface passed as argument"); + return false; + } + uint8 r, g, b, a; + for (int i = 0; i < surf->h; i++) { + for (int j = 0; j < surf->w; j++) { + uint32 pix = *(uint32 *)surf->getBasePtr(j, i); + surf->format.colorToARGB(pix, a, r, g, b); + if (a != 255) { + return true; + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime, bool keepLoaded) { + /* BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); */ + _filename = filename; +// const Graphics::Surface *surface = image->getSurface(); + + if (defaultCK) { + ckRed = 255; + ckGreen = 0; + ckBlue = 255; + } + + _ckDefault = defaultCK; + _ckRed = ckRed; + _ckGreen = ckGreen; + _ckBlue = ckBlue; + + if (_lifeTime == 0 || lifeTime == -1 || lifeTime > _lifeTime) { + _lifeTime = lifeTime; + } + + _keepLoaded = keepLoaded; + if (_keepLoaded) { + _lifeTime = -1; + } + + return STATUS_OK; +} + +bool BaseSurfaceOSystem::finishLoad() { + BaseImage *image = new BaseImage(); + if (!image->loadFile(_filename)) { + return false; + } + + _width = image->getSurface()->w; + _height = image->getSurface()->h; + + bool isSaveGameGrayscale = scumm_strnicmp(_filename.c_str(), "savegame:", 9) == 0 && (_filename.c_str()[_filename.size() - 1] == 'g' || _filename.c_str()[_filename.size() - 1] == 'G'); + if (isSaveGameGrayscale) { + warning("grayscaleConversion not yet implemented"); + // FIBITMAP *newImg = FreeImage_ConvertToGreyscale(img); TODO + } + + // no alpha, set color key + /* if (surface->format.bytesPerPixel != 4) + SDL_SetColorKey(surf, SDL_TRUE, SDL_MapRGB(surf->format, ck_red, ck_green, ck_blue));*/ + + // convert 32-bit BMPs to 24-bit or they appear totally transparent (does any app actually write alpha in BMP properly?) + // Well, actually, we don't convert via 24-bit as the color-key application overwrites the Alpha-channel anyhow. + _surface->free(); + delete _surface; + if (_filename.hasSuffix(".bmp") && image->getSurface()->format.bytesPerPixel == 4) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette()); + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue); + } else if (image->getSurface()->format.bytesPerPixel == 1 && image->getPalette()) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat(), image->getPalette()); + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true); + } else if (image->getSurface()->format.bytesPerPixel >= 3 && image->getSurface()->format != g_system->getScreenFormat()) { + _surface = image->getSurface()->convertTo(g_system->getScreenFormat()); + if (image->getSurface()->format.bytesPerPixel == 3) { + TransparentSurface trans(*_surface); + trans.applyColorKey(_ckRed, _ckGreen, _ckBlue, true); + } + } else { + _surface = new Graphics::Surface(); + _surface->copyFrom(*image->getSurface()); + } + + _hasAlpha = hasTransparency(_surface); + _valid = true; + + _gameRef->addMem(_width * _height * 4); + + delete image; + + _loaded = true; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSurfaceOSystem::genAlphaMask(Graphics::Surface *surface) { + warning("BaseSurfaceOSystem::GenAlphaMask - Not ported yet"); + return; + // TODO: Reimplement this + delete[] _alphaMask; + _alphaMask = NULL; + if (!surface) { + return; + } + + bool hasColorKey; + /* uint32 colorKey; */ + uint8 ckRed, ckGreen, ckBlue; + /* if (SDL_GetColorKey(surface, &colorKey) == 0) { + hasColorKey = true; + SDL_GetRGB(colorKey, surface->format, &ckRed, &ckGreen, &ckBlue); + } else hasColorKey = false; + */ + _alphaMask = new byte[surface->w * surface->h]; + + bool hasTransparency = false; + for (int y = 0; y < surface->h; y++) { + for (int x = 0; x < surface->w; x++) { + uint32 pixel = getPixelAt(surface, x, y); + + uint8 r, g, b, a; + surface->format.colorToARGB(pixel, a, r, g, b); + //SDL_GetRGBA(pixel, surface->format, &r, &g, &b, &a); + + if (hasColorKey && r == ckRed && g == ckGreen && b == ckBlue) { + a = 0; + } + + _alphaMask[y * surface->w + x] = a; + if (a < 255) { + hasTransparency = true; + } + } + } + + if (!hasTransparency) { + delete[] _alphaMask; + _alphaMask = NULL; + } +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSurfaceOSystem::getPixelAt(Graphics::Surface *surface, int x, int y) { + warning("BaseSurfaceOSystem::GetPixel - Not ported yet"); + int bpp = surface->format.bytesPerPixel; + /* Here p is the address to the pixel we want to retrieve */ + uint8 *p = (uint8 *)surface->pixels + y * surface->pitch + x * bpp; + + switch (bpp) { + case 1: + return *p; + break; + + case 2: + return *(uint16 *)p; + break; + + case 3: +#ifdef SCUMM_BIG_ENDIAN + // if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + return p[0] << 16 | p[1] << 8 | p[2]; +#else + //else + return p[0] | p[1] << 8 | p[2] << 16; +#endif + break; + + case 4: + return *(uint32 *)p; + break; + + default: + return 0; /* shouldn't happen, but avoids warnings */ + } + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::create(int width, int height) { + _width = width; + _height = height; + + _gameRef->addMem(_width * _height * 4); + + _valid = true; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::isTransparentAt(int x, int y) { + return isTransparentAtLite(x, y); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::isTransparentAtLite(int x, int y) { + if (x < 0 || x >= _surface->w || y < 0 || y >= _surface->h) { + return true; + } + + if (_surface->format.bytesPerPixel == 4) { + uint32 pixel = *(uint32 *)_surface->getBasePtr(x, y); + uint8 r, g, b, a; + _surface->format.colorToARGB(pixel, a, r, g, b); + if (a <= 128) { + return true; + } else { + return false; + } + } + + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::startPixelOp() { + //SDL_LockTexture(_texture, NULL, &_lockPixels, &_lockPitch); + // Any pixel-op makes the caching useless: + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::endPixelOp() { + //SDL_UnlockTexture(_texture); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, 100, 100, 0xFFFFFFFF, true, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTrans(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransOffset(int x, int y, Rect32 rect, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) { + return drawSprite(x, y, &rect, 100, 100, alpha, false, blendMode, mirrorX, mirrorY, offsetX, offsetY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha, bool transparent, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, !transparent, blendMode, mirrorX, mirrorY); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::displayTransform(int x, int y, int hotX, int hotY, Rect32 rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY) { + return drawSprite(x, y, &rect, zoomX, zoomY, alpha, false, blendMode, mirrorX, mirrorY); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSurfaceOSystem::drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + if (!_loaded) { + finishLoad(); + } + + if (renderer->_forceAlphaColor != 0) { + alpha = renderer->_forceAlphaColor; + } + + byte r = RGBCOLGetR(alpha); + byte g = RGBCOLGetG(alpha); + byte b = RGBCOLGetB(alpha); + byte a = RGBCOLGetA(alpha); + + renderer->setAlphaMod(a); + renderer->setColorMod(r, g, b); + +#if 0 // These are kept for reference if BlendMode is reimplemented at some point. + if (alphaDisable) { + SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_NONE); + } else { + SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND); + } +#endif + // TODO: This _might_ miss the intended behaviour by 1 in each direction + // But I think it fits the model used in Wintermute. + Common::Rect srcRect; + srcRect.left = rect->left; + srcRect.top = rect->top; + srcRect.setWidth(rect->right - rect->left); + srcRect.setHeight(rect->bottom - rect->top); + + Common::Rect position; + position.left = x + offsetX; + position.top = y + offsetY; + + // Crop off-by-ones: + if (position.left == -1) { + position.left = 0; // TODO: Something is wrong + } + if (position.top == -1) { + position.top = 0; // TODO: Something is wrong + } + + position.setWidth((int16)((float)srcRect.width() * zoomX / 100.f)); + position.setHeight((int16)((float)srcRect.height() * zoomX / 100.f)); + + renderer->modTargetRect(&position); + + /* position.left += offsetX; + position.top += offsetY;*/ + + // TODO: This actually requires us to have the SAME source-offsets every time, + // But no checking is in place for that yet. + + // TODO: Optimize by not doing alpha-blits if we lack or disable alpha + bool hasAlpha; + if (_hasAlpha && !alphaDisable) { + hasAlpha = true; + } else { + hasAlpha = false; + } + if (alphaDisable) { + warning("BaseSurfaceOSystem::drawSprite - AlphaDisable ignored"); + } + + renderer->drawSurface(this, _surface, &srcRect, &position, mirrorX, mirrorY, !hasAlpha); + + return STATUS_OK; +} + +bool BaseSurfaceOSystem::putSurface(const Graphics::Surface &surface, bool hasAlpha) { + _loaded = true; + _surface->free(); + _surface->copyFrom(surface); + _hasAlpha = hasAlpha; + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + renderer->invalidateTicketsFromSurface(this); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/gfx/osystem/base_surface_osystem.h b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h new file mode 100644 index 0000000000..43422ef4e7 --- /dev/null +++ b/engines/wintermute/base/gfx/osystem/base_surface_osystem.h @@ -0,0 +1,99 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SURFACESDL_H +#define WINTERMUTE_BASE_SURFACESDL_H + +#include "graphics/surface.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "common/list.h" + +namespace Wintermute { +struct TransparentSurface; +class BaseImage; +class BaseSurfaceOSystem : public BaseSurface { +public: + BaseSurfaceOSystem(BaseGame *inGame); + ~BaseSurfaceOSystem(); + + bool create(const Common::String &filename, bool defaultCK, byte ckRed, byte ckGreen, byte ckBlue, int lifeTime = -1, bool keepLoaded = false); + bool create(int width, int height); + + bool isTransparentAt(int x, int y); + bool isTransparentAtLite(int x, int y); + + bool startPixelOp(); + bool endPixelOp(); + + + bool displayTransZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTrans(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTransOffset(int x, int y, Rect32 rect, uint32 alpha = 0xFFFFFFFF, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false, int offsetX = 0, int offsetY = 0); + bool display(int x, int y, Rect32 rect, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayZoom(int x, int y, Rect32 rect, float zoomX, float zoomY, uint32 alpha = 0xFFFFFFFF, bool transparent = false, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + bool displayTransform(int x, int y, int hotX, int hotY, Rect32 Rect, float zoomX, float zoomY, uint32 alpha, float rotate, TSpriteBlendMode blendMode = BLEND_NORMAL, bool mirrorX = false, bool mirrorY = false); + virtual bool putSurface(const Graphics::Surface &surface, bool hasAlpha = false); + /* static unsigned DLL_CALLCONV ReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle); + static int DLL_CALLCONV SeekProc(fi_handle handle, long offset, int origin); + static long DLL_CALLCONV TellProc(fi_handle handle);*/ + virtual int getWidth() { + if (!_loaded) { + finishLoad(); + } + if (_surface) { + return _surface->w; + } + return _width; + } + virtual int getHeight() { + if (!_loaded) { + finishLoad(); + } + if (_surface) { + return _surface->h; + } + return _height; + } + +private: + Graphics::Surface *_surface; + bool _loaded; + bool finishLoad(); + bool drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX = 0, int offsetY = 0); + void genAlphaMask(Graphics::Surface *surface); + uint32 getPixelAt(Graphics::Surface *surface, int x, int y); + + bool _hasAlpha; + void *_lockPixels; + int _lockPitch; + byte *_alphaMask; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_emitter.cpp b/engines/wintermute/base/particles/part_emitter.cpp new file mode 100644 index 0000000000..bab4d4609e --- /dev/null +++ b/engines/wintermute/base/particles/part_emitter.cpp @@ -0,0 +1,1257 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/particles/part_particle.h" +#include "engines/wintermute/math/vector2.h" +#include "engines/wintermute/math/matrix4.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/math.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(PartEmitter, false) + +////////////////////////////////////////////////////////////////////////// +PartEmitter::PartEmitter(BaseGame *inGame, BaseScriptHolder *owner) : BaseObject(inGame) { + _width = _height = 0; + + BasePlatform::setRectEmpty(&_border); + _borderThicknessLeft = _borderThicknessRight = _borderThicknessTop = _borderThicknessBottom = 0; + + _angle1 = _angle2 = 0; + + _velocity1 = _velocity2 = 0.0f; + _velocityZBased = false; + + _scale1 = _scale2 = 100.0f; + _scaleZBased = false; + + _maxParticles = 100; + + _lifeTime1 = _lifeTime2 = 1000; + _lifeTimeZBased = false; + + _lastGenTime = 0; + _genInterval = 0; + _genAmount = 1; + + _overheadTime = 0; + _running = false; + + _maxBatches = 0; + _batchesGenerated = 0; + + _fadeInTime = _fadeOutTime = 0; + + _alpha1 = _alpha2 = 255; + _alphaTimeBased = false; + + _rotation1 = _rotation2 = 0.0f; + _angVelocity1 = _angVelocity2 = 0.0f; + + _growthRate1 = _growthRate2 = 0.0f; + _exponentialGrowth = false; + + _useRegion = false; + + _emitEvent = NULL; + _owner = owner; +} + + +////////////////////////////////////////////////////////////////////////// +PartEmitter::~PartEmitter(void) { + for (uint32 i = 0; i < _particles.size(); i++) { + delete _particles[i]; + } + _particles.clear(); + + for (uint32 i = 0; i < _forces.size(); i++) { + delete _forces[i]; + } + _forces.clear(); + + + for (uint32 i = 0; i < _sprites.size(); i++) { + delete[] _sprites[i]; + } + _sprites.clear(); + + delete[] _emitEvent; + _emitEvent = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::addSprite(const char *filename) { + if (!filename) { + return STATUS_FAILED; + } + + // do we already have the file? + for (uint32 i = 0; i < _sprites.size(); i++) { + if (scumm_stricmp(filename, _sprites[i]) == 0) { + return STATUS_OK; + } + } + + // check if file exists + Common::SeekableReadStream *File = BaseFileManager::getEngineInstance()->openFile(filename); + if (!File) { + _gameRef->LOG(0, "Sprite '%s' not found", filename); + return STATUS_FAILED; + } else { + BaseFileManager::getEngineInstance()->closeFile(File); + } + + char *str = new char[strlen(filename) + 1]; + strcpy(str, filename); + _sprites.add(str); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::removeSprite(const char *filename) { + for (uint32 i = 0; i < _sprites.size(); i++) { + if (scumm_stricmp(filename, _sprites[i]) == 0) { + delete[] _sprites[i]; + _sprites.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta) { + if (!particle) { + return STATUS_FAILED; + } + if (_sprites.size() == 0) { + return STATUS_FAILED; + } + + int posX = BaseUtils::randomInt(_posX, _posX + _width); + int posY = BaseUtils::randomInt(_posY, _posY + _height); + float posZ = BaseUtils::randomFloat(0.0f, 100.0f); + + float velocity; + if (_velocityZBased) { + velocity = _velocity1 + posZ * (_velocity2 - _velocity1) / 100; + } else { + velocity = BaseUtils::randomFloat(_velocity1, _velocity2); + } + + float scale; + if (_scaleZBased) { + scale = _scale1 + posZ * (_scale2 - _scale1) / 100; + } else { + scale = BaseUtils::randomFloat(_scale1, _scale2); + } + + int lifeTime; + if (_lifeTimeZBased) { + lifeTime = (int)(_lifeTime2 - posZ * (_lifeTime2 - _lifeTime1) / 100); + } else { + lifeTime = BaseUtils::randomInt(_lifeTime1, _lifeTime2); + } + + float angle = BaseUtils::randomAngle(_angle1, _angle2); + int spriteIndex = BaseUtils::randomInt(0, _sprites.size() - 1); + + float rotation = BaseUtils::randomAngle(_rotation1, _rotation2); + float angVelocity = BaseUtils::randomFloat(_angVelocity1, _angVelocity2); + float growthRate = BaseUtils::randomFloat(_growthRate1, _growthRate2); + + if (!BasePlatform::isRectEmpty(&_border)) { + int thicknessLeft = (int)(_borderThicknessLeft - (float)_borderThicknessLeft * posZ / 100.0f); + int thicknessRight = (int)(_borderThicknessRight - (float)_borderThicknessRight * posZ / 100.0f); + int thicknessTop = (int)(_borderThicknessTop - (float)_borderThicknessTop * posZ / 100.0f); + int thicknessBottom = (int)(_borderThicknessBottom - (float)_borderThicknessBottom * posZ / 100.0f); + + particle->_border = _border; + particle->_border.left += thicknessLeft; + particle->_border.right -= thicknessRight; + particle->_border.top += thicknessTop; + particle->_border.bottom -= thicknessBottom; + } + + Vector2 vecPos((float)posX, (float)posY); + Vector2 vecVel(0, velocity); + + Matrix4 matRot; + matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + matRot.transformVector2(vecVel); + + if (_alphaTimeBased) { + particle->_alpha1 = _alpha1; + particle->_alpha2 = _alpha2; + } else { + int alpha = BaseUtils::randomInt(_alpha1, _alpha2); + particle->_alpha1 = alpha; + particle->_alpha2 = alpha; + } + + particle->_creationTime = currentTime; + particle->_pos = vecPos; + particle->_posZ = posZ; + particle->_velocity = vecVel; + particle->_scale = scale; + particle->_lifeTime = lifeTime; + particle->_rotation = rotation; + particle->_angVelocity = angVelocity; + particle->_growthRate = growthRate; + particle->_exponentialGrowth = _exponentialGrowth; + particle->_isDead = DID_FAIL(particle->setSprite(_sprites[spriteIndex])); + particle->fadeIn(currentTime, _fadeInTime); + + + if (particle->_isDead) { + return STATUS_FAILED; + } else { + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::update() { + if (!_running) { + return STATUS_OK; + } else { + return updateInternal(_gameRef->_timer, _gameRef->_timerDelta); + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::updateInternal(uint32 currentTime, uint32 timerDelta) { + int numLive = 0; + + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->update(this, currentTime, timerDelta); + + if (!_particles[i]->_isDead) { + numLive++; + } + } + + + // we're understaffed + if (numLive < _maxParticles) { + bool needsSort = false; + if ((int)(currentTime - _lastGenTime) > _genInterval) { + _lastGenTime = currentTime; + _batchesGenerated++; + + if (_maxBatches > 0 && _batchesGenerated > _maxBatches) { + return STATUS_OK; + } + + int toGen = MIN(_genAmount, _maxParticles - numLive); + while (toGen > 0) { + int firstDeadIndex = -1; + for (uint32 i = 0; i < _particles.size(); i++) { + if (_particles[i]->_isDead) { + firstDeadIndex = i; + break; + } + } + + PartParticle *particle; + if (firstDeadIndex >= 0) { + particle = _particles[firstDeadIndex]; + } else { + particle = new PartParticle(_gameRef); + _particles.add(particle); + } + initParticle(particle, currentTime, timerDelta); + needsSort = true; + + toGen--; + } + } + if (needsSort && (_scaleZBased || _velocityZBased || _lifeTimeZBased)) { + sortParticlesByZ(); + } + + // we actually generated some particles and we're not in fast-forward mode + if (needsSort && _overheadTime == 0) { + if (_owner && _emitEvent) { + _owner->applyEvent(_emitEvent); + } + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::display(BaseRegion *region) { + if (_sprites.size() <= 1) { + _gameRef->_renderer->startSpriteBatch(); + } + + for (uint32 i = 0; i < _particles.size(); i++) { + if (region != NULL && _useRegion) { + if (!region->pointInRegion((int)_particles[i]->_pos.x, (int)_particles[i]->_pos.y)) { + continue; + } + } + + _particles[i]->display(this); + } + + if (_sprites.size() <= 1) { + _gameRef->_renderer->endSpriteBatch(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::start() { + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->_isDead = true; + } + _running = true; + _batchesGenerated = 0; + + + if (_overheadTime > 0) { + uint32 delta = 500; + int steps = _overheadTime / delta; + uint32 currentTime = _gameRef->_timer - _overheadTime; + + for (int i = 0; i < steps; i++) { + updateInternal(currentTime, delta); + currentTime += delta; + } + _overheadTime = 0; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::sortParticlesByZ() { + // sort particles by _posY + Common::sort(_particles.begin(), _particles.end(), PartEmitter::compareZ); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int PartEmitter::compareZ(const void *obj1, const void *obj2) { + const PartParticle *p1 = *(const PartParticle *const *)obj1; + const PartParticle *p2 = *(const PartParticle *const *)obj2; + + if (p1->_posZ < p2->_posZ) { + return -1; + } else if (p1->_posZ > p2->_posZ) { + return 1; + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::setBorder(int x, int y, int width, int height) { + BasePlatform::setRect(&_border, x, y, x + width, y + height); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom) { + _borderThicknessLeft = thicknessLeft; + _borderThicknessRight = thicknessRight; + _borderThicknessTop = thicknessTop; + _borderThicknessBottom = thicknessBottom; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +PartForce *PartEmitter::addForceByName(const Common::String &name) { + PartForce *force = NULL; + + for (uint32 i = 0; i < _forces.size(); i++) { + if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) { + force = _forces[i]; + break; + } + } + if (!force) { + force = new PartForce(_gameRef); + if (force) { + force->setName(name.c_str()); + _forces.add(force); + } + } + return force; +} + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength) { + PartForce *force = addForceByName(name); + if (!force) { + return STATUS_FAILED; + } + + force->_type = type; + force->_pos = Vector2(posX, posY); + + force->_direction = Vector2(0, strength); + Matrix4 matRot; + matRot.rotationZ(Common::deg2rad(BaseUtils::normalizeAngle(angle - 180))); + matRot.transformVector2(force->_direction); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::removeForce(const Common::String &name) { + for (uint32 i = 0; i < _forces.size(); i++) { + if (scumm_stricmp(name.c_str(), _forces[i]->getName()) == 0) { + delete _forces[i]; + _forces.remove_at(i); + return STATUS_OK; + } + } + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetBorder + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetBorder") == 0) { + stack->correctParams(4); + int borderX = stack->pop()->getInt(); + int borderY = stack->pop()->getInt(); + int borderWidth = stack->pop()->getInt(); + int borderHeight = stack->pop()->getInt(); + + stack->pushBool(DID_SUCCEED(setBorder(borderX, borderY, borderWidth, borderHeight))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetBorderThickness + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetBorderThickness") == 0) { + stack->correctParams(4); + int left = stack->pop()->getInt(); + int right = stack->pop()->getInt(); + int top = stack->pop()->getInt(); + int bottom = stack->pop()->getInt(); + + stack->pushBool(DID_SUCCEED(setBorderThickness(left, right, top, bottom))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AddSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddSprite") == 0) { + stack->correctParams(1); + const char *spriteFile = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(addSprite(spriteFile))); + + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // RemoveSprite + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveSprite") == 0) { + stack->correctParams(1); + const char *spriteFile = stack->pop()->getString(); + stack->pushBool(DID_SUCCEED(removeSprite(spriteFile))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Start + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Start") == 0) { + stack->correctParams(1); + _overheadTime = stack->pop()->getInt(); + stack->pushBool(DID_SUCCEED(start())); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Stop + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Stop") == 0) { + stack->correctParams(0); + + for (uint32 i = 0; i < _particles.size(); i++) { + delete _particles[i]; + } + _particles.clear(); + + _running = false; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pause + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pause") == 0) { + stack->correctParams(0); + _running = false; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Resume + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Resume") == 0) { + stack->correctParams(0); + _running = true; + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddGlobalForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddGlobalForce") == 0) { + stack->correctParams(3); + const char *forceName = stack->pop()->getString(); + float angle = stack->pop()->getFloat(); + float strength = stack->pop()->getFloat(); + + stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, 0, 0, angle, strength))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AddPointForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AddPointForce") == 0) { + stack->correctParams(5); + const char *forceName = stack->pop()->getString(); + int posX = stack->pop()->getInt(); + int posY = stack->pop()->getInt(); + float angle = stack->pop()->getFloat(); + float strength = stack->pop()->getFloat(); + + stack->pushBool(DID_SUCCEED(addForce(forceName, PartForce::FORCE_GLOBAL, posX, posY, angle, strength))); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RemoveForce + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RemoveForce") == 0) { + stack->correctParams(1); + const char *forceName = stack->pop()->getString(); + + stack->pushBool(DID_SUCCEED(removeForce(forceName))); + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + +////////////////////////////////////////////////////////////////////////// +ScValue *PartEmitter::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("particle-emitter"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + else if (name == "X") { + _scValue->setInt(_posX); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (name == "Y") { + _scValue->setInt(_posY); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale1") { + _scValue->setFloat(_scale1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Scale2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Scale2") { + _scValue->setFloat(_scale2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // ScaleZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "ScaleZBased") { + _scValue->setBool(_scaleZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Velocity1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Velocity1") { + _scValue->setFloat(_velocity1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Velocity2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Velocity2") { + _scValue->setFloat(_velocity2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // VelocityZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "VelocityZBased") { + _scValue->setBool(_velocityZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // LifeTime1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTime1") { + _scValue->setInt(_lifeTime1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTime2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTime2") { + _scValue->setInt(_lifeTime2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTimeZBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "LifeTimeZBased") { + _scValue->setBool(_lifeTimeZBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Angle1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Angle1") { + _scValue->setInt(_angle1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Angle2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Angle2") { + _scValue->setInt(_angle2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AngVelocity1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "AngVelocity1") { + _scValue->setFloat(_angVelocity1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AngVelocity2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "AngVelocity2") { + _scValue->setFloat(_angVelocity2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotation1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotation1") { + _scValue->setFloat(_rotation1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Rotation2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Rotation2") { + _scValue->setFloat(_rotation2); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Alpha1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Alpha1") { + _scValue->setInt(_alpha1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Alpha2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "Alpha2") { + _scValue->setInt(_alpha2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaTimeBased + ////////////////////////////////////////////////////////////////////////// + else if (name == "AlphaTimeBased") { + _scValue->setBool(_alphaTimeBased); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxParticles + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxParticles") { + _scValue->setInt(_maxParticles); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // NumLiveParticles (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumLiveParticles") { + int numAlive = 0; + for (uint32 i = 0; i < _particles.size(); i++) { + if (_particles[i] && !_particles[i]->_isDead) { + numAlive++; + } + } + _scValue->setInt(numAlive); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // GenerationInterval + ////////////////////////////////////////////////////////////////////////// + else if (name == "GenerationInterval") { + _scValue->setInt(_genInterval); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // GenerationAmount + ////////////////////////////////////////////////////////////////////////// + else if (name == "GenerationAmount") { + _scValue->setInt(_genAmount); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // MaxBatches + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxBatches") { + _scValue->setInt(_maxBatches); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeInTime + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeInTime") { + _scValue->setInt(_fadeInTime); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // FadeOutTime + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeOutTime") { + _scValue->setInt(_fadeOutTime); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // GrowthRate1 + ////////////////////////////////////////////////////////////////////////// + else if (name == "GrowthRate1") { + _scValue->setFloat(_growthRate1); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // GrowthRate2 + ////////////////////////////////////////////////////////////////////////// + else if (name == "GrowthRate2") { + _scValue->setFloat(_growthRate2); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // ExponentialGrowth + ////////////////////////////////////////////////////////////////////////// + else if (name == "ExponentialGrowth") { + _scValue->setBool(_exponentialGrowth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // UseRegion + ////////////////////////////////////////////////////////////////////////// + else if (name == "UseRegion") { + _scValue->setBool(_useRegion); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // EmitEvent + ////////////////////////////////////////////////////////////////////////// + else if (name == "EmitEvent") { + if (!_emitEvent) { + _scValue->setNULL(); + } else { + _scValue->setString(_emitEvent); + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // X + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "X") == 0) { + _posX = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Y + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Y") == 0) { + _posY = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Scale1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale1") == 0) { + _scale1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Scale2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Scale2") == 0) { + _scale2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // ScaleZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ScaleZBased") == 0) { + _scaleZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Velocity1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Velocity1") == 0) { + _velocity1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Velocity2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Velocity2") == 0) { + _velocity2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // VelocityZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VelocityZBased") == 0) { + _velocityZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LifeTime1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTime1") == 0) { + _lifeTime1 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTime2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTime2") == 0) { + _lifeTime2 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // LifeTimeZBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LifeTimeZBased") == 0) { + _lifeTimeZBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Angle1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Angle1") == 0) { + _angle1 = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Angle2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Angle2") == 0) { + _angle2 = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // AngVelocity1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AngVelocity1") == 0) { + _angVelocity1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AngVelocity2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AngVelocity2") == 0) { + _angVelocity2 = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Rotation1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotation1") == 0) { + _rotation1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Rotation2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Rotation2") == 0) { + _rotation2 = value->getFloat(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Alpha1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Alpha1") == 0) { + _alpha1 = value->getInt(); + if (_alpha1 < 0) { + _alpha1 = 0; + } + if (_alpha1 > 255) { + _alpha1 = 255; + } + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Alpha2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Alpha2") == 0) { + _alpha2 = value->getInt(); + if (_alpha2 < 0) { + _alpha2 = 0; + } + if (_alpha2 > 255) { + _alpha2 = 255; + } + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // AlphaTimeBased + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "AlphaTimeBased") == 0) { + _alphaTimeBased = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxParticles + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxParticles") == 0) { + _maxParticles = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GenerationInterval + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GenerationInterval") == 0) { + _genInterval = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GenerationAmount + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GenerationAmount") == 0) { + _genAmount = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // MaxBatches + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxBatches") == 0) { + _maxBatches = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeInTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeInTime") == 0) { + _fadeInTime = value->getInt(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // FadeOutTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeOutTime") == 0) { + _fadeOutTime = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GrowthRate1 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GrowthRate1") == 0) { + _growthRate1 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GrowthRate2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GrowthRate2") == 0) { + _growthRate2 = value->getFloat(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // ExponentialGrowth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ExponentialGrowth") == 0) { + _exponentialGrowth = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // UseRegion + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "UseRegion") == 0) { + _useRegion = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // EmitEvent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "EmitEvent") == 0) { + delete[] _emitEvent; + _emitEvent = NULL; + if (!value->isNULL()) { + BaseUtils::setString(&_emitEvent, value->getString()); + } + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *PartEmitter::scToString() { + return "[particle emitter]"; +} + + + + +////////////////////////////////////////////////////////////////////////// +bool PartEmitter::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_width)); + persistMgr->transfer(TMEMBER(_height)); + + persistMgr->transfer(TMEMBER(_angle1)); + persistMgr->transfer(TMEMBER(_angle2)); + + persistMgr->transfer(TMEMBER(_velocity1)); + persistMgr->transfer(TMEMBER(_velocity2)); + persistMgr->transfer(TMEMBER(_velocityZBased)); + + persistMgr->transfer(TMEMBER(_scale1)); + persistMgr->transfer(TMEMBER(_scale2)); + persistMgr->transfer(TMEMBER(_scaleZBased)); + + persistMgr->transfer(TMEMBER(_maxParticles)); + + persistMgr->transfer(TMEMBER(_lifeTime1)); + persistMgr->transfer(TMEMBER(_lifeTime2)); + persistMgr->transfer(TMEMBER(_lifeTimeZBased)); + + persistMgr->transfer(TMEMBER(_genInterval)); + persistMgr->transfer(TMEMBER(_genAmount)); + + persistMgr->transfer(TMEMBER(_running)); + persistMgr->transfer(TMEMBER(_overheadTime)); + + persistMgr->transfer(TMEMBER(_border)); + persistMgr->transfer(TMEMBER(_borderThicknessLeft)); + persistMgr->transfer(TMEMBER(_borderThicknessRight)); + persistMgr->transfer(TMEMBER(_borderThicknessTop)); + persistMgr->transfer(TMEMBER(_borderThicknessBottom)); + + persistMgr->transfer(TMEMBER(_fadeInTime)); + persistMgr->transfer(TMEMBER(_fadeOutTime)); + + persistMgr->transfer(TMEMBER(_alpha1)); + persistMgr->transfer(TMEMBER(_alpha2)); + persistMgr->transfer(TMEMBER(_alphaTimeBased)); + + persistMgr->transfer(TMEMBER(_angVelocity1)); + persistMgr->transfer(TMEMBER(_angVelocity2)); + + persistMgr->transfer(TMEMBER(_rotation1)); + persistMgr->transfer(TMEMBER(_rotation2)); + + persistMgr->transfer(TMEMBER(_growthRate1)); + persistMgr->transfer(TMEMBER(_growthRate2)); + persistMgr->transfer(TMEMBER(_exponentialGrowth)); + + persistMgr->transfer(TMEMBER(_useRegion)); + + persistMgr->transfer(TMEMBER_INT(_maxBatches)); + persistMgr->transfer(TMEMBER_INT(_batchesGenerated)); + + persistMgr->transfer(TMEMBER(_emitEvent)); + persistMgr->transfer(TMEMBER(_owner)); + + + _sprites.persist(persistMgr); + + uint32 numForces; + if (persistMgr->getIsSaving()) { + numForces = _forces.size(); + persistMgr->transfer(TMEMBER(numForces)); + for (uint32 i = 0; i < _forces.size(); i++) { + _forces[i]->persist(persistMgr); + } + } else { + persistMgr->transfer(TMEMBER(numForces)); + for (uint32 i = 0; i < numForces; i++) { + PartForce *force = new PartForce(_gameRef); + force->persist(persistMgr); + _forces.add(force); + } + } + + uint32 numParticles; + if (persistMgr->getIsSaving()) { + numParticles = _particles.size(); + persistMgr->transfer(TMEMBER(numParticles)); + for (uint32 i = 0; i < _particles.size(); i++) { + _particles[i]->persist(persistMgr); + } + } else { + persistMgr->transfer(TMEMBER(numParticles)); + for (uint32 i = 0; i < numParticles; i++) { + PartParticle *particle = new PartParticle(_gameRef); + particle->persist(persistMgr); + _particles.add(particle); + } + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_emitter.h b/engines/wintermute/base/particles/part_emitter.h new file mode 100644 index 0000000000..f2c8f139f1 --- /dev/null +++ b/engines/wintermute/base/particles/part_emitter.h @@ -0,0 +1,140 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTEMITTER_H +#define WINTERMUTE_PARTEMITTER_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/particles/part_force.h" + +namespace Wintermute { +class BaseRegion; +class PartParticle; +class PartEmitter : public BaseObject { +public: + DECLARE_PERSISTENT(PartEmitter, BaseObject) + + PartEmitter(BaseGame *inGame, BaseScriptHolder *Owner); + virtual ~PartEmitter(void); + + int _fadeOutTime; + + bool start(); + + bool update(); + bool display() { return display(NULL); } // To avoid shadowing the inherited display-function. + bool display(BaseRegion *region); + + bool sortParticlesByZ(); + bool addSprite(const char *filename); + bool removeSprite(const char *filename); + bool setBorder(int x, int y, int width, int height); + bool setBorderThickness(int thicknessLeft, int thicknessRight, int thicknessTop, int thicknessBottom); + + bool addForce(const Common::String &name, PartForce::TForceType type, int posX, int posY, float angle, float strength); + bool removeForce(const Common::String &name); + + BaseArray<PartForce *> _forces; + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); + + +private: + int _width; + int _height; + + int _angle1; + int _angle2; + + float _rotation1; + float _rotation2; + + float _angVelocity1; + float _angVelocity2; + + float _growthRate1; + float _growthRate2; + bool _exponentialGrowth; + + float _velocity1; + float _velocity2; + bool _velocityZBased; + + float _scale1; + float _scale2; + bool _scaleZBased; + + int _maxParticles; + + int _lifeTime1; + int _lifeTime2; + bool _lifeTimeZBased; + + int _genInterval; + int _genAmount; + + bool _running; + int _overheadTime; + + int _maxBatches; + int _batchesGenerated; + + Rect32 _border; + int _borderThicknessLeft; + int _borderThicknessRight; + int _borderThicknessTop; + int _borderThicknessBottom; + + int _fadeInTime; + + int _alpha1; + int _alpha2; + bool _alphaTimeBased; + + bool _useRegion; + + char *_emitEvent; + BaseScriptHolder *_owner; + + PartForce *addForceByName(const Common::String &name); + int static compareZ(const void *obj1, const void *obj2); + bool initParticle(PartParticle *particle, uint32 currentTime, uint32 timerDelta); + bool updateInternal(uint32 currentTime, uint32 timerDelta); + uint32 _lastGenTime; + BaseArray<PartParticle *> _particles; + BaseArray<char *> _sprites; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_force.cpp b/engines/wintermute/base/particles/part_force.cpp new file mode 100644 index 0000000000..df84162504 --- /dev/null +++ b/engines/wintermute/base/particles/part_force.cpp @@ -0,0 +1,65 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/particles/part_force.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +PartForce::PartForce(BaseGame *inGame) : BaseNamedObject(inGame) { + _pos = Vector2(0.0f, 0.0f); + _direction = Vector2(0.0f, 0.0f); + _type = FORCE_POINT; +} + + +////////////////////////////////////////////////////////////////////////// +PartForce::~PartForce(void) { +} + + +////////////////////////////////////////////////////////////////////////// +bool PartForce::persist(BasePersistenceManager *persistMgr) { + if (persistMgr->getIsSaving()) { + const char *name = getName(); + persistMgr->transfer(TMEMBER(name)); + } else { + const char *name; + persistMgr->transfer(TMEMBER(name)); + setName(name); + } + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_direction)); + persistMgr->transfer(TMEMBER_INT(_type)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_force.h b/engines/wintermute/base/particles/part_force.h new file mode 100644 index 0000000000..27f4cb7d90 --- /dev/null +++ b/engines/wintermute/base/particles/part_force.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTFORCE_H +#define WINTERMUTE_PARTFORCE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/base_named_object.h" +#include "engines/wintermute/math/vector2.h" + +namespace Wintermute { + +class PartForce : public BaseNamedObject { +public: + enum TForceType { + FORCE_POINT, FORCE_GLOBAL + }; + + PartForce(BaseGame *inGame); + virtual ~PartForce(void); + + Vector2 _pos; + Vector2 _direction; + TForceType _type; + + bool persist(BasePersistenceManager *PersistMgr); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/particles/part_particle.cpp b/engines/wintermute/base/particles/part_particle.cpp new file mode 100644 index 0000000000..0b850d9618 --- /dev/null +++ b/engines/wintermute/base/particles/part_particle.cpp @@ -0,0 +1,269 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/particles/part_particle.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +PartParticle::PartParticle(BaseGame *inGame) : BaseClass(inGame) { + _pos = Vector2(0.0f, 0.0f); + _posZ = 0.0f; + _velocity = Vector2(0.0f, 0.0f); + _scale = 100.0f; + _sprite = NULL; + _creationTime = 0; + _lifeTime = 0; + _isDead = true; + BasePlatform::setRectEmpty(&_border); + + _state = PARTICLE_NORMAL; + _fadeStart = 0; + _fadeTime = 0; + _currentAlpha = 255; + + _alpha1 = _alpha2 = 255; + + _rotation = 0.0f; + _angVelocity = 0.0f; + + _growthRate = 0.0f; + _exponentialGrowth = false; +} + + +////////////////////////////////////////////////////////////////////////// +PartParticle::~PartParticle(void) { + delete _sprite; + _sprite = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::setSprite(const Common::String &filename) { + if (_sprite && _sprite->getFilename() && scumm_stricmp(filename.c_str(), _sprite->getFilename()) == 0) { + _sprite->reset(); + return STATUS_OK; + } + + delete _sprite; + _sprite = NULL; + + SystemClassRegistry::getInstance()->_disabled = true; + _sprite = new BaseSprite(_gameRef, (BaseObject*)_gameRef); + if (_sprite && DID_SUCCEED(_sprite->loadFile(filename))) { + SystemClassRegistry::getInstance()->_disabled = false; + return STATUS_OK; + } else { + delete _sprite; + _sprite = NULL; + SystemClassRegistry::getInstance()->_disabled = false; + return STATUS_FAILED; + } + +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta) { + if (_state == PARTICLE_FADEIN) { + if (currentTime - _fadeStart >= (uint32)_fadeTime) { + _state = PARTICLE_NORMAL; + _currentAlpha = _alpha1; + } else { + _currentAlpha = (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _alpha1); + } + + return STATUS_OK; + } else if (_state == PARTICLE_FADEOUT) { + if (currentTime - _fadeStart >= (uint32)_fadeTime) { + _isDead = true; + return STATUS_OK; + } else { + _currentAlpha = _fadeStartAlpha - (int)(((float)currentTime - (float)_fadeStart) / (float)_fadeTime * _fadeStartAlpha); + } + + return STATUS_OK; + } else { + // time is up + if (_lifeTime > 0) { + if (currentTime - _creationTime >= (uint32)_lifeTime) { + if (emitter->_fadeOutTime > 0) { + fadeOut(currentTime, emitter->_fadeOutTime); + } else { + _isDead = true; + } + } + } + + // particle hit the border + if (!_isDead && !BasePlatform::isRectEmpty(&_border)) { + Point32 p; + p.x = (int32)_pos.x; + p.y = (int32)_pos.y; + if (!BasePlatform::ptInRect(&_border, p)) { + fadeOut(currentTime, emitter->_fadeOutTime); + } + } + if (_state != PARTICLE_NORMAL) { + return STATUS_OK; + } + + // update alpha + if (_lifeTime > 0) { + int age = (int)(currentTime - _creationTime); + int alphaDelta = (int)(_alpha2 - _alpha1); + + _currentAlpha = _alpha1 + (int)(((float)alphaDelta / (float)_lifeTime * (float)age)); + } + + // update position + float elapsedTime = (float)timerDelta / 1000.f; + + for (uint32 i = 0; i < emitter->_forces.size(); i++) { + PartForce *force = emitter->_forces[i]; + switch (force->_type) { + case PartForce::FORCE_GLOBAL: + _velocity += force->_direction * elapsedTime; + break; + + case PartForce::FORCE_POINT: { + Vector2 vecDist = force->_pos - _pos; + float dist = fabs(vecDist.length()); + + dist = 100.0f / dist; + + _velocity += force->_direction * dist * elapsedTime; + } + break; + } + } + _pos += _velocity * elapsedTime; + + // update rotation + _rotation += _angVelocity * elapsedTime; + _rotation = BaseUtils::normalizeAngle(_rotation); + + // update scale + if (_exponentialGrowth) { + _scale += _scale / 100.0f * _growthRate * elapsedTime; + } else { + _scale += _growthRate * elapsedTime; + } + + if (_scale <= 0.0f) { + _isDead = true; + } + + + return STATUS_OK; + } +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::display(PartEmitter *emitter) { + if (!_sprite) { + return STATUS_FAILED; + } + if (_isDead) { + return STATUS_OK; + } + + _sprite->getCurrentFrame(); + return _sprite->display((int)_pos.x, (int)_pos.y, + NULL, + _scale, _scale, + BYTETORGBA(255, 255, 255, _currentAlpha), + _rotation, + emitter->_blendMode); +} + + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::fadeIn(uint32 currentTime, int fadeTime) { + _currentAlpha = 0; + _fadeStart = currentTime; + _fadeTime = fadeTime; + _state = PARTICLE_FADEIN; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::fadeOut(uint32 currentTime, int fadeTime) { + //_currentAlpha = 255; + _fadeStartAlpha = _currentAlpha; + _fadeStart = currentTime; + _fadeTime = fadeTime; + _state = PARTICLE_FADEOUT; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool PartParticle::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_alpha1)); + persistMgr->transfer(TMEMBER(_alpha2)); + persistMgr->transfer(TMEMBER(_border)); + persistMgr->transfer(TMEMBER(_pos)); + persistMgr->transfer(TMEMBER(_posZ)); + persistMgr->transfer(TMEMBER(_velocity)); + persistMgr->transfer(TMEMBER(_scale)); + persistMgr->transfer(TMEMBER(_creationTime)); + persistMgr->transfer(TMEMBER(_lifeTime)); + persistMgr->transfer(TMEMBER(_isDead)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_fadeStart)); + persistMgr->transfer(TMEMBER(_fadeTime)); + persistMgr->transfer(TMEMBER(_currentAlpha)); + persistMgr->transfer(TMEMBER(_angVelocity)); + persistMgr->transfer(TMEMBER(_rotation)); + persistMgr->transfer(TMEMBER(_growthRate)); + persistMgr->transfer(TMEMBER(_exponentialGrowth)); + persistMgr->transfer(TMEMBER(_fadeStartAlpha)); + + if (persistMgr->getIsSaving()) { + const char *filename = _sprite->getFilename(); + persistMgr->transfer(TMEMBER(filename)); + } else { + char *filename; + persistMgr->transfer(TMEMBER(filename)); + SystemClassRegistry::getInstance()->_disabled = true; + setSprite(filename); + SystemClassRegistry::getInstance()->_disabled = false; + delete[] filename; + filename = NULL; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/particles/part_particle.h b/engines/wintermute/base/particles/part_particle.h new file mode 100644 index 0000000000..4b8c2f131e --- /dev/null +++ b/engines/wintermute/base/particles/part_particle.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PARTPARTICLE_H +#define WINTERMUTE_PARTPARTICLE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/math/rect32.h" +#include "engines/wintermute/math/vector2.h" + +namespace Wintermute { + +class PartEmitter; +class BaseSprite; +class BasePersistenceManager; + +class PartParticle : public BaseClass { +public: + enum TParticleState { + PARTICLE_NORMAL, PARTICLE_FADEIN, PARTICLE_FADEOUT + }; + + PartParticle(BaseGame *inGame); + virtual ~PartParticle(void); + + float _growthRate; + bool _exponentialGrowth; + + float _rotation; + float _angVelocity; + + int _alpha1; + int _alpha2; + + Rect32 _border; + Vector2 _pos; + float _posZ; + Vector2 _velocity; + float _scale; + BaseSprite *_sprite; + uint32 _creationTime; + int _lifeTime; + bool _isDead; + TParticleState _state; + + bool update(PartEmitter *emitter, uint32 currentTime, uint32 timerDelta); + bool display(PartEmitter *emitter); + + bool setSprite(const Common::String &filename); + + bool fadeIn(uint32 currentTime, int fadeTime); + bool fadeOut(uint32 currentTime, int fadeTime); + + bool persist(BasePersistenceManager *PersistMgr); +private: + uint32 _fadeStart; + int _fadeTime; + int _currentAlpha; + int _fadeStartAlpha; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/saveload.cpp b/engines/wintermute/base/saveload.cpp new file mode 100644 index 0000000000..12204e1b35 --- /dev/null +++ b/engines/wintermute/base/saveload.cpp @@ -0,0 +1,204 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_persistence_manager.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/saveload.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" // Temporary +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "common/savefile.h" +#include "common/config-manager.h" + +namespace Wintermute { + +bool SaveLoad::loadGame(const Common::String &filename, BaseGame *gameRef) { + gameRef->LOG(0, "Loading game '%s'...", filename.c_str()); + + bool ret; + + gameRef->_renderer->initSaveLoad(false); + + gameRef->_loadInProgress = true; + BasePersistenceManager *pm = new BasePersistenceManager(); + if (DID_SUCCEED(ret = pm->initLoad(filename))) { + //if (DID_SUCCEED(ret = cleanup())) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadTable(gameRef, pm))) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->loadInstances(gameRef, pm))) { + // Restore random-seed: + BaseEngine::instance().getRandomSource()->setSeed(pm->getDWORD()); + + // data initialization after load + SaveLoad::initAfterLoad(); + + gameRef->applyEvent("AfterLoad", true); + + gameRef->displayContent(true, false); + //_renderer->flip(); + } + } + } + + delete pm; + gameRef->_loadInProgress = false; + + gameRef->_renderer->endSaveLoad(); + + //_gameRef->LOG(0, "Load end %d", BaseUtils::GetUsedMemMB()); + // AdGame: + if (DID_SUCCEED(ret)) { + SystemClassRegistry::getInstance()->enumInstances(SaveLoad::afterLoadRegion, "AdRegion", NULL); + } + return ret; +} + +bool SaveLoad::saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef) { + Common::String filename = SaveLoad::getSaveSlotFilename(slot); + + gameRef->LOG(0, "Saving game '%s'...", filename.c_str()); + + gameRef->applyEvent("BeforeSave", true); + + bool ret; + + BasePersistenceManager *pm = new BasePersistenceManager(); + if (DID_SUCCEED(ret = pm->initSave(desc))) { + gameRef->_renderer->initSaveLoad(true, quickSave); // TODO: The original code inited the indicator before the conditionals + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveTable(gameRef, pm, quickSave))) { + if (DID_SUCCEED(ret = SystemClassRegistry::getInstance()->saveInstances(gameRef, pm, quickSave))) { + pm->putDWORD(BaseEngine::instance().getRandomSource()->getSeed()); + if (DID_SUCCEED(ret = pm->saveFile(filename))) { + ConfMan.setInt("most_recent_saveslot", slot); + } + } + } + } + + delete pm; + + gameRef->_renderer->endSaveLoad(); + + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool SaveLoad::initAfterLoad() { + SystemClassRegistry::getInstance()->enumInstances(afterLoadRegion, "BaseRegion", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadSubFrame, "BaseSubFrame", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadSound, "BaseSound", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadFont, "BaseFontTT", NULL); + SystemClassRegistry::getInstance()->enumInstances(afterLoadScript, "ScScript", NULL); + // AdGame: + SystemClassRegistry::getInstance()->enumInstances(afterLoadScene, "AdScene", NULL); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadScene(void *scene, void *data) { + ((AdScene *)scene)->afterLoad(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadRegion(void *region, void *data) { + ((BaseRegion *)region)->createRegion(); +} + + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadSubFrame(void *subframe, void *data) { + ((BaseSubFrame *)subframe)->setSurfaceSimple(); +} + + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadSound(void *sound, void *data) { + ((BaseSound *)sound)->setSoundSimple(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadFont(void *font, void *data) { + ((BaseFont *)font)->afterLoad(); +} + +////////////////////////////////////////////////////////////////////////// +void SaveLoad::afterLoadScript(void *script, void *data) { + ((ScScript *)script)->afterLoad(); +} + +Common::String SaveLoad::getSaveSlotFilename(int slot) { + BasePersistenceManager *pm = new BasePersistenceManager(); + Common::String filename = pm->getFilenameForSlot(slot); + delete pm; + debugC(kWintermuteDebugSaveGame, "getSaveSlotFileName(%d) = %s", slot, filename.c_str()); + return filename; +} + +bool SaveLoad::getSaveSlotDescription(int slot, char *buffer) { + buffer[0] = '\0'; + + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + if (!pm) { + return false; + } + + if (!(pm->initLoad(filename))) { + delete pm; + return false; + } + + strcpy(buffer, pm->_savedDescription); + delete pm; + + return true; +} + +bool SaveLoad::isSaveSlotUsed(int slot) { + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + bool ret = pm->getSaveExists(slot); + delete pm; + return ret; +} + +bool SaveLoad::emptySaveSlot(int slot) { + Common::String filename = getSaveSlotFilename(slot); + BasePersistenceManager *pm = new BasePersistenceManager(); + ((WintermuteEngine *)g_engine)->getSaveFileMan()->removeSavefile(pm->getFilenameForSlot(slot)); + delete pm; + return true; +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/saveload.h b/engines/wintermute/base/saveload.h new file mode 100644 index 0000000000..722f7a89b6 --- /dev/null +++ b/engines/wintermute/base/saveload.h @@ -0,0 +1,57 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SAVEGAME_H +#define WINTERMUTE_SAVEGAME_H + +#include "common/str.h" + +namespace Wintermute { +class BaseGame; +class SaveLoad { +public: + static bool emptySaveSlot(int slot); + static bool isSaveSlotUsed(int slot); + static bool getSaveSlotDescription(int slot, char *buffer); + static Common::String getSaveSlotFilename(int slot); + + static bool loadGame(const Common::String &filename, BaseGame *gameRef); + static bool saveGame(int slot, const char *desc, bool quickSave, BaseGame *gameRef); + static bool initAfterLoad(); + static void afterLoadScene(void *scene, void *data); + static void afterLoadRegion(void *region, void *data); +private: + static void afterLoadSubFrame(void *subframe, void *data); + static void afterLoadSound(void *sound, void *data); + static void afterLoadFont(void *font, void *data); + static void afterLoadScript(void *script, void *data); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/dcscript.h b/engines/wintermute/base/scriptables/dcscript.h new file mode 100644 index 0000000000..4aae897dc2 --- /dev/null +++ b/engines/wintermute/base/scriptables/dcscript.h @@ -0,0 +1,141 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCSCRIPT_H +#define WINTERMUTE_DCSCRIPT_H + +namespace Wintermute { + +#define SCRIPT_MAGIC 0xDEC0ADDE +#define SCRIPT_VERSION 0x0102 + +// value types +typedef enum { + VAL_NULL, + VAL_STRING, + VAL_INT, + VAL_BOOL, + VAL_FLOAT, + VAL_OBJECT, + VAL_NATIVE, + VAL_VARIABLE_REF +} TValType; + + +// script states +typedef enum { + SCRIPT_RUNNING, + SCRIPT_WAITING, + SCRIPT_SLEEPING, + SCRIPT_FINISHED, + SCRIPT_PERSISTENT, + SCRIPT_ERROR, + SCRIPT_PAUSED, + SCRIPT_WAITING_SCRIPT, + SCRIPT_THREAD_FINISHED +} TScriptState; + +// opcodes +typedef enum { + II_DEF_VAR = 0, + II_DEF_GLOB_VAR, + II_RET, + II_RET_EVENT, + II_CALL, + II_CALL_BY_EXP, + II_EXTERNAL_CALL, + II_SCOPE, + II_CORRECT_STACK, + II_CREATE_OBJECT, + II_POP_EMPTY, + II_PUSH_VAR, + II_PUSH_VAR_REF, + II_POP_VAR, + II_PUSH_VAR_THIS, // push current this on stack + II_PUSH_INT, + II_PUSH_BOOL, + II_PUSH_FLOAT, + II_PUSH_STRING, + II_PUSH_NULL, + II_PUSH_THIS_FROM_STACK, + II_PUSH_THIS, + II_POP_THIS, + II_PUSH_BY_EXP, + II_POP_BY_EXP, + II_JMP, + II_JMP_FALSE, + II_ADD, + II_SUB, + II_MUL, + II_DIV, + II_MODULO, + II_NOT, + II_AND, + II_OR, + II_CMP_EQ, + II_CMP_NE, + II_CMP_L, + II_CMP_G, + II_CMP_LE, + II_CMP_GE, + II_CMP_STRICT_EQ, + II_CMP_STRICT_NE, + II_DBG_LINE, + II_POP_REG1, + II_PUSH_REG1, + II_DEF_CONST_VAR +} TInstruction; + +// external data types +typedef enum { + TYPE_VOID = 0, + TYPE_BOOL, + TYPE_LONG, + TYPE_BYTE, + TYPE_STRING, + TYPE_FLOAT, + TYPE_DOUBLE, + TYPE_MEMBUFFER +} TExternalType; + + +// call types +typedef enum { + CALL_STDCALL = 0, + CALL_CDECL, + CALL_THISCALL +} TCallType; + +// element types +typedef enum { + ELEMENT_STRING = 0 +} TElementType; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script.cpp b/engines/wintermute/base/scriptables/script.cpp new file mode 100644 index 0000000000..9469bd46a7 --- /dev/null +++ b/engines/wintermute/base/scriptables/script.cpp @@ -0,0 +1,1467 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "common/memstream.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScScript, false) + +////////////////////////////////////////////////////////////////////////// +ScScript::ScScript(BaseGame *inGame, ScEngine *engine) : BaseClass(inGame) { + _buffer = NULL; + _bufferSize = _iP = 0; + _scriptStream = NULL; + _filename = NULL; + _currentLine = 0; + + _symbols = NULL; + _numSymbols = 0; + + _engine = engine; + + _globals = NULL; + + _scopeStack = NULL; + _callStack = NULL; + _thisStack = NULL; + _stack = NULL; + + _operand = NULL; + _reg1 = NULL; + + _functions = NULL; + _numFunctions = 0; + + _methods = NULL; + _numMethods = 0; + + _events = NULL; + _numEvents = 0; + + _externals = NULL; + _numExternals = 0; + + _state = SCRIPT_FINISHED; + _origState = SCRIPT_FINISHED; + + _waitObject = NULL; + _waitTime = 0; + _waitFrozen = false; + _waitScript = NULL; + + _timeSlice = 0; + + _thread = false; + _methodThread = false; + _threadEvent = NULL; + + _freezable = true; + _owner = NULL; + + _unbreakable = false; + _parentScript = NULL; + + _tracingMode = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript::~ScScript() { + cleanup(); +} + +void ScScript::readHeader() { + uint32 oldPos = _scriptStream->pos(); + _scriptStream->seek(0); + _header.magic = _scriptStream->readUint32LE(); + _header.version = _scriptStream->readUint32LE(); + _header.codeStart = _scriptStream->readUint32LE(); + _header.funcTable = _scriptStream->readUint32LE(); + _header.symbolTable = _scriptStream->readUint32LE(); + _header.eventTable = _scriptStream->readUint32LE(); + _header.externalsTable = _scriptStream->readUint32LE(); + _header.methodTable = _scriptStream->readUint32LE(); + _scriptStream->seek(oldPos); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::initScript() { + if (!_scriptStream) { + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + } + readHeader(); + + if (_header.magic != SCRIPT_MAGIC) { + _gameRef->LOG(0, "File '%s' is not a valid compiled script", _filename); + cleanup(); + return STATUS_FAILED; + } + + if (_header.version > SCRIPT_VERSION) { + _gameRef->LOG(0, "Script '%s' has a wrong version %d.%d (expected %d.%d)", _filename, _header.version / 256, _header.version % 256, SCRIPT_VERSION / 256, SCRIPT_VERSION % 256); + cleanup(); + return STATUS_FAILED; + } + + initTables(); + + // init stacks + _scopeStack = new ScStack(_gameRef); + _callStack = new ScStack(_gameRef); + _thisStack = new ScStack(_gameRef); + _stack = new ScStack(_gameRef); + + _operand = new ScValue(_gameRef); + _reg1 = new ScValue(_gameRef); + + + // skip to the beginning + _iP = _header.codeStart; + _scriptStream->seek(_iP); + _currentLine = 0; + + // ready to rumble... + _state = SCRIPT_RUNNING; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::initTables() { + uint32 origIP = _iP; + + readHeader(); + // load symbol table + _iP = _header.symbolTable; + + _numSymbols = getDWORD(); + _symbols = new char*[_numSymbols]; + for (uint32 i = 0; i < _numSymbols; i++) { + uint32 index = getDWORD(); + _symbols[index] = getString(); + } + + // load functions table + _iP = _header.funcTable; + + _numFunctions = getDWORD(); + _functions = new TFunctionPos[_numFunctions]; + for (uint32 i = 0; i < _numFunctions; i++) { + _functions[i].pos = getDWORD(); + _functions[i].name = getString(); + } + + + // load events table + _iP = _header.eventTable; + + _numEvents = getDWORD(); + _events = new TEventPos[_numEvents]; + for (uint32 i = 0; i < _numEvents; i++) { + _events[i].pos = getDWORD(); + _events[i].name = getString(); + } + + + // load externals + if (_header.version >= 0x0101) { + _iP = _header.externalsTable; + + _numExternals = getDWORD(); + _externals = new TExternalFunction[_numExternals]; + for (uint32 i = 0; i < _numExternals; i++) { + _externals[i].dll_name = getString(); + _externals[i].name = getString(); + _externals[i].call_type = (TCallType)getDWORD(); + _externals[i].returns = (TExternalType)getDWORD(); + _externals[i].nu_params = getDWORD(); + if (_externals[i].nu_params > 0) { + _externals[i].params = new TExternalType[_externals[i].nu_params]; + for (int j = 0; j < _externals[i].nu_params; j++) { + _externals[i].params[j] = (TExternalType)getDWORD(); + } + } + } + } + + // load method table + _iP = _header.methodTable; + + _numMethods = getDWORD(); + _methods = new TMethodPos[_numMethods]; + for (uint32 i = 0; i < _numMethods; i++) { + _methods[i].pos = getDWORD(); + _methods[i].name = getString(); + } + + + _iP = origIP; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner) { + cleanup(); + + _thread = false; + _methodThread = false; + + delete[] _threadEvent; + _threadEvent = NULL; + + _filename = new char[strlen(filename) + 1]; + if (_filename) { + strcpy(_filename, filename); + } + + _buffer = new byte [size]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, buffer, size); + + _bufferSize = size; + + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // establish global variables table + _globals = new ScValue(_gameRef); + + _owner = owner; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::createThread(ScScript *original, uint32 initIP, const Common::String &eventName) { + cleanup(); + + _thread = true; + _methodThread = false; + _threadEvent = new char[eventName.size() + 1]; + if (_threadEvent) { + strcpy(_threadEvent, eventName.c_str()); + } + + // copy filename + _filename = new char[strlen(original->_filename) + 1]; + if (_filename) { + strcpy(_filename, original->_filename); + } + + // copy buffer + _buffer = new byte [original->_bufferSize]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, original->_buffer, original->_bufferSize); + _bufferSize = original->_bufferSize; + + // initialize + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // copy globals + _globals = original->_globals; + + // skip to the beginning of the event + _iP = initIP; + _scriptStream->seek(_iP); + + _timeSlice = original->_timeSlice; + _freezable = original->_freezable; + _owner = original->_owner; + + _engine = original->_engine; + _parentScript = original; + + return STATUS_OK; +} + + + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::createMethodThread(ScScript *original, const Common::String &methodName) { + uint32 ip = original->getMethodPos(methodName); + if (ip == 0) { + return STATUS_FAILED; + } + + cleanup(); + + _thread = true; + _methodThread = true; + _threadEvent = new char[methodName.size() + 1]; + if (_threadEvent) { + strcpy(_threadEvent, methodName.c_str()); + } + + // copy filename + _filename = new char[strlen(original->_filename) + 1]; + if (_filename) { + strcpy(_filename, original->_filename); + } + + // copy buffer + _buffer = new byte [original->_bufferSize]; + if (!_buffer) { + return STATUS_FAILED; + } + + memcpy(_buffer, original->_buffer, original->_bufferSize); + _bufferSize = original->_bufferSize; + + // initialize + bool res = initScript(); + if (DID_FAIL(res)) { + return res; + } + + // copy globals + _globals = original->_globals; + + // skip to the beginning of the event + _iP = ip; + + _timeSlice = original->_timeSlice; + _freezable = original->_freezable; + _owner = original->_owner; + + _engine = original->_engine; + _parentScript = original; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void ScScript::cleanup() { + if (_buffer) { + delete[] _buffer; + } + _buffer = NULL; + + if (_filename) { + delete[] _filename; + } + _filename = NULL; + + if (_symbols) { + delete[] _symbols; + } + _symbols = NULL; + _numSymbols = 0; + + if (_globals && !_thread) { + delete _globals; + } + _globals = NULL; + + delete _scopeStack; + _scopeStack = NULL; + + delete _callStack; + _callStack = NULL; + + delete _thisStack; + _thisStack = NULL; + + delete _stack; + _stack = NULL; + + if (_functions) { + delete[] _functions; + } + _functions = NULL; + _numFunctions = 0; + + if (_methods) { + delete[] _methods; + } + _methods = NULL; + _numMethods = 0; + + if (_events) { + delete[] _events; + } + _events = NULL; + _numEvents = 0; + + + if (_externals) { + for (uint32 i = 0; i < _numExternals; i++) { + if (_externals[i].nu_params > 0) { + delete[] _externals[i].params; + } + } + delete[] _externals; + } + _externals = NULL; + _numExternals = 0; + + delete _operand; + delete _reg1; + _operand = NULL; + _reg1 = NULL; + + delete[] _threadEvent; + _threadEvent = NULL; + + _state = SCRIPT_FINISHED; + + _waitObject = NULL; + _waitTime = 0; + _waitFrozen = false; + _waitScript = NULL; + + _parentScript = NULL; // ref only + + delete _scriptStream; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getDWORD() { + _scriptStream->seek((int32)_iP); + uint32 ret = _scriptStream->readUint32LE(); + _iP += sizeof(uint32); +// assert(oldRet == ret); + return ret; +} + +////////////////////////////////////////////////////////////////////////// +double ScScript::getFloat() { + _scriptStream->seek((int32)_iP); + byte buffer[8]; + _scriptStream->read(buffer, 8); + +#ifdef SCUMM_BIG_ENDIAN + // TODO: For lack of a READ_LE_UINT64 + SWAP(buffer[0], buffer[7]); + SWAP(buffer[1], buffer[6]); + SWAP(buffer[2], buffer[5]); + SWAP(buffer[3], buffer[4]); +#endif + + double ret = *(double *)(buffer); + _iP += 8; // Hardcode the double-size used originally. + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +char *ScScript::getString() { + char *ret = (char *)(_buffer + _iP); + while (*(char *)(_buffer + _iP) != '\0') { + _iP++; + } + _iP++; // string terminator + _scriptStream->seek(_iP); + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::executeInstruction() { + bool ret = STATUS_OK; + + uint32 dw; + const char *str = NULL; + + //ScValue* op = new ScValue(_gameRef); + _operand->cleanup(); + + ScValue *op1; + ScValue *op2; + + uint32 inst = getDWORD(); + switch (inst) { + + case II_DEF_VAR: + _operand->setNULL(); + dw = getDWORD(); + if (_scopeStack->_sP < 0) { + _globals->setProp(_symbols[dw], _operand); + } else { + _scopeStack->getTop()->setProp(_symbols[dw], _operand); + } + + break; + + case II_DEF_GLOB_VAR: + case II_DEF_CONST_VAR: { + dw = getDWORD(); + /* char *temp = _symbols[dw]; // TODO delete */ + // only create global var if it doesn't exist + if (!_engine->_globals->propExists(_symbols[dw])) { + _operand->setNULL(); + _engine->_globals->setProp(_symbols[dw], _operand, false, inst == II_DEF_CONST_VAR); + } + break; + } + + case II_RET: + if (_scopeStack->_sP >= 0 && _callStack->_sP >= 0) { + _scopeStack->pop(); + _iP = (uint32)_callStack->pop()->getInt(); + } else { + if (_thread) { + _state = SCRIPT_THREAD_FINISHED; + } else { + if (_numEvents == 0 && _numMethods == 0) { + _state = SCRIPT_FINISHED; + } else { + _state = SCRIPT_PERSISTENT; + } + } + } + + break; + + case II_RET_EVENT: + _state = SCRIPT_FINISHED; + break; + + + case II_CALL: + dw = getDWORD(); + + _operand->setInt(_iP); + _callStack->push(_operand); + + _iP = dw; + + break; + + case II_CALL_BY_EXP: { + // push var + // push string + str = _stack->pop()->getString(); + char *methodName = new char[strlen(str) + 1]; + strcpy(methodName, str); + + ScValue *var = _stack->pop(); + if (var->_type == VAL_VARIABLE_REF) { + var = var->_valRef; + } + + bool res = STATUS_FAILED; + bool triedNative = false; + + // we are already calling this method, try native + if (_thread && _methodThread && strcmp(methodName, _threadEvent) == 0 && var->_type == VAL_NATIVE && _owner == var->getNative()) { + triedNative = true; + res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName); + } + + if (DID_FAIL(res)) { + if (var->isNative() && var->getNative()->canHandleMethod(methodName)) { + if (!_unbreakable) { + _waitScript = var->getNative()->invokeMethodThread(methodName); + if (!_waitScript) { + _stack->correctParams(0); + runtimeError("Error invoking method '%s'.", methodName); + _stack->pushNULL(); + } else { + _state = SCRIPT_WAITING_SCRIPT; + _waitScript->copyParameters(_stack); + } + } else { + // can call methods in unbreakable mode + _stack->correctParams(0); + runtimeError("Cannot call method '%s'. Ignored.", methodName); + _stack->pushNULL(); + } + delete[] methodName; + break; + } + /* + ScValue* val = var->getProp(MethodName); + if (val){ + dw = GetFuncPos(val->getString()); + if (dw==0){ + TExternalFunction* f = GetExternal(val->getString()); + if (f){ + ExternalCall(_stack, _thisStack, f); + } + else{ + // not an internal nor external, try for native function + _gameRef->ExternalCall(this, _stack, _thisStack, val->getString()); + } + } + else{ + _operand->setInt(_iP); + _callStack->Push(_operand); + _iP = dw; + } + } + */ + else { + res = STATUS_FAILED; + if (var->_type == VAL_NATIVE && !triedNative) { + res = var->_valNative->scCallMethod(this, _stack, _thisStack, methodName); + } + + if (DID_FAIL(res)) { + _stack->correctParams(0); + runtimeError("Call to undefined method '%s'. Ignored.", methodName); + _stack->pushNULL(); + } + } + } + delete[] methodName; + } + break; + + case II_EXTERNAL_CALL: { + uint32 symbolIndex = getDWORD(); + + TExternalFunction *f = getExternal(_symbols[symbolIndex]); + if (f) { + externalCall(_stack, _thisStack, f); + } else { + _gameRef->externalCall(this, _stack, _thisStack, _symbols[symbolIndex]); + } + + break; + } + case II_SCOPE: + _operand->setNULL(); + _scopeStack->push(_operand); + break; + + case II_CORRECT_STACK: + dw = getDWORD(); // params expected + _stack->correctParams(dw); + break; + + case II_CREATE_OBJECT: + _operand->setObject(); + _stack->push(_operand); + break; + + case II_POP_EMPTY: + _stack->pop(); + break; + + case II_PUSH_VAR: { + ScValue *var = getVar(_symbols[getDWORD()]); + if (false && /*var->_type==VAL_OBJECT ||*/ var->_type == VAL_NATIVE) { + _operand->setReference(var); + _stack->push(_operand); + } else { + _stack->push(var); + } + break; + } + + case II_PUSH_VAR_REF: { + ScValue *var = getVar(_symbols[getDWORD()]); + _operand->setReference(var); + _stack->push(_operand); + break; + } + + case II_POP_VAR: { + char *varName = _symbols[getDWORD()]; + ScValue *var = getVar(varName); + if (var) { + ScValue *val = _stack->pop(); + if (!val) { + runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum."); + var->setNULL(); + } else { + if (val->getType() == VAL_VARIABLE_REF) { + val = val->_valRef; + } + if (val->_type == VAL_NATIVE) { + var->setValue(val); + } else { + var->copy(val); + } + } + } + + break; + } + + case II_PUSH_VAR_THIS: + _stack->push(_thisStack->getTop()); + break; + + case II_PUSH_INT: + _stack->pushInt((int)getDWORD()); + break; + + case II_PUSH_FLOAT: + _stack->pushFloat(getFloat()); + break; + + + case II_PUSH_BOOL: + _stack->pushBool(getDWORD() != 0); + + break; + + case II_PUSH_STRING: + _stack->pushString(getString()); + break; + + case II_PUSH_NULL: + _stack->pushNULL(); + break; + + case II_PUSH_THIS_FROM_STACK: + _operand->setReference(_stack->getTop()); + _thisStack->push(_operand); + break; + + case II_PUSH_THIS: + _operand->setReference(getVar(_symbols[getDWORD()])); + _thisStack->push(_operand); + break; + + case II_POP_THIS: + _thisStack->pop(); + break; + + case II_PUSH_BY_EXP: { + str = _stack->pop()->getString(); + ScValue *val = _stack->pop()->getProp(str); + if (val) { + _stack->push(val); + } else { + _stack->pushNULL(); + } + + break; + } + + case II_POP_BY_EXP: { + str = _stack->pop()->getString(); + ScValue *var = _stack->pop(); + ScValue *val = _stack->pop(); + + if (val == NULL) { + runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum."); + var->setNULL(); + } else { + var->setProp(str, val); + } + + break; + } + + case II_PUSH_REG1: + _stack->push(_reg1); + break; + + case II_POP_REG1: + _reg1->copy(_stack->pop()); + break; + + case II_JMP: + _iP = getDWORD(); + break; + + case II_JMP_FALSE: { + dw = getDWORD(); + //if (!_stack->pop()->getBool()) _iP = dw; + ScValue *val = _stack->pop(); + if (!val) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + } else { + if (!val->getBool()) { + _iP = dw; + } + } + break; + } + + case II_ADD: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_STRING || op2->getType() == VAL_STRING) { + char *tempStr = new char [strlen(op1->getString()) + strlen(op2->getString()) + 1]; + strcpy(tempStr, op1->getString()); + strcat(tempStr, op2->getString()); + _operand->setString(tempStr); + delete[] tempStr; + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() + op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() + op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_SUB: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() - op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() - op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_MUL: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op1->isNULL() || op2->isNULL()) { + _operand->setNULL(); + } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT) { + _operand->setInt(op1->getInt() * op2->getInt()); + } else { + _operand->setFloat(op1->getFloat() * op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_DIV: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op2->getFloat() == 0.0f) { + runtimeError("Division by zero."); + } + + if (op1->isNULL() || op2->isNULL() || op2->getFloat() == 0.0f) { + _operand->setNULL(); + } else { + _operand->setFloat(op1->getFloat() / op2->getFloat()); + } + + _stack->push(_operand); + + break; + + case II_MODULO: + op2 = _stack->pop(); + op1 = _stack->pop(); + + if (op2->getInt() == 0) { + runtimeError("Division by zero."); + } + + if (op1->isNULL() || op2->isNULL() || op2->getInt() == 0) { + _operand->setNULL(); + } else { + _operand->setInt(op1->getInt() % op2->getInt()); + } + + _stack->push(_operand); + + break; + + case II_NOT: + op1 = _stack->pop(); + //if (op1->isNULL()) _operand->setNULL(); + if (op1->isNULL()) { + _operand->setBool(true); + } else { + _operand->setBool(!op1->getBool()); + } + _stack->push(_operand); + + break; + + case II_AND: + op2 = _stack->pop(); + op1 = _stack->pop(); + if (op1 == NULL || op2 == NULL) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + _operand->setBool(false); + } else { + _operand->setBool(op1->getBool() && op2->getBool()); + } + _stack->push(_operand); + break; + + case II_OR: + op2 = _stack->pop(); + op1 = _stack->pop(); + if (op1 == NULL || op2 == NULL) { + runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?"); + _operand->setBool(false); + } else { + _operand->setBool(op1->getBool() || op2->getBool()); + } + _stack->push(_operand); + break; + + case II_CMP_EQ: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(false); + else if (op1->isNative() && op2->isNative()){ + _operand->setBool(op1->getNative() == op2->getNative()); + } + else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){ + _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())==0); + } + else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() == op2->getFloat()); + } + else{ + _operand->setBool(op1->getInt() == op2->getInt()); + } + */ + + _operand->setBool(ScValue::compare(op1, op2) == 0); + _stack->push(_operand); + break; + + case II_CMP_NE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if ((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(true); + else if (op1->isNative() && op2->isNative()){ + _operand->setBool(op1->getNative() != op2->getNative()); + } + else if (op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){ + _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())!=0); + } + else if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() != op2->getFloat()); + } + else{ + _operand->setBool(op1->getInt() != op2->getInt()); + } + */ + + _operand->setBool(ScValue::compare(op1, op2) != 0); + _stack->push(_operand); + break; + + case II_CMP_L: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() < op2->getFloat()); + } + else _operand->setBool(op1->getInt() < op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) < 0); + _stack->push(_operand); + break; + + case II_CMP_G: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() > op2->getFloat()); + } + else _operand->setBool(op1->getInt() > op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) > 0); + _stack->push(_operand); + break; + + case II_CMP_LE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() <= op2->getFloat()); + } + else _operand->setBool(op1->getInt() <= op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) <= 0); + _stack->push(_operand); + break; + + case II_CMP_GE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + /* + if (op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){ + _operand->setBool(op1->getFloat() >= op2->getFloat()); + } + else _operand->setBool(op1->getInt() >= op2->getInt()); + */ + + _operand->setBool(ScValue::compare(op1, op2) >= 0); + _stack->push(_operand); + break; + + case II_CMP_STRICT_EQ: + op2 = _stack->pop(); + op1 = _stack->pop(); + + //_operand->setBool(op1->getType()==op2->getType() && op1->getFloat()==op2->getFloat()); + _operand->setBool(ScValue::compareStrict(op1, op2) == 0); + _stack->push(_operand); + + break; + + case II_CMP_STRICT_NE: + op2 = _stack->pop(); + op1 = _stack->pop(); + + //_operand->setBool(op1->getType()!=op2->getType() || op1->getFloat()!=op2->getFloat()); + _operand->setBool(ScValue::compareStrict(op1, op2) != 0); + _stack->push(_operand); + break; + + case II_DBG_LINE: { + int newLine = getDWORD(); + if (newLine != _currentLine) { + _currentLine = newLine; + } + break; + + } + default: + _gameRef->LOG(0, "Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\n", inst, _filename, _currentLine, _iP - sizeof(uint32)); + _state = SCRIPT_FINISHED; + ret = STATUS_FAILED; + } // switch(instruction) + + //delete op; + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getFuncPos(const Common::String &name) { + for (uint32 i = 0; i < _numFunctions; i++) { + if (name == _functions[i].name) { + return _functions[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getMethodPos(const Common::String &name) { + for (uint32 i = 0; i < _numMethods; i++) { + if (name == _methods[i].name) { + return _methods[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScScript::getVar(char *name) { + ScValue *ret = NULL; + + // scope locals + if (_scopeStack->_sP >= 0) { + if (_scopeStack->getTop()->propExists(name)) { + ret = _scopeStack->getTop()->getProp(name); + } + } + + // script globals + if (ret == NULL) { + if (_globals->propExists(name)) { + ret = _globals->getProp(name); + } + } + + // engine globals + if (ret == NULL) { + if (_engine->_globals->propExists(name)) { + ret = _engine->_globals->getProp(name); + } + } + + if (ret == NULL) { + //RuntimeError("Variable '%s' is inaccessible in the current block. Consider changing the script.", name); + _gameRef->LOG(0, "Warning: variable '%s' is inaccessible in the current block. Consider changing the script (script:%s, line:%d)", name, _filename, _currentLine); + ScValue *val = new ScValue(_gameRef); + ScValue *scope = _scopeStack->getTop(); + if (scope) { + scope->setProp(name, val); + ret = _scopeStack->getTop()->getProp(name); + } else { + _globals->setProp(name, val); + ret = _globals->getProp(name); + } + delete val; + } + + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::waitFor(BaseObject *object) { + if (_unbreakable) { + runtimeError("Script cannot be interrupted."); + return STATUS_OK; + } + + _state = SCRIPT_WAITING; + _waitObject = object; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::waitForExclusive(BaseObject *object) { + _engine->resetObject(object); + return waitFor(object); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::sleep(uint32 duration) { + if (_unbreakable) { + runtimeError("Script cannot be interrupted."); + return STATUS_OK; + } + + _state = SCRIPT_SLEEPING; + if (_gameRef->_state == GAME_FROZEN) { + _waitTime = g_system->getMillis() + duration; + _waitFrozen = true; + } else { + _waitTime = _gameRef->_timer + duration; + _waitFrozen = false; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::finish(bool includingThreads) { + if (_state != SCRIPT_FINISHED && includingThreads) { + _state = SCRIPT_FINISHED; + finishThreads(); + } else { + _state = SCRIPT_FINISHED; + } + + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::run() { + _state = SCRIPT_RUNNING; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////// +void ScScript::runtimeError(const char *fmt, ...) { + char buff[256]; + va_list va; + + va_start(va, fmt); + vsprintf(buff, fmt, va); + va_end(va); + + _gameRef->LOG(0, "Runtime error. Script '%s', line %d", _filename, _currentLine); + _gameRef->LOG(0, " %s", buff); + + if (!_gameRef->_suppressScriptErrors) { + _gameRef->quickMessage("Script runtime error. View log for details."); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + // buffer + if (persistMgr->getIsSaving()) { + if (_state != SCRIPT_PERSISTENT && _state != SCRIPT_FINISHED && _state != SCRIPT_THREAD_FINISHED) { + persistMgr->transfer(TMEMBER(_bufferSize)); + persistMgr->putBytes(_buffer, _bufferSize); + } else { + // don't save idle/finished scripts + int bufferSize = 0; + persistMgr->transfer(TMEMBER(bufferSize)); + } + } else { + persistMgr->transfer(TMEMBER(_bufferSize)); + if (_bufferSize > 0) { + _buffer = new byte[_bufferSize]; + persistMgr->getBytes(_buffer, _bufferSize); + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + initTables(); + } else { + _buffer = NULL; + _scriptStream = NULL; + } + } + + persistMgr->transfer(TMEMBER(_callStack)); + persistMgr->transfer(TMEMBER(_currentLine)); + persistMgr->transfer(TMEMBER(_engine)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_freezable)); + persistMgr->transfer(TMEMBER(_globals)); + persistMgr->transfer(TMEMBER(_iP)); + persistMgr->transfer(TMEMBER(_scopeStack)); + persistMgr->transfer(TMEMBER(_stack)); + persistMgr->transfer(TMEMBER_INT(_state)); + persistMgr->transfer(TMEMBER(_operand)); + persistMgr->transfer(TMEMBER_INT(_origState)); + persistMgr->transfer(TMEMBER(_owner)); + persistMgr->transfer(TMEMBER(_reg1)); + persistMgr->transfer(TMEMBER(_thread)); + persistMgr->transfer(TMEMBER(_threadEvent)); + persistMgr->transfer(TMEMBER(_thisStack)); + persistMgr->transfer(TMEMBER(_timeSlice)); + persistMgr->transfer(TMEMBER(_waitObject)); + persistMgr->transfer(TMEMBER(_waitScript)); + persistMgr->transfer(TMEMBER(_waitTime)); + persistMgr->transfer(TMEMBER(_waitFrozen)); + + persistMgr->transfer(TMEMBER(_methodThread)); + persistMgr->transfer(TMEMBER(_methodThread)); + persistMgr->transfer(TMEMBER(_unbreakable)); + persistMgr->transfer(TMEMBER(_parentScript)); + + if (!persistMgr->getIsSaving()) { + _tracingMode = false; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *ScScript::invokeEventHandler(const Common::String &eventName, bool unbreakable) { + //if (_state!=SCRIPT_PERSISTENT) return NULL; + + uint32 pos = getEventPos(eventName); + if (!pos) { + return NULL; + } + + ScScript *thread = new ScScript(_gameRef, _engine); + if (thread) { + bool ret = thread->createThread(this, pos, eventName); + if (DID_SUCCEED(ret)) { + thread->_unbreakable = unbreakable; + _engine->_scripts.add(thread); + return thread; + } else { + delete thread; + return NULL; + } + } else { + return NULL; + } + +} + + +////////////////////////////////////////////////////////////////////////// +uint32 ScScript::getEventPos(const Common::String &name) { + for (int i = _numEvents - 1; i >= 0; i--) { + if (scumm_stricmp(name.c_str(), _events[i].name) == 0) { + return _events[i].pos; + } + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::canHandleEvent(const Common::String &eventName) { + return getEventPos(eventName) != 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::canHandleMethod(const Common::String &methodName) { + return getMethodPos(methodName) != 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::pause() { + if (_state == SCRIPT_PAUSED) { + _gameRef->LOG(0, "Attempting to pause a paused script ('%s', line %d)", _filename, _currentLine); + return STATUS_FAILED; + } + + if (!_freezable || _state == SCRIPT_PERSISTENT) { + return STATUS_OK; + } + + _origState = _state; + _state = SCRIPT_PAUSED; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::resume() { + if (_state != SCRIPT_PAUSED) { + return STATUS_OK; + } + + _state = _origState; + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +ScScript::TExternalFunction *ScScript::getExternal(char *name) { + for (uint32 i = 0; i < _numExternals; i++) { + if (strcmp(name, _externals[i].name) == 0) { + return &_externals[i]; + } + } + return NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function) { + + _gameRef->LOG(0, "External functions are not supported on this platform."); + stack->correctParams(0); + stack->pushNULL(); + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::copyParameters(ScStack *stack) { + int i; + int numParams = stack->pop()->getInt(); + for (i = numParams - 1; i >= 0; i--) { + _stack->push(stack->getAt(i)); + } + _stack->pushInt(numParams); + + for (i = 0; i < numParams; i++) { + stack->pop(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScScript::finishThreads() { + for (uint32 i = 0; i < _engine->_scripts.size(); i++) { + ScScript *scr = _engine->_scripts[i]; + if (scr->_thread && scr->_state != SCRIPT_FINISHED && scr->_owner == _owner && scumm_stricmp(scr->_filename, _filename) == 0) { + scr->finish(true); + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// IWmeDebugScript interface implementation +int ScScript::dbgGetLine() { + return _currentLine; +} + +////////////////////////////////////////////////////////////////////////// +const char *ScScript::dbgGetFilename() { + return _filename; +} + +////////////////////////////////////////////////////////////////////////// +void ScScript::afterLoad() { + if (_buffer == NULL) { + byte *buffer = _engine->getCompiledScript(_filename, &_bufferSize); + if (!buffer) { + _gameRef->LOG(0, "Error reinitializing script '%s' after load. Script will be terminated.", _filename); + _state = SCRIPT_ERROR; + return; + } + + _buffer = new byte [_bufferSize]; + memcpy(_buffer, buffer, _bufferSize); + + delete _scriptStream; + _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize); + + initTables(); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script.h b/engines/wintermute/base/scriptables/script.h new file mode 100644 index 0000000000..0616bce58a --- /dev/null +++ b/engines/wintermute/base/scriptables/script.h @@ -0,0 +1,174 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCSCRIPT_H +#define WINTERMUTE_SCSCRIPT_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView +#include "engines/wintermute/coll_templ.h" + +namespace Wintermute { +class BaseScriptHolder; +class BaseObject; +class ScEngine; +class ScStack; +class ScScript : public BaseClass { +public: + BaseArray<int> _breakpoints; + bool _tracingMode; + + ScScript *_parentScript; + bool _unbreakable; + bool finishThreads(); + bool copyParameters(ScStack *stack); + + void afterLoad(); +private: + ScValue *_operand; + ScValue *_reg1; +public: + bool _freezable; + bool resume(); + bool pause(); + bool canHandleEvent(const Common::String &eventName); + bool canHandleMethod(const Common::String &methodName); + bool createThread(ScScript *original, uint32 initIP, const Common::String &eventName); + bool createMethodThread(ScScript *original, const Common::String &methodName); + ScScript *invokeEventHandler(const Common::String &eventName, bool unbreakable = false); + uint32 _timeSlice; + DECLARE_PERSISTENT(ScScript, BaseClass) + void runtimeError(const char *fmt, ...); + bool run(); + bool finish(bool includingThreads = false); + bool sleep(uint32 duration); + bool waitForExclusive(BaseObject *object); + bool waitFor(BaseObject *object); + uint32 _waitTime; + bool _waitFrozen; + BaseObject *_waitObject; + ScScript *_waitScript; + TScriptState _state; + TScriptState _origState; + ScValue *getVar(char *name); + uint32 getFuncPos(const Common::String &name); + uint32 getEventPos(const Common::String &name); + uint32 getMethodPos(const Common::String &name); + typedef struct { + uint32 magic; + uint32 version; + uint32 codeStart; + uint32 funcTable; + uint32 symbolTable; + uint32 eventTable; + uint32 externalsTable; + uint32 methodTable; + } TScriptHeader; + + TScriptHeader _header; + + typedef struct { + char *name; + uint32 pos; + } TFunctionPos; + + typedef struct { + char *name; + uint32 pos; + } TMethodPos; + + typedef struct { + char *name; + uint32 pos; + } TEventPos; + + typedef struct { + char *name; + char *dll_name; + TCallType call_type; + TExternalType returns; + int nu_params; + TExternalType *params; + } TExternalFunction; + + + ScStack *_callStack; + ScStack *_thisStack; + ScStack *_scopeStack; + ScStack *_stack; + ScValue *_globals; + ScEngine *_engine; + int _currentLine; + bool executeInstruction(); + char *getString(); + uint32 getDWORD(); + double getFloat(); + void cleanup(); + bool create(const char *filename, byte *buffer, uint32 size, BaseScriptHolder *owner); + uint32 _iP; +private: + void readHeader(); + uint32 _bufferSize; + byte *_buffer; +public: + Common::SeekableReadStream *_scriptStream; + ScScript(BaseGame *inGame, ScEngine *engine); + virtual ~ScScript(); + char *_filename; + bool _thread; + bool _methodThread; + char *_threadEvent; + BaseScriptHolder *_owner; + ScScript::TExternalFunction *getExternal(char *name); + bool externalCall(ScStack *stack, ScStack *thisStack, ScScript::TExternalFunction *function); +private: + char **_symbols; + uint32 _numSymbols; + TFunctionPos *_functions; + TMethodPos *_methods; + TEventPos *_events; + uint32 _numExternals; + TExternalFunction *_externals; + uint32 _numFunctions; + uint32 _numMethods; + uint32 _numEvents; + + bool initScript(); + bool initTables(); + + +// IWmeDebugScript interface implementation +public: + virtual int dbgGetLine(); + virtual const char *dbgGetFilename(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_engine.cpp b/engines/wintermute/base/scriptables/script_engine.cpp new file mode 100644 index 0000000000..3d1863946e --- /dev/null +++ b/engines/wintermute/base/scriptables/script_engine.cpp @@ -0,0 +1,609 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/utils/utils.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScEngine, true) + +#define COMPILER_DLL "dcscomp.dll" +////////////////////////////////////////////////////////////////////////// +ScEngine::ScEngine(BaseGame *inGame) : BaseClass(inGame) { + _gameRef->LOG(0, "Initializing scripting engine..."); + + if (_compilerAvailable) { + _gameRef->LOG(0, " Script compiler bound successfuly"); + } else { + _gameRef->LOG(0, " Script compiler is NOT available"); + } + + _globals = new ScValue(_gameRef); + + + // register 'Game' as global variable + if (!_globals->propExists("Game")) { + ScValue val(_gameRef); + val.setNative(_gameRef, true); + _globals->setProp("Game", &val); + } + + // register 'Math' as global variable + if (!_globals->propExists("Math")) { + ScValue val(_gameRef); + val.setNative(_gameRef->_mathClass, true); + _globals->setProp("Math", &val); + } + + // prepare script cache + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + _cachedScripts[i] = NULL; + } + + _currentScript = NULL; + + _isProfiling = false; + _profilingStartTime = 0; + + //EnableProfiling(); +} + + +////////////////////////////////////////////////////////////////////////// +ScEngine::~ScEngine() { + _gameRef->LOG(0, "Shutting down scripting engine"); + + disableProfiling(); + + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::cleanup() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_thread && _scripts[i]->_owner) { + _scripts[i]->_owner->removeScript(_scripts[i]); + } + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + + _scripts.clear(); + + delete _globals; + _globals = NULL; + + emptyScriptCache(); + + _currentScript = NULL; // ref only + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +byte *ScEngine::loadFile(void *data, char *filename, uint32 *size) { + return BaseFileManager::getEngineInstance()->readWholeFile(filename, size); +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::closeFile(void *data, byte *buffer) { + delete[] buffer; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::parseElement(void *data, int line, int type, void *elementData) { +} + + +////////////////////////////////////////////////////////////////////////// +ScScript *ScEngine::runScript(const char *filename, BaseScriptHolder *owner) { + byte *compBuffer; + uint32 compSize; + + // get script from cache + compBuffer = getCompiledScript(filename, &compSize); + if (!compBuffer) { + return NULL; + } + + // add new script + ScScript *script = new ScScript(_gameRef, this); + bool ret = script->create(filename, compBuffer, compSize, owner); + if (DID_FAIL(ret)) { + _gameRef->LOG(ret, "Error running script '%s'...", filename); + delete script; + return NULL; + } else { + // publish the "self" pseudo-variable + ScValue val(_gameRef); + if (owner) { + val.setNative(owner, true); + } else { + val.setNULL(); + } + + script->_globals->setProp("self", &val); + script->_globals->setProp("this", &val); + + _scripts.add(script); + + return script; + } +} + + +////////////////////////////////////////////////////////////////////////// +byte *ScEngine::getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache) { + // is script in cache? + if (!ignoreCache) { + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i] && scumm_stricmp(_cachedScripts[i]->_filename.c_str(), filename) == 0) { + _cachedScripts[i]->_timestamp = g_system->getMillis(); + *outSize = _cachedScripts[i]->_size; + return _cachedScripts[i]->_buffer; + } + } + } + + // nope, load it + byte *compBuffer; + uint32 compSize; + + uint32 size; + + byte *buffer = BaseEngine::instance().getFileManager()->readWholeFile(filename, &size); + if (!buffer) { + _gameRef->LOG(0, "ScEngine::GetCompiledScript - error opening script '%s'", filename); + return NULL; + } + + // needs to be compiled? + if (FROM_LE_32(*(uint32 *)buffer) == SCRIPT_MAGIC) { + compBuffer = buffer; + compSize = size; + } else { + if (!_compilerAvailable) { + _gameRef->LOG(0, "ScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", filename); + delete[] buffer; + return NULL; + } + // This code will never be called, since _compilerAvailable is const false. + // It's only here in the event someone would want to reinclude the compiler. + error("Script needs compilation, ScummVM does not contain a WME compiler"); + } + + byte *ret = NULL; + + // add script to cache + CScCachedScript *cachedScript = new CScCachedScript(filename, compBuffer, compSize); + if (cachedScript) { + int index = 0; + uint32 minTime = g_system->getMillis(); + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i] == NULL) { + index = i; + break; + } else if (_cachedScripts[i]->_timestamp <= minTime) { + minTime = _cachedScripts[i]->_timestamp; + index = i; + } + } + + if (_cachedScripts[index] != NULL) { + delete _cachedScripts[index]; + } + _cachedScripts[index] = cachedScript; + + ret = cachedScript->_buffer; + *outSize = cachedScript->_size; + } + + + // cleanup + delete[] buffer; + + return ret; +} + + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::tick() { + if (_scripts.size() == 0) { + return STATUS_OK; + } + + + // resolve waiting scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + + switch (_scripts[i]->_state) { + case SCRIPT_WAITING: { + /* + bool obj_found=false; + for(int j=0; j<_gameRef->_regObjects.size(); j++) + { + if (_gameRef->_regObjects[j] == _scripts[i]->_waitObject) + { + if (_gameRef->_regObjects[j]->IsReady()) _scripts[i]->Run(); + obj_found = true; + break; + } + } + if (!obj_found) _scripts[i]->finish(); // _waitObject no longer exists + */ + if (_gameRef->validObject(_scripts[i]->_waitObject)) { + if (_scripts[i]->_waitObject->isReady()) { + _scripts[i]->run(); + } + } else { + _scripts[i]->finish(); + } + break; + } + + case SCRIPT_SLEEPING: { + if (_scripts[i]->_waitFrozen) { + if (_scripts[i]->_waitTime <= g_system->getMillis()) { + _scripts[i]->run(); + } + } else { + if (_scripts[i]->_waitTime <= _gameRef->_timer) { + _scripts[i]->run(); + } + } + break; + } + + case SCRIPT_WAITING_SCRIPT: { + if (!isValidScript(_scripts[i]->_waitScript) || _scripts[i]->_waitScript->_state == SCRIPT_ERROR) { + // fake return value + _scripts[i]->_stack->pushNULL(); + _scripts[i]->_waitScript = NULL; + _scripts[i]->run(); + } else { + if (_scripts[i]->_waitScript->_state == SCRIPT_THREAD_FINISHED) { + // copy return value + _scripts[i]->_stack->push(_scripts[i]->_waitScript->_stack->pop()); + _scripts[i]->run(); + _scripts[i]->_waitScript->finish(); + _scripts[i]->_waitScript = NULL; + } + } + break; + } + default: + break; + } // switch + } // for each script + + + // execute scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + + // skip paused scripts + if (_scripts[i]->_state == SCRIPT_PAUSED) { + continue; + } + + // time sliced script + if (_scripts[i]->_timeSlice > 0) { + uint32 startTime = g_system->getMillis(); + while (_scripts[i]->_state == SCRIPT_RUNNING && g_system->getMillis() - startTime < _scripts[i]->_timeSlice) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + if (_isProfiling && _scripts[i]->_filename) { + addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime); + } + } + + // normal script + else { + uint32 startTime = 0; + bool isProfiling = _isProfiling; + if (isProfiling) { + startTime = g_system->getMillis(); + } + + while (_scripts[i]->_state == SCRIPT_RUNNING) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + if (isProfiling && _scripts[i]->_filename) { + addScriptTime(_scripts[i]->_filename, g_system->getMillis() - startTime); + } + } + _currentScript = NULL; + } + + removeFinishedScripts(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::tickUnbreakable() { + // execute unbreakable scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + if (!_scripts[i]->_unbreakable) { + continue; + } + + while (_scripts[i]->_state == SCRIPT_RUNNING) { + _currentScript = _scripts[i]; + _scripts[i]->executeInstruction(); + } + _scripts[i]->finish(); + _currentScript = NULL; + } + removeFinishedScripts(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::removeFinishedScripts() { + // remove finished scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR) { + if (!_scripts[i]->_thread && _scripts[i]->_owner) { + _scripts[i]->_owner->removeScript(_scripts[i]); + } + + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int ScEngine::getNumScripts(int *running, int *waiting, int *persistent) { + int numRunning = 0, numWaiting = 0, numPersistent = 0, numTotal = 0; + + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_FINISHED) { + continue; + } + switch (_scripts[i]->_state) { + case SCRIPT_RUNNING: + case SCRIPT_SLEEPING: + case SCRIPT_PAUSED: + numRunning++; + break; + case SCRIPT_WAITING: + numWaiting++; + break; + case SCRIPT_PERSISTENT: + numPersistent++; + break; + default: + warning("ScEngine::GetNumScripts - unhandled enum"); + break; + } + numTotal++; + } + if (running) { + *running = numRunning; + } + if (waiting) { + *waiting = numWaiting; + } + if (persistent) { + *persistent = numPersistent; + } + + return numTotal; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::emptyScriptCache() { + for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) { + if (_cachedScripts[i]) { + delete _cachedScripts[i]; + _cachedScripts[i] = NULL; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resetObject(BaseObject *Object) { + // terminate all scripts waiting for this object + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_WAITING && _scripts[i]->_waitObject == Object) { + if (!_gameRef->_compatKillMethodThreads) { + resetScript(_scripts[i]); + } + + bool isThread = _scripts[i]->_methodThread || _scripts[i]->_thread; + _scripts[i]->finish(!isThread); // 1.9b1 - top-level script kills its threads as well + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resetScript(ScScript *script) { + // terminate all scripts waiting for this script + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_state == SCRIPT_WAITING_SCRIPT && _scripts[i]->_waitScript == script) { + _scripts[i]->finish(); + } + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::persist(BasePersistenceManager *persistMgr) { + if (!persistMgr->getIsSaving()) { + cleanup(); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_currentScript)); + persistMgr->transfer(TMEMBER(_globals)); + _scripts.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::editorCleanup() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i]->_owner == NULL && (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR)) { + delete _scripts[i]; + _scripts.remove_at(i); + i--; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::pauseAll() { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] != _currentScript) { + _scripts[i]->pause(); + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::resumeAll() { + for (uint32 i = 0; i < _scripts.size(); i++) { + _scripts[i]->resume(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::isValidScript(ScScript *script) { + for (uint32 i = 0; i < _scripts.size(); i++) { + if (_scripts[i] == script) { + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool ScEngine::clearGlobals(bool includingNatives) { + _globals->CleanProps(includingNatives); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void ScEngine::addScriptTime(const char *filename, uint32 time) { + if (!_isProfiling) { + return; + } + + AnsiString fileName = filename; + fileName.toLowercase(); + _scriptTimes[fileName] += time; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::enableProfiling() { + if (_isProfiling) { + return; + } + + // destroy old data, if any + _scriptTimes.clear(); + + _profilingStartTime = g_system->getMillis(); + _isProfiling = true; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::disableProfiling() { + if (!_isProfiling) { + return; + } + + dumpStats(); + _isProfiling = false; +} + + +////////////////////////////////////////////////////////////////////////// +void ScEngine::dumpStats() { + error("DumpStats not ported to ScummVM yet"); + /* uint32 totalTime = g_system->getMillis() - _profilingStartTime; + + typedef std::vector <std::pair<uint32, std::string> > TimeVector; + TimeVector times; + + ScriptTimes::iterator it; + for (it = _scriptTimes.begin(); it != _scriptTimes.end(); ++it) { + times.push_back(std::pair<uint32, std::string> (it->_value, it->_key)); + } + std::sort(times.begin(), times.end()); + + + TimeVector::reverse_iterator tit; + + _gameRef->LOG(0, "***** Script profiling information: *****"); + _gameRef->LOG(0, " %-40s %fs", "Total execution time", (float)totalTime / 1000); + + for (tit = times.rbegin(); tit != times.rend(); ++tit) { + _gameRef->LOG(0, " %-40s %fs (%f%%)", tit->second.c_str(), (float)tit->first / 1000, (float)tit->first / (float)totalTime * 100); + }*/ +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_engine.h b/engines/wintermute/base/scriptables/script_engine.h new file mode 100644 index 0000000000..1a023326eb --- /dev/null +++ b/engines/wintermute/base/scriptables/script_engine.h @@ -0,0 +1,135 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCENGINE_H +#define WINTERMUTE_SCENGINE_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" + +namespace Wintermute { + +#define MAX_CACHED_SCRIPTS 20 +class ScScript; +class ScValue; +class BaseObject; +class BaseScriptHolder; +class ScEngine : public BaseClass { +public: + class CScCachedScript { + public: + CScCachedScript(const char *filename, byte *buffer, uint32 size) { + _timestamp = g_system->getMillis(); + _buffer = new byte[size]; + if (_buffer) { + memcpy(_buffer, buffer, size); + } + _size = size; + _filename = filename; + }; + + ~CScCachedScript() { + if (_buffer) { + delete[] _buffer; + } + }; + + uint32 _timestamp; + byte *_buffer; + uint32 _size; + Common::String _filename; + }; + + class CScBreakpoint { + public: + CScBreakpoint(const char *filename) { + _filename = filename; + } + + ~CScBreakpoint() { + _lines.clear(); + } + + Common::String _filename; + BaseArray<int> _lines; + }; + +public: + bool clearGlobals(bool includingNatives = false); + bool tickUnbreakable(); + bool removeFinishedScripts(); + bool isValidScript(ScScript *script); + + ScScript *_currentScript; + bool resumeAll(); + bool pauseAll(); + void editorCleanup(); + bool resetObject(BaseObject *Object); + bool resetScript(ScScript *script); + bool emptyScriptCache(); + byte *getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache = false); + DECLARE_PERSISTENT(ScEngine, BaseClass) + bool cleanup(); + int getNumScripts(int *running = NULL, int *waiting = NULL, int *persistent = NULL); + bool tick(); + ScValue *_globals; + ScScript *runScript(const char *filename, BaseScriptHolder *owner = NULL); + static const bool _compilerAvailable = false; + + ScEngine(BaseGame *inGame); + virtual ~ScEngine(); + static byte *loadFile(void *data, char *filename, uint32 *size); + static void closeFile(void *data, byte *buffer); + static void parseElement(void *data, int line, int type, void *elementData); + + BaseArray<ScScript *> _scripts; + + void enableProfiling(); + void disableProfiling(); + bool getIsProfiling() { + return _isProfiling; + } + + void addScriptTime(const char *filename, uint32 Time); + void dumpStats(); + +private: + + CScCachedScript *_cachedScripts[MAX_CACHED_SCRIPTS]; + bool _isProfiling; + uint32 _profilingStartTime; + + typedef Common::HashMap<Common::String, uint32> ScriptTimes; + ScriptTimes _scriptTimes; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_array.cpp b/engines/wintermute/base/scriptables/script_ext_array.cpp new file mode 100644 index 0000000000..613cbd0758 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_array.cpp @@ -0,0 +1,252 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXArray, false) + +BaseScriptable *makeSXArray(BaseGame *inGame, ScStack *stack) { + return new SXArray(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXArray::SXArray(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + _length = 0; + _values = new ScValue(_gameRef); + + int numParams = stack->pop()->getInt(0); + + if (numParams == 1) { + _length = stack->pop()->getInt(0); + } else if (numParams > 1) { + _length = numParams; + char paramName[20]; + for (int i = 0; i < numParams; i++) { + sprintf(paramName, "%d", i); + _values->setProp(paramName, stack->pop()); + } + } +} + +////////////////////////////////////////////////////////////////////////// +SXArray::SXArray(BaseGame *inGame) : BaseScriptable(inGame) { + _length = 0; + _values = new ScValue(_gameRef); +} + + +////////////////////////////////////////////////////////////////////////// +SXArray::~SXArray() { + delete _values; + _values = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +const char *SXArray::scToString() { + char dummy[32768]; + strcpy(dummy, ""); + char propName[20]; + for (int i = 0; i < _length; i++) { + sprintf(propName, "%d", i); + ScValue *val = _values->getProp(propName); + if (val) { + if (strlen(dummy) + strlen(val->getString()) < 32768) { + strcat(dummy, val->getString()); + } + } + + if (i < _length - 1 && strlen(dummy) + 1 < 32768) { + strcat(dummy, ","); + } + } + _strRep = dummy; + return _strRep.c_str(); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Push + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Push") == 0) { + int numParams = stack->pop()->getInt(0); + char paramName[20]; + + for (int i = 0; i < numParams; i++) { + _length++; + sprintf(paramName, "%d", _length - 1); + _values->setProp(paramName, stack->pop(), true); + } + stack->pushInt(_length); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pop + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Pop") == 0) { + + stack->correctParams(0); + + if (_length > 0) { + char paramName[20]; + sprintf(paramName, "%d", _length - 1); + stack->push(_values->getProp(paramName)); + _values->deleteProp(paramName); + _length--; + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXArray::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("array"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + _scValue->setInt(_length); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // [number] + ////////////////////////////////////////////////////////////////////////// + else { + char paramName[20]; + if (validNumber(name.c_str(), paramName)) { // TODO: Change to Common::String + return _values->getProp(paramName); + } else { + return _scValue; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length") == 0) { + int origLength = _length; + _length = MAX(value->getInt(0), 0); + + char propName[20]; + if (_length < origLength) { + for (int i = _length; i < origLength; i++) { + sprintf(propName, "%d", i); + _values->deleteProp(propName); + } + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // [number] + ////////////////////////////////////////////////////////////////////////// + else { + char paramName[20]; + if (validNumber(name, paramName)) { + int index = atoi(paramName); + if (index >= _length) { + _length = index + 1; + } + return _values->setProp(paramName, value); + } else { + return STATUS_FAILED; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::persist(BasePersistenceManager *persistMgr) { + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_length)); + persistMgr->transfer(TMEMBER(_values)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXArray::validNumber(const char *origStr, char *outStr) { + bool isNumber = true; + for (uint32 i = 0; i < strlen(origStr); i++) { + if (!(origStr[i] >= '0' && origStr[i] <= '9')) { + isNumber = false; + break; + } + } + + if (isNumber) { + int index = atoi(origStr); + sprintf(outStr, "%d", index); + return true; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXArray::push(ScValue *val) { + char paramName[20]; + _length++; + sprintf(paramName, "%d", _length - 1); + _values->setProp(paramName, val, true); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_array.h b/engines/wintermute/base/scriptables/script_ext_array.h new file mode 100644 index 0000000000..284c547a27 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_array.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXARRAY_H +#define WINTERMUTE_SXARRAY_H + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXArray : public BaseScriptable { +public: + bool push(ScValue *val); + bool validNumber(const char *origStr, char *outStr); + DECLARE_PERSISTENT(SXArray, BaseScriptable) + SXArray(BaseGame *inGame, ScStack *stack); + SXArray(BaseGame *inGame); + virtual ~SXArray(); + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); +private: + int _length; + ScValue *_values; + Common::String _strRep; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_date.cpp b/engines/wintermute/base/scriptables/script_ext_date.cpp new file mode 100644 index 0000000000..5aa069d0b2 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_date.cpp @@ -0,0 +1,293 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_date.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXDate, false) + +BaseScriptable *makeSXDate(BaseGame *inGame, ScStack *stack) { + return new SXDate(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXDate::SXDate(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(6); + + memset(&_tm, 0, sizeof(_tm)); + + ScValue *valYear = stack->pop(); + _tm.tm_year = valYear->getInt() - 1900; + _tm.tm_mon = stack->pop()->getInt() - 1; + _tm.tm_mday = stack->pop()->getInt(); + _tm.tm_hour = stack->pop()->getInt(); + _tm.tm_min = stack->pop()->getInt(); + _tm.tm_sec = stack->pop()->getInt(); + + if (valYear->isNULL()) { + g_system->getTimeAndDate(_tm); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXDate::~SXDate() { + +} + +////////////////////////////////////////////////////////////////////////// +const char *SXDate::scToString() { + // TODO: Make this more stringy, and less ISO 8601-like + _strRep.format("%04d-%02d-%02d - %02d:%02d:%02d", _tm.tm_year, _tm.tm_mon, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec); + return _strRep.c_str(); + //return asctime(&_tm); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetYear + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetYear") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_year + 1900); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetMonth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMonth") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_mon + 1); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetDate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDate") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_mday); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetHours + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHours") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_hour); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetMinutes + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetMinutes") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_min); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetSeconds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetSeconds") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_sec); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // GetWeekday + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetWeekday") == 0) { + stack->correctParams(0); + stack->pushInt(_tm.tm_wday); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetYear + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetYear") == 0) { + stack->correctParams(1); + _tm.tm_year = stack->pop()->getInt() - 1900; + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetMonth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMonth") == 0) { + stack->correctParams(1); + _tm.tm_mon = stack->pop()->getInt() - 1; + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetDate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDate") == 0) { + stack->correctParams(1); + _tm.tm_mday = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetHours + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHours") == 0) { + stack->correctParams(1); + _tm.tm_hour = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetMinutes + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetMinutes") == 0) { + stack->correctParams(1); + _tm.tm_min = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // SetSeconds + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetSeconds") == 0) { + stack->correctParams(1); + _tm.tm_sec = stack->pop()->getInt(); + stack->pushNULL(); + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetCurrentTime + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetCurrentTime") == 0) { + stack->correctParams(0); + g_system->getTimeAndDate(_tm); + stack->pushNULL(); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXDate::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("date"); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (name == "Name")==0){ + setName(value->getString()); + return STATUS_OK; + } + + else*/ return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXDate::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + persistMgr->transfer(TMEMBER(_tm.tm_year)); + persistMgr->transfer(TMEMBER(_tm.tm_mon)); + persistMgr->transfer(TMEMBER(_tm.tm_mday)); + persistMgr->transfer(TMEMBER(_tm.tm_hour)); + persistMgr->transfer(TMEMBER(_tm.tm_min)); + persistMgr->transfer(TMEMBER(_tm.tm_sec)); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +int SXDate::scCompare(BaseScriptable *Value) { + TimeDate time1 = _tm; + TimeDate time2 = ((SXDate *)Value)->_tm; + + if (time1.tm_year < time2.tm_year) { + return -1; + } else if (time1.tm_year == time2.tm_year) { + if (time1.tm_mon < time2.tm_mon) { + return -1; + } else if (time1.tm_mon == time2.tm_mon) { + if (time1.tm_mday < time2.tm_mday) { + return -1; + } else if (time1.tm_mday == time2.tm_mday) { + if (time1.tm_hour < time2.tm_hour) { + return -1; + } else if (time1.tm_hour == time2.tm_hour) { + if (time1.tm_min < time2.tm_min) { + return -1; + } else if (time1.tm_min == time2.tm_min) { + if (time1.tm_sec < time2.tm_sec) { + return -1; + } else if (time1.tm_sec == time2.tm_sec) { + return 0; // Equal + } else { + return 1; // Sec + } + } else { + return 1; // Minute + } + } else { + return 1; // Hour + } + } else { + return 1; // Day + } + } else { + return 1; // Month + } + } else { + return 1; // Year + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_date.h b/engines/wintermute/base/scriptables/script_ext_date.h new file mode 100644 index 0000000000..062b7c55c7 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_date.h @@ -0,0 +1,54 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXDATE_H +#define WINTERMUTE_SXDATE_H + +#include "common/system.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXDate : public BaseScriptable { +public: + int scCompare(BaseScriptable *Value); + DECLARE_PERSISTENT(SXDate, BaseScriptable) + SXDate(BaseGame *inGame, ScStack *Stack); + virtual ~SXDate(); + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); +private: + TimeDate _tm; + Common::String _strRep; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_file.cpp b/engines/wintermute/base/scriptables/script_ext_file.cpp new file mode 100644 index 0000000000..a1d39c5d0a --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_file.cpp @@ -0,0 +1,828 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/scriptables/script_ext_file.h" + +// Note: This code is completely untested, as I have yet to find a game that uses SXFile. + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXFile, false) + +BaseScriptable *makeSXFile(BaseGame *inGame, ScStack *stack) { + return new SXFile(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXFile::SXFile(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + _filename = NULL; + if (!val->isNULL()) { + BaseUtils::setString(&_filename, val->getString()); + } + + _readFile = NULL; + _writeFile = NULL; + + _mode = 0; + _textMode = false; +} + + +////////////////////////////////////////////////////////////////////////// +SXFile::~SXFile() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +void SXFile::cleanup() { + delete[] _filename; + _filename = NULL; + close(); +} + + +////////////////////////////////////////////////////////////////////////// +void SXFile::close() { + if (_readFile) { + BaseFileManager::getEngineInstance()->closeFile(_readFile); + _readFile = NULL; + } + if (_writeFile) { + _writeFile->finalize(); + delete _writeFile; + _writeFile = NULL; + } + _mode = 0; + _textMode = false; +} + +////////////////////////////////////////////////////////////////////////// +const char *SXFile::scToString() { + if (_filename) { + return _filename; + } else { + return "[file object]"; + } +} + +#define FILE_BUFFER_SIZE 32768 +////////////////////////////////////////////////////////////////////////// +bool SXFile::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetFilename + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetFilename") == 0) { + stack->correctParams(1); + const char *filename = stack->pop()->getString(); + cleanup(); + BaseUtils::setString(&_filename, filename); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // OpenAsText / OpenAsBinary + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "OpenAsText") == 0 || strcmp(name, "OpenAsBinary") == 0) { + stack->correctParams(1); + close(); + _mode = stack->pop()->getInt(1); + if (_mode < 1 || _mode > 3) { + script->runtimeError("File.%s: invalid access mode. Setting read mode.", name); + _mode = 1; + } + if (_mode == 1) { + _readFile = BaseFileManager::getEngineInstance()->openFile(_filename); + if (!_readFile) { + //script->runtimeError("File.%s: Error opening file '%s' for reading.", Name, _filename); + close(); + } else { + _textMode = strcmp(name, "OpenAsText") == 0; + } + } else { + if (strcmp(name, "OpenAsText") == 0) { + if (_mode == 2) { + _writeFile = openForWrite(_filename, false); + } else { + _writeFile = openForAppend(_filename, false); + } + } else { + if (_mode == 2) { + _writeFile = openForWrite(_filename, true); + } else { + _writeFile = openForAppend(_filename, true); + } + } + + if (!_writeFile) { + //script->runtimeError("File.%s: Error opening file '%s' for writing.", Name, _filename); + close(); + } else { + _textMode = strcmp(name, "OpenAsText") == 0; + } + } + + if (_readFile || _writeFile) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Close + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Close") == 0) { + stack->correctParams(0); + close(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPosition + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPosition") == 0) { + stack->correctParams(1); + if (_mode == 0) { + script->runtimeError("File.%s: File is not open", name); + stack->pushBool(false); + } else { + int pos = stack->pop()->getInt(); + stack->pushBool(setPos(pos)); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Delete + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Delete") == 0) { + stack->correctParams(0); + close(); + error("SXFile-Method: \"Delete\" not supported"); + //stack->pushBool(BasePlatform::deleteFile(_filename) != false); + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Copy + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Copy") == 0) { + stack->correctParams(2); + /* const char *dest = */ stack->pop()->getString(); + /* bool overwrite = */ stack->pop()->getBool(true); + + close(); + error("SXFile-Method: Copy not supported"); + //stack->pushBool(BasePlatform::copyFile(_filename, Dest, !Overwrite) != false); + stack->pushBool(false); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadLine + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadLine") == 0) { + stack->correctParams(0); + if (!_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open in text mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 bufSize = FILE_BUFFER_SIZE; + byte *buf = (byte *)malloc(bufSize); + uint32 counter = 0; + byte b; + bool foundNewLine = false; + bool ret = STATUS_FAILED; + do { + ret = _readFile->read(&b, 1); + if (ret != 1) { + break; + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + if (b == '\n') { + buf[counter] = '\0'; + foundNewLine = true; + break; + } else if (b == 0x0D) { + continue; + } else { + buf[counter] = b; + counter++; + } + } while (DID_SUCCEED(ret)); + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + buf[counter] = '\0'; + + if (!foundNewLine && counter == 0) { + stack->pushNULL(); + } else { + stack->pushString((char *)buf); + } + + free(buf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadText") == 0) { + stack->correctParams(1); + int textLen = stack->pop()->getInt(); + + if (!_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open in text mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 bufSize = FILE_BUFFER_SIZE; + byte *buf = (byte *)malloc(bufSize); + uint32 counter = 0; + byte b; + + bool ret = STATUS_FAILED; + while (counter < (uint32)textLen) { + ret = _readFile->read(&b, 1); + if (ret != 1) { + break; + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + if (b == 0x0D) { + continue; + } else { + buf[counter] = b; + counter++; + } + } + + if (counter > bufSize) { + buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE); + bufSize += FILE_BUFFER_SIZE; + } + buf[counter] = '\0'; + + if (textLen > 0 && counter == 0) { + stack->pushNULL(); + } else { + stack->pushString((char *)buf); + } + + free(buf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteLine / WriteText + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteLine") == 0 || strcmp(name, "WriteText") == 0) { + stack->correctParams(1); + const char *line = stack->pop()->getString(); + if (!_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in text mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + Common::String writeLine; + if (strcmp(name, "WriteLine") == 0) { + writeLine = Common::String::format("%s\n", line); + } else { + writeLine = Common::String::format("%s", line); + } + _writeFile->writeString(writeLine); + _writeFile->writeByte(0); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + // ReadBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadBool") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + bool val; + if (_readFile->read(&val, sizeof(bool)) == sizeof(bool)) { + stack->pushBool(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadByte") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + byte val = _readFile->readByte(); + if (!_readFile->err()) { + stack->pushInt(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadShort") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + int16 val = _readFile->readSint16LE(); + if (!_readFile->err()) { + stack->pushInt(65536 + val); + } else { + stack->pushNULL(); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadInt / ReadLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadInt") == 0 || strcmp(name, "ReadLong") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + int32 val = _readFile->readSint32LE(); + if (!_readFile->err()) { + stack->pushInt(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadFloat") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + float val; + (*(uint32 *)&val) = _readFile->readUint32LE(); + if (!_readFile->err()) { + stack->pushFloat(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadDouble") == 0) { // TODO: Solve reading a 8 byte double. + error("SXFile::ReadDouble - Not endian safe yet"); + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + double val; + if (_readFile->read(&val, sizeof(double)) == sizeof(double)) { + stack->pushFloat(val); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ReadString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ReadString") == 0) { + stack->correctParams(0); + if (_textMode || !_readFile) { + script->runtimeError("File.%s: File must be open for reading in binary mode.", name); + stack->pushNULL(); + return STATUS_OK; + } + uint32 size = _readFile->readUint32LE(); + if (!_readFile->err()) { + byte *str = new byte[size + 1]; + if (str) { + if (_readFile->read(str, size) == size) { + str[size] = '\0'; + stack->pushString((char *)str); + } + delete[] str; + } else { + stack->pushNULL(); + } + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteBool") == 0) { + stack->correctParams(1); + bool val = stack->pop()->getBool(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeByte(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteByte") == 0) { + stack->correctParams(1); + byte val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeByte(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteShort") == 0) { + stack->correctParams(1); + int16 val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeSint16LE(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteInt / WriteLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteInt") == 0 || strcmp(name, "WriteLong") == 0) { + stack->correctParams(1); + int32 val = stack->pop()->getInt(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + _writeFile->writeSint32LE(val); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteFloat") == 0) { + stack->correctParams(1); + float val = stack->pop()->getFloat(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + uint32 *ptr = (uint32 *)&val; + _writeFile->writeUint32LE(*ptr); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteDouble") == 0) { + error("SXFile::WriteDouble - Not endian safe yet"); + stack->correctParams(1); + /* double val = */ stack->pop()->getFloat(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + //fwrite(&val, sizeof(val), 1, (FILE *)_writeFile); + stack->pushBool(true); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // WriteString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "WriteString") == 0) { + stack->correctParams(1); + const char *val = stack->pop()->getString(); + + if (_textMode || !_writeFile) { + script->runtimeError("File.%s: File must be open for writing in binary mode.", name); + stack->pushBool(false); + return STATUS_OK; + } + + uint32 size = strlen(val); + _writeFile->writeUint32LE(size); + _writeFile->writeString(val); + + stack->pushBool(true); + + return STATUS_OK; + } else { + return BaseScriptable::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXFile::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("file"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Filename (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Filename") { + _scValue->setString(_filename); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Position (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Position") { + _scValue->setInt(getPos()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Length (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + _scValue->setInt(getLength()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextMode") { + _scValue->setBool(_textMode); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // AccessMode (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "AccessMode") { + _scValue->setInt(_mode); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXFile::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length")==0){ + int origLength = _length; + _length = max(value->getInt(0), 0); + + char propName[20]; + if (_length < OrigLength){ + for(int i=_length; i<OrigLength; i++){ + sprintf(PropName, "%d", i); + _values->DeleteProp(PropName); + } + } + return STATUS_OK; + } + else*/ return BaseScriptable::scSetProperty(name, value); +} + +////////////////////////////////////////////////////////////////////////// +uint32 SXFile::getPos() { + if (_mode == 1 && _readFile) { + return _readFile->pos(); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - getPos for WriteFile not supported"); + return 0; +// return ftell((FILE *)_writeFile); + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXFile::setPos(uint32 pos, int whence) { + if (_mode == 1 && _readFile) { + return _readFile->seek(pos, whence); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - seeking in WriteFile not supported"); + return false; +// return fseek((FILE *)_writeFile, pos, (int)origin) == 0; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +uint32 SXFile::getLength() { + if (_mode == 1 && _readFile) { + return _readFile->size(); + } else if ((_mode == 2 || _mode == 3) && _writeFile) { + error("SXFile - reading length for WriteFile not supported"); + return 0; + /* + uint32 currentPos = ftell((FILE *)_writeFile); + fseek((FILE *)_writeFile, 0, SEEK_END); + int ret = ftell((FILE *)_writeFile); + fseek((FILE *)_writeFile, CurrentPos, SEEK_SET); + return Ret;*/ + } else { + return 0; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SXFile::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_mode)); + persistMgr->transfer(TMEMBER(_textMode)); + + uint32 pos = 0; + if (persistMgr->getIsSaving()) { + pos = getPos(); + persistMgr->transfer(TMEMBER(pos)); + } else { + persistMgr->transfer(TMEMBER(pos)); + + // try to re-open file if needed + _writeFile = NULL; + _readFile = NULL; + + if (_mode != 0) { + // open for reading + if (_mode == 1) { + _readFile = BaseFileManager::getEngineInstance()->openFile(_filename); + if (!_readFile) { + close(); + } + } + // open for writing / appending + else { + if (_textMode) { + if (_mode == 2) { + _writeFile = openForWrite(_filename, false); + } else { + _writeFile = openForAppend(_filename, false); + } + } else { + if (_mode == 2) { + _writeFile = openForWrite(_filename, true); + } else { + _writeFile = openForAppend(_filename, true); + } + } + if (_writeFile) { + close(); + } + } + setPos(pos); + } + } + + return STATUS_OK; +} + +// Should replace fopen(..., "wb+") and fopen(..., "w+") +Common::WriteStream *SXFile::openForWrite(const Common::String &filename, bool binary) { + error("SXFile::openForWrite - WriteFiles not supported"); +} + +// Should replace fopen(..., "ab+") and fopen(..., "a+") +Common::WriteStream *SXFile::openForAppend(const Common::String &filename, bool binary) { + error("SXFile::openForAppend - WriteFiles not supported"); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_file.h b/engines/wintermute/base/scriptables/script_ext_file.h new file mode 100644 index 0000000000..f7c72fcfb3 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_file.h @@ -0,0 +1,66 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTES_SXFILE_H +#define WINTERMUTES_SXFILE_H + + +#include "engines/wintermute/base/base_scriptable.h" +#include "common/stream.h" + +namespace Wintermute { + +class BaseFile; + +class SXFile : public BaseScriptable { +public: + DECLARE_PERSISTENT(SXFile, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); + SXFile(BaseGame *inGame, ScStack *Stack); + virtual ~SXFile(); +private: + Common::SeekableReadStream *_readFile; + Common::WriteStream *_writeFile; + int _mode; // 0..none, 1..read, 2..write, 3..append + bool _textMode; + void close(); + void cleanup(); + uint32 getPos(); + uint32 getLength(); + bool setPos(uint32 pos, int whence = SEEK_SET); + char *_filename; + Common::WriteStream *openForWrite(const Common::String &filename, bool binary); + Common::WriteStream *openForAppend(const Common::String &filename, bool binary); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_math.cpp b/engines/wintermute/base/scriptables/script_ext_math.cpp new file mode 100644 index 0000000000..d816fbec65 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_math.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/persistent.h" +#include "common/math.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +IMPLEMENT_PERSISTENT(SXMath, true) + +BaseScriptable *makeSXMath(BaseGame *inGame) { + return new SXMath(inGame); +} + +////////////////////////////////////////////////////////////////////////// +SXMath::SXMath(BaseGame *inGame) : BaseScriptable(inGame) { + +} + + +////////////////////////////////////////////////////////////////////////// +SXMath::~SXMath() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMath::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Abs + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Abs") == 0) { + stack->correctParams(1); + stack->pushFloat(fabs(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Acos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Acos") == 0) { + stack->correctParams(1); + stack->pushFloat(acos(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Asin + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Asin") == 0) { + stack->correctParams(1); + stack->pushFloat(asin(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Atan + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Atan") == 0) { + stack->correctParams(1); + stack->pushFloat(atan(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Atan2 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Atan2") == 0) { + stack->correctParams(2); + double y = stack->pop()->getFloat(); + double x = stack->pop()->getFloat(); + stack->pushFloat(atan2(y, x)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Ceil + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Ceil") == 0) { + stack->correctParams(1); + stack->pushFloat(ceil(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Cos + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Cos") == 0) { + stack->correctParams(1); + stack->pushFloat(cos(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Cosh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Cosh") == 0) { + stack->correctParams(1); + stack->pushFloat(cosh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Exp + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Exp") == 0) { + stack->correctParams(1); + stack->pushFloat(exp(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Floor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Floor") == 0) { + stack->correctParams(1); + stack->pushFloat(floor(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Log + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Log") == 0) { + stack->correctParams(1); + stack->pushFloat(log(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Log10 + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Log10") == 0) { + stack->correctParams(1); + stack->pushFloat(log10(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Pow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pow") == 0) { + stack->correctParams(2); + double x = stack->pop()->getFloat(); + double y = stack->pop()->getFloat(); + + stack->pushFloat(pow(x, y)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sin + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sin") == 0) { + stack->correctParams(1); + stack->pushFloat(sin(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sinh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sinh") == 0) { + stack->correctParams(1); + stack->pushFloat(sinh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Tan + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Tan") == 0) { + stack->correctParams(1); + stack->pushFloat(tan(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Tanh + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Tanh") == 0) { + stack->correctParams(1); + stack->pushFloat(tanh(degreeToRadian(stack->pop()->getFloat()))); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Sqrt + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Sqrt") == 0) { + stack->correctParams(1); + stack->pushFloat(sqrt(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DegToRad + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DegToRad") == 0) { + stack->correctParams(1); + stack->pushFloat(degreeToRadian(stack->pop()->getFloat())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // RadToDeg + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "RadToDeg") == 0) { + stack->correctParams(1); + stack->pushFloat(radianToDegree(stack->pop()->getFloat())); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXMath::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("math"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PI + ////////////////////////////////////////////////////////////////////////// + else if (name == "PI") { + _scValue->setFloat(M_PI); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +double SXMath::degreeToRadian(double value) { + return value * (M_PI / 180.0f); +} + + +////////////////////////////////////////////////////////////////////////// +double SXMath::radianToDegree(double value) { + return value * (180.0f / M_PI); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMath::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_math.h b/engines/wintermute/base/scriptables/script_ext_math.h new file mode 100644 index 0000000000..48c43ea7e8 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_math.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXMATH_H +#define WINTERMUTE_SXMATH_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXMath : public BaseScriptable { +public: + DECLARE_PERSISTENT(SXMath, BaseScriptable) + SXMath(BaseGame *inGame); + virtual ~SXMath(); + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + +private: + double degreeToRadian(double value); + double radianToDegree(double value); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp new file mode 100644 index 0000000000..8f05b7bff6 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp @@ -0,0 +1,529 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h" +#include "common/file.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXMemBuffer, false) + +BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack) { + return new SXMemBuffer(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::SXMemBuffer(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + stack->correctParams(1); + _buffer = NULL; + _size = 0; + + int newSize = stack->pop()->getInt(); + resize(MAX(0, newSize)); +} + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::SXMemBuffer(BaseGame *inGame, void *buffer) : BaseScriptable(inGame) { + _size = 0; + _buffer = buffer; +} + + +////////////////////////////////////////////////////////////////////////// +SXMemBuffer::~SXMemBuffer() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +void *SXMemBuffer::scToMemBuffer() { + return _buffer; +} + +////////////////////////////////////////////////////////////////////////// +void SXMemBuffer::cleanup() { + if (_size) { + free(_buffer); + } + _buffer = NULL; + _size = 0; +} + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::resize(int newSize) { + int oldSize = _size; + + if (_size == 0) { + _buffer = malloc(newSize); + if (_buffer) { + _size = newSize; + } + } else { + void *newBuf = realloc(_buffer, newSize); + if (!newBuf) { + if (newSize == 0) { + _buffer = newBuf; + _size = newSize; + } else { + return STATUS_FAILED; + } + } else { + _buffer = newBuf; + _size = newSize; + } + } + + if (_buffer && _size > oldSize) { + memset((byte *)_buffer + oldSize, 0, _size - oldSize); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::checkBounds(ScScript *script, int start, int length) { + if (_buffer == NULL) { + script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer"); + return false; + } + if (_size == 0) { + return true; + } + + if (start < 0 || length == 0 || start + length > _size) { + script->runtimeError("Set/Get method call is out of bounds"); + return false; + } else { + return true; + } +} + +////////////////////////////////////////////////////////////////////////// +const char *SXMemBuffer::scToString() { + return "[membuffer object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSize + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSize") == 0) { + stack->correctParams(1); + int newSize = stack->pop()->getInt(); + newSize = MAX(0, newSize); + if (DID_SUCCEED(resize(newSize))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetBool") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(bool))) { + stack->pushNULL(); + } else { + stack->pushBool(*(bool *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetByte") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(byte))) { + stack->pushNULL(); + } else { + stack->pushInt(*(byte *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetShort") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(short))) { + stack->pushNULL(); + } else { + stack->pushInt(65536 + * (short *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInt / GetLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(int))) { + stack->pushNULL(); + } else { + stack->pushInt(*(int *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFloat") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(float))) { + stack->pushNULL(); + } else { + stack->pushFloat(*(float *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDouble") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(double))) { + stack->pushNULL(); + } else { + stack->pushFloat(*(double *)((byte *)_buffer + start)); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetString") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int length = stack->pop()->getInt(); + + // find end of string + if (length == 0 && start >= 0 && start < _size) { + for (int i = start; i < _size; i++) { + if (((char *)_buffer)[i] == '\0') { + length = i - start; + break; + } + } + } + + if (!checkBounds(script, start, length)) { + stack->pushNULL(); + } else { + char *str = new char[length + 1]; + Common::strlcpy(str, (const char *)_buffer + start, length + 1); + stack->pushString(str); + delete[] str; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPointer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPointer") == 0) { + stack->correctParams(1); + int start = stack->pop()->getInt(); + if (!checkBounds(script, start, sizeof(void *))) { + stack->pushNULL(); + } else { + void *pointer = *(void **)((byte *)_buffer + start); + SXMemBuffer *buf = new SXMemBuffer(_gameRef, pointer); + stack->pushNative(buf, false); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetBool + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetBool") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + bool val = stack->pop()->getBool(); + + if (!checkBounds(script, start, sizeof(bool))) { + stack->pushBool(false); + } else { + *(bool *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetByte + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetByte") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + byte val = (byte)stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(byte))) { + stack->pushBool(false); + } else { + *(byte *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetShort + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetShort") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + short val = (short)stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(short))) { + stack->pushBool(false); + } else { + *(short *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInt / SetLong + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int val = stack->pop()->getInt(); + + if (!checkBounds(script, start, sizeof(int))) { + stack->pushBool(false); + } else { + *(int *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFloat + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFloat") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + float val = (float)stack->pop()->getFloat(); + + if (!checkBounds(script, start, sizeof(float))) { + stack->pushBool(false); + } else { + *(float *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetDouble + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDouble") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + double val = stack->pop()->getFloat(); + + if (!checkBounds(script, start, sizeof(double))) { + stack->pushBool(false); + } else { + *(double *)((byte *)_buffer + start) = val; + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetString + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetString") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + const char *val = stack->pop()->getString(); + + if (!checkBounds(script, start, strlen(val) + 1)) { + stack->pushBool(false); + } else { + memcpy((byte *)_buffer + start, val, strlen(val) + 1); + stack->pushBool(true); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPointer + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPointer") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + /* ScValue *val = */ stack->pop(); + + if (!checkBounds(script, start, sizeof(void *))) { + stack->pushBool(false); + } else { + /* + int pointer = (int)Val->getMemBuffer(); + memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*)); + stack->pushBool(true); + */ + // TODO fix + stack->pushBool(false); + + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DEBUG_Dump + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DEBUG_Dump") == 0) { + stack->correctParams(0); + if (_buffer && _size) { + warning("SXMemBuffer::ScCallMethod - DEBUG_Dump"); + Common::DumpFile f; + f.open("buffer.bin"); + f.write(_buffer, _size); + f.close(); + } + stack->pushNULL(); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXMemBuffer::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("membuffer"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Size (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Size") { + _scValue->setInt(_size); + return _scValue; + } else { + return BaseScriptable::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::scSetProperty(const char *name, ScValue *value) { + /* + ////////////////////////////////////////////////////////////////////////// + // Length + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Length")==0){ + int origLength = _length; + _length = max(value->getInt(0), 0); + + char propName[20]; + if (_length < OrigLength){ + for(int i=_length; i<OrigLength; i++){ + sprintf(PropName, "%d", i); + _values->DeleteProp(PropName); + } + } + return STATUS_OK; + } + else*/ return BaseScriptable::scSetProperty(name, value); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXMemBuffer::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_size)); + + if (persistMgr->getIsSaving()) { + if (_size > 0) { + persistMgr->putBytes((byte *)_buffer, _size); + } + } else { + if (_size > 0) { + _buffer = malloc(_size); + persistMgr->getBytes((byte *)_buffer, _size); + } else { + _buffer = NULL; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int SXMemBuffer::scCompare(BaseScriptable *val) { + if (_buffer == val->scToMemBuffer()) { + return 0; + } else { + return 1; + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_mem_buffer.h b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h new file mode 100644 index 0000000000..1527a323dc --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_mem_buffer.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXMEMBUFFER_H +#define WINTERMUTE_SXMEMBUFFER_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXMemBuffer : public BaseScriptable { +public: + virtual int scCompare(BaseScriptable *Val); + DECLARE_PERSISTENT(SXMemBuffer, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + const char *scToString(); + SXMemBuffer(BaseGame *inGame, ScStack *stack); + SXMemBuffer(BaseGame *inGame, void *buffer); + virtual ~SXMemBuffer(); + virtual void *scToMemBuffer(); +private: + int _size; + + bool resize(int newSize); + void *_buffer; + void cleanup(); + bool checkBounds(ScScript *script, int start, int length); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_object.cpp b/engines/wintermute/base/scriptables/script_ext_object.cpp new file mode 100644 index 0000000000..b87aac81f9 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_object.cpp @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_ext_object.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(SXObject, false) + +BaseScriptable *makeSXObject(BaseGame *inGame, ScStack *stack) { + return new SXObject(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXObject::SXObject(BaseGame *inGame, ScStack *stack) : BaseObject(inGame) { + int numParams = stack->pop()->getInt(0); + for (int i = 0; i < numParams; i++) { + addScript(stack->pop()->getString()); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXObject::~SXObject() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool SXObject::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_object.h b/engines/wintermute/base/scriptables/script_ext_object.h new file mode 100644 index 0000000000..c85d16d44e --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_object.h @@ -0,0 +1,46 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXOBJECT_H +#define WINTERMUTE_SXOBJECT_H + + +#include "engines/wintermute/base/base_object.h" + +namespace Wintermute { + +class SXObject : public BaseObject { +public: + DECLARE_PERSISTENT(SXObject, BaseObject) + SXObject(BaseGame *inGame, ScStack *Stack); + virtual ~SXObject(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_ext_string.cpp b/engines/wintermute/base/scriptables/script_ext_string.cpp new file mode 100644 index 0000000000..5f7da1c2dd --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_string.cpp @@ -0,0 +1,436 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/base/scriptables/script_ext_string.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" +#include "engines/wintermute/utils/string_util.h" +#include "common/tokenizer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(SXString, false) + +BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack) { + return new SXString(inGame, stack); +} + +////////////////////////////////////////////////////////////////////////// +SXString::SXString(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) { + _string = NULL; + _capacity = 0; + + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (val->isInt()) { + _capacity = MAX(0, val->getInt()); + if (_capacity > 0) { + _string = new char[_capacity]; + memset(_string, 0, _capacity); + } + } else { + setStringVal(val->getString()); + } + + if (_capacity == 0) { + setStringVal(""); + } +} + + +////////////////////////////////////////////////////////////////////////// +SXString::~SXString() { + if (_string) { + delete[] _string; + } +} + + +////////////////////////////////////////////////////////////////////////// +void SXString::setStringVal(const char *val) { + int len = strlen(val); + if (len >= _capacity) { + _capacity = len + 1; + delete[] _string; + _string = NULL; + _string = new char[_capacity]; + memset(_string, 0, _capacity); + } + strcpy(_string, val); +} + + +////////////////////////////////////////////////////////////////////////// +const char *SXString::scToString() { + if (_string) { + return _string; + } else { + return "[null string]"; + } +} + + +////////////////////////////////////////////////////////////////////////// +void SXString::scSetString(const char *val) { + setStringVal(val); +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // Substring + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Substring") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + int end = stack->pop()->getInt(); + + if (end < start) { + BaseUtils::swap(&start, &end); + } + + //try { + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + //WideString subStr = str.substr(start, end - start + 1); + WideString subStr(str.c_str() + start, end - start + 1); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); + } + // } catch (std::exception &) { + // stack->pushNULL(); + // } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Substr + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Substr") == 0) { + stack->correctParams(2); + int start = stack->pop()->getInt(); + + ScValue *val = stack->pop(); + int len = val->getInt(); + + if (!val->isNULL() && len <= 0) { + stack->pushString(""); + return STATUS_OK; + } + + if (val->isNULL()) { + len = strlen(_string) - start; + } + +// try { + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + +// WideString subStr = str.substr(start, len); + WideString subStr(str.c_str() + start, len); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); + } +// } catch (std::exception &) { +// stack->pushNULL(); +// } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ToUpperCase + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToUpperCase") == 0) { + stack->correctParams(0); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + str.toUppercase(); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(str).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(str).c_str()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ToLowerCase + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ToLowerCase") == 0) { + stack->correctParams(0); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + str.toLowercase(); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + stack->pushString(StringUtil::wideToUtf8(str).c_str()); + } else { + stack->pushString(StringUtil::wideToAnsi(str).c_str()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // IndexOf + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "IndexOf") == 0) { + stack->correctParams(2); + + const char *strToFind = stack->pop()->getString(); + int index = stack->pop()->getInt(); + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + WideString toFind; + if (_gameRef->_textEncoding == TEXT_UTF8) { + toFind = StringUtil::utf8ToWide(strToFind); + } else { + toFind = StringUtil::ansiToWide(strToFind); + } + + int indexOf = StringUtil::indexOf(str, toFind, index); + stack->pushInt(indexOf); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Split + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Split") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + char separators[MAX_PATH_LENGTH] = ","; + if (!val->isNULL()) { + strcpy(separators, val->getString()); + } + + SXArray *array = new SXArray(_gameRef); + if (!array) { + stack->pushNULL(); + return STATUS_OK; + } + + + WideString str; + if (_gameRef->_textEncoding == TEXT_UTF8) { + str = StringUtil::utf8ToWide(_string); + } else { + str = StringUtil::ansiToWide(_string); + } + + WideString delims; + if (_gameRef->_textEncoding == TEXT_UTF8) { + delims = StringUtil::utf8ToWide(separators); + } else { + delims = StringUtil::ansiToWide(separators); + } + + Common::Array<WideString> parts; + + + + Common::StringTokenizer tokenizer(str, delims); + while (!tokenizer.empty()) { + Common::String str2 = tokenizer.nextToken(); + parts.push_back(str2); + } + // TODO: Clean this up + /*do { + pos = StringUtil::IndexOf(Common::String(str.c_str() + start), delims, start); + //pos = str.find_first_of(delims, start); + if (pos == start) { + start = pos + 1; + } else if (pos == str.size()) { + parts.push_back(Common::String(str.c_str() + start)); + break; + } else { + parts.push_back(Common::String(str.c_str() + start, pos - start)); + start = pos + 1; + } + //start = str.find_first_not_of(delims, start); + start = StringUtil::LastIndexOf(Common::String(str.c_str() + start), delims, start) + 1; + + } while (pos != str.size());*/ + + for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) { + WideString &part = (*it); + + if (_gameRef->_textEncoding == TEXT_UTF8) { + val = new ScValue(_gameRef, StringUtil::wideToUtf8(part).c_str()); + } else { + val = new ScValue(_gameRef, StringUtil::wideToAnsi(part).c_str()); + } + + array->push(val); + delete val; + val = NULL; + } + + stack->pushNative(array, false); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *SXString::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type (RO) + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("string"); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Length (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Length") { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(_string); + _scValue->setInt(wstr.size()); + } else { + _scValue->setInt(strlen(_string)); + } + + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Capacity + ////////////////////////////////////////////////////////////////////////// + else if (name == "Capacity") { + _scValue->setInt(_capacity); + return _scValue; + } else { + return _scValue; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Capacity + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Capacity") == 0) { + int32 newCap = (uint32)value->getInt(); + if (newCap < (int32)(strlen(_string) + 1)) { + _gameRef->LOG(0, "Warning: cannot lower string capacity"); + } else if (newCap != _capacity) { + char *newStr = new char[newCap]; + if (newStr) { + memset(newStr, 0, newCap); + strcpy(newStr, _string); + delete[] _string; + _string = newStr; + _capacity = newCap; + } + } + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SXString::persist(BasePersistenceManager *persistMgr) { + + BaseScriptable::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_capacity)); + + if (persistMgr->getIsSaving()) { + if (_capacity > 0) { + persistMgr->putBytes((byte *)_string, _capacity); + } + } else { + if (_capacity > 0) { + _string = new char[_capacity]; + persistMgr->getBytes((byte *)_string, _capacity); + } else { + _string = NULL; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +int SXString::scCompare(BaseScriptable *val) { + return strcmp(_string, ((SXString *)val)->_string); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_ext_string.h b/engines/wintermute/base/scriptables/script_ext_string.h new file mode 100644 index 0000000000..00bffab3a9 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_ext_string.h @@ -0,0 +1,58 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SXSTRING_H +#define WINTERMUTE_SXSTRING_H + + +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +class SXString : public BaseScriptable { +public: + virtual int scCompare(BaseScriptable *Val); + DECLARE_PERSISTENT(SXString, BaseScriptable) + ScValue *scGetProperty(const Common::String &name); + bool scSetProperty(const char *name, ScValue *value); + bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + void scSetString(const char *val); + const char *scToString(); + void setStringVal(const char *val); + + SXString(BaseGame *inGame, ScStack *Stack); + virtual ~SXString(); + +private: + char *_string; + int _capacity; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_stack.cpp b/engines/wintermute/base/scriptables/script_stack.cpp new file mode 100644 index 0000000000..77367045c2 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_stack.cpp @@ -0,0 +1,232 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/base_game.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(ScStack, false) + +////////////////////////////////////////////////////////////////////////// +ScStack::ScStack(BaseGame *inGame) : BaseClass(inGame) { + _sP = -1; +} + + +////////////////////////////////////////////////////////////////////////// +ScStack::~ScStack() { + +#if _DEBUG + //_gameRef->LOG(0, "STAT: Stack size: %d, SP=%d", _values.size(), _sP); +#endif + + for (uint32 i = 0; i < _values.size(); i++) { + delete _values[i]; + } + _values.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::pop() { + if (_sP < 0) { + _gameRef->LOG(0, "Fatal: Stack underflow"); + return NULL; + } + + return _values[_sP--]; +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::push(ScValue *val) { + _sP++; + + if (_sP < (int32)_values.size()) { + _values[_sP]->cleanup(); + _values[_sP]->copy(val); + } else { + ScValue *copyVal = new ScValue(_gameRef); + copyVal->copy(val); + _values.add(copyVal); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getPushValue() { + _sP++; + + if (_sP >= (int32)_values.size()) { + ScValue *val = new ScValue(_gameRef); + _values.add(val); + } + _values[_sP]->cleanup(); + return _values[_sP]; +} + + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getTop() { + if (_sP < 0 || _sP >= (int32)_values.size()) { + return NULL; + } else { + return _values[_sP]; + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScStack::getAt(int index) { + index = _sP - index; + if (index < 0 || index >= (int32)_values.size()) { + return NULL; + } else { + return _values[index]; + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::correctParams(uint32 expectedParams) { + uint32 nuParams = (uint32)pop()->getInt(); + + if (expectedParams < nuParams) { // too many params + while (expectedParams < nuParams) { + //Pop(); + delete _values[_sP - expectedParams]; + _values.remove_at(_sP - expectedParams); + nuParams--; + _sP--; + } + } else if (expectedParams > nuParams) { // need more params + while (expectedParams > nuParams) { + //Push(null_val); + ScValue *nullVal = new ScValue(_gameRef); + nullVal->setNULL(); + _values.insert_at(_sP - nuParams + 1, nullVal); + nuParams++; + _sP++; + + if ((int32)_values.size() > _sP + 1) { + delete _values[_values.size() - 1]; + _values.remove_at(_values.size() - 1); + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushNULL() { + /* + ScValue* val = new ScValue(_gameRef); + val->setNULL(); + Push(val); + delete val; + */ + getPushValue()->setNULL(); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushInt(int val) { + /* + ScValue* val = new ScValue(_gameRef); + val->setInt(Val); + Push(val); + delete val; + */ + getPushValue()->setInt(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushFloat(double val) { + /* + ScValue* val = new ScValue(_gameRef); + val->setFloat(Val); + Push(val); + delete val; + */ + getPushValue()->setFloat(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushBool(bool val) { + /* + ScValue* val = new ScValue(_gameRef); + val->setBool(Val); + Push(val); + delete val; + */ + getPushValue()->setBool(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushString(const char *val) { + /* + ScValue* val = new ScValue(_gameRef); + val->setString(Val); + Push(val); + delete val; + */ + getPushValue()->setString(val); +} + + +////////////////////////////////////////////////////////////////////////// +void ScStack::pushNative(BaseScriptable *val, bool persistent) { + /* + ScValue* val = new ScValue(_gameRef); + val->setNative(Val, Persistent); + Push(val); + delete val; + */ + + getPushValue()->setNative(val, persistent); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScStack::persist(BasePersistenceManager *persistMgr) { + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_sP)); + _values.persist(persistMgr); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_stack.h b/engines/wintermute/base/scriptables/script_stack.h new file mode 100644 index 0000000000..86d246cf34 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_stack.h @@ -0,0 +1,66 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCSTACK_H +#define WINTERMUTE_SCSTACK_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/persistent.h" + +namespace Wintermute { + +class ScValue; +class BaseScriptable; + +class ScStack : public BaseClass { +public: + ScValue *getAt(int Index); + ScValue *getPushValue(); + DECLARE_PERSISTENT(ScStack, BaseClass) + void pushNative(BaseScriptable *val, bool persistent); + void pushString(const char *val); + void pushBool(bool val); + void pushInt(int val); + void pushFloat(double val); + void pushNULL(); + void correctParams(uint32 expectedParams); + ScValue *getTop(); + void push(ScValue *val); + ScValue *pop(); + ScStack(BaseGame *inGame); + virtual ~ScStack(); + BaseArray<ScValue *> _values; + int _sP; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/scriptables/script_value.cpp b/engines/wintermute/base/scriptables/script_value.cpp new file mode 100644 index 0000000000..0bc7ab5807 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_value.cpp @@ -0,0 +1,995 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_scriptable.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +IMPLEMENT_PERSISTENT(ScValue, false) + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame) : BaseClass(inGame) { + _type = VAL_NULL; + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, bool val) : BaseClass(inGame) { + _type = VAL_BOOL; + _valBool = val; + + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, int val) : BaseClass(inGame) { + _type = VAL_INT; + _valInt = val; + + _valFloat = 0.0f; + _valBool = false; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, double val) : BaseClass(inGame) { + _type = VAL_FLOAT; + _valFloat = val; + + _valInt = 0; + _valBool = false; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +ScValue::ScValue(BaseGame *inGame, const char *val) : BaseClass(inGame) { + _type = VAL_STRING; + _valString = NULL; + setStringVal(val); + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::cleanup(bool ignoreNatives) { + deleteProps(); + + if (_valString) { + delete[] _valString; + } + + if (!ignoreNatives) { + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + delete _valNative; + _valNative = NULL; + } + } + } + + + _type = VAL_NULL; + + _valBool = false; + _valInt = 0; + _valFloat = 0.0f; + _valNative = NULL; + _valString = NULL; + _valRef = NULL; + _persistent = false; + _isConstVar = false; +} + + + +////////////////////////////////////////////////////////////////////////// +ScValue::~ScValue() { + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *ScValue::getProp(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getProp(name); + } + + if (_type == VAL_STRING && strcmp(name, "Length") == 0) { + _gameRef->_scValue->_type = VAL_INT; + + if (_gameRef->_textEncoding == TEXT_ANSI) { + _gameRef->_scValue->setInt(strlen(_valString)); + } else { + WideString wstr = StringUtil::utf8ToWide(_valString); + _gameRef->_scValue->setInt(wstr.size()); + } + + return _gameRef->_scValue; + } + + ScValue *ret = NULL; + + if (_type == VAL_NATIVE && _valNative) { + ret = _valNative->scGetProperty(name); + } + + if (ret == NULL) { + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + ret = _valIter->_value; + } + } + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::deleteProp(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->deleteProp(name); + } + + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + delete _valIter->_value; + _valIter->_value = NULL; + } + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProp(const char *name, ScValue *val, bool copyWhole, bool setAsConst) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->setProp(name, val); + } + + bool ret = STATUS_FAILED; + if (_type == VAL_NATIVE && _valNative) { + ret = _valNative->scSetProperty(name, val); + } + + if (DID_FAIL(ret)) { + ScValue *newVal = NULL; + + _valIter = _valObject.find(name); + if (_valIter != _valObject.end()) { + newVal = _valIter->_value; + } + if (!newVal) { + newVal = new ScValue(_gameRef); + } else { + newVal->cleanup(); + } + + newVal->copy(val, copyWhole); + newVal->_isConstVar = setAsConst; + _valObject[name] = newVal; + + if (_type != VAL_NATIVE) { + _type = VAL_OBJECT; + } + + /* + _valIter = _valObject.find(Name); + if (_valIter != _valObject.end()){ + delete _valIter->_value; + _valIter->_value = NULL; + } + ScValue* val = new ScValue(_gameRef); + val->Copy(Val, CopyWhole); + val->_isConstVar = SetAsConst; + _valObject[Name] = val; + + if (_type!=VAL_NATIVE) _type = VAL_OBJECT; + */ + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::propExists(const char *name) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->propExists(name); + } + _valIter = _valObject.find(name); + + return (_valIter != _valObject.end()); +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::deleteProps() { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + delete(ScValue *)_valIter->_value; + _valIter++; + } + _valObject.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::CleanProps(bool includingNatives) { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + if (!_valIter->_value->_isConstVar && (!_valIter->_value->isNative() || includingNatives)) { + _valIter->_value->setNULL(); + } + _valIter++; + } +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isNULL() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isNULL(); + } + + return (_type == VAL_NULL); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isNative() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isNative(); + } + + return (_type == VAL_NATIVE); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isString() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isString(); + } + + return (_type == VAL_STRING); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isFloat() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isFloat(); + } + + return (_type == VAL_FLOAT); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isInt() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isInt(); + } + + return (_type == VAL_INT); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isBool() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isBool(); + } + + return (_type == VAL_BOOL); +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::isObject() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->isObject(); + } + + return (_type == VAL_OBJECT); +} + + +////////////////////////////////////////////////////////////////////////// +TValType ScValue::getTypeTolerant() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getType(); + } + + return _type; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setBool(bool val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setBool(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetBool(val); + return; + } + + _valBool = val; + _type = VAL_BOOL; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setInt(int val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setInt(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetInt(val); + return; + } + + _valInt = val; + _type = VAL_INT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setFloat(double val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setFloat(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetFloat(val); + return; + } + + _valFloat = val; + _type = VAL_FLOAT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setString(const char *val) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setString(val); + return; + } + + if (_type == VAL_NATIVE) { + _valNative->scSetString(val); + return; + } + + setStringVal(val); + if (_valString) { + _type = VAL_STRING; + } else { + _type = VAL_NULL; + } +} + +void ScValue::setString(const Common::String &val) { + setString(val.c_str()); +} + +////////////////////////////////////////////////////////////////////////// +void ScValue::setStringVal(const char *val) { + if (_valString) { + delete[] _valString; + _valString = NULL; + } + + if (val == NULL) { + _valString = NULL; + return; + } + + _valString = new char [strlen(val) + 1]; + if (_valString) { + strcpy(_valString, val); + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setNULL() { + if (_type == VAL_VARIABLE_REF) { + _valRef->setNULL(); + return; + } + + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + delete _valNative; + } + } + _valNative = NULL; + deleteProps(); + + _type = VAL_NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setNative(BaseScriptable *val, bool persistent) { + if (_type == VAL_VARIABLE_REF) { + _valRef->setNative(val, persistent); + return; + } + + if (val == NULL) { + setNULL(); + } else { + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + if (_valNative != val) { + delete _valNative; + } + _valNative = NULL; + } + } + + _type = VAL_NATIVE; + _persistent = persistent; + + _valNative = val; + if (_valNative && !_persistent) { + _valNative->_refCount++; + } + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setObject() { + if (_type == VAL_VARIABLE_REF) { + _valRef->setObject(); + return; + } + + deleteProps(); + _type = VAL_OBJECT; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setReference(ScValue *val) { + _valRef = val; + _type = VAL_VARIABLE_REF; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::getBool(bool defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getBool(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool; + + case VAL_NATIVE: + return _valNative->scToBool(); + + case VAL_INT: + return (_valInt != 0); + + case VAL_FLOAT: + return (_valFloat != 0.0f); + + case VAL_STRING: + return (scumm_stricmp(_valString, "1") == 0 || scumm_stricmp(_valString, "yes") == 0 || scumm_stricmp(_valString, "true") == 0); + + default: + return defaultVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +int ScValue::getInt(int defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getInt(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool ? 1 : 0; + + case VAL_NATIVE: + return _valNative->scToInt(); + + case VAL_INT: + return _valInt; + + case VAL_FLOAT: + return (int)_valFloat; + + case VAL_STRING: + return atoi(_valString); + + default: + return defaultVal; + } +} + + +////////////////////////////////////////////////////////////////////////// +double ScValue::getFloat(double defaultVal) { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getFloat(); + } + + switch (_type) { + case VAL_BOOL: + return _valBool ? 1.0f : 0.0f; + + case VAL_NATIVE: + return _valNative->scToFloat(); + + case VAL_INT: + return (double)_valInt; + + case VAL_FLOAT: + return _valFloat; + + case VAL_STRING: + return atof(_valString); + + default: + return defaultVal; + } +} + +////////////////////////////////////////////////////////////////////////// +void *ScValue::getMemBuffer() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getMemBuffer(); + } + + if (_type == VAL_NATIVE) { + return _valNative->scToMemBuffer(); + } else { + return (void *)NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *ScValue::getString() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getString(); + } + + switch (_type) { + case VAL_OBJECT: + setStringVal("[object]"); + break; + + case VAL_NULL: + setStringVal("[null]"); + break; + + case VAL_NATIVE: { + const char *strVal = _valNative->scToString(); + setStringVal(strVal); + return strVal; + break; + } + + case VAL_BOOL: + setStringVal(_valBool ? "yes" : "no"); + break; + + case VAL_INT: { + char dummy[50]; + sprintf(dummy, "%d", _valInt); + setStringVal(dummy); + break; + } + + case VAL_FLOAT: { + char dummy[50]; + sprintf(dummy, "%f", _valFloat); + setStringVal(dummy); + break; + } + + case VAL_STRING: + break; + + default: + setStringVal(""); + } + + return _valString; +} + + +////////////////////////////////////////////////////////////////////////// +BaseScriptable *ScValue::getNative() { + if (_type == VAL_VARIABLE_REF) { + return _valRef->getNative(); + } + + if (_type == VAL_NATIVE) { + return _valNative; + } else { + return NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +TValType ScValue::getType() { + return _type; +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::copy(ScValue *orig, bool copyWhole) { + _gameRef = orig->_gameRef; + + if (_valNative && !_persistent) { + _valNative->_refCount--; + if (_valNative->_refCount <= 0) { + if (_valNative != orig->_valNative) { + delete _valNative; + } + _valNative = NULL; + } + } + + if (orig->_type == VAL_VARIABLE_REF && orig->_valRef && copyWhole) { + orig = orig->_valRef; + } + + cleanup(true); + + _type = orig->_type; + _valBool = orig->_valBool; + _valInt = orig->_valInt; + _valFloat = orig->_valFloat; + setStringVal(orig->_valString); + + _valRef = orig->_valRef; + _persistent = orig->_persistent; + + _valNative = orig->_valNative; + if (_valNative && !_persistent) { + _valNative->_refCount++; + } +//!!!! ref->native++ + + // copy properties + if (orig->_type == VAL_OBJECT && orig->_valObject.size() > 0) { + orig->_valIter = orig->_valObject.begin(); + while (orig->_valIter != orig->_valObject.end()) { + _valObject[orig->_valIter->_key] = new ScValue(_gameRef); + _valObject[orig->_valIter->_key]->copy(orig->_valIter->_value); + orig->_valIter++; + } + } else { + _valObject.clear(); + } +} + + +////////////////////////////////////////////////////////////////////////// +void ScValue::setValue(ScValue *val) { + if (val->_type == VAL_VARIABLE_REF) { + setValue(val->_valRef); + return; + } + + // if being assigned a simple type, preserve native state + if (_type == VAL_NATIVE && (val->_type == VAL_INT || val->_type == VAL_STRING || val->_type == VAL_BOOL)) { + switch (val->_type) { + case VAL_INT: + _valNative->scSetInt(val->getInt()); + break; + case VAL_FLOAT: + _valNative->scSetFloat(val->getFloat()); + break; + case VAL_BOOL: + _valNative->scSetBool(val->getBool()); + break; + case VAL_STRING: + _valNative->scSetString(val->getString()); + break; + default: + warning("ScValue::setValue - unhandled enum"); + break; + } + } + // otherwise just copy everything + else { + copy(val); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::persist(BasePersistenceManager *persistMgr) { + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_persistent)); + persistMgr->transfer(TMEMBER(_isConstVar)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_valBool)); + persistMgr->transfer(TMEMBER(_valFloat)); + persistMgr->transfer(TMEMBER(_valInt)); + persistMgr->transfer(TMEMBER(_valNative)); + + int size; + const char *str; + if (persistMgr->getIsSaving()) { + size = _valObject.size(); + persistMgr->transfer("", &size); + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + str = _valIter->_key.c_str(); + persistMgr->transfer("", &str); + persistMgr->transfer("", &_valIter->_value); + + _valIter++; + } + } else { + ScValue *val; + persistMgr->transfer("", &size); + for (int i = 0; i < size; i++) { + persistMgr->transfer("", &str); + persistMgr->transfer("", &val); + + _valObject[str] = val; + delete[] str; + } + } + + persistMgr->transfer(TMEMBER(_valRef)); + persistMgr->transfer(TMEMBER(_valString)); + + /* + FILE* f = fopen("c:\\val.log", "a+"); + switch(_type) + { + case VAL_STRING: + fprintf(f, "str %s\n", _valString); + break; + + case VAL_INT: + fprintf(f, "int %d\n", _valInt); + break; + + case VAL_BOOL: + fprintf(f, "bool %d\n", _valBool); + break; + + case VAL_NULL: + fprintf(f, "null\n"); + break; + + case VAL_NATIVE: + fprintf(f, "native\n"); + break; + + case VAL_VARIABLE_REF: + fprintf(f, "ref\n"); + break; + + case VAL_OBJECT: + fprintf(f, "obj\n"); + break; + + case VAL_FLOAT: + fprintf(f, "float\n"); + break; + + } + fclose(f); + */ + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::saveAsText(BaseDynamicBuffer *buffer, int indent) { + _valIter = _valObject.begin(); + while (_valIter != _valObject.end()) { + buffer->putTextIndent(indent, "PROPERTY {\n"); + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _valIter->_key.c_str()); + buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _valIter->_value->getString()); + buffer->putTextIndent(indent, "}\n\n"); + + _valIter++; + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// -1 ... left is less, 0 ... equals, 1 ... left is greater +int ScValue::compare(ScValue *val1, ScValue *val2) { + // both natives? + if (val1->isNative() && val2->isNative()) { + // same class? + if (strcmp(val1->getNative()->getClassName(), val2->getNative()->getClassName()) == 0) { + return val1->getNative()->scCompare(val2->getNative()); + } else { + return strcmp(val1->getString(), val2->getString()); + } + } + + // both objects? + if (val1->isObject() && val2->isObject()) { + return -1; + } + + + // null states + if (val1->isNULL() && !val2->isNULL()) { + return -1; + } else if (!val1->isNULL() && val2->isNULL()) { + return 1; + } else if (val1->isNULL() && val2->isNULL()) { + return 0; + } + + // one of them is string? convert both to string + if (val1->isString() || val2->isString()) { + return strcmp(val1->getString(), val2->getString()); + } + + // one of them is float? + if (val1->isFloat() || val2->isFloat()) { + if (val1->getFloat() < val2->getFloat()) { + return -1; + } else if (val1->getFloat() > val2->getFloat()) { + return 1; + } else { + return 0; + } + } + + // otherwise compare as int's + if (val1->getInt() < val2->getInt()) { + return -1; + } else if (val1->getInt() > val2->getInt()) { + return 1; + } else { + return 0; + } +} + + +////////////////////////////////////////////////////////////////////////// +int ScValue::compareStrict(ScValue *val1, ScValue *val2) { + if (val1->getTypeTolerant() != val2->getTypeTolerant()) { + return -1; + } else { + return ScValue::compare(val1, val2); + } +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, int value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, const char *value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, double value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName, bool value) { + ScValue *val = new ScValue(_gameRef, value); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + + +////////////////////////////////////////////////////////////////////////// +bool ScValue::setProperty(const char *propName) { + ScValue *val = new ScValue(_gameRef); + bool ret = DID_SUCCEED(setProp(propName, val)); + delete val; + return ret; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/scriptables/script_value.h b/engines/wintermute/base/scriptables/script_value.h new file mode 100644 index 0000000000..bf7d9cd8a1 --- /dev/null +++ b/engines/wintermute/base/scriptables/script_value.h @@ -0,0 +1,113 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SCVALUE_H +#define WINTERMUTE_SCVALUE_H + + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/base/scriptables/dcscript.h" // Added by ClassView +#include "common/str.h" + +namespace Wintermute { + +class ScScript; +class BaseScriptable; + +class ScValue : public BaseClass { +public: + static int compare(ScValue *val1, ScValue *val2); + static int compareStrict(ScValue *val1, ScValue *val2); + TValType getTypeTolerant(); + void cleanup(bool ignoreNatives = false); + DECLARE_PERSISTENT(ScValue, BaseClass) + + bool _isConstVar; + bool saveAsText(BaseDynamicBuffer *buffer, int indent); + void setValue(ScValue *val); + bool _persistent; + bool propExists(const char *name); + void copy(ScValue *orig, bool copyWhole = false); + void setStringVal(const char *val); + TValType getType(); + bool getBool(bool defaultVal = false); + int getInt(int defaultVal = 0); + double getFloat(double defaultVal = 0.0f); + const char *getString(); + void *getMemBuffer(); + BaseScriptable *getNative(); + bool deleteProp(const char *name); + void deleteProps(); + void CleanProps(bool includingNatives); + void setBool(bool val); + void setInt(int val); + void setFloat(double val); + void setString(const char *val); + void setString(const Common::String &val); + void setNULL(); + void setNative(BaseScriptable *val, bool persistent = false); + void setObject(); + void setReference(ScValue *val); + bool isNULL(); + bool isNative(); + bool isString(); + bool isBool(); + bool isFloat(); + bool isInt(); + bool isObject(); + bool setProp(const char *name, ScValue *val, bool copyWhole = false, bool setAsConst = false); + ScValue *getProp(const char *name); + BaseScriptable *_valNative; + ScValue *_valRef; +private: + bool _valBool; + int _valInt; + double _valFloat; + char *_valString; +public: + TValType _type; + ScValue(BaseGame *inGame); + ScValue(BaseGame *inGame, bool Val); + ScValue(BaseGame *inGame, int Val); + ScValue(BaseGame *inGame, double Val); + ScValue(BaseGame *inGame, const char *Val); + virtual ~ScValue(); + Common::HashMap<Common::String, ScValue *> _valObject; + Common::HashMap<Common::String, ScValue *>::iterator _valIter; + + bool setProperty(const char *propName, int value); + bool setProperty(const char *propName, const char *value); + bool setProperty(const char *propName, double value); + bool setProperty(const char *propName, bool value); + bool setProperty(const char *propName); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound.cpp b/engines/wintermute/base/sound/base_sound.cpp new file mode 100644 index 0000000000..00d07cd3c2 --- /dev/null +++ b/engines/wintermute/base/sound/base_sound.cpp @@ -0,0 +1,292 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(BaseSound, false) + +BaseSound::BaseSound(BaseGame *inGame) : BaseClass(inGame) { + _sound = NULL; + _soundFilename = ""; + + _soundType = Audio::Mixer::kSFXSoundType; + _soundStreamed = false; + _soundLooping = false; + _soundPlaying = false; + _soundPaused = false; + _soundFreezePaused = false; + _soundPosition = 0; + _soundPrivateVolume = 0; + _soundLoopStart = 0; + + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; +} + +BaseSound::~BaseSound() { + if (_sound) { + _gameRef->_soundMgr->removeSound(_sound); + } + _sound = NULL; +} + +bool BaseSound::setSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) { + if (_sound) { + _gameRef->_soundMgr->removeSound(_sound); + _sound = NULL; + } + _soundFilename = Common::String(); // Set empty + + _sound = _gameRef->_soundMgr->addSound(filename, type, streamed); + if (_sound) { + _soundFilename = filename; + + _soundType = type; + _soundStreamed = streamed; + + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::setSoundSimple() { + _sound = _gameRef->_soundMgr->addSound(_soundFilename, _soundType, _soundStreamed); + if (_sound) { + if (_soundPosition) { + _sound->setPosition(_soundPosition); + } + _sound->setLooping(_soundLooping); + _sound->setPrivateVolume(_soundPrivateVolume); + _sound->setLoopStart(_soundLoopStart); + _sound->_freezePaused = _soundFreezePaused; + if (_soundPlaying) { + return _sound->resume(); + } else { + return STATUS_OK; + } + } else { + return STATUS_FAILED; + } +} + +uint32 BaseSound::getLength() { + if (_sound) { + return _sound->getLength(); + } else { + return 0; + } +} + +bool BaseSound::play(bool looping) { + if (_sound) { + _soundPaused = false; + return _sound->play(looping, _soundPosition); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::stop() { + if (_sound) { + _soundPaused = false; + return _sound->stop(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::pause(bool freezePaused) { + if (_sound) { + _soundPaused = true; + if (freezePaused) { + _sound->_freezePaused = true; + } + return _sound->pause(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::resume() { + if (_sound && _soundPaused) { + _soundPaused = false; + return _sound->resume(); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::persist(BasePersistenceManager *persistMgr) { + if (persistMgr->getIsSaving() && _sound) { + _soundPlaying = _sound->isPlaying(); + _soundLooping = _sound->_looping; + _soundPrivateVolume = _sound->_privateVolume; + if (_soundPlaying) { + _soundPosition = _sound->getPosition(); + } + _soundLoopStart = _sound->_loopStart; + _soundFreezePaused = _sound->_freezePaused; + } + + if (persistMgr->getIsSaving()) { + _sFXType = SFX_NONE; + _sFXParam1 = _sFXParam2 = _sFXParam3 = _sFXParam4 = 0; + } + + persistMgr->transfer(TMEMBER(_gameRef)); + + persistMgr->transfer(TMEMBER(_soundFilename)); + persistMgr->transfer(TMEMBER(_soundLooping)); + persistMgr->transfer(TMEMBER(_soundPaused)); + persistMgr->transfer(TMEMBER(_soundFreezePaused)); + persistMgr->transfer(TMEMBER(_soundPlaying)); + persistMgr->transfer(TMEMBER(_soundPosition)); + persistMgr->transfer(TMEMBER(_soundPrivateVolume)); + persistMgr->transfer(TMEMBER(_soundStreamed)); + persistMgr->transfer(TMEMBER_INT(_soundType)); + persistMgr->transfer(TMEMBER(_soundLoopStart)); + + return STATUS_OK; +} + +bool BaseSound::isPlaying() { + return _sound && _sound->isPlaying(); +} + +bool BaseSound::isPaused() { + return _sound && _soundPaused; +} + +bool BaseSound::setPositionTime(uint32 time) { + if (!_sound) { + return STATUS_FAILED; + } + _soundPosition = time; + bool ret = _sound->setPosition(_soundPosition); + if (_sound->isPlaying()) { + _soundPosition = 0; + } + return ret; +} + +uint32 BaseSound::getPositionTime() { + if (!_sound) { + return 0; + } + + if (!_sound->isPlaying()) { + return 0; + } else { + return _sound->getPosition(); + } +} + +bool BaseSound::setVolumePercent(int percent) { + if (!_sound) { + return STATUS_FAILED; + } else { + return _sound->setPrivateVolume(percent * 255 / 100); + } +} + +bool BaseSound::setVolume(int volume) { + if (!_sound) { + return STATUS_FAILED; + } else { + return _sound->setPrivateVolume(volume); + } +} + +bool BaseSound::setPrivateVolume(int volume) { + if (!_sound) { + return STATUS_FAILED; + } else { + _sound->_privateVolume = volume; + return STATUS_OK; + } +} + +int BaseSound::getVolumePercent() { + if (!_sound) { + return 0; + } else { + return _sound->_privateVolume * 100 / 255; + } +} + +int BaseSound::getVolume() { + if (!_sound) { + return 0; + } else { + return _sound->_privateVolume; + } +} + +bool BaseSound::setLoopStart(uint32 pos) { + if (!_sound) { + return STATUS_FAILED; + } else { + _sound->setLoopStart(pos); + return STATUS_OK; + } +} + +bool BaseSound::setPan(float pan) { + if (_sound) { + return _sound->setPan(pan); + } else { + return STATUS_FAILED; + } +} + +bool BaseSound::applyFX(TSFXType type, float param1, float param2, float param3, float param4) { + if (!_sound) { + return STATUS_OK; + } + + if (type != _sFXType || param1 != _sFXParam1 || param2 != _sFXParam2 || param3 != _sFXParam3 || param4 != _sFXParam4) { + bool ret = _sound->applyFX(type, param1, param2, param3, param4); + + _sFXType = type; + _sFXParam1 = param1; + _sFXParam2 = param2; + _sFXParam3 = param3; + _sFXParam4 = param4; + + return ret; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound.h b/engines/wintermute/base/sound/base_sound.h new file mode 100644 index 0000000000..637061b7cc --- /dev/null +++ b/engines/wintermute/base/sound/base_sound.h @@ -0,0 +1,87 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUND_H +#define WINTERMUTE_BASE_SOUND_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/persistent.h" +#include "audio/mixer.h" + +namespace Wintermute { + +class BaseSoundBuffer; +class BaseSound : public BaseClass { +public: + bool setPan(float pan); + int getVolume(); + int getVolumePercent(); + bool setVolumePercent(int percent); + bool setVolume(int volume); + bool setPrivateVolume(int volume); + bool setLoopStart(uint32 pos); + uint32 getPositionTime(); + bool setPositionTime(uint32 time); + bool isPlaying(); + bool isPaused(); + DECLARE_PERSISTENT(BaseSound, BaseClass) + bool resume(); + bool pause(bool freezePaused = false); + bool stop(); + bool play(bool looping = false); + uint32 getLength(); + const char *getFilename() { return _soundFilename.c_str(); } + bool setSoundSimple(); + bool setSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false); + BaseSound(BaseGame *inGame); + virtual ~BaseSound(); + + bool applyFX(TSFXType type = SFX_NONE, float param1 = 0, float param2 = 0, float param3 = 0, float param4 = 0); +private: + Common::String _soundFilename; + bool _soundStreamed; + Audio::Mixer::SoundType _soundType; + int _soundPrivateVolume; + uint32 _soundLoopStart; + uint32 _soundPosition; + bool _soundPlaying; + bool _soundLooping; + bool _soundPaused; + bool _soundFreezePaused; + TSFXType _sFXType; + float _sFXParam1; + float _sFXParam2; + float _sFXParam3; + float _sFXParam4; + BaseSoundBuffer *_sound; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound_buffer.cpp b/engines/wintermute/base/sound/base_sound_buffer.cpp new file mode 100644 index 0000000000..250570f2b8 --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_buffer.cpp @@ -0,0 +1,295 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/wintermute.h" +#include "audio/audiostream.h" +#include "audio/mixer.h" +#include "audio/decoders/vorbis.h" +#include "audio/decoders/wave.h" +#include "audio/decoders/raw.h" +#include "common/system.h" +#include "common/substream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +#define MAX_NONSTREAMED_FILE_SIZE 1024*1024 + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer::BaseSoundBuffer(BaseGame *inGame) : BaseClass(inGame) { + _stream = NULL; + _handle = NULL; +// _sync = NULL; + + _streamed = false; + _filename = ""; + _file = NULL; + _privateVolume = 255; + _volume = 255; + + _looping = false; + _loopStart = 0; + _startPos = 0; + + _type = Audio::Mixer::kSFXSoundType; + + _freezePaused = false; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer::~BaseSoundBuffer() { + stop(); + + if (_handle) { + g_system->getMixer()->stopHandle(*_handle); + delete _handle; + _handle = NULL; + } + delete _stream; + _stream = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setStreaming(bool streamed, uint32 numBlocks, uint32 blockSize) { + _streamed = streamed; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::loadFromFile(const Common::String &filename, bool forceReload) { + debugC(kWintermuteDebugAudio, "BSoundBuffer::LoadFromFile(%s,%d)", filename.c_str(), forceReload); + + // Load a file, but avoid having the File-manager handle the disposal of it. + _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false); + if (!_file) { + _gameRef->LOG(0, "Error opening sound file '%s'", filename.c_str()); + return STATUS_FAILED; + } + Common::String strFilename(filename); + strFilename.toLowercase(); + if (strFilename.hasSuffix(".ogg")) { + _stream = Audio::makeVorbisStream(_file, DisposeAfterUse::YES); + } else if (strFilename.hasSuffix(".wav")) { + int waveSize, waveRate; + byte waveFlags; + uint16 waveType; + + if (Audio::loadWAVFromStream(*_file, waveSize, waveRate, waveFlags, &waveType)) { + if (waveType == 1) { + // We need to wrap the file in a substream to make sure the size is right. + _file = new Common::SeekableSubReadStream(_file, 0, waveSize); + _stream = Audio::makeRawStream(_file, waveRate, waveFlags, DisposeAfterUse::YES); + } else { + error("BSoundBuffer::LoadFromFile - WAVE not supported yet for %s with type %d", filename.c_str(), waveType); + } + } + } else { + error("BSoundBuffer::LoadFromFile - Unknown filetype for %s", filename.c_str()); + } + if (!_stream) { + return STATUS_FAILED; + } + _filename = filename; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::play(bool looping, uint32 startSample) { + if (_handle) { + g_system->getMixer()->stopHandle(*_handle); + delete _handle; + _handle = NULL; + } + // Store the loop-value for save-games. + setLooping(looping); + if (_stream) { + _stream->seek(startSample); + _handle = new Audio::SoundHandle; + if (_looping) { + Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + g_system->getMixer()->playStream(_type, _handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); + } else { + g_system->getMixer()->playStream(_type, _handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setLooping(bool looping) { + if (isPlaying()) { + // This warning is here, to see if this is ever the case. + warning("BSoundBuffer::SetLooping(%d) - won't change a playing sound", looping); // TODO + } + _looping = looping; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::resume() { + // If the sound was paused while active: + if (_stream && _handle) { + g_system->getMixer()->pauseHandle(*_handle, false); + } else if (_stream) { // Otherwise we come from a savegame, and thus have no handle + play(_looping, _startPos); + } else { + warning("BaseSoundBuffer::resume - Called without a handle or a stream"); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::stop() { + if (_stream && _handle) { + g_system->getMixer()->stopHandle(*_handle); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::pause() { + if (_stream && _handle) { + g_system->getMixer()->pauseHandle(*_handle, true); + } + return STATUS_OK; + +} + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSoundBuffer::getLength() { + if (_stream) { + uint32 len = _stream->getLength().msecs(); + return len * 1000; + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::setType(Audio::Mixer::SoundType type) { + _type = type; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundBuffer::updateVolume() { + setVolume(_privateVolume); +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setVolume(int volume) { + _volume = volume * _gameRef->_soundMgr->getMasterVolume() / 255; + if (_stream && _handle) { + byte vol = (byte)(_volume); + g_system->getMixer()->setChannelVolume(*_handle, vol); + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPrivateVolume(int volume) { + _privateVolume = volume; + return setVolume(_privateVolume); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::isPlaying() { + if (_stream && _handle) { + return _freezePaused || g_system->getMixer()->isSoundHandleActive(*_handle); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseSoundBuffer::getPosition() { + if (_stream && _handle) { + uint32 pos = g_system->getMixer()->getSoundElapsedTime(*_handle); + return pos; + } + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPosition(uint32 pos) { + if (isPlaying()) { + warning("BaseSoundBuffer::SetPosition - not implemented for playing sounds yet."); + } + _startPos = pos; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setLoopStart(uint32 pos) { + _loopStart = pos; + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::setPan(float pan) { + if (_handle) { + g_system->getMixer()->setChannelBalance(*_handle, (int8)(pan * 127)); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundBuffer::applyFX(TSFXType type, float param1, float param2, float param3, float param4) { + // This function was already stubbed out in WME Lite, and thus isn't reimplemented here either. + switch (type) { + case SFX_ECHO: + //warning("BaseSoundBuffer::ApplyFX(SFX_ECHO, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4); + break; + + case SFX_REVERB: + //warning("BaseSoundBuffer::ApplyFX(SFX_REVERB, %f, %f, %f, %f) - not implemented yet", param1, param2, param3, param4); + break; + + default: + break; + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound_buffer.h b/engines/wintermute/base/sound/base_sound_buffer.h new file mode 100644 index 0000000000..9c39f4c34b --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_buffer.h @@ -0,0 +1,100 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUNDBUFFER_H +#define WINTERMUTE_BASE_SOUNDBUFFER_H + + +#include "engines/wintermute/base/base.h" +#include "audio/mixer.h" +#include "common/stream.h" + +namespace Audio { +class SeekableAudioStream; +class SoundHandle; +} + +namespace Wintermute { + +class BaseFile; +class BaseSoundBuffer : public BaseClass { +public: + + BaseSoundBuffer(BaseGame *inGame); + virtual ~BaseSoundBuffer(); + + bool pause(); + bool play(bool looping = false, uint32 startSample = 0); + bool resume(); + bool stop(); + bool isPlaying(); + + void setLooping(bool looping); + + uint32 getPosition(); + bool setPosition(uint32 pos); + uint32 getLength(); + + bool setLoopStart(uint32 pos); + uint32 getLoopStart() const { + return _loopStart; + } + + bool setPan(float pan); + bool setPrivateVolume(int colume); + bool setVolume(int colume); + void updateVolume(); + + void setType(Audio::Mixer::SoundType Type); + + bool loadFromFile(const Common::String &filename, bool forceReload = false); + void setStreaming(bool streamed, uint32 numBlocks = 0, uint32 blockSize = 0); + bool applyFX(TSFXType type, float param1, float param2, float param3, float param4); + + //HSTREAM _stream; + //HSYNC _sync; + Audio::SeekableAudioStream *_stream; + Audio::SoundHandle *_handle; + + bool _freezePaused; + uint32 _loopStart; + Audio::Mixer::SoundType _type; + bool _looping; + + int _privateVolume; +private: + uint32 _startPos; + Common::String _filename; + bool _streamed; + Common::SeekableReadStream *_file; + int _volume; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp new file mode 100644 index 0000000000..441793144d --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_manager.cpp @@ -0,0 +1,283 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/utils/path_util.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound_buffer.h" +#include "engines/wintermute/wintermute.h" +#include "common/config-manager.h" +#include "audio/mixer.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +//IMPLEMENT_PERSISTENT(BaseSoundMgr, true); + +////////////////////////////////////////////////////////////////////////// +BaseSoundMgr::BaseSoundMgr(BaseGame *inGame) : BaseClass(inGame) { + _soundAvailable = false; + _volumeMaster = 255; +} + + +////////////////////////////////////////////////////////////////////////// +BaseSoundMgr::~BaseSoundMgr() { + saveSettings(); + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::cleanup() { + for (uint32 i = 0; i < _sounds.size(); i++) { + delete _sounds[i]; + } + _sounds.clear(); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void BaseSoundMgr::saveSettings() { + if (_soundAvailable) { + ConfMan.setInt("master_volume", _volumeMaster); + } +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::initialize() { + _soundAvailable = false; + + if (!g_system->getMixer()->isReady()) { + return STATUS_FAILED; + } + _volumeMaster = (ConfMan.hasKey("master_volume") ? ConfMan.getInt("master_volume") : 255); + _soundAvailable = true; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseSoundBuffer *BaseSoundMgr::addSound(const Common::String &filename, Audio::Mixer::SoundType type, bool streamed) { + if (!_soundAvailable) { + return NULL; + } + + BaseSoundBuffer *sound; + + Common::String useFilename = filename; + // try to switch WAV to OGG file (if available) + AnsiString ext = PathUtil::getExtension(filename); + if (StringUtil::compareNoCase(ext, "wav")) { + AnsiString path = PathUtil::getDirectoryName(filename); + AnsiString name = PathUtil::getFileNameWithoutExtension(filename); + + AnsiString newFile = PathUtil::combine(path, name + "ogg"); + if (BaseFileManager::getEngineInstance()->hasFile(newFile)) { + useFilename = newFile; + } + } + + sound = new BaseSoundBuffer(_gameRef); + if (!sound) { + return NULL; + } + + sound->setStreaming(streamed); + sound->setType(type); + + + bool res = sound->loadFromFile(useFilename); + if (DID_FAIL(res)) { + _gameRef->LOG(res, "Error loading sound '%s'", useFilename.c_str()); + delete sound; + return NULL; + } + + // Make sure the master-volume is applied to the sound. + sound->updateVolume(); + + // register sound + _sounds.push_back(sound); + + return sound; + + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type) { + if (!sound) { + return STATUS_FAILED; + } + + // Make sure the master-volume is applied to the sound. + sound->updateVolume(); + + // register sound + _sounds.push_back(sound); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::removeSound(BaseSoundBuffer *sound) { + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i] == sound) { + delete _sounds[i]; + _sounds.remove_at(i); + return STATUS_OK; + } + } + + return STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setVolume(Audio::Mixer::SoundType type, int volume) { + if (!_soundAvailable) { + return STATUS_OK; + } + + switch (type) { + case Audio::Mixer::kSFXSoundType: + ConfMan.setInt("sfx_volume", volume); + break; + case Audio::Mixer::kSpeechSoundType: + ConfMan.setInt("speech_volume", volume); + break; + case Audio::Mixer::kMusicSoundType: + ConfMan.setInt("music_volume", volume); + break; + case Audio::Mixer::kPlainSoundType: + error("Plain sound type shouldn't be used in WME"); + } + g_engine->syncSoundSettings(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setVolumePercent(Audio::Mixer::SoundType type, byte percent) { + return setVolume(type, percent * 255 / 100); +} + + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getVolumePercent(Audio::Mixer::SoundType type) { + int volume = 0; + + switch (type) { + case Audio::Mixer::kSFXSoundType: + case Audio::Mixer::kSpeechSoundType: + case Audio::Mixer::kMusicSoundType: + volume = g_system->getMixer()->getVolumeForSoundType(type); + break; + default: + error("Sound-type not set"); + break; + } + + return (byte)(volume * 100 / 255); +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setMasterVolume(byte value) { + _volumeMaster = value; + for (uint32 i = 0; i < _sounds.size(); i++) { + _sounds[i]->updateVolume(); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::setMasterVolumePercent(byte percent) { + setMasterVolume(percent * 255 / 100); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getMasterVolumePercent() { + return getMasterVolume() * 100 / 255; +} + +////////////////////////////////////////////////////////////////////////// +byte BaseSoundMgr::getMasterVolume() { + return (byte)_volumeMaster; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::pauseAll(bool includingMusic) { + + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i]->isPlaying() && (_sounds[i]->_type != Audio::Mixer::kMusicSoundType || includingMusic)) { + _sounds[i]->pause(); + _sounds[i]->_freezePaused = true; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool BaseSoundMgr::resumeAll() { + + for (uint32 i = 0; i < _sounds.size(); i++) { + if (_sounds[i]->_freezePaused) { + _sounds[i]->resume(); + _sounds[i]->_freezePaused = false; + } + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +float BaseSoundMgr::posToPan(int x, int y) { + float relPos = (float)x / ((float)_gameRef->_renderer->_width); + + float minPan = -0.7f; + float maxPan = 0.7f; + + return minPan + relPos * (maxPan - minPan); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/base/sound/base_sound_manager.h b/engines/wintermute/base/sound/base_sound_manager.h new file mode 100644 index 0000000000..36a729b5ae --- /dev/null +++ b/engines/wintermute/base/sound/base_sound_manager.h @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_BASE_SOUNDMGR_H +#define WINTERMUTE_BASE_SOUNDMGR_H + +#include "engines/wintermute/coll_templ.h" +#include "engines/wintermute/base/base.h" +#include "audio/mixer.h" +#include "common/array.h" + +namespace Wintermute { +class BaseSoundBuffer; +class BaseSoundMgr : public BaseClass { +public: + float posToPan(int x, int y); + bool resumeAll(); + bool pauseAll(bool includingMusic = true); + bool cleanup(); + //DECLARE_PERSISTENT(BaseSoundMgr, BaseClass); + byte getMasterVolumePercent(); + byte getMasterVolume(); + bool setMasterVolume(byte percent); + bool setMasterVolumePercent(byte percent); + byte getVolumePercent(Audio::Mixer::SoundType type); + bool setVolumePercent(Audio::Mixer::SoundType type, byte percent); + bool setVolume(Audio::Mixer::SoundType type, int volume); + uint32 _volumeOriginal; + int _volumeMaster; + bool removeSound(BaseSoundBuffer *sound); + BaseSoundBuffer *addSound(const Common::String &filename, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType, bool streamed = false); + bool addSound(BaseSoundBuffer *sound, Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType); + bool initialize(); + bool _soundAvailable; + BaseSoundMgr(BaseGame *inGame); + virtual ~BaseSoundMgr(); + Common::Array<BaseSoundBuffer *> _sounds; + void saveSettings(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/coll_templ.h b/engines/wintermute/coll_templ.h new file mode 100644 index 0000000000..493ea07015 --- /dev/null +++ b/engines/wintermute/coll_templ.h @@ -0,0 +1,86 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_COLL_TEMPL_H +#define WINTERMUTE_COLL_TEMPL_H + +#include "common/array.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +// Basically Common::Array with peristence-support. +template<typename TYPE> +class BaseArray : public Common::Array<TYPE> { +public: +// TODO: Might want to make sure that destructors are called when replacing/deleting/getting destructed + bool persist(BasePersistenceManager *persistMgr) { + int j; + if (persistMgr->getIsSaving()) { + j = Common::Array<TYPE>::size(); + persistMgr->transfer("ArraySize", &j); + typename Common::Array<TYPE>::const_iterator it = Common::Array<TYPE>::begin(); + for (; it != Common::Array<TYPE>::end(); ++it) { + TYPE obj = *it; + persistMgr->transfer("", &obj); + } + } else { + Common::Array<TYPE>::clear(); + persistMgr->transfer("ArraySize", &j); + for (int i = 0; i < j; i++) { + TYPE obj; + persistMgr->transfer("", &obj); + add(obj); + } + } + return true; + } + int add(TYPE newElement) { + Common::Array<TYPE>::push_back(newElement); + return Common::Array<TYPE>::size() - 1; + } + void remove_at(uint32 idx) { + Common::Array<TYPE>::remove_at(idx); + } + void remove_at(uint32 idx, uint32 num) { + while (num) { + if (idx >= Common::Array<TYPE>::size()) { + break; + } + Common::Array<TYPE>::remove_at(idx); + } + } + template<typename T2> + void copy(const BaseArray<T2> &src) { + Common::Array<TYPE>::insert_at(0, src); + } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/dcgf.h b/engines/wintermute/dcgf.h new file mode 100644 index 0000000000..fc4174094b --- /dev/null +++ b/engines/wintermute/dcgf.h @@ -0,0 +1,51 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCGF_H +#define WINTERMUTE_DCGF_H + + +////////////////////////////////////////////////////////////////////////// +#define DCGF_VER_MAJOR 1 +#define DCGF_VER_MINOR 1 +#define DCGF_VER_BUILD 1 +#define DCGF_VER_SUFFIX "beta" +#define DCGF_VER_BETA true + +#define DCGF_NAME "WME Lite" +#define DCGF_MAGIC 0xDEC0ADDE + +// minimal saved game version we support +#define SAVEGAME_VER_MAJOR 1 +#define SAVEGAME_VER_MINOR 1 +#define SAVEGAME_VER_BUILD 1 +////////////////////////////////////////////////////////////////////////// + +#define COMPRESSED_FILE_MAGIC 0x504D435A // ZCMP + +#endif diff --git a/engines/wintermute/dctypes.h b/engines/wintermute/dctypes.h new file mode 100644 index 0000000000..bd4966eb6b --- /dev/null +++ b/engines/wintermute/dctypes.h @@ -0,0 +1,225 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_DCTYPES_H +#define WINTERMUTE_DCTYPES_H + + +#include "common/str.h" +#include "common/list.h" +#include "common/array.h" + +namespace Wintermute { + +//typedef std::string AnsiString; +//typedef std::string Utf8String; +//typedef std::wstring WideString; +typedef Common::String AnsiString; +typedef Common::String Utf8String; +typedef Common::String WideString; // NB: Not actually true I presume. + +typedef Common::List<WideString> WideStringList; +typedef Common::List<AnsiString> AnsiStringList; + +typedef Common::Array<WideString> WideStringArray; +typedef Common::Array<AnsiString> AnsiStringArray; + + +enum TGameState { + GAME_RUNNING, + GAME_FROZEN, + GAME_SEMI_FROZEN +}; + + +enum TImageType { + IMG_PALETTED8, + IMG_TRUECOLOR +}; + + +enum TTextAlign { + TAL_LEFT = 0, + TAL_RIGHT, + TAL_CENTER, + NUM_TEXT_ALIGN +}; + + +enum TVerticalAlign { + VAL_TOP = 0, + VAL_CENTER, + VAL_BOTTOM, + NUM_VERTICAL_ALIGN +}; + + +enum TDirection { + DI_UP = 0, + DI_UPRIGHT = 1, + DI_RIGHT = 2, + DI_DOWNRIGHT = 3, + DI_DOWN = 4, + DI_DOWNLEFT = 5, + DI_LEFT = 6, + DI_UPLEFT = 7, + NUM_DIRECTIONS = 8, + DI_NONE = 9 +}; + +enum TEventType { + EVENT_NONE = 0, + EVENT_INIT = 1, + EVENT_SHUTDOWN = 2, + EVENT_LEFT_CLICK = 3, + EVENT_RIGHT_CLICK = 4, + EVENT_MIDDLE_CLICK = 5, + EVENT_LEFT_DBLCLICK = 6, + EVENT_PRESS = 7, + EVENT_IDLE = 8, + EVENT_MOUSE_OVER = 9, + EVENT_LEFT_RELEASE = 10, + EVENT_RIGHT_RELEASE = 11, + EVENT_MIDDLE_RELEASE = 12, + NUM_EVENTS +}; + +enum TUIObjectType { + UI_UNKNOWN, + UI_BUTTON, + UI_WINDOW, + UI_STATIC, + UI_EDIT, + UI_HTML, + UI_CUSTOM +}; + + +enum TRendererState { + RSTATE_3D, + RSTATE_2D, + RSTATE_LINES, + RSTATE_NONE +}; + + +enum TDynamicConstructor { + DYNAMIC_CONSTRUCTOR +}; + + +enum TVideoMode { + VIDEO_WINDOW, + VIDEO_FULLSCREEN, + VIDEO_ANY +}; + + +enum TVideoPlayback { + VID_PLAY_POS = 0, + VID_PLAY_STRETCH = 1, + VID_PLAY_CENTER = 2 +}; + + +enum TMouseEvent { + MOUSE_CLICK, + MOUSE_RELEASE, + MOUSE_DBLCLICK +}; + + +enum TMouseButton { + MOUSE_BUTTON_LEFT, + MOUSE_BUTTON_RIGHT, + MOUSE_BUTTON_MIDDLE +}; + + +enum TTransMgrState { + TRANS_MGR_RUNNING, + TRANS_MGR_READY +}; + + +enum TTransitionType { + TRANSITION_NONE = 0, + TRANSITION_FADE_OUT = 1, + TRANSITION_FADE_IN = 2, + NUM_TRANSITION_TYPES +}; + + +enum TWindowMode { + WINDOW_NORMAL, + WINDOW_EXCLUSIVE, + WINDOW_SYSTEM_EXCLUSIVE +}; + +enum TSFXType { + SFX_NONE, + SFX_ECHO, + SFX_REVERB +}; + + +enum TSpriteCacheType { + CACHE_ALL, + CACHE_HALF +}; + +enum TTextEncoding { + TEXT_ANSI = 0, + TEXT_UTF8 = 1, + NUM_TEXT_ENCODINGS +}; + +enum TSpriteBlendMode { + BLEND_UNKNOWN = -1, + BLEND_NORMAL = 0, + BLEND_ADDITIVE = 1, + BLEND_SUBTRACTIVE = 2, + NUM_BLEND_MODES +}; + +enum TTTSType { + TTS_CAPTION = 0, + TTS_TALK, + TTS_KEYPRESS +}; + +enum TShadowType { + SHADOW_NONE = 0, + SHADOW_SIMPLE = 1, + SHADOW_FLAT = 2, + SHADOW_STENCIL = 3 +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp new file mode 100644 index 0000000000..04f7f3b112 --- /dev/null +++ b/engines/wintermute/detection.cpp @@ -0,0 +1,174 @@ +/* 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 "engines/advancedDetector.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +#include "common/config-manager.h" +#include "common/error.h" +#include "common/fs.h" +#include "common/util.h" + +#include "engines/metaengine.h" + +#include "engines/wintermute/detection_tables.h" + +namespace Wintermute { + +/** + * The fallback game descriptor used by the Wintermute engine's fallbackDetector. + * Contents of this struct are overwritten by the fallbackDetector. (logic copied partially + * from the SCI-engine). + */ +static ADGameDescription s_fallbackDesc = { + "", + "", + AD_ENTRY1(0, 0), // This should always be AD_ENTRY1(0, 0) in the fallback descriptor + Common::UNK_LANG, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() +}; +static char s_fallbackGameIdBuf[256]; + +static const char *directoryGlobs[] = { + "language", // To detect the various languages + 0 +}; + +class WintermuteMetaEngine : public AdvancedMetaEngine { +public: + WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(ADGameDescription), Wintermute::wintermuteGames) { + _singleid = "wintermute"; + _maxScanDepth = 2; + _directoryGlobs = directoryGlobs; + } + virtual const char *getName() const { + return "Wintermute"; + } + + virtual const char *getOriginalCopyright() const { + return "Copyright (c) 2011 Jan Nedoma"; + } + + virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { + // Set some defaults + s_fallbackDesc.extra = ""; + s_fallbackDesc.language = Common::UNK_LANG; + s_fallbackDesc.flags = ADGF_UNSTABLE; + s_fallbackDesc.platform = Common::kPlatformWindows; // default to Windows + s_fallbackDesc.gameid = "wintermute"; + s_fallbackDesc.guioptions = GUIO0(); + + if (allFiles.contains("data.dcp")) { + Common::String name, caption; + if (WintermuteEngine::getGameInfo(fslist, name, caption)) { + for (uint32 i = 0; i < name.size(); i++) { + // Replace spaces (and other non-alphanumerics) with underscores + if (!Common::isAlnum(name[(int32)i])) { + name.setChar('_', (uint32)i); + } + } + // Prefix to avoid collisions with actually known games + name = "wmefan-" + name; + Common::strlcpy(s_fallbackGameIdBuf, name.c_str(), sizeof(s_fallbackGameIdBuf) - 1); + s_fallbackDesc.gameid = s_fallbackGameIdBuf; + if (caption != name) { + caption += " (fangame) "; + char *offset = s_fallbackGameIdBuf + name.size() + 1; + uint32 remainingLength = (sizeof(s_fallbackGameIdBuf) - 1) - (name.size() + 1); + Common::strlcpy(offset, caption.c_str(), remainingLength); + s_fallbackDesc.extra = offset; + s_fallbackDesc.flags |= ADGF_USEEXTRAASTITLE; + } + return &s_fallbackDesc; + } // Fall through to return 0; + } + return 0; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + assert(syst); + assert(engine); + + *engine = new Wintermute::WintermuteEngine(syst, desc); + return true; + } + + bool hasFeature(MetaEngineFeature f) const { + switch (f) { + case MetaEngine::kSupportsListSaves: + return true; + case MetaEngine::kSupportsLoadingDuringStartup: + return true; + case MetaEngine::kSupportsDeleteSave: + return true; + case MetaEngine::kSavesSupportCreationDate: + return true; + case MetaEngine::kSavesSupportMetaInfo: + return true; + case MetaEngine::kSavesSupportThumbnail: + return true; + default: + return false; + } + } + + SaveStateList listSaves(const char *target) const { + SaveStateList saves; + Wintermute::BasePersistenceManager pm(target, true); + for (int i = 0; i < getMaximumSaveSlot(); i++) { + if (pm.getSaveExists(i)) { + SaveStateDescriptor desc; + pm.getSaveStateDesc(i, desc); + saves.push_back(desc); + } + } + return saves; + } + + int getMaximumSaveSlot() const { + return 100; + } + + void removeSaveState(const char *target, int slot) const { + Wintermute::BasePersistenceManager pm(target, true); + pm.deleteSaveSlot(slot); + } + + virtual SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const { + Wintermute::BasePersistenceManager pm(target, true); + SaveStateDescriptor retVal; + retVal.setDescription("Invalid savegame"); + pm.getSaveStateDesc(slot, retVal); + return retVal; + } +}; + +} // end of namespace Wintermute + +#if PLUGIN_ENABLED_DYNAMIC(WINTERMUTE) +REGISTER_PLUGIN_DYNAMIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine); +#else +REGISTER_PLUGIN_STATIC(WINTERMUTE, PLUGIN_TYPE_ENGINE, Wintermute::WintermuteMetaEngine); +#endif diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h new file mode 100644 index 0000000000..4f8a962591 --- /dev/null +++ b/engines/wintermute/detection_tables.h @@ -0,0 +1,265 @@ +/* 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. + * + */ + +namespace Wintermute { + +static const PlainGameDescriptor wintermuteGames[] = { + {"5ld", "Five Lethal Demons"}, + {"5ma", "Five Magical Amulets"}, + {"actualdest", "Actual Destination"}, + {"chivalry", "Chivalry is Not Dead"}, + {"deadcity", "Dead City"}, + {"dirtysplit", "Dirty Split"}, + {"eastside", "East Side Story"}, + {"ghostsheet", "Ghost in the Sheet"}, + {"hamlet", "Hamlet or the last game without MMORPS features, shaders and product placement"}, + {"julia", "J.U.L.I.A."}, + {"mirage", "Mirage"}, + {"pigeons", "Pigeons in the Park"}, + {"reversion", "Reversion"}, + {"rosemary", "Rosemary"}, + {"thebox", "The Box"}, + {"twc", "the white chamber"}, + {"wintermute", "Wintermute engine game"}, + {0, 0} +}; + +static const ADGameDescription gameDescriptions[] = { + // Five Lethal Demons + { + "5ld", + "", + AD_ENTRY1s("data.dcp", "1037a77cbd001e0644898addc022322c", 15407750), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Five Magical Amulets + { + "5ma", + "", + AD_ENTRY1s("data.dcp", "0134e92bcd5fd2837df3971087e96067", 163316498), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Actual Destination + { + "actualdest", + "", + AD_ENTRY1s("data.dcp", "6926f44b26f21ceb1d840eaab9aeb510", 9081740), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Chivalry is Not Dead + { + "chivalry", + "", + AD_ENTRY1s("data.dcp", "ebd0915d9a12df5224be22f53bb23eb6", 7278306), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (English) + { + "deadcity", + "", + { + {"english.dcp", 0, "c591046d6de7e381d76f70e0787b2b1f", 415935}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (Italian) + { + "deadcity", + "", + { + {"italian.dcp", 0, "92d8efb94436bec7bd1b7fe0b548192e", 454037}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::IT_ITA, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dead City (Russian) + { + "deadcity", + "", + { + {"russian.dcp", 0, "a0ae71e9e1185596fffb07ad2c951eb9", 653317}, + {"data.dcp", 0, "7ebfd50d1a22370ed7b079bcaa631d62", 9070205}, + AD_LISTEND + }, + Common::RU_RUS, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dirty Split (English) + { + "dirtysplit", + "", + AD_ENTRY1s("data.dcp", "8f3dae199361ece0f59fb20cfff6eed3", 88577621), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Dirty Split (German) + { + "dirtysplit", + "", + AD_ENTRY1s("data.dcp", "139d8a25579e969f8b37d20e6e3de5f9", 92668291), + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // East Side Story (Demo) + { + "eastside", + "Demo", + AD_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Ghosts in the Sheet + { + "ghostsheet", + "Demo", + AD_ENTRY1s("data.dcp", "dc1f6595f412ac25a52eaf47dad4ab81", 169083), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Hamlet or the last game without MMORPS features, shaders and product placement + { + "hamlet", + "", + AD_ENTRY1s("data.dcp", "f624add957a77c9930529fb28cc2450f", 88183022), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // J.U.L.I.A. (English) + { + "julia", + "", + AD_ENTRY1s("data.dcp", "c2264b4f8fcd132d2913ff5b6076a24f", 10109741), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // J.U.L.I.A. (English) (Demo) + { + "julia", + "Demo", + AD_ENTRY1s("data.dcp", "f0bbc3394555a9811f6050dae428cab6", 7655237), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE | + ADGF_DEMO, + GUIO0() + }, + // Mirage + { + "mirage", + "", + AD_ENTRY1s("data.dcp", "d230b0b99c0aa77b9ecd094d8ee5573b", 17844056), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Pigeons in the Park + { + "pigeons", + "", + AD_ENTRY1s("data.dcp", "9143a5b6ff8206aefe3c4c643add3ec7", 2611100), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Reversion + { + "reversion", + "", + AD_ENTRY1s("data.dcp", "cd616f98ebfd047e0c540b50b4b70761", 254384531), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // Rosemary + { + "rosemary", + "", + AD_ENTRY1s("data.dcp", "4f2631138bd4d27587d9043f8aeff3df", 29483643), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // The Box + { + "thebox", + "", + AD_ENTRY1s("data.dcp", "ec5f0c7e8174e307701447b53afe7e2f", 108372483), + Common::EN_ANY, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + // the white chamber (multi-language) + { + "twc", + "", + AD_ENTRY1s("data.dcp", "0011d01142547c61e51ba24dc42b579e", 186451273), + Common::UNK_LANG, + Common::kPlatformWindows, + ADGF_UNSTABLE, + GUIO0() + }, + AD_TABLE_END_MARKER +}; + +} // End of namespace Wintermute diff --git a/engines/wintermute/graphics/transparent_surface.cpp b/engines/wintermute/graphics/transparent_surface.cpp new file mode 100644 index 0000000000..9319899495 --- /dev/null +++ b/engines/wintermute/graphics/transparent_surface.cpp @@ -0,0 +1,440 @@ +/* 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/algorithm.h" +#include "common/endian.h" +#include "common/util.h" +#include "common/rect.h" +#include "common/textconsole.h" +#include "graphics/primitives.h" +#include "engines/wintermute/graphics/transparent_surface.h" + +namespace Wintermute { + +byte *TransparentSurface::_lookup = NULL; + +void TransparentSurface::destroyLookup() { + delete _lookup; + _lookup = NULL; +} + +TransparentSurface::TransparentSurface() : Surface(), _enableAlphaBlit(true) {} + +TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _enableAlphaBlit(true) { + if (copyData) { + copyFrom(surf); + } else { + w = surf.w; + h = surf.h; + pitch = surf.pitch; + format = surf.format; + pixels = surf.pixels; + } +} + +void doBlitOpaque(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { + byte *in, *out; + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; +#else + const int aIndex = 0; +#endif + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + memcpy(out, in, width * 4); + for (uint32 j = 0; j < width; j++) { + out[aIndex] = 0xFF; + out += 4; + } + outo += pitch; + ino += inoStep; + } +} + +void TransparentSurface::generateLookup() { + _lookup = new byte[256 * 256]; + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + _lookup[(i << 8) + j] = (i * j) >> 8; + } + } +} + +void TransparentSurface::doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) { + byte *in, *out; + + if (!_lookup) { + generateLookup(); + } + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; + const int bIndex = 0; + const int gIndex = 1; + const int rIndex = 2; +#else + const int aIndex = 0; + const int bIndex = 3; + const int gIndex = 2; + const int rIndex = 1; +#endif + + const int bShift = 0;//img->format.bShift; + const int gShift = 8;//img->format.gShift; + const int rShift = 16;//img->format.rShift; + const int aShift = 24;//img->format.aShift; + + const int bShiftTarget = 0;//target.format.bShift; + const int gShiftTarget = 8;//target.format.gShift; + const int rShiftTarget = 16;//target.format.rShift; + + for (uint32 i = 0; i < height; i++) { + out = outo; + in = ino; + for (uint32 j = 0; j < width; j++) { + uint32 pix = *(uint32 *)in; + uint32 oPix = *(uint32 *) out; + int b = (pix >> bShift) & 0xff; + int g = (pix >> gShift) & 0xff; + int r = (pix >> rShift) & 0xff; + int a = (pix >> aShift) & 0xff; + int outb, outg, outr, outa; + in += inStep; + + switch (a) { + case 0: // Full transparency + out += 4; + break; + case 255: // Full opacity + outb = b; + outg = g; + outr = r; + outa = a; + + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + break; + + default: // alpha blending + outa = 255; + + outb = _lookup[(((oPix >> bShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outg = _lookup[(((oPix >> gShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outr = _lookup[(((oPix >> rShiftTarget) & 0xff)) + ((255 - a) << 8)]; + outb += _lookup[b + (a << 8)]; + outg += _lookup[g + (a << 8)]; + outr += _lookup[r + (a << 8)]; + + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + } + } + outo += pitch; + ino += inoStep; + } +} + + +Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height) { + int ca = (color >> 24) & 0xff; + + Common::Rect retSize; + retSize.top = 0; + retSize.left = 0; + retSize.setWidth(0); + retSize.setHeight(0); + // Check if we need to draw anything at all + if (ca == 0) + return retSize; + + int cr = (color >> 16) & 0xff; + int cg = (color >> 8) & 0xff; + int cb = (color >> 0) & 0xff; + + // Compensate for transparency. Since we're coming + // down to 255 alpha, we just compensate for the colors here + if (ca != 255) { + cr = cr * ca >> 8; + cg = cg * ca >> 8; + cb = cb * ca >> 8; + } + + // Create an encapsulating surface for the data + TransparentSurface srcImage(*this, false); + // TODO: Is the data really in the screen format? + if (format.bytesPerPixel != 4) { + warning("TransparentSurface can only blit 32 bpp images"); + return retSize; + } + + if (pPartRect) { + srcImage.pixels = &((char *)pixels)[pPartRect->top * srcImage.pitch + pPartRect->left * 4]; + srcImage.w = pPartRect->width(); + srcImage.h = pPartRect->height(); + + debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, + pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height); + } else { + + debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0, + srcImage.w, srcImage.h, color, width, height); + } + + if (width == -1) + width = srcImage.w; + if (height == -1) + height = srcImage.h; + +#ifdef SCALING_TESTING + // Hardcode scaling to 66% to test scaling + width = width * 2 / 3; + height = height * 2 / 3; +#endif + + Graphics::Surface *img; + Graphics::Surface *imgScaled = NULL; + byte *savedPixels = NULL; + if ((width != srcImage.w) || (height != srcImage.h)) { + // Scale the image + img = imgScaled = srcImage.scale(width, height); + savedPixels = (byte *)img->pixels; + } else { + img = &srcImage; + } + + // Handle off-screen clipping + if (posY < 0) { + img->h = MAX(0, (int)img->h - -posY); + img->pixels = (byte *)img->pixels + img->pitch * -posY; + posY = 0; + } + + if (posX < 0) { + img->w = MAX(0, (int)img->w - -posX); + img->pixels = (byte *)img->pixels + (-posX * 4); + posX = 0; + } + + img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0)); + img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0)); + + if ((img->w > 0) && (img->h > 0)) { + int xp = 0, yp = 0; + + int inStep = 4; + int inoStep = img->pitch; + if (flipping & TransparentSurface::FLIP_V) { + inStep = -inStep; + xp = img->w - 1; + } + + if (flipping & TransparentSurface::FLIP_H) { + inoStep = -inoStep; + yp = img->h - 1; + } + + byte *ino = (byte *)img->getBasePtr(xp, yp); + byte *outo = (byte *)target.getBasePtr(posX, posY); + byte *in, *out; + +#ifdef SCUMM_LITTLE_ENDIAN + const int aIndex = 3; + const int bIndex = 0; + const int gIndex = 1; + const int rIndex = 2; +#else + const int aIndex = 0; + const int bIndex = 3; + const int gIndex = 2; + const int rIndex = 1; +#endif + const int bShift = 0;//img->format.bShift; + const int gShift = 8;//img->format.gShift; + const int rShift = 16;//img->format.rShift; + const int aShift = 24;//img->format.aShift; + + const int bShiftTarget = 0;//target.format.bShift; + const int gShiftTarget = 8;//target.format.gShift; + const int rShiftTarget = 16;//target.format.rShift; + + if (ca == 255 && cb == 255 && cg == 255 && cr == 255) { + if (_enableAlphaBlit) { + doBlitAlpha(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); + } else { + doBlitOpaque(ino, outo, img->w, img->h, target.pitch, inStep, inoStep); + } + } else { + for (int i = 0; i < img->h; i++) { + out = outo; + in = ino; + for (int j = 0; j < img->w; j++) { + uint32 pix = *(uint32 *)in; + uint32 o_pix = *(uint32 *) out; + int b = (pix >> bShift) & 0xff; + int g = (pix >> gShift) & 0xff; + int r = (pix >> rShift) & 0xff; + int a = (pix >> aShift) & 0xff; + int outb, outg, outr, outa; + in += inStep; + + if (ca != 255) { + a = a * ca >> 8; + } + + switch (a) { + case 0: // Full transparency + out += 4; + break; + case 255: // Full opacity + if (cb != 255) + outb = (b * cb) >> 8; + else + outb = b; + + if (cg != 255) + outg = (g * cg) >> 8; + else + outg = g; + + if (cr != 255) + outr = (r * cr) >> 8; + else + outr = r; + outa = a; + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + break; + + default: // alpha blending + outa = 255; + outb = (o_pix >> bShiftTarget) & 0xff; + outg = (o_pix >> gShiftTarget) & 0xff; + outr = (o_pix >> rShiftTarget) & 0xff; + if (cb == 0) + outb = 0; + else if (cb != 255) + outb += ((b - outb) * a * cb) >> 16; + else + outb += ((b - outb) * a) >> 8; + if (cg == 0) + outg = 0; + else if (cg != 255) + outg += ((g - outg) * a * cg) >> 16; + else + outg += ((g - outg) * a) >> 8; + if (cr == 0) + outr = 0; + else if (cr != 255) + outr += ((r - outr) * a * cr) >> 16; + else + outr += ((r - outr) * a) >> 8; + out[aIndex] = outa; + out[bIndex] = outb; + out[gIndex] = outg; + out[rIndex] = outr; + out += 4; + } + } + outo += target.pitch; + ino += inoStep; + } + } + } + + if (imgScaled) { + imgScaled->pixels = savedPixels; + imgScaled->free(); + delete imgScaled; + } + + retSize.setWidth(img->w); + retSize.setHeight(img->h); + return retSize; +} + +TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const { + Common::Rect srcRect(0, 0, (int16)w, (int16)h); + Common::Rect dstRect(0, 0, (int16)newWidth, (int16)newHeight); + return scale(srcRect, dstRect); +} + +// Copied from clone2727's https://github.com/clone2727/scummvm/blob/pegasus/engines/pegasus/surface.cpp#L247 +TransparentSurface *TransparentSurface::scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const { + // I'm doing simple linear scaling here + // dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); + TransparentSurface *target = new TransparentSurface(); + + int srcW = srcRect.width(); + int srcH = srcRect.height(); + int dstW = dstRect.width(); + int dstH = dstRect.height(); + + target->create((uint16)dstW, (uint16)dstH, this->format); + + for (int y = 0; y < dstH; y++) { + for (int x = 0; x < dstW; x++) { + uint32 color = READ_UINT32((const byte *)getBasePtr(x * srcW / dstW + srcRect.left, + y * srcH / dstH + srcRect.top)); + WRITE_UINT32((byte *)target->getBasePtr(x + dstRect.left, y + dstRect.top), color); + } + } + return target; + +} + +/** + * Writes a color key to the alpha channel of the surface + * @param rKey the red component of the color key + * @param gKey the green component of the color key + * @param bKey the blue component of the color key + * @param overwriteAlpha if true, all other alpha will be set fully opaque + */ +void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) { + assert(format.bytesPerPixel == 4); + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + uint32 pix = ((uint32 *)pixels)[i * w + j]; + uint8 r, g, b, a; + format.colorToARGB(pix, a, r, g, b); + if (r == rKey && g == gKey && b == bKey) { + a = 0; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } else if (overwriteAlpha) { + a = 255; + ((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b); + } + } + } +} + +} // End of namespace Graphics diff --git a/engines/wintermute/graphics/transparent_surface.h b/engines/wintermute/graphics/transparent_surface.h new file mode 100644 index 0000000000..8b00dccbd9 --- /dev/null +++ b/engines/wintermute/graphics/transparent_surface.h @@ -0,0 +1,131 @@ +/* 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. + */ + +#ifndef GRAPHICS_TRANSPARENTSURFACE_H +#define GRAPHICS_TRANSPARENTSURFACE_H + +#include "graphics/surface.h" + +/* + * This code is based on Broken Sword 2.5 engine + * + * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer + * + * Licensed under GNU GPL v2 + * + */ + +// TODO: Find a better solution for this. +#define BS_RGB(R,G,B) (0xFF000000 | ((R) << 16) | ((G) << 8) | (B)) +#define BS_ARGB(A,R,G,B) (((A) << 24) | ((R) << 16) | ((G) << 8) | (B)) + +namespace Wintermute { + +/** + * A transparent graphics surface, which implements alpha blitting. + */ +struct TransparentSurface : public Graphics::Surface { + TransparentSurface(); + TransparentSurface(const Graphics::Surface &surf, bool copyData = false); + + void setColorKey(char r, char g, char b); + void disableColorKey(); + + // Enums + /** + @brief The possible flipping parameters for the blit methode. + */ + enum FLIP_FLAGS { + /// The image will not be flipped. + FLIP_NONE = 0, + /// The image will be flipped at the horizontal axis. + FLIP_H = 1, + /// The image will be flipped at the vertical axis. + FLIP_V = 2, + /// The image will be flipped at the horizontal and vertical axis. + FLIP_HV = FLIP_H | FLIP_V, + /// The image will be flipped at the horizontal and vertical axis. + FLIP_VH = FLIP_H | FLIP_V + }; + + bool _enableAlphaBlit; + + /** + @brief renders the surface to another surface + @param pDest a pointer to the target image. In most cases this is the framebuffer. + @param PosX the position on the X-axis in the target image in pixels where the image is supposed to be rendered.<br> + The default value is 0. + @param PosY the position on the Y-axis in the target image in pixels where the image is supposed to be rendered.<br> + The default value is 0. + @param Flipping how the the image should be flipped.<br> + The default value is BS_Image::FLIP_NONE (no flipping) + @param pSrcPartRect Pointer on Common::Rect which specifies the section to be rendered. If the whole image has to be rendered the Pointer is NULL.<br> + This referes to the unflipped and unscaled image.<br> + The default value is NULL. + @param Color an ARGB color value, which determines the parameters for the color modulation und alpha blending.<br> + The alpha component of the color determines the alpha blending parameter (0 = no covering, 255 = full covering).<br> + The color components determines the color for color modulation.<br> + The default value is BS_ARGB(255, 255, 255, 255) (full covering, no color modulation). + The macros BS_RGB and BS_ARGB can be used for the creation of the color value. + @param Width the output width of the screen section. + The images will be scaled if the output width of the screen section differs from the image section.<br> + The value -1 determines that the image should not be scaled.<br> + The default value is -1. + @param Width the output height of the screen section. + The images will be scaled if the output width of the screen section differs from the image section.<br> + The value -1 determines that the image should not be scaled.<br> + The default value is -1. + @return returns false if the rendering failed. + */ + + Common::Rect blit(Graphics::Surface &target, int posX = 0, int posY = 0, + int flipping = FLIP_NONE, + Common::Rect *pPartRect = NULL, + uint color = BS_ARGB(255, 255, 255, 255), + int width = -1, int height = -1); + void applyColorKey(uint8 r, uint8 g, uint8 b, bool overwriteAlpha = false); + // The following scale-code supports arbitrary scaling (i.e. no repeats of column 0 at the end of lines) + TransparentSurface *scale(uint16 newWidth, uint16 newHeight) const; + TransparentSurface *scale(const Common::Rect &srcRect, const Common::Rect &dstRect) const; + static byte *_lookup; + static void destroyLookup(); +private: + static void doBlitAlpha(byte *ino, byte* outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep); + static void generateLookup(); +}; + +/** + * A deleter for Surface objects which can be used with SharedPtr. + * + * This deleter assures Surface::free is called on deletion. + */ +/*struct SharedPtrTransparentSurfaceDeleter { + void operator()(TransparentSurface *ptr) { + ptr->free(); + delete ptr; + } +};*/ + + +} // End of namespace Graphics + + +#endif diff --git a/engines/wintermute/math/math_util.cpp b/engines/wintermute/math/math_util.cpp new file mode 100644 index 0000000000..31af77538a --- /dev/null +++ b/engines/wintermute/math/math_util.cpp @@ -0,0 +1,52 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/math_util.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +float MathUtil::round(float val) { + float result = floor(val); + if (val - result >= 0.5f) { + result += 1.0; + } + return result; +} + +////////////////////////////////////////////////////////////////////////// +float MathUtil::roundUp(float val) { + float result = floor(val); + if (val - result > 0) { + result += 1.0; + } + return result; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/math_util.h b/engines/wintermute/math/math_util.h new file mode 100644 index 0000000000..38b6d9abf9 --- /dev/null +++ b/engines/wintermute/math/math_util.h @@ -0,0 +1,42 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_MATHUTIL_H +#define WINTERMUTE_MATHUTIL_H + +namespace Wintermute { + +class MathUtil { +public: + static float round(float val); + static float roundUp(float val); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/matrix4.cpp b/engines/wintermute/math/matrix4.cpp new file mode 100644 index 0000000000..ca1eae8813 --- /dev/null +++ b/engines/wintermute/math/matrix4.cpp @@ -0,0 +1,86 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/matrix4.h" +#include "engines/wintermute/math/vector2.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +Matrix4::Matrix4() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = 0.0f; + } + } +} + +////////////////////////////////////////////////////////////////////////// +Matrix4::~Matrix4() { +} + + +////////////////////////////////////////////////////////////////////////// +void Matrix4::identity() { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = 0.0f; + } + } + m[0][0] = 1.0f; + m[1][1] = 1.0f; + m[2][2] = 1.0f; + m[3][3] = 1.0f; + +} + +////////////////////////////////////////////////////////////////////////// +void Matrix4::rotationZ(float angle) { + identity(); + + m[0][0] = cos(angle); + m[1][1] = cos(angle); + m[0][1] = sin(angle); + m[1][0] = -sin(angle); +} + +////////////////////////////////////////////////////////////////////////// +void Matrix4::transformVector2(Vector2 &vec) { + float norm; + + norm = m[0][3] * vec.x + m[1][3] * vec.y + m[3][3]; + + float x = (m[0][0] * vec.x + m[1][0] * vec.y + m[3][0]) / norm; + float y = (m[0][1] * vec.x + m[1][1] * vec.y + m[3][1]) / norm; + + vec.x = x; + vec.y = y; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/matrix4.h b/engines/wintermute/math/matrix4.h new file mode 100644 index 0000000000..273633f723 --- /dev/null +++ b/engines/wintermute/math/matrix4.h @@ -0,0 +1,50 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_MATRIX4_H +#define WINTERMUTE_MATRIX4_H + +namespace Wintermute { + +class Vector2; + +class Matrix4 { +public: + Matrix4(); + ~Matrix4(); + + void identity(); + void rotationZ(float angle); + void transformVector2(Vector2 &vec); + + float m[4][4]; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/rect32.h b/engines/wintermute/math/rect32.h new file mode 100644 index 0000000000..190c1135cf --- /dev/null +++ b/engines/wintermute/math/rect32.h @@ -0,0 +1,94 @@ +/* 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. + * + */ + +#ifndef WINTERMUTE_RECT32_H +#define WINTERMUTE_RECT32_H + +#include "common/system.h" + +namespace Wintermute { + +struct Point32 { + int32 x; + int32 y; +}; + +struct Rect32 { + int32 top, left; ///< The point at the top left of the rectangle (part of the rect). + int32 bottom, right; ///< The point at the bottom right of the rectangle (not part of the rect). + + Rect32() : top(0), left(0), bottom(0), right(0) {} + Rect32(int32 w, int32 h) : top(0), left(0), bottom(h), right(w) {} + Rect32(int32 x1, int32 y1, int32 x2, int32 y2) : top(y1), left(x1), bottom(y2), right(x2) { + assert(isValidRect()); + } + bool operator==(const Rect32 &rhs) const { + return equals(rhs); + } + bool operator!=(const Rect32 &rhs) const { + return !equals(rhs); + } + + int32 width() const { + return right - left; + } + int32 height() const { + return bottom - top; + } + + void setWidth(int32 aWidth) { + right = left + aWidth; + } + + void setHeight(int32 aHeight) { + bottom = top + aHeight; + } + + void setEmpty() { + left = right = top = bottom = 0; + } + + void offsetRect(int dx, int dy) { + left += dx; + top += dy; + right += dx; + bottom += dy; + } + /** + * Check if the given rect is equal to this one. + * + * @param r The rectangle to check + * + * @return true if the given rect is equal, false otherwise + */ + bool equals(const Rect32 &r) const { + return (left == r.left) && (right == r.right) && (top == r.top) && (bottom == r.bottom); + } + + bool isValidRect() const { + return (left <= right && top <= bottom); + } +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/math/vector2.cpp b/engines/wintermute/math/vector2.cpp new file mode 100644 index 0000000000..74c5586d62 --- /dev/null +++ b/engines/wintermute/math/vector2.cpp @@ -0,0 +1,55 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/math/vector2.h" +#include <math.h> + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +Vector2::Vector2() { + x = y = 0.0f; +} + +////////////////////////////////////////////////////////////////////////// +Vector2::Vector2(float xVal, float yVal) { + this->x = xVal; + this->y = yVal; +} + +////////////////////////////////////////////////////////////////////////// +Vector2::~Vector2() { +} + + +////////////////////////////////////////////////////////////////////////// +float Vector2::length() const { + return (float)sqrt(x * x + y * y); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/math/vector2.h b/engines/wintermute/math/vector2.h new file mode 100644 index 0000000000..31f31daaa0 --- /dev/null +++ b/engines/wintermute/math/vector2.h @@ -0,0 +1,75 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VECTOR2_H +#define WINTERMUTE_VECTOR2_H + +namespace Wintermute { + +class Vector2 { +public: + Vector2(); + Vector2(float x, float y); + ~Vector2(); + + float length() const; + + inline Vector2 &operator= (const Vector2 &other) { + x = other.x; + y = other.y; + + return *this; + } + + inline Vector2 operator+ (const Vector2 &other) const { + return Vector2(x + other.x, y + other.y); + } + + inline Vector2 operator- (const Vector2 &other) const { + return Vector2(x - other.x, y - other.y); + } + + inline Vector2 operator* (const float scalar) const { + return Vector2(x * scalar, y * scalar); + } + + inline Vector2 &operator+= (const Vector2 &other) { + x += other.x; + y += other.y; + + return *this; + } + + + float x; + float y; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk new file mode 100644 index 0000000000..7b5b1b1a3a --- /dev/null +++ b/engines/wintermute/module.mk @@ -0,0 +1,123 @@ +MODULE := engines/wintermute + +MODULE_OBJS := \ + ad/ad_actor.o \ + ad/ad_entity.o \ + ad/ad_game.o \ + ad/ad_inventory.o \ + ad/ad_inventory_box.o \ + ad/ad_item.o \ + ad/ad_layer.o \ + ad/ad_node_state.o \ + ad/ad_object.o \ + ad/ad_path.o \ + ad/ad_path_point.o \ + ad/ad_region.o \ + ad/ad_response.o \ + ad/ad_response_box.o \ + ad/ad_response_context.o \ + ad/ad_rot_level.o \ + ad/ad_scale_level.o \ + ad/ad_scene.o \ + ad/ad_scene_node.o \ + ad/ad_scene_state.o \ + ad/ad_sentence.o \ + ad/ad_sprite_set.o \ + ad/ad_talk_def.o \ + ad/ad_talk_holder.o \ + ad/ad_talk_node.o \ + ad/ad_waypoint_group.o \ + base/scriptables/script.o \ + base/scriptables/script_engine.o \ + base/scriptables/script_stack.o \ + base/scriptables/script_value.o \ + base/scriptables/script_ext_array.o \ + base/scriptables/script_ext_date.o \ + base/scriptables/script_ext_file.o \ + base/scriptables/script_ext_math.o \ + base/scriptables/script_ext_object.o \ + base/scriptables/script_ext_mem_buffer.o \ + base/scriptables/script_ext_string.o \ + base/file/base_disk_file.o \ + base/file/base_file.o \ + base/file/base_file_entry.o \ + base/file/base_package.o \ + base/file/base_resources.o \ + base/file/base_save_thumb_file.o \ + base/font/base_font_bitmap.o \ + base/font/base_font_truetype.o \ + base/font/base_font.o \ + base/font/base_font_storage.o \ + base/gfx/base_image.o \ + base/gfx/base_renderer.o \ + base/gfx/base_surface.o \ + base/gfx/osystem/base_surface_osystem.o \ + base/gfx/osystem/base_render_osystem.o \ + base/particles/part_particle.o \ + base/particles/part_emitter.o \ + base/particles/part_force.o \ + base/sound/base_sound.o \ + base/sound/base_sound_buffer.o \ + base/sound/base_sound_manager.o \ + base/base_active_rect.o \ + base/base.o \ + base/base_dynamic_buffer.o \ + base/base_engine.o \ + base/base_fader.o \ + base/base_file_manager.o \ + base/base_frame.o \ + base/base_game.o \ + base/base_keyboard_state.o \ + base/base_named_object.o \ + base/base_object.o \ + base/base_parser.o \ + base/base_persistence_manager.o \ + base/base_point.o \ + base/base_quick_msg.o \ + base/base_region.o \ + base/base_save_thumb_helper.o \ + base/base_scriptable.o \ + base/base_script_holder.o \ + base/base_sprite.o \ + base/base_string_table.o \ + base/base_sub_frame.o \ + base/base_surface_storage.o \ + base/base_transition_manager.o \ + base/base_viewport.o \ + base/saveload.o \ + detection.o \ + graphics/transparent_surface.o \ + math/math_util.o \ + math/matrix4.o \ + math/vector2.o \ + platform_osystem.o \ + system/sys_class.o \ + system/sys_class_registry.o \ + system/sys_instance.o \ + ui/ui_button.o \ + ui/ui_edit.o \ + ui/ui_entity.o \ + ui/ui_object.o \ + ui/ui_text.o \ + ui/ui_tiled_image.o \ + ui/ui_window.o \ + utils/convert_utf.o \ + utils/crc.o \ + utils/path_util.o \ + utils/string_util.o \ + utils/utils.o \ + video/video_player.o \ + video/video_theora_player.o \ + wintermute.o \ + persistent.o + +MODULE_DIRS += \ + engines/wintermute + +# This module can be built as a plugin +ifeq ($(ENABLE_WINTERMUTE), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/wintermute/persistent.cpp b/engines/wintermute/persistent.cpp new file mode 100644 index 0000000000..514fd61d34 --- /dev/null +++ b/engines/wintermute/persistent.cpp @@ -0,0 +1,168 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_actor.h" +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/ad/ad_inventory.h" +#include "engines/wintermute/ad/ad_inventory_box.h" +#include "engines/wintermute/ad/ad_item.h" +#include "engines/wintermute/ad/ad_layer.h" +#include "engines/wintermute/ad/ad_node_state.h" +#include "engines/wintermute/ad/ad_object.h" +#include "engines/wintermute/ad/ad_path.h" +#include "engines/wintermute/ad/ad_path_point.h" +#include "engines/wintermute/ad/ad_region.h" +#include "engines/wintermute/ad/ad_response.h" +#include "engines/wintermute/ad/ad_response_box.h" +#include "engines/wintermute/ad/ad_response_context.h" +#include "engines/wintermute/ad/ad_rot_level.h" +#include "engines/wintermute/ad/ad_scale_level.h" +#include "engines/wintermute/ad/ad_scene.h" +#include "engines/wintermute/ad/ad_scene_node.h" +#include "engines/wintermute/ad/ad_scene_state.h" +#include "engines/wintermute/ad/ad_sentence.h" +#include "engines/wintermute/ad/ad_sprite_set.h" +#include "engines/wintermute/ad/ad_talk_def.h" +#include "engines/wintermute/ad/ad_talk_holder.h" +#include "engines/wintermute/ad/ad_talk_node.h" +#include "engines/wintermute/ad/ad_waypoint_group.h" +#include "engines/wintermute/base/base_fader.h" +#include "engines/wintermute/base/font/base_font_bitmap.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font_truetype.h" +#include "engines/wintermute/base/base_frame.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/base/base_point.h" +#include "engines/wintermute/base/base_region.h" +#include "engines/wintermute/base/base_scriptable.h" +#include "engines/wintermute/base/base_script_holder.h" +#include "engines/wintermute/base/sound/base_sound.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/particles/part_emitter.h" +#include "engines/wintermute/base/scriptables/script_engine.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_ext_array.h" +#include "engines/wintermute/base/scriptables/script_ext_date.h" +#include "engines/wintermute/base/scriptables/script_ext_file.h" +#include "engines/wintermute/base/scriptables/script_ext_math.h" +#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h" +#include "engines/wintermute/base/scriptables/script_ext_object.h" +#include "engines/wintermute/base/scriptables/script_ext_string.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/system/sys_class.h" + +// SystemClass adds these objects to the registry, thus they aren't as leaked as they look +#define REGISTER_CLASS(class_name, persistent_class)\ + new Wintermute::SystemClass(class_name::_className, class_name::persistBuild, class_name::persistLoad, persistent_class); + +namespace Wintermute { + +// This is done in a separate file, to avoid including the kitchensink in SystemClassRegistry. +void SystemClassRegistry::registerClasses() { + REGISTER_CLASS(AdActor, false) + REGISTER_CLASS(AdEntity, false) + REGISTER_CLASS(AdGame, true) + REGISTER_CLASS(AdInventory, false) + REGISTER_CLASS(AdInventoryBox, false) + REGISTER_CLASS(AdItem, false) + REGISTER_CLASS(AdLayer, false) + REGISTER_CLASS(AdNodeState, false) + REGISTER_CLASS(AdObject, false) + REGISTER_CLASS(AdPath, false) + REGISTER_CLASS(AdPathPoint, false) + REGISTER_CLASS(AdRegion, false) + REGISTER_CLASS(AdResponse, false) + REGISTER_CLASS(AdResponseBox, false) + REGISTER_CLASS(AdResponseContext, false) + REGISTER_CLASS(AdRotLevel, false) + REGISTER_CLASS(AdScaleLevel, false) + REGISTER_CLASS(AdScene, false) + REGISTER_CLASS(AdSceneNode, false) + REGISTER_CLASS(AdSceneState, false) + REGISTER_CLASS(AdSentence, false) + REGISTER_CLASS(AdSpriteSet, false) + REGISTER_CLASS(AdTalkDef, false) + REGISTER_CLASS(AdTalkHolder, false) + REGISTER_CLASS(AdTalkNode, false) + REGISTER_CLASS(AdWaypointGroup, false) + + REGISTER_CLASS(BaseFader, false) + REGISTER_CLASS(BaseFont, false) + REGISTER_CLASS(BaseFontBitmap, false) + REGISTER_CLASS(BaseFontStorage, true) + REGISTER_CLASS(BaseFontTT, false) + REGISTER_CLASS(BaseFrame, false) + REGISTER_CLASS(BaseGame, true) + REGISTER_CLASS(BaseKeyboardState, false) + REGISTER_CLASS(BaseObject, false) + REGISTER_CLASS(BasePoint, false) + REGISTER_CLASS(BaseRegion, false) + REGISTER_CLASS(BaseScriptable, false) + REGISTER_CLASS(BaseScriptHolder, false) + REGISTER_CLASS(BaseSound, false) + REGISTER_CLASS(BaseSprite, false) + REGISTER_CLASS(BaseSubFrame, false) + + REGISTER_CLASS(BaseViewport, false) + REGISTER_CLASS(PartEmitter, false) + REGISTER_CLASS(ScEngine, true) + REGISTER_CLASS(ScScript, false) + REGISTER_CLASS(ScStack, false) + REGISTER_CLASS(ScValue, false) + REGISTER_CLASS(SXArray, false) + REGISTER_CLASS(SXDate, false) + REGISTER_CLASS(SXFile, false) + REGISTER_CLASS(SXMath, true) + REGISTER_CLASS(SXMemBuffer, false) + REGISTER_CLASS(SXObject, false) + REGISTER_CLASS(SXString, false) + + REGISTER_CLASS(UIButton, false) + REGISTER_CLASS(UIEdit, false) + REGISTER_CLASS(UIEntity, false) + REGISTER_CLASS(UIObject, false) + REGISTER_CLASS(UIText, false) + REGISTER_CLASS(UITiledImage, false) + REGISTER_CLASS(UIWindow, false) + REGISTER_CLASS(VideoTheoraPlayer, false) +} + +} diff --git a/engines/wintermute/persistent.h b/engines/wintermute/persistent.h new file mode 100644 index 0000000000..c862df5d6b --- /dev/null +++ b/engines/wintermute/persistent.h @@ -0,0 +1,89 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PERSISTENT_H +#define WINTERMUTE_PERSISTENT_H + +namespace Wintermute { + +class BasePersistenceManager; + +// persistence support +typedef void *(*PERSISTBUILD)(void); +typedef bool(*PERSISTLOAD)(void *, BasePersistenceManager *); +typedef void (*SYS_INSTANCE_CALLBACK)(void *Instance, void *Data); +} // end of namespace Wintermute + +#include "engines/wintermute/system/sys_class_registry.h" +namespace Wintermute { + + +#define DECLARE_PERSISTENT(class_name, parent_class)\ + static const char _className[];\ + static void* persistBuild(void);\ + virtual const char* getClassName();\ + static bool persistLoad(void* Instance, BasePersistenceManager* PersistMgr);\ + class_name(TDynamicConstructor p1, TDynamicConstructor p2) :parent_class(p1, p2){ /*memset(this, 0, sizeof(class_name));*/ };\ + virtual bool persist(BasePersistenceManager* PersistMgr);\ + void* operator new (size_t size);\ + void operator delete(void* p);\ + + +#define IMPLEMENT_PERSISTENT(class_name, persistent_class)\ + const char class_name::_className[] = #class_name;\ + void* class_name::persistBuild(){\ + return ::new class_name(DYNAMIC_CONSTRUCTOR, DYNAMIC_CONSTRUCTOR);\ + }\ + \ + bool class_name::persistLoad(void* Instance, BasePersistenceManager* PersistMgr){\ + return ((class_name*)Instance)->persist(PersistMgr);\ + }\ + \ + const char* class_name::getClassName(){\ + return #class_name;\ + }\ + \ + /*SystemClass Register##class_name(class_name::_className, class_name::PersistBuild, class_name::PersistLoad, persistent_class);*/\ + \ + void* class_name::operator new (size_t size){\ + void* ret = ::operator new(size);\ + SystemClassRegistry::getInstance()->registerInstance(#class_name, ret);\ + return ret;\ + }\ + \ + void class_name::operator delete (void* p){\ + SystemClassRegistry::getInstance()->unregisterInstance(#class_name, p);\ + ::operator delete(p);\ + }\ + +#define TMEMBER(member_name) #member_name, &member_name +#define TMEMBER_INT(member_name) #member_name, (int*)&member_name + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/platform_osystem.cpp b/engines/wintermute/platform_osystem.cpp new file mode 100644 index 0000000000..0bd99b11cd --- /dev/null +++ b/engines/wintermute/platform_osystem.cpp @@ -0,0 +1,262 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/osystem/base_render_osystem.h" +#include "engines/wintermute/platform_osystem.h" +#include "common/str.h" +#include "common/textconsole.h" +#include "common/system.h" + +namespace Wintermute { + +BaseGame *BasePlatform::_gameRef = NULL; + +#define CLASS_NAME "GF_FRAME" +int BasePlatform::initialize(BaseGame *inGame, int argc, char *argv[]) { + _gameRef = inGame; + return true; +} + +////////////////////////////////////////////////////////////////////////// +void BasePlatform::handleEvent(Common::Event *event) { + switch (event->type) { + + case Common::EVENT_LBUTTONDOWN: + if (_gameRef) { + if (_gameRef->isLeftDoubleClick()) { + _gameRef->onMouseLeftDblClick(); + } else { + _gameRef->onMouseLeftDown(); + } + } + break; + case Common::EVENT_RBUTTONDOWN: + if (_gameRef) { + if (_gameRef->isRightDoubleClick()) { + _gameRef->onMouseRightDblClick(); + } else { + _gameRef->onMouseRightDown(); + } + } + break; + case Common::EVENT_MBUTTONDOWN: + if (_gameRef) { + _gameRef->onMouseMiddleDown(); + } + break; + case Common::EVENT_LBUTTONUP: + if (_gameRef) { + _gameRef->onMouseLeftUp(); + } + break; + case Common::EVENT_RBUTTONUP: + if (_gameRef) { + _gameRef->onMouseRightUp(); + } + break; + case Common::EVENT_MBUTTONUP: + if (_gameRef) { + _gameRef->onMouseMiddleUp(); + } + break; + case Common::EVENT_KEYDOWN: + if (_gameRef) { + _gameRef->handleKeypress(event); + } + break; + case Common::EVENT_KEYUP: + if (_gameRef) { + _gameRef->handleKeyRelease(event); + } + break; + case Common::EVENT_WHEELUP: + case Common::EVENT_WHEELDOWN: + if (_gameRef) { + _gameRef->handleMouseWheel(event->mouse.y); + } + break; +// Focus-events have been removed (_gameRef->onActivate originally) + case Common::EVENT_RTL: + _gameRef->_quitting = true; + break; + case Common::EVENT_QUIT: +// Block kept in case we want to support autoSaveOnExit. +// Originally this was the behaviour for WME Lite on iOS: +// if (_gameRef) { +// _gameRef->AutoSaveOnExit(); +// _gameRef->_quitting = true; +// } + if (_gameRef) { + _gameRef->onWindowClose(); + } + break; + default: + // TODO: Do we care about any other events? + break; + + } +} + + +////////////////////////////////////////////////////////////////////////// +// Win32 API bindings +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::getCursorPos(Point32 *lpPoint) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + Common::Point p = g_system->getEventManager()->getMousePos(); + lpPoint->x = p.x; + lpPoint->y = p.y; + + renderer->pointFromScreen(lpPoint); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setCursorPos(int x, int y) { + BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); + + Point32 p; + p.x = x; + p.y = y; + renderer->pointToScreen(&p); + + g_system->warpMouse(x, y); + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::showWindow(int nCmdShow) { + return false; +} + +////////////////////////////////////////////////////////////////////////// +void BasePlatform::setCapture() { + return; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::releaseCapture() { + return false; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setRectEmpty(Rect32 *lprc) { + lprc->left = lprc->right = lprc->top = lprc->bottom = 0; + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::isRectEmpty(const Rect32 *lprc) { + return (lprc->left >= lprc->right) || (lprc->top >= lprc->bottom); +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::ptInRect(Rect32 *lprc, Point32 p) { + return (p.x >= lprc->left) && (p.x < lprc->right) && (p.y >= lprc->top) && (p.y < lprc->bottom); +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::setRect(Rect32 *lprc, int left, int top, int right, int bottom) { + lprc->left = left; + lprc->top = top; + lprc->right = right; + lprc->bottom = bottom; + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2) { + if (isRectEmpty(lprcSrc1) || isRectEmpty(lprcSrc2) || + lprcSrc1->left >= lprcSrc2->right || lprcSrc2->left >= lprcSrc1->right || + lprcSrc1->top >= lprcSrc2->bottom || lprcSrc2->top >= lprcSrc1->bottom) { + setRectEmpty(lprcDst); + return false; + } + lprcDst->left = MAX(lprcSrc1->left, lprcSrc2->left); + lprcDst->right = MIN(lprcSrc1->right, lprcSrc2->right); + lprcDst->top = MAX(lprcSrc1->top, lprcSrc2->top); + lprcDst->bottom = MIN(lprcSrc1->bottom, lprcSrc2->bottom); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2) { + if (isRectEmpty(lprcSrc1)) { + if (isRectEmpty(lprcSrc2)) { + setRectEmpty(lprcDst); + return false; + } else { + *lprcDst = *lprcSrc2; + } + } else { + if (isRectEmpty(lprcSrc2)) { + *lprcDst = *lprcSrc1; + } else { + lprcDst->left = MIN(lprcSrc1->left, lprcSrc2->left); + lprcDst->top = MIN(lprcSrc1->top, lprcSrc2->top); + lprcDst->right = MAX(lprcSrc1->right, lprcSrc2->right); + lprcDst->bottom = MAX(lprcSrc1->bottom, lprcSrc2->bottom); + } + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// +bool BasePlatform::copyRect(Rect32 *lprcDst, Rect32 *lprcSrc) { + if (lprcDst == NULL || lprcSrc == NULL) { + return false; + } + + *lprcDst = *lprcSrc; + return true; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString BasePlatform::getPlatformName() { + // TODO: Should conform to the WME-spec. + //return AnsiString(SDL_GetPlatform()); + return AnsiString("ScummVM"); +} + +////////////////////////////////////////////////////////////////////////// +char *BasePlatform::strlwr(char *string) { + if (string) { + for (size_t i = 0; i < strlen(string); ++i) { + string[i] = tolower(string[i]); + } + } + return string; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/platform_osystem.h b/engines/wintermute/platform_osystem.h new file mode 100644 index 0000000000..21a77e0a0e --- /dev/null +++ b/engines/wintermute/platform_osystem.h @@ -0,0 +1,73 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PLATFORMSDL_H +#define WINTERMUTE_PLATFORMSDL_H + +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/math/rect32.h" +#include "common/events.h" + +namespace Wintermute { + +class BaseGame; + +////////////////////////////////////////////////////////////////////////// +class BasePlatform { +public: + static int initialize(BaseGame *inGame, int argc, char *argv[]); + static void handleEvent(Common::Event *event); + static AnsiString getPlatformName(); + + // Win32 API bindings + static bool getCursorPos(Point32 *lpPoint); + static bool setCursorPos(int x, int y); + static bool showWindow(int nCmdShow); + + static void setCapture(); + static bool releaseCapture(); + + static bool setRectEmpty(Rect32 *lprc); + static bool isRectEmpty(const Rect32 *lprc); + static bool ptInRect(Rect32 *lprc, Point32 p); + static bool setRect(Rect32 *lprc, int left, int top, int right, int bottom); + static bool intersectRect(Rect32 *lprcDst, const Rect32 *lprcSrc1, const Rect32 *lprcSrc2); + static bool unionRect(Rect32 *lprcDst, Rect32 *lprcSrc1, Rect32 *lprcSrc2); + static bool copyRect(Rect32 *lprcDst, Rect32 *lprcSrc); + + // string functions + static char *strlwr(char *string); + +private: + // Set by initialize on game-startup, the object referred to is also deleted by deinit in WintermuteEngine + static BaseGame *_gameRef; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_class.cpp b/engines/wintermute/system/sys_class.cpp new file mode 100644 index 0000000000..06b36b84de --- /dev/null +++ b/engines/wintermute/system/sys_class.cpp @@ -0,0 +1,220 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_persistence_manager.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemClass::SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass) { + _name = name; + + _build = build; + _load = load; + _next = NULL; + _savedID = -1; + _persistent = persistentClass; + _numInst = 0; + + SystemClassRegistry::getInstance()->registerClass(this); +} + + +////////////////////////////////////////////////////////////////////////// +SystemClass::~SystemClass() { + SystemClassRegistry::getInstance()->unregisterClass(this); + removeAllInstances(); +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClass::removeAllInstances() { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + delete(it->_value); + } + _instances.clear(); + _instanceMap.clear(); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +SystemInstance *SystemClass::addInstance(void *instance, int id, int savedId) { + SystemInstance *inst = new SystemInstance(instance, id, this); + inst->setSavedID(savedId); + _instances[inst] = (inst); + + _instanceMap[instance] = inst; + + SystemClassRegistry::getInstance()->addInstanceToTable(inst, instance); + + return inst; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClass::removeInstance(void *instance) { + InstanceMap::iterator mapIt = _instanceMap.find(instance); + if (mapIt == _instanceMap.end()) { + return false; + } + + Instances::iterator it = _instances.find((mapIt->_value)); + if (it != _instances.end()) { + delete(it->_value); + _instances.erase(it); + } + + _instanceMap.erase(mapIt); + + return false; +} + +////////////////////////////////////////////////////////////////////////// +int SystemClass::getInstanceID(void *pointer) { + InstanceMap::iterator mapIt = _instanceMap.find(pointer); + if (mapIt == _instanceMap.end()) { + return -1; + } else { + return (mapIt->_value)->getID(); + } +} + +////////////////////////////////////////////////////////////////////////// +void *SystemClass::idToPointer(int savedID) { + //slow + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + if ((it->_value)->getSavedID() == savedID) { + return (it->_value)->getInstance(); + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +int SystemClass::getNumInstances() { + return _instances.size(); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::dump(Common::WriteStream *stream) { + Common::String str; + str = Common::String::format("%03d %c %-20s instances: %d\n", _iD, _persistent ? 'p' : ' ', _name.c_str(), getNumInstances()); + stream->write(str.c_str(), str.size()); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + persistMgr->putString(_name.c_str()); + persistMgr->putDWORD(_iD); + persistMgr->putDWORD(_instances.size()); + + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + persistMgr->putDWORD((it->_value)->getID()); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + _savedID = persistMgr->getDWORD(); + int numInstances = persistMgr->getDWORD(); + + for (int i = 0; i < numInstances; i++) { + int instID = persistMgr->getDWORD(); + if (_persistent) { + + if (i > 0) { + gameRef->LOG(0, "Warning: attempting to load multiple instances of persistent class %s (%d)", _name.c_str(), numInstances); + continue; + } + + Instances::iterator it = _instances.begin(); + if (it != _instances.end()) { + (it->_value)->setSavedID(instID); + SystemClassRegistry::getInstance()->addInstanceToTable((it->_value), (it->_value)->getInstance()); + } else { + gameRef->LOG(0, "Warning: instance %d of persistent class %s not found", i, _name.c_str()); + } + } + // normal instances, create empty objects + else { + void *emptyObject = _build(); + if (!emptyObject) { + warning("HALT"); + } + + addInstance(emptyObject, SystemClassRegistry::getInstance()->getNextID(), instID); + } + + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::saveInstances(BaseGame *Game, BasePersistenceManager *persistMgr) { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + // write instace header + persistMgr->putString("<INSTANCE_HEAD>"); + persistMgr->putDWORD(_iD); + persistMgr->putDWORD((it->_value)->getID()); + persistMgr->putString("</INSTANCE_HEAD>"); + _load((it->_value)->getInstance(), persistMgr); + persistMgr->putString("</INSTANCE>"); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::loadInstance(void *instance, BasePersistenceManager *persistMgr) { + _load(instance, persistMgr); +} + + +////////////////////////////////////////////////////////////////////////// +void SystemClass::resetSavedIDs() { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + (it->_value)->setSavedID(-1); + } +} + +////////////////////////////////////////////////////////////////////////// +void SystemClass::instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData) { + Instances::iterator it; + for (it = _instances.begin(); it != _instances.end(); ++it) { + lpCallback((it->_value)->getInstance(), lpData); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_class.h b/engines/wintermute/system/sys_class.h new file mode 100644 index 0000000000..3f91723ed8 --- /dev/null +++ b/engines/wintermute/system/sys_class.h @@ -0,0 +1,130 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSCLASS_H +#define WINTERMUTE_SYSCLASS_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/dctypes.h" +#include "common/hashmap.h" +#include "common/func.h" +#include "common/stream.h" + +namespace Wintermute { +class SystemInstance; +class BaseGame; +class BasePersistenceManager; +class SystemClass; + +} + +namespace Common { +template<typename T> struct Hash; + +template<> struct Hash<void *> : public UnaryFunction<void *, uint> { + uint operator()(void *val) const { + return (uint)((size_t)val); + } +}; + +template<> struct Hash<Wintermute::SystemInstance *> : public UnaryFunction<Wintermute::SystemInstance *, uint> { + uint operator()(Wintermute::SystemInstance *val) const { + return (uint)((size_t)val); + } +}; + + +} + +namespace Wintermute { + +class SystemClass { +public: + SystemClass(const AnsiString &name, PERSISTBUILD build, PERSISTLOAD load, bool persistentClass); + ~SystemClass(); + + int getNumInstances(); + bool removeInstance(void *instance); + SystemInstance *addInstance(void *instance, int id, int savedId = -1); + bool removeAllInstances(); + + int getInstanceID(void *pointer); + void *idToPointer(int savedID); + + void setID(int id) { + _iD = id; + } + int getID() const { + return _iD; + } + + int getSavedID() const { + return _savedID; + } + + bool isPersistent() const { + return _persistent; + } + + AnsiString getName() const { + return _name; + } + + void saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + void loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + + void saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr); + void loadInstance(void *instance, BasePersistenceManager *PersistMgr); + + void instanceCallback(SYS_INSTANCE_CALLBACK lpCallback, void *lpData); + + void resetSavedIDs(); + + void dump(Common::WriteStream *stream); + +private: + int _numInst; + bool _persistent; + SystemClass *_next; + int _iD; + int _savedID; + AnsiString _name; + PERSISTBUILD _build; + PERSISTLOAD _load; + + //typedef std::set<SystemInstance *> Instances; + typedef Common::HashMap<SystemInstance *, SystemInstance *> Instances; + Instances _instances; + + typedef Common::HashMap<void *, SystemInstance *> InstanceMap; + InstanceMap _instanceMap; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_class_registry.cpp b/engines/wintermute/system/sys_class_registry.cpp new file mode 100644 index 0000000000..7c1911c2bf --- /dev/null +++ b/engines/wintermute/system/sys_class_registry.cpp @@ -0,0 +1,336 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_engine.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" +#include "engines/wintermute/wintermute.h" +#include "common/stream.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry::SystemClassRegistry() { + _count = 0; + _disabled = false; +} + + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry::~SystemClassRegistry() { + unregisterClasses(); +} + +////////////////////////////////////////////////////////////////////////// +SystemClassRegistry *SystemClassRegistry::getInstance() { + return BaseEngine::instance().getClassRegistry(); +} + +void SystemClassRegistry::unregisterClasses() { + // SystemClass calls UnregisterClass upon destruction. + while (_classes.size() > 0) { + delete _classes.begin()->_value; + } +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::registerClass(SystemClass *classObj) { + classObj->setID(_count++); + //_classes.insert(classObj); + _classes[classObj] = classObj; + + _nameMap[classObj->getName()] = classObj; + _idMap[classObj->getID()] = classObj; + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::unregisterClass(SystemClass *classObj) { + + Classes::iterator it = _classes.find(classObj); + if (it == _classes.end()) { + return false; + } + + if (classObj->getNumInstances() != 0) { + debugC(Wintermute::kWintermuteDebugSaveGame, "Memory leak@class %-20s: %d instance(s) left\n", classObj->getName().c_str(), classObj->getNumInstances()); + } + _classes.erase(it); + + NameMap::iterator mapIt = _nameMap.find(classObj->getName()); + if (mapIt != _nameMap.end()) { + _nameMap.erase(mapIt); + } + + IdMap::iterator idIt = _idMap.find(classObj->getID()); + if (idIt != _idMap.end()) { + _idMap.erase(idIt); + } + + + return true; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::registerInstance(const char *className, void *instance) { + if (_disabled) { + return true; + } + + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return false; + } + + SystemInstance *inst = (*mapIt)._value->addInstance(instance, _count++); + return (inst != NULL); +} + +////////////////////////////////////////////////////////////////////////// +void SystemClassRegistry::addInstanceToTable(SystemInstance *instance, void *pointer) { + _instanceMap[pointer] = instance; + + if (instance->getSavedID() >= 0) { + _savedInstanceMap[instance->getSavedID()] = instance; + } +} + +////////////////////////////////////////////////////////////////////////// +int SystemClassRegistry::getNextID() { + return _count++; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::unregisterInstance(const char *className, void *instance) { + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return false; + } + (*mapIt)._value->removeInstance(instance); + + InstanceMap::iterator instIt = _instanceMap.find(instance); + if (instIt != _instanceMap.end()) { + _instanceMap.erase(instIt); + return true; + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::getPointerID(void *pointer, int *classID, int *instanceID) { + if (pointer == NULL) { + return true; + } + + InstanceMap::iterator it = _instanceMap.find(pointer); + if (it == _instanceMap.end()) { + return false; + } + + + SystemInstance *inst = (*it)._value; + *instanceID = inst->getID(); + *classID = inst->getClass()->getID(); + + return true; +} + +////////////////////////////////////////////////////////////////////////// +void *SystemClassRegistry::idToPointer(int classID, int instanceID) { + SavedInstanceMap::iterator it = _savedInstanceMap.find(instanceID); + if (it == _savedInstanceMap.end()) { + return NULL; + } else { + return (*it)._value->getInstance(); + } +} + +bool checkHeader(const char *tag, BasePersistenceManager *pm) { + char *test = pm->getString(); + Common::String verify = test; + delete[] test; + bool retVal = (verify == tag); + if (!retVal) { + error("Expected %s in Save-file not found", tag); + } + return retVal; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::saveTable(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) { + persistMgr->putString("<CLASS_REGISTRY_TABLE>"); + persistMgr->putDWORD(_classes.size()); + + int counter = 0; + + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + counter++; + + if (!quickSave) { + gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)_classes.size() / (float)counter))); + } + + (it->_value)->saveTable(gameRef, persistMgr); + } + persistMgr->putString("</CLASS_REGISTRY_TABLE>"); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::loadTable(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + checkHeader("<CLASS_REGISTRY_TABLE>", persistMgr); + + // reset SavedID of current instances + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + (it->_value)->resetSavedIDs(); + } + + for (it = _classes.begin(); it != _classes.end(); ++it) { + if ((it->_value)->isPersistent()) { + continue; + } + (it->_value)->removeAllInstances(); + } + + _instanceMap.clear(); + + uint32 numClasses = persistMgr->getDWORD(); + + for (uint32 i = 0; i < numClasses; i++) { + gameRef->_renderer->setIndicatorVal((int)(50.0f / (float)((float)numClasses / (float)i))); + + Common::String className = persistMgr->getStringObj(); + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt != _nameMap.end()) { + (*mapIt)._value->loadTable(gameRef, persistMgr); + } + } + + checkHeader("</CLASS_REGISTRY_TABLE>", persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::saveInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr, bool quickSave) { + + Classes::iterator it; + + // count total instances + int numInstances = 0; + for (it = _classes.begin(); it != _classes.end(); ++it) { + numInstances += (it->_value)->getNumInstances(); + } + + persistMgr->putDWORD(numInstances); + + int counter = 0; + for (it = _classes.begin(); it != _classes.end(); ++it) { + counter++; + + if (!quickSave) { + if (counter % 20 == 0) { + gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)_classes.size() / (float)counter))); + } + } + gameRef->miniUpdate(); + + (it->_value)->saveInstances(gameRef, persistMgr); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::loadInstances(BaseGame *gameRef, BasePersistenceManager *persistMgr) { + // get total instances + int numInstances = persistMgr->getDWORD(); + + for (int i = 0; i < numInstances; i++) { + if (i % 20 == 0) { + gameRef->_renderer->setIndicatorVal((int)(50.0f + 50.0f / (float)((float)numInstances / (float)i))); + } + + checkHeader("<INSTANCE_HEAD>", persistMgr); + + int classID = persistMgr->getDWORD(); + int instanceID = persistMgr->getDWORD(); + void *instance = idToPointer(classID, instanceID); + + checkHeader("</INSTANCE_HEAD>", persistMgr); + + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + if ((it->_value)->getSavedID() == classID) { + (it->_value)->loadInstance(instance, persistMgr); + break; + } + } + checkHeader("</INSTANCE>", persistMgr); + } + + _savedInstanceMap.clear(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool SystemClassRegistry::enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData) { + NameMap::iterator mapIt = _nameMap.find(className); + if (mapIt == _nameMap.end()) { + return STATUS_FAILED; + } + + (*mapIt)._value->instanceCallback(lpCallback, lpData); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void SystemClassRegistry::dumpClasses(Common::WriteStream *stream) { + Classes::iterator it; + for (it = _classes.begin(); it != _classes.end(); ++it) { + (it->_value)->dump(stream); + } +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_class_registry.h b/engines/wintermute/system/sys_class_registry.h new file mode 100644 index 0000000000..ef7218c7c1 --- /dev/null +++ b/engines/wintermute/system/sys_class_registry.h @@ -0,0 +1,106 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSCLASSREGISTRY_H +#define WINTERMUTE_SYSCLASSREGISTRY_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/dctypes.h" +#include "engines/wintermute/system/sys_class.h" +#include "common/hashmap.h" +#include "common/hash-str.h" +#include "common/func.h" +#include "common/stream.h" + +namespace Wintermute { +class SystemClass; +} + +namespace Common { +template<typename T> struct Hash; +template<> struct Hash<Wintermute::SystemClass *> : public UnaryFunction<Wintermute::SystemClass *, uint> { + uint operator()(Wintermute::SystemClass *val) const { + return (uint)((size_t)val); + } +}; + +} + +namespace Wintermute { + +class BaseGame; +class BasePersistenceManager; +class SystemInstance; + +class SystemClassRegistry { + void unregisterClasses(); +public: + void registerClasses(); // persistent.cpp + static SystemClassRegistry *getInstance(); + + SystemClassRegistry(); + virtual ~SystemClassRegistry(); + + bool enumInstances(SYS_INSTANCE_CALLBACK lpCallback, const char *className, void *lpData); + bool loadTable(BaseGame *Game, BasePersistenceManager *PersistMgr); + bool saveTable(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave); + bool loadInstances(BaseGame *Game, BasePersistenceManager *PersistMgr); + bool saveInstances(BaseGame *Game, BasePersistenceManager *PersistMgr, bool quickSave); + void *idToPointer(int classID, int instanceID); + bool getPointerID(void *pointer, int *classID, int *instanceID); + bool registerClass(SystemClass *classObj); + bool unregisterClass(SystemClass *classObj); + bool registerInstance(const char *className, void *instance); + bool unregisterInstance(const char *className, void *instance); + void dumpClasses(Common::WriteStream *stream); + int getNextID(); + void addInstanceToTable(SystemInstance *instance, void *pointer); + + bool _disabled; + int _count; + + typedef Common::HashMap<SystemClass *, SystemClass *> Classes; + Classes _classes; + + typedef Common::HashMap<AnsiString, SystemClass *> NameMap; + NameMap _nameMap; + + typedef Common::HashMap<int, SystemClass *> IdMap; + IdMap _idMap; + + typedef Common::HashMap<void *, SystemInstance *> InstanceMap; + InstanceMap _instanceMap; + + typedef Common::HashMap<int, SystemInstance *> SavedInstanceMap; + SavedInstanceMap _savedInstanceMap; + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/system/sys_instance.cpp b/engines/wintermute/system/sys_instance.cpp new file mode 100644 index 0000000000..d106119dba --- /dev/null +++ b/engines/wintermute/system/sys_instance.cpp @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/system/sys_instance.h" +#include "engines/wintermute/system/sys_class_registry.h" +#include "engines/wintermute/system/sys_class.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +SystemInstance::SystemInstance(void *instance, int id, SystemClass *sysClass) { + _instance = instance; + _id = id; + _savedID = -1; + _class = sysClass; + + _used = false; +} + +////////////////////////////////////////////////////////////////////////// +SystemInstance::~SystemInstance() { +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/system/sys_instance.h b/engines/wintermute/system/sys_instance.h new file mode 100644 index 0000000000..215a6d1437 --- /dev/null +++ b/engines/wintermute/system/sys_instance.h @@ -0,0 +1,68 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_SYSINSTANCE_H +#define WINTERMUTE_SYSINSTANCE_H + +namespace Wintermute { + +class SystemClass; + +class SystemInstance { +public: + SystemInstance(void *instance, int id, SystemClass *sysClass); + virtual ~SystemInstance(); + + int getID() const { + return _id; + } + int getSavedID() const { + return _savedID; + } + void *getInstance() const { + return _instance; + } + SystemClass *getClass() const { + return _class; + } + + void setSavedID(int id) { + _savedID = id; + } + +private: + bool _used; + int _id; + int _savedID; + void *_instance; + SystemClass *_class; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_button.cpp b/engines/wintermute/ui/ui_button.cpp new file mode 100644 index 0000000000..7967d566f9 --- /dev/null +++ b/engines/wintermute/ui/ui_button.cpp @@ -0,0 +1,1209 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIButton, false) + +////////////////////////////////////////////////////////////////////////// +UIButton::UIButton(BaseGame *inGame) : UIObject(inGame) { + _backPress = _backHover = _backDisable = _backFocus = NULL; + + _fontHover = _fontPress = _fontDisable = _fontFocus = NULL; + + _imageDisable = _imagePress = _imageHover = _imageFocus = NULL; + + _align = TAL_CENTER; + + _hover = _press = false; + + _type = UI_BUTTON; + + _canFocus = false; + _stayPressed = false; + + _oneTimePress = false; + _centerImage = false; + + _pixelPerfect = false; +} + + +////////////////////////////////////////////////////////////////////////// +UIButton::~UIButton() { + delete _backPress; + delete _backHover; + delete _backDisable; + delete _backFocus; + + if (!_sharedFonts) { + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + } + + if (!_sharedImages) { + delete _imageHover; + delete _imagePress; + delete _imageDisable; + delete _imageFocus; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIButton::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing BUTTON file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(BUTTON) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(FOCUSABLE) +TOKEN_DEF(BACK_HOVER) +TOKEN_DEF(BACK_PRESS) +TOKEN_DEF(BACK_DISABLE) +TOKEN_DEF(BACK_FOCUS) +TOKEN_DEF(BACK) +TOKEN_DEF(CENTER_IMAGE) +TOKEN_DEF(IMAGE_HOVER) +TOKEN_DEF(IMAGE_PRESS) +TOKEN_DEF(IMAGE_DISABLE) +TOKEN_DEF(IMAGE_FOCUS) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_HOVER) +TOKEN_DEF(FONT_PRESS) +TOKEN_DEF(FONT_DISABLE) +TOKEN_DEF(FONT_FOCUS) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(EVENTS) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(PRESSED) +TOKEN_DEF(PIXEL_PERFECT) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIButton::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(BUTTON) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(FOCUSABLE) + TOKEN_TABLE(BACK_HOVER) + TOKEN_TABLE(BACK_PRESS) + TOKEN_TABLE(BACK_DISABLE) + TOKEN_TABLE(BACK_FOCUS) + TOKEN_TABLE(BACK) + TOKEN_TABLE(CENTER_IMAGE) + TOKEN_TABLE(IMAGE_HOVER) + TOKEN_TABLE(IMAGE_PRESS) + TOKEN_TABLE(IMAGE_DISABLE) + TOKEN_TABLE(IMAGE_FOCUS) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_HOVER) + TOKEN_TABLE(FONT_PRESS) + TOKEN_TABLE(FONT_DISABLE) + TOKEN_TABLE(FONT_FOCUS) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(EVENTS) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(PRESSED) + TOKEN_TABLE(PIXEL_PERFECT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_BUTTON) { + _gameRef->LOG(0, "'BUTTON' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_HOVER: + delete _backHover; + _backHover = new UITiledImage(_gameRef); + if (!_backHover || DID_FAIL(_backHover->loadFile((char *)params))) { + delete _backHover; + _backHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_PRESS: + delete _backPress; + _backPress = new UITiledImage(_gameRef); + if (!_backPress || DID_FAIL(_backPress->loadFile((char *)params))) { + delete _backPress; + _backPress = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_DISABLE: + delete _backDisable; + _backDisable = new UITiledImage(_gameRef); + if (!_backDisable || DID_FAIL(_backDisable->loadFile((char *)params))) { + delete _backDisable; + _backDisable = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_FOCUS: + delete _backFocus; + _backFocus = new UITiledImage(_gameRef); + if (!_backFocus || DID_FAIL(_backFocus->loadFile((char *)params))) { + delete _backFocus; + _backFocus = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_HOVER: + delete _imageHover; + _imageHover = new BaseSprite(_gameRef); + if (!_imageHover || DID_FAIL(_imageHover->loadFile((char *)params))) { + delete _imageHover; + _imageHover = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_PRESS: + delete _imagePress; + _imagePress = new BaseSprite(_gameRef); + if (!_imagePress || DID_FAIL(_imagePress->loadFile((char *)params))) { + delete _imagePress; + _imagePress = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_DISABLE: + delete _imageDisable; + _imageDisable = new BaseSprite(_gameRef); + if (!_imageDisable || DID_FAIL(_imageDisable->loadFile((char *)params))) { + delete _imageDisable; + _imageDisable = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_FOCUS: + delete _imageFocus; + _imageFocus = new BaseSprite(_gameRef); + if (!_imageFocus || DID_FAIL(_imageFocus->loadFile((char *)params))) { + delete _imageFocus; + _imageFocus = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_HOVER: + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + _fontHover = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontHover) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_PRESS: + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + _fontPress = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontPress) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_DISABLE: + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + _fontDisable = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontDisable) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_FOCUS: + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + _fontFocus = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontFocus) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _align = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _align = TAL_RIGHT; + } else { + _align = TAL_CENTER; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_FOCUSABLE: + parser.scanStr((char *)params, "%b", &_canFocus); + break; + + case TOKEN_CENTER_IMAGE: + parser.scanStr((char *)params, "%b", &_centerImage); + break; + + case TOKEN_PRESSED: + parser.scanStr((char *)params, "%b", &_stayPressed); + break; + + case TOKEN_PIXEL_PERFECT: + parser.scanStr((char *)params, "%b", &_pixelPerfect); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in BUTTON definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading BUTTON definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIButton::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "BUTTON\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + if (_backHover && _backHover->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_HOVER=\"%s\"\n", _backHover->getFilename()); + } + if (_backPress && _backPress->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_PRESS=\"%s\"\n", _backPress->getFilename()); + } + if (_backDisable && _backDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_DISABLE=\"%s\"\n", _backDisable->getFilename()); + } + if (_backFocus && _backFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_FOCUS=\"%s\"\n", _backFocus->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + if (_imageHover && _imageHover->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_HOVER=\"%s\"\n", _imageHover->getFilename()); + } + if (_imagePress && _imagePress->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_PRESS=\"%s\"\n", _imagePress->getFilename()); + } + if (_imageDisable && _imageDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_DISABLE=\"%s\"\n", _imageDisable->getFilename()); + } + if (_imageFocus && _imageFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_FOCUS=\"%s\"\n", _imageFocus->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontHover && _fontHover->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_HOVER=\"%s\"\n", _fontHover->getFilename()); + } + if (_fontPress && _fontPress->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_PRESS=\"%s\"\n", _fontPress->getFilename()); + } + if (_fontDisable && _fontDisable->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_DISABLE=\"%s\"\n", _fontDisable->getFilename()); + } + if (_fontFocus && _fontFocus->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_FOCUS=\"%s\"\n", _fontFocus->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + switch (_align) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + warning("UIButton::SaveAsText - unhandled enum"); + break; + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "FOCUSABLE=%s\n", _canFocus ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CENTER_IMAGE=%s\n", _centerImage ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PRESSED=%s\n", _stayPressed ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PIXEL_PERFECT=%s\n", _pixelPerfect ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void UIButton::correctSize() { + Rect32 rect; + + BaseSprite *img = NULL; + if (_image) { + img = _image; + } else if (_imageDisable) { + img = _imageDisable; + } else if (_imageHover) { + img = _imageHover; + } else if (_imagePress) { + img = _imagePress; + } else if (_imageFocus) { + img = _imageFocus; + } + + if (_width <= 0) { + if (img) { + img->getBoundingRect(&rect, 0, 0); + _width = rect.right - rect.left; + } else { + _width = 100; + } + } + + if (_height <= 0) { + if (img) { + img->getBoundingRect(&rect, 0, 0); + _height = rect.bottom - rect.top; + } + } + + if (_text) { + int text_height; + if (_font) { + text_height = _font->getTextHeight((byte *)_text, _width); + } else { + text_height = _gameRef->_systemFont->getTextHeight((byte *)_text, _width); + } + + if (text_height > _height) { + _height = text_height; + } + } + + if (_height <= 0) { + _height = 100; + } + + if (_back) { + _back->correctSize(&_width, &_height); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + UITiledImage *back = NULL; + BaseSprite *image = NULL; + BaseFont *font = 0; + + //RECT rect; + //BasePlatform::setRect(&rect, OffsetX + _posX, OffsetY + _posY, OffsetX+_posX+_width, OffsetY+_posY+_height); + //_hover = (!_disable && BasePlatform::ptInRect(&rect, _gameRef->_mousePos)!=FALSE); + _hover = (!_disable && _gameRef->_activeObject == this && (_gameRef->_interactive || _gameRef->_state == GAME_SEMI_FROZEN)); + + if ((_press && _hover && !_gameRef->_mouseLeftDown) || + (_oneTimePress && g_system->getMillis() - _oneTimePressTime >= 100)) { + press(); + } + + + if (_disable) { + if (_backDisable) { + back = _backDisable; + } + if (_imageDisable) { + image = _imageDisable; + } + if (_text && _fontDisable) { + font = _fontDisable; + } + } else if (_press || _oneTimePress || _stayPressed) { + if (_backPress) { + back = _backPress; + } + if (_imagePress) { + image = _imagePress; + } + if (_text && _fontPress) { + font = _fontPress; + } + } else if (_hover) { + if (_backHover) { + back = _backHover; + } + if (_imageHover) { + image = _imageHover; + } + if (_text && _fontHover) { + font = _fontHover; + } + } else if (_canFocus && isFocused()) { + if (_backFocus) { + back = _backFocus; + } + if (_imageFocus) { + image = _imageFocus; + } + if (_text && _fontFocus) { + font = _fontFocus; + } + } + + if (!back && _back) { + back = _back; + } + if (!image && _image) { + image = _image; + } + if (_text && !font) { + if (_font) { + font = _font; + } else { + font = _gameRef->_systemFont; + } + } + + int imageX = offsetX + _posX; + int imageY = offsetY + _posY; + + if (image && _centerImage) { + Rect32 rc; + image->getBoundingRect(&rc, 0, 0); + imageX += (_width - (rc.right - rc.left)) / 2; + imageY += (_height - (rc.bottom - rc.top)) / 2; + } + + if (back) { + back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + //if (image) image->Draw(ImageX +((_press||_oneTimePress)&&back?1:0), ImageY +((_press||_oneTimePress)&&back?1:0), NULL); + if (image) { + image->draw(imageX + ((_press || _oneTimePress) && back ? 1 : 0), imageY + ((_press || _oneTimePress) && back ? 1 : 0), _pixelPerfect ? this : NULL); + } + + if (font && _text) { + int text_offset = (_height - font->getTextHeight((byte *)_text, _width)) / 2; + font->drawText((byte *)_text, offsetX + _posX + ((_press || _oneTimePress) ? 1 : 0), offsetY + _posY + text_offset + ((_press || _oneTimePress) ? 1 : 0), _width, _align); + } + + if (!_pixelPerfect || !_image) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false)); + } + + // reset unused sprites + if (_image && _image != image) { + _image->reset(); + } + if (_imageDisable && _imageDisable != image) { + _imageDisable->reset(); + } + if (_imageFocus && _imageFocus != image) { + _imageFocus->reset(); + } + if (_imagePress && _imagePress != image) { + _imagePress->reset(); + } + if (_imageHover && _imageHover != image) { + _imageHover->reset(); + } + + _press = _hover && _gameRef->_mouseLeftDown && _gameRef->_capturedObject == this; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIButton::press() { + applyEvent("Press"); + if (_listenerObject) { + _listenerObject->listen(_listenerParamObject, _listenerParamDWORD); + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + _oneTimePress = false; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIButton::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetDisabledFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetDisabledFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontDisable) { + _gameRef->_fontStorage->removeFont(_fontDisable); + } + if (val->isNULL()) { + _fontDisable = NULL; + stack->pushBool(true); + } else { + _fontDisable = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontDisable != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetHoverFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHoverFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontHover) { + _gameRef->_fontStorage->removeFont(_fontHover); + } + if (val->isNULL()) { + _fontHover = NULL; + stack->pushBool(true); + } else { + _fontHover = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontHover != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPressedFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPressedFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontPress) { + _gameRef->_fontStorage->removeFont(_fontPress); + } + if (val->isNULL()) { + _fontPress = NULL; + stack->pushBool(true); + } else { + _fontPress = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontPress != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFocusedFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFocusedFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_fontFocus) { + _gameRef->_fontStorage->removeFont(_fontFocus); + } + if (val->isNULL()) { + _fontFocus = NULL; + stack->pushBool(true); + } else { + _fontFocus = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_fontFocus != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetDisabledImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetDisabledImage") == 0) { + stack->correctParams(1); + + delete _imageDisable; + _imageDisable = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageDisable || DID_FAIL(_imageDisable->loadFile(filename))) { + delete _imageDisable; + _imageDisable = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDisabledImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDisabledImage") == 0) { + stack->correctParams(0); + if (!_imageDisable || !_imageDisable->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageDisable->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetDisabledImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetDisabledImageObject") == 0) { + stack->correctParams(0); + if (!_imageDisable) { + stack->pushNULL(); + } else { + stack->pushNative(_imageDisable, true); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // SetHoverImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetHoverImage") == 0) { + stack->correctParams(1); + + delete _imageHover; + _imageHover = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageHover || DID_FAIL(_imageHover->loadFile(filename))) { + delete _imageHover; + _imageHover = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverImage") == 0) { + stack->correctParams(0); + if (!_imageHover || !_imageHover->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageHover->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetHoverImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetHoverImageObject") == 0) { + stack->correctParams(0); + if (!_imageHover) { + stack->pushNULL(); + } else { + stack->pushNative(_imageHover, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetPressedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetPressedImage") == 0) { + stack->correctParams(1); + + delete _imagePress; + _imagePress = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imagePress || DID_FAIL(_imagePress->loadFile(filename))) { + delete _imagePress; + _imagePress = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPressedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPressedImage") == 0) { + stack->correctParams(0); + if (!_imagePress || !_imagePress->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imagePress->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetPressedImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetPressedImageObject") == 0) { + stack->correctParams(0); + if (!_imagePress) { + stack->pushNULL(); + } else { + stack->pushNative(_imagePress, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetFocusedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetFocusedImage") == 0) { + stack->correctParams(1); + + delete _imageFocus; + _imageFocus = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageFocus || DID_FAIL(_imageFocus->loadFile(filename))) { + delete _imageFocus; + _imageFocus = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFocusedImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFocusedImage") == 0) { + stack->correctParams(0); + if (!_imageFocus || !_imageFocus->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageFocus->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetFocusedImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetFocusedImageObject") == 0) { + stack->correctParams(0); + if (!_imageFocus) { + stack->pushNULL(); + } else { + stack->pushNative(_imageFocus, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Press + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Press") == 0) { + stack->correctParams(0); + + if (_visible && !_disable) { + _oneTimePress = true; + _oneTimePressTime = g_system->getMillis(); + } + stack->pushNULL(); + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIButton::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("button"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextAlign") { + _scValue->setInt(_align); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Focusable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Focusable") { + _scValue->setBool(_canFocus); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // Pressed + ////////////////////////////////////////////////////////////////////////// + else if (name == "Pressed") { + _scValue->setBool(_stayPressed); + return _scValue; + } + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (name == "PixelPerfect") { + _scValue->setBool(_pixelPerfect); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "TextAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_TEXT_ALIGN) { + i = 0; + } + _align = (TTextAlign)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Focusable + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Focusable") == 0) { + _canFocus = value->getBool(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // Pressed + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Pressed") == 0) { + _stayPressed = value->getBool(); + return STATUS_OK; + } + ////////////////////////////////////////////////////////////////////////// + // PixelPerfect + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PixelPerfect") == 0) { + _pixelPerfect = value->getBool(); + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIButton::scToString() { + return "[button]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIButton::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER_INT(_align)); + persistMgr->transfer(TMEMBER(_backDisable)); + persistMgr->transfer(TMEMBER(_backFocus)); + persistMgr->transfer(TMEMBER(_backHover)); + persistMgr->transfer(TMEMBER(_backPress)); + persistMgr->transfer(TMEMBER(_centerImage)); + persistMgr->transfer(TMEMBER(_fontDisable)); + persistMgr->transfer(TMEMBER(_fontFocus)); + persistMgr->transfer(TMEMBER(_fontHover)); + persistMgr->transfer(TMEMBER(_fontPress)); + persistMgr->transfer(TMEMBER(_hover)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_imageDisable)); + persistMgr->transfer(TMEMBER(_imageFocus)); + persistMgr->transfer(TMEMBER(_imageHover)); + persistMgr->transfer(TMEMBER(_imagePress)); + persistMgr->transfer(TMEMBER(_pixelPerfect)); + persistMgr->transfer(TMEMBER(_press)); + persistMgr->transfer(TMEMBER(_stayPressed)); + + if (!persistMgr->getIsSaving()) { + _oneTimePress = false; + _oneTimePressTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_button.h b/engines/wintermute/ui/ui_button.h new file mode 100644 index 0000000000..93333a2534 --- /dev/null +++ b/engines/wintermute/ui/ui_button.h @@ -0,0 +1,80 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIBUTTON_H +#define WINTERMUTE_UIBUTTON_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView + +namespace Wintermute { + +class UIButton : public UIObject { +public: + bool _pixelPerfect; + bool _stayPressed; + bool _centerImage; + bool _oneTimePress; + uint32 _oneTimePressTime; + DECLARE_PERSISTENT(UIButton, UIObject) + void press(); + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX, int offsetY); + bool _press; + bool _hover; + void correctSize(); + TTextAlign _align; + BaseSprite *_imageHover; + BaseSprite *_imagePress; + BaseSprite *_imageDisable; + BaseSprite *_imageFocus; + BaseFont *_fontDisable; + BaseFont *_fontPress; + BaseFont *_fontHover; + BaseFont *_fontFocus; + UITiledImage *_backPress; + UITiledImage *_backHover; + UITiledImage *_backDisable; + UITiledImage *_backFocus; + UIButton(BaseGame *inGame = NULL); + virtual ~UIButton(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_edit.cpp b/engines/wintermute/ui/ui_edit.cpp new file mode 100644 index 0000000000..a3283d5a01 --- /dev/null +++ b/engines/wintermute/ui/ui_edit.cpp @@ -0,0 +1,952 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/utils/utils.h" +#include "common/util.h" +#include "common/keyboard.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIEdit, false) + +////////////////////////////////////////////////////////////////////////// +UIEdit::UIEdit(BaseGame *inGame) : UIObject(inGame) { + _type = UI_EDIT; + + _fontSelected = NULL; + + _selStart = _selEnd = 10000; + _scrollOffset = 0; + + _cursorChar = NULL; + setCursorChar("|"); + + _cursorBlinkRate = 600; + + _frameWidth = 0; + + setText(""); + + _lastBlinkTime = 0; + _cursorVisible = true; + + _maxLength = -1; + + _canFocus = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIEdit::~UIEdit() { + if (!_sharedFonts) { + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + } + + delete[] _cursorChar; + _cursorChar = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIEdit::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing EDIT file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_SELECTED) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR_BLINK_RATE) +TOKEN_DEF(CURSOR) +TOKEN_DEF(FRAME_WIDTH) +TOKEN_DEF(NAME) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(MAX_LENGTH) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(EDIT) +TOKEN_DEF(CAPTION) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIEdit::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_SELECTED) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR_BLINK_RATE) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(FRAME_WIDTH) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(MAX_LENGTH) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(EDIT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_EDIT) { + _gameRef->LOG(0, "'EDIT' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_SELECTED: + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + _fontSelected = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontSelected) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_MAX_LENGTH: + parser.scanStr((char *)params, "%d", &_maxLength); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_CURSOR_BLINK_RATE: + parser.scanStr((char *)params, "%d", &_cursorBlinkRate); + break; + + case TOKEN_FRAME_WIDTH: + parser.scanStr((char *)params, "%d", &_frameWidth); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in EDIT definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading EDIT definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "EDIT\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontSelected && _fontSelected->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_SELECTED=\"%s\"\n", _fontSelected->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + buffer->putTextIndent(indent + 2, "MAX_LENGTH=%d\n", _maxLength); + buffer->putTextIndent(indent + 2, "CURSOR_BLINK_RATE=%d\n", _cursorBlinkRate); + buffer->putTextIndent(indent + 2, "FRAME_WIDTH=%d\n", _frameWidth); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIEdit::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetSelectedFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetSelectedFont") == 0) { + stack->correctParams(1); + + if (_fontSelected) { + _gameRef->_fontStorage->removeFont(_fontSelected); + } + _fontSelected = _gameRef->_fontStorage->addFont(stack->pop()->getString()); + stack->pushBool(_fontSelected != NULL); + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIEdit::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("editor"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SelStart + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelStart") { + _scValue->setInt(_selStart); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SelEnd + ////////////////////////////////////////////////////////////////////////// + else if (name == "SelEnd") { + _scValue->setInt(_selEnd); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorBlinkRate + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorBlinkRate") { + _scValue->setInt(_cursorBlinkRate); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorChar + ////////////////////////////////////////////////////////////////////////// + else if (name == "CursorChar") { + _scValue->setString(_cursorChar); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FrameWidth + ////////////////////////////////////////////////////////////////////////// + else if (name == "FrameWidth") { + _scValue->setInt(_frameWidth); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxLength + ////////////////////////////////////////////////////////////////////////// + else if (name == "MaxLength") { + _scValue->setInt(_maxLength); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (name == "Text") { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::ansiToWide(_text); + _scValue->setString(StringUtil::wideToUtf8(wstr).c_str()); + } else { + _scValue->setString(_text); + } + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // SelStart + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SelStart") == 0) { + _selStart = value->getInt(); + _selStart = MAX(_selStart, 0); + _selStart = (int)MIN((size_t)_selStart, strlen(_text)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SelEnd + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SelEnd") == 0) { + _selEnd = value->getInt(); + _selEnd = MAX(_selEnd, 0); + _selEnd = (int)MIN((size_t)_selEnd, strlen(_text)); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorBlinkRate + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorBlinkRate") == 0) { + _cursorBlinkRate = (uint32)value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CursorChar + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CursorChar") == 0) { + setCursorChar(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FrameWidth + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FrameWidth") == 0) { + _frameWidth = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MaxLength + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MaxLength") == 0) { + _maxLength = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Text") == 0) { + if (_gameRef->_textEncoding == TEXT_UTF8) { + WideString wstr = StringUtil::utf8ToWide(value->getString()); + setText(StringUtil::wideToAnsi(wstr).c_str()); + } else { + setText(value->getString()); + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIEdit::scToString() { + return "[edit]"; +} + + +////////////////////////////////////////////////////////////////////////// +void UIEdit::setCursorChar(const char *character) { + if (!character) { + return; + } + delete[] _cursorChar; + _cursorChar = new char [strlen(character) + 1]; + if (_cursorChar) { + strcpy(_cursorChar, character); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + + // hack! + TTextEncoding OrigEncoding = _gameRef->_textEncoding; + _gameRef->_textEncoding = TEXT_ANSI; + + if (_back) { + _back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + if (_image) { + _image->draw(offsetX + _posX, offsetY + _posY, NULL); + } + + // prepare fonts + BaseFont *font; + BaseFont *sfont; + + if (_font) { + font = _font; + } else { + font = _gameRef->_systemFont; + } + + if (_fontSelected) { + sfont = _fontSelected; + } else { + sfont = font; + } + + bool focused = isFocused(); + + _selStart = MAX(_selStart, 0); + _selEnd = MAX(_selEnd, 0); + + _selStart = (int)MIN((size_t)_selStart, strlen(_text)); + _selEnd = (int)MIN((size_t)_selEnd, strlen(_text)); + + //int CursorWidth = font->GetCharWidth(_cursorChar[0]); + int cursorWidth = font->getTextWidth((byte *)_cursorChar); + + int s1, s2; + bool curFirst; + // modify scroll offset + if (_selStart >= _selEnd) { + while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selEnd - _scrollOffset)) > _width - cursorWidth - 2 * _frameWidth) { + _scrollOffset++; + if (_scrollOffset >= (int)strlen(_text)) { + break; + } + } + + _scrollOffset = MIN(_scrollOffset, _selEnd); + + s1 = _selEnd; + s2 = _selStart; + curFirst = true; + } else { + while (font->getTextWidth((byte *)_text + _scrollOffset, MAX(0, _selStart - _scrollOffset)) + + sfont->getTextWidth((byte *)(_text + MAX(_scrollOffset, _selStart)), _selEnd - MAX(_scrollOffset, _selStart)) + + > _width - cursorWidth - 2 * _frameWidth) { + _scrollOffset++; + if (_scrollOffset >= (int)strlen(_text)) { + break; + } + } + + _scrollOffset = MIN(_scrollOffset, _selEnd); + + s1 = _selStart; + s2 = _selEnd; + curFirst = false; + } + + + int alignOffset = 0; + + for (int count = 0; count < 2; count++) { + // draw text + int xxx, yyy, width, height; + + xxx = _posX + _frameWidth + offsetX; + yyy = _posY + _frameWidth + offsetY; + + width = _posX + _width + offsetX - _frameWidth; + height = MAX(font->getLetterHeight(), sfont->getLetterHeight()); + + if (_gameRef->_textRTL) { + xxx += alignOffset; + } + + TTextAlign align = TAL_LEFT; + + + // unselected 1 + if (s1 > _scrollOffset) { + if (count) { + font->drawText((byte *)_text + _scrollOffset, xxx, yyy, width - xxx, align, height, s1 - _scrollOffset); + } + xxx += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset); + alignOffset += font->getTextWidth((byte *)_text + _scrollOffset, s1 - _scrollOffset); + } + + // cursor + if (focused && curFirst) { + if (count) { + if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) { + _lastBlinkTime = g_system->getMillis(); + _cursorVisible = !_cursorVisible; + } + if (_cursorVisible) { + font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1); + } + } + xxx += cursorWidth; + alignOffset += cursorWidth; + } + + // selected + int s3 = MAX(s1, _scrollOffset); + + if (s2 - s3 > 0) { + if (count) { + sfont->drawText((byte *)_text + s3, xxx, yyy, width - xxx, align, height, s2 - s3); + } + xxx += sfont->getTextWidth((byte *)_text + s3, s2 - s3); + alignOffset += sfont->getTextWidth((byte *)_text + s3, s2 - s3); + } + + // cursor + if (focused && !curFirst) { + if (count) { + if (g_system->getMillis() - _lastBlinkTime >= _cursorBlinkRate) { + _lastBlinkTime = g_system->getMillis(); + _cursorVisible = !_cursorVisible; + } + if (_cursorVisible) { + font->drawText((byte *)_cursorChar, xxx, yyy, width - xxx, align, height, 1); + } + } + xxx += cursorWidth; + alignOffset += cursorWidth; + } + + // unselected 2 + if (count) { + font->drawText((byte *)_text + s2, xxx, yyy, width - xxx, align, height); + } + alignOffset += font->getTextWidth((byte *)_text + s2); + + alignOffset = (_width - 2 * _frameWidth) - alignOffset; + if (alignOffset < 0) { + alignOffset = 0; + } + } + + + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, offsetX + _posX, offsetY + _posY, _width, _height, 100, 100, false)); + + + _gameRef->_textEncoding = OrigEncoding; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::handleKeypress(Common::Event *event, bool printable) { + bool handled = false; + + if (event->type == Common::EVENT_KEYDOWN && !printable) { + switch (event->kbd.keycode) { + case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_TAB: + case Common::KEYCODE_RETURN: + return false; + + // ctrl+A + case Common::KEYCODE_a: + if (BaseKeyboardState::isControlDown()) { + _selStart = 0; + _selEnd = strlen(_text); + handled = true; + } + break; + + case Common::KEYCODE_BACKSPACE: + if (_selStart == _selEnd) { + if (_gameRef->_textRTL) { + deleteChars(_selStart, _selStart + 1); + } else { + deleteChars(_selStart - 1, _selStart); + } + } else { + deleteChars(_selStart, _selEnd); + } + if (_selEnd >= _selStart) { + _selEnd -= MAX(1, _selEnd - _selStart); + } + _selStart = _selEnd; + + handled = true; + break; + + case Common::KEYCODE_LEFT: + case Common::KEYCODE_UP: + _selEnd--; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + handled = true; + break; + + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_DOWN: + _selEnd++; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + handled = true; + break; + + case Common::KEYCODE_HOME: + if (_gameRef->_textRTL) { + _selEnd = strlen(_text); + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } else { + _selEnd = 0; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } + handled = true; + break; + + case Common::KEYCODE_END: + if (_gameRef->_textRTL) { + _selEnd = 0; + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } else { + _selEnd = strlen(_text); + if (!BaseKeyboardState::isShiftDown()) { + _selStart = _selEnd; + } + } + handled = true; + break; + + case Common::KEYCODE_DELETE: + if (_selStart == _selEnd) { + if (_gameRef->_textRTL) { + deleteChars(_selStart - 1, _selStart); + _selEnd--; + if (_selEnd < 0) { + _selEnd = 0; + } + } else { + deleteChars(_selStart, _selStart + 1); + } + } else { + deleteChars(_selStart, _selEnd); + } + if (_selEnd > _selStart) { + _selEnd -= (_selEnd - _selStart); + } + + _selStart = _selEnd; + handled = true; + break; + default: + break; + } + return handled; + } else if (event->type == Common::EVENT_KEYDOWN && printable) { + if (_selStart != _selEnd) { + deleteChars(_selStart, _selEnd); + } + + //WideString wstr = StringUtil::Utf8ToWide(event->kbd.ascii); + WideString wstr; + wstr += (char)event->kbd.ascii; + _selEnd += insertChars(_selEnd, (const byte *)StringUtil::wideToAnsi(wstr).c_str(), 1); + + if (_gameRef->_textRTL) { + _selEnd = _selStart; + } else { + _selStart = _selEnd; + } + + return true; + } + + return false; +} + + + +////////////////////////////////////////////////////////////////////////// +int UIEdit::deleteChars(int start, int end) { + if (start > end) { + BaseUtils::swap(&start, &end); + } + + start = MAX(start, (int)0); + end = MIN((size_t)end, strlen(_text)); + + char *str = new char[strlen(_text) - (end - start) + 1]; + if (str) { + if (start > 0) { + memcpy(str, _text, start); + } + memcpy(str + MAX(0, start), _text + end, strlen(_text) - end + 1); + + delete[] _text; + _text = str; + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + return end - start; +} + + +////////////////////////////////////////////////////////////////////////// +int UIEdit::insertChars(int pos, const byte *chars, int num) { + if ((int)strlen(_text) + num > _maxLength) { + num -= (strlen(_text) + num - _maxLength); + } + + pos = MAX(pos, (int)0); + pos = MIN((size_t)pos, strlen(_text)); + + char *str = new char[strlen(_text) + num + 1]; + if (str) { + if (pos > 0) { + memcpy(str, _text, pos); + } + memcpy(str + pos + num, _text + pos, strlen(_text) - pos + 1); + + memcpy(str + pos, chars, num); + + delete[] _text; + _text = str; + } + if (_parentNotify && _parent) { + _parent->applyEvent(getName()); + } + + return num; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIEdit::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_cursorBlinkRate)); + persistMgr->transfer(TMEMBER(_cursorChar)); + persistMgr->transfer(TMEMBER(_fontSelected)); + persistMgr->transfer(TMEMBER(_frameWidth)); + persistMgr->transfer(TMEMBER(_maxLength)); + persistMgr->transfer(TMEMBER(_scrollOffset)); + persistMgr->transfer(TMEMBER(_selEnd)); + persistMgr->transfer(TMEMBER(_selStart)); + + if (!persistMgr->getIsSaving()) { + _cursorVisible = false; + _lastBlinkTime = 0; + } + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_edit.h b/engines/wintermute/ui/ui_edit.h new file mode 100644 index 0000000000..5bb31422b6 --- /dev/null +++ b/engines/wintermute/ui/ui_edit.h @@ -0,0 +1,72 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIEDIT_H +#define WINTERMUTE_UIEDIT_H + +#include "engines/wintermute/persistent.h" +#include "engines/wintermute/ui/ui_object.h" +#include "common/events.h" + +namespace Wintermute { +class BaseFont; +class UIEdit : public UIObject { +public: + DECLARE_PERSISTENT(UIEdit, UIObject) + int _maxLength; + int insertChars(int pos, const byte *chars, int num); + int deleteChars(int start, int end); + bool _cursorVisible; + uint32 _lastBlinkTime; + virtual bool display(int offsetX, int offsetY); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + int _scrollOffset; + int _frameWidth; + uint32 _cursorBlinkRate; + void setCursorChar(const char *character); + char *_cursorChar; + int _selEnd; + int _selStart; + BaseFont *_fontSelected; + UIEdit(BaseGame *inGame); + virtual ~UIEdit(); + + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_entity.cpp b/engines/wintermute/ui/ui_entity.cpp new file mode 100644 index 0000000000..1cb4e0926b --- /dev/null +++ b/engines/wintermute/ui/ui_entity.cpp @@ -0,0 +1,366 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ad/ad_entity.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/ui/ui_entity.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIEntity, false) + +////////////////////////////////////////////////////////////////////////// +UIEntity::UIEntity(BaseGame *inGame) : UIObject(inGame) { + _type = UI_CUSTOM; + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +UIEntity::~UIEntity() { + if (_entity) { + _gameRef->unregisterObject(_entity); + } + _entity = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIEntity::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing ENTITY container file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(ENTITY_CONTAINER) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(NAME) +TOKEN_DEF(ENTITY) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIEntity::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(ENTITY_CONTAINER) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(NAME) + TOKEN_TABLE(ENTITY) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_ENTITY_CONTAINER) { + _gameRef->LOG(0, "'ENTITY_CONTAINER' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_ENTITY: + if (DID_FAIL(setEntity((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in ENTITY_CONTAINER definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading ENTITY_CONTAINER definition"); + return STATUS_FAILED; + } + + correctSize(); + + if (_gameRef->_editorMode) { + _width = 50; + _height = 50; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "ENTITY_CONTAINER\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + + if (_entity && _entity->getFilename()) { + buffer->putTextIndent(indent + 2, "ENTITY=\"%s\"\n", _entity->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::setEntity(const char *filename) { + if (_entity) { + _gameRef->unregisterObject(_entity); + } + _entity = new AdEntity(_gameRef); + if (!_entity || DID_FAIL(_entity->loadFile(filename))) { + delete _entity; + _entity = NULL; + return STATUS_FAILED; + } else { + _entity->_nonIntMouseEvents = true; + _entity->_sceneIndependent = true; + _entity->makeFreezable(false); + _gameRef->registerObject(_entity); + } + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + if (_entity) { + _entity->_posX = offsetX + _posX; + _entity->_posY = offsetY + _posY; + if (_entity->_scale < 0) { + _entity->_zoomable = false; + } + _entity->_shadowable = false; + + _entity->update(); + + bool origReg = _entity->_registrable; + + if (_entity->_registrable && _disable) { + _entity->_registrable = false; + } + + _entity->display(); + _entity->_registrable = origReg; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIEntity::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetEntity + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetEntity") == 0) { + stack->correctParams(0); + + if (_entity) { + stack->pushNative(_entity, true); + } else { + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetEntity + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetEntity") == 0) { + stack->correctParams(1); + + const char *filename = stack->pop()->getString(); + + if (DID_SUCCEED(setEntity(filename))) { + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIEntity::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("entity container"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Freezable + ////////////////////////////////////////////////////////////////////////// + else if (name == "Freezable") { + if (_entity) { + _scValue->setBool(_entity->_freezable); + } else { + _scValue->setBool(false); + } + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Freezable + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Freezable") == 0) { + if (_entity) { + _entity->makeFreezable(value->getBool()); + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIEntity::scToString() { + return "[entity container]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIEntity::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_entity)); + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_entity.h b/engines/wintermute/ui/ui_entity.h new file mode 100644 index 0000000000..b5f4450071 --- /dev/null +++ b/engines/wintermute/ui/ui_entity.h @@ -0,0 +1,59 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIENTITY_H +#define WINTERMUTE_UIENTITY_H + +#include "engines/wintermute/ui/ui_object.h" + +namespace Wintermute { +class AdEntity; +class UIEntity : public UIObject { +public: + DECLARE_PERSISTENT(UIEntity, UIObject) + UIEntity(BaseGame *inGame); + virtual ~UIEntity(); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX, int offsetY); + AdEntity *_entity; + bool setEntity(const char *filename); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_object.cpp b/engines/wintermute/ui/ui_object.cpp new file mode 100644 index 0000000000..8e5bae993c --- /dev/null +++ b/engines/wintermute/ui/ui_object.cpp @@ -0,0 +1,651 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/ui/ui_object.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/font/base_font_storage.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIObject, false) + +////////////////////////////////////////////////////////////////////////// +UIObject::UIObject(BaseGame *inGame) : BaseObject(inGame) { + _back = NULL; + _image = NULL; + _font = NULL; + _text = NULL; + _sharedFonts = _sharedImages = false; + + _width = _height = 0; + + _listenerObject = NULL; + _listenerParamObject = NULL; + _listenerParamDWORD = 0; + + _disable = false; + _visible = true; + + _type = UI_UNKNOWN; + _parent = NULL; + + _parentNotify = false; + + _focusedWidget = NULL; + + _canFocus = false; + _nonIntMouseEvents = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIObject::~UIObject() { + if (!_gameRef->_loadInProgress) { + SystemClassRegistry::getInstance()->enumInstances(BaseGame::invalidateValues, "ScValue", (void *)this); + } + + if (_back) { + delete _back; + } + if (_font && !_sharedFonts) { + _gameRef->_fontStorage->removeFont(_font); + } + + if (_image && !_sharedImages) { + delete _image; + } + + if (_text) { + delete[] _text; + } + + _focusedWidget = NULL; // ref only +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::setText(const char *text) { + if (_text) { + delete[] _text; + } + _text = new char [strlen(text) + 1]; + if (_text) { + strcpy(_text, text); + for (uint32 i = 0; i < strlen(_text); i++) { + if (_text[i] == '|') { + _text[i] = '\n'; + } + } + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::display(int offsetX, int offsetY) { + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam) { + _listenerObject = object; + _listenerParamObject = listenerObject; + _listenerParamDWORD = listenerParam; +} + + +////////////////////////////////////////////////////////////////////////// +void UIObject::correctSize() { + Rect32 rect; + + if (_width <= 0) { + if (_image) { + _image->getBoundingRect(&rect, 0, 0); + _width = rect.right - rect.left; + } else { + _width = 100; + } + } + + if (_height <= 0) { + if (_image) { + _image->getBoundingRect(&rect, 0, 0); + _height = rect.bottom - rect.top; + } + } + + if (_back) { + _back->correctSize(&_width, &_height); + } +} + + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIObject::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SetFont + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SetFont") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + if (val->isNULL()) { + _font = NULL; + stack->pushBool(true); + } else { + _font = _gameRef->_fontStorage->addFont(val->getString()); + stack->pushBool(_font != NULL); + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetImage") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + /* const char *filename = */ val->getString(); + + delete _image; + _image = NULL; + if (val->isNULL()) { + stack->pushBool(true); + return STATUS_OK; + } + + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile(val->getString()))) { + delete _image; + _image = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetImage") == 0) { + stack->correctParams(0); + if (!_image || !_image->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_image->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetImageObject") == 0) { + stack->correctParams(0); + if (!_image) { + stack->pushNULL(); + } else { + stack->pushNative(_image, true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Focus + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Focus") == 0) { + stack->correctParams(0); + focus(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveAfter / MoveBefore + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveAfter") == 0 || strcmp(name, "MoveBefore") == 0) { + stack->correctParams(1); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + + uint32 i; + bool found = false; + ScValue *val = stack->pop(); + // find directly + if (val->isNative()) { + UIObject *widget = (UIObject *)val->getNative(); + for (i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == widget) { + found = true; + break; + } + } + } + // find by name + else { + const char *findName = val->getString(); + for (i = 0; i < win->_widgets.size(); i++) { + if (scumm_stricmp(win->_widgets[i]->getName(), findName) == 0) { + found = true; + break; + } + } + } + + if (found) { + bool done = false; + for (uint32 j = 0; j < win->_widgets.size(); j++) { + if (win->_widgets[j] == this) { + if (strcmp(name, "MoveAfter") == 0) { + i++; + } + if (j >= i) { + j++; + } + + win->_widgets.insert_at(i, this); + win->_widgets.remove_at(j); + + done = true; + stack->pushBool(true); + break; + } + } + if (!done) { + stack->pushBool(false); + } + } else { + stack->pushBool(false); + } + + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveToBottom + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveToBottom") == 0) { + stack->correctParams(0); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + win->_widgets.remove_at(i); + win->_widgets.insert_at(0, this); + break; + } + } + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // MoveToTop + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "MoveToTop") == 0) { + stack->correctParams(0); + + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + win->_widgets.remove_at(i); + win->_widgets.add(this); + break; + } + } + stack->pushBool(true); + } else { + stack->pushBool(false); + } + + return STATUS_OK; + } else { + return BaseObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIObject::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("ui_object"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + else if (name == "Name") { + _scValue->setString(getName()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Parent (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "Parent") { + _scValue->setNative(_parent, true); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ParentNotify + ////////////////////////////////////////////////////////////////////////// + else if (name == "ParentNotify") { + _scValue->setBool(_parentNotify); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (name == "Width") { + _scValue->setInt(_width); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (name == "Height") { + _scValue->setInt(_height); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Visible + ////////////////////////////////////////////////////////////////////////// + else if (name == "Visible") { + _scValue->setBool(_visible); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Disabled + ////////////////////////////////////////////////////////////////////////// + else if (name == "Disabled") { + _scValue->setBool(_disable); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (name == "Text") { + _scValue->setString(_text); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NextSibling (RO) / PrevSibling (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NextSibling" || name == "PrevSibling") { + _scValue->setNULL(); + if (_parent && _parent->_type == UI_WINDOW) { + UIWindow *win = (UIWindow *)_parent; + for (uint32 i = 0; i < win->_widgets.size(); i++) { + if (win->_widgets[i] == this) { + if (name == "NextSibling") { + if (i < win->_widgets.size() - 1) { + _scValue->setNative(win->_widgets[i + 1], true); + } + } else { + if (i > 0) { + _scValue->setNative(win->_widgets[i - 1], true); + } + } + break; + } + } + } + return _scValue; + } else { + return BaseObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ParentNotify + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ParentNotify") == 0) { + _parentNotify = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Width + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Width") == 0) { + _width = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Height + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Height") == 0) { + _height = value->getInt(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Visible + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Visible") == 0) { + _visible = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Disabled + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Disabled") == 0) { + _disable = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Text") == 0) { + setText(value->getString()); + return STATUS_OK; + } else { + return BaseObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIObject::scToString() { + return "[ui_object]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::isFocused() { + if (!_gameRef->_focusedWindow) { + return false; + } + if (_gameRef->_focusedWindow == this) { + return true; + } + + UIObject *obj = _gameRef->_focusedWindow; + while (obj) { + if (obj == this) { + return true; + } else { + obj = obj->_focusedWidget; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::handleMouse(TMouseEvent event, TMouseButton button) { + // handle focus change + if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) { + focus(); + } + return BaseObject::handleMouse(event, button); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::focus() { + UIObject *obj = this; + bool disabled = false; + while (obj) { + if (obj->_disable && obj->_type == UI_WINDOW) { + disabled = true; + break; + } + obj = obj->_parent; + } + if (!disabled) { + obj = this; + while (obj) { + if (obj->_parent) { + if (!obj->_disable && obj->_canFocus) { + obj->_parent->_focusedWidget = obj; + } + } else { + if (obj->_type == UI_WINDOW) { + _gameRef->focusWindow((UIWindow *)obj); + } + } + + obj = obj->_parent; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::getTotalOffset(int *offsetX, int *offsetY) { + int offX = 0, offY = 0; + + UIObject *obj = _parent; + while (obj) { + offX += obj->_posX; + offY += obj->_posY; + + obj = obj->_parent; + } + if (offsetX) { + *offsetX = offX; + } + if (offsetY) { + *offsetY = offY; + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIObject::persist(BasePersistenceManager *persistMgr) { + + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_back)); + persistMgr->transfer(TMEMBER(_canFocus)); + persistMgr->transfer(TMEMBER(_disable)); + persistMgr->transfer(TMEMBER(_focusedWidget)); + persistMgr->transfer(TMEMBER(_font)); + persistMgr->transfer(TMEMBER(_height)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_listenerObject)); + persistMgr->transfer(TMEMBER(_listenerParamObject)); + persistMgr->transfer(TMEMBER(_listenerParamDWORD)); + persistMgr->transfer(TMEMBER(_parent)); + persistMgr->transfer(TMEMBER(_parentNotify)); + persistMgr->transfer(TMEMBER(_sharedFonts)); + persistMgr->transfer(TMEMBER(_sharedImages)); + persistMgr->transfer(TMEMBER(_text)); + persistMgr->transfer(TMEMBER_INT(_type)); + persistMgr->transfer(TMEMBER(_visible)); + persistMgr->transfer(TMEMBER(_width)); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIObject::saveAsText(BaseDynamicBuffer *buffer, int indent) { + return STATUS_FAILED; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_object.h b/engines/wintermute/ui/ui_object.h new file mode 100644 index 0000000000..ec2ea33de1 --- /dev/null +++ b/engines/wintermute/ui/ui_object.h @@ -0,0 +1,85 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIOBJECT_H +#define WINTERMUTE_UIOBJECT_H + + +#include "engines/wintermute/base/base_object.h" +#include "engines/wintermute/dctypes.h" // Added by ClassView + +namespace Wintermute { + +class UITiledImage; +class BaseFont; +class UIObject : public BaseObject { +public: + + bool getTotalOffset(int *offsetX, int *offsetY); + bool _canFocus; + bool focus(); + virtual bool handleMouse(TMouseEvent event, TMouseButton button); + bool isFocused(); + bool _parentNotify; + DECLARE_PERSISTENT(UIObject, BaseObject) + UIObject *_parent; + virtual bool display() { return display(0, 0); } + virtual bool display(int offsetX) { return display(offsetX, 0); } + virtual bool display(int offsetX, int offsetY); + virtual void correctSize(); + bool _sharedFonts; + bool _sharedImages; + void setText(const char *text); + char *_text; + BaseFont *_font; + bool _visible; + UITiledImage *_back; + bool _disable; + UIObject(BaseGame *inGame = NULL); + virtual ~UIObject(); + int _width; + int _height; + TUIObjectType _type; + BaseSprite *_image; + void setListener(BaseScriptHolder *object, BaseScriptHolder *listenerObject, uint32 listenerParam); + BaseScriptHolder *_listenerParamObject; + uint32 _listenerParamDWORD; + BaseScriptHolder *_listenerObject; + UIObject *_focusedWidget; + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_text.cpp b/engines/wintermute/ui/ui_text.cpp new file mode 100644 index 0000000000..2c10f176c7 --- /dev/null +++ b/engines/wintermute/ui/ui_text.cpp @@ -0,0 +1,522 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIText, false) + +////////////////////////////////////////////////////////////////////////// +UIText::UIText(BaseGame *inGame) : UIObject(inGame) { + _textAlign = TAL_LEFT; + _verticalAlign = VAL_CENTER; + _type = UI_STATIC; + _canFocus = false; +} + + +////////////////////////////////////////////////////////////////////////// +UIText::~UIText() { + +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::display(int offsetX, int offsetY) { + if (!_visible) { + return STATUS_OK; + } + + + BaseFont *font = _font; + if (!font) { + font = _gameRef->_systemFont; + } + + if (_back) { + _back->display(offsetX + _posX, offsetY + _posY, _width, _height); + } + if (_image) { + _image->draw(offsetX + _posX, offsetY + _posY, NULL); + } + + if (font && _text) { + int textOffset; + switch (_verticalAlign) { + case VAL_TOP: + textOffset = 0; + break; + case VAL_BOTTOM: + textOffset = _height - font->getTextHeight((byte *)_text, _width); + break; + default: + textOffset = (_height - font->getTextHeight((byte *)_text, _width)) / 2; + } + font->drawText((byte *)_text, offsetX + _posX, offsetY + _posY + textOffset, _width, _textAlign, _height); + } + + //_gameRef->_renderer->_rectList.add(new BaseActiveRect(_gameRef, this, NULL, OffsetX + _posX, OffsetY + _posY, _width, _height, 100, 100, false)); + + return STATUS_OK; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIText::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIText::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing STATIC file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(STATIC) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT) +TOKEN_DEF(TEXT_ALIGN) +TOKEN_DEF(VERTICAL_ALIGN) +TOKEN_DEF(TEXT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIText::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(STATIC) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TEXT_ALIGN) + TOKEN_TABLE(VERTICAL_ALIGN) + TOKEN_TABLE(TEXT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_STATIC) { + _gameRef->LOG(0, "'STATIC' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd > 0 && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TEXT: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TEXT_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _textAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _textAlign = TAL_RIGHT; + } else { + _textAlign = TAL_CENTER; + } + break; + + case TOKEN_VERTICAL_ALIGN: + if (scumm_stricmp((char *)params, "top") == 0) { + _verticalAlign = VAL_TOP; + } else if (scumm_stricmp((char *)params, "bottom") == 0) { + _verticalAlign = VAL_BOTTOM; + } else { + _verticalAlign = VAL_CENTER; + } + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in STATIC definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading STATIC definition"); + return STATUS_FAILED; + } + + correctSize(); + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIText::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "STATIC\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + if (_text) { + buffer->putTextIndent(indent + 2, "TEXT=\"%s\"\n", _text); + } + + switch (_textAlign) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TEXT_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIText::SaveAsText - Unhandled enum"); + break; + } + + switch (_verticalAlign) { + case VAL_TOP: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "top"); + break; + case VAL_BOTTOM: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "bottom"); + break; + case VAL_CENTER: + buffer->putTextIndent(indent + 2, "VERTICAL_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIText::SaveAsText - Unhandled enum value: NUM_VERTICAL_ALIGN"); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIText::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // SizeToFit + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "SizeToFit") == 0) { + stack->correctParams(0); + sizeToFit(); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // HeightToFit + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "HeightToFit") == 0) { + stack->correctParams(0); + if (_font && _text) { + _height = _font->getTextHeight((byte *)_text, _width); + } + stack->pushNULL(); + return STATUS_OK; + } else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIText::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("static"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "TextAlign") { + _scValue->setInt(_textAlign); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // VerticalAlign + ////////////////////////////////////////////////////////////////////////// + else if (name == "VerticalAlign") { + _scValue->setInt(_verticalAlign); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // TextAlign + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "TextAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_TEXT_ALIGN) { + i = 0; + } + _textAlign = (TTextAlign)i; + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // VerticalAlign + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "VerticalAlign") == 0) { + int i = value->getInt(); + if (i < 0 || i >= NUM_VERTICAL_ALIGN) { + i = 0; + } + _verticalAlign = (TVerticalAlign)i; + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIText::scToString() { + return "[static]"; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIText::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + persistMgr->transfer(TMEMBER_INT(_textAlign)); + persistMgr->transfer(TMEMBER_INT(_verticalAlign)); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIText::sizeToFit() { + if (_font && _text) { + _width = _font->getTextWidth((byte *)_text); + _height = _font->getTextHeight((byte *)_text, _width); + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_text.h b/engines/wintermute/ui/ui_text.h new file mode 100644 index 0000000000..da4d113500 --- /dev/null +++ b/engines/wintermute/ui/ui_text.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UITEXT_H +#define WINTERMUTE_UITEXT_H + + +#include "engines/wintermute/ui/ui_object.h" + +namespace Wintermute { + +class UIText : public UIObject { +private: + bool sizeToFit(); +public: + virtual bool display(int offsetX, int offsetY); + DECLARE_PERSISTENT(UIText, UIObject) + UIText(BaseGame *inGame = NULL); + virtual ~UIText(); + TTextAlign _textAlign; + TVerticalAlign _verticalAlign; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_tiled_image.cpp b/engines/wintermute/ui/ui_tiled_image.cpp new file mode 100644 index 0000000000..2b337330c7 --- /dev/null +++ b/engines/wintermute/ui/ui_tiled_image.cpp @@ -0,0 +1,394 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/base/gfx/base_surface.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_sub_frame.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UITiledImage, false) + +////////////////////////////////////////////////////////////////////////// +UITiledImage::UITiledImage(BaseGame *inGame) : BaseObject(inGame) { + _image = NULL; + + BasePlatform::setRectEmpty(&_upLeft); + BasePlatform::setRectEmpty(&_upMiddle); + BasePlatform::setRectEmpty(&_upRight); + BasePlatform::setRectEmpty(&_middleLeft); + BasePlatform::setRectEmpty(&_middleMiddle); + BasePlatform::setRectEmpty(&_middleRight); + BasePlatform::setRectEmpty(&_downLeft); + BasePlatform::setRectEmpty(&_downMiddle); + BasePlatform::setRectEmpty(&_downRight); +} + + +////////////////////////////////////////////////////////////////////////// +UITiledImage::~UITiledImage() { + delete _image; + _image = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::display(int x, int y, int width, int height) { + if (!_image) { + return STATUS_FAILED; + } + + int tileWidth = _middleMiddle.right - _middleMiddle.left; + int tileHeight = _middleMiddle.bottom - _middleMiddle.top; + + int nuColumns = (width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth; + int nuRows = (height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight; + + int col, row; + + _gameRef->_renderer->startSpriteBatch(); + + // top left/right + _image->_surface->displayTrans(x, y, _upLeft); + _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y, _upRight); + + // bottom left/right + _image->_surface->displayTrans(x, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downLeft); + _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downRight); + + // left/right + int yyy = y + (_upMiddle.bottom - _upMiddle.top); + for (row = 0; row < nuRows; row++) { + _image->_surface->displayTrans(x, yyy, _middleLeft); + _image->_surface->displayTrans(x + (_middleLeft.right - _middleLeft.left) + nuColumns * tileWidth, yyy, _middleRight); + yyy += tileWidth; + } + + // top/bottom + int xxx = x + (_upLeft.right - _upLeft.left); + for (col = 0; col < nuColumns; col++) { + _image->_surface->displayTrans(xxx, y, _upMiddle); + _image->_surface->displayTrans(xxx, y + (_upMiddle.bottom - _upMiddle.top) + nuRows * tileHeight, _downMiddle); + xxx += tileWidth; + } + + // tiles + yyy = y + (_upMiddle.bottom - _upMiddle.top); + for (row = 0; row < nuRows; row++) { + xxx = x + (_upLeft.right - _upLeft.left); + for (col = 0; col < nuColumns; col++) { + _image->_surface->displayTrans(xxx, yyy, _middleMiddle); + xxx += tileWidth; + } + yyy += tileWidth; + } + + _gameRef->_renderer->endSpriteBatch(); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UITiledImage::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing TILED_IMAGE file '%s'", filename); + } + + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(TILED_IMAGE) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(IMAGE) +TOKEN_DEF(UP_LEFT) +TOKEN_DEF(UP_RIGHT) +TOKEN_DEF(UP_MIDDLE) +TOKEN_DEF(DOWN_LEFT) +TOKEN_DEF(DOWN_RIGHT) +TOKEN_DEF(DOWN_MIDDLE) +TOKEN_DEF(MIDDLE_LEFT) +TOKEN_DEF(MIDDLE_RIGHT) +TOKEN_DEF(MIDDLE_MIDDLE) +TOKEN_DEF(VERTICAL_TILES) +TOKEN_DEF(HORIZONTAL_TILES) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(TILED_IMAGE) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(UP_LEFT) + TOKEN_TABLE(UP_RIGHT) + TOKEN_TABLE(UP_MIDDLE) + TOKEN_TABLE(DOWN_LEFT) + TOKEN_TABLE(DOWN_RIGHT) + TOKEN_TABLE(DOWN_MIDDLE) + TOKEN_TABLE(MIDDLE_LEFT) + TOKEN_TABLE(MIDDLE_RIGHT) + TOKEN_TABLE(MIDDLE_MIDDLE) + TOKEN_TABLE(VERTICAL_TILES) + TOKEN_TABLE(HORIZONTAL_TILES) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE_END + + byte *params; + int cmd; + BaseParser parser; + bool hTiles = false, vTiles = false; + int h1 = 0, h2 = 0, h3 = 0; + int v1 = 0, v2 = 0, v3 = 0; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_TILED_IMAGE) { + _gameRef->LOG(0, "'TILED_IMAGE' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while ((cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) > 0) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSubFrame(_gameRef); + if (!_image || DID_FAIL(_image->setSurface((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_UP_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upLeft.left, &_upLeft.top, &_upLeft.right, &_upLeft.bottom); + break; + + case TOKEN_UP_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upRight.left, &_upRight.top, &_upRight.right, &_upRight.bottom); + break; + + case TOKEN_UP_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_upMiddle.left, &_upMiddle.top, &_upMiddle.right, &_upMiddle.bottom); + break; + + case TOKEN_DOWN_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downLeft.left, &_downLeft.top, &_downLeft.right, &_downLeft.bottom); + break; + + case TOKEN_DOWN_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downRight.left, &_downRight.top, &_downRight.right, &_downRight.bottom); + break; + + case TOKEN_DOWN_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_downMiddle.left, &_downMiddle.top, &_downMiddle.right, &_downMiddle.bottom); + break; + + case TOKEN_MIDDLE_LEFT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleLeft.left, &_middleLeft.top, &_middleLeft.right, &_middleLeft.bottom); + break; + + case TOKEN_MIDDLE_RIGHT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleRight.left, &_middleRight.top, &_middleRight.right, &_middleRight.bottom); + break; + + case TOKEN_MIDDLE_MIDDLE: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_middleMiddle.left, &_middleMiddle.top, &_middleMiddle.right, &_middleMiddle.bottom); + break; + + case TOKEN_HORIZONTAL_TILES: + parser.scanStr((char *)params, "%d,%d,%d", &h1, &h2, &h3); + hTiles = true; + break; + + case TOKEN_VERTICAL_TILES: + parser.scanStr((char *)params, "%d,%d,%d", &v1, &v2, &v3); + vTiles = true; + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in TILED_IMAGE definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading TILED_IMAGE definition"); + return STATUS_FAILED; + } + + if (vTiles && hTiles) { + // up row + BasePlatform::setRect(&_upLeft, 0, 0, h1, v1); + BasePlatform::setRect(&_upMiddle, h1, 0, h1 + h2, v1); + BasePlatform::setRect(&_upRight, h1 + h2, 0, h1 + h2 + h3, v1); + + // middle row + BasePlatform::setRect(&_middleLeft, 0, v1, h1, v1 + v2); + BasePlatform::setRect(&_middleMiddle, h1, v1, h1 + h2, v1 + v2); + BasePlatform::setRect(&_middleRight, h1 + h2, v1, h1 + h2 + h3, v1 + v2); + + // down row + BasePlatform::setRect(&_downLeft, 0, v1 + v2, h1, v1 + v2 + v3); + BasePlatform::setRect(&_downMiddle, h1, v1 + v2, h1 + h2, v1 + v2 + v3); + BasePlatform::setRect(&_downRight, h1 + h2, v1 + v2, h1 + h2 + h3, v1 + v2 + v3); + } + + // default + if (_image && _image->_surface) { + int width = _image->_surface->getWidth() / 3; + int height = _image->_surface->getHeight() / 3; + + if (BasePlatform::isRectEmpty(&_upLeft)) { + BasePlatform::setRect(&_upLeft, 0, 0, width, height); + } + if (BasePlatform::isRectEmpty(&_upMiddle)) { + BasePlatform::setRect(&_upMiddle, width, 0, 2 * width, height); + } + if (BasePlatform::isRectEmpty(&_upRight)) { + BasePlatform::setRect(&_upRight, 2 * width, 0, 3 * width, height); + } + + if (BasePlatform::isRectEmpty(&_middleLeft)) { + BasePlatform::setRect(&_middleLeft, 0, height, width, 2 * height); + } + if (BasePlatform::isRectEmpty(&_middleMiddle)) { + BasePlatform::setRect(&_middleMiddle, width, height, 2 * width, 2 * height); + } + if (BasePlatform::isRectEmpty(&_middleRight)) { + BasePlatform::setRect(&_middleRight, 2 * width, height, 3 * width, 2 * height); + } + + if (BasePlatform::isRectEmpty(&_downLeft)) { + BasePlatform::setRect(&_downLeft, 0, 2 * height, width, 3 * height); + } + if (BasePlatform::isRectEmpty(&_downMiddle)) { + BasePlatform::setRect(&_downMiddle, width, 2 * height, 2 * width, 3 * height); + } + if (BasePlatform::isRectEmpty(&_downRight)) { + BasePlatform::setRect(&_downRight, 2 * width, 2 * height, 3 * width, 3 * height); + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "TILED_IMAGE\n"); + buffer->putTextIndent(indent, "{\n"); + + if (_image && _image->getSurfaceFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getSurfaceFilename()); + } + + int h1, h2, h3; + int v1, v2, v3; + + h1 = _upLeft.right; + h2 = _upMiddle.right - _upMiddle.left; + h3 = _upRight.right - _upRight.left; + + v1 = _upLeft.bottom; + v2 = _middleLeft.bottom - _middleLeft.top; + v3 = _downLeft.bottom - _downLeft.top; + + + buffer->putTextIndent(indent + 2, "VERTICAL_TILES { %d, %d, %d }\n", v1, v2, v3); + buffer->putTextIndent(indent + 2, "HORIZONTAL_TILES { %d, %d, %d }\n", h1, h2, h3); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +void UITiledImage::correctSize(int *width, int *height) { + int tileWidth = _middleMiddle.right - _middleMiddle.left; + int tileHeight = _middleMiddle.bottom - _middleMiddle.top; + + int nuColumns = (*width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth; + int nuRows = (*height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight; + + *width = (_middleLeft.right - _middleLeft.left) + (_middleRight.right - _middleRight.left) + nuColumns * tileWidth; + *height = (_upMiddle.bottom - _upMiddle.top) + (_downMiddle.bottom - _downMiddle.top) + nuRows * tileHeight; +} + + +////////////////////////////////////////////////////////////////////////// +bool UITiledImage::persist(BasePersistenceManager *persistMgr) { + BaseObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_downLeft)); + persistMgr->transfer(TMEMBER(_downMiddle)); + persistMgr->transfer(TMEMBER(_downRight)); + persistMgr->transfer(TMEMBER(_image)); + persistMgr->transfer(TMEMBER(_middleLeft)); + persistMgr->transfer(TMEMBER(_middleMiddle)); + persistMgr->transfer(TMEMBER(_middleRight)); + persistMgr->transfer(TMEMBER(_upLeft)); + persistMgr->transfer(TMEMBER(_upMiddle)); + persistMgr->transfer(TMEMBER(_upRight)); + + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_tiled_image.h b/engines/wintermute/ui/ui_tiled_image.h new file mode 100644 index 0000000000..c413e7f129 --- /dev/null +++ b/engines/wintermute/ui/ui_tiled_image.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UITILEDIMAGE_H +#define WINTERMUTE_UITILEDIMAGE_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "common/rect.h" + +namespace Wintermute { +class BaseSubFrame; +class UITiledImage : public BaseObject { +public: + DECLARE_PERSISTENT(UITiledImage, BaseObject) + void correctSize(int *width, int *height); + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + bool display(int x, int y, int width, int height); + UITiledImage(BaseGame *inGame = NULL); + virtual ~UITiledImage(); + BaseSubFrame *_image; + Rect32 _upLeft; + Rect32 _upMiddle; + Rect32 _upRight; + Rect32 _middleLeft; + Rect32 _middleMiddle; + Rect32 _middleRight; + Rect32 _downLeft; + Rect32 _downMiddle; + Rect32 _downRight; +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp new file mode 100644 index 0000000000..9606486efb --- /dev/null +++ b/engines/wintermute/ui/ui_window.cpp @@ -0,0 +1,1445 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_parser.h" +#include "engines/wintermute/base/base_active_rect.h" +#include "engines/wintermute/base/base_dynamic_buffer.h" +#include "engines/wintermute/base/base_keyboard_state.h" +#include "engines/wintermute/base/scriptables/script_value.h" +#include "engines/wintermute/ui/ui_button.h" +#include "engines/wintermute/ui/ui_edit.h" +#include "engines/wintermute/ui/ui_text.h" +#include "engines/wintermute/ui/ui_tiled_image.h" +#include "engines/wintermute/ui/ui_window.h" +#include "engines/wintermute/base/base_viewport.h" +#include "engines/wintermute/base/font/base_font_storage.h" +#include "engines/wintermute/base/font/base_font.h" +#include "engines/wintermute/base/base_string_table.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script.h" +#include "engines/wintermute/base/scriptables/script_stack.h" +#include "engines/wintermute/base/base_sprite.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/platform_osystem.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(UIWindow, false) + +////////////////////////////////////////////////////////////////////////// +UIWindow::UIWindow(BaseGame *inGame) : UIObject(inGame) { + BasePlatform::setRectEmpty(&_titleRect); + BasePlatform::setRectEmpty(&_dragRect); + _titleAlign = TAL_LEFT; + _transparent = false; + + _backInactive = NULL; + _fontInactive = NULL; + _imageInactive = NULL; + + _type = UI_WINDOW; + _canFocus = true; + + _dragging = false; + _dragFrom.x = _dragFrom.y = 0; + + _mode = WINDOW_NORMAL; + _shieldWindow = NULL; + _shieldButton = NULL; + + _fadeColor = 0x00000000; + _fadeBackground = false; + + _ready = true; + _isMenu = false; + _inGame = false; + + _clipContents = false; + _viewport = NULL; + + _pauseMusic = true; +} + + +////////////////////////////////////////////////////////////////////////// +UIWindow::~UIWindow() { + close(); + cleanup(); +} + + +////////////////////////////////////////////////////////////////////////// +void UIWindow::cleanup() { + delete _shieldWindow; + delete _shieldButton; + delete _viewport; + _shieldWindow = NULL; + _shieldButton = NULL; + _viewport = NULL; + + delete _backInactive; + if (!_sharedFonts && _fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + if (!_sharedImages && _imageInactive) { + delete _imageInactive; + } + + for (uint32 i = 0; i < _widgets.size(); i++) { + delete _widgets[i]; + } + _widgets.clear(); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::display(int offsetX, int offsetY) { + // go exclusive + if (_mode == WINDOW_EXCLUSIVE || _mode == WINDOW_SYSTEM_EXCLUSIVE) { + if (!_shieldWindow) { + _shieldWindow = new UIWindow(_gameRef); + } + if (_shieldWindow) { + _shieldWindow->_posX = _shieldWindow->_posY = 0; + _shieldWindow->_width = _gameRef->_renderer->_width; + _shieldWindow->_height = _gameRef->_renderer->_height; + + _shieldWindow->display(); + } + } else if (_isMenu) { + if (!_shieldButton) { + _shieldButton = new UIButton(_gameRef); + _shieldButton->setName("close"); + _shieldButton->setListener(this, _shieldButton, 0); + _shieldButton->_parent = this; + } + if (_shieldButton) { + _shieldButton->_posX = _shieldButton->_posY = 0; + _shieldButton->_width = _gameRef->_renderer->_width; + _shieldButton->_height = _gameRef->_renderer->_height; + + _shieldButton->display(); + } + } + + if (!_visible) { + return STATUS_OK; + } + + if (_fadeBackground) { + Graphics::PixelFormat format = _gameRef->_renderer->getPixelFormat(); + byte fadeR, fadeG, fadeB, fadeA; + // First convert from the internal format to the screen-format + uint32 fadeColor = format.ARGBToColor(RGBCOLGetA(_fadeColor), RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor)); + // Then get components + format.colorToARGB(fadeColor, fadeA, fadeR, fadeG, fadeB); + _gameRef->_renderer->fadeToColor(fadeR, fadeG, fadeB, fadeA); + } + + if (_dragging) { + _posX += (_gameRef->_mousePos.x - _dragFrom.x); + _posY += (_gameRef->_mousePos.y - _dragFrom.y); + + _dragFrom.x = _gameRef->_mousePos.x; + _dragFrom.y = _gameRef->_mousePos.y; + } + + if (!_focusedWidget || (!_focusedWidget->_canFocus || _focusedWidget->_disable || !_focusedWidget->_visible)) { + moveFocus(); + } + + bool popViewport = false; + if (_clipContents) { + if (!_viewport) { + _viewport = new BaseViewport(_gameRef); + } + if (_viewport) { + _viewport->setRect(_posX + offsetX, _posY + offsetY, _posX + _width + offsetX, _posY + _height + offsetY); + _gameRef->pushViewport(_viewport); + popViewport = true; + } + } + + + UITiledImage *back = _back; + BaseSprite *image = _image; + BaseFont *font = _font; + + if (!isFocused()) { + if (_backInactive) { + back = _backInactive; + } + if (_imageInactive) { + image = _imageInactive; + } + if (_fontInactive) { + font = _fontInactive; + } + } + + if (_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = _alphaColor; + } + if (back) { + back->display(_posX + offsetX, _posY + offsetY, _width, _height); + } + if (image) { + image->draw(_posX + offsetX, _posY + offsetY, _transparent ? NULL : this); + } + + if (!BasePlatform::isRectEmpty(&_titleRect) && font && _text) { + font->drawText((byte *)_text, _posX + offsetX + _titleRect.left, _posY + offsetY + _titleRect.top, _titleRect.right - _titleRect.left, _titleAlign, _titleRect.bottom - _titleRect.top); + } + + if (!_transparent && !image) { + _gameRef->_renderer->addRectToList(new BaseActiveRect(_gameRef, this, NULL, _posX + offsetX, _posY + offsetY, _width, _height, 100, 100, false)); + } + + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->display(_posX + offsetX, _posY + offsetY); + } + + if (_alphaColor != 0) { + _gameRef->_renderer->_forceAlphaColor = 0; + } + + if (popViewport) { + _gameRef->popViewport(); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::loadFile(const char *filename) { + byte *buffer = BaseFileManager::getEngineInstance()->readWholeFile(filename); + if (buffer == NULL) { + _gameRef->LOG(0, "UIWindow::LoadFile failed for file '%s'", filename); + return STATUS_FAILED; + } + + bool ret; + + setFilename(filename); + + if (DID_FAIL(ret = loadBuffer(buffer, true))) { + _gameRef->LOG(0, "Error parsing WINDOW file '%s'", filename); + } + + delete[] buffer; + + return ret; +} + + +TOKEN_DEF_START +TOKEN_DEF(WINDOW) +TOKEN_DEF(ALPHA_COLOR) +TOKEN_DEF(ALPHA) +TOKEN_DEF(TEMPLATE) +TOKEN_DEF(DISABLED) +TOKEN_DEF(VISIBLE) +TOKEN_DEF(BACK_INACTIVE) +TOKEN_DEF(BACK) +TOKEN_DEF(IMAGE_INACTIVE) +TOKEN_DEF(IMAGE) +TOKEN_DEF(FONT_INACTIVE) +TOKEN_DEF(FONT) +TOKEN_DEF(TITLE_ALIGN) +TOKEN_DEF(TITLE_RECT) +TOKEN_DEF(TITLE) +TOKEN_DEF(DRAG_RECT) +TOKEN_DEF(X) +TOKEN_DEF(Y) +TOKEN_DEF(WIDTH) +TOKEN_DEF(HEIGHT) +TOKEN_DEF(FADE_ALPHA) +TOKEN_DEF(FADE_COLOR) +TOKEN_DEF(CURSOR) +TOKEN_DEF(NAME) +TOKEN_DEF(BUTTON) +TOKEN_DEF(STATIC) +TOKEN_DEF(TRANSPARENT) +TOKEN_DEF(SCRIPT) +TOKEN_DEF(CAPTION) +TOKEN_DEF(PARENT_NOTIFY) +TOKEN_DEF(MENU) +TOKEN_DEF(IN_GAME) +TOKEN_DEF(CLIP_CONTENTS) +TOKEN_DEF(PAUSE_MUSIC) +TOKEN_DEF(EDITOR_PROPERTY) +TOKEN_DEF(EDIT) +TOKEN_DEF_END +////////////////////////////////////////////////////////////////////////// +bool UIWindow::loadBuffer(byte *buffer, bool complete) { + TOKEN_TABLE_START(commands) + TOKEN_TABLE(WINDOW) + TOKEN_TABLE(ALPHA_COLOR) + TOKEN_TABLE(ALPHA) + TOKEN_TABLE(TEMPLATE) + TOKEN_TABLE(DISABLED) + TOKEN_TABLE(VISIBLE) + TOKEN_TABLE(BACK_INACTIVE) + TOKEN_TABLE(BACK) + TOKEN_TABLE(IMAGE_INACTIVE) + TOKEN_TABLE(IMAGE) + TOKEN_TABLE(FONT_INACTIVE) + TOKEN_TABLE(FONT) + TOKEN_TABLE(TITLE_ALIGN) + TOKEN_TABLE(TITLE_RECT) + TOKEN_TABLE(TITLE) + TOKEN_TABLE(DRAG_RECT) + TOKEN_TABLE(X) + TOKEN_TABLE(Y) + TOKEN_TABLE(WIDTH) + TOKEN_TABLE(HEIGHT) + TOKEN_TABLE(FADE_ALPHA) + TOKEN_TABLE(FADE_COLOR) + TOKEN_TABLE(CURSOR) + TOKEN_TABLE(NAME) + TOKEN_TABLE(BUTTON) + TOKEN_TABLE(STATIC) + TOKEN_TABLE(TRANSPARENT) + TOKEN_TABLE(SCRIPT) + TOKEN_TABLE(CAPTION) + TOKEN_TABLE(PARENT_NOTIFY) + TOKEN_TABLE(MENU) + TOKEN_TABLE(IN_GAME) + TOKEN_TABLE(CLIP_CONTENTS) + TOKEN_TABLE(PAUSE_MUSIC) + TOKEN_TABLE(EDITOR_PROPERTY) + TOKEN_TABLE(EDIT) + TOKEN_TABLE_END + + byte *params; + int cmd = 2; + BaseParser parser; + + int fadeR = 0, fadeG = 0, fadeB = 0, fadeA = 0; + int ar = 0, ag = 0, ab = 0, alpha = 0; + + if (complete) { + if (parser.getCommand((char **)&buffer, commands, (char **)¶ms) != TOKEN_WINDOW) { + _gameRef->LOG(0, "'WINDOW' keyword expected."); + return STATUS_FAILED; + } + buffer = params; + } + + while (cmd >= PARSERR_TOKENNOTFOUND && (cmd = parser.getCommand((char **)&buffer, commands, (char **)¶ms)) >= PARSERR_TOKENNOTFOUND) { + switch (cmd) { + case TOKEN_TEMPLATE: + if (DID_FAIL(loadFile((char *)params))) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_NAME: + setName((char *)params); + break; + + case TOKEN_CAPTION: + setCaption((char *)params); + break; + + case TOKEN_BACK: + delete _back; + _back = new UITiledImage(_gameRef); + if (!_back || DID_FAIL(_back->loadFile((char *)params))) { + delete _back; + _back = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BACK_INACTIVE: + delete _backInactive; + _backInactive = new UITiledImage(_gameRef); + if (!_backInactive || DID_FAIL(_backInactive->loadFile((char *)params))) { + delete _backInactive; + _backInactive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE: + delete _image; + _image = new BaseSprite(_gameRef); + if (!_image || DID_FAIL(_image->loadFile((char *)params))) { + delete _image; + _image = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_IMAGE_INACTIVE: + delete _imageInactive, + _imageInactive = new BaseSprite(_gameRef); + if (!_imageInactive || DID_FAIL(_imageInactive->loadFile((char *)params))) { + delete _imageInactive; + _imageInactive = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT: + if (_font) { + _gameRef->_fontStorage->removeFont(_font); + } + _font = _gameRef->_fontStorage->addFont((char *)params); + if (!_font) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_FONT_INACTIVE: + if (_fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + _fontInactive = _gameRef->_fontStorage->addFont((char *)params); + if (!_fontInactive) { + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_TITLE: + setText((char *)params); + _gameRef->_stringTable->expand(&_text); + break; + + case TOKEN_TITLE_ALIGN: + if (scumm_stricmp((char *)params, "left") == 0) { + _titleAlign = TAL_LEFT; + } else if (scumm_stricmp((char *)params, "right") == 0) { + _titleAlign = TAL_RIGHT; + } else { + _titleAlign = TAL_CENTER; + } + break; + + case TOKEN_TITLE_RECT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_titleRect.left, &_titleRect.top, &_titleRect.right, &_titleRect.bottom); + break; + + case TOKEN_DRAG_RECT: + parser.scanStr((char *)params, "%d,%d,%d,%d", &_dragRect.left, &_dragRect.top, &_dragRect.right, &_dragRect.bottom); + break; + + case TOKEN_X: + parser.scanStr((char *)params, "%d", &_posX); + break; + + case TOKEN_Y: + parser.scanStr((char *)params, "%d", &_posY); + break; + + case TOKEN_WIDTH: + parser.scanStr((char *)params, "%d", &_width); + break; + + case TOKEN_HEIGHT: + parser.scanStr((char *)params, "%d", &_height); + break; + + case TOKEN_CURSOR: + delete _cursor; + _cursor = new BaseSprite(_gameRef); + if (!_cursor || DID_FAIL(_cursor->loadFile((char *)params))) { + delete _cursor; + _cursor = NULL; + cmd = PARSERR_GENERIC; + } + break; + + case TOKEN_BUTTON: { + UIButton *btn = new UIButton(_gameRef); + if (!btn || DID_FAIL(btn->loadBuffer(params, false))) { + delete btn; + btn = NULL; + cmd = PARSERR_GENERIC; + } else { + btn->_parent = this; + _widgets.add(btn); + } + } + break; + + case TOKEN_STATIC: { + UIText *text = new UIText(_gameRef); + if (!text || DID_FAIL(text->loadBuffer(params, false))) { + delete text; + text = NULL; + cmd = PARSERR_GENERIC; + } else { + text->_parent = this; + _widgets.add(text); + } + } + break; + + case TOKEN_EDIT: { + UIEdit *edit = new UIEdit(_gameRef); + if (!edit || DID_FAIL(edit->loadBuffer(params, false))) { + delete edit; + edit = NULL; + cmd = PARSERR_GENERIC; + } else { + edit->_parent = this; + _widgets.add(edit); + } + } + break; + + case TOKEN_WINDOW: { + UIWindow *win = new UIWindow(_gameRef); + if (!win || DID_FAIL(win->loadBuffer(params, false))) { + delete win; + win = NULL; + cmd = PARSERR_GENERIC; + } else { + win->_parent = this; + _widgets.add(win); + } + } + break; + + + case TOKEN_TRANSPARENT: + parser.scanStr((char *)params, "%b", &_transparent); + break; + + case TOKEN_SCRIPT: + addScript((char *)params); + break; + + case TOKEN_PARENT_NOTIFY: + parser.scanStr((char *)params, "%b", &_parentNotify); + break; + + case TOKEN_PAUSE_MUSIC: + parser.scanStr((char *)params, "%b", &_pauseMusic); + break; + + case TOKEN_DISABLED: + parser.scanStr((char *)params, "%b", &_disable); + break; + + case TOKEN_VISIBLE: + parser.scanStr((char *)params, "%b", &_visible); + break; + + case TOKEN_MENU: + parser.scanStr((char *)params, "%b", &_isMenu); + break; + + case TOKEN_IN_GAME: + parser.scanStr((char *)params, "%b", &_inGame); + break; + + case TOKEN_CLIP_CONTENTS: + parser.scanStr((char *)params, "%b", &_clipContents); + break; + + case TOKEN_FADE_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &fadeR, &fadeG, &fadeB); + _fadeBackground = true; + break; + + case TOKEN_FADE_ALPHA: + parser.scanStr((char *)params, "%d", &fadeA); + _fadeBackground = true; + break; + + case TOKEN_EDITOR_PROPERTY: + parseEditorProperty(params, false); + break; + + case TOKEN_ALPHA_COLOR: + parser.scanStr((char *)params, "%d,%d,%d", &ar, &ag, &ab); + break; + + case TOKEN_ALPHA: + parser.scanStr((char *)params, "%d", &alpha); + break; + + + default: + if (DID_FAIL(_gameRef->windowLoadHook(this, (char **)&buffer, (char **)params))) { + cmd = PARSERR_GENERIC; + } + } + } + if (cmd == PARSERR_TOKENNOTFOUND) { + _gameRef->LOG(0, "Syntax error in WINDOW definition"); + return STATUS_FAILED; + } + if (cmd == PARSERR_GENERIC) { + _gameRef->LOG(0, "Error loading WINDOW definition"); + return STATUS_FAILED; + } + + correctSize(); + + if (alpha != 0 && ar == 0 && ag == 0 && ab == 0) { + ar = ag = ab = 255; + } + _alphaColor = BYTETORGBA(ar, ag, ab, alpha); + + if (_fadeBackground) { + _fadeColor = BYTETORGBA(fadeR, fadeG, fadeB, fadeA); + } + + _focusedWidget = NULL; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::saveAsText(BaseDynamicBuffer *buffer, int indent) { + buffer->putTextIndent(indent, "WINDOW\n"); + buffer->putTextIndent(indent, "{\n"); + + buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", getName()); + buffer->putTextIndent(indent + 2, "CAPTION=\"%s\"\n", getCaption()); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_back && _back->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK=\"%s\"\n", _back->getFilename()); + } + if (_backInactive && _backInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "BACK_INACTIVE=\"%s\"\n", _backInactive->getFilename()); + } + + if (_image && _image->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE=\"%s\"\n", _image->getFilename()); + } + if (_imageInactive && _imageInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "IMAGE_INACTIVE=\"%s\"\n", _imageInactive->getFilename()); + } + + if (_font && _font->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT=\"%s\"\n", _font->getFilename()); + } + if (_fontInactive && _fontInactive->getFilename()) { + buffer->putTextIndent(indent + 2, "FONT_INACTIVE=\"%s\"\n", _fontInactive->getFilename()); + } + + if (_cursor && _cursor->getFilename()) { + buffer->putTextIndent(indent + 2, "CURSOR=\"%s\"\n", _cursor->getFilename()); + } + + buffer->putTextIndent(indent + 2, "\n"); + + if (_text) { + buffer->putTextIndent(indent + 2, "TITLE=\"%s\"\n", _text); + } + + switch (_titleAlign) { + case TAL_LEFT: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "left"); + break; + case TAL_RIGHT: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "right"); + break; + case TAL_CENTER: + buffer->putTextIndent(indent + 2, "TITLE_ALIGN=\"%s\"\n", "center"); + break; + default: + error("UIWindow::SaveAsText - Unhandled enum-value NUM_TEXT_ALIGN"); + } + + if (!BasePlatform::isRectEmpty(&_titleRect)) { + buffer->putTextIndent(indent + 2, "TITLE_RECT { %d, %d, %d, %d }\n", _titleRect.left, _titleRect.top, _titleRect.right, _titleRect.bottom); + } + + if (!BasePlatform::isRectEmpty(&_dragRect)) { + buffer->putTextIndent(indent + 2, "DRAG_RECT { %d, %d, %d, %d }\n", _dragRect.left, _dragRect.top, _dragRect.right, _dragRect.bottom); + } + + buffer->putTextIndent(indent + 2, "\n"); + + buffer->putTextIndent(indent + 2, "X=%d\n", _posX); + buffer->putTextIndent(indent + 2, "Y=%d\n", _posY); + buffer->putTextIndent(indent + 2, "WIDTH=%d\n", _width); + buffer->putTextIndent(indent + 2, "HEIGHT=%d\n", _height); + + buffer->putTextIndent(indent + 2, "DISABLED=%s\n", _disable ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "VISIBLE=%s\n", _visible ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PARENT_NOTIFY=%s\n", _parentNotify ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "TRANSPARENT=%s\n", _transparent ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "PAUSE_MUSIC=%s\n", _pauseMusic ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "MENU=%s\n", _isMenu ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "IN_GAME=%s\n", _inGame ? "TRUE" : "FALSE"); + buffer->putTextIndent(indent + 2, "CLIP_CONTENTS=%s\n", _clipContents ? "TRUE" : "FALSE"); + + buffer->putTextIndent(indent + 2, "\n"); + + if (_fadeBackground) { + buffer->putTextIndent(indent + 2, "FADE_COLOR { %d, %d, %d }\n", RGBCOLGetR(_fadeColor), RGBCOLGetG(_fadeColor), RGBCOLGetB(_fadeColor)); + buffer->putTextIndent(indent + 2, "FADE_ALPHA=%d\n", RGBCOLGetA(_fadeColor)); + } + + buffer->putTextIndent(indent + 2, "ALPHA_COLOR { %d, %d, %d }\n", RGBCOLGetR(_alphaColor), RGBCOLGetG(_alphaColor), RGBCOLGetB(_alphaColor)); + buffer->putTextIndent(indent + 2, "ALPHA=%d\n", RGBCOLGetA(_alphaColor)); + + buffer->putTextIndent(indent + 2, "\n"); + + // scripts + for (uint32 i = 0; i < _scripts.size(); i++) { + buffer->putTextIndent(indent + 2, "SCRIPT=\"%s\"\n", _scripts[i]->_filename); + } + + buffer->putTextIndent(indent + 2, "\n"); + + // editor properties + BaseClass::saveAsText(buffer, indent + 2); + + // controls + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->saveAsText(buffer, indent + 2); + } + + + buffer->putTextIndent(indent, "}\n"); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::enableWidget(const char *name, bool enable) { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), name) == 0) { + _widgets[i]->_disable = !enable; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::showWidget(const char *name, bool visible) { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), name) == 0) { + _widgets[i]->_visible = visible; + } + } + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +// high level scripting interface +////////////////////////////////////////////////////////////////////////// +bool UIWindow::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { + ////////////////////////////////////////////////////////////////////////// + // GetWidget / GetControl + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "GetWidget") == 0 || strcmp(name, "GetControl") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + if (val->getType() == VAL_INT) { + int widget = val->getInt(); + if (widget < 0 || widget >= (int32)_widgets.size()) { + stack->pushNULL(); + } else { + stack->pushNative(_widgets[widget], true); + } + } else { + for (uint32 i = 0; i < _widgets.size(); i++) { + if (scumm_stricmp(_widgets[i]->getName(), val->getString()) == 0) { + stack->pushNative(_widgets[i], true); + return STATUS_OK; + } + } + stack->pushNULL(); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInactiveFont + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInactiveFont") == 0) { + stack->correctParams(1); + + if (_fontInactive) { + _gameRef->_fontStorage->removeFont(_fontInactive); + } + _fontInactive = _gameRef->_fontStorage->addFont(stack->pop()->getString()); + stack->pushBool(_fontInactive != NULL); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SetInactiveImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SetInactiveImage") == 0) { + stack->correctParams(1); + + delete _imageInactive; + _imageInactive = new BaseSprite(_gameRef); + const char *filename = stack->pop()->getString(); + if (!_imageInactive || DID_FAIL(_imageInactive->loadFile(filename))) { + delete _imageInactive; + _imageInactive = NULL; + stack->pushBool(false); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInactiveImage + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInactiveImage") == 0) { + stack->correctParams(0); + if (!_imageInactive || !_imageInactive->getFilename()) { + stack->pushNULL(); + } else { + stack->pushString(_imageInactive->getFilename()); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GetInactiveImageObject + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GetInactiveImageObject") == 0) { + stack->correctParams(0); + if (!_imageInactive) { + stack->pushNULL(); + } else { + stack->pushNative(_imageInactive, true); + } + + return STATUS_OK; + } + + + ////////////////////////////////////////////////////////////////////////// + // Close + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Close") == 0) { + stack->correctParams(0); + stack->pushBool(DID_SUCCEED(close())); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoExclusive") == 0) { + stack->correctParams(0); + goExclusive(); + script->waitFor(this); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // GoSystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "GoSystemExclusive") == 0) { + stack->correctParams(0); + goSystemExclusive(); + script->waitFor(this); + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Center + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Center") == 0) { + stack->correctParams(0); + _posX = (_gameRef->_renderer->_width - _width) / 2; + _posY = (_gameRef->_renderer->_height - _height) / 2; + stack->pushNULL(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // LoadFromFile + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "LoadFromFile") == 0) { + stack->correctParams(1); + + ScValue *val = stack->pop(); + cleanup(); + if (!val->isNULL()) { + stack->pushBool(DID_SUCCEED(loadFile(val->getString()))); + } else { + stack->pushBool(true); + } + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateButton + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateButton") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIButton *btn = new UIButton(_gameRef); + if (!val->isNULL()) { + btn->setName(val->getString()); + } + stack->pushNative(btn, true); + + btn->_parent = this; + _widgets.add(btn); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateStatic + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateStatic") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIText *sta = new UIText(_gameRef); + if (!val->isNULL()) { + sta->setName(val->getString()); + } + stack->pushNative(sta, true); + + sta->_parent = this; + _widgets.add(sta); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateEditor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateEditor") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIEdit *edi = new UIEdit(_gameRef); + if (!val->isNULL()) { + edi->setName(val->getString()); + } + stack->pushNative(edi, true); + + edi->_parent = this; + _widgets.add(edi); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // CreateWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "CreateWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + + UIWindow *win = new UIWindow(_gameRef); + if (!val->isNULL()) { + win->setName(val->getString()); + } + stack->pushNative(win, true); + + win->_parent = this; + _widgets.add(win); + + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // DeleteControl / DeleteButton / DeleteStatic / DeleteEditor / DeleteWindow + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "DeleteControl") == 0 || strcmp(name, "DeleteButton") == 0 || strcmp(name, "DeleteStatic") == 0 || strcmp(name, "DeleteEditor") == 0 || strcmp(name, "DeleteWindow") == 0) { + stack->correctParams(1); + ScValue *val = stack->pop(); + UIObject *obj = (UIObject *)val->getNative(); + + for (uint32 i = 0; i < _widgets.size(); i++) { + if (_widgets[i] == obj) { + delete _widgets[i]; + _widgets.remove_at(i); + if (val->getType() == VAL_VARIABLE_REF) { + val->setNULL(); + } + } + } + stack->pushNULL(); + return STATUS_OK; + } else if DID_SUCCEED(_gameRef->windowScriptMethodHook(this, script, stack, name)) { + return STATUS_OK; + } + + else { + return UIObject::scCallMethod(script, stack, thisStack, name); + } +} + + +////////////////////////////////////////////////////////////////////////// +ScValue *UIWindow::scGetProperty(const Common::String &name) { + _scValue->setNULL(); + + ////////////////////////////////////////////////////////////////////////// + // Type + ////////////////////////////////////////////////////////////////////////// + if (name == "Type") { + _scValue->setString("window"); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // NumWidgets / NumControls (RO) + ////////////////////////////////////////////////////////////////////////// + else if (name == "NumWidgets" || name == "NumControls") { + _scValue->setInt(_widgets.size()); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Exclusive + ////////////////////////////////////////////////////////////////////////// + else if (name == "Exclusive") { + _scValue->setBool(_mode == WINDOW_EXCLUSIVE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // SystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (name == "SystemExclusive") { + _scValue->setBool(_mode == WINDOW_SYSTEM_EXCLUSIVE); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Menu + ////////////////////////////////////////////////////////////////////////// + else if (name == "Menu") { + _scValue->setBool(_isMenu); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // InGame + ////////////////////////////////////////////////////////////////////////// + else if (name == "InGame") { + _scValue->setBool(_inGame); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic + ////////////////////////////////////////////////////////////////////////// + else if (name == "PauseMusic") { + _scValue->setBool(_pauseMusic); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // ClipContents + ////////////////////////////////////////////////////////////////////////// + else if (name == "ClipContents") { + _scValue->setBool(_clipContents); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // Transparent + ////////////////////////////////////////////////////////////////////////// + else if (name == "Transparent") { + _scValue->setBool(_transparent); + return _scValue; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeColor + ////////////////////////////////////////////////////////////////////////// + else if (name == "FadeColor") { + _scValue->setInt((int)_fadeColor); + return _scValue; + } else { + return UIObject::scGetProperty(name); + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::scSetProperty(const char *name, ScValue *value) { + ////////////////////////////////////////////////////////////////////////// + // Name + ////////////////////////////////////////////////////////////////////////// + if (strcmp(name, "Name") == 0) { + setName(value->getString()); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Menu + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Menu") == 0) { + _isMenu = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // InGame + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "InGame") == 0) { + _inGame = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // PauseMusic + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "PauseMusic") == 0) { + _pauseMusic = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // ClipContents + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "ClipContents") == 0) { + _clipContents = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Transparent + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Transparent") == 0) { + _transparent = value->getBool(); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // FadeColor + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "FadeColor") == 0) { + _fadeColor = (uint32)value->getInt(); + _fadeBackground = (_fadeColor != 0); + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // Exclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "Exclusive") == 0) { + if (value->getBool()) { + goExclusive(); + } else { + close(); + _visible = true; + } + return STATUS_OK; + } + + ////////////////////////////////////////////////////////////////////////// + // SystemExclusive + ////////////////////////////////////////////////////////////////////////// + else if (strcmp(name, "SystemExclusive") == 0) { + if (value->getBool()) { + goSystemExclusive(); + } else { + close(); + _visible = true; + } + return STATUS_OK; + } else { + return UIObject::scSetProperty(name, value); + } +} + + +////////////////////////////////////////////////////////////////////////// +const char *UIWindow::scToString() { + return "[window]"; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleKeypress(Common::Event *event, bool printable) { +//TODO + if (event->type == Common::EVENT_KEYDOWN && event->kbd.keycode == Common::KEYCODE_TAB) { + return DID_SUCCEED(moveFocus(!BaseKeyboardState::isShiftDown())); + } else { + if (_focusedWidget) { + return _focusedWidget->handleKeypress(event, printable); + } else { + return false; + } + } + return false; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleMouseWheel(int Delta) { + if (_focusedWidget) { + return _focusedWidget->handleMouseWheel(Delta); + } else { + return false; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::handleMouse(TMouseEvent event, TMouseButton button) { + bool res = UIObject::handleMouse(event, button); + + // handle window dragging + if (!BasePlatform::isRectEmpty(&_dragRect)) { + // start drag + if (event == MOUSE_CLICK && button == MOUSE_BUTTON_LEFT) { + Rect32 dragRect = _dragRect; + int offsetX, offsetY; + getTotalOffset(&offsetX, &offsetY); + dragRect.offsetRect(_posX + offsetX, _posY + offsetY); + + if (BasePlatform::ptInRect(&dragRect, _gameRef->_mousePos)) { + _dragFrom.x = _gameRef->_mousePos.x; + _dragFrom.y = _gameRef->_mousePos.y; + _dragging = true; + } + } + // end drag + else if (_dragging && event == MOUSE_RELEASE && button == MOUSE_BUTTON_LEFT) { + _dragging = false; + } + } + + return res; +} + + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::persist(BasePersistenceManager *persistMgr) { + + UIObject::persist(persistMgr); + + persistMgr->transfer(TMEMBER(_backInactive)); + persistMgr->transfer(TMEMBER(_clipContents)); + persistMgr->transfer(TMEMBER(_dragFrom)); + persistMgr->transfer(TMEMBER(_dragging)); + persistMgr->transfer(TMEMBER(_dragRect)); + persistMgr->transfer(TMEMBER(_fadeBackground)); + persistMgr->transfer(TMEMBER(_fadeColor)); + persistMgr->transfer(TMEMBER(_fontInactive)); + persistMgr->transfer(TMEMBER(_imageInactive)); + persistMgr->transfer(TMEMBER(_inGame)); + persistMgr->transfer(TMEMBER(_isMenu)); + persistMgr->transfer(TMEMBER_INT(_mode)); + persistMgr->transfer(TMEMBER(_shieldButton)); + persistMgr->transfer(TMEMBER(_shieldWindow)); + persistMgr->transfer(TMEMBER_INT(_titleAlign)); + persistMgr->transfer(TMEMBER(_titleRect)); + persistMgr->transfer(TMEMBER(_transparent)); + persistMgr->transfer(TMEMBER(_viewport)); + persistMgr->transfer(TMEMBER(_pauseMusic)); + + _widgets.persist(persistMgr); + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::moveFocus(bool forward) { + int i; + bool found = false; + for (i = 0; i < (int32)_widgets.size(); i++) { + if (_widgets[i] == _focusedWidget) { + found = true; + break; + } + } + if (!found) { + _focusedWidget = NULL; + } + + if (!_focusedWidget) { + if (_widgets.size() > 0) { + i = 0; + } else { + return STATUS_OK; + } + } + + int numTries = 0; + bool done = false; + + while (numTries <= (int32)_widgets.size()) { + if (_widgets[i] != _focusedWidget && _widgets[i]->_canFocus && _widgets[i]->_visible && !_widgets[i]->_disable) { + _focusedWidget = _widgets[i]; + done = true; + break; + } + + if (forward) { + i++; + if (i >= (int32)_widgets.size()) { + i = 0; + } + } else { + i--; + if (i < 0) { + i = _widgets.size() - 1; + } + } + numTries++; + } + + return done ? STATUS_OK : STATUS_FAILED; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::goExclusive() { + if (_mode == WINDOW_EXCLUSIVE) { + return STATUS_OK; + } + + if (_mode == WINDOW_NORMAL) { + _ready = false; + _mode = WINDOW_EXCLUSIVE; + _visible = true; + _disable = false; + _gameRef->focusWindow(this); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::goSystemExclusive() { + if (_mode == WINDOW_SYSTEM_EXCLUSIVE) { + return STATUS_OK; + } + + makeFreezable(false); + + _mode = WINDOW_SYSTEM_EXCLUSIVE; + _ready = false; + _visible = true; + _disable = false; + _gameRef->focusWindow(this); + + _gameRef->freeze(_pauseMusic); + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::close() { + if (_mode == WINDOW_SYSTEM_EXCLUSIVE) { + _gameRef->unfreeze(); + } + + _mode = WINDOW_NORMAL; + _visible = false; + _ready = true; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::listen(BaseScriptHolder *param1, uint32 param2) { + UIObject *obj = (UIObject *)param1; + + switch (obj->_type) { + case UI_BUTTON: + if (scumm_stricmp(obj->getName(), "close") == 0) { + close(); + } else { + return BaseObject::listen(param1, param2); + } + break; + default: + return BaseObject::listen(param1, param2); + } + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +void UIWindow::makeFreezable(bool freezable) { + for (uint32 i = 0; i < _widgets.size(); i++) { + _widgets[i]->makeFreezable(freezable); + } + + BaseObject::makeFreezable(freezable); +} + + +////////////////////////////////////////////////////////////////////////// +bool UIWindow::getWindowObjects(BaseArray<UIObject *> &objects, bool interactiveOnly) { + for (uint32 i = 0; i < _widgets.size(); i++) { + UIObject *control = _widgets[i]; + if (control->_disable && interactiveOnly) { + continue; + } + + switch (control->_type) { + case UI_WINDOW: + ((UIWindow *)control)->getWindowObjects(objects, interactiveOnly); + break; + + case UI_BUTTON: + case UI_EDIT: + objects.add(control); + break; + + default: + if (!interactiveOnly) { + objects.add(control); + } + } + } + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/ui/ui_window.h b/engines/wintermute/ui/ui_window.h new file mode 100644 index 0000000000..ae035c65c7 --- /dev/null +++ b/engines/wintermute/ui/ui_window.h @@ -0,0 +1,94 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UIWINDOW_H +#define WINTERMUTE_UIWINDOW_H + + +#include "engines/wintermute/ui/ui_object.h" +#include "common/events.h" + +namespace Wintermute { + +class UIButton; +class BaseViewport; +class UIWindow : public UIObject { + uint32 _fadeColor; +public: + bool getWindowObjects(BaseArray<UIObject *> &Objects, bool InteractiveOnly); + + bool _pauseMusic; + void cleanup(); + virtual void makeFreezable(bool freezable); + BaseViewport *_viewport; + bool _clipContents; + bool _inGame; + bool _isMenu; + bool _fadeBackground; + + virtual bool handleMouseWheel(int delta); + UIWindow *_shieldWindow; + UIButton *_shieldButton; + bool close(); + bool goSystemExclusive(); + bool goExclusive(); + TWindowMode _mode; + bool moveFocus(bool forward = true); + virtual bool handleMouse(TMouseEvent Event, TMouseButton Button); + Point32 _dragFrom; + bool _dragging; + DECLARE_PERSISTENT(UIWindow, UIObject) + bool _transparent; + bool showWidget(const char *name, bool visible = true); + bool enableWidget(const char *name, bool enable = true); + Rect32 _titleRect; + Rect32 _dragRect; + virtual bool display(int offsetX = 0, int offsetY = 0); + UIWindow(BaseGame *inGame); + virtual ~UIWindow(); + virtual bool handleKeypress(Common::Event *event, bool printable = false); + BaseArray<UIObject *> _widgets; + TTextAlign _titleAlign; + bool loadFile(const char *filename); + bool loadBuffer(byte *buffer, bool complete = true); + UITiledImage *_backInactive; + BaseFont *_fontInactive; + BaseSprite *_imageInactive; + virtual bool listen(BaseScriptHolder *param1, uint32 param2); + virtual bool saveAsText(BaseDynamicBuffer *buffer, int indent); + + // scripting interface + virtual ScValue *scGetProperty(const Common::String &name); + virtual bool scSetProperty(const char *name, ScValue *value); + virtual bool scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name); + virtual const char *scToString(); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/convert_utf.cpp b/engines/wintermute/utils/convert_utf.cpp new file mode 100644 index 0000000000..7ebc011d01 --- /dev/null +++ b/engines/wintermute/utils/convert_utf.cpp @@ -0,0 +1,615 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Source code file. + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Sept 2001: fixed const & error conditions per + mods suggested by S. Parent & A. Lillich. + June 2002: Tim Dodd added detection and handling of incomplete + source sequences, enhanced error detection, added casts + to eliminate compiler warnings. + July 2003: slight mods to back out aggressive FFFE detection. + Jan 2004: updated switches in from-UTF8 conversions. + Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + + See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "engines/wintermute/utils/convert_utf.h" +#ifdef CVTUTF_DEBUG +#include "common/textconsole.h" +#endif + +namespace Wintermute { + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; + break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF32 *target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; + break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG + if (result == sourceIllegal) { + warning("ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + } +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL + }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. + * Constants have been gathered. Loops & conditionals have been removed as + * much as possible for efficiency, in favor of drop-through switches. + * (See "Note A" at the bottom of the file for equivalent code.) + * If your compiler supports it, the "isLegalUTF8" call can be turned + * into an inline function. + */ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source + length; + switch (length) { + default: + return false; + /* Everything else falls through when "true"... */ + case 4: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: + if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: + if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: + if (a < 0xA0) return false; + break; + case 0xED: + if (a > 0x9F) return false; + break; + case 0xF0: + if (a < 0x90) return false; + break; + case 0xF4: + if (a > 0x8F) return false; + break; + default: + if (a < 0x80) return false; + } + + case 1: + if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source] + 1; + if (source + length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF16 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 4: + ch += *source++; + ch <<= 6; /* remember, illegal UTF-8 */ + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead + 1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up source pointer! */ + result = targetExhausted; + break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32 *source = *sourceStart; + UTF8 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { + bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { + bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { + bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { + bytesToWrite = 4; + } else { + bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; + result = targetExhausted; + break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 3: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 2: + *--target = (UTF8)((ch | byteMark) & byteMask); + ch >>= 6; + case 1: + *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8 *source = *sourceStart; + UTF32 *target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; + break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead + 1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: + ch += *source++; + ch <<= 6; + case 4: + ch += *source++; + ch <<= 6; + case 3: + ch += *source++; + ch <<= 6; + case 2: + ch += *source++; + ch <<= 6; + case 1: + ch += *source++; + ch <<= 6; + case 0: + ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead + 1); /* Back up the source pointer! */ + result = targetExhausted; + break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead + 1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + + Note A. + The fall-through switches in UTF-8 reading code save a + temp variable, some decrements & conditionals. The switches + are equivalent to the following loop: + { + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); + } + In UTF-8 writing code, the switches on "bytesToWrite" are + similarly unrolled loops. + + --------------------------------------------------------------------- */ + +} // End of namespace Wintermute diff --git a/engines/wintermute/utils/convert_utf.h b/engines/wintermute/utils/convert_utf.h new file mode 100644 index 0000000000..a5f34456f5 --- /dev/null +++ b/engines/wintermute/utils/convert_utf.h @@ -0,0 +1,148 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +// NOTE: Modifications have been made to the code for inclusion +// into ScummVM. + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, + or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ +#include "common/system.h" + +namespace Wintermute { + +typedef uint32 UTF32; /* at least 32 bits */ +typedef uint16 UTF16; /* at least 16 bits */ +typedef uint8 UTF8; /* typically 8 bits */ +typedef uint8 Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +ConversionResult ConvertUTF8toUTF16( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32( + const UTF8 **sourceStart, const UTF8 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32( + const UTF16 **sourceStart, const UTF16 *sourceEnd, + UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16( + const UTF32 **sourceStart, const UTF32 *sourceEnd, + UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +} // End of namespace Wintermute + +/* --------------------------------------------------------------------- */ diff --git a/engines/wintermute/utils/crc.cpp b/engines/wintermute/utils/crc.cpp new file mode 100644 index 0000000000..e7ec45511b --- /dev/null +++ b/engines/wintermute/utils/crc.cpp @@ -0,0 +1,237 @@ +/********************************************************************** + * + * Filename: crc.c + * + * Description: Slow and fast implementations of the CRC standards. + * + * Notes: The parameters for each supported CRC standard are + * defined in the header file crc.h. The implementations + * here should stand up to further additions to that list. + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#include "engines/wintermute/utils/crc.h" + +namespace Wintermute { + +/* + * Derive parameters from the standard-specific parameters in crc.h. + */ +#define WIDTH (8 * sizeof(crc)) +#define TOPBIT (1 << (WIDTH - 1)) + +#if (REFLECT_DATA == TRUE) +#undef REFLECT_DATA +#define REFLECT_DATA(X) ((unsigned char) reflect((X), 8)) +#else +#undef REFLECT_DATA +#define REFLECT_DATA(X) (X) +#endif + +#if (REFLECT_REMAINDER == TRUE) +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) ((crc) reflect((X), WIDTH)) +#else +#undef REFLECT_REMAINDER +#define REFLECT_REMAINDER(X) (X) +#endif + + +/********************************************************************* + * + * Function: reflect() + * + * Description: Reorder the bits of a binary sequence, by reflecting + * them about the middle position. + * + * Notes: No checking is done that nBits <= 32. + * + * Returns: The reflection of the original data. + * + *********************************************************************/ +static unsigned long +reflect(unsigned long data, unsigned char nBits) { + unsigned long reflection = 0x00000000; + unsigned char bit; + + /* + * Reflect the data about the center bit. + */ + for (bit = 0; bit < nBits; ++bit) { + /* + * If the LSB bit is set, set the reflection of it. + */ + if (data & 0x01) { + reflection |= (1 << ((nBits - 1) - bit)); + } + + data = (data >> 1); + } + + return (reflection); + +} /* reflect() */ + + +/********************************************************************* + * + * Function: crcSlow() + * + * Description: Compute the CRC of a given message. + * + * Notes: + * + * Returns: The CRC of the message. + * + *********************************************************************/ +crc +crcSlow(unsigned char const message[], int nBytes) { + crc remainder = INITIAL_REMAINDER; + int byte; + unsigned char bit; + + + /* + * Perform modulo-2 division, a byte at a time. + */ + for (byte = 0; byte < nBytes; ++byte) { + /* + * Bring the next byte into the remainder. + */ + remainder ^= (REFLECT_DATA(message[byte]) << (WIDTH - 8)); + + /* + * Perform modulo-2 division, a bit at a time. + */ + for (bit = 8; bit > 0; --bit) { + /* + * Try to divide the current data bit. + */ + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ POLYNOMIAL; + } else { + remainder = (remainder << 1); + } + } + } + + /* + * The final remainder is the CRC result. + */ + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); + +} /* crcSlow() */ + + +crc crcTable[256]; + + +/********************************************************************* + * + * Function: crcInit() + * + * Description: Populate the partial CRC lookup table. + * + * Notes: This function must be rerun any time the CRC standard + * is changed. If desired, it can be run "offline" and + * the table results stored in an embedded system's ROM. + * + * Returns: None defined. + * + *********************************************************************/ +void +crcInit(void) { + crc remainder; + int dividend; + unsigned char bit; + + + /* + * Compute the remainder of each possible dividend. + */ + for (dividend = 0; dividend < 256; ++dividend) { + /* + * Start with the dividend followed by zeros. + */ + remainder = dividend << (WIDTH - 8); + + /* + * Perform modulo-2 division, a bit at a time. + */ + for (bit = 8; bit > 0; --bit) { + /* + * Try to divide the current data bit. + */ + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ POLYNOMIAL; + } else { + remainder = (remainder << 1); + } + } + + /* + * Store the result into the table. + */ + crcTable[dividend] = remainder; + } + +} /* crcInit() */ + + +/********************************************************************* + * + * Function: crcFast() + * + * Description: Compute the CRC of a given message. + * + * Notes: crcInit() must be called first. + * + * Returns: The CRC of the message. + * + *********************************************************************/ +crc +crcFast(unsigned char const message[], int nBytes) { + crc remainder = INITIAL_REMAINDER; + unsigned char data; + int byte; + + + /* + * Divide the message by the polynomial, a byte at a time. + */ + for (byte = 0; byte < nBytes; ++byte) { + data = (unsigned char)(REFLECT_DATA(message[byte]) ^ (remainder >> (WIDTH - 8))); + remainder = crcTable[data] ^ (remainder << 8); + } + + /* + * The final remainder is the CRC. + */ + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); + +} /* crcFast() */ + + + +crc crc_initialize(void) { + crcInit(); + return INITIAL_REMAINDER; +} + +crc crc_process_byte(unsigned char byteVal, crc remainder) { + unsigned char data; + data = (unsigned char)(REFLECT_DATA(byteVal) ^ (remainder >> (WIDTH - 8))); + remainder = crcTable[data] ^ (remainder << 8); + return remainder; +} + +crc crc_finalize(crc remainder) { + return (REFLECT_REMAINDER(remainder) ^ FINAL_XOR_VALUE); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/crc.h b/engines/wintermute/utils/crc.h new file mode 100644 index 0000000000..77c2ea267f --- /dev/null +++ b/engines/wintermute/utils/crc.h @@ -0,0 +1,85 @@ +/********************************************************************** + * + * Filename: crc.h + * + * Description: A header file describing the various CRC standards. + * + * Notes: + * + * + * Copyright (c) 2000 by Michael Barr. This software is placed into + * the public domain and may be used for any purpose. However, this + * notice must not be changed or removed and no warranty is either + * expressed or implied by its publication or distribution. + **********************************************************************/ + +#ifndef _crc_h +#define _crc_h + +#include "common/system.h" // For types. + +namespace Wintermute { + +#ifndef TRUE +#define FALSE 0 +#define TRUE !FALSE +#endif + +/* + * Select the CRC standard from the list that follows. + */ +#define CRC32 + +#if defined(CRC_CCITT) + +typedef uint16 crc; + +#define CRC_NAME "CRC-CCITT" +#define POLYNOMIAL 0x1021 +#define INITIAL_REMAINDER 0xFFFF +#define FINAL_XOR_VALUE 0x0000 +#define REFLECT_DATA FALSE +#define REFLECT_REMAINDER FALSE +#define CHECK_VALUE 0x29B1 + +#elif defined(CRC16) + +typedef uint16 crc; + +#define CRC_NAME "CRC-16" +#define POLYNOMIAL 0x8005 +#define INITIAL_REMAINDER 0x0000 +#define FINAL_XOR_VALUE 0x0000 +#define REFLECT_DATA TRUE +#define REFLECT_REMAINDER TRUE +#define CHECK_VALUE 0xBB3D + +#elif defined(CRC32) + +typedef uint32 crc; + +#define CRC_NAME "CRC-32" +#define POLYNOMIAL 0x04C11DB7 +#define INITIAL_REMAINDER 0xFFFFFFFF +#define FINAL_XOR_VALUE 0xFFFFFFFF +#define REFLECT_DATA TRUE +#define REFLECT_REMAINDER TRUE +#define CHECK_VALUE 0xCBF43926 + +#else + +#error "One of CRC_CCITT, CRC16, or CRC32 must be #define'd." + +#endif + +void crcInit(void); +crc crcSlow(unsigned char const message[], int nBytes); +crc crcFast(unsigned char const message[], int nBytes); + +extern "C" crc crc_initialize(void); +extern "C" crc crc_process_byte(unsigned char byteVal, crc remainder); +extern "C" crc crc_finalize(crc remainder); + +} // End of namespace Wintermute + +#endif /* _crc_h */ diff --git a/engines/wintermute/utils/path_util.cpp b/engines/wintermute/utils/path_util.cpp new file mode 100644 index 0000000000..298f0c268f --- /dev/null +++ b/engines/wintermute/utils/path_util.cpp @@ -0,0 +1,101 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "common/file.h" +#include "engines/wintermute/utils/path_util.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::unifySeparators(const AnsiString &path) { + AnsiString newPath = path; + + for (uint32 i = 0; i < newPath.size(); i++) { + if (newPath[i] == '\\') { + newPath.setChar('/', i); + } + } + + return newPath; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::normalizeFileName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + newPath.toLowercase(); + return newPath; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::combine(const AnsiString &path1, const AnsiString &path2) { + AnsiString newPath1 = unifySeparators(path1); + AnsiString newPath2 = unifySeparators(path2); + + if (!newPath1.hasSuffix("/") && !newPath2.hasPrefix("/")) { + newPath1 += "/"; + } + + return newPath1 + newPath2; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getDirectoryName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + Common::String filename = getFileName(path); + return Common::String(path.c_str(), path.size() - filename.size()); +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getFileName(const AnsiString &path) { + AnsiString newPath = unifySeparators(path); + Common::String lastPart = Common::lastPathComponent(newPath, '/'); + if (lastPart[lastPart.size() - 1 ] != '/') { + return lastPart; + } else { + return path; + } +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getFileNameWithoutExtension(const AnsiString &path) { + AnsiString fileName = getFileName(path); + // TODO: Prettify this. + AnsiString extension = Common::lastPathComponent(fileName, '.'); + for (uint32 i = 0; i < extension.size() + 1; i++) { + fileName.deleteLastChar(); + } + return fileName; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString PathUtil::getExtension(const AnsiString &path) { + AnsiString fileName = getFileName(path); + return Common::lastPathComponent(path, '.'); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/path_util.h b/engines/wintermute/utils/path_util.h new file mode 100644 index 0000000000..7358c2aba0 --- /dev/null +++ b/engines/wintermute/utils/path_util.h @@ -0,0 +1,49 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_PATHUTILS_H +#define WINTERMUTE_PATHUTILS_H + +#include "engines/wintermute/dctypes.h" + +namespace Wintermute { + +class PathUtil { +public: + static AnsiString unifySeparators(const AnsiString &path); + static AnsiString normalizeFileName(const AnsiString &path); + static AnsiString combine(const AnsiString &path1, const AnsiString &path2); + static AnsiString getDirectoryName(const AnsiString &path); + static AnsiString getFileName(const AnsiString &path); + static AnsiString getFileNameWithoutExtension(const AnsiString &path); + static AnsiString getExtension(const AnsiString &path); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/string_util.cpp b/engines/wintermute/utils/string_util.cpp new file mode 100644 index 0000000000..2c3be8c2f5 --- /dev/null +++ b/engines/wintermute/utils/string_util.cpp @@ -0,0 +1,232 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "common/tokenizer.h" +#include "engines/wintermute/utils/string_util.h" +#include "engines/wintermute/utils/convert_utf.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////////// +bool StringUtil::compareNoCase(const AnsiString &str1, const AnsiString &str2) { + return (str1.compareToIgnoreCase(str2) == 0); +} + +////////////////////////////////////////////////////////////////////////// +/*bool StringUtil::CompareNoCase(const WideString &str1, const WideString &str2) { + WideString str1lc = str1; + WideString str2lc = str2; + + ToLowerCase(str1lc); + ToLowerCase(str2lc); + + return (str1lc == str2lc); +}*/ + +////////////////////////////////////////////////////////////////////////// +WideString StringUtil::utf8ToWide(const Utf8String &Utf8Str) { + error("StringUtil::Utf8ToWide - WideString not supported yet"); + /* size_t WideSize = Utf8Str.size(); + + if (sizeof(wchar_t) == 2) { + wchar_t *WideStringNative = new wchar_t[WideSize + 1]; + + const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str()); + const UTF8 *SourceEnd = SourceStart + WideSize; + + UTF16 *TargetStart = reinterpret_cast<UTF16 *>(WideStringNative); + UTF16 *TargetEnd = TargetStart + WideSize + 1; + + ConversionResult res = ConvertUTF8toUTF16(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] WideStringNative; + return L""; + } + *TargetStart = 0; + WideString ResultString(WideStringNative); + delete[] WideStringNative; + + return ResultString; + } else if (sizeof(wchar_t) == 4) { + wchar_t *WideStringNative = new wchar_t[WideSize + 1]; + + const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str()); + const UTF8 *SourceEnd = SourceStart + WideSize; + + UTF32 *TargetStart = reinterpret_cast<UTF32 *>(WideStringNative); + UTF32 *TargetEnd = TargetStart + WideSize; + + ConversionResult res = ConvertUTF8toUTF32(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] WideStringNative; + return L""; + } + *TargetStart = 0; + WideString ResultString(WideStringNative); + delete[] WideStringNative; + + return ResultString; + } else { + return L""; + }*/ + return ""; +} + +////////////////////////////////////////////////////////////////////////// +Utf8String StringUtil::wideToUtf8(const WideString &WideStr) { + error("StringUtil::wideToUtf8 - Widestring not supported yet"); + /* size_t WideSize = WideStr.length(); + + if (sizeof(wchar_t) == 2) { + size_t utf8Size = 3 * WideSize + 1; + char *utf8StringNative = new char[Utf8Size]; + + const UTF16 *SourceStart = reinterpret_cast<const UTF16 *>(WideStr.c_str()); + const UTF16 *SourceEnd = SourceStart + WideSize; + + UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative); + UTF8 *TargetEnd = TargetStart + Utf8Size; + + ConversionResult res = ConvertUTF16toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] Utf8StringNative; + return (Utf8String)""; + } + *TargetStart = 0; + Utf8String ResultString(Utf8StringNative); + delete[] Utf8StringNative; + return ResultString; + } else if (sizeof(wchar_t) == 4) { + size_t utf8Size = 4 * WideSize + 1; + char *utf8StringNative = new char[Utf8Size]; + + const UTF32 *SourceStart = reinterpret_cast<const UTF32 *>(WideStr.c_str()); + const UTF32 *SourceEnd = SourceStart + WideSize; + + UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative); + UTF8 *TargetEnd = TargetStart + Utf8Size; + + ConversionResult res = ConvertUTF32toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion); + if (res != conversionOK) { + delete[] Utf8StringNative; + return (Utf8String)""; + } + *TargetStart = 0; + Utf8String ResultString(Utf8StringNative); + delete[] Utf8StringNative; + return ResultString; + } else { + return (Utf8String)""; + }*/ + return ""; +} + +// Currently this only does Ansi->ISO 8859, and only for carets. +char simpleAnsiToWide(const AnsiString &str, uint32 &offset) { + char c = str[offset]; + + if (c == 92) { + offset++; + return '\''; + } else { + offset++; + return c; + } +} + +////////////////////////////////////////////////////////////////////////// +WideString StringUtil::ansiToWide(const AnsiString &str) { + // TODO: This function gets called a lot, so warnings like these drown out the usefull information + /*Common::String converted = ""; + uint32 index = 0; + while (index != str.size()) { + converted += simpleAnsiToWide(str, index); + }*/ + // using default os locale! + + /* setlocale(LC_CTYPE, ""); + size_t wideSize = mbstowcs(NULL, str.c_str(), 0) + 1; + wchar_t *wstr = new wchar_t[WideSize]; + mbstowcs(wstr, str.c_str(), WideSize); + WideString ResultString(wstr); + delete[] wstr; + return ResultString;*/ + return WideString(str); +} + +////////////////////////////////////////////////////////////////////////// +AnsiString StringUtil::wideToAnsi(const WideString &wstr) { + // using default os locale! + // TODO: This function gets called a lot, so warnings like these drown out the usefull information + /* setlocale(LC_CTYPE, ""); + size_t wideSize = wcstombs(NULL, wstr.c_str(), 0) + 1; + char *str = new char[WideSize]; + wcstombs(str, wstr.c_str(), WideSize); + AnsiString ResultString(str); + delete[] str; + return ResultString;*/ + return AnsiString(wstr); +} + +////////////////////////////////////////////////////////////////////////// +bool StringUtil::isUtf8BOM(const byte *buffer, uint32 bufferSize) { + if (bufferSize > 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) { + return true; + } else { + return false; + } +} + +////////////////////////////////////////////////////////////////////////// +int StringUtil::indexOf(const WideString &str, const WideString &toFind, size_t startFrom) { + const char *index = strstr(str.c_str(), toFind.c_str()); + if (index == NULL) { + return -1; + } else { + return index - str.c_str(); + } +} + +Common::String StringUtil::encodeSetting(const Common::String &str) { + if (str.contains('=')) { + error("Setting contains '='"); + } + return str; +} + +Common::String StringUtil::decodeSetting(const Common::String &str) { + return str; +} + +////////////////////////////////////////////////////////////////////////// +AnsiString StringUtil::toString(int val) { + return Common::String::format("%d", val); +} + + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/string_util.h b/engines/wintermute/utils/string_util.h new file mode 100644 index 0000000000..e419e2bca8 --- /dev/null +++ b/engines/wintermute/utils/string_util.h @@ -0,0 +1,56 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_STRINGUTIL_H +#define WINTERMUTE_STRINGUTIL_H + +#include "engines/wintermute/dctypes.h" + +namespace Wintermute { + +class StringUtil { +public: + static bool compareNoCase(const AnsiString &str1, const AnsiString &str2); + //static bool compareNoCase(const WideString &str1, const WideString &str2); + static WideString utf8ToWide(const Utf8String &Utf8Str); + static Utf8String wideToUtf8(const WideString &WideStr); + static WideString ansiToWide(const AnsiString &str); + static AnsiString wideToAnsi(const WideString &str); + + static bool isUtf8BOM(const byte *buffer, uint32 bufferSize); + static int indexOf(const WideString &str, const WideString &toFind, size_t startFrom); + + static Common::String encodeSetting(const Common::String &str); + static Common::String decodeSetting(const Common::String &str); + + static AnsiString toString(int val); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/utils/utils.cpp b/engines/wintermute/utils/utils.cpp new file mode 100644 index 0000000000..824b16ccdb --- /dev/null +++ b/engines/wintermute/utils/utils.cpp @@ -0,0 +1,261 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#include "engines/wintermute/utils/utils.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/base/base_engine.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +static inline unsigned Sqr(int x) { + return (x * x); +} + +////////////////////////////////////////////////////////////////////////////////// +// Swap - swaps two integers +////////////////////////////////////////////////////////////////////////////////// +void BaseUtils::swap(int *a, int *b) { + int temp = *a; + *a = *b; + *b = temp; +} + + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::normalizeAngle(float angle) { + while (angle > 360) { + angle -= 360; + } + while (angle < 0) { + angle += 360; + } + + return angle; +} + + +//////////////////////////////////////////////////////////////////////////////// +void BaseUtils::createPath(const char *path, bool pathOnly) { + /* AnsiString pathStr; + + if (!pathOnly) pathStr = PathUtil::getDirectoryName(path); + else pathStr = path; + */ +// try { + warning("BaseUtils::CreatePath - not implemented: %s", path); +// boost::filesystem::create_directories(path); +// } catch (...) { + return; +// } +} + + +////////////////////////////////////////////////////////////////////////// +void BaseUtils::debugMessage(const char *text) { + //MessageBox(hWnd, Text, "WME", MB_OK|MB_ICONINFORMATION); +} + + +////////////////////////////////////////////////////////////////////////// +char *BaseUtils::setString(char **string, const char *value) { + delete[] *string; + *string = new char[strlen(value) + 1]; + if (*string) { + strcpy(*string, value); + } + return *string; +} + +////////////////////////////////////////////////////////////////////////// +char *BaseUtils::strEntry(int entry, const char *str, const char delim) { + int numEntries = 0; + + const char *start = NULL; + int len = 0; + + for (uint32 i = 0; i <= strlen(str); i++) { + if (numEntries == entry) { + if (!start) { + start = str + i; + } else { + len++; + } + } + if (str[i] == delim || str[i] == '\0') { + numEntries++; + if (start) { + char *ret = new char[len + 1]; + memset(ret, 0, len + 1); + Common::strlcpy(ret, start, len + 1); + return ret; + } + } + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +int BaseUtils::randomInt(int from, int to) { + if (to < from) { + int i = to; + to = from; + from = i; + } + return BaseEngine::instance().randInt(from, to); +// return (rand() % (to - from + 1)) + from; +} + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::randomFloat(float from, float to) { + const uint32 randMax = RAND_MAX; + float randNum = (float)BaseEngine::instance().randInt(0, randMax) / (float)randMax; + return from + (to - from) * randNum; +} + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::randomAngle(float from, float to) { + while (to < from) { + to += 360; + } + return normalizeAngle(randomFloat(from, to)); +} + +////////////////////////////////////////////////////////////////////////// +void BaseUtils::RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL) { + float varR = (RGBCOLGetR(rgbColor) / 255.0f); + float varG = (RGBCOLGetG(rgbColor) / 255.0f); + float varB = (RGBCOLGetB(rgbColor) / 255.0f); + + //Min. value of RGB + float varMin = MIN(varR, varG); + varMin = MIN(varMin, varB); + + //Max. value of RGB + float varMax = MAX(varR, varG); + varMax = MAX(varMax, varB); + + //Delta RGB value + float delMax = varMax - varMin; + + float H = 0.0f, S = 0.0f, L = 0.0f; + + L = (varMax + varMin) / 2.0f; + + //This is a gray, no chroma... + if (delMax == 0) { + H = 0; + S = 0; + } + //Chromatic data... + else { + if (L < 0.5f) { + S = delMax / (varMax + varMin); + } else { + S = delMax / (2.0f - varMax - varMin); + } + + float delR = (((varMax - varR) / 6.0f) + (delMax / 2.0f)) / delMax; + float delG = (((varMax - varG) / 6.0f) + (delMax / 2.0f)) / delMax; + float delB = (((varMax - varB) / 6.0f) + (delMax / 2.0f)) / delMax; + + if (varR == varMax) { + H = delB - delG; + } else if (varG == varMax) { + H = (1.0f / 3.0f) + delR - delB; + } else if (varB == varMax) { + H = (2.0f / 3.0f) + delG - delR; + } + + if (H < 0) { + H += 1; + } + if (H > 1) { + H -= 1; + } + } + + *outH = (byte)(H * 255); + *outS = (byte)(S * 255); + *outL = (byte)(L * 255); +} + + +////////////////////////////////////////////////////////////////////////// +uint32 BaseUtils::HSLtoRGB(byte InH, byte InS, byte InL) { + float H = InH / 255.0f; + float S = InS / 255.0f; + float L = InL / 255.0f; + + byte R, G, B; + + + if (S == 0) { + R = (byte)(L * 255); + G = (byte)(L * 255); + B = (byte)(L * 255); + } else { + float var1, var2; + + if (L < 0.5) { + var2 = L * (1.0 + S); + } else { + var2 = (L + S) - (S * L); + } + + var1 = 2.0f * L - var2; + + R = (byte)(255 * Hue2RGB(var1, var2, H + (1.0f / 3.0f))); + G = (byte)(255 * Hue2RGB(var1, var2, H)); + B = (byte)(255 * Hue2RGB(var1, var2, H - (1.0f / 3.0f))); + } + return BYTETORGBA(255, R, G, B); +} + + +////////////////////////////////////////////////////////////////////////// +float BaseUtils::Hue2RGB(float v1, float v2, float vH) { + if (vH < 0.0f) { + vH += 1.0f; + } + if (vH > 1.0f) { + vH -= 1.0f; + } + if ((6.0f * vH) < 1.0f) { + return (v1 + (v2 - v1) * 6.0f * vH); + } + if ((2.0f * vH) < 1.0f) { + return (v2); + } + if ((3.0f * vH) < 2.0f) { + return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); + } + return (v1); +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/utils/utils.h b/engines/wintermute/utils/utils.h new file mode 100644 index 0000000000..d6a603ec72 --- /dev/null +++ b/engines/wintermute/utils/utils.h @@ -0,0 +1,64 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_UTILS_H +#define WINTERMUTE_UTILS_H + +#include "engines/wintermute/wintypes.h" +#include "engines/wintermute/math/rect32.h" + +namespace Wintermute { + +class BaseGame; + +class BaseUtils { +public: + static void swap(int *a, int *b); + static float normalizeAngle(float angle); + + static void createPath(const char *path, bool pathOnly = false); + + static void debugMessage(const char *text); + static char *setString(char **string, const char *value); + + static char *strEntry(int entry, const char *str, const char delim = ','); + + static int randomInt(int from, int to); + static float randomFloat(float from, float to); + static float randomAngle(float from, float to); + + static void RGBtoHSL(uint32 rgbColor, byte *outH, byte *outS, byte *outL); + static uint32 HSLtoRGB(byte H, byte S, byte L); + +private: + static float Hue2RGB(float v1, float v2, float vH); +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_player.cpp b/engines/wintermute/video/video_player.cpp new file mode 100644 index 0000000000..2577b8aedc --- /dev/null +++ b/engines/wintermute/video/video_player.cpp @@ -0,0 +1,109 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/video/video_player.h" + +namespace Wintermute { + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +VideoPlayer::VideoPlayer(BaseGame *inGame) : BaseClass(inGame) { + setDefaults(); +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::setDefaults() { + _playing = false; + _videoEndTime = 0; + _soundAvailable = false; + _startTime = 0; + _totalVideoTime = 0; + _playPosX = _playPosY = 0; + _playZoom = 0.0f; + + _filename = NULL; + + _slowRendering = false; + + _currentSubtitle = 0; + _showSubtitle = false; + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +VideoPlayer::~VideoPlayer() { + cleanup(); +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::cleanup() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::initialize(const char *inFilename, const char *subtitleFile) { + warning("VideoPlayer: %s %s - Not implemented yet", inFilename, subtitleFile); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::update() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::display() { + return 0; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::play(TVideoPlayback type, int x, int y, bool freezeMusic) { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::stop() { + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::isPlaying() { + return _playing; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoPlayer::loadSubtitles(const char *filename, const char *subtitleFile) { + return STATUS_OK; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/video/video_player.h b/engines/wintermute/video/video_player.h new file mode 100644 index 0000000000..d5466da679 --- /dev/null +++ b/engines/wintermute/video/video_player.h @@ -0,0 +1,90 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDPLAYER_H +#define WINTERMUTE_VIDPLAYER_H + +#include "engines/wintermute/dctypes.h" // Added by ClassView +#include "engines/wintermute/base/base.h" + +#define MAX_AUDIO_STREAMS 5 +#define MAX_VIDEO_STREAMS 5 + + +namespace Wintermute { + +// AVI-Video-player, currently fully stubbed +class VideoPlayer : public BaseClass { +public: + bool _showSubtitle; + int _currentSubtitle; + bool loadSubtitles(const char *filename, const char *subtitleFile); + bool _slowRendering; + bool isPlaying(); + char *_filename; + bool stop(); + bool play(TVideoPlayback Type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeMusic = true); + uint32 _totalVideoTime; + uint32 _startTime; + //CVidRenderer *_vidRenderer; + //BaseSoundAVI *_sound; + bool _soundAvailable; + bool setDefaults(); + bool _playing; + bool display(); + bool update(); + bool initialize(const char *inFilename, const char *subtitleFile = NULL); + bool cleanup(); + VideoPlayer(BaseGame *inGame); + virtual ~VideoPlayer(); + + /*PAVIFILE _aviFile; + + LONG _lastSample; + + PAVISTREAM _audioStream; + PAVISTREAM _videoStream; + + LPWAVEFORMAT _audioFormat; + + LPBITMAPINFO _videoFormat; + PGETFRAME _videoPGF;*/ + uint32 _videoEndTime; + + int _playPosX; + int _playPosY; + float _playZoom; + + /* LPBITMAPV4HEADER _targetFormat; + + BaseArray<CVidSubtitle *, CVidSubtitle *> _subtitles;*/ +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/video/video_theora_player.cpp b/engines/wintermute/video/video_theora_player.cpp new file mode 100644 index 0000000000..0f8bc018ea --- /dev/null +++ b/engines/wintermute/video/video_theora_player.cpp @@ -0,0 +1,505 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + + +#include "engines/wintermute/video/video_theora_player.h" +#include "engines/wintermute/base/base_game.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/osystem/base_surface_osystem.h" +#include "engines/wintermute/base/gfx/base_image.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/platform_osystem.h" +#include "video/theora_decoder.h" +#include "engines/wintermute/wintermute.h" +#include "common/system.h" + +namespace Wintermute { + +IMPLEMENT_PERSISTENT(VideoTheoraPlayer, false) + +////////////////////////////////////////////////////////////////////////// +VideoTheoraPlayer::VideoTheoraPlayer(BaseGame *inGame) : BaseClass(inGame) { + SetDefaults(); +} + +////////////////////////////////////////////////////////////////////////// +void VideoTheoraPlayer::SetDefaults() { + + _file = NULL; + _filename = ""; + _startTime = 0; + _looping = false; + + _freezeGame = false; + _currentTime = 0; + + _state = THEORA_STATE_NONE; + + _videoFrameReady = false; + _audioFrameReady = false; + _videobufTime = 0; + + _playbackStarted = false; + _dontDropFrames = false; + + _texture = NULL; + _alphaImage = NULL; + _alphaFilename = ""; + + _frameRendered = false; + + _seekingKeyframe = false; + _timeOffset = 0.0f; + + _posX = _posY = 0; + _playbackType = VID_PLAY_CENTER; + _playZoom = 0.0f; + + _savedState = THEORA_STATE_NONE; + _savedPos = 0; + _volume = 100; + _theoraDecoder = NULL; + + // TODO: Add subtitles-support + //_subtitler = NULL; +} + +////////////////////////////////////////////////////////////////////////// +VideoTheoraPlayer::~VideoTheoraPlayer(void) { + cleanup(); +// SAFE_DELETE(_subtitler); +} + +////////////////////////////////////////////////////////////////////////// +void VideoTheoraPlayer::cleanup() { + if (_file) { + BaseFileManager::getEngineInstance()->closeFile(_file); + _file = NULL; + } + + _surface.free(); + if (_theoraDecoder) { + _theoraDecoder->close(); + } + delete _theoraDecoder; + _theoraDecoder = NULL; + delete _alphaImage; + _alphaImage = NULL; + delete _texture; + _texture = NULL; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::initialize(const Common::String &filename, const Common::String &subtitleFile) { + cleanup(); + + _filename = filename; + _file = BaseFileManager::getEngineInstance()->openFile(filename, true, false); + if (!_file) { + return STATUS_FAILED; + } + +#if defined (USE_THEORADEC) + _theoraDecoder = new Video::TheoraDecoder(); +#else + return STATUS_FAILED; +#endif + _theoraDecoder->loadStream(_file); + + if (!_theoraDecoder->isVideoLoaded()) { + return STATUS_FAILED; + } + + _state = THEORA_STATE_PAUSED; + + // Additional setup. + _surface.create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight(), _theoraDecoder->getPixelFormat()); + _texture = new BaseSurfaceOSystem(_gameRef); + _texture->create(_theoraDecoder->getWidth(), _theoraDecoder->getHeight()); + _state = THEORA_STATE_PLAYING; + _playZoom = 100; + + return STATUS_OK; +} + + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::resetStream() { + warning("VidTheoraPlayer::resetStream - stubbed"); +#if 0 // Stubbed for now, as theora isn't seekable + if (_sound) { + _sound->Stop(); + } + + m_TimeOffset = 0.0f; + Initialize(m_Filename); + Play(m_PlaybackType, m_PosX, m_PosY, false, false, m_Looping, 0, m_PlayZoom); +#endif + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::play(TVideoPlayback type, int x, int y, bool freezeGame, bool freezeMusic, bool looping, uint32 startTime, float forceZoom, int volume) { + if (forceZoom < 0.0f) { + forceZoom = 100.0f; + } + if (volume < 0) { + _volume = _gameRef->_soundMgr->getVolumePercent(Audio::Mixer::kSFXSoundType); + } else { + _volume = volume; + } + + _freezeGame = freezeGame; + + if (!_playbackStarted && _freezeGame) { + _gameRef->freeze(freezeMusic); + } + + _playbackStarted = false; + float width, height; + if (_theoraDecoder) { + _surface.free(); + _surface.copyFrom(*_theoraDecoder->decodeNextFrame()); + _state = THEORA_STATE_PLAYING; + _looping = looping; + _playbackType = type; + + _startTime = startTime; + _volume = volume; + _posX = x; + _posY = y; + _playZoom = forceZoom; + + width = (float)_theoraDecoder->getWidth(); + height = (float)_theoraDecoder->getHeight(); + } else { + width = (float)_gameRef->_renderer->_width; + height = (float)_gameRef->_renderer->_height; + } + + switch (type) { + case VID_PLAY_POS: + _playZoom = forceZoom; + _posX = x; + _posY = y; + break; + + case VID_PLAY_STRETCH: { + float zoomX = (float)((float)_gameRef->_renderer->_width / width * 100); + float zoomY = (float)((float)_gameRef->_renderer->_height / height * 100); + _playZoom = MIN(zoomX, zoomY); + _posX = (int)((_gameRef->_renderer->_width - width * (_playZoom / 100)) / 2); + _posY = (int)((_gameRef->_renderer->_height - height * (_playZoom / 100)) / 2); + } + break; + + case VID_PLAY_CENTER: + _playZoom = 100.0f; + _posX = (int)((_gameRef->_renderer->_width - width) / 2); + _posY = (int)((_gameRef->_renderer->_height - height) / 2); + break; + } + _theoraDecoder->start(); + + return STATUS_OK; +#if 0 // Stubbed for now as theora isn't seekable + if (StartTime) SeekToTime(StartTime); + + Update(); +#endif + return STATUS_FAILED; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::stop() { + _theoraDecoder->close(); + _state = THEORA_STATE_FINISHED; + if (_freezeGame) { + _gameRef->unfreeze(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::update() { + _currentTime = _freezeGame ? _gameRef->_liveTimer : _gameRef->_timer; + + if (!isPlaying()) { + return STATUS_OK; + } + + if (_playbackStarted /*&& m_Sound && !m_Sound->IsPlaying()*/) { + return STATUS_OK; + } + + if (_playbackStarted && !_freezeGame && _gameRef->_state == GAME_FROZEN) { + return STATUS_OK; + } + + if (_theoraDecoder) { + if (_theoraDecoder->endOfVideo() && _looping) { + warning("Should loop movie %s", _filename.c_str()); + _theoraDecoder->rewind(); + } else if (_theoraDecoder->endOfVideo() && !_looping) { + debugC(kWintermuteDebugLog, "Finished movie %s", _filename.c_str()); + _state = THEORA_STATE_FINISHED; + _playbackStarted = false; + if (_freezeGame) { + _gameRef->unfreeze(); + } + } + if (_state == THEORA_STATE_PLAYING) { + if (!_theoraDecoder->endOfVideo() && _theoraDecoder->getTimeToNextFrame() == 0) { + const Graphics::Surface *decodedFrame = _theoraDecoder->decodeNextFrame(); + if (decodedFrame) { + _surface.free(); + _surface.copyFrom(*decodedFrame); + if (_texture) { + writeVideo(); + } + } + } + return STATUS_OK; + } + } + // Skip the busy-loop? + if ((!_texture || !_videoFrameReady) && !_theoraDecoder->endOfVideo()) { + // end playback + if (!_looping) { + _state = THEORA_STATE_FINISHED; + if (_freezeGame) { + _gameRef->unfreeze(); + } + return STATUS_OK; + } else { + resetStream(); + return STATUS_OK; + } + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +uint32 VideoTheoraPlayer::getMovieTime() { + if (!_playbackStarted) { + return 0; + } else { + return _theoraDecoder->getTime(); + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::writeVideo() { + if (!_texture) { + return STATUS_FAILED; + } + + _texture->startPixelOp(); + + writeAlpha(); + if (_alphaImage) { + _texture->putSurface(_surface, true); + } else { + _texture->putSurface(_surface, false); + } + + //RenderFrame(_texture, &yuv); + + _texture->endPixelOp(); + _videoFrameReady = true; + return STATUS_OK; +} + +void VideoTheoraPlayer::writeAlpha() { + if (_alphaImage && _surface.w == _alphaImage->getSurface()->w && _surface.h == _alphaImage->getSurface()->h) { + assert(_alphaImage->getSurface()->format.bytesPerPixel == 4); + assert(_surface.format.bytesPerPixel == 4); + const byte *alphaData = (const byte *)_alphaImage->getSurface()->getBasePtr(0, 0); +#ifdef SCUMM_LITTLE_ENDIAN + int alphaPlace = (_alphaImage->getSurface()->format.aShift / 8); +#else + int alphaPlace = 3 - (_alphaImage->getSurface()->format.aShift / 8); +#endif + alphaData += alphaPlace; + byte *imgData = (byte *)_surface.getBasePtr(0, 0); +#ifdef SCUMM_LITTLE_ENDIAN + imgData += (_surface.format.aShift / 8); +#else + imgData += 3 - (_surface.format.aShift / 8); +#endif + for (int i = 0; i < _surface.w * _surface.h; i++) { + *imgData = *alphaData; + alphaData += 4; + imgData += 4; + } + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::display(uint32 alpha) { + Rect32 rc; + bool res; + + if (_texture && _videoFrameReady) { + BasePlatform::setRect(&rc, 0, 0, _texture->getWidth(), _texture->getHeight()); + if (_playZoom == 100.0f) { + res = _texture->displayTrans(_posX, _posY, rc, alpha); + } else { + res = _texture->displayTransZoom(_posX, _posY, rc, _playZoom, _playZoom, alpha); + } + } else { + res = STATUS_FAILED; + } + // TODO: Add subtitles-support +/* if (m_Subtitler && _gameRef->m_VideoSubtitles) { + m_Subtitler->display(); + }*/ + + return res; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::setAlphaImage(const Common::String &filename) { + delete _alphaImage; + _alphaImage = new BaseImage(); + if (!_alphaImage || DID_FAIL(_alphaImage->loadFile(filename))) { + delete _alphaImage; + _alphaImage = NULL; + _alphaFilename = ""; + return STATUS_FAILED; + } + + if (_alphaFilename != filename) { + _alphaFilename = filename; + } + //TODO: Conversion. + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +byte VideoTheoraPlayer::getAlphaAt(int x, int y) { + if (_alphaImage) { + return _alphaImage->getAlphaAt(x, y); + } else { + return 0xFF; + } +} + + +////////////////////////////////////////////////////////////////////////// +inline int intlog(int num) { + int r = 0; + while (num > 0) { + num = num / 2; + r = r + 1; + } + + return r; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::seekToTime(uint32 time) { + warning("VideoTheoraPlayer::SeekToTime(%d) - not supported", time); + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::pause() { + if (_state == THEORA_STATE_PLAYING) { + _state = THEORA_STATE_PAUSED; + _theoraDecoder->pauseVideo(true); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::resume() { + if (_state == THEORA_STATE_PAUSED) { + _state = THEORA_STATE_PLAYING; + _theoraDecoder->pauseVideo(false); + return STATUS_OK; + } else { + return STATUS_FAILED; + } +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::persist(BasePersistenceManager *persistMgr) { + //BaseClass::persist(persistMgr); + + if (persistMgr->getIsSaving()) { + _savedPos = getMovieTime() * 1000; + _savedState = _state; + } else { + SetDefaults(); + } + + persistMgr->transfer(TMEMBER(_gameRef)); + persistMgr->transfer(TMEMBER(_savedPos)); + persistMgr->transfer(TMEMBER(_savedState)); + persistMgr->transfer(TMEMBER(_filename)); + persistMgr->transfer(TMEMBER(_alphaFilename)); + persistMgr->transfer(TMEMBER(_posX)); + persistMgr->transfer(TMEMBER(_posY)); + persistMgr->transfer(TMEMBER(_playZoom)); + persistMgr->transfer(TMEMBER_INT(_playbackType)); + persistMgr->transfer(TMEMBER(_looping)); + persistMgr->transfer(TMEMBER(_volume)); + + if (!persistMgr->getIsSaving() && (_savedState != THEORA_STATE_NONE)) { + initializeSimple(); + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +bool VideoTheoraPlayer::initializeSimple() { + if (DID_SUCCEED(initialize(_filename))) { + if (_alphaFilename != "") { + setAlphaImage(_alphaFilename); + } + play(_playbackType, _posX, _posY, false, false, _looping, _savedPos, _playZoom); + } else { + _state = THEORA_STATE_FINISHED; + } + + return STATUS_OK; +} + +////////////////////////////////////////////////////////////////////////// +BaseSurface *VideoTheoraPlayer::getTexture() { + return _texture; +} + +} // end of namespace Wintermute diff --git a/engines/wintermute/video/video_theora_player.h b/engines/wintermute/video/video_theora_player.h new file mode 100644 index 0000000000..593c1b9666 --- /dev/null +++ b/engines/wintermute/video/video_theora_player.h @@ -0,0 +1,148 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_VIDTHEORAPLAYER_H +#define WINTERMUTE_VIDTHEORAPLAYER_H + +#include "engines/wintermute/base/base.h" +#include "engines/wintermute/persistent.h" +#include "video/video_decoder.h" +#include "common/stream.h" +#include "graphics/surface.h" + +namespace Wintermute { +class BaseSurface; +class BaseImage; +class VideoTheoraPlayer : public BaseClass { +private: + enum { + THEORA_STATE_NONE = 0, + THEORA_STATE_PLAYING = 1, + THEORA_STATE_PAUSED = 2, + THEORA_STATE_FINISHED = 3 + }; + Video::VideoDecoder *_theoraDecoder; + Graphics::Surface _surface; +public: + DECLARE_PERSISTENT(VideoTheoraPlayer, BaseClass) + + VideoTheoraPlayer(BaseGame *inGame); + virtual ~VideoTheoraPlayer(void); + + // external objects + Common::SeekableReadStream *_file; + Common::String _filename; + + BaseSurface *_texture; + //CVidSubtitler *_subtitler; + + // control methods + bool initialize(const Common::String &filename, const Common::String &subtitleFile = NULL); + bool initializeSimple(); + bool update(); + bool play(TVideoPlayback type = VID_PLAY_CENTER, int x = 0, int y = 0, bool freezeGame = false, bool freezeMusic = true, bool looping = false, uint32 startTime = 0, float forceZoom = -1.0f, int volume = -1); + bool stop(); + bool display(uint32 alpha = 0xFFFFFFFF); + + bool pause(); + bool resume(); + + bool isPlaying() { + return _state == THEORA_STATE_PLAYING; + }; + bool isFinished() { + return _state == THEORA_STATE_FINISHED; + }; + bool isPaused() { + return _state == THEORA_STATE_PAUSED; + }; + + uint32 getMovieTime(); + + BaseSurface *getTexture(); + + int _state; + uint32 _startTime; + + int _savedState; + uint32 _savedPos; + + + // alpha related + BaseImage *_alphaImage; + Common::String _alphaFilename; + bool setAlphaImage(const Common::String &filename); + __inline byte getAlphaAt(int x, int y); + void writeAlpha(); + + bool seekToTime(uint32 Time); + + + void cleanup(); + bool resetStream(); + + // video properties + TVideoPlayback _playbackType; + int _posX; + int _posY; + float _playZoom; + int _volume; + + bool _looping; + bool _dontDropFrames; + bool _freezeGame; + uint32 _currentTime; + + +private: + // seeking support + bool _seekingKeyframe; + float _timeOffset; + + bool _frameRendered; + + bool getIsFrameReady() { + return _videoFrameReady; + } +private: + bool _audioFrameReady; + bool _videoFrameReady; + float _videobufTime; + + bool writeVideo(); + + bool _playbackStarted; + + // helpers + void SetDefaults(); + +}; + +} // end of namespace Wintermute + +#endif diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp new file mode 100644 index 0000000000..c9726e150a --- /dev/null +++ b/engines/wintermute/wintermute.cpp @@ -0,0 +1,383 @@ +/* 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/scummsys.h" + +#include "common/config-manager.h" +#include "common/debug.h" +#include "common/debug-channels.h" +#include "common/error.h" +#include "common/EventRecorder.h" +#include "common/file.h" +#include "common/fs.h" +#include "common/tokenizer.h" + +#include "engines/util.h" +#include "engines/wintermute/ad/ad_game.h" +#include "engines/wintermute/wintermute.h" +#include "engines/wintermute/platform_osystem.h" +#include "engines/wintermute/base/base_engine.h" + +#include "engines/wintermute/base/sound/base_sound_manager.h" +#include "engines/wintermute/base/base_file_manager.h" +#include "engines/wintermute/base/gfx/base_renderer.h" +#include "engines/wintermute/base/scriptables/script_engine.h" + +namespace Wintermute { + +// Simple constructor for detection - we need to setup the persistence to avoid special-casing in-engine +// This might not be the prettiest solution +WintermuteEngine::WintermuteEngine() : Engine(g_system) { + _game = new AdGame(""); +} + +WintermuteEngine::WintermuteEngine(OSystem *syst, const ADGameDescription *desc) + : Engine(syst), _gameDescription(desc) { + // Put your engine in a sane state, but do nothing big yet; + // in particular, do not load data from files; rather, if you + // need to do such things, do them from init(). + + // Do not initialize graphics here + + // However this is the place to specify all default directories + const Common::FSNode gameDataDir(ConfMan.get("path")); + //SearchMan.addSubDirectoryMatching(gameDataDir, "sound"); + + // Here is the right place to set up the engine specific debug channels + DebugMan.addDebugChannel(kWintermuteDebugLog, "enginelog", "Covers the same output as the log-file in WME"); + DebugMan.addDebugChannel(kWintermuteDebugSaveGame, "savegame", "Savegames"); + DebugMan.addDebugChannel(kWintermuteDebugFont, "font", "Text-drawing-related messages"); + DebugMan.addDebugChannel(kWintermuteDebugFileAccess, "file-access", "Non-critical problems like missing files"); + DebugMan.addDebugChannel(kWintermuteDebugAudio, "audio", "audio-playback-related issues"); + DebugMan.addDebugChannel(kWintermuteDebugGeneral, "general", "various issues not covered by any of the above"); + + _game = NULL; +} + +WintermuteEngine::~WintermuteEngine() { + // Dispose your resources here + deinit(); + delete _game; + delete _console; + + // Remove all of our debug levels here + DebugMan.clearAllDebugChannels(); +} + +bool WintermuteEngine::hasFeature(EngineFeature f) const { + switch (f) { + case kSupportsRTL: + return true; + case kSupportsLoadingDuringRuntime: + return true; + case kSupportsSavingDuringRuntime: + return true; + default: + return false; + } + return false; +} + +Common::Error WintermuteEngine::run() { + // Initialize graphics using following: + Graphics::PixelFormat format(4, 8, 8, 8, 8, 16, 8, 0, 24); + initGraphics(800, 600, true, &format); + if (g_system->getScreenFormat() != format) { + error("Wintermute currently REQUIRES 32bpp"); + } + + // Create debugger console. It requires GFX to be initialized + _console = new Console(this); + +// DebugMan.enableDebugChannel("enginelog"); + debugC(1, kWintermuteDebugLog, "Engine Debug-LOG enabled"); + debugC(2, kWintermuteDebugSaveGame , "Savegame debugging-enabled"); + + int ret = 1; + + // Additional setup. + debugC(kWintermuteDebugLog, "WintermuteEngine::init"); + ret = init(); + + debugC(kWintermuteDebugLog, "WintermuteEngine::messageLoop"); + if (ret == 0) { + ret = messageLoop(); + } + deinit(); + return Common::kNoError; +} + +int WintermuteEngine::init() { + BaseEngine::createInstance(_targetName, _gameDescription->language); + _game = new AdGame(_targetName); + if (!_game) { + return 1; + } + BaseEngine::instance().setGameRef(_game); + BasePlatform::initialize(_game, 0, NULL); + + bool windowedMode = !ConfMan.getBool("fullscreen"); + + if (ConfMan.hasKey("debug_mode")) { + if (ConfMan.getBool("debug_mode")) { + _game->DEBUG_DebugEnable("./wme.log"); + } + } + + if (ConfMan.hasKey("show_fps")) { + _game->_debugShowFPS = ConfMan.getBool("show_fps"); + } else { + _game->_debugShowFPS = false; + } + + if (ConfMan.hasKey("disable_smartcache")) { + _game->_smartCache = ConfMan.getBool("disable_smartcache"); + } else { + _game->_smartCache = true; + } + + if (!_game->_smartCache) { + _game->LOG(0, "Smart cache is DISABLED"); + } + + // load general game settings + _game->initialize1(); + + // set gameId, for savegame-naming: + _game->setGameId(_targetName); + + if (DID_FAIL(_game->loadSettings("startup.settings"))) { + _game->LOG(0, "Error loading game settings."); + delete _game; + _game = NULL; + + warning("Some of the essential files are missing. Please reinstall."); + return 2; + } + + _game->initialize2(); + + bool ret; + + // initialize the renderer + ret = _game->_renderer->initRenderer(_game->_settingsResWidth, _game->_settingsResHeight, windowedMode); + if (DID_FAIL(ret)) { + _game->LOG(ret, "Error initializing renderer. Exiting."); + + delete _game; + _game = NULL; + return 3; + } + + _game->initialize3(); + + // initialize sound manager (non-fatal if we fail) + ret = _game->_soundMgr->initialize(); + if (DID_FAIL(ret)) { + _game->LOG(ret, "Sound is NOT available."); + } + + + // load game + uint32 dataInitStart = g_system->getMillis(); + + if (DID_FAIL(_game->loadFile(_game->_settingsGameFile ? _game->_settingsGameFile : "default.game"))) { + _game->LOG(ret, "Error loading game file. Exiting."); + delete _game; + _game = NULL; + return false; + } + + _game->_renderer->_ready = true; + _game->_miniUpdateEnabled = true; + + _game->LOG(0, "Engine initialized in %d ms", g_system->getMillis() - dataInitStart); + _game->LOG(0, ""); + + if (ConfMan.hasKey("save_slot")) { + int slot = ConfMan.getInt("save_slot"); + _game->loadGame(slot); + } + + // all set, ready to go + return 0; +} + +int WintermuteEngine::messageLoop() { + bool done = false; + + uint32 prevTime = _system->getMillis(); + uint32 time = _system->getMillis(); + uint32 diff = 0; + + const uint32 maxFPS = 60; + const uint32 frameTime = (uint32)((1.0 / maxFPS) * 1000); + while (!done) { + Common::Event event; + while (_system->getEventManager()->pollEvent(event)) { + BasePlatform::handleEvent(&event); + } + + if (_game && _game->_renderer->_active && _game->_renderer->_ready) { + _game->displayContent(); + _game->displayQuickMsg(); + + _game->displayDebugInfo(); + + time = _system->getMillis(); + diff = time - prevTime; + if (frameTime > diff) { // Avoid overflows + _system->delayMillis(frameTime - diff); + } + + // ***** flip + if (!_game->_suspendedRendering) { + _game->_renderer->flip(); + } + if (_game->_loading) { + _game->loadGame(_game->_scheduledLoadSlot); + } + prevTime = time; + } + if (_game->_quitting) { + break; + } + } + + if (_game) { + delete _game; + _game = NULL; + } + return 0; +} + +void WintermuteEngine::deinit() { + BaseEngine::destroy(); +} + +Common::Error WintermuteEngine::loadGameState(int slot) { + BaseEngine::instance().getGameRef()->loadGame(slot); + return Common::kNoError; +} + +Common::Error WintermuteEngine::saveGameState(int slot, const Common::String &desc) { + BaseEngine::instance().getGameRef()->saveGame(slot, desc.c_str(), false); + return Common::kNoError; +} + +bool WintermuteEngine::canSaveGameStateCurrently() { + return true; +} + +bool WintermuteEngine::canLoadGameStateCurrently() { + return true; +} + +bool WintermuteEngine::getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption) { + bool retVal = false; + caption = name = "(invalid)"; + Common::SeekableReadStream *stream = NULL; + // Quick-fix, instead of possibly breaking the persistence-system, let's just roll with it + BaseFileManager *fileMan = new BaseFileManager(Common::UNK_LANG); + fileMan->registerPackages(fslist); + stream = fileMan->openFile("startup.settings", false, false); + + // The process is as follows: Check the "GAME=" tag in startup.settings, to decide where the + // game-settings are (usually "default.game"), then look into the game-settings to find + // the NAME = and CAPTION = tags, to use them to generate a gameid and extras-field + + Common::String settingsGameFile = "default.game"; + // If the stream-open failed, lets at least attempt to open the default game file afterwards + // so, we don't call it a failure yet. + if (stream) { + while (!stream->eos() && !stream->err()) { + Common::String line = stream->readLine(); + line.trim(); // Get rid of indentation + // Expect "SETTINGS {" or comment, or empty line + if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) { + continue; + } else { + // We are looking for "GAME =" + Common::StringTokenizer token(line, "="); + Common::String key = token.nextToken(); + Common::String value = token.nextToken(); + if (value.size() == 0) { + continue; + } + if (value[0] == '\"') { + value.deleteChar(0); + } else { + continue; + } + if (value.lastChar() == '\"') { + value.deleteLastChar(); + } + if (key == "GAME") { + settingsGameFile = value; + break; + } + } + } + } + + delete stream; + stream = fileMan->openFile(settingsGameFile, false, false); + if (stream) { + // We do some manual parsing here, as the engine needs gfx to be initalized to do that. + while (!stream->eos() && !stream->err()) { + Common::String line = stream->readLine(); + line.trim(); // Get rid of indentation + // Expect "GAME {" or comment, or empty line + if (line.size() == 0 || line[0] == ';' || (line.contains("{"))) { + continue; + } else { + Common::StringTokenizer token(line, "="); + Common::String key = token.nextToken(); + Common::String value = token.nextToken(); + if (value.size() == 0) { + continue; + } + if (value[0] == '\"') { + value.deleteChar(0); + } else { + continue; // not a string + } + if (value.lastChar() == '\"') { + value.deleteLastChar(); + } + if (key == "NAME") { + retVal = true; + name = value; + } else if (key == "CAPTION") { + retVal = true; + caption = value; + } + } + } + delete stream; + } + delete fileMan; + BaseEngine::destroy(); + return retVal; +} + +} // End of namespace Wintermute diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h new file mode 100644 index 0000000000..d24b120658 --- /dev/null +++ b/engines/wintermute/wintermute.h @@ -0,0 +1,78 @@ +/* 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. + * + */ + +#ifndef WINTERMUTE_H +#define WINTERMUTE_H + +#include "engines/engine.h" +#include "engines/advancedDetector.h" +#include "gui/debugger.h" + +namespace Wintermute { + +class Console; +class BaseGame; +class SystemClassRegistry; +// our engine debug channels +enum { + kWintermuteDebugLog = 1 << 0, // The debug-logs from the original engine + kWintermuteDebugSaveGame = 1 << 1, + kWintermuteDebugFont = 1 << 2, // next new channel must be 1 << 2 (4) + kWintermuteDebugFileAccess = 1 << 3, // the current limitation is 32 debug channels (1 << 31 is the last one) + kWintermuteDebugAudio = 1 << 4, + kWintermuteDebugGeneral = 1 << 5 +}; + +class WintermuteEngine : public Engine { +public: + WintermuteEngine(OSystem *syst, const ADGameDescription *desc); + WintermuteEngine(); + ~WintermuteEngine(); + + virtual Common::Error run(); + virtual bool hasFeature(EngineFeature f) const; + Common::SaveFileManager *getSaveFileMan() { return _saveFileMan; } + virtual Common::Error loadGameState(int slot); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual bool canSaveGameStateCurrently(); + // For detection-purposes: + static bool getGameInfo(const Common::FSList &fslist, Common::String &name, Common::String &caption); +private: + int init(); + void deinit(); + int messageLoop(); + Console *_console; + BaseGame *_game; + const ADGameDescription *_gameDescription; +}; + +// Example console class +class Console : public GUI::Debugger { +public: + Console(WintermuteEngine *vm) {} + virtual ~Console(void) {} +}; + +} // End of namespace Wintermute + +#endif diff --git a/engines/wintermute/wintypes.h b/engines/wintermute/wintypes.h new file mode 100644 index 0000000000..c7723808ea --- /dev/null +++ b/engines/wintermute/wintypes.h @@ -0,0 +1,53 @@ +/* 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. + * + */ + +/* + * This file is based on WME Lite. + * http://dead-code.org/redir.php?target=wmelite + * Copyright (c) 2011 Jan Nedoma + */ + +#ifndef WINTERMUTE_WINTYPES_H +#define WINTERMUTE_WINTYPES_H + +#include "common/scummsys.h" + +namespace Wintermute { + +#define BYTETORGBA(r,g,b,a) ((uint32)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff))) + +#define RGBCOLGetB(rgb) ((byte )(rgb)) +#define RGBCOLGetG(rgb) ((byte )(((uint16)(rgb)) >> 8)) +#define RGBCOLGetR(rgb) ((byte )((rgb)>>16)) +#define RGBCOLGetA(rgb) ((byte )((rgb)>>24)) + +#define DID_SUCCEED(hr) ((bool)(hr)) +#define DID_FAIL(hr) (!((bool)(hr))) + +#define STATUS_OK (true) +#define STATUS_FAILED (false) + +#define MAX_PATH_LENGTH 512 + +} // end of namespace Wintermute + +#endif |