I am using xEdit.4.1.4c.EXTREMELY.EXPERIMENTAL and I can run it through the Vortex dashboard xEdit.exe -sse When I want to use your extension I get "Vortex could not find TES5VREdit. Please check the tool in your starter dashlet is pointing to the right place."
How can I get it working with pure xEdit? I tried duplicating the exe or making simlink to SSEEdit.exe or TES5VREdit.exe without success.
edit: Same for 4.0.4c version edit2: there is something that I should miss because I have the same problem with SSEEdit from Nexus directly...
@Pickysaurus The xEdit Cleaning Extension allows for cleaning of Bethesda game based plugin and master files within Vortex, however this occurs on an individual level which is pretty time consuming. However there's a plugin for Mod Organizer 2 which allows for batch plugin and master files cleaning, however Vortex even with the extension can't do this.
Is it possible please for the extension developed to have a feature added which allows for the user, to select multiple items from plugins list in Vortex, so that they can be batch cleaned? So that they can be cleaned one after the other in the batch, without the user needing to do each one individually one at a time.
Other than this critique the extension is truly useful and good bit of work when running Vortex in order to manage the modifications on the user's computer.
Installing it from Vortex for FO4 installs this extension (validated when I follow it back to Nexus from Vortex). Upon clicking any Vortex xEdit button you get a Vortex error "xEdit not found".
The directions for this extension say to extract "ElminsterAU's xEdit 7z archive" to %appdata%\Vortex\plugins. It's not clear if you are supposed to put the files in the /plugins folder itself or in a subfolder called: "%appdata%\Vortex\plugins\Vortex Extension Update - xEdit Cleaning Extension v1.0.13" which is what Vortex does automatically. I assume this is what is supposed to happen even though the extension author does not specify it.
I downloaded and moved the files from the "ElminsterAU's xEdit 7z archive" into the folder Vortex created and still get the error.
Getting more info from the error message, it is looking for FO4edit which is NOT in the "ElminsterAU's xEdit 7z archive" the extension author offers as a link. I already had FO4Edit, so I moved the FO4Edit files into that same folder and still get the "xEdit not found" error.
I can find no place in Vortex, nor in the Vortex plugin json file where the specific tool on the dashlet is mapped to a specific path and executable so cannot resolve this further until I find another approach. Where is a tool in Vortex mapped to a specific path/executable?
*** Update later that evening.... It appears the executable mappings are done using vortex-api calls from the index.js included with the extension. The extension index.js does not get the configuration for xEdit from the vortex-api and then fails as a result of that. The vortex-api only reports BodySlide and F4SE on my installation as "tools" and does not include the xEdit extension that Vortex installed. I can't tell if the problem is with the api or the getSafe() call from the index.js. I can't find documentation on the getSafe() call on GitHub.
Here are the some debug log statements I put in the index.js to go to the vortex.log to demonstrate this: Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const tools = vortex_api_1.util.getSafe(state, ['settings', 'gameMode', 'discovered', activeGameId, 'tools'], undefined);: Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] Object.keys(tools) Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] bodyslide,f4se ;-----------------------------------------------------------------------------------Note that Object.keys(tools) only reports BS and f4se, not xEdit ext which it installed
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const xEditKey = tools ? Object.keys(tools).find(t => t === xEditData.exeName) : undefined;: Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] xEditKey Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] undefined ;------------------------------------------------now everything is undefined
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const xEditTool = xEditKey ? tools[xEditKey] : undefined;: Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] xEditTool Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] undefined ;------------------------------------------------again undefined
Thu, 06 Jul 2023 06:51:34 GMT - error: xEdit not found text=Vortex could not find FO4Edit. Please check the tool in your starter dashlet is pointing to the right place., wrap=true ;------------------------------------------------Vortex throws the error in the UI because it can't find a path to the exe, it's undefined
I'm not sure where you're reading that. This EXTENSION goes in the plugins folder. XEdit can be installed anywhere and you must configure the shortcut under "Tools" on the dashboard. I suspect you're just doing it wrong tbh.
Thank you!!! I had no idea there was that section on the dashboard. And actually I don't use the dashboard for anything and forgot it was even there. Works like a charm. Learn something new everyday!
It looks like it does not work with GOG version :/ I mean SSEEdit does not find the INIs with new paths and this plugin does not take command line arguments into account.
The extension ignores additional command line parameters that have been set in the Vortex tools which would allow to setup custom paths for INIs, loadorder.txt etc..
In the event someone else finds this post in a hunt to diagnose this, there IS a silly workaround (currently): In the file %AppData%\Vortex\Plugins\Vortex Extension Update - xEdit Cleaning Extension v1.0.13\index.jsfind the section marked "const xEditParams" Change it to this (AND FOR THE LOVE OF TALOS, PLEASE CHANGE "YOUR-LOGIN" TO MATCH YOUR USERNAME!): const xEditParams = { "quickautoclean": ["{gamePara}", "-quickautoclean", "-autoexit", "-autoload", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "{pluginName}"], "autoloadplugin": ["{gamePara}", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "-quickedit:{pluginName}"], "autoloadall": ["{gamePara}", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "-autoload"] }; Now, why is this a "Silly" workaround? Because if the plugin updates? This breaks. Also, if your game is somewhere else? It won't work (without modification). And if you have another Bethesda game (Like Fallout4, morrowind, etc...) and try to use the plugin? It breaks. And possibly breaks your other game.
A better way might be to manipulate "gameParam" to be an index instead of a string, and stuff all this stuff there, but frankly, I am no good with javascript, and I leave that to smarter folk than I.
Same error here, if you use Vortex, you likely have to install SSEEdit. Manually download the zip file, and just extract it into wherever you like to keep your modding tools. Go back into Vortex and go to your Dashboard. Scroll down to Tools. SSEEdit should already have an entry there. Click the three dot button and Edit. Under Target, find where you extracted the zip file and point it to SSEEdit.exe, Save. You may have to restart Vortex, but now when you go to 'Clean with xEdit' on a Plugin, it should work.
I feel you should be doing this regardless of using this extension or not. Same with QuickAutoClean, SKSE64 (you need to also set it as "Primary here") and several other tools.
Just a heads up, xEdit 4.0.3h has implemented the support for "Open in xEdit" feature, so it will be in the 4.0.4 release (also, if anyone wants to get it now the 4.0.3h is freely available on the xEdit discord, link to the discord is in the top right of xEdit if you want to join).
Just needs a minor update. Notes on the "quickedit" update:
QuickEdit By starting xEdit with a -quickedit:somefile.esp parameter, only that file (and it's required masters) will be initially selected in the Module Selection Form. It is possible to list multiple files, e.g. -quickedit:dawnguard.esm dragonborn.esm. This parameter can be combined with -autoload (see below).
I tested it with a bat file, and quickedit works well.
Finally! I've been waiting for Elm to release that option for over a year. Naturally, it no longer uses the param he told me it would. Updated to 1.0.8
I could not get this to work from within Vortex. it throws error "Selected plugin cant find "" does not exist" in xedit.
From a bat or link file it works as advertised.
I am sure the problem lies in this original line "autoloadplugin": ["{gamePara}", "-quickedit", "{pluginName}"]
it needs something more like below: "autoloadplugin": ["{gamePara}", "-quickedit:{pluginName}"] it wants the ":" without a space afterwards and then plugin name else you get that error.
If I hardcode the line to an existing mod something like this then it works from within vortex. "autoloadplugin": ["{gamePara}", "-quickedit:fallout4.esm"]
I could not get the {pluginName} to return with any variation I could think of without causing a space. I don't know how to display the executed command/line so I can see what it is actually executing, I wish I knew.
This is my complete script I did for myself. Maybe someone else finds it useful.
I added other Options for my convenience. I know the Conflicts is not for the specific mod but I did not want it on the top Context menu or for the specific mod only.
Spoiler:
Show
module.exports = /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/index.ts"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./src/index.ts": /*!**********************!*\ !*** ./src/index.ts ***! \**********************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runxEdit = exports.setCleaning = exports.doNotCleanMessages = exports.excludedPlugins = exports.gameSupportData = void 0; const vortex_api_1 = __webpack_require__(/*! vortex-api */ "vortex-api"); exports.gameSupportData = [ { game: "skyrimse", exeName: "SSEEdit", gameParam: "-sse" }, { game: "skyrim", exeName: "TES5Edit", gameParam: "-tes5" }, { game: "skyrimvr", exeName: "TES5VREdit", gameParam: "-tes5vr" }, { game: "fallout4", exeName: "FO4Edit", gameParam: "-fo4" }, { game: "oblivion", exeName: "TES4Edit", gameParam: "-tes4" }, { game: "enderal", exeName: "EnderalEdit", gameParam: "-enderal" }, { game: "fallout3", exeName: "FO3Edit", gameParam: "-fo3" }, { game: "falloutnv", exeName: "FNVEdit", gameParam: "-fnv" }, { game: "fallout4vr", exeName: "FO4VREdit", gameParam: "-fo4vr" }, { game: "fallout76", exeName: "FO76Edit", gameParam: "-fo76" }, { game: "morrowind", exeName: "TES3Edit", gameParam: "-tes3" }, ]; exports.excludedPlugins = ["skyrim.esm", "fallout4.esm", "falloutnv.esm", "fallout3.esm", " oblivion.esm", "seventysix.esm", "enderal - forgotten stories.esm"]; exports.doNotCleanMessages = [ 'Do not clean ITM records, they are intentional and required for the mod to function. It is safe to undelete records, but do not do anything other than that.', 'Rengør ikke ITM-poster: de er forsætlige og krævede for at mod’en fungerer. Det er sikkert at gendanne poster, men gør ikke andet end dét.', 'ITM-Einträge in diesem Plugin sollten nicht gesäubert werden, sie sind absichtlich enthalten und werden benötigt, damit die Mod richtig funktioniert. Gelöschte Einträge wiederherzustellen ist in Ordnung, alles andere aber nicht.', 'No limpiar las referencias ITM (iguales al master), ya que son intencionales y necesarias para que el Mod funcione. Sí es seguro restaurar las UDR (referencias borradas), pero no haga más que eso.', 'IMT?????????????????????????????????????Mod?????????????????????????????????????????????????', 'ITM ??? ???? ????. ??? ?? ???? ?? ????? ??? ?????. ??? ???? ? ???? ???? ????.', 'Nie czyść rekordów ITM, są one zamierzone i potrzebne do działania tego moda. Jest bezpieczne aby cofnąć usunięcie rekordów (UDR), ale nie rób nic innego ponad to. ', 'Não apagar os registos ITM. São intencionais e necessárias para o funcionamento do mod. É seguro restaurar os registos, mas não faça nada mais que isso.', 'Não apague os registros ITM. Eles são intencionais e necessários para que o mod funcione. É seguro restaurar os registros, mas nada mais além disso.', 'Не очищать ITM-записи. "Грязные" правки оставлены специально и требуются для функционирования мода. Восстановить удаленные записи (UDR) можно безопасно, но идентичные мастерфайлу лучше оставить.', 'Städa inte bort ITM records, de är avsiktliga och krävs för att modden ska fungera. Det är säkert att återställa records, men gör ingenting förutom det.', '????"?"?????????mod??????' ]; const xEditParams = { "quickautocleanshow": ["{gamePara}", "-quickautoclean", "-autoload", "{pluginName}"], "quickautoclean": ["{gamePara}", "-quickautoclean", "-autoexit", "-autoload", "{pluginName}"], "loadplugin": ["{gamePara}", "-quickedit:{pluginName}"], "autoloadplugin": ["{gamePara}", "-quickedit:{pluginName}", "-autoload"], "CheckConflicts": ["{gamePara}", "-veryquickshowconflicts"], "autoloadall": ["{gamePara}", "-autoload"] }; let cleaningInProgress = false; let pluginBeingCleaned = ""; function setCleaning(status, pluginName = "") { cleaningInProgress = status; pluginBeingCleaned = pluginName; } exports.setCleaning = setCleaning; function init(context) { context.requireVersion('^1.1.0'); context.requireExtension('gamebryo-plugin-management'); context.registerAction('gamebryo-plugin-icons', 300, 'xEdit', {}, 'Open xEdit', () => { runxEdit('', context.api, [...xEditParams['autoloadall']]); }, () => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'Clean with xEdit - Show Result', instanceIds => { runxEdit(instanceIds[0], context.api, [...xEditParams['quickautocleanshow']]); }, instanceIds => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'Clean with xEdit - Auto Exit', instanceIds => { runxEdit(instanceIds[0], context.api, [...xEditParams['quickautoclean']]); }, instanceIds => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.registerAction('gamebryo-plugins-action-icons', 100, 'xEdit', {}, 'Open in xEdit', instanceIds => { runxEdit(instanceIds[0], context.api, [...xEditParams['loadplugin']]); }, instanceIds => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.registerAction('gamebryo-plugins-action-icons', 100, 'xEdit', {}, 'AutoOpen in xEdit', instanceIds => { runxEdit(instanceIds[0], context.api, [...xEditParams['autoloadplugin']]); }, instanceIds => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'xEdit Conflicts', instanceIds => { runxEdit(instanceIds[0], context.api, [...xEditParams['CheckConflicts']]); }, instanceIds => { const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState()); return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false; }); context.once(() => { vortex_api_1.util.installIconSet('xedit-icons', `${__dirname}/xediticon.svg`); context.api.onStateChange(['session', 'base', 'toolsRunning'], (previous, current) => __awaiter(this, void 0, void 0, function* () { if (cleaningInProgress && (Object.keys(previous).length > 0) && (Object.keys(current).length === 0)) { context.api.sendNotification({ type: "success", title: "Plugin Cleaning Completed", message: `${pluginBeingCleaned} was cleaned with xEdit.`, group: "xEdit-cleaning-done", displayMS: 10000 }); setCleaning(false); vortex_api_1.log("debug", "xEdit plugin cleaning completed"); } })); }); } function runxEdit(pluginName, api, params) { const store = api.store; const activeGameId = vortex_api_1.selectors.activeGameId(store.getState()); const pluginData = vortex_api_1.util.getSafe(store.getState(), ['session', 'plugins', 'pluginInfo', pluginName.toLowerCase()], undefined); if (pluginData) { const lootMessages = pluginData.messages || []; const doNotCleanMessage = lootMessages.find(m => exports.doNotCleanMessages.includes(m.value)); const missingMaster = pluginData.warnings['missing-master']; if (doNotCleanMessage) return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name}, please check the LOOT messages.`, displayMS: 5000 }); if (missingMaster) return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name} as it has missing masters.`, displayMS: 5000 }); } if (exports.excludedPlugins.indexOf(pluginName.toLowerCase()) !== -1 && params.includes('-quickautoclean')) return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name} as it is the game master file.`, displayMS: 5000 }); const xEditData = exports.gameSupportData.find(g => g.game === activeGameId); params.indexOf('{gamePara}') !== -1 && xEditData.gameParam ? params[params.indexOf('{gamePara}')] = xEditData.gameParam : null; params.indexOf('{pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('{pluginName}')] = pluginName : null; params.indexOf('-quickedit:{pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('-quickedit:{pluginName}')] = `-quickedit:${pluginName}` : null; const gamePath = vortex_api_1.util.getSafe(store.getState(), ['settings', 'gameMode', 'discovered', activeGameId, 'path'], undefined); const tools = vortex_api_1.util.getSafe(store.getState(), ['settings', 'gameMode', 'discovered', activeGameId, 'tools'], undefined); const xEditKey = tools ? Object.keys(tools).find(t => t === xEditData.exeName) : undefined; const xEditTool = xEditKey ? tools[xEditKey] : undefined; if (!xEditTool || !xEditTool.path) return api.showErrorNotification(`xEdit not found`, `Vortex could not find ${xEditData.exeName}. Please check the tool in your starter dashlet is pointing to the right place.`); api.runExecutable(xEditTool.path, params, { cwd: gamePath, suggestDeploy: false, shell: false, onSpawned: () => api.store.dispatch(vortex_api_1.actions.setToolRunning(xEditTool.path, Date.now(), true)) }).then(params.includes('-quickautoclean') ? setCleaning(true, pluginData.name || pluginName) : null) .catch(err => { if (err.errno === 'ENOENT') { api.showErrorNotification(`xEdit not found`, `Failed to run tool. Vortex could not find xEdit at ${xEditTool.path}. Please check the tool in your starter dashlet is pointing to the right place.`); } else console.log(err); }); } exports.runxEdit = runxEdit; exports.default = init; /***/ }), /***/ "vortex-api": /*!*****************************!*\ !*** external "vortex-api" ***! \*****************************/ /*! no static exports found */ /***/ (function(module, exports) { module.exports = require("vortex-api"); /***/ }) /******/ }); //# sourceMappingURL=xedit-integration.js.map
I had really wanted a "show result" option, notably when giving feedback to other modders on the Nexus. Thanks for sharing! I now have an "xEdit Integration - Hacked" home-brewed extension lol
Thank you HellPhantom, those tweaks are awesome ! Really nice additions to an already amazing plugin. Perhaps these should be included in the base plugin.
@Kakashi
Goto your ......Users\Appdata\Roaming\Vortex\Plugins..... folder
Okay so I've updated to fo4edit 4.0.4 and I removed and updated this extension for good measure (it was still on 1.0.8 but now its 1.0.11) and I'm getting the Selected plugin "" does not exist error. This might need an update for the new xedit?
EDIT: I wanted the extra features that HellPhantom implimented below so I used his version and it works fine. If anyone else ends up here and would like to do the same, all you need to do is look at the comment with the screenshots showing the extra features and click the 'Spoiler' tag. go to C:\user\[appdata]\roaming\vortex\plugins and find this plugin, open the .js file with notepad and copy the code under that spoiler tag into the .js file replacing whats there. Save and exit and you get all the updated features just like that.
I particularly like the fact it has an option for showing you what was cleaned or not, which is something this original extension sorely missed. It was good that it just cleaned it all easily, but the fact that there was no way to see what was cleaned (it just did it and exited) was a bit bothersome
I use this one all the time so thank you. Looks like fo4edit 4.0.4 came out on the nexus yesterday so I don't know how thats going to affect this. I'm actually here because I was wondering how to make the Open in xEdit button open just that file and its required masters but I see thats already been discussed below :)
67 comments
I am using xEdit.4.1.4c.EXTREMELY.EXPERIMENTAL and I can run it through the Vortex dashboard
xEdit.exe -sse
When I want to use your extension I get "Vortex could not find TES5VREdit. Please check the tool in your starter dashlet is pointing to the right place."
How can I get it working with pure xEdit? I tried duplicating the exe or making simlink to SSEEdit.exe or TES5VREdit.exe without success.
edit: Same for 4.0.4c version
edit2: there is something that I should miss because I have the same problem with SSEEdit from Nexus directly...
Is it possible please for the extension developed to have a feature added which allows for the user, to select multiple items from plugins list in Vortex, so that they can be batch cleaned? So that they can be cleaned one after the other in the batch, without the user needing to do each one individually one at a time.
Other than this critique the extension is truly useful and good bit of work when running Vortex in order to manage the modifications on the user's computer.
Installing it from Vortex for FO4 installs this extension (validated when I follow it back to Nexus from Vortex). Upon clicking any Vortex xEdit button you get a Vortex error "xEdit not found".
The directions for this extension say to extract "ElminsterAU's xEdit 7z archive" to %appdata%\Vortex\plugins. It's not clear if you are supposed to put the files in the /plugins folder itself or in a subfolder called: "%appdata%\Vortex\plugins\Vortex Extension Update - xEdit Cleaning Extension v1.0.13" which is what Vortex does automatically. I assume this is what is supposed to happen even though the extension author does not specify it.
I downloaded and moved the files from the "ElminsterAU's xEdit 7z archive" into the folder Vortex created and still get the error.
Getting more info from the error message, it is looking for FO4edit which is NOT in the "ElminsterAU's xEdit 7z archive" the extension author offers as a link. I already had FO4Edit, so I moved the FO4Edit files into that same folder and still get the "xEdit not found" error.
I can find no place in Vortex, nor in the Vortex plugin json file where the specific tool on the dashlet is mapped to a specific path and executable so cannot resolve this further until I find another approach. Where is a tool in Vortex mapped to a specific path/executable?
*** Update later that evening....
It appears the executable mappings are done using vortex-api calls from the index.js included with the extension. The extension index.js does not get the configuration for xEdit from the vortex-api and then fails as a result of that. The vortex-api only reports BodySlide and F4SE on my installation as "tools" and does not include the xEdit extension that Vortex installed. I can't tell if the problem is with the api or the getSafe() call from the index.js. I can't find documentation on the getSafe() call on GitHub.
Here are the some debug log statements I put in the index.js to go to the vortex.log to demonstrate this:
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const tools = vortex_api_1.util.getSafe(state, ['settings', 'gameMode', 'discovered', activeGameId, 'tools'], undefined);:
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] Object.keys(tools)
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] bodyslide,f4se
;-----------------------------------------------------------------------------------Note that Object.keys(tools) only reports BS and f4se, not xEdit ext which it installed
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const xEditKey = tools ? Object.keys(tools).find(t => t === xEditData.exeName) : undefined;:
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] xEditKey
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] undefined
;------------------------------------------------now everything is undefined
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] let's see what we get from const xEditTool = xEditKey ? tools[xEditKey] : undefined;:
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] xEditTool
Thu, 06 Jul 2023 06:51:34 GMT - debug: [xedit-cleaning-extension] undefined
;------------------------------------------------again undefined
Thu, 06 Jul 2023 06:51:34 GMT - error: xEdit not found text=Vortex could not find FO4Edit. Please check the tool in your starter dashlet is pointing to the right place., wrap=true
;------------------------------------------------Vortex throws the error in the UI because it can't find a path to the exe, it's undefined
I mean SSEEdit does not find the INIs with new paths and this plugin does not take command line arguments into account.
In the file
%AppData%\Vortex\Plugins\Vortex Extension Update - xEdit Cleaning Extension v1.0.13\index.js
find the section marked "const xEditParams"Change it to this (AND FOR THE LOVE OF TALOS, PLEASE CHANGE "YOUR-LOGIN" TO MATCH YOUR USERNAME!):
const xEditParams = {
"quickautoclean": ["{gamePara}", "-quickautoclean", "-autoexit", "-autoload", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "{pluginName}"],
"autoloadplugin": ["{gamePara}", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "-quickedit:{pluginName}"],
"autoloadall": ["{gamePara}", "-D:\"C:\\GOG Games\\Skyrim Anniversary Edition\\Data\\\"", "-I:\"C:\\Users\\YOUR-LOGIN\\Documents\\My Games\\Skyrim Special Edition GOG\\Skyrim.ini\"", "-P:\"C:\\Users\\YOUR-LOGIN\\AppData\\Local\\Skyrim Special Edition GOG\\plugins.txt\"", "-autoload"]
};
Now, why is this a "Silly" workaround? Because if the plugin updates? This breaks. Also, if your game is somewhere else? It won't work (without modification). And if you have another Bethesda game (Like Fallout4, morrowind, etc...) and try to use the plugin? It breaks. And possibly breaks your other game.
A better way might be to manipulate "gameParam" to be an index instead of a string, and stuff all this stuff there, but frankly, I am no good with javascript, and I leave that to smarter folk than I.
Good luck!
fixed it
Just needs a minor update. Notes on the "quickedit" update:
QuickEdit
By starting xEdit with a -quickedit:somefile.esp parameter, only that file (and it's required masters) will be initially selected in the Module Selection Form.
It is possible to list multiple files, e.g. -quickedit:dawnguard.esm dragonborn.esm.
This parameter can be combined with -autoload (see below).
I tested it with a bat file, and quickedit works well.
From a bat or link file it works as advertised.
I am sure the problem lies in this original line
"autoloadplugin": ["{gamePara}", "-quickedit", "{pluginName}"]
it needs something more like below:
"autoloadplugin": ["{gamePara}", "-quickedit:{pluginName}"]
it wants the ":" without a space afterwards and then plugin name else you get that error.
If I hardcode the line to an existing mod something like this then it works from within vortex.
"autoloadplugin": ["{gamePara}", "-quickedit:fallout4.esm"]
I could not get the {pluginName} to return with any variation I could think of without causing a space.
I don't know how to display the executed command/line so I can see what it is actually executing, I wish I knew.
Thanks for your changes it did allow me to be able to make it work.
Their command line does not want a space between the ":" and first pluginName, and I think their was a copy paste error in the second line
But if you change these 2 lines from :
"autoloadplugin": ["{gamePara}", "-quickedit: {pluginName}"],
params.indexOf('-quickedit: {pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('{pluginName-quickedit: {pluginName}')] = `-quickedit: ${pluginName}` : null;
To This it will work.
"autoloadplugin": ["{gamePara}", "-quickedit:{pluginName}"],
params.indexOf('-quickedit:{pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('-quickedit:{pluginName}')] = `-quickedit:${pluginName}` : null;
I added other Options for my convenience. I know the Conflicts is not for the specific mod but I did not want it on the top Context menu or for the specific mod only.
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./src/index.ts");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./src/index.ts":
/*!**********************!*\
!*** ./src/index.ts ***!
\**********************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runxEdit = exports.setCleaning = exports.doNotCleanMessages = exports.excludedPlugins = exports.gameSupportData = void 0;
const vortex_api_1 = __webpack_require__(/*! vortex-api */ "vortex-api");
exports.gameSupportData = [
{
game: "skyrimse",
exeName: "SSEEdit",
gameParam: "-sse"
},
{
game: "skyrim",
exeName: "TES5Edit",
gameParam: "-tes5"
},
{
game: "skyrimvr",
exeName: "TES5VREdit",
gameParam: "-tes5vr"
},
{
game: "fallout4",
exeName: "FO4Edit",
gameParam: "-fo4"
},
{
game: "oblivion",
exeName: "TES4Edit",
gameParam: "-tes4"
},
{
game: "enderal",
exeName: "EnderalEdit",
gameParam: "-enderal"
},
{
game: "fallout3",
exeName: "FO3Edit",
gameParam: "-fo3"
},
{
game: "falloutnv",
exeName: "FNVEdit",
gameParam: "-fnv"
},
{
game: "fallout4vr",
exeName: "FO4VREdit",
gameParam: "-fo4vr"
},
{
game: "fallout76",
exeName: "FO76Edit",
gameParam: "-fo76"
},
{
game: "morrowind",
exeName: "TES3Edit",
gameParam: "-tes3"
},
];
exports.excludedPlugins = ["skyrim.esm", "fallout4.esm", "falloutnv.esm", "fallout3.esm", " oblivion.esm", "seventysix.esm", "enderal - forgotten stories.esm"];
exports.doNotCleanMessages = [
'Do not clean ITM records, they are intentional and required for the mod to function. It is safe to undelete records, but do not do anything other than that.',
'Rengør ikke ITM-poster: de er forsætlige og krævede for at mod’en fungerer. Det er sikkert at gendanne poster, men gør ikke andet end dét.',
'ITM-Einträge in diesem Plugin sollten nicht gesäubert werden, sie sind absichtlich enthalten und werden benötigt, damit die Mod richtig funktioniert. Gelöschte Einträge wiederherzustellen ist in Ordnung, alles andere aber nicht.',
'No limpiar las referencias ITM (iguales al master), ya que son intencionales y necesarias para que el Mod funcione. Sí es seguro restaurar las UDR (referencias borradas), pero no haga más que eso.',
'IMT?????????????????????????????????????Mod?????????????????????????????????????????????????',
'ITM ??? ???? ????. ??? ?? ???? ?? ????? ??? ?????. ??? ???? ? ???? ???? ????.',
'Nie czyść rekordów ITM, są one zamierzone i potrzebne do działania tego moda. Jest bezpieczne aby cofnąć usunięcie rekordów (UDR), ale nie rób nic innego ponad to. ',
'Não apagar os registos ITM. São intencionais e necessárias para o funcionamento do mod. É seguro restaurar os registos, mas não faça nada mais que isso.',
'Não apague os registros ITM. Eles são intencionais e necessários para que o mod funcione. É seguro restaurar os registros, mas nada mais além disso.',
'Не очищать ITM-записи. "Грязные" правки оставлены специально и требуются для функционирования мода. Восстановить удаленные записи (UDR) можно безопасно, но идентичные мастерфайлу лучше оставить.',
'Städa inte bort ITM records, de är avsiktliga och krävs för att modden ska fungera. Det är säkert att återställa records, men gör ingenting förutom det.',
'????"?"?????????mod??????'
];
const xEditParams = {
"quickautocleanshow": ["{gamePara}", "-quickautoclean", "-autoload", "{pluginName}"],
"quickautoclean": ["{gamePara}", "-quickautoclean", "-autoexit", "-autoload", "{pluginName}"],
"loadplugin": ["{gamePara}", "-quickedit:{pluginName}"],
"autoloadplugin": ["{gamePara}", "-quickedit:{pluginName}", "-autoload"],
"CheckConflicts": ["{gamePara}", "-veryquickshowconflicts"],
"autoloadall": ["{gamePara}", "-autoload"]
};
let cleaningInProgress = false;
let pluginBeingCleaned = "";
function setCleaning(status, pluginName = "") {
cleaningInProgress = status;
pluginBeingCleaned = pluginName;
}
exports.setCleaning = setCleaning;
function init(context) {
context.requireVersion('^1.1.0');
context.requireExtension('gamebryo-plugin-management');
context.registerAction('gamebryo-plugin-icons', 300, 'xEdit', {}, 'Open xEdit', () => {
runxEdit('', context.api, [...xEditParams['autoloadall']]);
}, () => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'Clean with xEdit - Show Result', instanceIds => {
runxEdit(instanceIds[0], context.api, [...xEditParams['quickautocleanshow']]);
}, instanceIds => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'Clean with xEdit - Auto Exit', instanceIds => {
runxEdit(instanceIds[0], context.api, [...xEditParams['quickautoclean']]);
}, instanceIds => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.registerAction('gamebryo-plugins-action-icons', 100, 'xEdit', {}, 'Open in xEdit', instanceIds => {
runxEdit(instanceIds[0], context.api, [...xEditParams['loadplugin']]);
}, instanceIds => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.registerAction('gamebryo-plugins-action-icons', 100, 'xEdit', {}, 'AutoOpen in xEdit', instanceIds => {
runxEdit(instanceIds[0], context.api, [...xEditParams['autoloadplugin']]);
}, instanceIds => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.registerAction('gamebryo-plugins-action-icons', 500, 'xEdit', {}, 'xEdit Conflicts', instanceIds => {
runxEdit(instanceIds[0], context.api, [...xEditParams['CheckConflicts']]);
}, instanceIds => {
const activeGameId = vortex_api_1.selectors.activeGameId(context.api.store.getState());
return exports.gameSupportData.find(g => g.game === activeGameId) ? true : false;
});
context.once(() => {
vortex_api_1.util.installIconSet('xedit-icons', `${__dirname}/xediticon.svg`);
context.api.onStateChange(['session', 'base', 'toolsRunning'], (previous, current) => __awaiter(this, void 0, void 0, function* () {
if (cleaningInProgress && (Object.keys(previous).length > 0) && (Object.keys(current).length === 0)) {
context.api.sendNotification({
type: "success",
title: "Plugin Cleaning Completed",
message: `${pluginBeingCleaned} was cleaned with xEdit.`,
group: "xEdit-cleaning-done",
displayMS: 10000
});
setCleaning(false);
vortex_api_1.log("debug", "xEdit plugin cleaning completed");
}
}));
});
}
function runxEdit(pluginName, api, params) {
const store = api.store;
const activeGameId = vortex_api_1.selectors.activeGameId(store.getState());
const pluginData = vortex_api_1.util.getSafe(store.getState(), ['session', 'plugins', 'pluginInfo', pluginName.toLowerCase()], undefined);
if (pluginData) {
const lootMessages = pluginData.messages || [];
const doNotCleanMessage = lootMessages.find(m => exports.doNotCleanMessages.includes(m.value));
const missingMaster = pluginData.warnings['missing-master'];
if (doNotCleanMessage)
return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name}, please check the LOOT messages.`, displayMS: 5000 });
if (missingMaster)
return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name} as it has missing masters.`, displayMS: 5000 });
}
if (exports.excludedPlugins.indexOf(pluginName.toLowerCase()) !== -1 && params.includes('-quickautoclean'))
return api.sendNotification({ type: 'warning', title: `Cannot clean this plugin`, message: `Vortex could not clean ${pluginData.name} as it is the game master file.`, displayMS: 5000 });
const xEditData = exports.gameSupportData.find(g => g.game === activeGameId);
params.indexOf('{gamePara}') !== -1 && xEditData.gameParam ? params[params.indexOf('{gamePara}')] = xEditData.gameParam : null;
params.indexOf('{pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('{pluginName}')] = pluginName : null;
params.indexOf('-quickedit:{pluginName}') !== -1 && pluginName !== '' ? params[params.indexOf('-quickedit:{pluginName}')] = `-quickedit:${pluginName}` : null;
const gamePath = vortex_api_1.util.getSafe(store.getState(), ['settings', 'gameMode', 'discovered', activeGameId, 'path'], undefined);
const tools = vortex_api_1.util.getSafe(store.getState(), ['settings', 'gameMode', 'discovered', activeGameId, 'tools'], undefined);
const xEditKey = tools ? Object.keys(tools).find(t => t === xEditData.exeName) : undefined;
const xEditTool = xEditKey ? tools[xEditKey] : undefined;
if (!xEditTool || !xEditTool.path)
return api.showErrorNotification(`xEdit not found`, `Vortex could not find ${xEditData.exeName}. Please check the tool in your starter dashlet is pointing to the right place.`);
api.runExecutable(xEditTool.path, params, {
cwd: gamePath,
suggestDeploy: false,
shell: false,
onSpawned: () => api.store.dispatch(vortex_api_1.actions.setToolRunning(xEditTool.path, Date.now(), true))
}).then(params.includes('-quickautoclean') ? setCleaning(true, pluginData.name || pluginName) : null)
.catch(err => {
if (err.errno === 'ENOENT') {
api.showErrorNotification(`xEdit not found`, `Failed to run tool. Vortex could not find xEdit at ${xEditTool.path}. Please check the tool in your starter dashlet is pointing to the right place.`);
}
else
console.log(err);
});
}
exports.runxEdit = runxEdit;
exports.default = init;
/***/ }),
/***/ "vortex-api":
/*!*****************************!*\
!*** external "vortex-api" ***!
\*****************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = require("vortex-api");
/***/ })
/******/ });
//# sourceMappingURL=xedit-integration.js.map
I had really wanted a "show result" option, notably when giving feedback to other modders on the Nexus. Thanks for sharing!
I now have an "xEdit Integration - Hacked" home-brewed extension lol
@Kakashi
Goto your ......Users\Appdata\Roaming\Vortex\Plugins..... folder
EDIT: I wanted the extra features that HellPhantom implimented below so I used his version and it works fine. If anyone else ends up here and would like to do the same, all you need to do is look at the comment with the screenshots showing the extra features and click the 'Spoiler' tag. go to C:\user\[appdata]\roaming\vortex\plugins and find this plugin, open the .js file with notepad and copy the code under that spoiler tag into the .js file replacing whats there. Save and exit and you get all the updated features just like that.
I particularly like the fact it has an option for showing you what was cleaned or not, which is something this original extension sorely missed. It was good that it just cleaned it all easily, but the fact that there was no way to see what was cleaned (it just did it and exited) was a bit bothersome
Thank you Picky