Jump to content

Strref to actor


igi

Recommended Posts

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

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

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

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

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, 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

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

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

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 String

1 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 String

1 * 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
StrRef Dialog NPC State String

1 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

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 0x10

PHP_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

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...