Author Topic: Checking whether a string includes something  (Read 261 times)

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Checking whether a string includes something
« on: October 17, 2017, 01:48:00 PM »
Hello.

Is it possible to check whether a string includes specific words without necessarily having only those words?

Case in point—I would like to patch all CREs that belong to the Flaming Fist. That was easy enough in BG/BG:EE, where all Flaming Fist members had their class set to "Flaming Fist"; Siege of Dragonspear, however, has regular player classes assigned to all its Flaming Fist CREs. Unfortunately, it is Siege of Dragonspear's Flaming Fist CREs that I'm planning to target, so I wanted to do something like "check whether the CRE's name at 0x8 includes the words Flaming Fist and return true if it does." Is that possible?

Alternatively, I could just patch individual CREs one by one but that would be a chore and would exclude any Flaming Fist CREs added by mods (if any.)

Offline Mike1072

  • Planewalker
  • *****
  • Posts: 280
  • Gender: Male
Re: Checking whether a string includes something
« Reply #1 on: October 17, 2017, 04:05:25 PM »
This should do it.

Code: [Select]
READ_STRREF 0x08 name
PATCH_IF (~%name%~ STRING_CONTAINS_REGEXP ~Flaming Fist~) BEGIN
  // do stuff
END


Edit: woops, should be...

Code: [Select]
READ_STRREF 0x08 name
PATCH_IF (~%name%~ STRING_CONTAINS_REGEXP ~Flaming Fist~ == 0) BEGIN
  // do stuff
END
« Last Edit: October 18, 2017, 04:11:34 PM by Mike1072 »

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Checking whether a string includes something
« Reply #2 on: October 17, 2017, 04:37:16 PM »
Thank you, Mike!

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Checking whether a string includes something
« Reply #3 on: October 18, 2017, 02:37:55 PM »
So, here's my code:

Code: [Select]
COPY_EXISTING_REGEXP GLOB ~.*\.cre~ ~override~
READ_STRREF 0x8 name
READ_LONG 0x28 anim
PATCH_IF (~%name%~ STRING_CONTAINS_REGEXP ~Flaming Fist~) BEGIN
  WRITE_BYTE 0x2c 35 // metal color
  WRITE_BYTE 0x2d 46 // minor color
  WRITE_BYTE 0x2e 63 // major color
  WRITE_BYTE 0x2f 12 // skin color
  WRITE_BYTE 0x30 63 // leather color
  WRITE_BYTE 0x31 30 // armor color
  WRITE_BYTE 0x32 0 // hair color
  PATCH_IF (MOD_IS_INSTALLED ~setup-animod.tp2~ 20) OR
            (MOD_IS_INSTALLED ~setup-animod.tp2~ 21) THEN BEGIN
PATCH_PHP_EACH bg1_animation AS oldbg => newbg BEGIN
PATCH_IF (~%anim%~ = ~%oldbg%~) BEGIN
WRITE_LONG 0x28 ~%newbg%~
END
END
END ELSE BEGIN
PATCH_PHP_EACH bg2_animation AS oldbg2 => newbg2 BEGIN
PATCH_IF (~%anim%~ = ~%oldbg2%~) BEGIN
WRITE_LONG 0x28 ~%newbg2%~
END
END
END
END
    BUT_ONLY

There are two associative arrays before this code called "bg2_animation" and "bg1_animation" respectively.

The purpose of the code is to do the following:

  • Check whether the CRE's name includes Flaming Fist
  • If it does, patch the CRE's colors as instructed
  • Still subject to the CRE's name's including Flaming Fist, check whether either of these mod components are installed; if they are, patch the CRE's animation using the bg1_animation associative array.
  • If the name includes Flaming Fist but neither mod component is installed, patch the CRE's animation using the bg2_animation associative array.

The problem is that the STRING_CONTAINS_REGEXP command appears to work the other way around: CREs are patched if their name does not include Flaming Fist. Starting a new game in Siege of Dragonspear shows that Flaming Fist members are entirely unchanged, whereas party members or even the Watchers in Candlekeep are.

Is it possible to reverse the behavior?

Offline Mike1072

  • Planewalker
  • *****
  • Posts: 280
  • Gender: Male
