
    UHjZ                       d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	m
Z
 ddlmZ ddlmZmZmZ  ee      j%                         j&                  d   Zedz  Zd	Zdd
ZddZddZddZd dZd!dZ	 	 	 	 	 	 d"dZd#dZd$dZd%dZ d&dZ!	 	 	 	 	 	 	 	 	 	 	 	 	 	 d'dZ"d(dZ#d(dZ$d)dZ%d)dZ&efd*dZ'd+dZ(e)dk(  r e(        yy),zGScan save-selector script streams for selection-buffer readers/writers.    )annotationsN)Counterdefaultdict)Path)find_cns_stringsread_sectionsva_to_offset   out    c                "    | rt        | d      S d S )N   intvalues    F/home/exedev/hwanse/tools/summarize_save_selector_selection_writers.py	parse_hexr      s    "3ub>,,    c                    d| dS )N0x08x r   s    r   hex32r          c{r   c                    d| dS )Nr   02xr   r   s    r   hex8r      r   r   c                v    t        ||      }||dz   t        |       kD  ry t        j                  d| |      d   S )N   <Ir   )r	   lenstructunpack_from)exesectionsvaoffsets       r   dword_atr)   !   s?    (B'F~!c#h.dC033r   c                >    | D ]  }|d   dk(  s|c S  t        d      )Nname.dataz Hwanse2.exe has no .data section)
ValueError)r&   sections     r   data_sectionr/   (   s0     6?g%N 7
88r   c                    | dz  }|dvry | dz	  dz  }| dz	  dz  }|dk(  rdnd}|dk(  rd	d
|| dt        |       ddS dd|d|dk(  rdnd|dk(  rdt        |       ddS dt        |       ddS )N   >               r   r   primaryBranchStatesecondaryBranchStater2   reader0x11z[selectionBuffer[z]] == 1)	operation	opcodeHex
stateTablemeaningwriterr   r   r3   zactive branch-state tablezruntime slot matchzselectionBuffer[z%] = selected active branch-state slotz] = matching runtime slot)r   )r   opcodestream_plus_1stream_plus_2state_tables        r   stream_operationrC   /   s    T\F''aZ4'Mb[D(M*71*<&BXK~!%%&7]8K7LGT	
 	
 &&5;t^1I] ~ tM233XY	 	 $D$7#88QR	 	r   c           
        i }| D ]  }|j                  d      xs g }|st        |d         }|-|j                  |t        |      g g g g d      }|j                  d      }|r||d   vr|d   j	                  |       |j                  d      }|r||d   vr|d   j	                  |       |j                  d      s|j                  d	      s|j                  d      |j                  d	      |j                  d
      d}	|	|d   vs
|d   j	                  |	         |D ]  }
t        |
j                  d            }| |j                  |t        |      g g g g d      }|
j                  d      |
j                  d      |
j                  d      d}||d   vr|d   j	                  |       |
j                  d      xs g D ]  }	|	|d   vs|d   j	                  |	          t        |      }||fS )NpathHex)leafPointerHexlabels	resourcesfieldRecordsfrontierPairslabelrH   resourcerI   sceneMatchedfilename
sceneIdHexmaprO   rP   rJ   rG   sourcetargetselector)rS   rT   rU   rK   )getr   
setdefaultr   appendsorted)selector_referencesleaf_streamscontextsrefpathleafcontextrL   rM   recordstreampairleavess                r   build_leaf_indexre   I   s4    !#H" 7wwy!'Rb"<%%"'+ "!#	
  U'("33H$$U+77:&(<<K ''177>"swwz':wwz*GGJ/!ggl3F
 W^44'..v6=7@  7$456<%%"'+ "!#	
 jj*jj*

:.

 w//O$++D1jj06B 	7FW^44'..v6	7+70 HF8r   c           	        i }| D ]  }|j                  d      }t        |t              s&|j                  |t	        |      g g g d      }|j                  d      }|r||d   vr|d   j                  |       |j                  d      }|r||d   vr|d   j                  |       |j                  d      dk(  s|j                  d	      s|j                  d      |j                  d	      |j                  d
      d}||d   vs|d   j                  |        t        |      }t        |      D ]F  \  }	}|	dz   t        |      k  r||	dz      nd }
