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; }
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); } } }
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; }
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; }
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(); }
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 }
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; }
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())); } }
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"); }