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; }
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; }