t	        |      ||   d<   |
t	        |
      nd ||   d<   H ||fS )NselectedPointer)selectedPointerHexrH   rI   rJ   rL   rH   rM   rI   kindfieldMaprO   rP   rQ   rJ   r
   rangeStartHexrangeEndHex)	rV   
isinstancer   rW   r   rX   rY   	enumerater"   )rZ   r\   r]   rootr`   rL   rM   ra   rootsindex	next_roots              r   build_root_indexrs      s    "H" 7ww()$$%%&+Dk "	
  U'("33H$$U+77:&(<<K ''1776?j(SWWZ-@wwz*GGJ/!ggl3F
 W^44'..v6576 8E ' \t(-	CJ(>E%!)$D	*/+'<E<Qi(8W[}%\ (?r   c                    t        j                  ||       dz
  }|dk  ry ||   }| |z
  }|dk  s||kD  ry t        ||         }d|d|d<   ||d<   |S Nr
   r   z+0x03xrelativeOffsetHexrelativeOffset)bisectbisect_rightdict)r'   rd   r\   max_distancerq   r_   distancer`   s           r   nearest_leaf_contextr~      sx    +a/Eqy%=DDyH!|x,.8D>"G%(##7G  (GNr   c                    t        j                  ||       dz
  }|dk  ry ||   }|dz   t        |      k  r||dz      nd }|| |k\  ry t        ||         }d| |z
  d|d<   | |z
  |d<   |S ru   )ry   rz   r"   r{   )r'   rp   r\   rq   ro   rr   r`   s          r   root_context_for_var      s    r*Q.Eqy<D$)AIE
$:eai Iy8D>"G%(d3#8G  "T	GNr   c                j   t        t              }| D ]  }|j                  d      xs g D ]  }t        |j                  d            }| ||   j	                  |j                  d      |j                  d      |j                  d      |j                  d      |j                  d      d         |S )	NbranchStepsstreamVaHexrS   rT   	conditionfallthroughVaHexbranchTargetHex)rS   rT   r   r   r   )r   listrV   r   rX   )frontier_brancheslookupedgebranchr'   s        r   frontier_branch_lookupr      s    $/$5F! hh}-3 
	F6::m45Bz2J((8,((8,#ZZ4$*JJ/A$B#)::.?#@ 	
	 Mr   c                B   t        |      }t        | |      }t        ||      \  }}	t        |      \  }
}t	        |      }t               }g }|d   }|d   |d   z   dz
  }t        ||d      D ]9  }t        j                  d| |      d   }t        |      }|s,|d   |z   |d   z
  }|dz	  d	z  }|d
z	  d	z  }||d   ||d   fxx   dz  cc<   |t        k7  rjt        | ||dz         }t        |      t        |      |d   |d   t        |      t        |      ||d   |d   |t        |      nd ||j                  |      nd t        |||	|      t!        ||
|      |j                  |g       d}|d   dk(  r|t        |      |d<   ||v rd||    |d<   |j#                  |       < |D cg c]  }|d   dk(  s| }}|D cg c]  }|d   dk(  s| }}t%        |j'                         d       D cg c]  \  \  }}}}|t        |      |||d }}}}}g } |D ]%  }|j                  d      s| j#                  |       ' | D ch c]7  }|j                  d      r$|j                  d      xs i j                  d      9 }!}|!j)                  d        |D cg c]*  }|j                  d      xs i j                  d      |!v r|, }"}t+        d | D        d       }#|"D cg c],  }|j                  d      dk(  r|#t-        |d   d
      |#k  r|. }$}dd d!d"d#d$d%t        t              d&|d'||t/        |      t/        |      | t%        |!      |"t1        d( |"D              t1        d) |"D              |$d*S c c}w c c}w c c}}}}w c c}w c c}w c c}w )+Nrawraw_size   r    r!   r   r'   r5   r1   r   r:   r<   r
   r;   r=   )vaHexvalueHexr:   r;   streamPlus1HexselectionBufferOffsetHexselectionBufferOffsetr<   r=   nextDwordHexnextDwordCnsselectorContextselectorRootContextfrontierBranchesr8   r   zcns:branchTargetKindr>   c                0    | d   d   | d   d   | d   d   fS )Nr   r
      r   )items    r   <lambda>z!scan_operations.<locals>.<lambda>  s%    d1gaj$q'!*d1gajA r   )key)r:   r   r   r<   countr   r   rh   c              3  :   K   | ]  }t        |d    d        yw)r   r   Nr   .0rows     r   	<genexpr>z"scan_operations.<locals>.<genexpr>0  s     ;3S\2	;s   )defaultr   r,   dword
