Advertisement
kpvw

Skyrim - NPC Facegen Patcher.pas

Nov 21st, 2022
3,237
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 23.41 KB | None | 0 0
  1. {
  2.     NPC Facegen Patcher for Skyrim
  3.  
  4. Scans NPC records of the selected race and creates Facegeoms in the specified directory with
  5. updated texture paths. Designed to be used alongside mods that allow races to have body
  6. textures unique to a race e.g.:
  7. - Unique Everything
  8. - Unique Bodies by Race
  9. - VTF- Vampire Texture Framework
  10. Using those mods without this patcher will cause NPCs to have body/face texture mismatch.
  11. Run this script on entire load order (Ctrl+A then right click -> Apply script...). If you
  12. want to run it on a single esp then load only that plugin (and Dawnguard for vampire face
  13. texture resources) in xEdit. The head meshes generated by this patcher should then override
  14. all other. I advise to pack them into an archive format and load the archive as mod with a
  15. mod manager. This will allow for a clean way to remove them - which should be done before
  16. you use this script again (like after adding new mods with NPCs in them).
  17.  
  18. }
  19.  
  20. unit userscript;
  21.  
  22. //used throughout the script
  23. const
  24.   SavedPrefs = ScriptsPath + 'Skyrim - NPC Facegen Patcher.txt';
  25.   sOutputPath = '';
  26.   cb1 = 0;
  27.   cb2 = 0;
  28.   cb3 = 0;
  29.   cb4 = 0;
  30.   cb5 = 0;
  31.   cb6 = 0;
  32.   cb7 = 0;
  33.   cb8 = 0;
  34.   cb9 = 0;
  35.   cb10 = 0;
  36.   cb11 = 0;
  37.   cb12 = 0;
  38.   cb13 = 0;
  39.   cb14 = 0;
  40.   cb15 = 0;
  41.   cb16 = 0;
  42.   cb17 = 0;
  43.   cb18 = 0;
  44.   cb19 = 0;
  45.   cb20 = 0;
  46.   cb21 = 0;
  47.   cb22 = 0;
  48.  
  49. //called to check whether npc's race was selected for patching, note: BRACKETS ARE IMPORTANT.
  50. function IsSupportedRace(f: IInterface): boolean;
  51. var
  52.     race: string;
  53. begin
  54.     Result := false;
  55.     race := EditorID(LinksTo(ElementByPath(f, 'RNAM')));
  56.     if (
  57.  
  58.         ((race = 'ArgonianRace') and (cb1 = 1)) or //ArgonianRace
  59.         ((race = 'ArgonianRaceVampire') and (cb2 = 1)) or //ArgonianRaceVampire
  60.         ((race = 'BretonRace') and (cb3 = 1)) or //BretonRace
  61.         ((race = 'BretonRaceVampire') and (cb4 = 1)) or //BretonRaceVampire
  62.         ((race = 'DarkElfRace') and (cb5 = 1)) or //DarkElfRace
  63.         ((race = 'DarkElfRaceVampire') and (cb6 = 1)) or //DarkElfRaceVampire
  64.         ((race = 'ElderRace') and (cb7 = 1)) or //ElderRace
  65.         ((race = 'ElderRaceVampire') and (cb8 = 1)) or //ElderRaceVampire
  66.         ((race = 'HighElfRace') and (cb9 = 1)) or //HighElfRace
  67.         ((race = 'HighElfRaceVampire') and (cb10 = 1)) or //HighElfRaceVampire
  68.         ((race = 'ImperialRace') and (cb11 = 1)) or //ImperialRace
  69.         ((race = 'ImperialRaceVampire') and (cb12 = 1)) or //ImperialRaceVampire
  70.         ((race = 'KhajiitRace') and (cb13 = 1)) or //KhajiitRace
  71.         ((race = 'KhajiitRaceVampire') and (cb14 = 1)) or //KhajiitRaceVampire
  72.         ((race = 'NordRace') and (cb15 = 1)) or //NordRace
  73.         ((race = 'NordRaceVampire') and (cb16 = 1)) or //NordRaceVampire
  74.         ((race = 'OrcRace') and (cb17 = 1)) or //OrcRace
  75.         ((race = 'OrcRaceVampire') and (cb18 = 1)) or //OrcRaceVampire
  76.         ((race = 'RedguardRace') and (cb19 = 1)) or //RedguardRace
  77.         ((race = 'RedguardRaceVampire') and (cb20 = 1)) or //RedguardRaceVampire
  78.         ((race = 'WoodElfRace') and (cb21 = 1)) or //WoodElfRace
  79.         ((race = 'WoodElfRaceVampire') and (cb22 = 1)) //WoodElfRaceVampire
  80.  
  81.     ) then  Result := true; //race is supported
  82. end;
  83.  
  84. //main process - scanning records and doing the patching here, first thing is to declare all the variables we will use
  85. function Process(e: IInterface): integer;
  86. var
  87.     e2: IInterface;
  88.     el: TdfElement;
  89.     ncnt, j, fti: integer;
  90.     Elements: TList;
  91.     Nif: TwbNifFile;
  92.     Block: TwbNifBlock;
  93.     slResList: TStringList;
  94.     s: WideString;
  95.     race: string;
  96.     aContainerName, string_id, string_fp, file_source, file_path, r_tx00, r_tx01, r_tx03, r_tx04, r_tx07, s2, n, n2: string;
  97. begin
  98.     Result := 0; //will terminate script when changed
  99.  
  100.     //skipping all non-NPC records
  101.     if(Signature(WinningOverride(e)) <> 'NPC_') then
  102.         Exit;
  103.  
  104.     //skipping record if it is overriden by another record in loard order, that way we will only copy each nif once, so it is important to select all plugins in load order
  105.     if not IsWinningOverride(e) then begin
  106.         // AddMessage('Skipping: ' + Name(e) + ' --> Overriden by load order.');
  107.         Exit;
  108.     end;
  109.  
  110.     //skip if npc race is not one of the selected races, call function for that
  111.     if not IsSupportedRace(WinningOverride(e)) then begin
  112.         // AddMessage('Skipping: ' + Name(WinningOverride(e)) + ' --> Unsupported race.');
  113.         Exit;
  114.     end;
  115.  
  116.     //if npc record has assigned skin skip it, means it uses a custom skin, so we want to preserve its look
  117.     if (ElementExists(WinningOverride(e), 'WNAM - Worn Armor')) then begin
  118.         // AddMessage('Skipping: ' + Name(WinningOverride(e)) + ' --> Uses custom skin.');
  119.         Exit;
  120.     end;
  121.  
  122.     //find default head textureset based on race and gender
  123.     if (GetElementNativeValues(WinningOverride(e), 'ACBS\Flags') and 1 > 0) then
  124.         e2 := LinksTo(ElementByPath(WinningOverride(LinksTo(ElementByPath(WinningOverride(e), 'RNAM'))), 'Head Data\Female Head Data\DFTF - Default Face Texture Female'))
  125.       else
  126.         e2 := LinksTo(ElementByPath(WinningOverride(LinksTo(ElementByPath(WinningOverride(e), 'RNAM'))), 'Head Data\Male Head Data\DFTM - Default Face Texture Male'));
  127.  
  128.     //if npc has a head textureset assigned in its record use that instead
  129.     if (ElementExists(WinningOverride(e), 'FTST - Head texture')) then
  130.         e2 := LinksTo(ElementByPath(WinningOverride(e), 'FTST - Head texture'));
  131.  
  132.     //if not found a head textureset record in the two previous attempts assign vanilla defaults based on race and gender
  133.     if (not Assigned(e2)) then begin
  134.         race := EditorID(LinksTo(ElementByPath(WinningOverride(e), 'RNAM')));
  135.         if (GetFileName(FileByLoadOrder(2)) = 'Dawnguard.esm') then begin //Dawnguard detected
  136.  
  137.             if (GetElementNativeValues(WinningOverride(e), 'ACBS\Flags') and 1 = 0) then begin
  138.                
  139.                 if (race = 'ArgonianRace') then e2 := RecordByFormID(FileByIndex(0), $00069CDD, True);//ArgonianRace
  140.                 if (race = 'ArgonianRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000B3047, True);//ArgonianRaceVampire
  141.                 if (race = 'BretonRace') then e2 := RecordByFormID(FileByIndex(0), $0005164A, True);//BretonRace
  142.                 if (race = 'BretonRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//BretonRaceVampire
  143.                 if (race = 'DarkElfRace') then e2 := RecordByFormID(FileByIndex(0), $0001938E, True);//DarkElfRace
  144.                 if (race = 'DarkElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//DarkElfRaceVampire
  145.                 if (race = 'ElderRace') then e2 := RecordByFormID(FileByIndex(0), $00067CDC, True);//ElderRace
  146.                 if (race = 'ElderRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00067CDC, True);//ElderRaceVampire
  147.                 if (race = 'HighElfRace') then e2 := RecordByFormID(FileByIndex(0), $00038A60, True);//HighElfRace
  148.                 if (race = 'HighElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//HighElfRaceVampire
  149.                 if (race = 'ImperialRace') then e2 := RecordByFormID(FileByIndex(0), $00051646, True);//ImperialRace
  150.                 if (race = 'ImperialRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//ImperialRaceVampire
  151.                 if (race = 'KhajiitRace') then e2 := RecordByFormID(FileByIndex(0), $00081BA1, True);//KhajiitRace
  152.                 if (race = 'KhajiitRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000F123A, True);//KhajiitRaceVampire
  153.                 if (race = 'NordRace') then e2 := RecordByFormID(FileByIndex(0), $0003B521, True);//NordRace
  154.                 if (race = 'NordRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//NordRaceVampire
  155.                 if (race = 'OrcRace') then e2 := RecordByFormID(FileByIndex(0), $0007A5FF, True);//OrcRace
  156.                 if (race = 'OrcRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//OrcRaceVampire
  157.                 if (race = 'RedguardRace') then e2 := RecordByFormID(FileByIndex(0), $0005164B, True);//RedguardRace
  158.                 if (race = 'RedguardRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//RedguardRaceVampire
  159.                 if (race = 'WoodElfRace') then e2 := RecordByFormID(FileByIndex(0), $0003D2AB, True);//WoodElfRace
  160.                 if (race = 'WoodElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F83, True);//WoodElfRaceVampire
  161.             end;
  162.             if (GetElementNativeValues(WinningOverride(e), 'ACBS\Flags') and 1 > 0) then begin
  163.                 if (race = 'ArgonianRace') then e2 := RecordByFormID(FileByIndex(0), $00069CE0, True);//ArgonianRace
  164.                 if (race = 'ArgonianRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000B30F2, True);//ArgonianRaceVampire
  165.                 if (race = 'BretonRace') then e2 := RecordByFormID(FileByIndex(0), $00051647, True);//BretonRace
  166.                 if (race = 'BretonRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//BretonRaceVampire
  167.                 if (race = 'DarkElfRace') then e2 := RecordByFormID(FileByIndex(0), $00019390, True);//DarkElfRace
  168.                 if (race = 'DarkElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//DarkElfRaceVampire
  169.                 if (race = 'ElderRace') then e2 := RecordByFormID(FileByIndex(0), $00068390, True);//ElderRace
  170.                 if (race = 'ElderRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00068390, True);//ElderRaceVampire
  171.                 if (race = 'HighElfRace') then e2 := RecordByFormID(FileByIndex(0), $00038A5F, True);//HighElfRace
  172.                 if (race = 'HighElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//HighElfRaceVampire
  173.                 if (race = 'ImperialRace') then e2 := RecordByFormID(FileByIndex(0), $00051648, True);//ImperialRace
  174.                 if (race = 'ImperialRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//ImperialRaceVampire
  175.                 if (race = 'KhajiitRace') then e2 := RecordByFormID(FileByIndex(0), $000B79B6, True);//KhajiitRace
  176.                 if (race = 'KhajiitRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000F1239, True);//KhajiitRaceVampire
  177.                 if (race = 'NordRace') then e2 := RecordByFormID(FileByIndex(0), $0003B522, True);//NordRace
  178.                 if (race = 'NordRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//NordRaceVampire
  179.                 if (race = 'OrcRace') then e2 := RecordByFormID(FileByIndex(0), $00093150, True);//OrcRace
  180.                 if (race = 'OrcRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//OrcRaceVampire
  181.                 if (race = 'RedguardRace') then e2 := RecordByFormID(FileByIndex(0), $00051649, True);//RedguardRace
  182.                 if (race = 'RedguardRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//RedguardRaceVampire
  183.                 if (race = 'WoodElfRace') then e2 := RecordByFormID(FileByIndex(0), $0003D2AC, True);//WoodElfRace
  184.                 if (race = 'WoodElfRaceVampire') then e2 := RecordByFormID(FileByLoadOrder(2), $02006F9C, True);//WoodElfRaceVampire
  185.             end;
  186.         end;
  187.         if (not (GetFileName(FileByLoadOrder(2)) = 'Dawnguard.esm')) then begin //no Dawnguard
  188.             if (GetElementNativeValues(WinningOverride(e), 'ACBS\Flags') and 1 = 0) then begin
  189.                 if (race = 'ArgonianRace') then e2 := RecordByFormID(FileByIndex(0), $00069CDD, True);//ArgonianRace
  190.                 if (race = 'ArgonianRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000B3047, True);//ArgonianRaceVampire
  191.                 if (race = 'BretonRace') then e2 := RecordByFormID(FileByIndex(0), $0005164A, True);//BretonRace
  192.                 if (race = 'BretonRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0005164A, True);//BretonRaceVampire
  193.                 if (race = 'DarkElfRace') then e2 := RecordByFormID(FileByIndex(0), $0001938E, True);//DarkElfRace
  194.                 if (race = 'DarkElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0001938E, True);//DarkElfRaceVampire
  195.                 if (race = 'ElderRace') then e2 := RecordByFormID(FileByIndex(0), $00067CDC, True);//ElderRace
  196.                 if (race = 'ElderRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00067CDC, True);//ElderRaceVampire
  197.                 if (race = 'HighElfRace') then e2 := RecordByFormID(FileByIndex(0), $00038A60, True);//HighElfRace
  198.                 if (race = 'HighElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00038A60, True);//HighElfRaceVampire
  199.                 if (race = 'ImperialRace') then e2 := RecordByFormID(FileByIndex(0), $00051646, True);//ImperialRace
  200.                 if (race = 'ImperialRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00051646, True);//ImperialRaceVampire
  201.                 if (race = 'KhajiitRace') then e2 := RecordByFormID(FileByIndex(0), $00081BA1, True);//KhajiitRace
  202.                 if (race = 'KhajiitRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000F123A, True);//KhajiitRaceVampire
  203.                 if (race = 'NordRace') then e2 := RecordByFormID(FileByIndex(0), $0003B521, True);//NordRace
  204.                 if (race = 'NordRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0003B521, True);//NordRaceVampire
  205.                 if (race = 'OrcRace') then e2 := RecordByFormID(FileByIndex(0), $0007A5FF, True);//OrcRace
  206.                 if (race = 'OrcRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0007A5FF, True);//OrcRaceVampire
  207.                 if (race = 'RedguardRace') then e2 := RecordByFormID(FileByIndex(0), $0005164B, True);//RedguardRace
  208.                 if (race = 'RedguardRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0005164B, True);//RedguardRaceVampire
  209.                 if (race = 'WoodElfRace') then e2 := RecordByFormID(FileByIndex(0), $0003D2AB, True);//WoodElfRace
  210.                 if (race = 'WoodElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0003D2AB, True);//WoodElfRaceVampire
  211.             end;
  212.             if (GetElementNativeValues(WinningOverride(e), 'ACBS\Flags') and 1 > 0) then begin
  213.                 if (race = 'ArgonianRace') then e2 := RecordByFormID(FileByIndex(0), $00069CE0, True);//ArgonianRace
  214.                 if (race = 'ArgonianRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000B30F2, True);//ArgonianRaceVampire
  215.                 if (race = 'BretonRace') then e2 := RecordByFormID(FileByIndex(0), $00051647, True);//BretonRace
  216.                 if (race = 'BretonRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00051647, True);//BretonRaceVampire
  217.                 if (race = 'DarkElfRace') then e2 := RecordByFormID(FileByIndex(0), $00019390, True);//DarkElfRace
  218.                 if (race = 'DarkElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00019390, True);//DarkElfRaceVampire
  219.                 if (race = 'ElderRace') then e2 := RecordByFormID(FileByIndex(0), $00068390, True);//ElderRace
  220.                 if (race = 'ElderRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00068390, True);//ElderRaceVampire
  221.                 if (race = 'HighElfRace') then e2 := RecordByFormID(FileByIndex(0), $00038A5F, True);//HighElfRace
  222.                 if (race = 'HighElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00038A5F, True);//HighElfRaceVampire
  223.                 if (race = 'ImperialRace') then e2 := RecordByFormID(FileByIndex(0), $00051648, True);//ImperialRace
  224.                 if (race = 'ImperialRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00051648, True);//ImperialRaceVampire
  225.                 if (race = 'KhajiitRace') then e2 := RecordByFormID(FileByIndex(0), $000B79B6, True);//KhajiitRace
  226.                 if (race = 'KhajiitRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $000F1239, True);//KhajiitRaceVampire
  227.                 if (race = 'NordRace') then e2 := RecordByFormID(FileByIndex(0), $0003B522, True);//NordRace
  228.                 if (race = 'NordRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0003B522, True);//NordRaceVampire
  229.                 if (race = 'OrcRace') then e2 := RecordByFormID(FileByIndex(0), $00093150, True);//OrcRace
  230.                 if (race = 'OrcRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00093150, True);//OrcRaceVampire
  231.                 if (race = 'RedguardRace') then e2 := RecordByFormID(FileByIndex(0), $00051649, True);//RedguardRace
  232.                 if (race = 'RedguardRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $00051649, True);//RedguardRaceVampire
  233.                 if (race = 'WoodElfRace') then e2 := RecordByFormID(FileByIndex(0), $0003D2AC, True);//WoodElfRace
  234.                 if (race = 'WoodElfRaceVampire') then e2 := RecordByFormID(FileByIndex(0), $0003D2AC, True);//WoodElfRaceVampire
  235.             end;
  236.  
  237.         end;
  238.  
  239.     end;
  240.  
  241.     //if all attempts to find the head textureset failed give up on this record
  242.     if (not Assigned(e2)) then begin
  243.         // AddMessage('Skipping ' + Name(WinningOverride(e)) + ' -> ' + 'face texture not assigned.');
  244.         Exit;
  245.     end;
  246.    
  247.     //make sure we are looking for texture paths in the latest TXST record
  248.     e2 := WinningOverride(e2);
  249.     el := ElementBySignature(e2, 'TX00');
  250.  
  251.     //write paths for textures from the textureset record to strings, we'll use them for patching the nif
  252.     r_tx00 := GetEditValue(ElementBySignature(el, 'TX00'));
  253.     r_tx01 := GetEditValue(ElementBySignature(el, 'TX01'));
  254.     r_tx03 := GetEditValue(ElementBySignature(el, 'TX03'));
  255.     r_tx04 := GetEditValue(ElementBySignature(el, 'TX04'));
  256.     r_tx07 := GetEditValue(ElementBySignature(el, 'TX07'));
  257.  
  258.     // find the path to where the FaceGen nif is located and its name
  259.     string_id := IntToHex(FixedFormID(WinningOverride(e)) and $00FFFFFF, 8);
  260.     string_fp := GetFileName(GetFile(MasterOrSelf(WinningOverride(e))));
  261.     file_source := 'meshes\actors\character\FaceGenData\FaceGeom\' + string_fp + '\' + string_id + '.nif';
  262.     file_path := sOutputPath + 'meshes\actors\character\FaceGenData\FaceGeom\' + string_fp + '\' + string_id + '.nif';
  263.  
  264.     // extract the FaceGen nif, find all files containing it to use the latest one
  265.     slResList := TStringList.Create;
  266.     ncnt:= ResourceCount(file_source, slResList);
  267.     if ncnt = 0 then begin
  268.         Result := false;
  269.         // AddMessage('Skipping ' + Name(WinningOverride(e)) + ' -> ' + file_source + ' does not exist.');
  270.         Exit;
  271.     end;
  272.     aContainerName := slResList[slResList.Count - 1];
  273.     slResList.Free;
  274.     if ResourceExists(file_source) then begin
  275.         AddMessage('Looking in ' + aContainerName + ' for ' + file_source);
  276.         AddMessage('Copying ' + file_source + ' -> ' + file_path);
  277.         ResourceCopy(aContainerName,file_source,file_path);
  278.     end;
  279.    
  280.     //begin work on NIF
  281.     fti := 0; //face texture index - this will identify the block that stores head textures inside nif
  282.  
  283.     n := '\' + string_id + '.dds'; //name of tintmask file for this npc, this will be used to find the block inside nif that stores textures for face/head
  284.  
  285.     //make sure the nif file was extracted before we proceed, some npcs use templates and do not possess a facegen
  286.     if FileExists(file_path) then
  287.         AddMessage('Processing ' + Name(WinningOverride(e)) + ' -> ' + file_path)
  288.     else begin
  289.         // AddMessage('Skipping ' + Name(WinningOverride(e)) + ' -> ' + file_path + ' does not exist.');
  290.         Exit;
  291.     end;
  292.    
  293.     try
  294.         // collecting elements with textures
  295.         // *.NIF file
  296.  
  297.         Elements := TList.Create;
  298.         Nif := TwbNifFile.Create;
  299.         Nif.LoadFromFile(file_path);
  300.  
  301.         // iterate over all blocks in a nif file and locate elements holding textures, first we will collect all the texture paths before we find the proper ones
  302.         for j := 0 to Pred(Nif.BlocksCount) do begin
  303.             Block := Nif.Blocks[j];
  304.             if Block.BlockType = 'BSShaderTextureSet' then begin
  305.                 el := Block.Elements['Textures'];
  306.                 for j := 0 to Pred(el.Count) do
  307.                     Elements.Add(el[j]);
  308.             end
  309.         end;
  310.  
  311.         // iterate thorugh all collected stuff and do text replacement in selected parts
  312.         for j := 0 to Pred(Elements.Count) do begin
  313.             if not Assigned(Elements[j]) then
  314.                 Continue
  315.             else begin
  316.                 el := TdfElement(Elements[j]);
  317.  
  318.                 // getting the texture name stored into a string we'll use for comparison
  319.                 s := el.EditValue;
  320.                 // compare if texture name contains the path for the tintmask texture, if so we have found the block we are looking for
  321.                 if pos(LowerCase(n), LowerCase(s)) > 0 then
  322.                     fti := j //marks the position of the tintmask texture string
  323.                 else begin
  324.                     n2 := LowerCase(n); //just abbreviation
  325.                     s2 := LowerCase(s);
  326.                     //finding a tintmask texture which path points to a different dds file, this probably won't be happening because we skipped records with custom skin
  327.                     if (pos('.esm', s2) > 0) or (pos('.esp', s2) > 0) and (pos('facetint', s2) > 0) and (copy(s2, Length(s2) - 4, 4) = '.dds') then begin
  328.                         fti := j; //marks the position of the tintmask texture string
  329.                         AddMessage('--> WARNING: CharGen texture block detected but FaceTint used points to a different file.');
  330.                     end
  331.                 end
  332.             end
  333.         end;
  334.         //if the block conaining Tintmask texture was found use its position as a reference for which strings to replace with our corrected texture paths
  335.         if fti = 0 then
  336.             AddMessage('--> NIF has no CharGen texture block');
  337.         if fti <> 0 then begin
  338.             TdfElement(Elements[fti-6]).EditValue := r_tx00;
  339.             TdfElement(Elements[fti-5]).EditValue := r_tx01;
  340.             TdfElement(Elements[fti-4]).EditValue := r_tx03;
  341.             TdfElement(Elements[fti-3]).EditValue := r_tx04;
  342.             TdfElement(Elements[fti+1]).EditValue := r_tx07;
  343.             el.Root.SaveToFile(file_path);
  344.         end;
  345.     finally
  346.         Elements.Free;
  347.         Nif.Free;
  348.     end;
  349.  
  350. end;
  351.  
  352. // On starting the script
  353. function Initialize: Integer;
  354. var
  355.     frm: TForm;
  356.     clb: TCheckListBox;
  357.     slPreferences: TStringList;
  358. begin
  359.     Result := 0; //will terminate script when changed
  360.     if not (GetFileName(FileByLoadOrder(2)) = 'Dawnguard.esm') then begin
  361.         AddMessage('-->WARNING: Dawnguard.esm was not found. Check if its the third plugin (after Skyrim.esm and Update.esm) in your load order.');
  362.     end;
  363.     frm := frmFileSelect;
  364.     frm.Caption := 'Select races to scan:';
  365.     clb := TCheckListBox(frm.FindComponent('CheckListBox1'));
  366.     clb.Items.Append('Argonian');
  367.     clb.Items.Append('Argonian (Vampire)');
  368.     clb.Items.Append('Breton');
  369.     clb.Items.Append('Breton (Vampire)');
  370.     clb.Items.Append('Dark Elf');
  371.     clb.Items.Append('Dark Elf (Vampire)');
  372.     clb.Items.Append('Old People');
  373.     clb.Items.Append('Old People (Vampire)');
  374.     clb.Items.Append('High Elf');
  375.     clb.Items.Append('High Elf (Vampire)');
  376.     clb.Items.Append('Imperial');
  377.     clb.Items.Append('Imperial (Vampire)');
  378.     clb.Items.Append('Khajit');
  379.     clb.Items.Append('Khajit (Vampire)');
  380.     clb.Items.Append('Nord');
  381.     clb.Items.Append('Nord (Vampire)');
  382.     clb.Items.Append('Orc');
  383.     clb.Items.Append('Orc (Vampire)');
  384.     clb.Items.Append('Redguard');
  385.     clb.Items.Append('Redguard (Vampire)');
  386.     clb.Items.Append('Wood Elf');
  387.     clb.Items.Append('Wood Elf (Vampire)');
  388.     if frm.ShowModal <> mrOk then begin
  389.         Result := 1;
  390.         Exit;
  391.     end;
  392.     if clb.Checked[0] then cb1 := 1;
  393.     if clb.Checked[1] then cb2 := 1;
  394.     if clb.Checked[2] then cb3 := 1;
  395.     if clb.Checked[3] then cb4 := 1;
  396.     if clb.Checked[4] then cb5 := 1;
  397.     if clb.Checked[5] then cb6 := 1;
  398.     if clb.Checked[6] then cb7 := 1;
  399.     if clb.Checked[7] then cb8 := 1;
  400.     if clb.Checked[8] then cb9 := 1;
  401.     if clb.Checked[9] then cb10 := 1;
  402.     if clb.Checked[10] then cb11 := 1;
  403.     if clb.Checked[11] then cb12 := 1;
  404.     if clb.Checked[12] then cb13 := 1;
  405.     if clb.Checked[13] then cb14 := 1;
  406.     if clb.Checked[14] then cb15 := 1;
  407.     if clb.Checked[15] then cb16 := 1;
  408.     if clb.Checked[16] then cb17 := 1;
  409.     if clb.Checked[17] then cb18 := 1;
  410.     if clb.Checked[18] then cb19 := 1;
  411.     if clb.Checked[19] then cb20 := 1;
  412.     if clb.Checked[20] then cb21 := 1;
  413.     if clb.Checked[21] then cb22 := 1;
  414.     clb.Free;
  415.     frm.Free;
  416.     if (cb1+cb2+cb3+cb4+cb5+cb6+cb7+cb8+cb9+cb10+cb11+cb12+cb13+cb14+cb15+cb16+cb17+cb18+cb19+cb20+cb21+cb22=0) then begin
  417.         Result := 1;
  418.         Exit;
  419.     end;
  420.     slPreferences := TStringList.Create;
  421.     if FileExists(SavedPrefs) then begin
  422.         slPreferences.LoadFromFile(SavedPrefs);
  423.         sOutputPath := slPreferences[0];
  424.     end;
  425.     if not InputQuery('NPC Facegen Patcher', 'Directory to extract files to:', sOutputPath) then begin
  426.         Result := 1;
  427.         Exit;
  428.     end;
  429.     if sOutputPath = '' then begin
  430.         Result := 1;
  431.         Exit;
  432.     end;
  433.     sOutputPath := IncludeTrailingBackslash(sOutputPath);
  434.     if sOutputPath = DataPath then begin
  435.         AddMessage('-->ERROR: Output directory should not be the Skyrim Data folder i.e. ' +DataPath);
  436.         Result := 1;
  437.         Exit;
  438.     end;
  439.     if not ForceDirectories(sOutputPath) then begin
  440.         AddMessage('-->ERROR: Invalid directory: ' +sOutputPath);
  441.         Result := 1;
  442.         Exit;
  443.     end;
  444.     if not FileExists(SavedPrefs) then
  445.         slPreferences.Add(sOutputPath)
  446.     else
  447.         slPreferences[0] := sOutputPath;
  448.     slPreferences.SaveToFile(SavedPrefs);
  449.     slPreferences.Free;
  450. end;
  451.  
  452. end.
  453.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement