예제 #1
0
void FileSelector::RefreshFiles()
{
    FileSystem* fileSystem = GetSubsystem<FileSystem>();

    ignoreEvents_ = true;

    fileList_->RemoveAllItems();
    fileEntries_.Clear();

    Vector<String> directories;
    Vector<String> files;
    fileSystem->ScanDir(directories, path_, "*", SCAN_DIRS, false);
    fileSystem->ScanDir(files, path_, GetFilter(), SCAN_FILES, false);

    fileEntries_.Reserve(directories.Size() + files.Size());

    for (unsigned i = 0; i < directories.Size(); ++i)
    {
        FileSelectorEntry newEntry;
        newEntry.name_ = directories[i];
        newEntry.directory_ = true;
        fileEntries_.Push(newEntry);
    }

    for (unsigned i = 0; i < files.Size(); ++i)
    {
        FileSelectorEntry newEntry;
        newEntry.name_ = files[i];
        newEntry.directory_ = false;
        fileEntries_.Push(newEntry);
    }

    // Sort and add to the list view
    // While items are being added, disable layout update for performance optimization
    Sort(fileEntries_.Begin(), fileEntries_.End(), CompareEntries);
    UIElement* listContent = fileList_->GetContentElement();
    listContent->DisableLayoutUpdate();
    for (unsigned i = 0; i < fileEntries_.Size(); ++i)
    {
        String displayName;
        if (fileEntries_[i].directory_)
            displayName = "<DIR> " + fileEntries_[i].name_;
        else
            displayName = fileEntries_[i].name_;

        Text* entryText = new Text(context_);
        fileList_->AddItem(entryText);
        entryText->SetText(displayName);
        entryText->SetStyle("FileSelectorListText");
    }
    listContent->EnableLayoutUpdate();
    listContent->UpdateLayout();

    ignoreEvents_ = false;

    // Clear filename from the previous dir so that there is no confusion
    SetFileName(String::EMPTY);
    lastUsedFilter_ = GetFilter();
}
void AssetDatabase::PruneOrphanedDotAssetFiles()
{

    if (project_.Null())
    {
        LOGDEBUG("AssetDatabase::PruneOrphanedDotAssetFiles - called without project loaded");
        return;
    }

    FileSystem* fs = GetSubsystem<FileSystem>();

    const String& resourcePath = project_->GetResourcePath();

    Vector<String> allResults;

    fs->ScanDir(allResults, resourcePath, "*.asset", SCAN_FILES, true);

    for (unsigned i = 0; i < allResults.Size(); i++)
    {
        String dotAssetFilename = resourcePath + allResults[i];
        String assetFilename = ReplaceExtension(dotAssetFilename, "");

        // remove orphaned asset files
        if (!fs->FileExists(assetFilename) && !fs->DirExists(assetFilename))
        {

            LOGINFOF("Removing orphaned asset file: %s", dotAssetFilename.CString());
            fs->Delete(dotAssetFilename);
        }

    }
}
static int FileSystem_ScanDir(duk_context* ctx)
{
    duk_push_this(ctx);

    FileSystem* fs = js_to_class_instance<FileSystem>(ctx, -1, 0);

    if ( !duk_is_string(ctx, 0) || !duk_is_string(ctx, 1) ||
            !duk_is_number(ctx, 2) || !duk_is_boolean(ctx, 3))
    {
        duk_push_string(ctx, "FileSystem::ScanDir bad args");
        duk_throw(ctx);
    }

    const char* pathName = duk_to_string(ctx, 0);
    const char* filter = duk_to_string(ctx, 1);
    unsigned flags = duk_to_number(ctx, 2);
    bool recursive = duk_to_boolean(ctx, 3) ? true : false;

    Vector<String> result;

    fs->ScanDir(result, pathName, filter, flags, recursive);

    duk_push_array(ctx);

    for (unsigned i = 0; i < result.Size(); i++)
    {
        duk_push_string(ctx, result[i].CString());
        duk_put_prop_index(ctx, -2, i);
    }

    return 1;
}
예제 #4
0
void JSBModule::ScanHeaders()
{
    JSBind* jsbind = GetSubsystem<JSBind>();
    FileSystem* fs = GetSubsystem<FileSystem>();

    const String& sourceRoot = jsbind->GetSourceRootFolder();

    for (unsigned i = 0; i < sourceDirs_.Size(); i++)
    {
        const String& dir = sourceRoot + sourceDirs_[i] + "/";

        Vector<String> fileNames;
        fs->ScanDir(fileNames, dir, "*.h", SCAN_FILES, false);

        for (unsigned k = 0; k < fileNames.Size(); k++)
        {
            String filepath = dir + fileNames[k];

            SharedPtr<JSBHeader> header(new JSBHeader(context_, this, filepath));

            // Parse the C++ header
            header->Parse();

            headers_.Push(header);
        }

    }

}
예제 #5
0
bool BuildWindows::BuildManaged(const String& buildPath)
{
    ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
    ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
    FileSystem* fileSystem = GetSubsystem<FileSystem>();
    Project* project = toolSystem->GetProject();
    ProjectSettings* settings = project->GetProjectSettings();

    String projectPath = project->GetProjectPath();

#ifdef ATOMIC_DEBUG
    String config = "Debug";
#else
    String config = "Release";
#endif

    String managedBins = projectPath + ToString("AtomicNET/%s/Bin/Desktop/", config.CString());
    String managedExe = managedBins + settings->GetName() + ".exe";

    if (!fileSystem->FileExists(managedExe))
    {
        FailBuild(ToString("Error building managed project, please compile the %s binary %s before building", config.CString(), managedExe.CString()));
        return false;
    }

    StringVector results;
    StringVector filtered;

    fileSystem->ScanDir(results, managedBins, "", SCAN_FILES, false);

    StringVector filterList;

    StringVector::Iterator itr = results.Begin();
    while (itr != results.End())
    {
        unsigned i;
        for (i = 0; i < filterList.Size(); i++)
        {
            if (itr->Contains(filterList[i]))
                break;
        }

        if (i == filterList.Size())
            filtered.Push(*itr);

        itr++;
    }

    for (unsigned i = 0; i < filtered.Size(); i++)
    {
        String filename = filtered[i];

        if (!BuildCopyFile(managedBins + filename, buildPath_ + "/" + filename))
            return false;

    }

    return true;

}
예제 #6
0
void BuildBase::ScanResourceDirectory(const String& resourceDir)
{
    Vector<String> fileNames;
    FileSystem* fileSystem = GetSubsystem<FileSystem>();
    fileSystem->ScanDir(fileNames, resourceDir, "*.*", SCAN_FILES, true);

    for (unsigned i = 0; i < fileNames.Size(); i++)
    {
        const String& filename = fileNames[i];

        for (unsigned j = 0; j < resourceEntries_.Size(); j++)
        {
            const BuildResourceEntry* entry = resourceEntries_[j];

            if (entry->packagePath_ == filename)
            {
                BuildWarn(ToString("Resource Path: %s already exists", filename.CString()));
                continue;
            }
        }

        // TODO: Add additional filters
        if (GetExtension(filename) == ".psd")
            continue;

        BuildResourceEntry* newEntry = new BuildResourceEntry;

// BEGIN LICENSE MANAGEMENT
        if (GetExtension(filename) == ".mdl")
        {
            containsMDL_ = true;
        }
// END LICENSE MANAGEMENT

        newEntry->absolutePath_ = resourceDir + filename;
        newEntry->resourceDir_ = resourceDir;

        if (resourceDir.EndsWith("/Cache/"))
            newEntry->packagePath_ = "Cache/" + filename;
        else
            newEntry->packagePath_ = filename;

        resourceEntries_.Push(newEntry);

        //LOGINFOF("Adding resource: %s : %s", newEntry->absolutePath_.CString(), newEntry->packagePath_.CString());
    }
}
    bool CSComponentAssembly::PreloadClassAssemblies()
    {
        // TEMPORARY SOLUTION, Desktop only

        ATOMIC_LOGINFO("Preloading Class Assemblies");

        Context* context = ScriptSystem::GetContext();
        assert(context);

        ResourceCache* cache = context->GetSubsystem<ResourceCache>();
        FileSystem* fileSystem = context->GetSubsystem<FileSystem>();

        const StringVector& resourceDirs = cache->GetResourceDirs();

        for (unsigned i = 0; i < resourceDirs.Size(); i++)
        {
            const String& resourceDir = resourceDirs[i];

            ATOMIC_LOGINFOF("Scanning: %s", resourceDir.CString());

            StringVector results;
            fileSystem->ScanDir(results, resourceDir, "*.dll", SCAN_FILES, true);

            for (unsigned j = 0; j < results.Size(); j++)
            {
                // FIXME: This filtering is necessary as we're loading setting project root folder as a resource dir
                // https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1037

                String filter = results[j].ToLower();

                if (filter.StartsWith("atomicnet/") || filter.StartsWith("resources/"))
                {
                    ATOMIC_LOGINFOF("Skipping Assembly: %s (https://github.com/AtomicGameEngine/AtomicGameEngine/issues/1037)", results[j].CString());
                    continue;
                }

                ATOMIC_LOGINFOF("Loading Assembly: %s", results[j].CString());

                cache->GetResource<CSComponentAssembly>(results[j]);
            }

        }

        return true;

    }
예제 #8
0
bool ResourceCache::AddResourceDir(const String& pathName)
{
    FileSystem* fileSystem = GetSubsystem<FileSystem>();
    if (!fileSystem || !fileSystem->DirExists(pathName))
    {
        LOGERROR("Could not open directory " + pathName);
        return false;
    }
    
    String fixedPath = AddTrailingSlash(pathName);
    
    // Check that the same path does not already exist
    for (unsigned i = 0; i < resourceDirs_.Size(); ++i)
    {
        if (!resourceDirs_[i].Compare(fixedPath, false))
            return true;
    }
    
    resourceDirs_.Push(fixedPath);
    
    // Scan the path for files recursively and add their hash-to-name mappings
    Vector<String> fileNames;
    fileSystem->ScanDir(fileNames, fixedPath, "*.*", SCAN_FILES, true);
    for (unsigned i = 0; i < fileNames.Size(); ++i)
        StoreNameHash(fileNames[i]);
    
    // If resource auto-reloading active, create a file watcher for the directory
    if (autoReloadResources_)
    {
        SharedPtr<FileWatcher> watcher(new FileWatcher(context_));
        watcher->StartWatching(fixedPath, true);
        fileWatchers_.Push(watcher);
    }
    
    LOGINFO("Added resource path " + fixedPath);
    return true;
}
void AssetDatabase::Scan()
{
    PruneOrphanedDotAssetFiles();

    FileSystem* fs = GetSubsystem<FileSystem>();
    const String& resourcePath = project_->GetResourcePath();

    Vector<String> allResults;

    fs->ScanDir(allResults, resourcePath, "", SCAN_FILES | SCAN_DIRS, true);

    Vector<String> filteredResults;

    filteredResults.Push(RemoveTrailingSlash(resourcePath));

    for (unsigned i = 0; i < allResults.Size(); i++)
    {
        allResults[i] = resourcePath + allResults[i];

        const String& path = allResults[i];

        if (path.StartsWith(".") || path.EndsWith("."))
            continue;

        String ext = GetExtension(path);

        if (ext == ".asset")
            continue;

        filteredResults.Push(path);
    }

    for (unsigned i = 0; i < filteredResults.Size(); i++)
    {
        const String& path = filteredResults[i];

        String dotAssetFilename = GetDotAssetFilename(path);

        if (!fs->FileExists(dotAssetFilename))
        {
            // new asset
            SharedPtr<Asset> asset(new Asset(context_));

            if (asset->SetPath(path))
                AddAsset(asset);
        }
        else
        {
            SharedPtr<File> file(new File(context_, dotAssetFilename));
            SharedPtr<JSONFile> json(new JSONFile(context_));
            json->Load(*file);
            file->Close();

            JSONValue root = json->GetRoot();

            assert(root.Get("version").GetInt() == ASSET_VERSION);

            String guid = root.Get("guid").GetString();

            if (!GetAssetByGUID(guid))
            {
                SharedPtr<Asset> asset(new Asset(context_));
                asset->SetPath(path);
                AddAsset(asset);
            }

        }

    }

    PreloadAssets();

    if (ImportDirtyAssets())
        Scan();

}
예제 #10
0
bool ModelImporter::ImportAnimations()
{
    if (!animationInfo_.Size())
    {
       if (!ImportAnimation(asset_->GetPath(), "RootAnim"))
           return false;
    }

    // embedded animations
    for (unsigned i = 0; i < animationInfo_.Size(); i++)
    {
        const SharedPtr<AnimationImportInfo>& info = animationInfo_[i];
        if (!ImportAnimation(asset_->GetPath(), info->GetName(), info->GetStartTime(), info->GetEndTime()))
            return false;
    }

    // add @ animations

    FileSystem* fs = GetSubsystem<FileSystem>();
    String pathName, fileName, ext;
    SplitPath(asset_->GetPath(), pathName, fileName, ext);

    Vector<String> results;

    fs->ScanDir(results, pathName, ext, SCAN_FILES, false);

    for (unsigned i = 0; i < results.Size(); i++)
    {
        const String& result = results[i];

        if (result.Contains("@"))
        {
            Vector<String> components = GetFileName(result).Split('@');

            if (components.Size() == 2 && components[1].Length() && components[0] == fileName)
            {
                String animationName = components[1];
                AssetDatabase* db = GetSubsystem<AssetDatabase>();
                Asset* asset = db->GetAssetByPath(pathName + result);
                assert(asset);
                assert(asset->GetImporter()->GetType() == ModelImporter::GetTypeStatic());

                ModelImporter* importer = (ModelImporter*) asset->GetImporter();

                if (!importer->animationInfo_.Size())
                {
                   if (!ImportAnimation(asset->GetPath(), animationName))
                       return false;
                }
                else
                {
                    // embedded animations
                    for (unsigned i = 0; i < importer->animationInfo_.Size(); i++)
                    {
                        const SharedPtr<AnimationImportInfo>& info = importer->animationInfo_[i];
                        if (!ImportAnimation(asset->GetPath(), info->GetName(), info->GetStartTime(), info->GetEndTime()))
                            return false;
                    }
                }


            }
        }
    }



    return true;
}
void AtomicTool::Start()
{
    // Subscribe to events
    SubscribeToEvent(E_COMMANDERROR, HANDLER(AtomicTool, HandleCommandError));
    SubscribeToEvent(E_COMMANDFINISHED, HANDLER(AtomicTool, HandleCommandFinished));

    SubscribeToEvent(E_LICENSE_EULAREQUIRED, HANDLER(AtomicTool, HandleLicenseEulaRequired));
    SubscribeToEvent(E_LICENSE_ACTIVATIONREQUIRED, HANDLER(AtomicTool, HandleLicenseActivationRequired));
    SubscribeToEvent(E_LICENSE_ERROR, HANDLER(AtomicTool, HandleLicenseError));
    SubscribeToEvent(E_LICENSE_SUCCESS, HANDLER(AtomicTool, HandleLicenseSuccess));

    const Vector<String>& arguments = GetArguments();

    ToolSystem* tsystem = new ToolSystem(context_);
    context_->RegisterSubsystem(tsystem);

    ToolEnvironment* env = new ToolEnvironment(context_);
    context_->RegisterSubsystem(env);

//#ifdef ATOMIC_DEV_BUILD

    if (!env->InitFromJSON())
    {
        ErrorExit(ToString("Unable to initialize tool environment from %s", env->GetDevConfigFilename().CString()));
        return;
    }

    if (!cliDataPath_.Length())
    {
        cliDataPath_ = env->GetRootSourceDir() + "/Resources/";
    }

//#endif

    tsystem->SetCLI();
    tsystem->SetDataPath(cliDataPath_);


    if (activationKey_.Length())
    {
        DoActivation();
        return;
    } else if (deactivate_)
    {
        DoDeactivation();
        return;
    }

    BuildSystem* buildSystem = GetSubsystem<BuildSystem>();

    SharedPtr<CommandParser> parser(new CommandParser(context_));

    SharedPtr<Command> cmd(parser->Parse(arguments));
    if (!cmd)
    {
        String error = "No command found";

        if (parser->GetErrorMessage().Length())
            error = parser->GetErrorMessage();

        ErrorExit(error);
        return;
    }

    if (cmd->RequiresProjectLoad())
    {
        FileSystem* fileSystem = GetSubsystem<FileSystem>();

        String projectDirectory = fileSystem->GetCurrentDir();

        Vector<String> projectFiles;
        fileSystem->ScanDir(projectFiles, projectDirectory, "*.atomic", SCAN_FILES, false);
        if (!projectFiles.Size())
        {
            ErrorExit(ToString("No .atomic project file in %s", projectDirectory.CString()));
            return;
        }
        else if (projectFiles.Size() > 1)
        {
            ErrorExit(ToString("Multiple .atomic project files found in %s", projectDirectory.CString()));
            return;
        }

        String projectFile = projectDirectory + "/" + projectFiles[0];

        if (!tsystem->LoadProject(projectFile))
        {
            //ErrorExit(ToString("Failed to load project: %s", projectFile.CString()));
            //return;
        }

        // Set the build path
        String buildFolder = projectDirectory + "/" + "Build";
        buildSystem->SetBuildPath(buildFolder);

        if (!fileSystem->DirExists(buildFolder))
        {
            fileSystem->CreateDir(buildFolder);

            if (!fileSystem->DirExists(buildFolder))
            {
                ErrorExit(ToString("Failed to create build folder: %s", buildFolder.CString()));
                return;
            }
        }

    }

    command_ = cmd;

    // BEGIN LICENSE MANAGEMENT
    if (cmd->RequiresLicenseValidation())
    {
        GetSubsystem<LicenseSystem>()->Initialize();
    }
    else
    {
        if (command_.Null())
        {
            GetSubsystem<Engine>()->Exit();
            return;
        }

        command_->Run();
    }
    // END LICENSE MANAGEMENT

}
예제 #12
0
bool CEJavascript::CheckJSErrors(bool fullCheck)
{
    errors_.Clear();

    Editor* editor = GetSubsystem<Editor>();
    Project* project = editor->GetProject();

    FileSystem* fileSystem = GetSubsystem<FileSystem>();
    if (!project)
    {
        modifiedJS_.Clear();
        return true;
    }

    Vector<String>& filesToCheck = modifiedJS_;

    Vector<String> allFiles;

    if (fullCheck)
    {
        filesToCheck = allFiles;

        String componentsPath = AddTrailingSlash(project->GetComponentsPath());
        String scriptsPath = AddTrailingSlash(project->GetScriptsPath());
        String modulesPath = AddTrailingSlash(project->GetModulesPath());

        Vector<String> files;
        fileSystem->ScanDir(files, componentsPath, "*.js", SCAN_FILES, true );
        for (unsigned i = 0; i < files.Size(); i++)
            allFiles.Push(componentsPath + files[i]);

        fileSystem->ScanDir(files, scriptsPath, "*.js", SCAN_FILES, true );
        for (unsigned i = 0; i < files.Size(); i++)
            allFiles.Push(scriptsPath + files[i]);

        fileSystem->ScanDir(files, modulesPath, "*.js", SCAN_FILES, true );
        for (unsigned i = 0; i < files.Size(); i++)
            allFiles.Push(modulesPath + files[i]);

    }

    bool ok = true;

    for (unsigned j = 0; j < filesToCheck.Size(); j++)
    {
        String source;
        String fullpath = filesToCheck[j];
        ReadZeroTerminatedSourceFile(fullpath, source);

        duk_get_global_string(ctx_, "__clockwork_parse_error_check");
        duk_push_string(ctx_, source.CString());

        if (duk_pcall(ctx_, 1))
        {
            printf("Error: %s\n", duk_safe_to_string(ctx_, -1));
        }
        else
        {
            if (duk_is_boolean(ctx_, -1))
            {
                // error
                if (duk_to_boolean(ctx_, -1))
                    ok = false;
            }
            else if (duk_is_object(ctx_, -1))
            {
                ok = false;
                JSError error;

                error.fullpath = fullpath;

                duk_get_prop_string(ctx_, -1, "message");
                error.message = duk_to_string(ctx_, -1);
                duk_pop(ctx_);

                duk_get_prop_string(ctx_, -1, "loc");
                duk_get_prop_string(ctx_, -1, "line");
                error.line = duk_to_number(ctx_, -1);
                duk_get_prop_string(ctx_, -2, "column");
                error.column = duk_to_number(ctx_, -1);
                duk_get_prop_string(ctx_, -4, "raisedAt");
                error.tokenPos = duk_to_number(ctx_, -1);

                duk_pop_3(ctx_);

                errors_.Push(error);
            }
            else
            {
                // what to do?
            }

        }

        // ignore result
        duk_pop(ctx_);

    }

    modifiedJS_.Clear();


    return !ok;

}
예제 #13
0
void BuildWindows::BuildAtomicNET()
{
    // AtomicNET

    FileSystem* fileSystem = GetSubsystem<FileSystem>();
    ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
    ToolSystem* tsystem = GetSubsystem<ToolSystem>();
    Project* project = tsystem->GetProject();
    String projectResources = project->GetResourcePath();

    String assembliesPath = projectResources + "Assemblies/";

    // if no assemblies path, no need to install AtomicNET
    if (!fileSystem->DirExists(assembliesPath))
        return;

    Vector<String> results;
    fileSystem->ScanDir(results, assembliesPath, "*.dll", SCAN_FILES, true);

    // if no assembiles in Assemblies path, no need to install AtomicNET
    if (!results.Size())
        return;

    BuildLog("Building AtomicNET");

    fileSystem->CreateDir(buildPath_ + "/AtomicPlayer_Resources/AtomicNET");
    fileSystem->CreateDir(buildPath_ + "/AtomicPlayer_Resources/AtomicNET/Atomic");
    fileSystem->CreateDir(buildPath_ + "/AtomicPlayer_Resources/AtomicNET/Atomic/Assemblies");

    fileSystem->CopyDir(tenv->GetNETCoreCLRAbsPath(), buildPath_ + "/AtomicPlayer_Resources/AtomicNET/CoreCLR");
    fileSystem->CopyDir(tenv->GetNETTPAPaths(), buildPath_ + "/AtomicPlayer_Resources/AtomicNET/Atomic/TPA");

    // Atomic Assemblies

    const String& assemblyLoadPaths = tenv->GetNETAssemblyLoadPaths();
    Vector<String> paths = assemblyLoadPaths.Split(';');

    for (unsigned i = 0; i < paths.Size(); i++)
    {
        Vector<String> loadResults;
        fileSystem->ScanDir(loadResults, paths[i], "*.dll", SCAN_FILES, true);

        for (unsigned j = 0; j < loadResults.Size(); j++)
        {
            String pathName, fileName, ext;
            SplitPath(loadResults[j], pathName, fileName, ext);

            if (fileName != "AtomicNETEngine")
                continue;

            fileSystem->Copy(paths[i] + "/" + loadResults[j], ToString("%s/AtomicPlayer_Resources/AtomicNET/Atomic/Assemblies/%s.dll", buildPath_.CString(), fileName.CString()));
        }

    }

    // Project assemblied
    for (unsigned i = 0; i < results.Size(); i++)
    {
        String pathName, fileName, ext;
        SplitPath(results[i], pathName, fileName, ext);
        fileSystem->Copy(assembliesPath + results[i], ToString("%s/AtomicPlayer_Resources/AtomicNET/Atomic/Assemblies/%s.dll", buildPath_.CString(), fileName.CString()));
    }



}
예제 #14
0
void Script::DumpAPI(DumpMode mode, const String& sourceTree)
{
    // Does not use LOGRAW macro here to ensure the messages are always dumped regardless of CLOCKWORK_LOGGING compiler directive
    // and of Log subsystem availability

    // Dump event descriptions and attribute definitions in Doxygen mode. For events, this means going through the header files,
    // as the information is not available otherwise. 
    /// \todo Dump events + attributes before the actual script API because the remarks (readonly / writeonly) seem to throw off
    // Doxygen parsing and the following page definition(s) may not be properly recognized
    if (mode == DOXYGEN)
    {
        Log::WriteRaw("namespace Clockwork\n{\n\n/**\n");

        FileSystem* fileSystem = GetSubsystem<FileSystem>();
        Vector<String> headerFileNames;
        String path = AddTrailingSlash(sourceTree);
        if (!path.Empty())
            path.Append("Source/Clockwork/");

        fileSystem->ScanDir(headerFileNames, path, "*.h", SCAN_FILES, true);

        /// \hack Rename any Events2D to 2DEvents to work with the event category creation correctly (currently PhysicsEvents2D)
        Vector<HeaderFile> headerFiles;
        for (unsigned i = 0; i < headerFileNames.Size(); ++i)
        {
            HeaderFile entry;
            entry.fileName = headerFileNames[i];
            entry.sectionName = GetFileNameAndExtension(entry.fileName).Replaced("Events2D", "2DEvents");
            if (entry.sectionName.EndsWith("Events.h"))
                headerFiles.Push(entry);
        }

        if (!headerFiles.Empty())
        {
            Log::WriteRaw("\n\\page EventList Event list\n");
            Sort(headerFiles.Begin(), headerFiles.End(), CompareHeaderFiles);

            for (unsigned i = 0; i < headerFiles.Size(); ++i)
            {
                SharedPtr<File> file(new File(context_, path + headerFiles[i].fileName, FILE_READ));
                if (!file->IsOpen())
                    continue;

                const String& sectionName = headerFiles[i].sectionName;
                unsigned start = sectionName.Find('/') + 1;
                unsigned end = sectionName.Find("Events.h");
                Log::WriteRaw("\n## %" + sectionName.Substring(start, end - start) + " events\n");

                while (!file->IsEof())
                {
                    String line = file->ReadLine();
                    if (line.StartsWith("EVENT"))
                    {
                        Vector<String> parts = line.Split(',');
                        if (parts.Size() == 2)
                            Log::WriteRaw("\n### " + parts[1].Substring(0, parts[1].Length() - 1).Trimmed() + "\n");
                    }
                    if (line.Contains("PARAM"))
                    {
                        Vector<String> parts = line.Split(',');
                        if (parts.Size() == 2)
                        {
                            String paramName = parts[1].Substring(0, parts[1].Find(')')).Trimmed();
                            String paramType = parts[1].Substring(parts[1].Find("// ") + 3);
                            if (!paramName.Empty() && !paramType.Empty())
                                Log::WriteRaw("- %" + paramName + " : " + paramType + "\n");
                        }
                    }
                }
            }

            Log::WriteRaw("\n");
        }

        Log::WriteRaw("\n\\page AttributeList Attribute list\n");

        const HashMap<StringHash, Vector<AttributeInfo> >& attributes = context_->GetAllAttributes();

        Vector<String> objectTypes;
        for (HashMap<StringHash, Vector<AttributeInfo> >::ConstIterator i = attributes.Begin(); i != attributes.End(); ++i)
            objectTypes.Push(context_->GetTypeName(i->first_));

        Sort(objectTypes.Begin(), objectTypes.End());

        for (unsigned i = 0; i < objectTypes.Size(); ++i)
        {
            const Vector<AttributeInfo>& attrs = attributes.Find(objectTypes[i])->second_;
            unsigned usableAttrs = 0;
            for (unsigned j = 0; j < attrs.Size(); ++j)
            {
                // Attributes that are not shown in the editor are typically internal and not usable for eg. attribute
                // animation
                if (attrs[j].mode_ & AM_NOEDIT)
                    continue;
                ++usableAttrs;
            }

            if (!usableAttrs)
                continue;

            Log::WriteRaw("\n### " + objectTypes[i] + "\n");

            for (unsigned j = 0; j < attrs.Size(); ++j)
            {
                if (attrs[j].mode_ & AM_NOEDIT)
                    continue;
                // Prepend each word in the attribute name with % to prevent unintended links
                Vector<String> nameParts = attrs[j].name_.Split(' ');
                for (unsigned k = 0; k < nameParts.Size(); ++k)
                {
                    if (nameParts[k].Length() > 1 && IsAlpha((unsigned)nameParts[k][0]))
                        nameParts[k] = "%" + nameParts[k];
                }
                String name;
                name.Join(nameParts, " ");
                String type = Variant::GetTypeName(attrs[j].type_);
                // Variant typenames are all uppercase. Convert primitive types to the proper lowercase form for the documentation
                if (type == "Int" || type == "Bool" || type == "Float")
                    type[0] = (char)ToLower((unsigned)type[0]);

                Log::WriteRaw("- " + name + " : " + type + "\n");
            }
        }

        Log::WriteRaw("\n");
    }

    if (mode == DOXYGEN)
        Log::WriteRaw("\n\\page ScriptAPI Scripting API\n\n");
    else if (mode == C_HEADER)
        Log::WriteRaw(
            "// Script API header intended to be 'force included' in IDE for AngelScript content assist / code completion\n\n"
                "#define int8 signed char\n"
                "#define int16 signed short\n"
                "#define int64 long\n"
                "#define uint8 unsigned char\n"
                "#define uint16 unsigned short\n"
                "#define uint64 unsigned long\n"
                "#define null 0\n");

    unsigned types = scriptEngine_->GetObjectTypeCount();
    Vector<Pair<String, unsigned> > sortedTypes;
    for (unsigned i = 0; i < types; ++i)
    {
        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(i);
        if (type)
        {
            String typeName(type->GetName());
            sortedTypes.Push(MakePair(typeName, i));
        }
    }
    Sort(sortedTypes.Begin(), sortedTypes.End());

    if (mode == DOXYGEN)
    {
        Log::WriteRaw("\\section ScriptAPI_TableOfContents Table of contents\n"
            "\\ref ScriptAPI_ClassList \"Class list\"<br>\n"
            "\\ref ScriptAPI_Classes \"Classes\"<br>\n"
            "\\ref ScriptAPI_Enums \"Enumerations\"<br>\n"
            "\\ref ScriptAPI_GlobalFunctions \"Global functions\"<br>\n"
            "\\ref ScriptAPI_GlobalProperties \"Global properties\"<br>\n"
            "\\ref ScriptAPI_GlobalConstants \"Global constants\"<br>\n\n");

        Log::WriteRaw("\\section ScriptAPI_ClassList Class list\n\n");

        for (unsigned i = 0; i < sortedTypes.Size(); ++i)
        {
            asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(sortedTypes[i].second_);
            if (type)
            {
                String typeName(type->GetName());
                Log::WriteRaw("<a href=\"#Class_" + typeName + "\"><b>" + typeName + "</b></a>\n");
            }
        }

        Log::WriteRaw("\n\\section ScriptAPI_Classes Classes\n");
    }
    else if (mode == C_HEADER)
        Log::WriteRaw("\n// Classes\n");

    for (unsigned i = 0; i < sortedTypes.Size(); ++i)
    {
        asIObjectType* type = scriptEngine_->GetObjectTypeByIndex(sortedTypes[i].second_);
        if (type)
        {
            String typeName(type->GetName());
            Vector<String> methodDeclarations;
            Vector<PropertyInfo> propertyInfos;

            if (mode == DOXYGEN)
            {
                Log::WriteRaw("<a name=\"Class_" + typeName + "\"></a>\n");
                Log::WriteRaw("\n### " + typeName + "\n");
            }
            else if (mode == C_HEADER)
            {
                ///\todo Find a cleaner way to do this instead of hardcoding
                if (typeName == "Array")
                    Log::WriteRaw("\ntemplate <class T> class " + typeName + "\n{\n");
                else
                    Log::WriteRaw("\nclass " + typeName + "\n{\n");
            }

            unsigned methods = type->GetMethodCount();
            for (unsigned j = 0; j < methods; ++j)
            {
                asIScriptFunction* method = type->GetMethodByIndex(j);
                String methodName(method->GetName());
                String declaration(method->GetDeclaration());

                // Recreate tab escape sequences
                declaration.Replace("\t", "\\t");

                if (methodName.Contains("get_") || methodName.Contains("set_"))
                    ExtractPropertyInfo(methodName, declaration, propertyInfos);
                else
                {
                    // Sanitate the method name. \todo For now, skip the operators
                    if (!declaration.Contains("::op"))
                    {
                        String prefix(typeName + "::");
                        declaration.Replace(prefix, "");
                        ///\todo Is there a better way to mark deprecated API bindings for AngelScript?
                        unsigned posBegin = declaration.FindLast("const String&in = \"deprecated:");
                        if (posBegin != String::NPOS)
                        {
                            // Assume this 'mark' is added as the last parameter
                            unsigned posEnd = declaration.Find(')', posBegin);
                            if (posEnd != String::NPOS)
                            {
                                declaration.Replace(posBegin, posEnd - posBegin, "");
                                posBegin = declaration.Find(", ", posBegin - 2);
                                if (posBegin != String::NPOS)
                                    declaration.Replace(posBegin, 2, "");
                                if (mode == DOXYGEN)
                                    declaration += " // deprecated";
                                else if (mode == C_HEADER)
                                    declaration = "/* deprecated */\n" + declaration;
                            }
                        }
                        methodDeclarations.Push(declaration);
                    }
                }
            }

            // Assume that the same property is never both an accessor property, and a direct one
            unsigned properties = type->GetPropertyCount();
            for (unsigned j = 0; j < properties; ++j)
            {
                const char* propertyName;
                const char* propertyDeclaration;
                int typeId;

                type->GetProperty(j, &propertyName, &typeId);
                propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);

                PropertyInfo newInfo;
                newInfo.name_ = String(propertyName);
                newInfo.type_ = String(propertyDeclaration);
                newInfo.read_ = newInfo.write_ = true;
                propertyInfos.Push(newInfo);
            }

            Sort(methodDeclarations.Begin(), methodDeclarations.End(), ComparePropertyStrings);
            Sort(propertyInfos.Begin(), propertyInfos.End(), ComparePropertyInfos);

            if (!methodDeclarations.Empty())
            {
                if (mode == DOXYGEN)
                    Log::WriteRaw("\nMethods:\n\n");
                else if (mode == C_HEADER)
                    Log::WriteRaw("// Methods:\n");
                for (unsigned j = 0; j < methodDeclarations.Size(); ++j)
                    OutputAPIRow(mode, methodDeclarations[j]);
            }

            if (!propertyInfos.Empty())
            {
                if (mode == DOXYGEN)
                    Log::WriteRaw("\nProperties:\n\n");
                else if (mode == C_HEADER)
                    Log::WriteRaw("\n// Properties:\n");
                for (unsigned j = 0; j < propertyInfos.Size(); ++j)
                {
                    String remark;
                    String cppdoc;
                    if (!propertyInfos[j].write_)
                        remark = "readonly";
                    else if (!propertyInfos[j].read_)
                        remark = "writeonly";
                    if (!remark.Empty())
                    {
                        if (mode == DOXYGEN)
                        {
                            remark = " // " + remark;
                        }
                        else if (mode == C_HEADER)
                        {
                            cppdoc = "/* " + remark + " */\n";
                            remark.Clear();
                        }
                    }

                    OutputAPIRow(mode, cppdoc + propertyInfos[j].type_ + " " + propertyInfos[j].name_ + remark);
                }
            }

            if (mode == DOXYGEN)
                Log::WriteRaw("\n");
            else if (mode == C_HEADER)
                Log::WriteRaw("};\n");
        }
    }

    Vector<PropertyInfo> globalPropertyInfos;
    Vector<String> globalFunctions;

    unsigned functions = scriptEngine_->GetGlobalFunctionCount();
    for (unsigned i = 0; i < functions; ++i)
    {
        asIScriptFunction* function = scriptEngine_->GetGlobalFunctionByIndex(i);
        String functionName(function->GetName());
        String declaration(function->GetDeclaration());

        // Recreate tab escape sequences
        declaration.Replace("\t", "\\t");

        if (functionName.Contains("set_") || functionName.Contains("get_"))
            ExtractPropertyInfo(functionName, declaration, globalPropertyInfos);
        else
            globalFunctions.Push(declaration);
    }

    Sort(globalFunctions.Begin(), globalFunctions.End(), ComparePropertyStrings);
    Sort(globalPropertyInfos.Begin(), globalPropertyInfos.End(), ComparePropertyInfos);

    if (mode == DOXYGEN)
        Log::WriteRaw("\\section ScriptAPI_Enums Enumerations\n");
    else if (mode == C_HEADER)
        Log::WriteRaw("\n// Enumerations\n");

    unsigned enums = scriptEngine_->GetEnumCount();
    Vector<Pair<String, unsigned> > sortedEnums;
    for (unsigned i = 0; i < enums; ++i)
    {
        int typeId;
        sortedEnums.Push(MakePair(String(scriptEngine_->GetEnumByIndex(i, &typeId)), i));
    }
    Sort(sortedEnums.Begin(), sortedEnums.End());

    for (unsigned i = 0; i < sortedEnums.Size(); ++i)
    {
        int typeId = 0;
        if (mode == DOXYGEN)
            Log::WriteRaw("\n### " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n\n");
        else if (mode == C_HEADER)
            Log::WriteRaw("\nenum " + String(scriptEngine_->GetEnumByIndex(sortedEnums[i].second_, &typeId)) + "\n{\n");

        for (unsigned j = 0; j < (unsigned)scriptEngine_->GetEnumValueCount(typeId); ++j)
        {
            int value = 0;
            const char* name = scriptEngine_->GetEnumValueByIndex(typeId, j, &value);
            OutputAPIRow(mode, String(name), false, ",");
        }

        if (mode == DOXYGEN)
            Log::WriteRaw("\n");
        else if (mode == C_HEADER)
            Log::WriteRaw("};\n");
    }

    if (mode == DOXYGEN)
        Log::WriteRaw("\\section ScriptAPI_GlobalFunctions Global functions\n");
    else if (mode == C_HEADER)
        Log::WriteRaw("\n// Global functions\n");

    for (unsigned i = 0; i < globalFunctions.Size(); ++i)
        OutputAPIRow(mode, globalFunctions[i]);

    if (mode == DOXYGEN)
        Log::WriteRaw("\\section ScriptAPI_GlobalProperties Global properties\n");
    else if (mode == C_HEADER)
        Log::WriteRaw("\n// Global properties\n");

    for (unsigned i = 0; i < globalPropertyInfos.Size(); ++i)
        OutputAPIRow(mode, globalPropertyInfos[i].type_ + " " + globalPropertyInfos[i].name_, true);

    if (mode == DOXYGEN)
        Log::WriteRaw("\\section ScriptAPI_GlobalConstants Global constants\n");
    else if (mode == C_HEADER)
        Log::WriteRaw("\n// Global constants\n");

    Vector<String> globalConstants;
    unsigned properties = scriptEngine_->GetGlobalPropertyCount();
    for (unsigned i = 0; i < properties; ++i)
    {
        const char* propertyName;
        const char* propertyDeclaration;
        int typeId;
        scriptEngine_->GetGlobalPropertyByIndex(i, &propertyName, 0, &typeId);
        propertyDeclaration = scriptEngine_->GetTypeDeclaration(typeId);

        String type(propertyDeclaration);
        globalConstants.Push(type + " " + String(propertyName));
    }

    Sort(globalConstants.Begin(), globalConstants.End(), ComparePropertyStrings);

    for (unsigned i = 0; i < globalConstants.Size(); ++i)
        OutputAPIRow(mode, globalConstants[i], true);

    if (mode == DOXYGEN)
        Log::WriteRaw("*/\n\n}\n");
}