0x0040b4e6
0x0040b55f
0x0040b696)r9   0x120x13
0x000000a8)contextFieldHexinterestingOffsetHex)r.   	alignmentopcodeHandlersselectionBuffercontextWindowBytesc              3  2   K   | ]  }|d    dk(  sd  yw)r:   r>   r
   Nr   r   s     r   r   z"scan_operations.<locals>.<genexpr>Q       4t3WZ[fWgksWsQ4t   c              3  2   K   | ]  }|d    dk(  sd  yw)r:   r8   r
   Nr   r   s     r   r   z"scan_operations.<locals>.<genexpr>R  r   r   )scancountsByOffsetinterestingOffsetRowswriterCountFor0x20readerCountFor0x20currentFrontierRowscurrentFrontierRootHexescurrentFrontierRootRows%currentFrontierRootWriterCountFor0x20%currentFrontierRootReaderCountFor0x20+currentFrontierRootWritersBeforeFirstReader)r/   r   re   rs   r   r   ranger#   r$   rC   INTERESTING_OFFSETr)   r   r   rV   r~   r   rX   rY   itemsdiscardminr   r"   sum)%r%   r&   rZ   r[   r   context_windowr.   stringsrd   r\   rp   root_contextsfrontier_lookupcountsinteresting_rowsstartendr(   r   opr'   r@   rA   
next_valuer   writer_rowsreader_rowsr:   selection_offsetrB   r   counts_by_offsetcurrent_frontiercurrent_root_hexescurrent_root_rowsfirst_frontier_va$current_root_writers_before_frontiers%                                        r   scan_operationsr      s    8$GsH-G'(;\JFH+,?@E=,->?O,3IFENE
%.7:.
.
2CsA& !%""4f5a8e$T]V#gen4!t+",;<0@ABaGB..c8R!V4
2YeKK"=1(,](;%2\*)}1;1GE*-T7A7MGKK
3SW3B.Y#6r5-#P / 3 3B ;
  k?h&:+A%*:%6C!"W$,01D0E*F&'$C!%F #3S3c+6F(6R3SKS"2S3c+6F(6R3SKS BHLLNAB
  >6Y(+ #(,-=(>%5%	
   )77%&##C()
 $77() 
&	'	-2223GH 
 t$ $GG)*0b556JKOaa 	 
 ;*:; %,77;8+)Gb!$55	 	,( ,  $$$ $0(,-?(@  #1
 +!1!+.!+./$*+=$>#4144tBS4t1t144tBS4t1t7[1 U TS",s0   -N ;N NN:N

<N!/N*1Nc           	     @   | j                  d      xs i }g }|j                  d      r)|j                  ddj                  |d   d d       z          |j                  d      r)|j                  ddj                  |d   d d       z          |j                  d      xs g }|r-|j                  d	dj                  d
 |d d D              z          |j                  d      r*|j                  d|j                  dd      z   |d   z          dj                  |      xs dS )Nr   rH   labels=,   rI   z
