void ThemeEngine::listUsableThemes(Common::List<ThemeDescriptor> &list) { #ifndef DISABLE_GUI_BUILTIN_THEME ThemeDescriptor th; th.name = "ScummVM Classic Theme (Builtin Version)"; th.id = "builtin"; th.filename.clear(); list.push_back(th); #endif if (ConfMan.hasKey("themepath")) listUsableThemes(Common::FSNode(ConfMan.get("themepath")), list); listUsableThemes(SearchMan, list); // Now we need to strip all duplicates // TODO: It might not be the best idea to strip duplicates. The user might // have different versions of a specific theme in his paths, thus this code // might show him the wrong version. The problem is we have no ways of checking // a theme version currently. Also since we want to avoid saving the full path // in the config file we can not do any better currently. Common::List<ThemeDescriptor> output; for (Common::List<ThemeDescriptor>::const_iterator i = list.begin(); i != list.end(); ++i) { if (Common::find_if(output.begin(), output.end(), TDComparator(i->id)) == output.end()) output.push_back(*i); } list = output; output.clear(); }
/** * Determines the first matching format between two lists. * * @param backend The higher priority list, meant to be a list of formats supported by the backend * @param frontend The lower priority list, meant to be a list of formats supported by the engine * @return The first item on the backend list that also occurs on the frontend list * or PixelFormat::createFormatCLUT8() if no matching formats were found. */ inline Graphics::PixelFormat findCompatibleFormat(const Common::List<Graphics::PixelFormat> &backend, const Common::List<Graphics::PixelFormat> &frontend) { #ifdef USE_RGB_COLOR for (Common::List<Graphics::PixelFormat>::const_iterator i = backend.begin(); i != backend.end(); ++i) { for (Common::List<Graphics::PixelFormat>::const_iterator j = frontend.begin(); j != frontend.end(); ++j) { if (*i == *j) return *i; } } #endif return Graphics::PixelFormat::createFormatCLUT8(); }
/** * Determines the first matching format between two lists. * * @param backend The higher priority list, meant to be a list of formats supported by the backend * @param frontend The lower priority list, meant to be a list of formats supported by the engine * @return The first item on the backend list that also occurs on the frontend list * or PixelFormat::createFormatCLUT8() if no matching formats were found. */ inline PixelFormat findCompatibleFormat(Common::List<PixelFormat> backend, Common::List<PixelFormat> frontend) { #ifdef USE_RGB_COLOR for (Common::List<PixelFormat>::iterator i = backend.begin(); i != backend.end(); ++i) { for (Common::List<PixelFormat>::iterator j = frontend.begin(); j != frontend.end(); ++j) { if (*i == *j) return *i; } } #endif return PixelFormat::createFormatCLUT8(); }
void RMWindow::getNewFrame(RMGfxTargetBuffer &bigBuf, Common::Rect *rcBoundEllipse) { // Get a pointer to the bytes of the source buffer byte *lpBuf = bigBuf; if (rcBoundEllipse != NULL) { // Circular wipe effect getNewFrameWipe(lpBuf, *rcBoundEllipse); _wiping = true; } else if (_wiping) { // Just finished a wiping effect, so copy the full screen copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY); _wiping = false; } else { // Standard screen copy - iterate through the dirty rects Common::List<Common::Rect> dirtyRects = bigBuf.getDirtyRects(); Common::List<Common::Rect>::iterator i; // If showing dirty rects, copy the entire screen background and set up a surface pointer Graphics::Surface *s = NULL; if (_showDirtyRects) { copyRectToScreen(lpBuf, RM_SX * 2, 0, 0, RM_SX, RM_SY); s = g_system->lockScreen(); } for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) { Common::Rect &r = *i; const byte *lpSrc = lpBuf + (RM_SX * 2) * r.top + (r.left * 2); copyRectToScreen(lpSrc, RM_SX * 2, r.left, r.top, r.width(), r.height()); } if (_showDirtyRects) { for (i = dirtyRects.begin(); i != dirtyRects.end(); ++i) { // Frame the copied area with a rectangle s->frameRect(*i, 0xffffff); } g_system->unlockScreen(); } } if (_bGrabThumbnail) { // Need to generate a thumbnail RMSnapshot s; s.grabScreenshot(lpBuf, 4, _wThumbBuf); _bGrabThumbnail = false; } // Clear the dirty rect list bigBuf.clearDirtyRects(); }
SciVersion GameFeatures::detectMessageFunctionType() { if (_messageFunctionType != SCI_VERSION_NONE) return _messageFunctionType; if (getSciVersion() > SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } else if (getSciVersion() < SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_LATE; return _messageFunctionType; } Common::List<ResourceId> resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1); if (resources.empty()) { // No messages found, so this doesn't really matter anyway... _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } Resource *res = g_sci->getResMan()->findResource(*resources.begin(), false); assert(res); // Only v2 Message resources use the kGetMessage kernel function. // v3-v5 use the kMessage kernel function. if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2) _messageFunctionType = SCI_VERSION_1_LATE; else _messageFunctionType = SCI_VERSION_1_1; debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType)); return _messageFunctionType; }
void clearList(Common::List<T> &list) { while (!list.empty()) { T p = list.front(); list.erase(list.begin()); delete p; } }
Common::String ThemeEngine::getThemeFile(const Common::String &id) { // FIXME: Actually "default" rather sounds like it should use // our default theme which would mean "scummmodern" instead // of the builtin one. if (id.equalsIgnoreCase("default")) return Common::String(); // For our builtin theme we don't have to do anything for now too if (id.equalsIgnoreCase("builtin")) return Common::String(); Common::FSNode node(id); // If the given id is a full path we'll just use it if (node.exists() && (node.isDirectory() || node.getName().matchString("*.zip", true))) return id; // FIXME: // A very ugly hack to map a id to a filename, this will generate // a complete theme list, thus it is slower than it could be. // But it is the easiest solution for now. Common::List<ThemeDescriptor> list; listUsableThemes(list); for (Common::List<ThemeDescriptor>::const_iterator i = list.begin(); i != list.end(); ++i) { if (id.equalsIgnoreCase(i->id)) return i->filename; } warning("Could not find theme '%s' falling back to builtin", id.c_str()); // If no matching id has been found we will // just fall back to the builtin theme return Common::String(); }
void VideoPlayer::processVideoEvents(Common::List<Common::Event> &stopEvents) { Common::Event curEvent; Common::EventManager *eventMan = g_system->getEventManager(); // Process events, and skip video if esc is pressed while (eventMan->pollEvent(curEvent)) { if (curEvent.type == Common::EVENT_RTL || curEvent.type == Common::EVENT_QUIT) { _skipVideo = true; } for (Common::List<Common::Event>::const_iterator iter = stopEvents.begin(); iter != stopEvents.end(); iter++) { if (curEvent.type == iter->type) { if (iter->type == Common::EVENT_KEYDOWN || iter->type == Common::EVENT_KEYUP) { if (curEvent.kbd.keycode == iter->kbd.keycode) { _skipVideo = true; break; } } else { _skipVideo = true; break; } } } } }
/** * Checks both the active and free process list to insure all the links are valid, * and that no processes have been lost */ void Scheduler::CheckStack() { Common::List<PROCESS *> pList; // Check both the active and free process lists for (int i = 0; i < 2; ++i) { PROCESS *p = (i == 0) ? active : pFreeProcesses; if (p != NULL) { // Make sure the linkages are correct while (p->pNext != NULL) { assert(p->pNext->pPrevious == p); pList.push_back(p); p = p->pNext; } pList.push_back(p); } } // Make sure all processes are accounted for for (int idx = 0; idx < NUM_PROCESS; idx++) { bool found = false; for (Common::List<PROCESS *>::iterator i = pList.begin(); i != pList.end(); ++i) { PROCESS *pTemp = *i; if (*i == &processList[idx]) { found = true; break; } } assert(found); } }
void ViewManager::updateState() { Common::List<View *> viewList = _views; for (ListIterator i = viewList.begin(); i != viewList.end(); ++i) { if (_vm->_events->quitFlag) return; View *v = *i; v->updateState(); } }
void Location::freeList(Common::List<T> &list, bool removeAll, Common::MemFunc1<bool, T, Location> filter) { typedef typename Common::List<T>::iterator iterator; iterator it = list.begin(); while (it != list.end()) { T z = *it; if (!removeAll && filter(this, z)) { ++it; } else { z->_commands.clear(); it = list.erase(it); } } }
GameList ScummMetaEngine::detectGames(const Common::FSList &fslist) const { GameList detectedGames; Common::List<DetectorResult> results; ::detectGames(fslist, results, 0); // TODO: We still don't handle the FM-TOWNS demos (like zakloom) very well. // In particular, they are detected as ZakTowns, which is bad. for (Common::List<DetectorResult>::iterator x = results.begin(); x != results.end(); ++x) { const PlainGameDescriptor *g = findPlainGameDescriptor(x->game.gameid, gameDescriptions); assert(g); GameDescriptor dg(x->game.gameid, g->description, x->language, x->game.platform); // Append additional information, if set, to the description. dg.updateDesc(x->extra); // Compute and set the preferred target name for this game. // Based on generateComplexID() in advancedDetector.cpp. dg["preferredtarget"] = generatePreferredTarget(*x); // HACK: Detect and distinguish the FM-TOWNS demos if (x->game.platform == Common::kPlatformFMTowns && (x->game.features & GF_DEMO)) { if (x->md5 == "2d388339d6050d8ccaa757b64633954e") { // Indy + Loom demo dg.description() = "Indiana Jones and the Last Crusade & Loom"; dg.updateDesc(x->extra); dg["preferredtarget"] = "indyloom"; } else if (x->md5 == "77f5c9cc0986eb729c1a6b4c8823bbae") { // Zak + Loom demo dg.description() = "Zak McKracken & Loom"; dg.updateDesc(x->extra); dg["preferredtarget"] = "zakloom"; } else if (x->md5 == "3938ee1aa4433fca9d9308c9891172b1") { // Indy + Zak demo dg.description() = "Indiana Jones and the Last Crusade & Zak McKracken"; dg.updateDesc(x->extra); dg["preferredtarget"] = "indyzak"; } } dg.setGUIOptions(x->game.guioptions | MidiDriver::musicType2GUIO(x->game.midi)); dg.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(x->language)); detectedGames.push_back(dg); } return detectedGames; }
void GlkEngine::initGraphicsMode() { uint width = ConfMan.hasKey("width") ? ConfMan.getInt("width") : 640; uint height = ConfMan.hasKey("height") ? ConfMan.getInt("height") : 480; Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats(); Graphics::PixelFormat format = formats.front(); for (Common::List<Graphics::PixelFormat>::iterator i = formats.begin(); i != formats.end(); ++i) { if ((*i).bytesPerPixel > 1) { format = *i; break; } } initGraphics(width, height, &format); }
bool Diving::play(uint16 playerCount, bool hasPearlLocation) { init(); initScreen(); _vm->_draw->blitInvalidated(); _vm->_video->retrace(); EvilFish shark(*_objects, 320, 0, 14, 8, 9, 3); Common::List<ANIObject *> objects; objects.push_back(_water); objects.push_back(&shark); shark.enter(EvilFish::kDirectionLeft, 90); while (!_vm->_util->keyPressed() && !_vm->shouldQuit()) { int16 left, top, right, bottom; // Clear the previous animation frames for (Common::List<ANIObject *>::iterator o = objects.reverse_begin(); o != objects.end(); --o) { (*o)->clear(*_vm->_draw->_backSurface, left, top, right, bottom); _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); } // Draw the current animation frames for (Common::List<ANIObject *>::iterator o = objects.begin(); o != objects.end(); ++o) { (*o)->draw(*_vm->_draw->_backSurface, left, top, right, bottom); _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); (*o)->advance(); } _vm->_draw->blitInvalidated(); _vm->_util->waitEndFrame(); _vm->_util->processInput(); } deinit(); return true; }
reg_t kLock(EngineState *s, int argc, reg_t *argv) { int state = argc > 2 ? argv[2].toUint16() : 1; ResourceType type = g_sci->getResMan()->convertResType(argv[0].toUint16()); ResourceId id = ResourceId(type, argv[1].toUint16()); Resource *which; switch (state) { case 1 : g_sci->getResMan()->findResource(id, 1); break; case 0 : if (id.getNumber() == 0xFFFF) { // Unlock all resources of the requested type Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(type); Common::List<ResourceId>::iterator itr = resources->begin(); while (itr != resources->end()) { Resource *res = g_sci->getResMan()->testResource(*itr); if (res->isLocked()) g_sci->getResMan()->unlockResource(res); ++itr; } delete resources; } else { which = g_sci->getResMan()->findResource(id, 0); if (which) g_sci->getResMan()->unlockResource(which); else { if (id.getType() == kResourceTypeInvalid) warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), argv[0].toUint16()); else // Happens in CD games (e.g. LSL6CD) with the message // resource. It isn't fatal, and it's usually caused // by leftover scripts. debugC(kDebugLevelResMan, "[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str()); } } break; } return s->r_acc; }
Common::Archive *ResLoaderInsMalcolm::load(Common::ArchiveMemberPtr memberFile, Common::SeekableReadStream &stream) const { Common::List<Common::String> filenames; Common::ScopedPtr<PlainArchive> result(new PlainArchive(memberFile)); if (!result) return 0; // thanks to eriktorbjorn for this code (a bit modified though) stream.seek(3, SEEK_SET); // first file is the index table uint32 size = stream.readUint32LE(); Common::String temp; for (uint32 i = 0; i < size; ++i) { byte c = stream.readByte(); if (c == '\\') { temp.clear(); } else if (c == 0x0D) { // line endings are CRLF c = stream.readByte(); assert(c == 0x0A); ++i; filenames.push_back(temp); } else { temp += (char)c; } } stream.seek(3, SEEK_SET); for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) { const uint32 fileSize = stream.readUint32LE(); const uint32 fileOffset = stream.pos(); result->addFileEntry(*file, PlainArchive::Entry(fileOffset, fileSize)); stream.seek(fileSize, SEEK_CUR); } return result.release(); }
bool ScriptInterpreter::interpret(Common::List<ScriptChunk *> &chunks) { bool queued = false; // Create new random variables for the evaluation below _vm->_variables->reRollRandom(); // Push the first script with met conditions into the queue for (Common::List<ScriptChunk *>::iterator it = chunks.begin(); it != chunks.end(); ++it) { if ((*it)->conditionsMet() && _vm->_events->cameFrom((*it)->getFrom())) { (*it)->rewind(); _scripts.push_back(Script(*it)); queued = true; break; } } if (queued) _updatesWithoutChanges = 0; return queued; }
Common::SeekableReadStream *LangFilter::createReadStreamForMember(const Common::String &name) const { if (!_arc) return 0; //Search the right file Common::String fullName; Common::List<Common::String> namesToTry; namesToTry.push_front(kLanguages1[_lang] + name); namesToTry.push_front(kLanguages1[kCommon] + name); namesToTry.push_front(kLanguages2[_lang] + name); namesToTry.push_front(kLanguages2[kCommon] + name); for (Common::List<Common::String>::const_iterator it = namesToTry.begin(); it != namesToTry.end(); it++) if (_arc->hasFile(*it)) { fullName = *it; break; } if (fullName.empty()) return 0; return _arc->createReadStreamForMember(fullName); }
/** * Process to handle changes in the mouse buttons. */ static void MouseProcess(CORO_PARAM, const void *) { // COROUTINE CORO_BEGIN_CONTEXT; bool lastLWasDouble; bool lastRWasDouble; uint32 lastLeftClick, lastRightClick; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); _ctx->lastLWasDouble = false; _ctx->lastRWasDouble = false; _ctx->lastLeftClick = _ctx->lastRightClick = DwGetCurrentTime(); while (true) { if (mouseButtons.empty()) { // allow scheduling CORO_SLEEP(1); continue; } // get next mouse button event Common::EventType type = *mouseButtons.begin(); mouseButtons.erase(mouseButtons.begin()); int xp, yp; GetCursorXYNoWait(&xp, &yp, true); const Common::Point mousePos(xp, yp); switch (type) { case Common::EVENT_LBUTTONDOWN: // left button press if (DwGetCurrentTime() - _ctx->lastLeftClick < (uint32)dclickSpeed) { // Left button double-click if (TinselV2) { // Kill off the button process and fire off the action command g_scheduler->killMatchingProcess(PID_BTN_CLICK, -1); PlayerEvent(PLR_ACTION, clickPos); } else { // signal left drag start ProcessButEvent(PLR_DRAG1_START); // signal left double click event ProcessButEvent(PLR_DLEFT); } _ctx->lastLWasDouble = true; } else { // Initial mouse down - either for a single click, or potentially // the start of a double-click action if (TinselV2) { PlayerEvent(PLR_DRAG1_START, mousePos); ProvNotProcessed(); PlayerEvent(PLR_PROV_WALKTO, mousePos); } else { // signal left drag start ProcessButEvent(PLR_DRAG1_START); // signal left single click event ProcessButEvent(PLR_SLEFT); } _ctx->lastLWasDouble = false; } break; case Common::EVENT_LBUTTONUP: // left button release // update click timer if (_ctx->lastLWasDouble == false) { _ctx->lastLeftClick = DwGetCurrentTime(); // If player control is enabled, start a process which, if it times out, // will activate a single button click if (TinselV2 && ControlIsOn()) { clickPos = mousePos; g_scheduler->createProcess(PID_BTN_CLICK, SingleLeftProcess, NULL, 0); } } else _ctx->lastLeftClick -= dclickSpeed; if (TinselV2) // Signal left drag end PlayerEvent(PLR_DRAG1_END, mousePos); else // signal left drag end ProcessButEvent(PLR_DRAG1_END); break; case Common::EVENT_RBUTTONDOWN: // right button press if (DwGetCurrentTime() - _ctx->lastRightClick < (uint32)dclickSpeed) { // Right button double-click if (TinselV2) { PlayerEvent(PLR_NOEVENT, clickPos); } else { // signal right drag start ProcessButEvent(PLR_DRAG2_START); // signal right double click event ProcessButEvent(PLR_DRIGHT); } _ctx->lastRWasDouble = true; } else { if (TinselV2) { PlayerEvent(PLR_DRAG2_START, mousePos); PlayerEvent(PLR_LOOK, mousePos); } else { // signal right drag start ProcessButEvent(PLR_DRAG2_START); // signal right single click event ProcessButEvent(PLR_SRIGHT); } _ctx->lastRWasDouble = false; } break; case Common::EVENT_RBUTTONUP: // right button release // update click timer if (_ctx->lastRWasDouble == false) _ctx->lastRightClick = DwGetCurrentTime(); else _ctx->lastRightClick -= dclickSpeed; if (TinselV2) // Signal left drag end PlayerEvent(PLR_DRAG2_END, mousePos); else // signal right drag end ProcessButEvent(PLR_DRAG2_END); break; default: break; } } CORO_END_CODE; }
void Actor::walkTo(const Math::Vector3d &p) { if (p == _pos) _walking = false; else { _walking = true; _destPos = p; _path.clear(); if (_constrain) { g_grim->getCurrSet()->findClosestSector(p, NULL, &_destPos); Common::List<PathNode *> openList; Common::List<PathNode *> closedList; PathNode *start = new PathNode; start->parent = NULL; start->pos = _pos; start->dist = 0.f; start->cost = 0.f; openList.push_back(start); g_grim->getCurrSet()->findClosestSector(_pos, &start->sect, NULL); Common::List<Sector *> sectors; for (int i = 0; i < g_grim->getCurrSet()->getSectorCount(); ++i) { Sector *s = g_grim->getCurrSet()->getSectorBase(i); int type = s->getType(); if ((type == Sector::WalkType || type == Sector::HotType || type == Sector::FunnelType) && s->isVisible()) { sectors.push_back(s); } } Sector *endSec = NULL; g_grim->getCurrSet()->findClosestSector(_destPos, &endSec, NULL); do { PathNode *node = NULL; float cost = -1.f; for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) { PathNode *n = *j; float c = n->dist + n->cost; if (cost < 0.f || c < cost) { cost = c; node = n; } } closedList.push_back(node); openList.remove(node); Sector *sector = node->sect; if (sector == endSec) { PathNode *n = closedList.back(); // Don't put the start position in the list, or else // the first angle calculated in updateWalk() will be // meaningless. The only node without parent is the start // one. while (n->parent) { _path.push_back(n->pos); n = n->parent; } break; } for (Common::List<Sector *>::iterator i = sectors.begin(); i != sectors.end(); ++i) { Sector *s = *i; bool inClosed = false; for (Common::List<PathNode *>::iterator j = closedList.begin(); j != closedList.end(); ++j) { if ((*j)->sect == s) { inClosed = true; break; } } if (inClosed) continue; Common::List<Math::Line3d> bridges = sector->getBridgesTo(s); if (bridges.empty()) continue; // The sectors are not adjacent. Math::Vector3d closestPoint = s->getClosestPoint(_destPos); Math::Vector3d best; float bestDist = 1e6f; Math::Line3d l(node->pos, closestPoint); while (!bridges.empty()) { Math::Line3d bridge = bridges.back(); Math::Vector3d pos; const bool useXZ = (g_grim->getGameType() == GType_MONKEY4); if (!bridge.intersectLine2d(l, &pos, useXZ)) { pos = bridge.middle(); } float dist = (pos - closestPoint).getMagnitude(); if (dist < bestDist) { bestDist = dist; best = pos; } bridges.pop_back(); } best = handleCollisionTo(node->pos, best); PathNode *n = NULL; for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) { if ((*j)->sect == s) { n = *j; break; } } if (n) { float newCost = node->cost + (best - node->pos).getMagnitude(); if (newCost < n->cost) { n->cost = newCost; n->parent = node; n->pos = best; n->dist = (n->pos - _destPos).getMagnitude(); } } else { n = new PathNode; n->parent = node; n->sect = s; n->pos = best; n->dist = (n->pos - _destPos).getMagnitude(); n->cost = node->cost + (n->pos - node->pos).getMagnitude(); openList.push_back(n); } } } while (!openList.empty()); for (Common::List<PathNode *>::iterator j = closedList.begin(); j != closedList.end(); ++j) { delete *j; } for (Common::List<PathNode *>::iterator j = openList.begin(); j != openList.end(); ++j) { delete *j; } } _path.push_front(_destPos); } }
Common::List<Math::Line3d> Sector::getBridgesTo(Sector *sector) const { // This returns a list of "bridges", which are edges that can be travelled // through to get to another sector. 0 bridges mean the sectors aren't // connected. // The algorithm starts by considering all the edges of sector A // bridges. It then narrows them down by cutting the bridges against // sector B, so we end up with a list of lines which are at the border // of sector A and inside sector B. Common::List<Math::Line3d> bridges; Common::List<Math::Line3d>::iterator it; for (int i = 0; i < _numVertices; i++) { bridges.push_back(Math::Line3d(_vertices[i], _vertices[i + 1])); } Math::Vector3d *sectorVertices = sector->getVertices(); for (int i = 0; i < sector->getNumVertices(); i++) { Math::Vector3d pos, edge, delta_b1, delta_b2; Math::Line3d line(sectorVertices[i], sectorVertices[i + 1]); it = bridges.begin(); while (it != bridges.end()) { Math::Line3d &bridge = (*it); edge = line.end() - line.begin(); delta_b1 = bridge.begin() - line.begin(); delta_b2 = bridge.end() - line.begin(); Math::Vector3d cross_b1 = Math::Vector3d::crossProduct(edge, delta_b1); Math::Vector3d cross_b2 = Math::Vector3d::crossProduct(edge, delta_b2); bool b1_out = cross_b1.dotProduct(_normal) < 0; bool b2_out = cross_b2.dotProduct(_normal) < 0; bool useXZ = (g_grim->getGameType() == GType_MONKEY4); if (b1_out && b2_out) { // Both points are outside. it = bridges.erase(it); continue; } else if (b1_out) { if (bridge.intersectLine2d(line, &pos, useXZ)) { bridge = Math::Line3d(pos, bridge.end()); } } else if (b2_out) { if (bridge.intersectLine2d(line, &pos, useXZ)) { bridge = Math::Line3d(bridge.begin(), pos); } } ++it; } } // All the bridges should be at the same height on both sectors. it = bridges.begin(); while (it != bridges.end()) { if (g_grim->getGameType() == GType_MONKEY4) { // Set pac contains sectors which are not parallel to any // other sector or share any edge. Since one sector isn't // a plane, finding the intersections in 3D would be complicated. // // Checking for bridges using a projection in 2D and having a height // threshold to avoid that characters jump from lower to higher floors // seems to be a good compromise. // // The value of at least 0.1 was chosen to fix a path finding issue // in set pac when guybrush tried to reach the pile of rocks. if (fabs(getProjectionToPlane((*it).begin()).y() - sector->getProjectionToPlane((*it).begin()).y()) > 0.1f || fabs(getProjectionToPlane((*it).end()).y() - sector->getProjectionToPlane((*it).end()).y()) > 0.1f) { it = bridges.erase(it); continue; } } else { if (fabs(getProjectionToPlane((*it).begin()).z() - sector->getProjectionToPlane((*it).begin()).z()) > 0.01f || fabs(getProjectionToPlane((*it).end()).z() - sector->getProjectionToPlane((*it).end()).z()) > 0.01f) { it = bridges.erase(it); continue; } } ++it; } return bridges; }
Common::List<Math::Line3d> Sector::getBridgesTo(Sector *sector) const { // This returns a list of "bridges", which are edges that can be travelled // through to get to another sector. 0 bridges mean the sectors aren't // connected. // The algorithm starts by considering all the edges of sector A // bridges. It then narrows them down by cutting the bridges against // sector B, so we end up with a list of lines which are at the border // of sector A and inside sector B. Common::List<Math::Line3d> bridges; Common::List<Math::Line3d>::iterator it; for (int i = 0; i < _numVertices; i++){ bridges.push_back(Math::Line3d(_vertices[i], _vertices[i+1])); } Math::Vector3d* sectorVertices = sector->getVertices(); for (int i = 0; i < sector->getNumVertices(); i++) { Math::Vector3d pos, edge, delta_b1, delta_b2; Math::Line3d line(sectorVertices[i], sectorVertices[i+1]); it = bridges.begin(); while (it != bridges.end()) { Math::Line3d& bridge = (*it); edge = line.end() - line.begin(); delta_b1 = bridge.begin() - line.begin(); delta_b2 = bridge.end() - line.begin(); bool b1_out = edge.x() * delta_b1.y() < edge.y() * delta_b1.x(); bool b2_out = edge.x() * delta_b2.y() < edge.y() * delta_b2.x(); if (b1_out && b2_out) { // Both points are outside. it = bridges.erase(it); continue; } else if (b1_out) { if (bridge.intersectLine2d(line, &pos)) { bridge = Math::Line3d(pos, bridge.end()); } } else if (b2_out) { if (bridge.intersectLine2d(line, &pos)) { bridge = Math::Line3d(bridge.begin(), pos); } } if ((bridge.end() - bridge.begin()).getMagnitude() < 0.01f) { it = bridges.erase(it); continue; } ++it; } } // All the bridges should be at the same height on both sectors. while (it != bridges.end()) { if (fabs(getProjectionToPlane((*it).begin()).z() - sector->getProjectionToPlane((*it).begin()).z()) > 0.01f || fabs(getProjectionToPlane((*it).end()).z() - sector->getProjectionToPlane((*it).end()).z()) > 0.01f) { it = bridges.erase(it); continue; } ++it; } return bridges; }
OSystem::TransactionError OpenGLGraphicsManager::endGFXTransaction() { assert(_transactionMode == kTransactionActive); uint transactionError = OSystem::kTransactionSuccess; bool setupNewGameScreen = false; if ( _oldState.gameWidth != _currentState.gameWidth || _oldState.gameHeight != _currentState.gameHeight) { setupNewGameScreen = true; } #ifdef USE_RGB_COLOR if (_oldState.gameFormat != _currentState.gameFormat) { setupNewGameScreen = true; } // Check whether the requested format can actually be used. Common::List<Graphics::PixelFormat> supportedFormats = getSupportedFormats(); // In case the requested format is not usable we will fall back to CLUT8. if (Common::find(supportedFormats.begin(), supportedFormats.end(), _currentState.gameFormat) == supportedFormats.end()) { _currentState.gameFormat = Graphics::PixelFormat::createFormatCLUT8(); transactionError |= OSystem::kTransactionFormatNotSupported; } #endif do { uint requestedWidth = _currentState.gameWidth; uint requestedHeight = _currentState.gameHeight; const uint desiredAspect = getDesiredGameScreenAspect(); requestedHeight = intToFrac(requestedWidth) / desiredAspect; if (!loadVideoMode(requestedWidth, requestedHeight, #ifdef USE_RGB_COLOR _currentState.gameFormat #else Graphics::PixelFormat::createFormatCLUT8() #endif ) // HACK: This is really nasty but we don't have any guarantees of // a context existing before, which means we don't know the maximum // supported texture size before this. Thus, we check whether the // requested game resolution is supported over here. || ( _currentState.gameWidth > (uint)Texture::getMaximumTextureSize() || _currentState.gameHeight > (uint)Texture::getMaximumTextureSize())) { if (_transactionMode == kTransactionActive) { // Try to setup the old state in case its valid and is // actually different from the new one. if (_oldState.valid && _oldState != _currentState) { // Give some hints on what failed to set up. if ( _oldState.gameWidth != _currentState.gameWidth || _oldState.gameHeight != _currentState.gameHeight) { transactionError |= OSystem::kTransactionSizeChangeFailed; } #ifdef USE_RGB_COLOR if (_oldState.gameFormat != _currentState.gameFormat) { transactionError |= OSystem::kTransactionFormatNotSupported; } #endif if (_oldState.aspectRatioCorrection != _currentState.aspectRatioCorrection) { transactionError |= OSystem::kTransactionAspectRatioFailed; } if (_oldState.graphicsMode != _currentState.graphicsMode) { transactionError |= OSystem::kTransactionModeSwitchFailed; } // Roll back to the old state. _currentState = _oldState; _transactionMode = kTransactionRollback; // Try to set up the old state. continue; } } // DON'T use error(), as this tries to bring up the debug // console, which WON'T WORK now that we might no have a // proper screen. warning("OpenGLGraphicsManager::endGFXTransaction: Could not load any graphics mode!"); g_system->quit(); } // In case we reach this we have a valid state, yay. _transactionMode = kTransactionNone; _currentState.valid = true; } while (_transactionMode == kTransactionRollback); if (setupNewGameScreen) { delete _gameScreen; _gameScreen = nullptr; #ifdef USE_RGB_COLOR _gameScreen = createTexture(_currentState.gameFormat); #else _gameScreen = createTexture(Graphics::PixelFormat::createFormatCLUT8()); #endif assert(_gameScreen); if (_gameScreen->hasPalette()) { _gameScreen->setPalette(0, 256, _gamePalette); } _gameScreen->allocate(_currentState.gameWidth, _currentState.gameHeight); _gameScreen->enableLinearFiltering(_currentState.graphicsMode == GFX_LINEAR); // We fill the screen to all black or index 0 for CLUT8. if (_currentState.gameFormat.bytesPerPixel == 1) { _gameScreen->fill(0); } else { _gameScreen->fill(_gameScreen->getSurface()->format.RGBToColor(0, 0, 0)); } } // Update our display area and cursor scaling. This makes sure we pick up // aspect ratio correction and game screen changes correctly. recalculateDisplayArea(); recalculateCursorScaling(); // Something changed, so update the screen change ID. ++_screenChangeID; // Since transactionError is a ORd list of TransactionErrors this is // clearly wrong. But our API is simply broken. return (OSystem::TransactionError)transactionError; }
reg_t kLock(EngineState *s, int argc, reg_t *argv) { // NOTE: In SSCI, kLock uses a boolean lock flag, not a lock counter. // ScummVM's current counter-based implementation should be better than SSCI // at dealing with game scripts that unintentionally lock & unlock the same // resource multiple times (e.g. through recursion), but it will introduce // memory bugs (resource leaks lasting until the engine is restarted, or // destruction of kernel locks that lead to a use-after-free) that are // masked by ResourceManager's LRU cache if scripts rely on kLock being // idempotent like it was in SSCI. // // Like SSCI, resource locks are not persisted in save games in ScummVM // until GK2, so it is also possible that kLock bugs will appear only after // restoring a save game. // // See also kUnLoad. ResourceType type = g_sci->getResMan()->convertResType(argv[0].toUint16()); if (type == kResourceTypeSound && getSciVersion() >= SCI_VERSION_1_1) { type = g_sci->_soundCmd->getSoundResourceType(argv[1].toUint16()); } const ResourceId id(type, argv[1].toUint16()); const bool lock = argc > 2 ? argv[2].toUint16() : true; #ifdef ENABLE_SCI32 // SSCI GK2+SCI3 also saves lock states for View, Pic, and Sync resources, // but so far it seems like audio resources are the only ones that actually // need to be handled if (g_sci->_features->hasSci3Audio() && type == kResourceTypeAudio) { g_sci->_audio32->lockResource(id, lock); return s->r_acc; } #endif if (getSciVersion() == SCI_VERSION_1_1 && (type == kResourceTypeAudio36 || type == kResourceTypeSync36)) { return s->r_acc; } if (lock) { g_sci->getResMan()->findResource(id, true); } else { if (getSciVersion() < SCI_VERSION_2 && id.getNumber() == 0xFFFF) { // Unlock all resources of the requested type Common::List<ResourceId> resources = g_sci->getResMan()->listResources(type); Common::List<ResourceId>::iterator itr; for (itr = resources.begin(); itr != resources.end(); ++itr) { Resource *res = g_sci->getResMan()->testResource(*itr); if (res->isLocked()) g_sci->getResMan()->unlockResource(res); } } else { Resource *which = g_sci->getResMan()->findResource(id, false); if (which) g_sci->getResMan()->unlockResource(which); else { if (id.getType() == kResourceTypeInvalid) warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), argv[0].toUint16()); else // Happens in CD games (e.g. LSL6CD) with the message // resource. It isn't fatal, and it's usually caused // by leftover scripts. debugC(kDebugLevelResMan, "[resMan] Attempt to unlock non-existent resource %s", id.toString().c_str()); } } } return s->r_acc; }
/** * Create a ScummEngine instance, based on the given detector data. * * This is heavily based on our MD5 detection scheme. */ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) const { assert(syst); assert(engine); const char *gameid = ConfMan.get("gameid").c_str(); // We start by checking whether the specified game ID is obsolete. // If that is the case, we automatically upgrade the target to use // the correct new game ID (and platform, if specified). for (const ADObsoleteGameID *o = obsoleteGameIDsTable; o->from; ++o) { if (!scumm_stricmp(gameid, o->from)) { // Match found, perform upgrade gameid = o->to; ConfMan.set("gameid", o->to); if (o->platform != Common::kPlatformUnknown) ConfMan.set("platform", Common::getPlatformCode(o->platform)); warning("Target upgraded from game ID %s to %s", o->from, o->to); ConfMan.flushToDisk(); break; } } // Fetch the list of files in the current directory Common::FSList fslist; Common::FSNode dir(ConfMan.get("path")); if (!dir.isDirectory()) return Common::kInvalidPathError; if (!dir.getChildren(fslist, Common::FSNode::kListFilesOnly)) return Common::kNoGameDataFoundError; // Invoke the detector, but fixed to the specified gameid. Common::List<DetectorResult> results; ::detectGames(fslist, results, gameid); // Unable to locate game data if (results.empty()) return Common::kNoGameDataFoundError; // No unique match found. If a platform override is present, try to // narrow down the list a bit more. if (results.size() > 1 && ConfMan.hasKey("platform")) { Common::Platform platform = Common::parsePlatform(ConfMan.get("platform")); Common::List<DetectorResult> tmp; // Copy only those candidates which match the platform setting for (Common::List<DetectorResult>::iterator x = results.begin(); x != results.end(); ++x) { if (x->game.platform == platform) { tmp.push_back(*x); } } // If we narrowed it down too much, print a warning, else use the list // we just computed as new candidates list. if (tmp.empty()) { warning("Engine_SCUMM_create: Game data inconsistent with platform override"); } else { results = tmp; } } // Still no unique match found -> print a warning if (results.size() > 1) warning("Engine_SCUMM_create: No unique game candidate found, using first one"); // Simply use the first match DetectorResult res(*(results.begin())); debug(1, "Using gameid %s, variant %s, extra %s", res.game.gameid, res.game.variant, res.extra); // Print the MD5 of the game; either verbose using printf, in case of an // unknown MD5, or with a medium debug level in case of a known MD5 (for // debugging purposes). if (!findInMD5Table(res.md5.c_str())) { printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n"); printf("version (in particular, not a fan-made translation), please, report the\n"); printf("following data to the ScummVM team along with name of the game you tried\n"); printf("to add and its version/language/etc.:\n"); printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, generateFilenameForDetection(res.fp.pattern, res.fp.genMethod).c_str(), res.md5.c_str()); } else { debug(1, "Using MD5 '%s'", res.md5.c_str()); } // If the GUI options were updated, we catch this here and update them in the users config // file transparently. Common::updateGameGUIOptions(res.game.guioptions, getGameGUIOptionsDescriptionLanguage(res.language)); // Check for a user override of the platform. We allow the user to override // the platform, to make it possible to add games which are not yet in // our MD5 database but require a specific platform setting. // TODO: Do we really still need / want the platform override ? if (ConfMan.hasKey("platform")) res.game.platform = Common::parsePlatform(ConfMan.get("platform")); // Language override if (ConfMan.hasKey("language")) res.language = Common::parseLanguage(ConfMan.get("language")); // V3 FM-TOWNS games *always* should use the corresponding music driver, // anything else makes no sense for them. // TODO: Maybe allow the null driver, too? if (res.game.platform == Common::kPlatformFMTowns && res.game.version == 3) res.game.midi = MDT_TOWNS; // Finally, we have massaged the GameDescriptor to our satisfaction, and can // instantiate the appropriate game engine. Hooray! switch (res.game.version) { case 0: *engine = new ScummEngine_v0(syst, res); break; case 1: case 2: *engine = new ScummEngine_v2(syst, res); break; case 3: if (res.game.features & GF_OLD256) *engine = new ScummEngine_v3(syst, res); else *engine = new ScummEngine_v3old(syst, res); break; case 4: *engine = new ScummEngine_v4(syst, res); break; case 5: *engine = new ScummEngine_v5(syst, res); break; case 6: switch (res.game.heversion) { #ifdef ENABLE_HE case 200: *engine = new ScummEngine_vCUPhe(syst, res); break; case 100: *engine = new ScummEngine_v100he(syst, res); break; case 99: *engine = new ScummEngine_v99he(syst, res); break; case 98: case 95: case 90: *engine = new ScummEngine_v90he(syst, res); break; case 85: case 80: *engine = new ScummEngine_v80he(syst, res); break; case 74: case 73: case 72: *engine = new ScummEngine_v72he(syst, res); break; case 71: *engine = new ScummEngine_v71he(syst, res); break; #endif case 70: *engine = new ScummEngine_v70he(syst, res); break; case 62: case 61: *engine = new ScummEngine_v60he(syst, res); break; default: *engine = new ScummEngine_v6(syst, res); } break; #ifdef ENABLE_SCUMM_7_8 case 7: *engine = new ScummEngine_v7(syst, res); break; case 8: *engine = new ScummEngine_v8(syst, res); break; #endif default: error("Engine_SCUMM_create(): Unknown version of game engine"); } return Common::kNoError; }
inline bool contains(const Common::List<T> &l, const T &v) { return (Common::find(l.begin(), l.end(), v) != l.end()); }
/** * Process to handle keypresses */ void KeyboardProcess(CORO_PARAM, const void *) { // COROUTINE CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); while (true) { if (keypresses.empty()) { // allow scheduling CORO_SLEEP(1); continue; } // Get the next keyboard event off the stack Common::Event evt = *keypresses.begin(); keypresses.erase(keypresses.begin()); const Common::Point mousePos = _vm->getMousePosition(); // Switch for special keys switch (evt.kbd.keycode) { // Drag action case Common::KEYCODE_LALT: case Common::KEYCODE_RALT: if (evt.type == Common::EVENT_KEYDOWN) { if (!bSwapButtons) ProcessButEvent(PLR_DRAG2_START); else ProcessButEvent(PLR_DRAG1_START); } else { if (!bSwapButtons) ProcessButEvent(PLR_DRAG1_END); else ProcessButEvent(PLR_DRAG2_END); } continue; case Common::KEYCODE_LCTRL: case Common::KEYCODE_RCTRL: if (evt.type == Common::EVENT_KEYDOWN) { ProcessKeyEvent(PLR_LOOK); } else { // Control key release } continue; default: break; } // At this point only key down events need processing if (evt.type == Common::EVENT_KEYUP) continue; if (_vm->_keyHandler != NULL) // Keyboard is hooked, so pass it on to that handler first if (!_vm->_keyHandler(evt.kbd)) continue; switch (evt.kbd.keycode) { /*** SPACE = WALKTO ***/ case Common::KEYCODE_SPACE: ProcessKeyEvent(PLR_WALKTO); continue; /*** RETURN = ACTION ***/ case Common::KEYCODE_RETURN: case Common::KEYCODE_KP_ENTER: ProcessKeyEvent(PLR_ACTION); continue; /*** l = LOOK ***/ case Common::KEYCODE_l: // LOOK ProcessKeyEvent(PLR_LOOK); continue; case Common::KEYCODE_ESCAPE: ProcessKeyEvent(PLR_ESCAPE); continue; #ifdef SLOW_RINCE_DOWN case '>': AddInterlude(1); continue; case '<': AddInterlude(-1); continue; #endif case Common::KEYCODE_F1: // Options dialog ProcessKeyEvent(PLR_MENU); continue; case Common::KEYCODE_F5: // Save game ProcessKeyEvent(PLR_SAVE); continue; case Common::KEYCODE_F7: // Load game ProcessKeyEvent(PLR_LOAD); continue; case Common::KEYCODE_m: // Debug facility - scene hopper if (TinselV2 && (evt.kbd.flags == Common::KBD_ALT)) ProcessKeyEvent(PLR_JUMP); break; case Common::KEYCODE_q: if ((evt.kbd.flags == Common::KBD_CTRL) || (evt.kbd.flags == Common::KBD_ALT)) ProcessKeyEvent(PLR_QUIT); continue; case Common::KEYCODE_PAGEUP: case Common::KEYCODE_KP9: ProcessKeyEvent(PLR_PGUP); continue; case Common::KEYCODE_PAGEDOWN: case Common::KEYCODE_KP3: ProcessKeyEvent(PLR_PGDN); continue; case Common::KEYCODE_HOME: case Common::KEYCODE_KP7: ProcessKeyEvent(PLR_HOME); continue; case Common::KEYCODE_END: case Common::KEYCODE_KP1: ProcessKeyEvent(PLR_END); continue; default: ProcessKeyEvent(PLR_NOEVENT); break; } } CORO_END_CODE; }
bool isExtensionSupported(const char *wanted) { return g_extensions.end() != find(g_extensions.begin(), g_extensions.end(), wanted); }