// Adds a FAYT Quick Key which lists the folders directly below a specified path, using custom rules to generate // shorthand versions of them. Intended for quick navigation regardless of the current folder. // Given a folder name "Sega Megadrive (Genesis, 32X)", path suggestions will be added to the list with // the full name; then the first word will be removed and the name on its own will be added, with a syntax // for a alternative names in brackets after the main name. // // - "Sega Megadrive (Genesis, 32X)" // - "Megadrive" // - "Genesis" // - "32X" // Suggestions using just the initials (capital letters and numbers) after the first word will also be generated: // // - "Sony PlayStation 1 (PSX)" // - "PlayStation 1" // - "PS1" // - "PSX" // // - "Nintendo Game Boy Advance" // - "Game Boy Advance" // - "GBA" // An optional " - " can be included between the first and second words, and will be stripped out when removing the first word: // // - "Arcade - MAME" // - "MAME" // If the chosen folder has a "Games" folder below it, navigation will go into it instead of the chosen folder itself. // (Configurable via g_strAutoChildPath below, currently.) // TODO: Configuration UI. Including making the command name etc. configurable. Multiple commands/folder sets from one script? // TODO: A way to refresh, since it caches the folder on initial use. // Path whose sub-directories should be turned into suggestions. var g_path = "\\\\Quad\\HH$\\Essentials\\Games\\Emulation"; // If a "Games" subdir exists under the chosen path, go to it instead of the chosen path. var g_strAutoChildPath = "Games"; // Global cache variables. Do not change. var g_sugs = null; var g_paths = null; function OnInit(initData) { initData.name = "FAYT Emulator Platforms"; initData.version = "1.0"; initData.copyright = "(c) 2023 Leo Davidson"; // initData.url = "https://resource.dopus.com/c/buttons-scripts/16"; initData.desc = ""; initData.default_enable = true; initData.min_version = "13.0"; } function OnAddCommands(addCmdData) { var cmd = addCmdData.AddCommand(); cmd.name = "FAYTEmus"; cmd.method = "OnFAYTEmus"; cmd.desc = ""; cmd.label = "FAYTEmus"; cmd.template = ""; cmd.hide = true; // Hide from button editor menus cmd.icon = "script"; var fayt = cmd.fayt; fayt.enable = true; fayt.key = "$"; fayt.textcolor = "#000000"; fayt.backcolor = "#FFFFFF"; fayt.label = "Emulator Platforms"; // fayt.flags - optional Map of flags (flag value -> label) - need to use DOpus.Create.Map fayt.realtime = true; // Call on each keypress, not just after return } function OnFAYTEmus(scriptFAYTData) { if (scriptFAYTData.fayt != "FAYTEmus") { DOpus.Output('Unexpected FAYT: "' + scriptFAYTData.fayt + '"'); return; } if (g_sugs == null || g_paths == null) { BuildSuggestions(); if (g_sugs == null || g_paths == null) { return; } } var tab = scriptFAYTData.tab; if (scriptFAYTData.key == "return") { ExecuteSuggestionInTab(tab, scriptFAYTData.cmdline); return; } if (scriptFAYTData.suggest) { tab.UpdateFAYTSuggestions(g_sugs); } } function ExecuteSuggestionInTab(tab, name) { if (!g_paths.exists(name)) { return; } var path = g_paths(name); if (g_strAutoChildPath && g_strAutoChildPath.length>0 && DOpus.FSUtil.GetType(path + '\\' + g_strAutoChildPath) == "dir") { path = path + '\\' + g_strAutoChildPath; } var cmd = DOpus.Create.Command(); cmd.SetSourceTab(tab); cmd.RunCommand('Go PATH="' + path + '"'); } function BuildSuggestions() { var folderEnum = DOpus.FSUtil.ReadDir(g_path); if (folderEnum.complete) { return; } var vecFolders = folderEnum.Next(-1); var folderCount = vecFolders.length; if (folderCount == 0) { return; } g_sugs = DOpus.Create.Map(); g_paths = DOpus.Create.Map(); // Whole Name. e.g. "Sega Megadrive (Genesis, 32X)" for (var i = 0; i < folderCount; ++i) { if (!vecFolders[i].is_dir) { continue; } ProcessName(vecFolders[i].display_name, vecFolders[i].realpath, null); } // If the name has two or more words, remove the first word, leaving e.g. "Megadrive (Genesis, 32X)". // If what's left ends in brackets, turn it into a list, e.g. "Megadrive, Genesis, 32X". // Add each of those to the map (if they don't already exist), and generate their "initials" for // consideration in the next step. var jsvecNameList = []; var jsvecInitials = []; for (var i = 0; i < folderCount; ++i) { if (!vecFolders[i].is_dir) { continue; } var displayName = vecFolders[i].display_name; var splitPair = SplitWords(displayName); if (splitPair != null) { if (!SplitWordList(splitPair.rest, jsvecNameList)) { ProcessName(splitPair.rest, vecFolders[i].realpath, jsvecInitials); } else { for (var j = 0; j < jsvecNameList.length; ++j) { ProcessName(jsvecNameList[j], vecFolders[i].realpath, jsvecInitials); } } } } // Add the "initials" versions of things. e.g. "GBA" for "Game Boy Advance" and "PS2" for "PlayStation 2". // Only add things which don't already exist, so real names aren't overwritten by generated initials. var initialsCount = jsvecInitials.length; for (var i = 0; i < initialsCount; ++i) { ProcessName(jsvecInitials[i].initials, jsvecInitials[i].path, null); } } function ProcessName(name, path, jsvecInitials) { if (g_sugs.exists(name)) { return; } g_sugs(name) = GetLastPathPart(path); g_paths(name) = path; if (jsvecInitials == null) { return; } var initials = GenerateInitials(name); if (initials == null) { return; } jsvecInitials.push( { initials: initials, path: path } ); } function TrimSpace(s) { return s.replace(/^\s+|\s+$/gm,''); } function SplitWords(s) { var spaceIdx = s.indexOf(" "); if (spaceIdx == -1) { return null; } var firstWord = s.slice(0, spaceIdx); // Allow an optional " - " between the first and second words, which is removed after the split. var rest = s.slice(spaceIdx+1).replace(/^[- ]* (.+)$/,'$1'); if (rest == "") { return null; } return { first: firstWord, rest: rest }; } function SplitWordList(s, vecOut) { var openPos = s.indexOf(" ("); if (openPos == -1 || openPos == 0 || s.slice(-1) != ")") { return false; } vecOut.length = 0; vecOut.push(TrimSpace(s.slice(0,openPos))); var wordList = s.slice(openPos+2,-1).split(", "); for (var i = 0; i < wordList.length; ++i) { var altName = TrimSpace(wordList[i]); if (altName.length > 0) { vecOut.push(altName); } } return true; } function GetLastPathPart(s) { s = s + ""; while (s.length > 0 && s.slice(-1) == "\\") { s = s.slice(0,-1); } var slashPos = s.lastIndexOf("\\"); if (slashPos != -1) { s = s.slice(slashPos+1); } return s; } function GenerateInitials(name) { var initials = name.replace(/[^A-Z0-9]/g,''); if (initials.length <= 1 || initials == name) { return null; } return initials; }