resources=rJ   fields=c              3  P   K   | ]  }t        |j                  d                ywrR   NstrrV   r   r   s     r   r   zrow_context.<locals>.<genexpr>`  s     )V4#dhhuo*>)V   $&r    rw   zleaf rG   -; )rV   rX   join)r   r`   partsfieldss       r   row_contextr   W  s   gg'(.BGE{{8Y'(*;BQ*?!@@A{{;\CHHW[-A"1-E$FFG[[(.BFY)V6RTST:)V!VVW{{&'Ww{{+;SAAGL_D``a99U"s"r   c           	        | j                  d      xs i }|sy|j                  d      xs g }d|j                  dd      z   |j                  d      r'ddj                  |j                  d      xs g       z   nd	|rd
dj                  d |d d D              z   nd	|j                  dd	      g}dj                  d |D              xs dS )Nr   r   rJ   zroot=rh   rH   r   r    r   c              3  P   K   | ]  }t        |j                  d                ywr   r   r   s     r   r   z#row_root_context.<locals>.<genexpr>n  s     IdS%1Ir      rw   r   c              3  &   K   | ]	  }|s|  y w)Nr   )r   parts     r   r   z#row_root_context.<locals>.<genexpr>q  s     4dtT4s   )rV   r   )r   r`   r   r   s       r   row_root_contextr   f  s    gg+,2G[[(.BF'++2C88=D[[=R	CHHW[[28b99XZMS	CHHIfRajIIIY[',	E 994e44;;r   c                   | d   }ddddd|d   d    dd	|d   d
    dd|d   d    dd|d   d    dd| d    dd| d    dd| j                  dd       ddt        | j                  d      xs g        ddddddg}| j                  d      xs g D ]j  }dj                  d  |j                  d!      xs g D              xs d"}|j                  d#|d$    d%|d&    d%|d'    d%|d(    d%| d%t	        |       d)       l | j                  d      s|j                  d*       |j                  g d+       | j                  d,      xs g D ]D  }|j                  d#|d$    d%|d&    d-|d.    d%|d'    d%t        |       d%t	        |       d)       F | j                  d,      s|j                  d*       | d/   D cg c]  }|d.   d0k(  s| }}|j                  g d1       |d d2 D ]2  }|j                  d#|d$    d%|d'    d%|d3    d%t	        |       d)	       4 |s|j                  d4       n/t        |      d2kD  r!|j                  d5t        |      d2z
   d6       |j                  g d7       | d8   D ]A  }|d9   d:kD  r|d9   t        k7  r|j                  d#|d.    d%|d;    d%|d<    d%|d=    d)	       C |j                  d       d>j                  |      S c c}w )?Nr   z!# Save Selector Selection Writersr   zcDword-aligned scan for script opcodes that read/write `context+0xa8` selection-buffer byte offsets.z!- opcode `0x11`: reader handler `r   r9   z`.z!- opcode `0x12`: writer handler `r   z.- opcode `0x13`: runtime-slot writer handler `r   z!- focus offset: `selectionBuffer[r   r   z]`.z - writer candidates for `0x20`: r   .z - reader candidates for `0x20`: r   z,- current selector-root writers for `0x20`: r   r   z>- current selector-root writers before first frontier reader: r   z## Current Frontierz0| va | op | value | meaning | branch | context |z%| --- | --- | --- | --- | --- | --- |r   r   c              3     K   | ];  }|j                  d        d|j                  d       d|j                  d        = ywrS   z->rT    r   N)rV   r   s     r   r   zmarkdown.<locals>.<genexpr>  sG      
 xx!""TXXh%7$8$((;:O9PQ
s   AAr   r   z| r   z | r;   r   r=   z |z| - | - | - | - | - |)r   z(## Current Selector Root 0x20 Operationsr   z1| va | op | value | root context | leaf context |z| --- | --- | --- | --- | --- |r   r   r:   r   r>   )r   z## 0x20 Writersr   z#| va | value | stream+1 | context || --- | --- | --- | --- |x   r   z| - | - | - | - |z| ... | ... | ... | z( additional rows omitted from markdown |)r   z## Counts By Offsetr   z&| operation | offset | table | count |r   r   r   0   r   r<   r   
)rV   r"   r   rX   r   extendr   r   )summaryr   linesr   branchesr   r   s          r   markdownr  t  s
   6?D+
m

+D1A,B6,J+K2N
+D1A,B6,J+K2N
8>N9OPV9W8XXZ[
+D1B,CDZ,[+\\_`
*73G+H*IK
*73G+H*IK
6w{{Cjlm7n6oopq
HW[[  ZG  NH  NN  LN  JO  IP  PQ  	R

:/#E& {{017R 
99 
 239r
 
   	 	Wc#k"2!33s:6Gs9~c(3{3/?.@D	

 ;;,-,-	LL   {{45; 
Wc#k"2!31S5E4Fc#j/IZZ]$%SS)9(:">	


 ;;01,-")*A"Bc3c+FVZbFb3cKc	LL   4C  
Wc#j/!2#c:J6K5LCP[\_P`Oaacd	

 ()	[	C	+C,<s,B+CCklm	LL   () 
'(4/D9P4QUg4gk"#3t,F'G&HDQ]L^K__bcghocpbqqst	

 
LL99U? ds   ?K!K!c                   d8d}| d   D cg c]  }|d   dk(  s| }}| d   D cg c]  }|d   dk(  s| }}dj                  d | d   D              }dj                  g d	d
ddddddddddddddddd| d    d| d    dd | j                  d!d"       d#t        | j                  d$      xs g        dd%d&d' || j                  d(      xs g       xs d) d*d+d&d' || j                  d,      xs g       xs d- d*d.d&d' ||d d/       xs d0 d*d1d&d' ||d d/       xs d2 d*d3d4d'| d*d5d6d7      S c c}w c c}w )9Nc                8   g }| D ]  }dj                  d |j                  d      xs g D              xs d}|j                  dt        j                  |d          dt        j                  |d          dt        j                  |d	          d
t        j                  |d          dt        j                  |d          d| dt        j                  t        |             dt        j                  t        |             d        dj                  |      S )Nz<br>c           
   3     K   | ]N  }t        j                  |j                  d        d|j                  d       d|j                  d              P ywr   )htmlescaperV   r   s     r   r   z0html_page.<locals>.table_rows.<locals>.<genexpr>  sQ      # txx12"TXXh5G4H$((S^J_I`ab#s   AAr   r   z<tr><td><code>r   z</code></td><td><code>r;   r   </code></td><td>r:   	</td><td>r=   
