Jump to content

boozehoundblue

Members
  • Posts

    9
  • Joined

  • Last visited

Posts posted by boozehoundblue

  1. Thanks for clearing that up! Got it working. According to the Wiki, Signature(e) is defined as returning a string.

     

    As far as the Case statement's purpose, I intend it as a means to operate on records individually. No, it doesn't serve a particular purpose in my posted script, but I plan to incorporate field exclusions from an ini to ultimately produce json (and other) files for different record types. The generic Write procedure(s) were mostly for me to have something to study.

     

    Since both 'Case int of' and 'if SigStr = 'ABCD' accomplish the same task, the choice comes down to which looks/performs better. Maybe the performance difference isn't worth the hassle or is negated by the cost of conversion.

     

    I see what you mean about being quite slow now, having run a working version over Fallout4.esm's Npcs. Yikes.

     

     

     

     

     

     

     

  2. Thanks for the advice. I've spent the last month fumbling around in Python and the source code, then decided an xEdit Script would probably be most useful. However, I've hit a couple of errors that I can't figure my way around, trying to create a switch case from (integer representations of) record signatures. If you have a chance, could you have a look? https://forums.nexusmods.com/index.php?/topic/6611661-tes5edit-scripting-compiling-woes-enumeration-type-et-al/

  3. Removed TSig type entirely. Compiles, but execution in Editor now raises 'Array type required' when attempting to get a character from a string (i.e. ch := SigStr in SigtoInt function). Idk how to get a char otherwise.

    unit myEditScript;
    
    interface
    
    uses xEditAPI, Classes, SysUtils, StrUtils, Windows;
    
    // Global variables
    var List : TStringList;
    var aIndent : string;
    var Sig : integer;
    
    procedure WriteElement(e : IInterface; Sig : integer; aIndent: string = '');
    procedure WriteContainer(e : IInterface; Sig : integer; aIndent: string = '');
    
    function RemoveNumbering(str : string): string;
    function SigToInt(SigStr: string): integer;
    function SigSwitch(Sig: integer): integer;
    
    implementation
    
    function RemoveNumbering(str : string): string;
    var i : integer;
    begin
      Result := '';
      i := Pos(' #', str);
      if (i = 0) then
        Result := str
      else
        Result := copy(str, 1, i-1);
    end;
    
    function SigToInt(SigStr: string): integer;
    var
      i, e, Sig : integer;
      c        : string;
      ch       : Char;
    begin
      e := 3;
      for i := 1 to 4 do
        ch := SigStr[i];
        Case e of
          0: Sig := Sig + ord(ch);
          1: Sig := Sig + ord(ch)*100;
          2: Sig := Sig + ord(ch)*10000;
          3: Sig := Sig + ord(ch)*1000000;
        End;
        e := e - 1;
      Result := Sig;
    end;
    
    function SigSwitch(Sig: integer): integer;
    begin
      Result :=0;
      Case Sig of
        78806795:
        begin
          Result := 1;
          List.Add('NPC_ found');
        end;
      End;
    end;
    
    procedure WriteContainer(e : IInterface; Sig : integer; aIndent: string = '');
    var
      i            : Integer;
    begin
      for i := 0 to Pred(ElementCount(e)) do
        WriteElement(ElementByIndex(e, i), Sig, aIndent);
    end;
    
    procedure WriteElement(e : IInterface; Sig : integer; aIndent: string = '');
    var
      Name        : string;
      Value       : string;
      Str         : string;
      defTypeStr  : string;
      eleTypeStr  : string;
      SigStr      : string;
    begin
    
      Name := ShortName(e);
      Value := GetEditValue(e);
      SigSwitch(Sig);
      if (Name <> 'Unused') then begin
        if (Name <> '')then
          Str := aIndent + RemoveNumbering(Name);
        if (Name <> '') or (Value <> '') then
          aIndent := aIndent + '  ';
        if (Value <> '') then begin
          Str := Str +': '  + Value;
          List.Add(Str)
        end else begin
          if (Name <> '') then
            List.Add(Str)
        end;
      end;
      WriteContainer(e, Sig, aIndent);
    
    end;
    // Called when the script starts
    function Initialize : integer;
    begin
    	List := TStringList.Create;
    end;
    
    // Called for each selected record in the TES5Edit tree
    // If an entire plugin is selected then all records in the plugin will be processed
    function Process(e : IInterface) : integer;
    begin
      aIndent := '';
      Sig := SigToInt(Signature(e));
      WriteContainer(e, Sig, aIndent);
    end;
    
    // Called after the script has finished processing every record
    function Finalize : integer;
    var filename : string;
    begin
    	filename := ProgramPath + 'Edit Scripts\Test.txt';
    	AddMessage('Saving to ' + filename);
    	List.SaveToFile(filename);
    	List.Free;
    end;
    
    end.
    
    
    
  4. I'm having a hard time getting a script together that will both compile in Delphi's Starter IDE and run in FO4Edit. It's typically one or the other, due to enums. For example, I can operate on DefType or ElementType returns, and have created a script that runs correctly, but it raises compile errors in IDE, which makes it difficult to check other syntax errors I may be making while trying to learn the language. That's a general problem I'm having. (But maybe I can add 'wbDefinitionsFO4' from source to uses to compile, then remove? Idk.)

     

    Now I have a script which doesn't (yet) utilize those type returns and which does compile correctly but raises "Class declaration expected but '(' found", due to my apparently otherwise seemingly correct implementation of my own enumeration type, based off Delphi's online and TES5Edit's source examples. The TES5 Scripting Functions Wiki notes that the 'type' keyword is unsupported but it can be used to "alias a class name" (whatever that means). I don't know how to define this enumeration otherwise, or if I'm even allowed to use it within the confines of the API. (It's all magic and a bunch of := and ; but not += to me. Yeesh.)

     

    Anyway, I'm trying to write a script that will write a collection of records to json(s). In order to do that, I first want to remove the numbering from things like 'Value #1' and otherwise put elements that are described as arrays (in 'wbDefinitionsFO4') into arrays (because ElementType doesn't tell the whole story in that regard). This entails pre-compiling lists (or delimited strings) of Names for each record type to search against as I cycle through the container(s) in order to format the json correctly. To do the search, I thought I'd grab the signature of the record and run it through a case statement, so I know which particular strings to search for. Can't do that for strings, and I don't want to write a bunch of if else statements if I can help it. So I figured I'd change to signature to its unique integer and run it through a case of similarly pre-determined signature enum integers and fork from there. Smart? Idk.(A timeit test in Python showed that, even with conversion cost, 'int in [ints]' was 2x+ faster than 'string in [strings]'. Python doesn't have cases, and from the Wiki, Edits scripts don't support 'in'. Yeesh.) I can't get my script to run in Editor, let alone set up some bench comparisons. (It did work before I added the enum/case stuff, and output in same format as an xDump, since my Write functions are adapted directly from it.)

     

    Rant over. Code with snipped enum below. Any insight/guidance would be appreciated. Thanks!

    unit myEditScript;
    
    interface
    
    uses xEditAPI, Classes, SysUtils, StrUtils, Windows, Math;
    
    type
      TSig = (
        _AACT = 65656784,
        _NPC_ = 78806795
        );
    // Global variables
    var List : TStringList;
    var aIndent : string;
    
    procedure WriteElement(e : IInterface; aIndent: string = '');
    procedure WriteContainer(e : IInterface; aIndent: string = '');
    
    function RemoveNumbering(str : string): string;
    function SigToTSig(SigStr: string): TSig;
    function SigSwitch(Sig: TSig): integer;
    
    implementation
    
    function RemoveNumbering(str : string): string;
    var i : integer;
    begin
      Result := '';
      i := Pos(' #', str);
      if (i = 0) then 
        Result := str
      else
        Result := copy(str, 1, i-1);
    end;
    
    //idk if this works yet, different than my Python test function
    function SigToTSig(SigStr: string): TSig;
    var
      i, ch, e, Sig : integer;
    begin
      e := 3;
      for i := 1 to 4 do
        ch := ord(SigStr[i]);
        Sig := Sig + Round(ch*IntPower(100,e));
        e := e - 1;
      Result := TSig(Sig);
    end;
    
    function SigSwitch(Sig: TSig): integer;
    begin
      Result :=0;
      Case Sig of
        _NPC_: Result:=1;
      End;
    end;
    
    procedure WriteContainer(e : IInterface; aIndent: string = '');
    var
      i            : Integer;
    begin
      for i := 0 to Pred(ElementCount(e)) do
        WriteElement(ElementByIndex(e, i), aIndent);
    end;
    
    procedure WriteElement(e : IInterface; aIndent: string = '');
    var
      Name        : string;
      Value       : string;
      Str         : string;
      defTypeStr  : string;
      eleTypeStr  : string;
      SigStr      : string;
    begin
    
      Name := ShortName(e);
      Value := GetEditValue(e);
      SigStr := Signature(e);
      //first if just a test to see if it will catch
      if (SigSwitch(SigToTSig(SigStr)) = 1) then
        List.Add('Match');
      if (Name <> 'Unused') then begin
        if (Name <> '')then
          Str := aIndent + RemoveNumbering(Name);
        if (Name <> '') or (Value <> '') then
          aIndent := aIndent + '  ';
        if (Value <> '') then begin
          Str := Str +': '  + Value;
          List.Add(Str)
        end else begin
          if (Name <> '') then
            List.Add(Str)
        end;
      end;
      WriteContainer(e, aIndent);
    
    end;
    // Called when the script starts
    function Initialize : integer;
    begin
    	List := TStringList.Create;
    end;
    
    // Called for each selected record in the TES5Edit tree
    // If an entire plugin is selected then all records in the plugin will be processed
    function Process(e : IInterface) : integer;
    begin
      aIndent := '';
      WriteContainer(e, aIndent);
    end;
    
    // Called after the script has finished processing every record
    function Finalize : integer;
    var filename : string;
    begin
    	filename := ProgramPath + 'Edit Scripts\Test.txt';
    	AddMessage('Saving to ' + filename);
    	List.SaveToFile(filename);
    	List.Free;
    end;
    
    end.
    
  5. Thanks for the replies.

    I am pretty sure they did work when I was working on them the last time around but that was a long time ago and I haven't tried since.
    Could you create a bug report on GitHub ? Hopefully I'll find some time to check this.

    https://github.com/TES5Edit/TES5Edit/issues

    I was leery about raising an issue on github if I was indeed making a simple mistake/misunderstanding. zilav asserts I am.

     

    You can exlude entire records only when dumping a group providing their signatures, there is no per field exclusions in xDump.

    Thanks for clearing that up. I guess -xcg:list doesn't serve a purpose if I'm interested in dumping specific groups, as each record has the same signature. I can just use -dg:list and -xr:TES4 (to at least remove the main file header, minus 2 lines of artifact).

     

    I was also wondering about the export option mentioned in the help screen (below). Couldn't figure out the proper syntax in my above command to export to Wiki Table format:
    Syntax: FO4Dump [options] inputfile
    or FO4Export [options] format
    Ultimately, I'm trying to cull and restructure the output to template a spreadsheet (for Articy:Draft) or xmls, json, whatever. Where might I look around in the source code to begin to understand how the esp/esm is read and how the dump structure is written? Is there any technical documentation about the file structure? For example, Bioware had a pretty comprehensive set of pdfs for NWN that I could follow to program reading/writing dialogues to json.
  6. FO4Dump

     

    I've been trying to dump an NPC_ group while excluding things like DNAM,OBND,DATA,Head Parts, etc; but I can't seem to get the child record exclusion list (-xcg:list) to work. For example:

     

    fo4dump.exe -dg:NPC_ -xcg:DNAM,OBND,DATA fallout4.esm >mydump.txt

     

    I can correctly dump the one group but all that data is still there.

     

    Am I making a trivial mistake or misunderstanding record child groups? Or do the options not work in this conjunction?

     

  7. Curious about the availability of using existing furniture models (such as the misc. bar, i think) or something like a floor mat as a store/emporium. I'd like to use different surfaces in actual custom built shops (interior/exterior) in addition to the vanilla models. I know the traders lean/clean the shop counter surface, so maybe furniture of that height, or disable those animations? Ideally just stand behind whatever 'counter' I place (i.e. a designated floor mat).

     

    Not finding anything like it. Any interest?

×
×
  • Create New...