Jump to content


Photo

Code sequence


  • Please log in to reply
16 replies to this topic

#1 Scar

Scar
  • Member
  • 22 posts

Posted 21 March 2004 - 08:44 AM

Well, I've read some side comments on other topics that lead me to believe that WeiDU does not necessarily execute code in the order it was written. I'd like to evaluate what can be done safely. Let's start with a bit of example code (FYI: it corrects the Wizard saving throw table, which was supposed to get a final boost at level 21):

COPY_EXISTING ~SAVEWIZ.2DA~ ~override/SAVEWIZ.2DA~
    REPLACE_TEXTUALLY ~2DA V1.0~ ~2DA_V1.0~
    REPLACE_TEXTUALLY ~ 1 ~ ~dummy 1 ~
    SET "col" = 40
    WHILE ("%col%" > 20) BEGIN
        SET_2DA_ENTRY 1 "%col%" 41 ~8~
        SET_2DA_ENTRY 2 "%col%" 41 ~3~
        SET_2DA_ENTRY 3 "%col%" 41 ~5~
        SET_2DA_ENTRY 4 "%col%" 41 ~7~
        SET_2DA_ENTRY 5 "%col%" 41 ~4~
        SET "col" = ("%col%" - 1)
    END
    REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~
    REPLACE_TEXTUALLY ~dummy~ ~     ~
The WHILE loop does the real work, and it's framed with a couple of R_T to protect the header formating. The whole snippet did alright for me, but when I sent it to a friend, he complained that the R_T part didn't work and the header formating was screwed up (he's at v153, while I'm still at v148 (being a Mac head, I didn't dare to compile a new version yet)). Any comments/suggestions?

Then there's these two snippets. Both yield the same result (changing the color of the Dragon Helm to something easier on the eyes) and work alright for me:

COPY_EXISTING ~helm21.itm~ ~override/helm21.itm~
    READ_SHORT 0x6A "fxoffset"
    READ_SHORT 0x70 "#fx"
    WHILE ("%#fx%" > 0) BEGIN
        READ_SHORT ("%fxoffset%" + ("%#fx%" - 1) * 0x30) "fxtype"
        READ_LONG ("%fxoffset%" + ("%#fx%" - 1) * 0x30 + 0x8) "fxpara2"
        SET "patch" = 1
        WHILE ("%fxtype%" = 7)
          AND (("%fxpara2%" = 53) OR ("%fxpara2%" = 48))
          AND "%patch%" BEGIN
            WRITE_LONG ("%fxoffset%" + ("%#fx%" - 1) * 0x30 + 0x4) 0x65
            SET "patch" = 0
        END
        SET "#fx" = ("%#fx%" - 1)
    END
The first one, above, closely follows Idobek's wonderful tutorial. Yet, there's some redundancy in there, namely four "%#fx%" - 1. So, I slightly changed it, into this:

COPY_EXISTING ~helm21.itm~ ~override/helm21.itm~
    READ_SHORT 0x6A "fxoffset"
    READ_SHORT 0x70 "#fx"
    WHILE ("%#fx%" > 0) BEGIN
        SET "patch" = 1
        SET "#fx" = ("%#fx%" - 1)
        READ_SHORT ("%fxoffset%" + "%#fx%" * 0x30) "fxtype"
        READ_LONG ("%fxoffset%" + "%#fx%" * 0x30 + 0x8) "fxpara2"
        WHILE ("%fxtype%" = 7)
          AND (("%fxpara2%" = 53) OR ("%fxpara2%" = 48))
          AND "%patch%" BEGIN
            WRITE_LONG ("%fxoffset%" + "%#fx%" * 0x30 + 0x4) 0x65
            SET "patch" = 0
        END
    END
Here I've moved the SET "#fx" = ("%#fx%" - 1) right to the beginning of the loop, so that I need to do the calculation only once. It seems to be working for me, yet the question remains: how safe is this? Can I be certain that the SET is always executed before the subsequent READ commands? Any comments/suggestions?

Cheers, Armin

#2 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 21 March 2004 - 09:57 AM

I've never thought of using variable in SET_2DA_ENTRY, don't know why. Good idea there.
The reason it may not be working for your friend is that ~ 1 ~ doesn't exist in his file (possibly because S_2_E has been used on the file before). I suggest swapping the REPLACE_TEXTUALLY ~ 1 ~ ~dummy 1~ with another SET_2DA_ENTRY and then again at the end, and do the R_T for the "2DA_V1.0" last.

WeiDU does execute commands in order. You can do this: SET "#fx" = ("%#fx%" - 1) at any time during the WHILE loop but I think it is good practice to place it at the end. Essentially your WHILE loops are exactly the same. Those READs you are concerned about won't be executed in either of them, because it is the "#fx" set in the WHILE loop rum before this one that is used to determine wether the current one is run. I hope that made sense.

Also you need to look at way you are using the "patch" variable - you are not using it at the moment. I think you want AND ("%patch%" = 1)

#3 Scar

Scar
  • Member
  • 22 posts

Posted 21 March 2004 - 04:19 PM

I've never thought of using variable in SET_2DA_ENTRY, don't know why. Good idea there.

Thanks. It just seemed logical. If WeiDU could convert INTs to STRINGs, it could even be applied for the value you want to write.

I suggest swapping the REPLACE_TEXTUALLY ~ 1 ~ ~dummy 1~ with another SET_2DA_ENTRY and then again at the end, and do the R_T for the "2DA_V1.0" last.

I'll give it a shot. Already changed it locally, works like a charm. Let's see how it works out for my friend...

WeiDU does execute commands in order. You can do this: SET "#fx" = ("%#fx%" - 1) at any time during the WHILE loop but I think it is good practice to place it at the end. Essentially your WHILE loops are exactly the same. Those READs you are concerned about won't be executed in either of them, because it is the "#fx" set in the WHILE loop rum before this one that is used to determine wether the current one is run. I hope that made sense.

It depends what you want to accomplish with the variable. Sometimes it makes sense to modify it at the beginning, but, talking readability, I agree with you. Yet, back to the commands in question:

READ_SHORT ("%fxoffset%" + ("%#fx%" - 1) * 0x30) "fxtype"
READ_LONG ("%fxoffset%" + ("%#fx%" - 1) * 0x30 + 0x8) "fxpara2"
Which were replaced with these:

SET "#fx" = ("%#fx%" - 1)
READ_SHORT ("%fxoffset%" + "%#fx%" * 0x30) "fxtype"
READ_LONG ("%fxoffset%" + "%#fx%" * 0x30 + 0x8) "fxpara2"
They are on the same level within the outer loop, so if the READ* would be executed before the SET, it would go rampant. BTW: the statement that made me thinking is from Wes himself. The circumstances may be different, but the commands applied are the same:

IF_EVIL can only (currently) use variables that are set by READ_BYTE and READ_SHORT and whatnot. The "SET" isn't actually evaluated before the IF_EVAL is checked, which is why "read" isn't showing up.


Also you need to look at way you are using the "patch" variable - you are not using it at the moment. I think you want AND ("%patch%" = 1)

Oh, I am using it; it works perfectly, otherwise I'd had an infinite loop, which I'd have noticed ^_^. ("%patch%" = 1) is redundant. As stated in the ReadMe, WHILE checks if a given value is non-zero. "%patch%" is 1, or TRUE in other words. It is set to 0, or FALSE, within the inner loop, causing the loop to exit next time.

Cheers, Armin

#4 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 21 March 2004 - 04:54 PM

*chuckle* I knew I should have read those WHILE loops more closely before commenting. You are, of course, right in all your points. I missed what you were doing with the "patch" variable completely.

If you are going to do this
SET "#fx" = ("%#fx%" - 1)
READ_SHORT ("%fxoffset%" + "%#fx%" * 0x30) "fxtype"
You may as well do this:
SET "variable" = ("%fxoffset%" + ("%#fx%" - 1) * 0x30)
READ_SHORT "%variable%" "fxtype"
You are doing it to save typing so you may as well save as much as possible. And you have your closing SET at the end so you don't lose it. :)

IF_EVALs are IF_EVALs, WHILEs are WHILEs and variables can be awkward. An IF_EVAL will completely ignore a WHILE loop, READs for IF_EVALs should be the first thing you do. In general READs done inside a WHILE loop will be ignored outside that loop you need to use a SET for written variables to be used elsewhere.

#5 -Guest-

-Guest-
  • Guest

Posted 21 March 2004 - 06:34 PM

Well, I've read some side comments on other topics that lead me to believe that WeiDU does not necessarily execute code in the order it was written. I'd like to evaluate what can be done safely. Let's start with a bit of example code (FYI: it corrects the Wizard saving throw table, which was supposed to get a final boost at level 21):

The WHILE loop does the real work, and it's framed with a couple of R_T to protect the header formating. The whole snippet did alright for me, but when I sent it to a friend, he complained that the R_T part didn't work and the header formating was screwed up (he's at v153, while I'm still at v148 (being a Mac head, I didn't dare to compile a new version yet)). Any comments/suggestions?

Then there's these two snippets. Both yield the same result (changing the color of the Dragon Helm to something easier on the eyes) and work alright for me:

The first one, above, closely follows Idobek's wonderful tutorial. Yet, there's some redundancy in there, namely four "%#fx%" - 1. So, I slightly changed it, into this:

Here I've moved the SET "#fx" = ("%#fx%" - 1) right to the beginning of the loop, so that I need to do the calculation only once. It seems to be working for me, yet the question remains: how safe is this? Can I be certain that the SET is always executed before the subsequent READ commands? Any comments/suggestions?

Cheers, Armin

Apologies I never followed up on this. It's good to see you here.

The problem with SET_2DA_ENTRY is that every execution will re-format the entire table (the columns are all made equal to the widest entry in the array). So if you try to do both the 2DA V1.0 and first column header with SET_2DA_ENTRY, only one will work (as the last action will mess with any previous formatting actions). The following works flawlessly:

COPY_EXISTING ~SAVEWIZ.2DA~ ~override~
  SET_2DA_ENTRY 0 0 2 ~2DA_V1.0~
  SET_2DA_ENTRY 0 1 2 ~~
  SET_2DA_ENTRY 0 0 40 ~2DA 1~
  SET ~column~ = 21
  WHILE (~%column%~ < 41)
  BEGIN
    SET_2DA_ENTRY 1 ~%column%~ 41 ~8 ~
    SET_2DA_ENTRY 2 ~%column%~ 41 ~3~
    SET_2DA_ENTRY 3 ~%column%~ 41 ~5~
    SET_2DA_ENTRY 4 ~%column%~ 41 ~7~
    SET_2DA_ENTRY 5 ~%column%~ 41 ~4~
    SET ~column~ = ~%column%~ + 1
  END
  SET_2DA_ENTRY 0 0 41 ~~
  REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~

The perfect solution would be if WeiDU didn't treat the 2DA header and magic number as valid entries when it goes to modify the file. Since it does, we can only use SET_2DA_ENTRY once for formatting (in the example above, I delete the V1.0 "column," essentially making the 2DA header one column). Additionally, WeiDU will treat any text-whitespace-text as two columns, and the auto-resizing of the columns makes it unnecessary to worry about width (meaning that all actions can be done by changing the first column to "2DA column1Header," or even "1 column1Header"). When using SET_2DA_ENTRY to remove the header, WeiDU will do a final re-formatting, ensuring that all the headers are in the right position.

The problem I was experiencing previously was most likely because I didn't take into account the fact that each SET_2DA_ENTRY action re-formats the table. I also ran into a strange problem when I tried to separate the formatting commands (done with REPLACE_TEXTUALLY) from the rest of the action block (I put them all at the end of the component). It almost seemed as if WeiDU was doing the REPLACE_TEXTUALLY actions before the blocks with SET_2DA_ENTRY (even though they were the last actions in the component). All of the formatting was incorrect (the column 1 header would actually appear at column 2 with whitespace for columns 0 and 1). I never bothered to fully research why this was happening (the code above works perfectly, so I just went with it).

I never tried your formatting actions, but I suspect they would have worked perfectly here (just a style decision- I tried to find a different way because I didn't like the idea of placing "dummy" text just to delete it later).

On the subject of variables (I had never though of using a loop for these either, the original action list was quite lengthy before Scar presented me with this solution), note that SET_2DA_ENTRY apparently performs variable substitution for the row, column and required column count parameters, but they cannot be used for the actual value (so SET_2DA_ENTRY 0 0 1 ~%variable%~ wouldn't work, it just sets the 2DA entry to %variable%). Additionally, unless it's keyed to ignore other files, this can potentially be used for any tabular file (IDS files come to mind). Also, SET_2DA_ENTRY can be used to add columns. Fixing XPLIST.2DA previously required you to REPLACE_TEXTUALLY the entire line, but now it can be done as such:

COPY_EXISTING ~XPLIST.2DA~ ~override~
  SET_2DA_ENTRY 0 0 2 ~2DA_V1.0~
  SET_2DA_ENTRY 0 1 2 ~~
  SET_2DA_ENTRY 0 0 41 ~2DA COMMENT~
  SET_2DA_ENTRY 67 38 39 ~-1 -1 -1 -1~
  SET_2DA_ENTRY 0 0 42 ~~
  REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~

Since WeiDU will reformat the table with the last SET_2DA_ENTRY action, no concern needs to be given to the spacing (any whitespace is treated as the start of the new colum for that row). This is, IMO, much cleaner than the previous method.

On the subject of variables, I've found that the READ actions are always executed first, regardless of their position in the block. For instance, in the following:

COPY_EXISTING ~nothing~ ~override~
  READ_LONG 0x00 ~someOffset~
  WRITE_LONG 0x00 ~%someOffset%~ + 0x14
  DO_SOMETHING
  READ_LONG 0x00 ~newOffset~

the value of newOffset will be identical to someOffset, even though you have a WRITE_LONG action that should change the value before the range is re-read. This made it tough for me to update things like spell indices, as the method I was using required WeiDU to read the updated bytes. The solution here was to move these particular actions to a separate block, so that the correct values will be read. In most cases, you won't want to re-read a byte more than once- you'll instead want to use SET to either replace the old variable or create a new one (so, if you changed the offset in the example above, you'd want to do the following):

COPY_EXISTING ~something~ ~override~
  READ_LONG 0x00 ~someOffset~
  WRITE_LONG 0x00 ~%someOffset%~ + 0x14
  SET ~someOffset~ = ~%someOffset%~ + 0x14

This will insure that the value of someOffset is correct when used again in the block. As far as I can tell, SET is performed sequentially with the rest of the actions (wherever it appears in the file). So you should never have to worry about the value if you explicitly use SET, as long as it occurs before the action that requires the variable to have the new value.

#6 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 22 March 2004 - 03:19 AM

On the subject of variables, I've found that the READ actions are always executed first, regardless of their position in the block. For instance, in the following:

COPY_EXISTING ~nothing~ ~override~
 READ_LONG 0x00 ~someOffset~
 WRITE_LONG 0x00 ~%someOffset%~ + 0x14
 DO_SOMETHING
 READ_LONG 0x00 ~newOffset~
the value of newOffset will be identical to someOffset, even though you have a WRITE_LONG action that should change the value before the range is re-read.


I agree with this, however, you can use a READ to close a WHILE loop. I haven't tried a READ, WRITE, READ sequence but WRITE, READ works so the READs are not necessarily done first, if they were I'd have a idefinite loop. This could possibly (probably) be secific to a WHILE loop. The READ will not be valid outside that loop, you need to use SET here.

#7 -Guest-

-Guest-
  • Guest

Posted 22 March 2004 - 01:37 PM

I agree with this, however, you can use a READ to close a WHILE loop. I haven't tried a READ, WRITE, READ sequence but WRITE, READ works so the READs are not necessarily done first, if they were I'd have a idefinite loop. This could possibly (probably) be secific to a WHILE loop. The READ will not be valid outside that loop, you need to use SET here.

Out of necessity, a WHILE loop has to be completely re-evaluated on each pass (it should be identical to having n number of separate COPY_EXISTING blocks). So the read will be performed after all actions in the loop have been executed for each run through the loop. I have little doubt that multiple reads in the same run (e.g., a loop containing code similar to that posted above) would fail, unless the actions in a WHILE loop are explicitly executed sequentially. I don't think I would ever depend on a READ to close a loop, simply because it would make it harder to determine exactly what's happening at a glance (not to mention that it would increase the possibility of human error).

SET_2DA_ENTRY does work on IDS files, so it should work on any text file at all (the only caveat here is that WeiDU will re-format the file, adding extra whitespace between columns, or after the entries if there's only one). A possible alternative to the traditional REPLACE_TEXTUALLY (you don't need to know the existing entry, and you don't have to worry about differences in whitespace).

#8 Scar

Scar
  • Member
  • 22 posts

Posted 24 March 2004 - 11:18 AM

I missed what you were doing with the "patch" variable completely.

FYI: I learned that trick years ago, programming VisualBasic in school. It even had a specialized class of variables called boolean - one bit flags that could only hold TRUE or FALSE.

You are doing it to save typing so you may as well save as much as possible. And you have your closing SET at the end so you don't lose it. :)

Now, that is a good one... why haven't I seen it myself :huh:. On goes my quest to reduce unnecessary redundancies. BTW: it's not only typing but also less work to do for WeiDU - instead of 4 calculations it's only one (and if you're approaching critical mass, every little helps, just ask our Guest here ;)).

IF_EVALs are IF_EVALs, WHILEs are WHILEs and variables can be awkward. An IF_EVAL will completely ignore a WHILE loop, READs for IF_EVALs should be the first thing you do. In general READs done inside a WHILE loop will be ignored outside that loop you need to use a SET for written variables to be used elsewhere.

See, that's the kind of information I'm looking for: "DOs And DON'Ts Of WeiDU-Coding". Anyone of the cracks here interested in writing such a tutorial?

Apologies I never followed up on this. It's good to see you here.

Well, since my way worked for me, it wasn't on the top of my list, either. Yet, it was a good example on topic. BTW: good to see you, too, and that I connected the right "face" to your posts.

The problem with SET_2DA_ENTRY is that every execution will re-format the entire table (the columns are all made equal to the widest entry in the array). So if you try to do both the 2DA V1.0 and first column header with SET_2DA_ENTRY, only one will work (as the last action will mess with any previous formatting actions).

That's exactly the point. To restore the header you need at least one R_T, since if you'd use two S_2_Es, the second one would mess up the first's work. I think I'm getting the hang of this...

I also ran into a strange problem when I tried to separate the formatting commands (done with REPLACE_TEXTUALLY) from the rest of the action block (I put them all at the end of the component). It almost seemed as if WeiDU was doing the REPLACE_TEXTUALLY actions before the blocks with SET_2DA_ENTRY (even though they were the last actions in the component). All of the formatting was incorrect (the column 1 header would actually appear at column 2 with whitespace for columns 0 and 1).

Ah, now I got it. You changed the sequence of my code, complaint that it wasn't working for you and left me wondering... tsk tsk. That's not very nice, you know ;).

I never tried your formatting actions, but I suspect they would have worked perfectly here (just a style decision- I tried to find a different way because I didn't like the idea of placing "dummy" text just to delete it later).

Yet you're doing pretty much the same, just you're using S_2_E for it and place a "2DA" instead of the "dummy". I changed my code to work with S_2_E, as well. It looks like this:

COPY_EXISTING ~SAVEWIZ.2DA~ ~override/SAVEWIZ.2DA~
    REPLACE_TEXTUALLY ~2DA V1.0~ ~2DA_V1.0~                             // Protect header
    SET_2DA_ENTRY 0 0 40 ~dummy 1~
    SET "col" = 40
    WHILE ("%col%" > 20) BEGIN
        SET_2DA_ENTRY 1 "%col%" 41 ~8~                                  // Death
        SET_2DA_ENTRY 2 "%col%" 41 ~3~                                  // Wands
        SET_2DA_ENTRY 3 "%col%" 41 ~5~                                  // Polymorph
        SET_2DA_ENTRY 4 "%col%" 41 ~7~                                  // Breath
        SET_2DA_ENTRY 5 "%col%" 41 ~4~                                  // Spells
        SET "col" = ("%col%" - 1)
    END
    SET_2DA_ENTRY 0 0 41 ~~                                             // Restore header
    REPLACE_TEXTUALLY ~2DA_V1.0~ ~2DA V1.0~
I kept the R_T for the first line (as opposed to your two S_2_Es) since it is quite unlikely to vary... wait, strike that... if somebody else's mod already messed with it, it does vary! OK, I'm sold ;).

Also, SET_2DA_ENTRY can be used to add columns.

Well, that depends on the complexity. Sometimes you're better off using APPEND_COL:
APPEND_COL  ~mxsplbrd.2da~   // Add column: 7th level spells
             ~x x 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 4 4 4 5 5 5 5 5 5 6 6 6 6 6 6 6~
And remove the dummy "x" in a second step (one R_T ~x~ ~~ is enough).


SET_2DA_ENTRY does work on IDS files, so it should work on any text file at all (the only caveat here is that WeiDU will re-format the file, adding extra whitespace between columns, or after the entries if there's only one). A possible alternative to the traditional REPLACE_TEXTUALLY (you don't need to know the existing entry, and you don't have to worry about differences in whitespace).

So it might even be used on the infamous music files (tho in that case formatting would be a major pain).

Cheers Armin

PS: kudos to anyone who beared with me through this overlong construct of quotes, codes and comments :D

#9 -Guest-

-Guest-
  • Guest

Posted 24 March 2004 - 01:37 PM

Now, that is a good one... why haven't I seen it myself :huh:. On goes my quest to reduce unnecessary redundancies. BTW: it's not only typing but also less work to do for WeiDU - instead of 4 calculations it's only one (and if you're approaching critical mass, every little helps, just ask our Guest here ;)).

I'm actually guily of the opposite. Comparison operations should have no performance overhead, and I've found that doing a lot of simple integer calculations doesn't provide a noticeable impact. In order to reduce the memory overhead, I just write it out directly instead of creating new variables (once you write a near-universal set of actions, it's mostly just copy/paste).

Well, since my way worked for me, it wasn't on the top of my list, either. Yet, it was a good example on topic. BTW: good to see you, too, and that I connected the right "face" to your posts.

Yeah; I'd be everyone's "Guest" if they let me get away with it. Unfortunately, it's all about registration these days...

That's exactly the point. To restore the header you need at least one R_T, since if you'd use two S_2_Es, the second one would mess up the first's work. I think I'm getting the hang of this...

I had to mess with it for awhile before I finally understood exactly how it works. Now, however, I can use it for just about any textual operation (of course, I still only use it for 2DA files).

Ah, now I got it. You changed the sequence of my code, complaint that it wasn't working for you and left me wondering... tsk tsk. That's not very nice, you know ;).

Sorry. I should have been more specific about exactly what was failing. I feel compelled to not only have the result look pretty, but also the Tp2 code that achieves said result. So, when the comments for the code that messes with the 2DA files starts looking funky, they all have to go (after the header comments went, it wasn't such a difficult choice to just remove all the comments). And when I start having to add temporary data to files just to work around undesirable behavior, I end up looking for the "better way" (which just ends up breaking everything, of course).

I kept the R_T for the first line (as opposed to your two S_2_Es) since it is quite unlikely to vary... wait, strike that... if somebody else's mod already messed with it, it does vary! OK, I'm sold ;).

Just remember to delete the V1.0 "column" (otherwise, you'd end up with 2DA V1.0 V1.0). The beauty of SET_2DA_ENTRY is that it always works, regardless of whether there's 1 space or 10,000 spaces between "columns."

Well, that depends on the complexity. Sometimes you're better off using APPEND_COL:

The assumption here is that you want to add an entirely new column (although you can use SET_2DA_ENTRY to fix up any extraneous entries).

So it might even be used on the infamous music files (tho in that case formatting would be a major pain).

It would actually work perfectly here. I haven't found any textual data file that requires specific whitespace (as long as there's a single whitespace between the elements, the game engine doesn't care). For MUS files, I just prefer to keep the spacing consistent (as per the IESDP description), simply because that's the format BioWare (or Black Isle in this case, most likely) chose to use (in all but one file).

I've taken to simply copying the IDS files (since I end up sorting them lexicographically by the ID name with BBEdit). Having WeiDU insert hundreds of spaces between the ID and the name wasn't particularly impressive, as well. So, although you can use SET_2DA_ENTRY for just about anything, I haven't found it practical to do so.

Hopefully, some future version of SET_2DA_ENTRY will ignore the 2DA headers and only resize a column if the replacement entry is longer than the current column width (and then only for that column). I haven't found anybody who's impressed with current behavior (it seems to me too much time is spent on fixing formatting issues- unfortunate, since it's far superior to REPLACE_TEXTUALLY when working with tabular data).

#10 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 24 March 2004 - 02:33 PM

See, that's the kind of information I'm looking for: "DOs And DON'Ts Of WeiDU-Coding". Anyone of the cracks here interested in writing such a tutorial?

This kind of tutorial is difficult to write. Mainly because people like to ask 'Why?' and I can only answer 'Because.' Tutorials for specific commands are a lot easier, most of these have now been covered.

I must send Wes an updated SET_2DA_ENTRY tutorial. Do you guys have any suggestions for the WHILE loop tutorial?

#11 weimer

weimer
  • Member
  • 1569 posts

Posted 24 March 2004 - 04:43 PM

WeiDU does execute commands in order.


Kinda. It's worse for D files, where there are actually three separate passes in order to get "exactly the evaluation order that Jason wants". In TP2 files, patching is the only tricky ordering bit. As some people pointed out, there are two phases there.

First, all top-level (i.e., not included inside a WHILE) READ_ commands are executed. This is a hack to support legacy IF_EVALs, which I don't recommend that anyone use. v154 (out soon) adds "BUT_ONLY_IF_IT_CHANGES" (which is mostly me picking on the hideous WeiDU syntax) to make it even easier to avoid IF_EVAL.

Then, all patch commands are executed (read commands will be executed again).

If WeiDU could convert INTs to STRINGs, it could even be applied for the value you want to write.


SET_2DA_ENTRY will now take an arbitrary patch_exp as the final argument, converting its int value to a string. If it doesn't evaluate (e.g., it is some arbitrary string that is not a variable), its text is used instead.

The problem with SET_2DA_ENTRY is that every execution will re-format the entire table


This is not a "problem" because the whitespace doesn't matter to WeiDU and it doesn't matter to the infinity engine.

The perfect solution would be if WeiDU didn't treat the 2DA header and magic number as valid entries


Unfortunately, the 2DA header isn't always present and that would prevent SET_2DA_ENTRY from making as much sense when applied to arbitrary text files.

All of the formatting was incorrect


I didn't actually see your file, so I can't be certain, but ... the formatting was fine. Really. No matter how ugly it looks to you, the human reader, the game engine and weidu handle it correctly. Since the resulting 2DA files aren't read by humans, it doesn't matter -- WeiDU only reformats as a courtesy to users, who for some reason felt "icky" about the old way (which involved no formatting but also worked fine).

(so SET_2DA_ENTRY 0 0 1 ~%variable%~ wouldn't work, it just sets the 2DA entry to %variable%).


It works now.

On the subject of variables, I've found that the READ actions are always executed first, regardless of their position in the block. For instance, in the following:


That was a bug! Thank you for pointing it out to me. READ actions are now executed "once, first" to support IF_EVAL, they'll be executed again (in the standard order) when and if the patching is done.

. I feel compelled to not only have the result look pretty,


Do not feel this way about 2DA files. The files are not meant for human consumption. :-) However, I'm all for having clean code. If only it were more possible with WeiDU. Sigh.

Having WeiDU insert hundreds of spaces


... doesn't matter. :-)

#12 -jcompton-

-jcompton-
  • Guest

Posted 24 March 2004 - 06:36 PM

Kinda. It's worse for D files, where there are actually three separate passes in order to get "exactly the evaluation order that Jason wants".

Doesn't make me a bad person.

#13 -Guest-

-Guest-
  • Guest

Posted 24 March 2004 - 07:51 PM

I must send Wes an updated SET_2DA_ENTRY tutorial. Do you guys have any suggestions for the WHILE loop tutorial?

It's *slow* (abnormally so). WeiDU tries, but it takes forever. I don't know if I would add it to the tutorial, simply because it would be too confusing (for something that would provide no benefit to most).

This is a hack to support legacy IF_EVALs, which I don't recommend that anyone use. v154 (out soon) adds "BUT_ONLY_IF_IT_CHANGES" (which is mostly me picking on the hideous WeiDU syntax) to make it even easier to avoid IF_EVAL.

I really wish this would be the default behavior (straight copies can be relegated to a new COPY_FOR_REAL action). Only because I know I'm going to go back and insert BUT_ONLY_IF_IT_CHANGES to all the COPY_EXISTING commands. Why copy if you don't actually patch (except to move files)?

It works now.

Good stuff. Of course, there's only one place where I could possibly use this, but it's nice to know the option's there.

That was a bug! Thank you for pointing it out to me. READ actions are now executed "once, first" to support IF_EVAL, they'll be executed again (in the standard order) when and if the patching is done.

And here I was thinking this was the intended behavior. One more reason to hate IF_EVAL.

#14 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 25 March 2004 - 05:17 AM

v154 (out soon) adds "BUT_ONLY_IF_IT_CHANGES" (which is mostly me picking on the hideous WeiDU syntax) to make it even easier to avoid IF_EVAL.

I'll update the WHILE tutorial to use this instead of IF_EVAL. Once I've thought of a good example to use I'll give it its own tutorial as well. One thing, BUT_ONLY_IF_IT_CHANGES has to go after the patch for it to work. I wonder if it would be possible to have if come first as well (or instead)?
COPY_EXISTING_REGEXP ~.*\.itm~ ~override~
BUT_ONLY_IF_IT_CHANGES
patch

 
(so SET_2DA_ENTRY 0 0 1 ~%variable%~ wouldn't work, it just sets the 2DA entry to %variable%).

It works now.

Umm... working on a 'don't ask don't get' policy, time for an 'Idobek is Lazy' feature request. Would to be possible for SET_2DA_ENTRY to support string refs and translation strings?
SET_2DA_ENTRY 0 0 1 @blah


I must send Wes an updated SET_2DA_ENTRY tutorial. Do you guys have any suggestions for the WHILE loop tutorial?

It's *slow* (abnormally so). WeiDU tries, but it takes forever. I don't know if I would add it to the tutorial, simply because it would be too confusing (for something that would provide no benefit to most).

The slowness is due to the large REGEXP copy. I did think about adding a comment on it, but as you say it would just confuse people more. When learning there are some things you don't need to know.

Edited by Idobek, 25 March 2004 - 05:22 AM.


#15 -Guest-

-Guest-
  • Guest

Posted 25 March 2004 - 12:55 PM

I'll update the WHILE tutorial to use this instead of IF_EVAL. Once I've thought of a good example to use I'll give it its own tutorial as well. One thing, BUT_ONLY_IF_IT_CHANGES has to go after the patch for it to work. I wonder if it would be possible to have if come first as well (or instead)?

I had thought it was going to be a true "flag" (e.g., COPY_EXISTING_REGEXP GLOB BUT_ONLY_IF_IT_CHANGES ~~ ~~). That said, I prefer the current "when" implementation, simply because it provides a nice frame for the command:
COPY_EXISTING_REGEXP GLOB ~~ ~~
  WRITE_ASCII 0x00 ~Hello!~
BUT_ONLY_IF_IT_CHANGES
There's no confusion where a patch ends and begins here, anymore.

Umm... working on a 'don't ask don't get' policy, time for an 'Idobek is Lazy' feature request. Would to be possible for SET_2DA_ENTRY to support string refs and translation strings?

How can you be sure that the STRREF is going to be identical in all cases? The first time a different STRREF or mistaken translation of the new value is used, the 2DA would be invalid. On the subject of feature requests, I'm waiting for WeiDU to support the archiving (.tgz) of the files. My installer script (posted below) does this automatically (in StuffIt X format), but it would be nice to have WeiDU do the same on the fly. That way, a user would only have to drag two files to the BGII directory, and be left with two files once the installation is complete.

The slowness is due to the large REGEXP copy. I did think about adding a comment on it, but as you say it would just confuse people more. When learning there are some things you don't need to know.

I'm not following. I was referring to using WHILE to patch with SET_2DA_ENTRY. This is always done on a single file (no regex). The problem here is that SET_2DA_ENTRY is slow (possibly out of necessity, since it has to mess with the "formatting" every action?). In comparison, most other patch actions and (far) more complex code using WHILE are instantaneous (I add a ~Searching game data files (this may take a moment) ...~ before each COPY_EXISTING_REGEXP action- this way you can see where the patching ends and the searching begins (once the files are found, patching takes no time at all)).

#!/bin/sh

archive="stuff -f sitx -m 3 -n $PWD/FixPack.sitx -q -o -x 23 -D"
command="$PWD/Setup.tp2"
decompress="unstuff -d $PWD -q -o -D"
directory="$PWD/FixPack"
executable="$PWD/WeiDU.exe"
options="--out $PWD/override --backup $PWD/FixPack/Backup --tlkout $PWD/dialog.tlk --log $PWD/FixPack/Setup.debug"

function help () { echo -e "\n[$PWD/Setup.sh] Setup version 001\n\nusage: Setup.sh\n\nInstallation Options:\n\n  N  default installation\n  Q  quiet installation (no feedback)\n  V  verbose installation (debug messages)\n"; read -p "     Press Enter For More Options" enter; echo -e "\nQuiet Mode Options:\n\n  I  install the mod\n  U  uninstall the mod\n"; read -p "     Press Enter For More Options" enter; echo -e "\nNormal Mode Settings:\n\n  Normal mode supplies the following options to WeiDU:\n     --out     $PWD/override\n     --backup  $PWD/FixPack/Backup\n     --tlkout  $PWD/dialog.tlk\n     --log     $PWD/FixPack/Setup.debug\n"; read -p "     Press Enter For More Options" enter; echo -e "\nQuiet Mode Settings:\n\n  Quiet mode installation supplies the following options to WeiDU:\n     --yes\n     --textout $PWD/FixPack/Setup.txt\n     --out     $PWD/override\n     --backup  $PWD/FixPack/Backup\n     --tlkout  $PWD/dialog.tlk\n     --log     $PWD/FixPack/Setup.debug\n\n  Quiet mode uninstallation supplies the following options to WeiDU:\n     --uninstall\n     --textout $PWD/FixPack/Setup.txt\n     --out     $PWD/override\n     --backup  $PWD/FixPack/Backup\n     --tlkout  $PWD/dialog.tlk\n     --log     $PWD/FixPack/Setup.debug\n"; read -p "     Press Enter For More Options" enter; echo -e "\nVerbose Mode Settings:\n\n  Verbose mode supplies the following options to WeiDU:\n     --debug-assign\n     --out     $PWD/override\n     --backup  $PWD/FixPack/Backup\n     --tlkout  $PWD/dialog.tlk\n     --log     $PWD/FixPack/Setup.debug\n"; }

echo -e "[$PWD/Setup.sh] Setup version 001\n\nPreparing for installation ..."
mkdir -p $PWD/docs

if [ -d "$PWD/CD*/Data" ]; then
  echo "Consolidating game data files ..."
  if [ -d "$PWD/CD3/Data" ]; then
    mv $PWD/CD3/Data/* $PWD/data
  fi
  if [ -d "$PWD/CD4/Data" ]; then
    mv $PWD/CD4/Data/* $PWD/data
  fi
  if [ -d "$PWD/CD2/Data" ]; then
    mv $PWD/CD2/Data/* $PWD/data
  fi
  if [ -d "$PWD/CD5/data" ]; then
    mv $PWD/CD5/data/* $PWD/data
  fi
fi
if [ -d "$PWD/CD*/Movies" ]; then
  echo "Consolidating game movie files ..."
  mkdir -p $PWD/movies; mv $PWD/CD*/movies/* $PWD/movies
fi
if [ -f "$PWD/FixPack.sitx" ]; then
  echo "Decompressing installer files ..."
  $decompress $PWD/FixPack.sitx; mv $PWD/FixPack/WeiDU.exe $PWD; mv $PWD/FixPack/Setup.tp2 $PWD
fi
if [ -d "$PWD/CD*" ]; then
  echo "Cleaning up ..."
  rm -rf $PWD/CD*
fi

echo -e "\n[$PWD/Setup.sh] Setup version 001\nUsing Mode [Default]\n\nAn installation method can be selected before proceeding.\n\nWhich installation method should be used?"
read -p "[N]ormal mode, [Q]uiet mode, [V]erbose mode? " method

if [ ! -d "$directory" ]; then
  echo -e "\nERROR: [$0] Installation Failed (File_not_found($directory))"
elif [ ! -f "$executable" ]; then
  echo -e "\nERROR: [$0] Installation Failed (File_not_found($executable))"
elif [ ! -f "$command" ]; then
  echo -e "\nERROR: [$0] Installation Failed (File_not_found($command))"
else
  ulimit -s 65536
  if [ "$method" = "N" -o "$method" = "n" ]; then
    echo -e "\nNormal mode will display the default information during installation.\n\nOK to continue?"
    read -p "[Y]es, [N]o? " prompt
    if [ "$prompt" = "Y" -o "$prompt" = "y" ]; then
      echo -e "\n[$PWD/Setup.sh] Setup version 001\nUsing Mode [Normal]\n"
      $executable $options $command && mv $PWD/WeiDU.exe $PWD/FixPack && mv $PWD/Setup.tp2 $PWD/FixPack && cp $PWD/FixPack/Setup.debug ~/Desktop/Setup.log $archive $PWD/FixPack
    elif [ "$prompt" = "N" -o "$prompt" = "n" ]; then
      echo -e "\nInstallation canceled."
    else
      echo -e "\nERROR: [$0] Installation Failed (Invalid_selection($prompt))\n"; help
    fi
  elif [ "$method" = "Q" -o "$method" = "q" ]; then
    echo -e "\nQuiet Mode will disable the display of all information during\ninstallation. This mode should NOT be used.\n\nOK to continue?"
    read -p "[Y]es, [N]o? " prompt
    if [ "$prompt" = "Y" -o "$prompt" = "y" ]; then
      echo -e "\nWould you like to re/install, or uninstall the mod?"
      read -p "[I]nstall, [U]ninstall? " prompt
      if [ "$prompt" = "I" -o "$prompt" = "i" ]; then
        echo -e "\n[$PWD/Setup.sh] Setup version 001\nUsing Mode [Quiet]\n"
        $executable $options --yes --textout $PWD/FixPack/Setup.txt $command > $PWD/FixPack/Setup.report | echo -e "Press ENTER to proceed." && mv $PWD/WeiDU.exe $PWD/FixPack && mv $PWD/Setup.tp2 $PWD/FixPack && cp $PWD/FixPack/Setup.debug ~/Desktop/Setup.log && $archive $PWD/FixPack
      elif [ "$prompt" = "U" -o "$prompt" = "u" ]; then
        echo -e "\n[$PWD/Setup.sh] Setup version 001\nUsing Mode [Quiet]\n"
        $executable $options --uninstall --textout $PWD/FixPack/Setup.txt $command > $PWD/FixPack/Setup.report | echo -e "Press ENTER to proceed." && mv $PWD/WeiDU.exe $PWD/FixPack && mv $PWD/Setup.tp2 $PWD/FixPack && cp $PWD/FixPack/Setup.debug ~/Desktop/Setup.log && $archive $PWD/FixPack
      else
        echo -e "\nERROR: [$0] Installation Failed (Invalid_selection($prompt))\n"; help
      fi
    elif [ "$prompt" = "N" -o "$prompt" = "n" ]; then
      echo -e "\nInstallation canceled."
    else
      echo -e "\nERROR: [$0] Installation Failed (Invalid_selection($prompt))"; help
    fi
  elif [ "$method" = "V" -o "$method" = "v" ]; then
    echo -e "\nVerbose mode will enable the display of additional information\nduring installation. This mode should only be used if you encounter\nerrors performing a normal installation.\n\nOK to continue?"
    read -p "[Y]es, [N]o? " prompt
    if [ "$prompt" = "Y" -o "$prompt" = "y" ]; then
      echo -e "\n[$PWD/Setup.sh] Setup version 001\nUsing Mode [Verbose]\n"
      $executable $options --debug-assign $command && mv $PWD/WeiDU.exe $PWD/FixPack && mv $PWD/Setup.tp2 $PWD/FixPack && cp $PWD/FixPack/Setup.debug ~/Desktop/Setup.log && $archive $PWD/FixPack
    elif [ "$prompt" = "N" -o "$prompt" = "n" ]; then
      echo -e "\nInstallation canceled."
    else
      echo -e "\nERROR: [$0] Installation Failed (Invalid_selection($prompt))"; help
    fi
  else
    echo -e "\nERROR: [$0] Installation Failed (Invalid_selection($method))"; help
  fi
fi


#16 Scar

Scar
  • Member
  • 22 posts

Posted 29 March 2004 - 06:37 AM

...
Then, all patch commands are executed (read commands will be executed again).

Thanks for clarifying that.

SET_2DA_ENTRY will now take an arbitrary patch_exp as the final argument, converting its int value to a string. If it doesn't evaluate (e.g., it is some arbitrary string that is not a variable), its text is used instead.

That's way cool. Thanks!

I must send Wes an updated SET_2DA_ENTRY tutorial. Do you guys have any suggestions for the WHILE loop tutorial?

Well, feel free to use my code and do some wicked things with loops and S_2_E. I've got another two snippets for your inspiration. The first one, when applied to MXSPLPRS.2DA, removes the duplicated row 30:

SET "i" = 0
WHILE ("%i%" < 8) BEGIN
    SET_2DA_ENTRY 33  0  0 ~~
    SET "i" = ("%i%" + 1)
END
Note the use of the fact that the edited line gets shorter with every S_2_E; you just need to write in the first cell all the time. Due to this dynamic, column count is set to 0 and line to 33, to account for the header. It produces an empty line in the middle of the table, which is automatically removed on the next use of S_2_E, e.g. for header formating.

And if you're feeling naughty and want to seriously nerf druids, this piece of code, applied to MXSPLDRU.2DA, brings their spell slots on level with PnP rules:

SET "row" = 40
WHILE ("%row%" > 14) BEGIN
    SET "col" = 7
    WHILE "%col%" BEGIN
        SET_2DA_ENTRY "%row%" "%col%" 8 ~6~
        SET "col" = ("%col%" - 1)
    END
    SET "row" = ("%row%" - 1)
END
Have fun! B)

On the subject of feature requests, I'm waiting for WeiDU to support the archiving (.tgz) of the files. My installer script (posted below) does this automatically (in StuffIt X format), but it would be nice to have WeiDU do the same on the fly. That way, a user would only have to drag two files to the BGII directory, and be left with two files once the installation is complete.

I second that, a clean disk is a good disk. And it might help keeping some Windows guys from using proprietary installer apps that are no use on our Unix-savvy Macs (the folks over at FWP are notorious for that :huh:).

The problem here is that SET_2DA_ENTRY is slow (possibly out of necessity, since it has to mess with the "formatting" every action?). In comparison, most other patch actions and (far) more complex code using WHILE are instantaneous

Yes, I've noticed that, too. It might be a Mac problem, tho...

Armin

#17 Idobek

Idobek

    Pocket Plane Gibberling

  • Member
  • 429 posts

Posted 29 March 2004 - 01:20 PM

Yes, I've noticed that, too. It might be a Mac problem, tho...

No, its universal. I didn't catch 'Guest's' meaning the first time because I had just read a separate post mentioning about the length of time a large C_E_R can take, but that can be speedy next to a large set of S_2_Es.

Well, feel free to use my code and do some wicked things with loops and S_2_E.

Thanks, but I think I'll keep WHILE and S_2_E separate for tutorial purposes. People are intimidated enough by them I don't want to make it worse. :P

Thanks for the funky examples. I have some things I'm going to use them for already.