1. Melee Mapping contest #3 - Poll is up! Vote for the best 4v4 melee maps!
    Dismiss Notice
  2. The 30th edition of the Modeling Contest is finally up! The Portable Buildings need your attention, so come along and have a blast!
    Dismiss Notice
  3. We have a new contest going on right now! Join the 11th Music Contest! You are to make a Cinematic modern sound-track for this contest, so come and compete with other people for fun.
    Dismiss Notice

Lua Object Generation

Discussion in 'JASS/AI Scripts Tutorials' started by PurgeandFire, Mar 17, 2011.

  1. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,416
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    k, I will add that and some other updates later on.
     
  2. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    544
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Very useful tutorial but how do I perform arithmetic operations on object IDs while keeping the hexadecimal representation in LUA?

    For example I have the ID "A000" and want to use 5 more IDs starting from this one: "A001", "A002" ...

    I need something like tohexstring(tonumber(baseId) + 1).

    If someone like me has many abilities to generate what would be the best way to choose their IDs. I thought of choosing a baseId and using a range from that by arithmetic operations.

    For example http://www.hiveworkshop.com/forums/graveyard-418/snippet-lua_object_id-176943/ says it may overwrite custom object ids and does it change the ID everytime I save the map? I want to keep some IDs constant for my generated abilities.
     
  3. Nestharus

    Nestharus

    Joined:
    Jul 10, 2007
    Messages:
    6,149
    Resources:
    8
    Spells:
    3
    Tutorials:
    4
    JASS:
    1
    Resources:
    8
  4. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    I think that object w3b is bugged, also for the ability field amcs


    Why?

    whenever I try to create a custom buff with w3b, I always receive an error, where it says that the base object cannot be found. I tried doing this on other buffs and it still gives me error.

    For the case of amcs (Mana Cost), no matter what value you put, it won't change :/

    Try this(if this works for you, then I think it is my ObjectMerger that is bugged):
    //! external ObjectMerger w3a AHtb TEST amcs 1 0
     
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,416
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    This might be your problem:
    Make sure you use w3h, not w3b. In your test line, you also used w3a.
     
  6. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    so that's why. I hate that :D

    In the test line, it was a demo how amcs (Mana Cost) remains unchanged. It uses Thunder Bolt as a a base ability.
     
  7. EdwardElric

    EdwardElric

    Joined:
    Jun 19, 2010
    Messages:
    49
    Resources:
    0
    Resources:
    0
    i'm here to report a bug of the ObjectMerger. it seems that unit 'hpea', field "uaen" can not be set to 0 through LUA trigger.
    here's a recent thread reply of mine showing my LUA code (post #3):
    link1: [vJASS] - LUA: Ascii typecasting on ObjectMerger

    after some chat with WaterKnight i tried to inspect ObjectMerger source code to fix the problem. got official source code from author PitzerMike from here:
    link2: http://www.hiveworkshop.com/pastebin/e10b1021af8b2b19368786869def5c9c2343/
    ( link3: Source code of all my work - Wc3C.net )

    however, im already stuck at "...\War3ToolsSource\ObjectMerger\main.cpp" at following line in main method:
    Code (Text):
        if (argc < 4 ) { // check parameter count
           fprintf(stderr, "Wrong parameters for object merger, usage:\nmap.w3x lookuppaths [ m | r | i ] mergefile { moremergefiles }\nmap.w3x lookuppaths ( w3u | w3t | w3b | w3d | w3a | w3h | w3q ) originalid newid { changeid [ level | variation ] value }\n");
           exit(-1);
        }
    the thing i do not understand is, that argc should already be < 4 for following command in JASS:
    //! external ObjectMerger Objects\war3map.w3u

    how exactly is this command interpreted?
    if it just runs ObjectMerger.exe & passes argument "Objects\war3map.w3u", then argc should be 1, which would cause ObjectMerger.exe to exit, because of the quoted if-block above... what am i missing?



    edit1:
    from the code comment example in "...\ObjectMerger\main.cpp" it seems, that argument passing can be explained like following:
    Code (Text):
    EXAMPLE CALL:
    //! external ObjectMerger Objects\war3map.w3u
    SHOULD PASS 4 ARGUMENTS LIKE:
    //argv[0] = "";    <-- ALWAYS NULL?!
    //argv[1] = "F:\\Sweeper.w3x";    <-- PATH TO MAP (AUTOMATICALLY!? BUT HOW & WHERE???)
    //argv[2] = "";    <-- LOOKUP PATHS (FOR RELATIVE PATHS) (AUTOMATICALLY!? BUT HOW & WHERE???)
    //argv[3] = "Objects\war3map.w3u";    <-- PASSED PATH TO W3U OBJECT DATA FILE FOR UNITS
    so, about the how & where: it happens on map saving, but is it hardcoded into JNGP (= Jass New Gen Pack) or can i view the responsible code anywhere?

    edit2:
    in "...\JNGP\wehack.lua":
    Code (Text):
    function runobjectmerger(mode)
        curmap = wehack.findmappath()
        if curmap ~= "" then
            source = wehack.openfiledialog("Unit files (*.w3u)|*.w3u|Item files (*.w3t)|*w3t|Doodad files (*.w3d)|*.w3d|Destructable files (*.w3b)|*.w3b|Ability files (*.w3a)|*.w3a|Buff files (*.w3h)|*.w3h|Upgrade files (*.w3q)|*.w3q|", "w3a", "Select files to import ...", true)
    grim.log("got in lua: " .. source)
            if source ~= "" then
                list = strsplit("|", source);
    --            cmdargs = "ObjectMerger \""..curmap.."\" "..wehack.getlookupfolders().." "..mode..fileargsjoin(list)
                cmdargs = "grimext\\ObjectMerger.exe \""..curmap.."\" "..wehack.getlookupfolders().." "..mode..fileargsjoin(list)
    grim.log("assembled cmdline: " .. cmdargs)
    --            wehack.messagebox(cmdargs,"Grimoire",false)
                wehack.savemap()
    grim.log("called saved map")
            end
        else
            showfirstsavewarning()
        end
    end
    but this function is only called via WE GUI (= World Editor Graphical User Interface) menu entries:
    - "World Editor/Extensions/Merge Object Editor Data"
    - "World Editor/Extensions/Replace Object Editor Data"
    - "World Editor/Extensions/Import Object Editor Data"
    so still I have no clue where to find the code which controls the passing of arguments to ObjectMerger.exe from trigger code calls & not from WE GUI menu entries.

    edit3:
    still in "...\JNGP\wehack.lua":
    Code (Text):
    function compilemap_path(mappath)
    ...
           toolresult = wehack.runprocess2(cmdline)
    ...
    function "compilemap_path" runs each map save & within, the function "wehack.runprocess2" runs for map triggers (including external tool calls)! at top of this LUA file following is written as comment:
    Code (Text):
    -- wehack.runprocess2:  Wait for exit code, don't report errors (jasshelper)
    so the magic parsing of arguments for trigger based ObjectMerger code is done here i guess. looked into "...\JNGP\vexorianjasshelper\" & saw "pjass.exe". "pjass.exe" means "Parse JASS" i guess. found its source code in tools from PitzerMike (see "link2" above)! but it doesn't contain words from error when external calls to ObjectMerger are wrong. there must be something like: "Invalid object id ...".
    so "pjass.exe" isn't passing arguments to ObjectMerger from trigger code!
    what ramains?
    --> "...\JNGP\vexorianjasshelper\jasshelper.exe"!
    official link to app & source code of latest JassHelper from author Vexorian (see attachment in post #1 in following thread):
    link4: JassHelper 0.A.2.B - A vJass and Zinc 2 Jass compiler - Wc3C.net

    edit4:
    the comment i found in source code of PitzerMike's Grimex (= GRIMoire EXtension pack) under "...\War3ToolsSource\ObjectMerger\main.cpp" which describes ObjectMerger argument usage, also is explained in the documentation under "...\JNGP\grimext\GrimexManual.html" under "Quickstart/Object Merger:":
    Code (Text):
    map.w3x lookuppaths [ m | r | i ] mergefile { moremergefiles }
    map.w3x lookuppaths ( w3u | w3t | w3b | w3d | w3a | w3h | w3q ) originalid newid { changeid [ level | variation ] value }
    edit5:
    i was able to compile ObjectMerger.exe from source code (see "link2" above). sadly this source code isn't the latest, as the author PitzerMike lost the current:
    the compiled ObjectMerger.exe file is 465 KB in size, while the channel ability hotfixed ObjectMerger.exe file is 548 KB in size:
    link5: Wc3C.net - View Single Post - Grim Extension Pack
    this makes me wonder how much PitzerMike must have been added to its source to make the file increase by 83 KB only by code, especially what else did he add to ObjectMerger than fixing channel ability???
    so even if i fix the problem with the unit 'hpea' with the field "uaen", some current features may be missing... maybe someone still has the latest source code of ObjectMerger?
    all what i found is a thread where PitzerMike linked his source code from 2011_10_16, but unfortunately it says "404 PAGE NOT FOUND" (last post #15):
    link6: Grim Extension Pack - Wc3C.net

    edit6:
    2 notes about version of latest available source code of ObjectMerger from author PitzerMike (see "link2" above):
    note 1:
    the compiled "ObjectMerger.exe" from source code needs "MpqCom.dll" in the same folder to run properly, otherwise ObjectMerger doesn't work & immediately terminates with an appcrash. mentioned "MpqCom.dll" can be found after compilation of ObjectMerger source code in folder "MpqCom". to explain the difference in file size between ObjectMerger.exe of compiled source code & channel ability hotfix, user WaterKnight gave me the idea, that PitzerMike could have compiled the DLL as static linked library into the EXE. after some tests, i could negate this theory. latest JNGP still contains this "MpqCom.dll", it just is a bit smaller & has another name! equivalent of "MpqCom.dll" is: "...\JNGP\grimext\grimex.dll" & 383 KB insize, while my compiled "MpqCom.dll" is 394 KB in size. to proof that, i just compiled ObjectMerger to use the "grimex.dll" instead of the "MpqCom.dll". i replaced "...\JNGP\grimext\ObjectMerger.exe" with my compiled one & removed the "MpqCom.dll" i formerly put in "...\JNGP\grimext\". result: ObjectMerger.exe didn't crash & worked successfully. everything i changed to source code was in "...\MpqCom\mpqcomapi.cpp" at line #77: mpqCom = LoadLibrary("MpqCom.dll"); to: mpqCom = LoadLibrary("grimex.dll");.
    note 2:
    source code of ObjectMerger from PitzerMike doesn't support LUA commands from triggers as it seems. simple JASS trigger commands like
    //! external ObjectMerger w3u hpea 1d1A unam "test"
    work fine, but ObjectMerger breaks for scripts inside:
    Code (vJASS):
    //! externalblock extension=lua ObjectMerger $FILENAME$
    ...
    //! endexternalblock

    the reason is, that a LUA file will be created by using an "externalblock" command in JASS. this LUA file can be found in a path like: "C:\Users\Source\AppData\Local\Temp\V706D.tmp.lua". i stumbled across this path, as the compiled ObjectMerger from source code will error report it in a message box as soon as preprocessor command "externalblock" is used in JASS in a trigger & the map is saved. message box example: "Could not open: C:\Users\Source\AppData\Local\Temp\V706D.tmp.lua". the LUA file gets created properly, because that's the job of "JassHelper", but the compiled ObjectMerger from source code isn't yet supporting LUA as this source code isn't the latest which current ObjectMerger.exe from JNGP uses. these LUA files contain all the code from the JASS code block "externalblock", without the leading
    //! i
    , which just tells JassHelper to save everything next to it into a LUA file.

    work of reference for inner workings:

    1 the entry point in the JassHelper source code (coding language = delphi) on map save:
    from my understanding, the actual source code file which runs "JassHelper" from WE is "grimoirejasshelper.dpr". the actual source code file which runs "JassHelper" from "clijasshelper.exe" should be "clijasshelper.dpr".

    2 the process where the LUA file gets prepared (parse JASS code):
    for WE, on map save, "JassHelper 0.A.2.B." starts in source code file "grimoirejasshelper.dpr" with function at line #326: function doWEWarlock(const m:string):boolean;. inside this function the if-block at line #569: if(not nopre) then begin is entered when "nopre" is "false".
    "nopre" is a variable of type boolean, declared inside "grimoirejasshelper.dpr" at line #26: debug:boolean=false;nopre:boolean=false;noopt:boolean=false;. "nopre" obviously stands for no preprocessor. "nopre" is initialized with value "false". if "JassHelper" runs without additional parameters, the value of "nopre" stays false, otherwise, if parameter "'--nopreprocessor" is added, value of "nopre" is changed to "true" at line #435: end else if(ParamStr(temi)='--nopreprocessor') then begin & line #436: nopre:=true;. note that "ParamStr(...)" is a native function in coding language pascal.​
    so, by default the if-block gets computed. the first line inside the if-block, line #570: jasshelper.DoJasserMagic('logs\currentmapscript.j',compiled,debug); calls a procedure from souce code file "jasshelper.pas". in "jasshelper.pas" this procedure is declared as a public procedure (in coding language pascal, declaring something as public is done at the file header in the interface section) at line #305: procedure DoJASSerMagic(f1:string; f2:string; debug:boolean);overload; & the actual procedure is declared at line #2.494: procedure DoJASSerMagic(f1:string; f2:string; debug:boolean);Overload;. inside this procedure the identically named function "DoJASSerMagic(...)" gets called at line #2.540: Write(ff2,DoJASSerMagic(inp,debug));. it's also made public at line #308: function DoJASSerMagic(sinput:string; debug:boolean):string; overload; & declared at line #2.616: function DoJASSerMagic(sinput:string; debug:boolean):string;overload;. here, also note, that identifiers are case-insensitive in coding language delphi/pascal! (DoJasserMagic = DoJASSerMagic) in this function "DoJasserMagic(...)" procedure "parseInput(...)" gets triggered at line #2.936: parseInput(debug);. procedure "parseInput(...)" is declared at line #1.447: procedure parseInput(debug:boolean);:
    - JASS code for lines with "externalblock" gets recognized in if-block at line #1.762: end else if(word='externalblock') then begin.
    - JASS code for everything between "externalblock" & "endexternalblock" gets recognized in if-block at line #1.790: end else if(word='i') and (ExternalBlockName <> '') then begin.
    - JASS code for lines with "endexternalblock" gets recognized in if-block at line #1.793: end else if(word='endexternalblock') then begin.
    so, everything between JASS lines with "externalblock" & "endexternalblock" is saved into a variable of type string called "ExternalBlockStdin" at line #1.791: SWriteLn(ExternalBlockStdin,Copy(input[k], wordend+1, Length(input[k]) ) );. "SWriteLn(...)" is no default function of coding language pascal, but a custom one written by the author to store every line inside the string variable "ExternalBlockStdin" (name of variable can be written out like: external block standard input). when JASS line with "endexternalblock" is reached, the concatenated string "ExternalBlockStdIn" gets passed & saved at line #1.794: Exter.add(ExternalBlockName, ExternalBlockArgs, ExternalBlockLn, ExternalBlockExtension, ExternalBlockStdIn);.

    3 the process where the LUA file is created (apply collected data):
    later in the JassHelper source code file "grimoirejasshelper.dpr" inside function at line #326: function doWEWarlock(const m:string):boolean; a procedure is called at line #699: doExternalThings;. "doExternalThings" is declared at line #272: procedure doExternalThings;.
    this procedure "doExternalThings" looks for entries in a variable called "Exter". "Exter" derives from JassHelper source code file "jasshelper.pas". in "jasshelper.pas" it's initialized at line #449: Exter: Texternalusage=nil;. "TexternalUsage" is a custom variable of type "class(TObject)" (TObject = Type Object => TexternalUsage = Type External Usage), defined at line #229: TexternalUsage=class(TObject) .... entries for this variable are added with the procedure at line #505: procedure Texternalusage.add(const na:string; const a:string; const i:integer; const ex:string; const si:string);. the last argument "si" (probably stands for string input or standard input) is for the already mentioned variable of type string named "ExternalBlockStdIn". in other words, each entry in "Exter" holds the whole JASS trigger code of lines between "externalblock" inside "Exter.stdin[N]". "Exter.N" returns the entry count.​
    inside "doExternalThings" each entry for "Exter" is passed to another procedure called "doTool" at line #292: doTool(EXTERNAL_Names[j],EXTERNAL_PROGRAMS[j],Exter.args, Exter.ext, Exter.stdin);. "doTool" is the actual procedure which creates the LUA files. "doTool" is declared at line #231: procedure doTool(const name:string; const prog:string; args:string; const ext:string; const stdin:string);. at line #238: tem:=TempFile; the variable "tem" of type string is set to a global function called "TempFile". "TempFile" comes from JassHelper source code file "vxFilesys.pas". "TempFile" is declared public at line #11: function TempFile: TFileName;. the actual function is declared at line #113: function TempFile: TFileName;. from what i understood, what it does is returning the absolute path to a new empty temporary file. at line #118: if GetTempFileName(PChar(GetTempDir),'V', 0, NomArchTemp) = 0 then "GetTempFileName" is a default function of coding language pascal with syntax: "function GetTempFileName(Dir: PChar;Prefix: PChar;uUnique: DWord;TempFileName: PChar):DWord;". "PChar" means Pointer to an array of Char. "GetTempDir" is a function declared at line #85: function GetTempDir: TFileName;. it returns the actual absolute path to a temporary file with function at line #90: SetString(Result, TmpDir, GetTempPath(MAX_PATH, TmpDir));. "GetTempPath" is a function from WinAPI. it is included in coding language pascal by using "uses" & adding "Windows". it retrieves the path of the directory designated for temporary files, like: "C:\Users\Source\AppData\Local\Temp\". back in JassHelper source code file "grimoirejasshelper.dpr" inside procedure "doTool" at line #242: Assign(ftem,tem); "Assign" assigns the name from "tem" to the file variable "ftem" of type Textfile. "Assign" is a default function of coding language pascal. at line #243: Rewrite(ftem); the file "ftem" gets opened for writing. "Rewrite" is a default function of coding language pascal. finally, at line #244: Write(ftem,stdin); the contents of the variable "stdin" are written to the file "ftem". also "Write" is a default function of coding language pascal. the LUA file is created.

    4 the argument passing to ObjectMerger.exe (of JASS "externalblock"/ LUA files):
    only a few lines later in the JassHelper source code file "grimoirejasshelper.dpr" inside procedure "doTool", following command sends appropriate arguments to ObjectMerger.exe in case of used JASS code "externalblock" at line #257: temi:= WinExec.StartApp(prog,'"'+map+'" "'+JasshelperConfigFile.WORK_PATHS+'" '+args,GetCurrentDir,0, tem4,tem2,tem3);.
    - "prog" is a variable of type string. "prog" contains the path to the application which should get executed. "prog" was passed from procedure "doExternalThings" from variable "EXTERNAL_PROGRAMS[j]" of type array of string from line #292: doTool(EXTERNAL_Names[j],EXTERNAL_PROGRAMS[j],Exter.args, Exter.ext, Exter.stdin);. "EXTERNAL_PROGRAMS[...]" comes from JassHelper source code file "jasshelperconfigfile.pas". what it does is reading the file "...\JNGP\jasshelper.conf" & fill "EXTERNAL_PROGRAMS[...]" with found application entries under section "[externaltools]" in that file.
    for example the line: "ObjectMerger","grimext\ObjectMerger.exe"
    is already explained at top: ...the syntax is "NAME","executable path"
    now in JassHelper source code file "jasshelperconfigfile.pas" the variable "a" of type string is set to the executable name, in this case: ObjectMerger & the variable "b" of type string is set to the executable path, in this case: grimext\ObjectMerger.exe. all this happens inside if-block at line #102: if(s_ext) then begin. "EXTERNAL_PROGRAMS[...]" is set to "b" at line #125: EXTERNAL_PROGRAMS[EXTERNAL_N]:=b;.
    - "map" is a variable of type string. "map" contains the path to the map currently saved from WE. "map" is set at line #469: map:=ParamStr(temi+2);.​
    "WinExec.StartApp" is declared in JassHelper source code file "winexec.pas" at line #15: function StartApp(AppName, ArgStr, workdir :String; Visibility : integer; input,output,error:string):integer;.
    example for a JASS "externalblock":
    WinExec.StartApp(
    grimext\ObjectMerger.exe,
    ''C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m" ".\jass\" "C:\Users\Source\AppData\Local\Temp\V758A.tmp.lua",
    C:\Spiele\JNGP2.0 2.0.0.7 (TriggerHappy) [2013_12_13]\vexorianjasshelper,
    0,
    C:\Users\Source\AppData\Local\Temp\V758D.tmp,
    C:\Users\Source\AppData\Local\Temp\V758B.tmp,
    C:\Users\Source\AppData\Local\Temp\V758C.tmp​
    );

    5 the entry point in the ObjectMerger source code (coding language = C/C++) on map save:
    when ObjectMerger.exe gets executed from JassHelper (see above), its entry point in source code is in file "...\ObjectMerger\main.cpp" (it has 332 lines of code) in function at line #247: int main(int argc, char **argv) {. first thing ObjectMerger source code does, is checking the number of passed arguments at line #257: if (argc < 4 ) { // check parameter count.
    "argc" is of type int & stands for "ARGgument Count". "argc" automatically returns the number of arguments passed to the program + 1. "+1" because the programs name always is the first argument. lets compare this with "argv". "argv" is of type char & stands for "ARGument Vector" (vector in meaning of array). "**argv" means pointer of pointer of char, you could also write "*char[]". the "*..." in cpp is called "indirection operator". "*expression" is the way to create a pointer to an expression. however, argv[0] contains the program's name like described for "argc" & argv[1...] contains the passed arguments.
    when you save a map with an "externalblock", JassHelper creates a LUA file & passes arguments to ObjectMerger.exe.
    example:
    argc=4
    argv[0]=grimext\ObjectMerger.exe
    this path comes from: "...\JNGP\jasshelper.conf" from section: "[externaltools]" from line with: ""ObjectMerger","grimext\ObjectMerger.exe"". JassHelper retrieves it from there & runs ObjectMerger by using this path.
    argv[1]=C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m
    this path is the path to the map currently saved. JassHelper detects it & runs ObjectMerger by passing this path as 1st argument.
    argv[2]=.\jass\
    this path comes from: "...\JNGP\jasshelper.conf" from section: "[lookupfolders]" from line with: "".\jass\"". JassHelper retrieves it from there & runs ObjectMerger by passing this path as 2nd argument.
    argv[3]=C:\Users\Source\AppData\Local\Temp\V758A.tmp.lua
    this path leads to the LUA file containing the "externalblock" code. JassHelper created it & runs ObjectMerger by passing this path as 3rd argument (explained above).
    so, although LUA isn't supported yet in the ObjectMerger source code (in the following we will proof this by stepping through the code), source code continues with code execution, because "argc" isn't < 4 (it is 4, see example)!​
    after this if-block there is a for-block at line #261: for (int i = 0; i < strlen(argv[2]); i++) // convert paths.
    here, "argv[2]" is checked for passed lookup paths from: "...\JNGP\jasshelper.conf" from section: "[lookupfolders]". found paths with slashes ("/") are replaced by paths with 2 backslashes ("\\").​
    after this for-block the next if-block is at line #271: if (strlen(argv[2])) {.
    in coding language C++ the if-condition is true, as long as the condition value is not 0 or null. in our case we have "strlen(argv[2])" as condition, which means that as long as "argv[2]" has at least 1 char, the condition is true & the if-block is entered. by default "argv[2]" contains ".\jass" as string, so the if-block is executed. the value of "argv[2]" derives from JassHelper source code from file "grimoirejasshelper.dpr" at line #257: temi:= WinExec.StartApp(prog,'"'+map+'" "'+JasshelperConfigFile.WORK_PATHS+'" '+args,GetCurrentDir,0, tem4,tem2,tem3);. "JasshelperConfigFile.WORK_PATHS" derives from file "jasshelperconfigfile.pas" where it is set at line #138: if (WORK_PATHS='') then WORK_PATHS:=a & line #139: else WORK_PATHS:=WORK_PATHS+';'+a;. the variable "a" is of type string & contains the paths which were found in file "...\JNGP\jasshelper.conf" from section: "[lookupfolders]".
    in this if-block the paths from "argv[2]" are saved to variable "paths" of type "LookupTable". "LookupTable" is a custom type defined at line #12: typedef set< string > LookupTable;. "LookupTable" is a set for elements of type string. the operator "<" stands for the sorting criterion, it makes sorting the elements in ascending order. for descending order you could do something like: typedef set< string, greater<string> > LookupTable;.​
    right after this if-block there comes another if-block at line #277: if (strcmpi(argv[3], "w3u") == 0 || strcmpi(argv[3], "w3t") == 0 || strcmpi(argv[3], "w3b") == 0 || strcmpi(argv[3], "w3d") == 0 || strcmpi(argv[3], "w3a") == 0 || strcmpi(argv[3], "w3h") == 0 || strcmpi(argv[3], "w3q") == 0) {.
    "strcmpi" is a default function of coding language C++. "strcmpi" compares 2 strings without sensitivity to case. "strcmpi" returns 0 when both strings are equal.
    here, ObjectMerger checks 3rd passed argument "argv[3]" for objecttype w3u, w3t, w3b, w3d, w3a, w3h & w3q. this would be the case for example for JASS preprocessor code
    //! external ObjectMerger w3q Remg Rgl2 glvl 2 gar1 2 "ReplaceableTextures\CommandButtons\BTNImpale.blp"
    (example taken from "...\JNGP\grimext\GrimexManual.html"). in our case, where "argv[3]" contains the path to a LUA file, the if-block isn't entered.​
    so, next comes the else-block at line #295: } else {.
    a new if-block checks whether "argv[3]" contains the merge mode ("m", "r" or "i") at next line #296: if (strlen(argv[3]) == 1) {.
    in our case "argv[3]" contains the path to the LUA file, hence this if-block isn't entered.​
    after that if-block there comes a for-block at line #305: for (int i = start; i < argc; i++) { // find files.
    as far as i understood this for-block checks each entry in "argv" for file existance. if the file couldn't be found, a subroutine checks file existance for incomplete paths. if the file still wasn't found an error window is returned & ObjectMerger is quit.​
    the last important command under the else-block & in this script is at line #326: if (!dofiles(argc, argv, mode, start)).
    "dofiles" is a function declared at line #74: bool dofiles(int argc, char *argv[], char mode, int start) {. passed variable "start" of type int contains value 3 in our case of LUA file.​
    "dofiles" loads a custom variable "OBJ" at line #79: OBJ temp(firstfile[strlen(firstfile) - 1]);.
    variable "firstfile" is a pointer to a char, it is declared at line #77: char* firstfile = argv[start];. in our case "firstfile" points to "argv[3]", the path to the LUA file. so, the content of the brackets of "OBJ temp(...)" points to the last char from "argv[3]". in cases with no LUA, but preprocessor code like:
    //! external ObjectMerger Objects\war3map.w3u
    (example taken from "...\JNGP\grimext\GrimexManual.html") it would be: "OBJ temp(u)". it is designed to receive the objecttype (like already mentioned a few lines above: w3u, w3t, w3b, w3d, w3a, w3h or w3q) which would be u, t, b, d, a, h or q for the bracket's content. in our case of a LUA file it receives "a". the custom variable "OBJ" is of type class & derives from source code file "...\common\obj.h".​
    the last step before the error for LUA files pops up an if-block is entered at line #90: if(!temp.load(argv[i], wts)) {.
    "wts" is another custom variable like "OBJ". "wts" is of type class & derives from "...\common\wts.h". "wts" is used at line #82: if (!loadpresentfiles(mpq, argv[1], wts, obj, OnlyExtension(argv[start]))) {.
    - argument 1 "mpq" stands for the map archive curretnly being saved, for example archive to "C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m".
    - argument 2 "argv[1]" contains the path to the map currently saved, like: "C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m".
    - argument 3 "wts" is null.
    - argument 4 "obj" is null.
    - argument 5 "OnlyExtension(argv[start])" in our case is "OnlyExtension(argv[3])" which can be "OnlyExtension("C:\Users\Source\AppData\Local\Temp\V758A.tmp.lua")" which would be "lua".
    in sum it could look like this: "if (!loadpresentfiles(mpq, "C:\Spiele\Warcraft III\Maps\_SourceSeeker\test_objectMergerW3u.w3m", null, null, "lua")) {". what it does there is passing the map's "war3map.wts" file to "wts" & "OBJ".
    back to if-block at line #90, "temp.load(...)" is a function in source code file "...\common\obj.cpp". the passed argument "argv[i]" from "temp.load(argv[i], wts)" contains the LUA file path. "temp.load(argv[i], wts)" is defined at line #638: bool OBJ::load(const char* filename, WTS &wts, bool inlinewts) {. there it opens the LUA file as an "ifstream" (= Input File STREAM) & passes the stream & the wts to another function at line #644: bool result = load(infile, wts, inlinewts);. "load(infile, wts, inlinewts)" is defined at line #614: bool OBJ::load(istream &stream, WTS &wts, bool inlinewts) {.​
    finally, the last check before the error report happens: first the "version" variable of the passed "stream" gets written at line #616: stream.read((char*)&version, sizeof(version));. variable "version" is of type unsigned int & defined in source code file "...\common\obj.h" at line #68: unsigned int version;. "sizeof(...)" is a default function of coding language C++ & it returns the size in bytes of the object representation of type. "sizeof(version)" is like "sizeof(unsigned int)" & this would be the value "4". so, "stream.read((char*)&version, sizeof(version));" can be read like "stream.read((char*)&version, 4);". now this can be read like: extract 4 characters from the "stream" & store them in the array pointed to by "version". and the last line, right before the error window will be showed, is at line #617: if (version != 1 && version != 2) {. because the first 4 bytes in the header of the LUA file aren't representing value 1 or 2 like warcraft objecttype files (*.w3u, *.w3t, etc.) have, the if-block returns "false" at line #618: return false;. just for reference, the first 4 bytes in the header of the LUA file returns the value 1668183398.
    back to source code file "...\ObjectMerger\main.cpp" the error message is reported at line #91: fprintf(stderr, "Could not open %s\n", argv[i]);.



    what arguments does ObjectMerger from source code accept?
    - either a path to an objecttype file (or before the path a merge mode like "m", "r" or "i") or objecttype.
    so when the path to a LUA file is passed as argument to compiled ObjectMerger.exe (that happens automatically when "externalblock" is used in JASS & JassHelper is activated in JNGP when map is saved), ObjectMerger exits with error popup, that it couldn't open the LUA file. the reason is, that ObjectMerger expected a file with a header with first 4 bytes being 1 or 2 like warcraft objecttype files (*.w3u, *.w3t, etc.) have.

    what is "preprocessor" code?
    - JASS code starting with
    //!
    . it calls external apps like ObjectMerger.exe & passes appropriate arguments. it must not be interpreted for the actual warcraft map, hence it gets removed again from the final trigger script which is saved into the map file (map file = file with extension .w3m or .w3x).

    so, ObjectMerger from source code misses LUA support, what would be the fix for supported preprocessor code like
    //! external ObjectMerger w3u hpea 1d1A uaen 0
    ?

    - in this case in source code file "...\ObjectMerger\main.cpp" the main function at line #247: int main(int argc, char **argv) { runs function "docreate" at line #293: if (!docreate(argc, argv, increment)). "docreate" is defined at line #177: bool docreate(int argc, char *argv[], int increment) { & runs function "makecopy" at line #210: if (!obj.makecopy(slk, War3Id(argv[4]), War3Id(argv[5]), increment, argc, argv)) {. function "makecopy" can be found in source code file "...\common\obj.cpp" at line 389: bool OBJ::makecopy(SLK &metadata, War3Id templateid, War3Id newid, int increment, int argc, char *argv[]) {. in this function "makecopy" another function gets executed at line #441: unsigned int vartype = getvartype(metadata.getValue(argv[i], "type").getString());. function "getvartype" is defined at line #379: unsigned int OBJ::getvartype(string name) {. it looks for "int", "bool", "real", "unreal", "string", but misses a definition for type "attackBits". "attackBits" derives from "...\Warcraft III\War3Patch.mpq", "Units\\UnitMetaData.slk". in SLK row 264 in column 1 ("ID") there should be the object editor field id "uaen". now look to the right until column 8 ("type"), there should be the "attackBits". so if you want to fix this, you just could change line #380: if (name == "int" || name == "bool") to: if (name == "int" || name == "bool" || name == "attackBits") & it should work. i didn't test it, because you still lack the feature for LUA support, hence i won't make any changes to this latest available source code of ObjectMerger, because it misses big parts of code of current ObjectMerger.exe.

    now that i know all this, i will try to write my own tool to fix ObjectMerger problems by adding it to: ""...\JNGP\jasshelper.conf" under section: "[externaltools]".

    edit7:
    finally, i was able to publish such a tool, here's the link:
    ObjectMergerFixer: fixing some known problems with ObjectMerger
     
    Last edited: Oct 23, 2016
  8. Barade

    Barade

    Joined:
    Feb 2, 2006
    Messages:
    544
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Hey, if you still want to write your own ObjectMerger. I got the problem that Umlaute like ä or ß in the beginning of ability names were cut off by the current Object Merger.

    My project wc3lib has support for reading and writing object data and getting default fields etc. There's also an alpha GUI object editor. Since I am interested myself in writing such a tool (to fix the other bug as well) just write me if you're still working on this.
     
  9. WaterKnight

    WaterKnight

    Joined:
    Aug 18, 2009
    Messages:
    4,018
    Resources:
    5
    Maps:
    1
    Tutorials:
    4
    Resources:
    5
    The functionality of the ObjectMerger is rather straightforward, I too have (java) libs to process the .w3u, .wts, slks, etc. But what do you want the input to look like? Equal to PitzerMike's ObjectMerger?