igi Posted October 30, 2010 Share Posted October 30, 2010 Given a strref, what is the most efficient way of finding which character it comes from (assuming the strref comes from character dialog)? Conversely, given a character (lets say a cre name) what is the most efficient way of funding all strref items for that character? Link to comment
cmorgan Posted October 30, 2010 Share Posted October 30, 2010 igi, I am not sure, but I am going to note here (and hope other modders follow suit as they work) that this difficulty is much easier to solve if folks use the in-strig comment function, so that @1 = ~[MYCRE1] Hi, I am a creature. You can find all my strings by weidu regexp search for the not-printed-to-screen-dialog-box comment \[MYCRE1\]~ @2 = ~[MYCRE2] Hi, I am another creature. What he said. Plus, for translators, they can actually see who is saying what right in the .tra, so their life is made much, much easier...~ I'd be very, very interested in how to do this more efficiently too. My current method is to decompile each dialog, manually edit everything out of the file except the SAY sttrefs acssociated with the speaking NPC, and work from there. But I can't use that information further than to use SATE_WHICH_SAYS, as the only stable sttrefs are those of vanilla BioWare NPCs. Link to comment
the bigg Posted October 30, 2010 Share Posted October 30, 2010 NI searches are pretty powerful, depending on your situation. Link to comment
Miloch Posted October 31, 2010 Share Posted October 31, 2010 I'm usually opposed to posting my freakish hackery (in this case, hackish code used to generate even more hackish code). But since you asked, you could probably tweak something like this: <<<<<<<< .../0dlgtest-inlined/0dlg.txt StrRef NPC Regexp String >>>>>>>> <<<<<<<< .../0dlgtest-inlined/0str.txt StrRef Dialog NPC State String >>>>>>>> COPY ~.../0dlgtest-inlined/0dlg.txt~ ~0dlgtest/0dlg.txt~ COPY ~.../0dlgtest-inlined/0str.txt~ ~0dlgtest/0str.txt~ OUTER_PATCH wlib BEGIN WRITE_BYTE 0 0xd READ_ASCII 0 mnl (1) WRITE_BYTE 1 0xa READ_ASCII 1 lnl (1) READ_ASCII 0 nl (2) WRITE_BYTE 2 0x9 READ_ASCII 2 tab (1) WRITE_BYTE 3 0x20 READ_ASCII 3 spc (1) END ACTION_DEFINE_ASSOCIATIVE_ARRAY npcs BEGIN ~NPCname~ => ~(regexp to search for)~ ... //this is only really useful if you want to search certain NPCs' dialogues for certain text END OUTER_PATCH whatever BEGIN FOR (t1 = 1; t1 < 74108; t1 += 1) BEGIN //Read the upper limit from your dialog.tlk if you like; I just hardcoded 74108 for vanilla ToB t-match = 0 t-match2 = 0 SPRINT t-strcln ~~ SPRINT t-npc ~~ GET_STRREF t1 t-str PHP_EACH npcs AS npc => rgxp BEGIN PATCH_IF NOT (VARIABLE_IS_SET $dx(EVALUATE_BUFFER ~%t1%~)) AND ((~%t-str%~ STRING_CONTAINS_REGEXP ~%npc%~ = 0) AND (~%t-str%~ STRING_CONTAINS_REGEXP ~%rgxp%~ = 0)) BEGIN t-match = 1 SPRINT t-npc ~%npc%~ INNER_PATCH_SAVE t-strcln ~%t-str%~ BEGIN REPLACE_TEXTUALLY ~[%nl%%lnl%%mnl%%tab%]~ ~~ END PATCH_IF NOT (VARIABLE_IS_SET $dx(EVALUATE_BUFFER ~%t1%~)) BEGIN SET $dx(EVALUATE_BUFFER ~%t1%~) = 1 INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0dlg.txt~ ~%t1% %npc% %rgxp% %t-strcln%~ END END END END PATCH_IF ((t-match = 1) AND (t-match2 = 0)) BEGIN INNER_ACTION BEGIN SILENT COPY_EXISTING_REGEXP GLOB ~^.*\.dlg$~ ~override~ t-match2 = 0 PATCH_IF SOURCE_SIZE > 0x34 BEGIN READ_LONG 0x30 t-frz PATCH_IF t-frz < 8 BEGIN SPRINT srcdlg ~%SOURCE_FILE%~ TO_LOWER srcdlg READ_LONG 0x08 st_num //State count READ_LONG 0x0c st_off //States offset READ_LONG 0x10 tr_num //Transition count READ_LONG 0x14 tr_off //Transitions offset READ_LONG 0x28 ac_off //Actions offset READ_LONG 0x2c ac_num //Actions count FOR (i = 0; i < st_num; i += 1) BEGIN //Cycle through states READ_SLONG (st_off + i * 0x10) sr //StringRef PATCH_IF sr = t1 BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %srcdlg% %t-npc% DlgSt%i% %t-strcln%~ END t-match2 = 1 i = st_num END END PATCH_IF t-match2 = 0 BEGIN FOR (j = 0; j < tr_num; j += 1) BEGIN //Cycle through transitions READ_BYTE (tr_off + j * 0x20) flg1 READ_BYTE (tr_off + j * 0x20 + 1) flg2 PATCH_IF (flg1 BOR 0b11111110 = 0b11111111) BEGIN READ_SLONG (tr_off + j * 0x20 + 0x4) sr //StringRef PATCH_IF sr = t1 BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %srcdlg% %t-npc% DlgTr%j% %t-strcln%~ END t-match2 = 1 j = tr_num END END PATCH_IF ((t-match2 = 0) AND (((flg1 BOR 0b11101111) = 0b11111111) OR ((flg1 BOR 0b10111111) = 0b11111111) OR ((flg1 BOR 0b01111111) = 0b11111111) OR ((flg2 BOR 0b11111110) = 0b11111111))) BEGIN //Journal READ_SLONG (tr_off + j * 0x20 + 0x8) sr //StringRef PATCH_IF sr = t1 BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %srcdlg% %t-npc% DlgJr%j% %t-strcln%~ END t-match2 = 1 j = tr_num END END END END PATCH_IF t-match2 = 0 BEGIN FOR (k = 0; k < ac_num; k += 1) BEGIN //Cycle through actions sr = 0 READ_LONG (ac_off + k * 8) as_off //Action string offset READ_LONG (ac_off + k * 8 + 4) as_len //Action string length READ_ASCII as_off ac_str (as_len) //Action string INNER_PATCH ~%ac_str%~ BEGIN REPLACE_EVALUATE CASE_INSENSITIVE ~AddJournalEntry(\([0-9]+\),~ BEGIN sr = %MATCH1% END ~AddJournalEntry(%sr%),~ END PATCH_IF sr = t1 BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %srcdlg% %t-npc% DlgAc%k% %t-strcln%~ END t-match2 = 1 k = ac_num END END END END END BUT_ONLY ACTION_IF t-match2 = 0 BEGIN SILENT COPY_EXISTING_REGEXP GLOB ~^.*\.itm$~ ~override~ PATCH_IF SOURCE_SIZE > 0x71 BEGIN SPRINT itname ~%SOURCE_FILE%~ TO_LOWER itname READ_SLONG 0x50 t-itn1 READ_SLONG 0x54 t-itn2 PATCH_IF ((t-itn1 = t1) OR (t-itn2 = t1)) BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %itname% %t-npc% Item %t-strcln%~ END t-match2 = 1 END END BUT_ONLY END ACTION_IF t-match2 = 0 BEGIN SILENT COPY_EXISTING_REGEXP GLOB ~^.*\.cre$~ ~override~ PATCH_IF SOURCE_SIZE > 0x2d3 BEGIN SPRINT crname ~%SOURCE_FILE%~ TO_LOWER crname FOR (s1 = 0xa4; s1 < 0x234; s1 += 0x4) BEGIN READ_SLONG s1 ss //Soundslots PATCH_IF (ss = t1) BEGIN INNER_ACTION BEGIN SILENT APPEND_OUTER ~0dlgtest/0str.txt~ ~%t1% %crname% %t-npc% Cre %t-strcln%~ END t-match2 = 1 s1 = 0x234 END END END BUT_ONLY END END END END END I used it to generate a list of all strings for given NPCs containing specific text. You could reverse it in theory to do the opposite. Edit: since it's not really commented, it does several passes for dialogues (to get states, transition replies, journal entries) and then moves on to look at item descriptions, then NPC soundclips and biographies as well. Link to comment
igi Posted October 31, 2010 Author Share Posted October 31, 2010 Thanks Miloch - this is what I was hoping to avoid having to write myself With a little modification your code should do what I want (though it is probably the slowest process I've ever encountered in IE modding). Link to comment
cmorgan Posted October 31, 2010 Share Posted October 31, 2010 igi, can you post what you have when you are done, please? It is beyond my capabilities to reverse this, and I would love a way of creating a list from a megamod install which ended up with output like JAHEIRA <<list of string #s that she says in all contexts>> <<contents of string>> MOD_ADDED_CRE <<list of string #s that it says in all contexts>> <<contents of string>> I am looking for stronger ways of keeping "character voice" consistent, and having a resource file of existing sttrefs and their content would be useful in many other ways, too. Link to comment
igi Posted October 31, 2010 Author Share Posted October 31, 2010 igi, can you post what you have when you are done, please? The only change I've made to the code Miloch presented is changing this line: PATCH_IF NOT (VARIABLE_IS_SET $dx(EVALUATE_BUFFER ~%t1%~)) AND ((~%t-str%~ STRING_CONTAINS_REGEXP ~%npc%~ = 0) AND (~%t-str%~ STRING_CONTAINS_REGEXP ~%rgxp%~ = 0)) BEGIN to this PATCH_IF NOT (VARIABLE_IS_SET $dx(EVALUATE_BUFFER ~%t1%~)) BEGIN I'm then running with the array set to ~*~ => ~~ The output, when finished (Thursday morning sometime, assuming my PC runs all the time) should be a list containing strref, text and resource name (dialog file, item file etc). I'll probably tweak the code around the transition states, as player text is sneaking in there. With a 'little' work on the output lists, it should be possible to see all the text from a given CRE, and use it as a lookup to get from strref to CRE. Link to comment
cmorgan Posted October 31, 2010 Share Posted October 31, 2010 If you want me to do a fast run for you, let me know - I will happily put my rig on it if you would like (unless yours is way better - I currently have a clean patched tob vanilla install on a Raptor, Q9450 OC'd to 3.4Ghz, 6GB ram, and I'm not doing anything with the computer for the next 24 hours so it can run overnight if needed). cmorganbg at gmail dot com, or all the usual places. Link to comment
Miloch Posted October 31, 2010 Share Posted October 31, 2010 Yeah, it's slow - it could probably be optimised by some WeiFu ninjery that's beyond me. Also I think I coded it so that it exits the current scan if it finds a "match" (which if you're using a generic regexp pattern match, will be the first hit). So you might want to take out the various match variables and checks. Then again, that might suffice if you just want the first "hit" for a stringref. In BG2, however, this won't always be a valid one since it contains BG1 legacy dialogues and items (I try to avoid "hits" in those by checking for valid dialogue flags via READ_LONG 0x30 t-frz but not all legacy dialogues are flagged thus). Also, I don't know if it's currently sophisticated to tell *who* says what necessarily. It can only tell which dialogue file something's in, and recursively whose dialogue file that is, and where (transition, journal entry, etc.). But sometimes NPC dialogues contain PC or other NPC responses I think. Most of that can probably be determined from the state type though. Link to comment
cmorgan Posted October 31, 2010 Share Posted October 31, 2010 heh. So I should probably stop running it now? It seems to be chunking along at 25% of CPU capacity, so it is doing something, (has been since 2:20pm). Link to comment
Miloch Posted October 31, 2010 Share Posted October 31, 2010 You can check the logs (0dlg.txt and 0str.txt) to see how far it may have gotten and whether it seems to be getting valid results. Link to comment
cmorgan Posted October 31, 2010 Share Posted October 31, 2010 OK, it ios definitely working, though I may be missing something between what I thought would be output and what I am getting. I have set up with igi's changes, and here is the kind of thing i get: 0str.txt StrRef Dialog NPC State String1 haerda.dlg * DlgTr100 No, I'm sorry, none of them sound familiar. 2 haerda.dlg * DlgTr101 You played Elminster? 3 haerda.dlg * DlgTr102 Uh, the yugoloth, was it? Yeah, you stole the show with that one, if I recall. 4 haerda.dlg * DlgSt54 And, who knows, we were rehearsing for Picoccio's "Three Days in the Ether." Perhaps we can give you a dress matinee. 5 haerda.dlg * DlgSt55 Oh, my dark ravens, let us stop our squawking. I shall remove this foul demeanor like a mask because, if you will still have me, I think I would quite enjoy the company of your troupe. 6 haerda.dlg * DlgTr104 Then join us by all means. You are welcome here. 7 haerda.dlg * DlgTr105 Perhaps another time, Haer'dalis. Your world seems rather strange to me...I'll have to think it over. 8 haerda.dlg * DlgTr106 Forget it. It's time I left here, and I suspect you should, as well. 9 haerda.dlg * DlgSt56 If we can find it and return it to the playhouse, the reward offered by our Stagemistress, Raelis Shai, shall be yours. Come, then, let us search for it: free money does not come often in the arts. 0dlg.txt StrRef Dialog NPC State String1 * No, I'm sorry, none of them sound familiar. 2 * You played Elminster? 3 * Uh, the yugoloth, was it? Yeah, you stole the show with that one, if I recall. 4 * And, who knows, we were rehearsing for Picoccio's "Three Days in the Ether." Perhaps we can give you a dress matinee. 5 * Oh, my dark ravens, let us stop our squawking. I shall remove this foul demeanor like a mask because, if you will still have me, I think I would quite enjoy the company of your troupe. I am going back to try with your original code, and see what happens, so I can learn a bit more. Link to comment
Miloch Posted November 1, 2010 Share Posted November 1, 2010 StrRef Dialog NPC State String1 haerda.dlg * DlgTr100 No, I'm sorry, none of them sound familiar. 2 haerda.dlg * DlgTr101 You played Elminster? 3 haerda.dlg * DlgTr102 Uh, the yugoloth, was it? Yeah, you stole the show with that one, if I recall. 4 haerda.dlg * DlgSt54 And, who knows, we were rehearsing for Picoccio's "Three Days in the Ether." Perhaps we can give you a dress matinee. 5 haerda.dlg * DlgSt55 Oh, my dark ravens, let us stop our squawking. I shall remove this foul demeanor like a mask because, if you will still have me, I think I would quite enjoy the company of your troupe. 6 haerda.dlg * DlgTr104 Then join us by all means. You are welcome here. 7 haerda.dlg * DlgTr105 Perhaps another time, Haer'dalis. Your world seems rather strange to me...I'll have to think it over. 8 haerda.dlg * DlgTr106 Forget it. It's time I left here, and I suspect you should, as well. 9 haerda.dlg * DlgSt56 If we can find it and return it to the playhouse, the reward offered by our Stagemistress, Raelis Shai, shall be yours. Come, then, let us search for it: free money does not come often in the arts. I guess that looks about right. A "DlgSt" response is something the actor says (a state). A "DlgTr" is something the PC can say in response (aka transition). Occasionally you might see "DlgJr" for journal entries. Instead of that * for NPC, we could do an inside loop to see which CRE owns the dialogue, but that'd slow it down quite a bit (would have to do a loop inside each existing loop to find the "hit"). In this case, it's fairly obvious from the dlg name, which should be the case for most NPCs. The other log is rather redundant in this case (it had a purpose when I was looking for specific text and specific NPC names). Edit: incidentally, this is easier to read if you copy and paste it into a spreadsheet. I remove line breaks etc. from text so you can do that and still read it. Link to comment
Ardanis Posted November 1, 2010 Share Posted November 1, 2010 For finding an actor who says a known strref, NI/DLTCEP should be enough. For finding all all strrefs an actor may say, can't you simply take a look at their dialogue files? CLEAR_ARRAY states GET_OFFSET_ARRAY states 0xc 4 0x8 4 0 0 0x10PHP_EACH states AS i => r BEGIN READ_SLONG r strref // GET_STRREF and APPEND|PRINT it somewhere END if you only care for states, but not transitions. You can also StartDialog()|StartDialogOverride() a specific DLG from a script. If these count as well then a BAF parser may also be needed. Link to comment
Recommended Posts
Archived
This topic is now archived and is closed to further replies.