void ResourceEditor::SetModified(bool modified)
{
    modified_ = modified;
    if (modified)
    {
        String filename = GetFileNameAndExtension(fullpath_);
        filename += "*";
        button_->SetText(filename.CString());
    }
    else
    {
        String filename = GetFileNameAndExtension(fullpath_);
        button_->SetText(filename.CString());
    }
}
Esempio n. 2
0
void ResourceCache::RemovePackageFile(const String& fileName, bool releaseResources, bool forceRelease)
{
    // Compare the name and extension only, not the path
    String fileNameNoPath = GetFileNameAndExtension(fileName);
    
    for (Vector<SharedPtr<PackageFile> >::Iterator i = packages_.Begin(); i != packages_.End(); ++i)
    {
        if (!GetFileNameAndExtension((*i)->GetName()).Compare(fileNameNoPath, false))
        {
            if (releaseResources)
                ReleasePackageResources(*i, forceRelease);
            LOGINFO("Removed resource package " + (*i)->GetName());
            packages_.Erase(i);
            return;
        }
    }
}
void CSModuleWriter::WriteIncludes(String& source)
{

    Vector<String>& includes = module_->includes_;
    for (unsigned i = 0; i < includes.Size(); i++)
    {
        if (includes[i].StartsWith("<"))
            source.AppendWithFormat("#include %s\n", includes[i].CString());
        else
            source.AppendWithFormat("#include \"%s\"\n", includes[i].CString());
    }

    Vector<JSBHeader*> allheaders;

    HashMap<StringHash, SharedPtr<JSBEnum> >::Iterator eitr = module_->enums_.Begin();
    while (eitr != module_->enums_.End())
    {
        allheaders.Push(eitr->second_->GetHeader());
        eitr++;
    }

    HashMap<StringHash, SharedPtr<JSBClass> >::Iterator citr = module_->classes_.Begin();
    while (citr != module_->classes_.End())
    {
        allheaders.Push(citr->second_->GetHeader());
        citr++;
    }

    Vector<JSBHeader*> included;

    for (unsigned i = 0; i < allheaders.Size(); i++)
    {
        JSBHeader* header = allheaders.At(i);

        if (included.Contains(header))
            continue;

        String headerPath = GetPath(header->GetFilePath());

        String headerfile = GetFileNameAndExtension(header->GetFilePath());

        JSBind* jsbind = header->GetSubsystem<JSBind>();

        headerPath.Replace(jsbind->GetSourceRootFolder() + "Source/", "");

        source.AppendWithFormat("#include <%s%s>\n", headerPath.CString(), headerfile.CString());

        included.Push(header);
    }

    source += ToString("\n#include \"CSPackage%s.h\"\n", module_->GetPackage()->GetName().CString());

}
Esempio n. 4
0
void JSBModule::WriteIncludes(String& source)
{

    Vector<JSBHeader*> allheaders;

    for (unsigned i = 0; i < enums_.Size(); i++)
    {
        allheaders.Push(enums_.At(i)->header_);
    }

    for (unsigned i = 0; i < classes_.Size(); i++)
    {
        allheaders.Push(classes_.At(i)->GetHeader());
    }

    Vector<JSBHeader*> included;
    for (unsigned i = 0; i < allheaders.Size(); i++)
    {
        JSBHeader* header = allheaders.At(i);

        if (included.Contains(header))
            continue;

        String headerPath = GetPath(header->filepath_);

		String headerfile = GetFileNameAndExtension(header->filepath_);

        headerPath.Replace(JSBind::ROOT_FOLDER + "/Source/Atomic/", "Atomic/");

        source.AppendWithFormat("#include <%s%s>\n", headerPath.CString(), headerfile.CString());

        included.Push(header);
    }

    for (unsigned i = 0; i < includes_.Size(); i++)
    {
        if (includes_[i].StartsWith("<"))
            source.AppendWithFormat("#include %s\n", includes_[i].CString());
        else
            source.AppendWithFormat("#include \"%s\"\n", includes_[i].CString());
    }
}
void IssuesWidget::UpdateIssues()
{
    AEJavascript* aejs = GetSubsystem<AEJavascript>();
    const Vector<JSError>& errors = aejs->GetJSErrors();

    issueList_->DeleteAllItems();

    for (unsigned i = 0; i < errors.Size(); i++)
    {
        const JSError& error = errors[i];

        String errorString;
        String filename = GetFileNameAndExtension(error.fullpath);

        errorString.AppendWithFormat("%s - %s - Line: %i Column: %i",
                                     filename.CString(), error.message.CString(), error.line, error.column);

        issueList_->AddItem(errorString.CString(), NULL, TBID(i));
    }

}
ResourceEditor::ResourceEditor(Context* context, const String& fullpath, UITabContainer *container):
    Object(context), fullpath_(fullpath), container_(container),
    editorTabLayout_(0), rootContentWidget_(0), button_(0), modified_(false)
{

    String filename = GetFileNameAndExtension(fullpath_);

    editorTabLayout_ = new EditorTabLayout();
    editorTabLayout_->SetID(TBIDC("tab"));

    button_ = new UIButton(context_);
    button_->SetText(filename.CString());
    button_->SetSqueezable(true);
    button_->SetSkinBg("TBButton.flat");
    button_->SetValue(1);
    editorTabLayout_->AddChild(button_->GetInternalWidget());

    TBButton* closebutton = new TBButton();
    editorTabLayout_->AddChild(closebutton);
    closebutton->SetSkinBg(TBIDC("TBWindow.close"));
    closebutton->SetIsFocusable(false);
    closebutton->SetID(TBIDC("tabclose"));

    editorTabLayout_->editor_ = this;
    editorTabLayout_->button_ = (TBButton*) button_->GetInternalWidget();
    editorTabLayout_->close_ = closebutton;
    editorTabLayout_->container_ = (TBTabContainer*) container->GetInternalWidget();

    ((TBTabContainer*)container_->GetInternalWidget())->GetTabLayout()->AddChild(editorTabLayout_);

    rootContentWidget_ = new UIWidget(context_);
    rootContentWidget_->SetGravity(UI_GRAVITY_ALL);
    container_->GetContentRoot()->AddChild(rootContentWidget_);

    SubscribeToEvent(E_FILECHANGED, HANDLER(ResourceEditor, HandleFileChanged));
}
 bool RequestClose()
 {
     if (editor_->HasUnsavedModifications())
     {
         TBMessageWindow *msg_win = new TBMessageWindow(this, TBIDC("unsaved_modifications_dialog"));
         TBMessageWindowSettings settings(TB_MSG_NONE, TBID(uint32(0)));
         settings.dimmer = true;
         settings.styling = true;
         String windowString = Atomic::ToString("%s has unsaved modifications.\nDo you wish to discard them and close?", GetFileNameAndExtension(editor_->GetFullPath()).CString());
         msg_win->Show("Unsaved Modifications",  windowString.CString(), &settings, 640, 360);
         msg_win->AddButtonLeft("dont_save", false);
         msg_win->AddButton("TBMessageWindow.cancel", false);
         msg_win->AddButton("save", true);
         return false;
     }
     else
     {
         editor_->Close(container_->GetNumPages()>1);
         return true;
     }
 }
Esempio n. 8
0
	File::File(std::string filePath){
		m_hasExtension = GetFileNameAndExtension(filePath, m_name, m_extension);
		m_size = GetFileSize(filePath);
	}
Esempio n. 9
0
void Urho3DPlayer::Setup()
{
    FileSystem* filesystem = GetSubsystem<FileSystem>();

    // Read command line from a file if no arguments given. This is primarily intended for mobile platforms.
    // Note that the command file name uses a hardcoded path that does not utilize the resource system
    // properly (including resource path prefix), as the resource system is not yet initialized at this point
    const String commandFileName = filesystem->GetProgramDir() + "Data/CommandLine.txt";
    if (GetArguments().Empty() && filesystem->FileExists(commandFileName))
    {
        SharedPtr<File> commandFile(new File(context_, commandFileName));
        String commandLine = commandFile->ReadLine();
        commandFile->Close();
        ParseArguments(commandLine, false);
        // Reparse engine startup parameters now
        engineParameters_ = Engine::ParseParameters(GetArguments());
    }

    // Check for script file name
    const Vector<String>& arguments = GetArguments();
    String scriptFileName;
    if (arguments.Size() && arguments[0][0] != '-')
        scriptFileName_ = GetInternalPath(arguments[0]);

    // Show usage if not found
    if (scriptFileName_.Empty())
    {
        ErrorExit("Usage: Urho3DPlayer <scriptfile> [options]\n\n"
            "The script file should implement the function void Start() for initializing the "
            "application and subscribing to all necessary events, such as the frame update.\n"
            #ifndef WIN32
            "\nCommand line options:\n"
            "-x <res>     Horizontal resolution\n"
            "-y <res>     Vertical resolution\n"
            "-m <level>   Enable hardware multisampling\n"
            "-v           Enable vertical sync\n"
            "-t           Enable triple buffering\n"
            "-w           Start in windowed mode\n"
            "-s           Enable resizing when in windowed mode\n"
            "-q           Enable quiet mode which does not log to standard output stream\n"
            "-b <length>  Sound buffer length in milliseconds\n"
            "-r <freq>    Sound mixing frequency in Hz\n"
            "-p <paths>   Resource path(s) to use, separated by semicolons\n"
            "-ap <paths>  Autoload resource path(s) to use, seperated by semicolons\n"
            "-log <level> Change the log level, valid 'level' values are 'debug', 'info', 'warning', 'error'\n"
            "-ds <file>   Dump used shader variations to a file for precaching\n"
            "-mq <level>  Material quality level, default 2 (high)\n"
            "-tq <level>  Texture quality level, default 2 (high)\n"
            "-tf <level>  Texture filter mode, default 2 (trilinear)\n"
            "-af <level>  Texture anisotropy level, default 4. Also sets anisotropic filter mode\n"
            "-gl2         Force OpenGL 2 use even if OpenGL 3 is available\n"
            "-flushgpu    Flush GPU command queue each frame. Effective only on Direct3D\n"
            "-borderless  Borderless window mode\n"
            "-headless    Headless mode. No application window will be created\n"
            "-landscape   Use landscape orientations (iOS only, default)\n"
            "-portrait    Use portrait orientations (iOS only)\n"
            "-prepass     Use light pre-pass rendering\n"
            "-deferred    Use deferred rendering\n"
            "-renderpath <name> Use the named renderpath (must enter full resource name)\n"
            "-lqshadows   Use low-quality (1-sample) shadow filtering\n"
            "-noshadows   Disable shadow rendering\n"
            "-nolimit     Disable frame limiter\n"
            "-nothreads   Disable worker threads\n"
            "-nosound     Disable sound output\n"
            "-noip        Disable sound mixing interpolation\n"
            "-touch       Touch emulation on desktop platform\n"
            #endif
        );
    }
    else
    {
        // Use the script file name as the base name for the log file
        engineParameters_["LogName"] = filesystem->GetAppPreferencesDir("urho3d", "logs") + GetFileNameAndExtension(scriptFileName_) + ".log";
    }

    // Construct a search path to find the resource prefix with two entries:
    // The first entry is an empty path which will be substituted with program/bin directory -- this entry is for binary when it is still in build tree
    // The second and third entries are possible relative paths from the installed program/bin directory to the asset directory -- these entries are for binary when it is in the Urho3D SDK installation location
    if (!engineParameters_.Contains("ResourcePrefixPaths"))
        engineParameters_["ResourcePrefixPaths"] = ";../share/Resources;../share/Urho3D/Resources";
}
Esempio n. 10
0
bool ParticleEffect2D::Save(Serializer& dest) const
{
    if (!sprite_)
        return false;

    XMLFile xmlFile(context_);
    XMLElement rootElem = xmlFile.CreateRoot("particleEmitterConfig");

    String fileName = GetFileNameAndExtension(sprite_->GetName());
    rootElem.CreateChild("texture").SetAttribute("name", fileName);

    WriteVector2(rootElem, "sourcePosition", Vector2::ZERO);
    WriteVector2(rootElem, "sourcePositionVariance", sourcePositionVariance_);

    WriteFloat(rootElem, "speed", speed_);
    WriteFloat(rootElem, "speedVariance", speedVariance_);

    WriteFloat(rootElem, "particleLifeSpan", particleLifeSpan_);
    WriteFloat(rootElem, "particleLifespanVariance", particleLifespanVariance_);

    WriteFloat(rootElem, "angle", angle_);
    WriteFloat(rootElem, "angleVariance", angleVariance_);

    WriteVector2(rootElem, "gravity", gravity_);

    WriteFloat(rootElem, "radialAcceleration", radialAcceleration_);
    WriteFloat(rootElem, "tangentialAcceleration", tangentialAcceleration_);

    WriteFloat(rootElem, "radialAccelVariance", radialAccelVariance_);
    WriteFloat(rootElem, "tangentialAccelVariance", tangentialAccelVariance_);

    WriteColor(rootElem, "startColor", startColor_);
    WriteColor(rootElem, "startColorVariance", startColorVariance_);

    WriteColor(rootElem, "finishColor", finishColor_);
    WriteColor(rootElem, "finishColorVariance", finishColorVariance_);

    WriteInt(rootElem, "maxParticles", maxParticles_);

    WriteFloat(rootElem, "startParticleSize", startParticleSize_);
    WriteFloat(rootElem, "startParticleSizeVariance", startParticleSizeVariance_);

    WriteFloat(rootElem, "finishParticleSize", finishParticleSize_);
    // Typo in pex file
    WriteFloat(rootElem, "FinishParticleSizeVariance", FinishParticleSizeVariance_);

    float duration = duration_;
    if (duration == M_INFINITY)
        duration = -1.0f;
    WriteFloat(rootElem, "duration", duration);
    WriteInt(rootElem, "emitterType", (int)emitterType_);

    WriteFloat(rootElem, "maxRadius", maxRadius_);
    WriteFloat(rootElem, "maxRadiusVariance", maxRadiusVariance_);
    WriteFloat(rootElem, "minRadius", minRadius_);
    WriteFloat(rootElem, "minRadiusVariance", minRadiusVariance_);

    WriteFloat(rootElem, "rotatePerSecond", rotatePerSecond_);
    WriteFloat(rootElem, "rotatePerSecondVariance", rotatePerSecondVariance_);

    WriteInt(rootElem, "blendFuncSource", srcBlendFuncs[blendMode_]);
    WriteInt(rootElem, "blendFuncDestination", destBlendFuncs[blendMode_]);

    WriteFloat(rootElem, "rotationStart", rotationStart_);
    WriteFloat(rootElem, "rotationStartVariance", rotationStartVariance_);

    WriteFloat(rootElem, "rotationEnd", rotationEnd_);
    WriteFloat(rootElem, "rotationEndVariance", rotationEndVariance_);

	return xmlFile.Save(dest);
}
Esempio n. 11
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");
}