</td></tr>r   )r   rV   rX   r  r  r   r   )rowsrenderedr   r  s       r   
table_rowszhtml_page.<locals>.table_rows  s-    	C{{ #GG$67=2#     OO![[W67 8![[[)9:; <![[Z9: ;{{3{#345 6{{3y>23 4j !{{#3C#89: ;{{;s#345 6		" yy""r   r   r:   r>   r8   r   c           
   3     K   | ]l  }|d    dk  s|d    t         k(  rVdt        j                  |d          dt        j                  |d          dt        j                  |d          d|d	    d
	 n yw)r   r   z<tr><td>r:   z</td><td><code>r   r	  r<   r
  r   r  N)r   r  r  r   s     r   r   zhtml_page.<locals>.<genexpr>  s      	 '(D0D9P4QUg4g	{{4,-. /[[&@!ABC D{{4-./ 0G}o 			s   A2A4r   z<!doctype html>z<html lang="en">z<head>z  <meta charset="utf-8">zF  <meta name="viewport" content="width=device-width, initial-scale=1">z0  <title>Save Selector Selection Writers</title>z	  <style>z^    body { margin: 24px; background: #101010; color: #eee; font: 14px system-ui, sans-serif; }zJ    table { border-collapse: collapse; width: 100%; margin: 16px 0 28px; }zM    th, td { border: 1px solid #333; padding: 6px 8px; vertical-align: top; }z9    th { background: #1d1d1d; position: sticky; top: 0; }z    code { color: #f5d76e; }z
  </style>z</head>z<body>z*  <h1>Save Selector Selection Writers</h1>zw  <p>Dword-aligned scan for script opcodes that read/write <code>context+0xa8</code> selection-buffer byte offsets.</p>z  <p>Handlers: <code>0x11</code> reader <code>0x0040b4e6</code>; <code>0x12</code> writer <code>0x0040b55f</code>; <code>0x13</code> runtime-slot writer <code>0x0040b696</code>.</p>z9  <p>Focus: <code>selectionBuffer[0x20]</code>. Writers: r   z. Readers: r   z.</p>z$  <p>Current selector-root writers: r   r   z(. Writers before first frontier reader: r   z  <h2>Current Frontier</h2>z  <table><thead><tr><th>va</th><th>op</th><th>value</th><th>kind</th><th>meaning</th><th>branch</th><th>root context</th><th>leaf context</th></tr></thead>z	  <tbody>r   z/<tr><td colspan="8">No frontier rows.</td></tr>z</tbody></table>z0  <h2>Current Selector Root 0x20 Operations</h2>r   z4<tr><td colspan="8">No selector-root rows.</td></tr>z  <h2>0x20 Writers</h2>i,  z-<tr><td colspan="8">No writer rows.</td></tr>z  <h2>0x20 Readers</h2>z-<tr><td colspan="8">No reader rows.</td></tr>z  <h2>Counts By Offset</h2>z^  <table><thead><tr><th>operation</th><th>offset</th><th>table</th><th>count</th></tr></thead>z</body>z</html>r   )r  
