int findAllVariants(String const &packageId, FS::FoundFiles &found) const
    {
        QStringList const components = packageId.split('.');

        String id;

        // The package may actually be inside other packages, so we need to check
        // each component of the package identifier.
        for(int i = components.size() - 1; i >= 0; --i)
        {
            id = components.at(i) + (!id.isEmpty()? "." + id : "");

            FS::FoundFiles files;
            App::fileSystem().findAllOfTypes(StringList()
                                             << DENG2_TYPE_NAME(Folder)
                                             << DENG2_TYPE_NAME(ArchiveFolder),
                                             id + ".pack", files);

            files.remove_if(PackageIdentifierDoesNotMatch(packageId));

            std::copy(files.begin(), files.end(), std::back_inserter(found));
        }

        return int(found.size());
    }
Value *OperatorExpression::evaluate(Evaluator &evaluator) const
{
    // Get the operands.
    Value *rightValue = (_op == MEMBER? 0 : evaluator.popResult());
    Value *leftValue = (_leftOperand? evaluator.popResult() : 0);
    Value *result = (leftValue? leftValue : rightValue);

    try
    {
        switch(_op)
        {
        case PLUS:
            if(leftValue)
            {
                leftValue->sum(*rightValue);
            }
            else
            {
                // Unary plus is a no-op.
            }
            break;

        case PLUS_ASSIGN:
            verifyAssignable(leftValue);
            leftValue->sum(*rightValue);
            break;    

        case MINUS:
            if(leftValue)
            {
                leftValue->subtract(*rightValue);
            }
            else
            {
                // Negation.
                rightValue->negate();
            }
            break;

        case MINUS_ASSIGN:
            verifyAssignable(leftValue);
            leftValue->subtract(*rightValue);
            break;    

        case DIVIDE:
            leftValue->divide(*rightValue);
            break;

        case DIVIDE_ASSIGN:
            verifyAssignable(leftValue);
            leftValue->divide(*rightValue);
            break;    

        case MULTIPLY:
            leftValue->multiply(*rightValue);
            break;

        case MULTIPLY_ASSIGN:
            verifyAssignable(leftValue);
            leftValue->multiply(*rightValue);
            break;    

        case MODULO:
            leftValue->modulo(*rightValue);
            break;

        case MODULO_ASSIGN:
            verifyAssignable(leftValue);
            leftValue->modulo(*rightValue);
            break;    

        case NOT:
            result = newBooleanValue(rightValue->isFalse());
            break;

        case AND:
            result = newBooleanValue(leftValue->isTrue() && rightValue->isTrue());
            break;

        case OR:
            result = newBooleanValue(leftValue->isTrue() || rightValue->isTrue());
            break;

        case EQUAL:
            result = newBooleanValue(!leftValue->compare(*rightValue));
            break;

        case NOT_EQUAL:
            result = newBooleanValue(leftValue->compare(*rightValue) != 0);
            break;

        case LESS:
            result = newBooleanValue(leftValue->compare(*rightValue) < 0);
            break;

        case GREATER:
            result = newBooleanValue(leftValue->compare(*rightValue) > 0);
            break;

        case LEQUAL:
            result = newBooleanValue(leftValue->compare(*rightValue) <= 0);
            break;

        case GEQUAL:
            result = newBooleanValue(leftValue->compare(*rightValue) >= 0);
            break;

        case IN:
            result = newBooleanValue(rightValue->contains(*leftValue));
            break;

        case CALL:
            leftValue->call(evaluator.process(), *rightValue);
            // Result comes from whatever is being called.
            result = 0;
            break;

        case INDEX:
        {
            /*
            LOG_DEV_TRACE("INDEX: types %s [ %s ] byref:%b",
                          DENG2_TYPE_NAME(*leftValue) << DENG2_TYPE_NAME(*rightValue)
                          << flags().testFlag(ByReference));
                          */

            // As a special case, records can be indexed also by reference.
            RecordValue *recValue = dynamic_cast<RecordValue *>(leftValue);
            if(flags().testFlag(ByReference) && recValue)
            {
                result = new RefValue(&recValue->dereference()[rightValue->asText()]);
            }
            else
            {
                // Index by value.
                result = leftValue->duplicateElement(*rightValue);
            }
            break;
        }

        case SLICE:
            result = performSlice(leftValue, rightValue);
            break;

        case MEMBER: 
        {
            RecordValue const *recValue = dynamic_cast<RecordValue const *>(leftValue);
            if(!recValue)
            {
                throw ScopeError("OperatorExpression::evaluate",
                    "Left side of " + operatorToText(_op) + " must evaluate to a record [" +
                                 DENG2_TYPE_NAME(*leftValue) + "]");
            }
            
            // Now that we know what the scope is, push the rest of the expression
            // for evaluation (in this specific scope).
            _rightOperand->push(evaluator, recValue->record());
            
            // Cleanup.
            delete leftValue;
            DENG2_ASSERT(rightValue == NULL);

            // The MEMBER operator does not evaluate to any result. 
            // Whatever is on the right side will be the result.
            return NULL;
        }

        default:
            throw Error("OperatorExpression::evaluate", 
                "Operator " + operatorToText(_op) + " not implemented");
        }
    }
    catch(Error const &)
    {
        delete rightValue;
        delete leftValue;
        throw;
    }

    // Delete the unnecessary values.
    if(result != rightValue) delete rightValue;
    if(result != leftValue) delete leftValue;
    
    return result;
}