IsharaMeradin Posted June 28, 2019 Share Posted June 28, 2019 Some users of my mod Inventory Management System Rebuilt (IMS) are reporting a " Load Order FileID [01] can not be mapped to file FileID " error. However, the xEdit patch script I supplied with my mod does not produce such an error with the load order that I have. After some discussion it was concluded that Complete Crafting Overhaul Remade (CCOR) was a common mod. I downloaded CCOR and gave my patch script a try in xEdit. I received the following error specifically: [00:03] Exception in unit TheCraftPatch line 92: Load order FileID [01] can not be mapped to file FileID for file "IMS_Patch.esp" [00:03] Error during Applying script "IMS - Compatibility Patch": Load order FileID [01] can not be mapped to file FileID for file "IMS_Patch.esp" In looking at what was put into the patch plugin, I was able to deduce that once the patch script hits a record supplied by CCOR but injected into Update.esm, it craps out. My question is twofold. One, is this a bug with xEdit that needs resolved? Two, can my patch script be modified to properly handle such records? Patch script follows: Spoiler { ************************************* **** Inventory Management System **** ********************************************* **** IMS - Crafting Components Patch.pas **** IMS - Recipe Item Patch.txt **** IMS - Workstation Patch.txt **************************************************************************************************** **** This script is a compatibility patch for Inventory Management System. **** **** This patch populates Form Lists with recipe components based upon their workbench keyword. **** Keyword and Form List pairs can be found in 'Edit Scripts\IMS - Recipe Item Patch.txt' **** If Hearthfires.esm is active the Sawn Log item is automatically excluded. **** **** This patch populates Form Lists with workstations by their assigned workbench keyword **** Keyword and Form List pairs can be found in 'Edit Scripts\IMS - Workstation Patch.txt' **** **** This patch populates Form Lists with items by their vendor keywords **** Keyword and Form List pairs can be found in 'Edit Scripts\IMS - Vendor Items Patch.txt' **** **** Feel free to share any local additions to the above text files. **** Current support: **** Base game and all DLC **** Legacy of the Dragonborn **** **** This patch populates Form Lists with ammo and bolt records **** Bolts must have 'bolt' in the name in order to be recognized. **************************************************************************************************** **** Credits: **** Zilav **** - wrote the initial version of this script and directed me in additional modifications **************************************************************************************************** } unit TheCraftPatch; const sModMaster = 'Inventory Management System.esp'; // master file name sModPatch = 'IMS_Patch.esp'; // patch file name var slCraftIndex, slFurnIndex, slVendorIndex: TStringList; ModMaster, ModPatch: IInterface; procedure AddObjectToList(list: string; comp: IInterface); var g, flst, entries, entry: IInterface; i: integer; begin // exclude Hearthfires Sawn Log If EditorID(comp) = 'BYOHMaterialLog' then Exit; // create new patch plugin if doesn't exist yet if not Assigned(ModPatch) then begin ModPatch := AddNewFileName(sModPatch); // if failed for some reason if not Assigned(ModPatch) then raise Exception.Create('Error creating a new patch plugin!'); end; // getting list record from patch file g := GroupBySignature(ModPatch, 'FLST'); flst := MainRecordByEditorID(g, list); // copy as override from master if doesn't exist yet if not Assigned(flst) then begin g := GroupBySignature(ModMaster, 'FLST'); flst := MainRecordByEditorID(g, list); if not Assigned(flst) then raise Exception.Create(list + ' FLST doesn''t exist in the master file!'); // add masters to patch plugin before copying record AddRequiredElementMasters(flst, ModPatch, False); // copy as override into patch flst := wbCopyElementToFile(flst, ModPatch, False, True); end; entries := ElementByName(flst, 'FormIDs'); if not Assigned(entries) then entries := Add(flst, 'FormIDs', True); for i := Pred(ElementCount(entries)) downto 0 do begin entry := ElementByIndex(entries, i); // component already in list if GetLoadOrderFormID(LinksTo(entry)) = GetLoadOrderFormID(comp) then Exit; // we checked all items in list but still no match found, add component to list if i = 0 then begin // if the first entry is not NULL then add a new one if Assigned(LinksTo(entry)) then entry := ElementAssign(entries, HighInteger, nil, False); AddMasterIfMissing(ModPatch, GetFileName(comp)); SetEditValue(entry, Name(comp)); end; end; end; procedure ProcessRecipe(e: IInterface); var i: integer; items, item, comp: IInterface; kwd: string; begin // workbench keyword EditorID kwd := EditorID(LinksTo(ElementBySignature(e, 'BNAM'))); // skip workbenches with unknown keyword if slCraftIndex.IndexOfName(kwd) = -1 then Exit; // iterate over components items := ElementByName(e, 'Items'); for i := 0 to Pred(ElementCount(items)) do begin item := ElementByIndex(items, i); comp := LinksTo(ElementByPath(item, 'CNTO\Item')); if Assigned(comp) then AddObjectToList(slCraftIndex.Values[kwd], comp); end; end; procedure ProcessFurniture(e: IInterface); var i: integer; keywords: IInterface; kwd: string; begin // skip furniture without a name, probably unplayable if not ElementExists(e, 'FULL') then Exit; // iterate over keywords keywords := ElementBySignature(e, 'KWDA'); for i := 0 to Pred(ElementCount(keywords)) do begin kwd := EditorID(LinksTo(ElementByIndex(keywords, i))); // add to list and exit upon the first matching keyword if slFurnIndex.IndexOfName(kwd) <> -1 then begin AddObjectToList(slFurnIndex.Values[kwd], e); Exit; end; end; end; procedure ProcessItems(e: IInterface); var i: integer; keywords: IInterface; kwd, x, fn: string; begin // skip those without a name, probably unplayable if not ElementExists(e, 'FULL') then Exit; // iterate over keywords keywords := ElementBySignature(e, 'KWDA'); fn := GetElementEditValues(e, 'FULL'); for i := 0 to Pred(ElementCount(keywords)) do begin x := i; kwd := EditorID(LinksTo(ElementByIndex(keywords, i))); // add to list and exit upon the first matching keyword if slVendorIndex.IndexOfName(kwd) <> -1 then begin AddObjectToList(slVendorIndex.Values[kwd], e); end; end; end; procedure ProcessAmmo(e: IInterface); var i: integer; keywords: IInterface; kwd, list: string; begin // skip main quest arrow if EditorID(e) = 'MQ101SteelArrow' then Exit; // skip test bolts If EditorID(e) = 'TestDLC1Bolt' then Exit; // skip non-playable by flag or missing name if (GetElementNativeValues(e, 'DATA\Flags') and 2 <> 0) or not ElementExists(e, 'FULL') then Exit; // skip bound projectiles (checking for WeapTypeBoundArrow [KYWD:0010D501]) keywords := ElementBySignature(e, 'KWDA'); for i := 0 to Pred(ElementCount(keywords)) do begin kwd := EditorID(LinksTo(ElementByIndex(keywords, i))); if kwd = 'WeapTypeBoundArrow' then Exit; end; // skip practice arrows (damage=0) if GetElementNativeValues(e, 'DATA\Damage') = 0 then Exit; // determine arrow or bolt by 'bolt' word in the name if Pos('bolt', LowerCase(GetElementEditValues(e, 'FULL'))) <> 0 then list := 'abim_IMS_TotalBoltList' else list := 'abim_IMS_TotalArrowList'; AddObjectToList(list, e); end; function Initialize: integer; var i, j: integer; f, g, r: IInterface; begin // locating master file and optionally patch file if exists for i := 0 to Pred(FileCount) do begin f := FileByIndex(i); if SameText(GetFileName(f), sModMaster) then ModMaster := f else if SameText(GetFileName(f), sModPatch) then ModPatch := f; end; // can't do anything without master if not Assigned(ModMaster) then begin MessageDlg(sModMaster + ' must be loaded in TES5Edit', mtInformation, [mbOk], 0); Result := 1; Exit; end; // associations between Workbench Keyword and list's EditorID to put components in slCraftIndex := TStringList.Create; slCraftIndex.LoadFromFile(ProgramPath + 'Edit Scripts\IMS - Recipe Item Patch.txt'); for i := Pred(slCraftIndex.Count) downto 0 do if Copy(slCraftIndex[i], 1, 2) = '//' then slCraftIndex.Delete(i); //slCraftIndex.Add('CraftingCookpot=SmithingList'); //slCraftIndex.Add('CraftingSmelter=SmithingList'); //slCraftIndex.Add('CraftingSmithingArmorTable=SmithingList'); //slCraftIndex.Add('CraftingSmithingForge=SmithingList'); //slCraftIndex.Add('CraftingSmithingSharpeningWheel=SmithingList'); //slCraftIndex.Add('CraftingSmithingSkyforge=SmithingList'); //slCraftIndex.Add('CraftingTanningRack=TanningList'); // associations between Workbench Keyword and list's EditorID to put furniture in slFurnIndex := TStringList.Create; slFurnIndex.LoadFromFile(ProgramPath + 'Edit Scripts\IMS - Workstation Patch.txt'); for i := Pred(slFurnIndex.Count) downto 0 do if Copy(slFurnIndex[i], 1, 2) = '//' then slFurnIndex.Delete(i); //slFurnIndex.Add('CraftingCookpot=FurnitureCookingList'); //slFurnIndex.Add('CraftingSmithingArmorTable=FurnitureSmithingList'); // associations between vendor keywords and list's EditorID to put items in slVendorIndex := TStringList.Create; slVendorIndex.LoadFromFile(ProgramPath + 'Edit Scripts\IMS - Vendor Items Patch.txt'); for i := Pred(slVendorIndex.Count) downto 0 do if Copy(slVendorIndex[i], 1, 2) = '//' then slVendorIndex.Delete(i); //slVendorIndex.Add('VendorItemFood=abim_IMS_VendorItemFood'); // processing all files in load order for i := 0 to Pred(FileCount) do begin f := FileByIndex(i); AddMessage('Processing ' + GetFileName(f) + '...'); // COBJ records g := GroupBySignature(f, 'COBJ'); if Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessRecipe(WinningOverride(r)); end; // FURN records g := GroupBySignature(f, 'FURN'); if Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessFurniture(WinningOverride(r)); end; // AMMO records g := GroupBySignature(f, 'AMMO'); if Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessAmmo(WinningOverride(r)); ProcessItems(WinningOverride(r)); end; // ALCH records g := GroupBySignature(f, 'ALCH'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // ARMO records g := GroupBySignature(f, 'ARMO'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // BOOK records g := GroupBySignature(f, 'BOOK'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // INGR records g := GroupBySignature(f, 'INGR'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // KEYM records g := GroupBySignature(f, 'KEYM'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // MISC records g := GroupBySignature(f, 'MISC'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // SCRL records g := GroupBySignature(f, 'SCRL'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // SLGM records g := GroupBySignature(f, 'SLGM'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; // WEAP records g := GroupBySignature(f, 'WEAP'); If Assigned(g) then for j := 0 to Pred(ElementCount(g)) do begin r := ElementByIndex(g, j); if IsMaster(r) then ProcessItems(WinningOverride(r)); end; end; end; function Finalize: integer; begin slCraftIndex.Free; slFurnIndex.Free; if Assigned(ModPatch) then begin CleanMasters(ModPatch); SortMasters(ModPatch); end; end; end. Link to comment Share on other sites More sharing options...
zilav Posted June 28, 2019 Author Share Posted June 28, 2019 Skip processing injected records, they serve as masterless intercommunication between plugins if IsInjected(record) then ... Or you need to add as masters both the plugin injected record comes from (if it uses any links to that plugin which is usually the case for injected records) and plugin it is injected into. Link to comment Share on other sites More sharing options...
IsharaMeradin Posted June 28, 2019 Share Posted June 28, 2019 12 hours ago, zilav said: Skip processing injected records, they serve as masterless intercommunication between plugins if IsInjected(record) then ... Or you need to add as masters both the plugin injected record comes from (if it uses any links to that plugin which is usually the case for injected records) and plugin it is injected into. Thank you. I added this to the top of the AddObjectToList procedure (right below begin) and it allowed the patch script to complete without any issue. // skip injected records If isInjected(comp) then Exit; The end result seems to be okay. Link to comment Share on other sites More sharing options...
Malonn Posted September 14, 2019 Share Posted September 14, 2019 I'm interested in writing a script to merge cell and worldspace object placement edits from an esp over to a master. How hard would this be? Are there any TESxEdit (TES4Edit for me) script writing resources available? Any insight for me would be nice... Link to comment Share on other sites More sharing options...
zilav Posted September 15, 2019 Author Share Posted September 15, 2019 What's the difference compared to merging one plugin into the other manually in xEdit? It can be done with a few clicks now. Link to comment Share on other sites More sharing options...
Malonn Posted September 15, 2019 Share Posted September 15, 2019 Just the amount of time it would take. I'm working with plugins that have thousands of edits that I'm trying to merge. I've done some manually, and it's quite tedious. I was just hoping that a script could automate it and get it done in seconds versus the days/weeks it would take to do manually. Link to comment Share on other sites More sharing options...
zilav Posted September 16, 2019 Author Share Posted September 16, 2019 It takes a few seconds and clicks to merge one plugin into the other in xEdit version 4.x, I described how to earlier in the thread. Unless you need some specific method of merging data in the records instead of complete overwriting, script will be vastly slower. Link to comment Share on other sites More sharing options...
Malonn Posted September 16, 2019 Share Posted September 16, 2019 Oh. That's news to me. Forgive my ignorance. Can you give me an idea where in this 84 page (!) thread I could find this info? I don't mind scanning some pages, but a starting point would be great. This is going to save me a shitload of time. Thanks. Link to comment Share on other sites More sharing options...
Malonn Posted September 17, 2019 Share Posted September 17, 2019 On 10/2/2018 at 7:52 AM, zilav said: No, you have been asking for how to copy as override records from esp1 to esp2 without adding esp1 as a master. I told you it is impossible and you should copy and then remove master the proper way, and described how to do so. You didn't like the answer and continued to insist on adding an option to xEdit of copying records without adding masters because you've been doing it wrong for 5 years. If you really want to know how to properly merge mods manually, then this is covered in the xEdit Training Manual (the Help button in the right top corner, and linked on every xEdit's description page on Nexus, but who reads nowadays). The latest xEdit builds made it easier: Right click on B, click Add Masters and chose A Right click on B and select "Inject Forms Into Master..." menu, chose A. It will ask to optionally preserve ObjectIDs (the YYYYYY part in FormIDs XXYYYYYY numbers), in your case it doesn't matter when simply merging two mods. Expand B, select all record groups, right click, hold Ctrl+Shift and click "Deep copy as override into", chose A. Say Yes to overwrite any changes if asked. Close and save A only. Now you have A+B properly merged into A. Am I correct to assume this is the described method for merging two mods quickly? Didn't take 5 minutes to track down when I decided to try... EDIT: The way you described above does not work in practice. Everything is good up until step 3. After step 2, you enter the next FormID and it automatically goes to injecting new records into mod "A". I don't want to inject new records at this time. I just want to merge two existing records. I want to quickly merge worldspace object placement records. I'll keep at it today, but I may need some assistance, if it can be done at all. EDIT 2: Okay, it works. Sorry for not testing more before posting. I guess my next question is is there a way to only merge existing records quickly (en-masse, so to speak)? I just want to overwrite A's records with B's. Only worldspace object placement records. I'll RTTFM too. EDIT 3: Just so people know, in order to overwrite identical FormIDs, for step 3 you need to select "Deep copy as override into (with overwriting)" Link to comment Share on other sites More sharing options...
zilav Posted September 18, 2019 Author Share Posted September 18, 2019 Ah yes, in later xEdit versions holding Ctrl+Shift was replaced with just a separate "with overwriting" menu to avoid using obscure hotkeys. Link to comment Share on other sites More sharing options...
Malonn Posted September 18, 2019 Share Posted September 18, 2019 Oh, okay. I think that is a good idea. Learning more hotkeys that have no other means of accomplishing the same objective can be a task. I would encourage the developers to whittle down as many hotkeys as possible. Do you contribute to xEdit development, zilav? Or are you just proficient in it's use? Link to comment Share on other sites More sharing options...
zilav Posted September 19, 2019 Author Share Posted September 19, 2019 Contributed in the past while Elminster was away. Link to comment Share on other sites More sharing options...
Malonn Posted September 19, 2019 Share Posted September 19, 2019 Cool, cool. How many lines of code did you have to wrap your head around for xEdit development? Link to comment Share on other sites More sharing options...
zilav Posted September 20, 2019 Author Share Posted September 20, 2019 xEdit source code is available on GitHub https://github.com/TES5Edit/TES5Edit/ Link to comment Share on other sites More sharing options...
Malonn Posted September 20, 2019 Share Posted September 20, 2019 I didn't know that. Cool. Link to comment Share on other sites More sharing options...
zilav Posted September 21, 2019 Author Share Posted September 21, 2019 It is in the first topic opening post Link to comment Share on other sites More sharing options...
Malonn Posted September 22, 2019 Share Posted September 22, 2019 Oops! Sorry, I dropped the ball on that one. Link to comment Share on other sites More sharing options...
Malonn Posted October 26, 2019 Share Posted October 26, 2019 Quick question to those in the know: if I want to change one FormID of a record to something new (load order corrected) for merging into another mod, how do I tell what FormIDs are free in the mod I am merging into?? I'm merging into a dense mod, so there are a lot of used FormIDs. If I have two or more FormIDs selected to change together, it's easy; just select "Change FormID" and it's taken care of by Tes4Edit. But if I only have 1 FormID to change, it asks what I want to change it to, and in some cases I don't know because I don't know what's free. Thanks go out to whomever answers this inquiry. Link to comment Share on other sites More sharing options...
zilav Posted October 27, 2019 Author Share Posted October 27, 2019 7 hours ago, Malonn said: Quick question to those in the know: if I want to change one FormID of a record to something new (load order corrected) for merging into another mod, how do I tell what FormIDs are free in the mod I am merging into?? I'm merging into a dense mod, so there are a lot of used FormIDs. If I have two or more FormIDs selected to change together, it's easy; just select "Change FormID" and it's taken care of by Tes4Edit. But if I only have 1 FormID to change, it asks what I want to change it to, and in some cases I don't know because I don't know what's free. Thanks go out to whomever answers this inquiry. Create any new record in a plugin, copy it's FormID, remove record. Link to comment Share on other sites More sharing options...
Malonn Posted October 27, 2019 Share Posted October 27, 2019 I've been doing similar. For lone records, I've been using "Copy as new record into..." to create a new record for the plugin I'm merging into, then deleting it from the plugin being merged from. But, I never thought of your way, so thanks for the answer. "Copy as new record into..." takes fewer steps, so I'll stick with that for now. Link to comment Share on other sites More sharing options...
XJDHDR Posted May 12, 2020 Share Posted May 12, 2020 Does anyone know if the "NIF - Batch textures replacement" script accepts wildcards? I am trying to replace a range of different texture paths in a number of Oblivion models with one specific texture. Something like this: Replace: textures\old\wood*.dds With: textures\new\wood.dds Can this be done with that script? If not, does anyone know of a tool that can help me out? Link to comment Share on other sites More sharing options...
zilav Posted May 13, 2020 Author Share Posted May 13, 2020 No, it doesn't accept wildcards. You'll have to modify it for that or just provide all the possible replacement pairs (it supports any number of them). For example replace all "textures\old\wood" with something else in report only mode so it will list all such used textures in the messages, then use that info for actual replacement. I doubt it will take you more than a few minutes. By the way it is a part of standalone Sniff tool https://www.nexusmods.com/newvegas/mods/67829 Link to comment Share on other sites More sharing options...
XJDHDR Posted May 13, 2020 Share Posted May 13, 2020 Thanks Zilav, your Sniff tool worked like a charm. I just converted all of my models into JSONs, used Notepad++ to search and replace all the texture paths I needed then converted them back into NIFs. There were a few hundred texture paths I needed to replace and so creating a replacement pair for all of them would have probably taken a while. Link to comment Share on other sites More sharing options...
Arthmoor Posted May 13, 2020 Share Posted May 13, 2020 An interesting looking tool. Any chance the JSON conversion could be made to work on SSE nifs? Link to comment Share on other sites More sharing options...
zilav Posted May 14, 2020 Author Share Posted May 14, 2020 12 hours ago, Arthmoor said: An interesting looking tool. Any chance the JSON conversion could be made to work on SSE nifs? JSON convertion works with all games from Morrowind to Fallout 4. When you select an operation in Sniff it shows the list of supported games. Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now