list[dict]returnr   )r   rV   r"   )r   r  r   r   r   
count_rowss         r   	html_pager    st   #* #**A"Bc3c+FVZbFb3cKc")*A"Bc3c+FVZbFb3cKc 	 ,-	 	J 99 ''' 	' 	#	'
 	Q' 	;' 	' 	i' 	U' 	X' 	D' 	'' 	' 	' 	'  	5!'" 	B#'$ 	@%'& DGL`DaCbbmnu  wK  oL  nM  MR  	S''( /w{{;bde/f.g  hP  QT  U\  U`  U`  aN  UO  UU  SU  QV  PW  W\  	])'* 	&+', 	f-'. Jw{{+@AGRHL  A  AQ  	R/'0 	;1'2 	f3'4 Jw{{+DEKL  I  QI  J  JZ  	[5'6 	"7'8 	f9': J{4C01f5fggwx;'< 	"='> 	f?'@ J{4C01f5fggwxA'B 	&C'D 	iE'F J</0G'H 	I'J 	K'L 	M' ' ' dcs   EEE E c                    |j                  dd       |dz  j                  t        j                  | dd      dz   d	       y )
NT)parentsexist_ok$save_selector_selection_writers.jsonF)r   :)ensure_ascii
separatorsr   utf-8encoding)mkdir
write_textjsondumps)r   out_dirs     r   write_outputsr#    sD    MM$M.55AA

7:FM B r   c                    t        j                  t              } | j                  dt        t
        dz         | j                  dt        t        dz         | j                  dt        t        dz         | j                  d	t        t        d
z         | j                  dt        t               | j                  dt        d       | j                         }|j                  j                         }t        |t        |      t        j                  |j                  j!                  d            t        j                  |j"                  j!                  d            t        j                  |j$                  j!                  d            |j&                        }t)        ||j*                         t-        d|d    d|d    d|j*                  dz          y )N)descriptionz--exezHwanse2.exe)typer   z--selector-referencesz#save_scene_selector_references.jsonz--leaf-streamszsave_selector_leaf_streams.jsonz--frontier-branchesz$save_selector_frontier_branches.jsonz	--out-dirz--context-windowi   r  r  zwrote selection writer scan (r   z
 writers, r   z readers for 0x20) -> r  )argparseArgumentParser__doc__add_argumentr   ROOTOUTr   
parse_argsr%   
read_bytesr   r   r   loadsrZ   	read_textr[   r   r   r#  r"  print)parserargsr%   r   s       r   mainr4    s   $$9F
dD=4HI
/dCJoDop
(tSCd=de
-D#HnBno
$<
*eDD
((


Cc

4++55w5GH

4$$...@A

4))33W3EFG '4<<(		()**W=Q5R4S TllCCD	Fr   __main__)r   z
str | Noner  
int | None)r   r   r  r   )r%   bytesr&   r  r'   r   r  r6  )r&   r  r  r{   )r   r   r  dict | None)rZ   r  r[   r  r  !tuple[list[int], dict[int, dict]])rZ   r  r  r9  )
r'   r   rd   	list[int]r\   dict[int, dict]r|   r   r  r8  )r'   r   rp   r:  r\   r;  r  r8  )r   r  r  zdict[int, list[dict]])r%   r7  r&   r  rZ   r  r[   r  r   r  r   r   r  r{   )r   r{   r  r   )r   r{   r  r   )r   r{   r"  r   r  None)r  r<  )*r)  
__future__r   r'  ry   r  r   r#   collectionsr   r   pathlibr   probe_exe_scene_tablesr   r   r	   __file__resolver  r+  r,  r   r   r   r   r)   r/   rC   re   rs   r~   r   r   r   r   r   r  r  r#  r4  __name__r   r   r   <module>rD     s2   M "      ,  P P H~''*
Ul -494>#>> '>B"J"x	xx $x 	x
 "x x 
xv#<OdIX 25 4 zF r   