bool LockerImpl::saveLockStateAndUnlock(Locker::LockSnapshot* stateOut) { // We shouldn't be saving and restoring lock state from inside a WriteUnitOfWork. invariant(!inAWriteUnitOfWork()); // Clear out whatever is in stateOut. stateOut->locks.clear(); stateOut->globalMode = MODE_NONE; // First, we look at the global lock. There is special handling for this (as the flush // lock goes along with it) so we store it separately from the more pedestrian locks. LockRequestsMap::Iterator globalRequest = _requests.find(resourceIdGlobal); if (!globalRequest) { // If there's no global lock there isn't really anything to do. Check that. for (auto it = _requests.begin(); !it.finished(); it.next()) { invariant(it.key().getType() == RESOURCE_MUTEX); } return false; } // If the global lock or RSTL has been acquired more than once, we're probably somewhere in a // DBDirectClient call. It's not safe to release and reacquire locks -- the context using // the DBDirectClient is probably not prepared for lock release. LockRequestsMap::Iterator rstlRequest = _requests.find(resourceIdReplicationStateTransitionLock); if (globalRequest->recursiveCount > 1 || (rstlRequest && rstlRequest->recursiveCount > 1)) { return false; } // The global lock must have been acquired just once stateOut->globalMode = globalRequest->mode; invariant(unlock(resourceIdGlobal)); // Next, the non-global locks. for (LockRequestsMap::Iterator it = _requests.begin(); !it.finished(); it.next()) { const ResourceId resId = it.key(); const ResourceType resType = resId.getType(); if (resType == RESOURCE_MUTEX) continue; // We should never have to save and restore metadata locks. invariant(RESOURCE_DATABASE == resId.getType() || RESOURCE_COLLECTION == resId.getType() || (RESOURCE_GLOBAL == resId.getType() && isSharedLockMode(it->mode)) || (resourceIdReplicationStateTransitionLock == resId && it->mode == MODE_IX)); // And, stuff the info into the out parameter. OneLock info; info.resourceId = resId; info.mode = it->mode; stateOut->locks.push_back(info); invariant(unlock(resId)); } invariant(!isLocked()); // Sort locks by ResourceId. They'll later be acquired in this canonical locking order. std::sort(stateOut->locks.begin(), stateOut->locks.end()); return true; }
DeadlockDetector::DeadlockDetector(const LockManager& lockMgr, const Locker* initialLocker) : _lockMgr(lockMgr), _initialLockerId(initialLocker->getId()), _foundCycle(false) { const ResourceId resId = initialLocker->getWaitingResource(); // If there is no resource waiting there is nothing to do if (resId.isValid()) { _queue.push_front(UnprocessedNode(_initialLockerId, resId)); } }
Resource ResourceLoader::get(const ResourceId &id) { LOG_TRACE(m_log, '\'' << id.toString() << '\''); Resource result; // Find existing resources with the identifier if (m_pCache) { result = m_pCache->find(id); } // Haven't found a resource if (!result) { // Try to retrieve the resource result = ResourceLoaderBase::get(id); // Cache the retrieved resource if (m_pCache && result) m_pCache->add(result); } return result; }
bool LockerImpl<IsForMMAPV1>::_shouldDelayUnlock(ResourceId resId, LockMode mode) const { switch (resId.getType()) { // The flush lock must not participate in two-phase locking because we need to temporarily // yield it while blocked waiting to acquire other locks. case RESOURCE_MMAPV1_FLUSH: case RESOURCE_MUTEX: return false; case RESOURCE_GLOBAL: case RESOURCE_DATABASE: case RESOURCE_COLLECTION: case RESOURCE_METADATA: break; default: MONGO_UNREACHABLE; } switch (mode) { case MODE_X: case MODE_IX: return true; case MODE_IS: case MODE_S: return _sharedLocksShouldTwoPhaseLock; default: MONGO_UNREACHABLE; } }
Resource FileResourceLoader::get(const ResourceId &id) { if (id.getType() & "File") { fs::path p((m_basePath / id.getName()).native_directory_string()); if (fs::exists(p)) { FileData *pData = new FileData(this); pData->setId(id); pData->setPath(p); return FileResource(pData); } } return Resource(); }
Dialog::Dialog(ResourceId dialogId, Widget* parent) : Frame(::CreateDialog(Application::getHandle(), dialogId.toLPTSTR(), parent ? parent->getHandle() : NULL, Dialog::globalDlgProc)) { m_state = false; }
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; }
LockResult LockerImpl<IsForMMAPV1>::lockBegin(OperationContext* opCtx, ResourceId resId, LockMode mode) { dassert(!getWaitingResource().isValid()); LockRequest* request; bool isNew = true; LockRequestsMap::Iterator it = _requests.find(resId); if (!it) { scoped_spinlock scopedLock(_lock); LockRequestsMap::Iterator itNew = _requests.insert(resId); itNew->initNew(this, &_notify); request = itNew.objAddr(); } else { request = it.objAddr(); isNew = false; } // If unlockPending is nonzero, that means a LockRequest already exists for this resource but // is planned to be released at the end of this WUOW due to two-phase locking. Rather than // unlocking the existing request, we can reuse it if the existing mode matches the new mode. if (request->unlockPending && isModeCovered(mode, request->mode)) { request->unlockPending--; if (!request->unlockPending) { _numResourcesToUnlockAtEndUnitOfWork--; } return LOCK_OK; } // Making this call here will record lock re-acquisitions and conversions as well. globalStats.recordAcquisition(_id, resId, mode); _stats.recordAcquisition(resId, mode); // Give priority to the full modes for global, parallel batch writer mode, // and flush lock so we don't stall global operations such as shutdown or flush. const ResourceType resType = resId.getType(); if (resType == RESOURCE_GLOBAL || (IsForMMAPV1 && resId == resourceIdMMAPV1Flush)) { if (mode == MODE_S || mode == MODE_X) { request->enqueueAtFront = true; request->compatibleFirst = true; } } else if (resType != RESOURCE_MUTEX) { // This is all sanity checks that the global and flush locks are always be acquired // before any other lock has been acquired and they must be in sync with the nesting. DEV { const LockRequestsMap::Iterator itGlobal = _requests.find(resourceIdGlobal); invariant(itGlobal->recursiveCount > 0); invariant(itGlobal->mode != MODE_NONE); // Check the MMAP V1 flush lock is held in the appropriate mode invariant(!IsForMMAPV1 || isLockHeldForMode(resourceIdMMAPV1Flush, _getModeForMMAPV1FlushLock())); }; }
static void Main() { Bitmap bitmap; bool success = #ifdef USE_GDI id.IsDefined() ? bitmap.Load(id) : #endif bitmap.LoadFile(path); if (!success) fprintf(stderr, "Failed to load image\n"); }
bool Bitmap::Load(ResourceId _id, Type _type) { assert(_id.IsDefined()); Reset(); id = _id; type = _type; AddSurfaceListener(*this); if (!surface_valid) return true; return Reload(); }
void ResourceManager::removeAudioResource(ResourceId resId) { // Remove resource, unless it was loaded from a patch if (_resMap.contains(resId)) { Resource *res = _resMap.getVal(resId); if (res->_source->getSourceType() == kSourceAudioVolume) { if (res->_status == kResStatusLocked) { warning("Failed to remove resource %s (still in use)", resId.toString().c_str()); } else { if (res->_status == kResStatusEnqueued) removeFromLRU(res); _resMap.erase(resId); delete res; } } } }
LockResult LockerImpl<IsForMMAPV1>::lockBegin(ResourceId resId, LockMode mode) { dassert(!getWaitingResource().isValid()); LockRequest* request; bool isNew = true; LockRequestsMap::Iterator it = _requests.find(resId); if (!it) { scoped_spinlock scopedLock(_lock); LockRequestsMap::Iterator itNew = _requests.insert(resId); itNew->initNew(this, &_notify); request = itNew.objAddr(); } else { request = it.objAddr(); isNew = false; } // Making this call here will record lock re-acquisitions and conversions as well. globalStats.recordAcquisition(_id, resId, mode); _stats.recordAcquisition(resId, mode); // Give priority to the full modes for global, parallel batch writer mode, // and flush lock so we don't stall global operations such as shutdown or flush. const ResourceType resType = resId.getType(); if (resType == RESOURCE_GLOBAL || (IsForMMAPV1 && resId == resourceIdMMAPV1Flush)) { if (mode == MODE_S || mode == MODE_X) { request->enqueueAtFront = true; request->compatibleFirst = true; } } else if (resType != RESOURCE_MUTEX) { // This is all sanity checks that the global and flush locks are always be acquired // before any other lock has been acquired and they must be in sync with the nesting. DEV { const LockRequestsMap::Iterator itGlobal = _requests.find(resourceIdGlobal); invariant(itGlobal->recursiveCount > 0); invariant(itGlobal->mode != MODE_NONE); // Check the MMAP V1 flush lock is held in the appropriate mode invariant(!IsForMMAPV1 || isLockHeldForMode(resourceIdMMAPV1Flush, _getModeForMMAPV1FlushLock())); }; }
// ---------------------------------------------------------------- // LoadResource // ---------------------------------------------------------------- bool CD2DBitmap::LoadResource( ResourceId id ) { ID2D1HwndRenderTarget * renderTarget = CD2DRenderer::GetInstance().GetHwndRenderTarget(); IWICImagingFactory * imagingFactory = CD2DRenderer::GetInstance().GetImagingFactory(); IWICBitmapDecoder * bitmapDecoder = nullptr; IWICBitmapFrameDecode* bitmapFrameDecode = nullptr; imagingFactory->CreateDecoderFromFilename( id.c_str(), nullptr, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &bitmapDecoder ); bitmapDecoder->GetFrame( 0, &bitmapFrameDecode ); imagingFactory->CreateFormatConverter( &m_FmtConverter ); m_FmtConverter->Initialize( bitmapFrameDecode, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nullptr, 0.0f, WICBitmapPaletteTypeCustom ); renderTarget->CreateBitmapFromWicBitmap( m_FmtConverter, nullptr, &m_D2DBitmap ); SafeRelease( bitmapDecoder ); SafeRelease( bitmapFrameDecode ); return true; }
void MaskedIcon::LoadResource(ResourceId id, ResourceId big_id, bool center) { if (Layout::ScaleEnabled()) { if (big_id.IsDefined()) bitmap.Load(big_id); else bitmap.LoadStretch(id, Layout::FastScale(1)); } else bitmap.Load(id); #ifdef ENABLE_OPENGL /* postpone CalculateLayout() call, because the OpenGL surface may be absent now */ size.cx = 0; size.cy = center; #else assert(IsDefined()); CalculateLayout(center); #endif }
bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId& resId, const SourceLine& source, const char16_t* validChars) { if (!name.package.empty() && name.package != mPackage) { Logger::error(source) << "resource '" << name << "' has incompatible package. Must be '" << mPackage << "'." << std::endl; return false; } auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); if (badCharIter != name.entry.end()) { Logger::error(source) << "resource '" << name << "' has invalid entry name '" << name.entry << "'. Invalid character '" << StringPiece16(badCharIter, 1) << "'." << std::endl; return false; } std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && type->typeId != resId.typeId()) { Logger::error(source) << "trying to make resource '" << name << "' public with ID " << resId << " but type '" << type->type << "' already has ID " << std::hex << type->typeId << std::dec << "." << std::endl; return false; } std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && entry->entryId != resId.entryId()) { Logger::error(source) << "trying to make resource '" << name << "' public with ID " << resId << " but resource already has ID " << ResourceId(mPackageId, type->typeId, entry->entryId) << "." << std::endl; return false; } type->publicStatus.isPublic = true; entry->publicStatus.isPublic = true; entry->publicStatus.source = source; if (resId.isValid()) { type->typeId = resId.typeId(); entry->entryId = resId.entryId(); } return true; }
bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId& resId, const ConfigDescription& config, const SourceLine& source, std::unique_ptr<Value> value, const char16_t* validChars) { if (!name.package.empty() && name.package != mPackage) { Logger::error(source) << "resource '" << name << "' has incompatible package. Must be '" << mPackage << "'." << std::endl; return false; } auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); if (badCharIter != name.entry.end()) { Logger::error(source) << "resource '" << name << "' has invalid entry name '" << name.entry << "'. Invalid character '" << StringPiece16(badCharIter, 1) << "'." << std::endl; return false; } std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && type->typeId != resId.typeId()) { Logger::error(source) << "trying to add resource '" << name << "' with ID " << resId << " but type '" << type->type << "' already has ID " << std::hex << type->typeId << std::dec << "." << std::endl; return false; } std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && entry->entryId != resId.entryId()) { Logger::error(source) << "trying to add resource '" << name << "' with ID " << resId << " but resource already has ID " << ResourceId(mPackageId, type->typeId, entry->entryId) << "." << std::endl; return false; } const auto endIter = std::end(entry->values); auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs); if (iter == endIter || iter->config != config) { // This resource did not exist before, add it. entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) }); } else { int collisionResult = defaultCollisionHandler(*iter->value, *value); if (collisionResult > 0) { // Take the incoming value. *iter = ResourceConfigValue{ config, source, {}, std::move(value) }; } else if (collisionResult == 0) { Logger::error(source) << "duplicate value for resource '" << name << "' " << "with config '" << iter->config << "'." << std::endl; Logger::error(iter->source) << "resource previously defined here." << std::endl; return false; } } if (resId.isValid()) { type->typeId = resId.typeId(); entry->entryId = resId.entryId(); } return true; }
void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) { _position = position; // Now init audio and sync resource uint32 audioNumber = ((noun & 0xff) << 24) | ((verb & 0xff) << 16) | ((cond & 0xff) << 8) | (seq & 0xff); #ifndef DEBUG_PORTRAIT_USE_SYNC_RESOURCES ResourceId raveResourceId = ResourceId(kResourceTypeRave, resourceId, noun, verb, cond, seq); Resource *raveResource = _resMan->findResource(raveResourceId, true); uint raveOffset = 0; #else ResourceId syncResourceId = ResourceId(kResourceTypeSync36, resourceId, noun, verb, cond, seq); Resource *syncResource = _resMan->findResource(syncResourceId, true); uint syncOffset = 0; #endif #ifdef DEBUG_PORTRAIT // prints out the current lip sync ASCII data char debugPrint[4000]; if (raveResource->size < 4000) { memcpy(debugPrint, raveResource->data, raveResource->size); debugPrint[raveResource->size] = 0; // set terminating NUL debug("kPortrait: using actor %s", _resourceName.c_str()); debug("kPortrait (noun %d, verb %d, cond %d, seq %d)", noun, verb, cond, seq); debug("kPortrait: rave data is '%s'", debugPrint); } #endif // TODO: maybe try to create the missing sync resources for low-res KQ6 out of the rave resources #ifdef DEBUG_PORTRAIT_USE_SYNC_RESOURCES // Dump the sync resources to disk Common::DumpFile *outFile = new Common::DumpFile(); Common::String outName = syncResourceId.toPatchNameBase36() + ".sync36"; outFile->open(outName); syncResource->writeToStream(outFile); outFile->finalize(); outFile->close(); ResourceId raveResourceId = ResourceId(kResourceTypeRave, resourceId, noun, verb, cond, seq); Resource *raveResource = _resMan->findResource(raveResourceId, true); outName = raveResourceId.toPatchNameBase36() + ".rave"; outFile->open(outName); raveResource->writeToStream(outFile); outFile->finalize(); outFile->close(); _resMan->unlockResource(raveResource); delete outFile; #endif // Set the portrait palette _palette->set(&_portraitPalette, false, true); // Draw base bitmap drawBitmap(0); bitsShow(); // Start playing audio... _audio->stopAudio(); _audio->startAudio(resourceId, audioNumber); #ifndef DEBUG_PORTRAIT_USE_SYNC_RESOURCES if (!raveResource) { warning("kPortrait: no rave resource %d %X", resourceId, audioNumber); return; } // Do animation depending on rave resource till audio is done playing int16 raveTicks; uint16 raveID; byte *raveLipSyncData; byte raveLipSyncTicks; byte raveLipSyncBitmapNr; int timerPosition = 0; int timerPositionWithin = 0; int curPosition; SciEvent curEvent; bool userAbort = false; while ((raveOffset < raveResource->size) && (!userAbort)) { // rave string starts with tick count, followed by lipSyncID, tick count and so on raveTicks = raveGetTicks(raveResource, &raveOffset); if (raveTicks < 0) break; // get lipSyncID raveID = raveGetID(raveResource, &raveOffset); if (raveID) { raveLipSyncData = raveGetLipSyncData(raveID); } else { raveLipSyncData = NULL; } #ifdef DEBUG_PORTRAIT if (raveID & 0x0ff) { debug("kPortrait: rave '%c%c' after %d ticks", raveID >> 8, raveID & 0x0ff, raveTicks); } else if (raveID) {
std::string Lock::ResourceMutex::getName(ResourceId resourceId) { invariant(resourceId.getType() == RESOURCE_MUTEX); return ResourceIdFactory::nameForId(resourceId); }
int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { #ifndef ENABLE_SCI32 // SCI32 support is not built in. Check if this is a SCI32 game // and if it is abort here. if (_volVersion >= kResVersionSci2) return SCI_ERROR_RESMAP_NOT_FOUND; #endif uint32 offset = 0; const ResourceId mapResId(kResourceTypeMap, map->_mapNumber); Resource *mapRes = _resMap.getVal(mapResId, nullptr); if (!mapRes) { warning("Failed to open %s", mapResId.toString().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } // Here, we allocate audio maps ourselves instead of using findResource to // do this for us. This is in order to prevent the map resources from // getting into the LRU cache. These resources must be read and then // deallocated in games with multi-disc audio in order to read the audio // maps from every CD, and LRU eviction freaks out if an unallocated // resource ends up in the LRU list. It is also not necessary for these // resources to be cached in the LRU at all, since they are only used upon // game startup to populate _resMap. assert(mapRes->_status == kResStatusNoMalloc); loadResource(mapRes); if (!mapRes->data()) { warning("Failed to read data for %s", mapResId.toString().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } ResourceSource *src = findVolume(map, map->_volumeNumber); if (!src) { warning("Failed to find volume for %s", mapResId.toString().c_str()); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } Common::SeekableReadStream *fileStream = getVolumeFile(src); if (!fileStream) { warning("Failed to open file stream for %s", src->getLocationName().c_str()); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } const uint32 srcSize = fileStream->size(); disposeVolumeFileStream(fileStream, src); SciSpan<const byte>::const_iterator ptr = mapRes->cbegin(); uint32 entrySize = 0; if (_volVersion >= kResVersionSci2) { // The heuristic size detection is incompatible with at least Torin RU, // which is fine because it is not needed for SCI32 entrySize = 11; } else { // Heuristic to detect entry size for (int i = mapRes->size() - 1; i >= 0; --i) { if (ptr[i] == 0xff) entrySize++; else break; } } if (map->_mapNumber == 65535) { while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16LE(); ptr += 2; if (n == 0xffff) break; if (entrySize == 6) { offset = ptr.getUint32LE(); ptr += 4; } else { offset += ptr.getUint24LE(); ptr += 3; } addResource(ResourceId(kResourceTypeAudio, n), src, offset, 0, map->getLocationName()); } } else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) { // QFG3 demo format // ptr[3] would be 'seq' in the normal format and cannot possibly be 0 while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16BE(); ptr += 2; if (n == 0xffff) break; offset = ptr.getUint32LE(); ptr += 4; uint32 size = ptr.getUint32LE(); ptr += 4; addResource(ResourceId(kResourceTypeAudio, n), src, offset, size, map->getLocationName()); } } else if (map->_mapNumber == 0 && entrySize == 8 && (ptr + 2).getUint16LE() == 0xffff) { // LB2 Floppy/Mother Goose SCI1.1 format Common::SeekableReadStream *stream = getVolumeFile(src); while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16LE(); ptr += 4; if (n == 0xffff) break; const ResourceId audioResId(kResourceTypeAudio, n); offset = ptr.getUint32LE(); ptr += 4; uint32 size; if (src->getAudioCompressionType() == 0) { // The size is not stored in the map and the entries have no order. // We need to dig into the audio resource in the volume to get the size. stream->seek(offset + 1); byte headerSize = stream->readByte(); if (headerSize != 11 && headerSize != 12) { error("Unexpected header size in %s: should be 11 or 12, got %d", audioResId.toString().c_str(), headerSize); } stream->skip(7); size = stream->readUint32LE() + headerSize + 2; } else { size = 0; } addResource(audioResId, src, offset, size, map->getLocationName()); } disposeVolumeFileStream(stream, src); } else { // EQ1CD & SQ4CD are "early" games; KQ6CD and all SCI32 are "late" games const bool isEarly = (entrySize != 11); if (!isEarly) { offset = ptr.getUint32LE(); ptr += 4; } enum { kRaveFlag = 0x40, kSyncFlag = 0x80, kEndOfMapFlag = 0xFF }; while (ptr != mapRes->cend()) { uint32 n = ptr.getUint32BE(); uint32 syncSize = 0; ptr += 4; // Checking the entire tuple breaks Torin RU and is not how SSCI // works if ((n & kEndOfMapFlag) == kEndOfMapFlag) { const uint32 bytesLeft = mapRes->cend() - ptr; if (bytesLeft >= entrySize) { warning("End of %s reached, but %u entries remain", mapResId.toString().c_str(), bytesLeft / entrySize); } break; } // GK1CD has a message whose audio36 resource has the wrong tuple and never plays. // The message tuple is 420 2 32 0 1 but the audio36 tuple is 420 2 32 3 1. bug #10819 if (g_sci->getGameId() == GID_GK1 && g_sci->isCD() && map->_mapNumber == 420 && n == 0x02200301) { n = 0x02200001; } // QFG4CD has a message whose audio36 resource has the wrong tuple and never plays. // The message tuple is 510 23 1 0 1 but the audio36 tuple is 510 199 1 0 1. bug #10848 if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() && map->_mapNumber == 510 && n == 0xc7010001) { n = 0x17010001; } // QFG4CD has an orphaned audio36 resource that additionally has the wrong tuple. // The audio36 tuple is 520 2 59 0 3. The message would be 520 2 59 0 2. bug #10849 // We restore the missing message in message.cpp. if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() && map->_mapNumber == 520 && n == 0x023b0003) { n = 0x023b0002; } if (isEarly) { offset = ptr.getUint32LE(); ptr += 4; } else { offset += ptr.getUint24LE(); ptr += 3; } if (isEarly || (n & kSyncFlag)) { syncSize = ptr.getUint16LE(); ptr += 2; // FIXME: The sync36 resource seems to be two bytes too big in KQ6CD // (bytes taken from the RAVE resource right after it) if (syncSize > 0) { // TODO: Add a mechanism to handle cases with missing resources like the ones below // // LB2CD is missing the sync resource for message 1885 1 6 30 2 but it's a duplicate // of 1885 1 6 16 2 which does have a sync resource so use that for both. bug #9956 if (g_sci->getGameId() == GID_LAURABOW2 && map->_mapNumber == 1885 && n == 0x01061002) { addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, 0x01061e02), src, offset, syncSize, map->getLocationName()); } addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize, map->getLocationName()); } } // Checking for this 0x40 flag breaks at least Laura Bow 2 CD 1.1 // map 448 if (g_sci->getGameId() == GID_KQ6 && (n & kRaveFlag)) { // This seems to define the size of raw lipsync data (at least // in KQ6 CD Windows). uint32 kq6HiresSyncSize = ptr.getUint16LE(); ptr += 2; if (kq6HiresSyncSize > 0) { // Rave resources do not have separate entries in the audio // map (their data was just appended to sync resources), so // we have to use the sync resource offset first and then // adjust the offset & size later, otherwise offset // validation will fail for compressed volumes (since the // relocation table in a compressed volume only contains // offsets that existed in the original audio map) Resource *res = addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize + kq6HiresSyncSize, map->getLocationName()); res->_fileOffset += syncSize; res->_size -= syncSize; syncSize += kq6HiresSyncSize; } } const ResourceId id(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f); // Map 405 on CD 1 of the US release of PQ:SWAT 1.000 is broken // and points to garbage in the RESOURCE.AUD. The affected audio36 // assets seem to be able to load successfully from one of the later // CDs, so just ignore the map on this disc if (g_sci->getGameId() == GID_PQSWAT && g_sci->getLanguage() == Common::EN_ANY && map->_volumeNumber == 1 && map->_mapNumber == 405) { continue; } if (g_sci->getGameId() == GID_GK2) { // At least version 1.00 of the US release, and the German // release, of GK2 have multiple invalid audio36 map entries on // CD 6 if (map->_volumeNumber == 6 && offset + syncSize >= srcSize) { bool skip; switch (g_sci->getLanguage()) { case Common::EN_ANY: skip = (map->_mapNumber == 22 || map->_mapNumber == 160); break; case Common::DE_DEU: skip = (map->_mapNumber == 22); break; default: skip = false; } if (skip) { continue; } } // Map 2020 on CD 1 of the German release of GK2 is invalid. // This content does not appear to ever be used by the game (it // does not even exist in the US release), and there is a // correct copy of it on CD 6, so just ignore the bad copy on // CD 1 if (g_sci->getLanguage() == Common::DE_DEU && map->_volumeNumber == 1 && map->_mapNumber == 2020) { continue; } } // Map 800 and 4176 contain content that was cut from the game. The // French version of the game includes map files from the US // release, but the audio resources are French so the maps don't // match. Since the content was never used, just ignore these maps // everywhere if (g_sci->getGameId() == GID_PHANTASMAGORIA2 && (map->_mapNumber == 800 || map->_mapNumber == 4176)) { continue; } addResource(id, src, offset + syncSize, 0, map->getLocationName()); } } mapRes->unalloc(); return 0; }
LockResult LockManager::lock(const ResourceId& resId, LockRequest* request, LockMode mode) { dassert(mode > MODE_NONE); // Fast path for acquiring the same lock multiple times in modes, which are already covered // by the current mode. It is safe to do this without locking, because 1) all calls for the // same lock request must be done on the same thread and 2) if there are lock requests // hanging off a given LockHead, then this lock will never disappear. if ((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == LockConflictsTable[request->mode]) { request->recursiveCount++; return LOCK_OK; } // TODO: For the time being we do not need conversions between unrelated lock modes (i.e., // modes which both add and remove to the conflicts set), so these are not implemented yet // (e.g., S -> IX). invariant((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == LockConflictsTable[mode]); LockBucket* bucket = _getBucket(resId); scoped_spinlock scopedLock(bucket->mutex); LockHead* lock; LockHeadMap::iterator it = bucket->data.find(resId); if (it == bucket->data.end()) { // Lock is free (not on the map) invariant(request->status == LockRequest::STATUS_NEW); lock = new LockHead(resId); bucket->data.insert(LockHeadPair(resId, lock)); } else { // Lock is not free lock = it->second; } // Sanity check if requests are being reused invariant(request->lock == NULL || request->lock == lock); request->lock = lock; request->recursiveCount++; if (request->status == LockRequest::STATUS_NEW) { invariant(request->recursiveCount == 1); // The flush lock must be FIFO ordered or else we will starve the dur threads and not // block new writers. This means that if anyone is blocking on a mustFIFO resource we // must get in line, even if we don't conflict with the grantedModes and could take the // lock immediately. const bool mustFIFO = (resId.getType() == RESOURCE_MMAPV1_FLUSH); // New lock request if (conflicts(mode, lock->grantedModes) || (mustFIFO && conflicts(mode, lock->conflictModes))) { request->status = LockRequest::STATUS_WAITING; request->mode = mode; request->convertMode = MODE_NONE; // Put it on the conflict queue. This is the place where various policies could be // applied for where in the wait queue does a request go. lock->addToConflictQueue(request); lock->changeConflictModeCount(mode, LockHead::Increment); return LOCK_WAITING; } else { // No conflict, new request request->status = LockRequest::STATUS_GRANTED; request->mode = mode; request->convertMode = MODE_NONE; lock->addToGrantedQueue(request); lock->changeGrantedModeCount(mode, LockHead::Increment); return LOCK_OK; } } else { // If we are here, we already hold the lock in some mode. In order to keep it simple, // we do not allow requesting a conversion while a lock is already waiting or pending // conversion, hence the assertion below. invariant(request->status == LockRequest::STATUS_GRANTED); invariant(request->recursiveCount > 1); invariant(request->mode != mode); // Construct granted mask without our current mode, so that it is not counted as // conflicting uint32_t grantedModesWithoutCurrentRequest = 0; // We start the counting at 1 below, because LockModesCount also includes MODE_NONE // at position 0, which can never be acquired/granted. for (uint32_t i = 1; i < LockModesCount; i++) { const uint32_t currentRequestHolds = (request->mode == static_cast<LockMode>(i) ? 1 : 0); if (lock->grantedCounts[i] > currentRequestHolds) { grantedModesWithoutCurrentRequest |= modeMask(static_cast<LockMode>(i)); } } // This check favours conversion requests over pending requests. For example: // // T1 requests lock L in IS // T2 requests lock L in X // T1 then upgrades L from IS -> S // // Because the check does not look into the conflict modes bitmap, it will grant L to // T1 in S mode, instead of block, which would otherwise cause deadlock. if (conflicts(mode, grantedModesWithoutCurrentRequest)) { request->status = LockRequest::STATUS_CONVERTING; request->convertMode = mode; lock->conversionsCount++; lock->changeGrantedModeCount(request->convertMode, LockHead::Increment); return LOCK_WAITING; } else { // No conflict, existing request lock->changeGrantedModeCount(mode, LockHead::Increment); lock->changeGrantedModeCount(request->mode, LockHead::Decrement); request->mode = mode; return LOCK_OK; } } }
void DeadlockDetector::_processNextNode(const UnprocessedNode& node) { // Locate the request LockManager::LockBucket* bucket = _lockMgr._getBucket(node.resId); SimpleMutex::scoped_lock scopedLock(bucket->mutex); LockManager::LockHeadMap::const_iterator iter = bucket->data.find(node.resId); if (iter == bucket->data.end()) { return; } const LockHead* lock = iter->second; LockRequest* request = lock->findRequest(node.lockerId); // It is possible that a request which was thought to be waiting suddenly became // granted, so check that before proceeding if (!request || (request->status == LockRequest::STATUS_GRANTED)) { return; } std::pair<WaitForGraph::iterator, bool> val = _graph.insert(WaitForGraphPair(node.lockerId, Edges(node.resId))); if (!val.second) { // We already saw this locker id, which means we have a cycle. if (!_foundCycle) { _foundCycle = (node.lockerId == _initialLockerId); } return; } Edges& edges = val.first->second; bool seen = false; for (LockRequest* it = lock->grantedQueueEnd; it != NULL; it = it->prev) { // We can't conflict with ourselves if (it == request) { seen = true; continue; } // If we are a regular conflicting request, both granted and conversion modes need to // be checked for conflict, since conversions will be granted first. if (request->status == LockRequest::STATUS_WAITING) { if (conflicts(request->mode, modeMask(it->mode)) || conflicts(request->mode, modeMask(it->convertMode))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } continue; } // If we are a conversion request, only requests, which are before us need to be // accounted for. invariant(request->status == LockRequest::STATUS_CONVERTING); if (conflicts(request->convertMode, modeMask(it->mode)) || (seen && conflicts(request->convertMode, modeMask(it->convertMode)))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } } // All conflicting waits, which would be granted before us for (LockRequest* it = request->prev; (request->status == LockRequest::STATUS_WAITING) && (it != NULL); it = it->prev) { // We started from the previous element, so we should never see ourselves invariant(it != request); if (conflicts(request->mode, modeMask(it->mode))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } } }
Resource *RCFile::GetRes() { int type; int val = 0; std::wstring name; ResourceId id; bool done = false; Resource *rv = NULL; while (!done && !AtEof()) { done= true; if (!IsNumber() && GetToken()->IsKeyword()) { type = GetToken()->GetKeyword(); name = GetToken()->GetString(); if (type >= Lexer::ACCELERATORS) { if (type != Lexer::STRINGTABLE && type != Lexer::LANGUAGE) { NextToken(); if (GetToken()->IsKeyword() && GetToken()->GetKeyword() >= Lexer::ACCELERATORS) { id.SetName(name); type = GetToken()->GetKeyword(); name = GetToken()->GetString(); } else if (GetToken()->IsIdentifier()) { id.SetName(name); type = -1; name =CvtString(GetToken()->GetId()); } else if (IsNumber()) { type = -2; val = GetNumber(); } else { throw new std::runtime_error("Expected resource type"); } if (type != -2) NextToken(); } else { NextToken(); } } else { throw new std::runtime_error("Expected resource identifier"); } } else { id.ReadRC(*this); if (GetToken()->IsKeyword()) { type = GetToken()->GetKeyword(); name = GetToken()->GetString(); } else if (GetToken()->IsIdentifier()) { type = -1; name = CvtString(GetToken()->GetId()); } else if (IsNumber()) { type = -2; val = GetNumber(); } else { throw new std::runtime_error("Expected resource type"); } if (type != -2) NextToken(); } ResourceInfo info(language); info.SetFlags(ResourceInfo::Moveable | ResourceInfo::Discardable); rv = NULL; switch (type) { case -1: for (int i=0 ; i < name.size(); i++) name[i] = toupper(name[i]); rv = new GenericResource(ResourceId(name), id, info); break; case -2: rv = new GenericResource(ResourceId(val), id, info); break; case Lexer::ACCELERATORS: rv = new Accelerators(id, info); break; case Lexer::TBITMAP: rv = new Bitmap(id, info); break; case Lexer::CURSOR: rv = new GroupCursor(id, info); break; case Lexer::DIALOG: rv = new Dialog(id, info, false); break; case Lexer::DIALOGEX: rv = new Dialog(id, info, true); break; case Lexer::DLGINCLUDE: rv = new DlgInclude(id, info); break; case Lexer::FONT: rv = new Font(id, info); break; case Lexer::ICON: rv = new GroupIcon(id, info); break; case Lexer::MENU: rv = new Menu(id, info, false); break; case Lexer::MENUEX: rv = new Menu(id, info, true); break; case Lexer::RCDATA: rv = new RCData(id, info); break; case Lexer::VERSIONINFO: rv = new VersionInfo(id, info); break; case Lexer::MESSAGETABLE: rv = new MessageTable(id, info); break; case Lexer::STRINGTABLE: rv = new StringTable(info); rv->ReadRC(*this); delete rv; rv = NULL; done = false; break; case Lexer::LANGUAGE: { language = GetNumber(); SkipComma(); language |= GetNumber() << 10; NeedEol(); done = false; break; } case Lexer::RCINCLUDE: { std::string name = GetFileName(); pp.IncludeFile(name); } break; default: throw new std::runtime_error("Invalid resource type"); break; } if (rv) rv->ReadRC(*this); } return rv; }
reg_t kDoSync(EngineState *s, int funct_nr, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case kSciAudioSyncStart: { ResourceId id; if (s->_sound._syncResource) { s->resmgr->unlockResource(s->_sound._syncResource); s->_sound._syncResource = NULL; } // Load sound sync resource and lock it if (argc == 3) { id = ResourceId(kResourceTypeSync, argv[2].toUint16()); } else if (argc == 7) { id = ResourceId(kResourceTypeSync36, argv[2].toUint16(), argv[3].toUint16(), argv[4].toUint16(), argv[5].toUint16(), argv[6].toUint16()); } else { warning("kDoSync: Start called with an unknown number of parameters (%d)", argc); return s->r_acc; } s->_sound._syncResource = s->resmgr->findResource(id, 1); if (s->_sound._syncResource) { PUT_SEL32V(argv[1], syncCue, 0); s->_sound._syncOffset = 0; } else { warning("DoSync: failed to find resource %s", id.toString().c_str()); // Notify the scripts to stop sound sync PUT_SEL32V(argv[1], syncCue, -1); } break; } case kSciAudioSyncNext: { Resource *res = s->_sound._syncResource; if (res && (s->_sound._syncOffset < res->size - 1)) { int16 syncCue = -1; int16 syncTime = (int16)READ_LE_UINT16(res->data + s->_sound._syncOffset); s->_sound._syncOffset += 2; if ((syncTime != -1) && (s->_sound._syncOffset < res->size - 1)) { syncCue = (int16)READ_LE_UINT16(res->data + s->_sound._syncOffset); s->_sound._syncOffset += 2; } PUT_SEL32V(argv[1], syncTime, syncTime); PUT_SEL32V(argv[1], syncCue, syncCue); } break; } case kSciAudioSyncStop: if (s->_sound._syncResource) { s->resmgr->unlockResource(s->_sound._syncResource); s->_sound._syncResource = NULL; } break; default: warning("DoSync: Unhandled subfunction %d", argv[0].toUint16()); } 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; }