Re: Checking whether a string includes something
« Reply #4 on: October 18, 2017, 04:10:37 PM »
Oh, yeah, my bad.

The check should be:

Code: [Select]
~%name%~ STRING_CONTAINS_REGEXP ~Flaming Fist~ == 0

Another note - this code will loop through each entry in the bg1_animation array:
Code: [Select]
PATCH_PHP_EACH bg1_animation AS oldbg => newbg BEGIN
  PATCH_IF (~%anim%~ = ~%oldbg%~) BEGIN
    WRITE_LONG 0x28 ~%newbg%~
  END
END

There's a way this can be done without using a loop, which can be helpful if you're concerned about performance.

Code: [Select]
PATCH_IF (VARIABLE_IS_SET $bg1_animation(~%anim%~)) BEGIN
  WRITE_LONG 0x28 $bg1_animation(~%anim%~)
END
« Last Edit: October 18, 2017, 05:44:27 PM by Mike1072 »

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Checking whether a string includes something
« Reply #5 on: October 18, 2017, 04:45:42 PM »
Thank you again, Mike!

I reckon "==" signifies "not equal to"?

After amending my code, it no longer patches non-Flaming Fist CREs, though Flaming Fist members still seem to be unaffected. It is worth noting that all these CREs have something else in their name after "Flaming Fist" (e.g. Flaming Fist Enforcer, Flaming Fist Mercenary, etc.)—can that be the reason?

Offline Mike1072

  • Planewalker
  • *****
  • Posts: 280
  • Gender: Male
Re: Checking whether a string includes something
« Reply #6 on: October 18, 2017, 05:28:32 PM »
Thank you again, Mike!

I reckon "==" signifies "not equal to"?
It means equal to.  The issue is that STRING_CONTAINS_REGEXP returns 0 when there is a match and non-zero if there's no match.  (Something that is very easy to forget!)  To determine if there is a match, we have to check if the returned result is 0.

Quote
After amending my code, it no longer patches non-Flaming Fist CREs, though Flaming Fist members still seem to be unaffected. It is worth noting that all these CREs have something else in their name after "Flaming Fist" (e.g. Flaming Fist Enforcer, Flaming Fist Mercenary, etc.)—can that be the reason?
No, that shouldn't matter.  I was able to run a test on my machine, and the if statement does pick up a number of creatures, so it's probably something going wrong with the changes that are being applied.  I'd recommend taking a closer look at the associative arrays.
« Last Edit: October 18, 2017, 05:29:13 PM by Mike1072 »

Offline qwertyqwerty

  • Planewalker
  • *****
  • Posts: 61
Re: Checking whether a string includes something
« Reply #7 on: October 18, 2017, 05:36:32 PM »
Keep in mind that names at 0x8 and 0xc are not necessarily the same, you may want to print the strings to debug.

Also note that most other "STRING_..." functions should be compared to 1, not 0. STRING_CONTAINS_REGEXP is an oddball, which has bitten me in the rear at times.
And if installing on non-english games, that will require other strings to compare.

Offline Andrea C.

  • Planewalker
  • *****
  • Posts: 80
  • Gender: Male
Re: Checking whether a string includes something
« Reply #8 on: October 19, 2017, 12:48:30 PM »
I've added

Code: [Select]
PRINT ~name is %name%~
after the PATCH_IF condition to see the 0x8 names of the CREs that were returning true, but my installer throws an error message when I run it. Last time I added a print command to do something similar it worked no problem  ???

And if installing on non-english games, that will require other strings to compare.

This is true. Unfortunately these CREs don't really have much else I can use to single them out. I might have to patch them one by one  (I don't think there are many mods out there adding Flaming Fist soldiers to the game, after all.)


EDIT: I tried patching CREs one by one and guess what? The Flaming Fist guys at the beginning of Siege of Dragonspear are still not getting patched. Is there a way for me to see which CREs those are? Tangentially, how I can find which CREs are being used in a given cutscene?
« Last Edit: October 19, 2017, 01:37:10 PM by Andrea C. »

Offline Mike1072

  • Planewalker
  • *****
  • Posts: 280
  • Gender: Male
Re: Checking whether a string includes something
« Reply #9 on: October 19, 2017, 04:06:33 PM »
I've added

Code: [Select]
PRINT ~name is %name%~

Try PATCH_PRINT.

Offline c4_angel

  • Planewalker
  • *****
  • Posts: 19
Re: Checking whether a string includes something
« Reply #10 on: October 20, 2017, 09:54:12 AM »
I've got another question maybe I can use the same topic title.
How to check whether a bcs file(or files) includes a particular string.

Here is my way:
1. Copy and decompile all bcs files to a temp folder, replace target string to something else, then use BUT_ONLY at the end.
2. Check the files copied into the temp folder, save the names.
3. Use the saved names to do what I want.

The main problem is this way is too slow, besides I found not every copied file is what I want, some does not include the target string at all.
« Last Edit: October 20, 2017, 09:55:57 AM by c4_angel »

Offline Argent77

  • Planewalker
  • *****
  • Posts: 144
Re: Checking whether a string includes something
« Reply #11 on: October 20, 2017, 10:29:14 AM »
I've got another question maybe I can use the same topic title.
How to check whether a bcs file(or files) includes a particular string.

Here is my way:
1. Copy and decompile all bcs files to a temp folder, replace target string to something else, then use BUT_ONLY at the end.
2. Check the files copied into the temp folder, save the names.
3. Use the saved names to do what I want.

The main problem is this way is too slow, besides I found not every copied file is what I want, some does not include the target string at all.

This should be enough:
Code: [Select]
COPY_EXISTING_REGEXP ~.+\.bcs~ ~override~
  DECOMPILE_AND_PATCH BEGIN
    PATCH_IF (INDEX_BUFFER(~search string~) >= 0) BEGIN // search string can be a regular expression
      // string found: do stuff...
    END
  END
BUT_ONLY

If you want to store the resource names for later use you can use dynamic arrays, e.g.
Code: [Select]
OUTER_SET resrefs = 0
COPY_EXISTING_REGEXP ~.+\.bcs~ ~override~
  DECOMPILE_AND_PATCH BEGIN
    PATCH_IF (INDEX_BUFFER(~search string~) >= 0) BEGIN
      TEXT_SPRINT EVAL ~resrefs_%resrefs%~ ~%SOURCE_FILE%~
      SET resrefs += 1
    END
  END
BUT_ONLY

// Loop through list of matches
OUTER_FOR (idx = 0; idx < resrefs; ++idx) BEGIN
  OUTER_TEXT_SPRINT resref EVAL ~%resrefs_%idx%%~
  PRINT ~Found match: %resref%~
END

Offline c4_angel

  • Planewalker
  • *****
  • Posts: 19
Re: Checking whether a string includes something
« Reply #12 on: October 20, 2017, 02:26:12 PM »
This should be enough:
Code: [Select]
COPY_EXISTING_REGEXP ~.+\.bcs~ ~override~
  DECOMPILE_AND_PATCH BEGIN
    PATCH_IF (INDEX_BUFFER(~search string~) >= 0) BEGIN // search string can be a regular expression
      // string found: do stuff...
    END
  END
BUT_ONLY

If you want to store the resource names for later use you can use dynamic arrays, e.g.
Code: [Select]
OUTER_SET resrefs = 0
COPY_EXISTING_REGEXP ~.+\.bcs~ ~override~
  DECOMPILE_AND_PATCH BEGIN
    PATCH_IF (INDEX_BUFFER(~search string~) >= 0) BEGIN
      TEXT_SPRINT EVAL ~resrefs_%resrefs%~ ~%SOURCE_FILE%~
      SET resrefs += 1
    END
  END
BUT_ONLY

// Loop through list of matches
OUTER_FOR (idx = 0; idx < resrefs; ++idx) BEGIN
  OUTER_TEXT_SPRINT resref EVAL ~%resrefs_%idx%%~
  PRINT ~Found match: %resref%~
END

Thanks a lot, Argent77! Quite helpful!

 

With Quick-Reply you can write a post when viewing a topic without loading a new page. You can still use bulletin board code and smileys as you would in a normal post.

Name: Email:
Verification:
Type the letters shown in the picture
Listen to the letters / Request another image
Type the letters shown in the picture:
What color is grass?:
What is the seventh word in this sentence?:
What is five minus two (use the full word)?: