Exemple #1
0
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;
}
Exemple #2
0
    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));
        }
    }
Exemple #3
0
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;
}
Exemple #4
0
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;
    }
}
Exemple #5
0
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();
}
Exemple #6
0
Dialog::Dialog(ResourceId dialogId, Widget* parent)
  : Frame(::CreateDialog(Application::getHandle(),
			 dialogId.toLPTSTR(),
			 parent ? parent->getHandle() : NULL,
			 Dialog::globalDlgProc))
{
  m_state = false;
}
Exemple #7
0
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;
}
Exemple #8
0
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()));
        };
    }
Exemple #9
0
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");
}
Exemple #10
0
bool
Bitmap::Load(ResourceId _id, Type _type)
{
  assert(_id.IsDefined());

  Reset();

  id = _id;
  type = _type;
  AddSurfaceListener(*this);

  if (!surface_valid)
    return true;

  return Reload();
}
Exemple #11
0
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;
			}
		}
	}
}
Exemple #12
0
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()));
        };
    }
Exemple #13
0
// ----------------------------------------------------------------
//	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;
}
Exemple #14
0
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;
}
Exemple #17
0
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) {
Exemple #18
0
std::string Lock::ResourceMutex::getName(ResourceId resourceId) {
    invariant(resourceId.getType() == RESOURCE_MUTEX);
    return ResourceIdFactory::nameForId(resourceId);
}
Exemple #19
0
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;
}
Exemple #20
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;
            }
        }
    }
Exemple #21
0
    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);
                }
            }
        }
    }
Exemple #22
0
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;
}
Exemple #23
0
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;
}
Exemple #24
0
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;
}