
    NjiT                       d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	  ee
      j                         j                  d   Zedz  Zedz  Zd	Zd
Zeez   ZdZdZdZdZdZg dZdZdZddddddddddddddddgdd d!d"d#d$d%d"d&dd'dgd(dd)d*d+d,d-d.d,gd/ZdDd0ZdEd1ZdEd2ZdFd3ZdGd4Z dGd5Z!dHd6Z"dId7Z#dJd8Z$dKd9Z%dLd:Z&dMd;Z'dNd<Z(dOd=Z)dPd>Z*dQd?Z+dRd@Z,dSdAZ-dTdBZ.e/dCk(  r e.        yy)Uat  Export the recovered monster HP-view stat table.

The table is grounded by the monster HP dword and a +0x34 boundary marker in
this exported HP-view.  The actor initializer uses an 8-byte wider row prefix;
see battle_actor_stat_layout_review for the actor struct mapping.  The monster
level lives in that prefix at actual row +0x06, which is HP-view -0x02 in this
export.
    )annotationsN)Path)Any   outHwanse2.exei  @ iH] 8   i  iN i N i N )	         
                     (   HP-view -0x02 / actual +0x06u16level)	offsetHexwidthlabel+0x00u32HP+0x2egold+0x34u16-byte-swappedzname id boundary marker+0x04..+0x14zu16 x9z$front numeric battle-stat candidates)offsetRangeHexr   r   +0x16..+0x27zu8 x18z%rate/resistance-style byte candidates+0x36z+next-row prefix level; not current-row dataz+0x02)r   r   z+0x28..+0x2czu16 x3)r#   r   z+0x30..+0x32zu16 x2)	confirmed	candidatereservedZeroc                    | t         z
  S )N)IMAGE_BASE_DELTA)vas     tools/export_enemy_stat_table.pyva_to_file_offsetr-   D   s           c                :    t         j                  | ||dz    d      S )N   littleint
from_bytesdataoffsets     r,   read_u16r8   H       >>$vfqj18<<r.   c                :    t         j                  | ||dz    d      S )Nr
   r1   r2   r5   s     r,   read_u32r;   L   r9   r.   c                    | dz  dz  | dz	  z  S )N   r    values    r,   swap_u16rA   P   s    T\aEQJ//r.   c                    d| dS )N0x08xr>   r?   s    r,   hex_varE   T       c{r.   c                    d| dS )NrC   06xr>   r?   s    r,   hex_offrI   X   rF   r.   c                B    | j                  dd      j                         S )Nu   　 )replacestripr?   s    r,   
clean_namerN   \   s    ==2&,,..r.   c                   t        |      }|dk  s|t        |       k\  ry| ||dz    }t        |      }dD ]%  }|j                  |      }|dk\  st        ||      }' 	 |d | j	                         j                  d      S # t        $ r Y yw xY w)Nr   rK   @   )   @    cp949)r-   lenfindminrM   decodeUnicodeDecodeError)r6   r+   r7   rawstopmarkerindexs          r,   read_marker_stringr]   `   s    r"FzVs4y(
vfrk
"Cs8D $ A:tU#D$5Dz!((11 s   !B   	BBc                   g }t        t              }d}||dz  z   dz   t        |       k  rt        | ||dz  z         }t        |cxk  rt
        k  s	 |S  	 |S t        | |      }|rt        d |D              s	 |S t        |z   }|j                  ||d|dt        |dz  z   t        t        |dz  z         |t        |      |t        |      d	       |dz  }||dz  z   dz   t        |       k  r|S )Nr   r
   c              3  <   K   | ]  }d |cxk  xr dk  nc   yw)u   가u   힣Nr>   ).0chars     r,   	<genexpr>z*read_name_pointer_table.<locals>.<genexpr>y   s     Mx4;8;;Ms   rC   04x)	r\   nameId	nameIdHexpointerTableVapointerTableVaHexnameVa	nameVaHexname	cleanNamer   )r-   NAME_POINTER_TABLE_VArT   r;   NAME_POINTER_MIN_VANAME_POINTER_MAX_VAr]   anyNAME_ID_BASEappendrE   rN   )r6   rowstable_offsetr\   ptrrj   name_ids          r,   read_name_pointer_tablerv   p   s&   !#D$%:;LE

"Q
&#d)
3t\EAI56#s@-@@" K% A" K! "$,3MMM K &gc]+3eai?!'(=	(I!J#D)

 
	 	
' 
"Q
&#d)
3( Kr.   c           	         g }t        dt        d      D ]/  }t        | ||z         }|j                  |d|d|d|dd       1 |S )Nr   r0   +0x02xrC   rc   )r7   r   r@   hex)rangeSTAT_ROW_SIZEr8   rq   )r6   r7   wordsword_offsetr@   s        r,   
stat_wordsr      sg    EQq1 v34!{3/0c{#	
 	 Lr.   c                   | ||t         z    }t        D cg c]6  }|d|dd|dd|dt        | ||z         dt        | ||z         ddd8 }}t        t        t
              D cg c]0  }|d|dd	|t        z
  d
d|t        z
  d
||   d||   ddd2 }}t        | |dz         }t        | |dz
        }ddd|ddddt        | |      ddddt        | |dz         ddddt        t        | |dz               dt        | |dz         ddg||dddd |d|dd!dd"j                  d# |D              d"j                  d$ |D              d%d&S c c}w c c}w )'Nrx   ry   u16_zu16 +0xrC   rc   zcandidate-front-numeric-field)r7   r   keyr   r@   rz   statusrate_02dz
rate byte z!candidate-rate-or-resistance-byte6   r0   r   r   r   )r   r   r   r@   r   r   r   r   r   .   zname idr    r!   4   )r   r   r   r@   rawHexr%   u16_36znext-row prefix levelz-next-row-boundary-field-not-current-row-level c              3  :   K   | ]  }d |d   dd|d      yw)+r7   ry   =r@   Nr>   r`   rows     r,   rb   z"field_breakdown.<locals>.<genexpr>   s)     'gSV!CM#+>aG~(N'gs   c              3  (   K   | ]
  }|d       yw)r@   Nr>   r   s     r,   rb   z"field_breakdown.<locals>.<genexpr>   s     #L#s7|n$5#Ls   z:structured-candidate-fields-not-promoted-to-formula-labels)confirmedFieldsfrontNumericFieldsrateByteFieldstailCandidateFieldfrontNumericSummaryrateByteSummarycandidateFieldStatus)	r|   FRONT_NUMERIC_FIELD_OFFSETSr8   r{   RATE_BYTE_STARTRATE_BYTE_ENDr;   rA   join)r6   r7   blobfield_offsetfront_fields
rate_bytes
tail_valuer   s           r,   field_breakdownr      s   v-.D 8  #|C01,s+,|C01dF\$9:v'<=cBC5	
L , "/=A  #|C01</9#>?!,"@!EF,'\*3/09	
J  $.JT6A:&E !;	 58TXZ`Kab7UXVZ\bei\iMjk"$+!(4$"?@xftm<SAB
" +$ ,
3'(E
  #xx'gZf'gg88#L#LL \?   5s   ;E(5Ec                   |D ci c]  }t        |d         | }}g }d}t        |dz   t        z  z   t        |       k  rt        |t        z  z   }|dz
  }t	        | |dz         }t        |      }	t        |z   }
|	|
k7  rn|j                  |	      }|snt        | |      }t        | |dz         }t	        | |dz
        }| |dz
     }| |d	z
     }t        | |      }t        | |      }|j                  i d
|d|dt        |      d|t        z   dt        |t        z         d|dd|dd|dd|dd|dt        |      d|t        z   dt        |t        z         dt        ddt        dd|d   d|d   i 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t	        | |d.z         dd/t	        | |d.z         d0dt	        | |d.z         dd1d2|d3j                  d4 |D              d5|       |dz  }t        |dz   t        z  z   t        |       k  ri }|D ]0  }t!        |d         }|j                  |d      dz   ||<   ||   |d6<   2 i }|D ](  }t!        |d         }|j                  |d      dz   ||<   * |D ]  }|t!        |d            |d7<    |S c c}w )8Nrd   r   r   r   r   r   r0   r
      r\   actorPrefixFileOffsetactorPrefixFileOffsetHexactorPrefixVaactorPrefixVaHexactorKindByteactorKindByteHexrC   ry   actorSubtypeByteactorSubtypeByteHex
fileOffsetfileOffsetHexr+   vaHexrowSize
rowSizeHexrj   rk   re   rc   	rawNameIdrawNameIdHexrh   ri   namePointerTableVarf   namePointerTableVaHexrg   hpr   r   levelHexlevelSourcezlactual stat row +0x06 / exported HP-view -0x02; copied to actor +0x06 and consumed by EXP formula 0x0042157b	field2Hexr   !hpViewPlus36NextRowLevelCandidate$hpViewPlus36NextRowLevelCandidateHexhpViewPlus36MeaningzNnext actual row prefix +0x06 level; shifted by one row, not this monster levelr   c              3  ,   K   | ]  }|d    dd   ywrz   r0   Nr>   r`   words     r,   rb   z!read_stat_rows.<locals>.<genexpr>  s     BDUABB   )r}   wordHexnameOccurrencenameDuplicateCount)r3   STAT_TABLE_FILE_OFFSETr|   rT   r8   rA   rp   getr;   r   r   rq   rI   r*   rE   r   str)r6   namesr   
name_by_idrr   r\   r7   actor_prefix_offsetraw_name_idru   expected_idname_rowr   r   r   actor_kind_byteactor_subtype_byter}   	breakdownoccurrence_countsr   total_by_names                         r,   read_stat_rowsr      s   5:;c#c(m$c);J;!#DE
 EAI#>
>#d)
K'%-*??$qjtVd]3;'"U*k!>>'*dF#ftm,vz*vz*!&1*-4(#D&1	 &
U&
#%8&
 '0C(D&
 03CC	&

 ':=M'M N&
 _&
 "_S$9 :&
  2&
 "R(:3'?#@&
 &&
 WV_&
 &++&
 VF%556&
 }&
 B}S12&
  HV$!&
" +.#&
$ g%&
& 2gc]+'&
( )&
* bS 12+&
, hx(-&
. +./&
0 !(+;"<1&
2 $X.A%B3&
4 "5&
6 D7&
8 U9&
: "U3K(;&
<   J=&
> 2htVd];C@A?&
@ 0$1NA&
B 3b$QU9VWZ8[4\C&
D "#sE&
F xxBEBBI&
J K&
 &	N 	
s !EAI#>
>#d)
Kt )+ 7#k"#!2!6!6sA!>!B# 1# 67 %'M ;#k"#*..sA6:c;  I$1#c+6F2G$H !IKQ <s   Kc           	     B   t         |t        z  z   }|t        z   t        |       kD  ry t        | |      }d|cxk  rdk  sy  y t	        | |dz         }t        |      }t        | |      }i ddddd|d	|d
t        |      d|t        z   dt        |t        z         d|d   d|d   d|d   d|d   d|dd|ddt	        | |dz         ddt	        | |dz         dd|dd|d|d|ddt	        | |dz         d|dj                  d |D              dS )Nr   i'  r   r   ztrailing-raw-block-candidatereasonzThe block starts where the next 0x38 stat row would start and has a plausible HP-like first value, but it lacks the row-local byte-swapped name id at +0x34.r\   r   r   r+   r   rj   rk   rd   re   hpCandidatehpCandidateHexrC   rD   u16At2er   
u16At2eHexrc   rawNameIdAt34rawNameIdAt34Hexr   r   c              3  ,   K   | ]  }|d    dd   ywr   r>   r   s     r,   rb   z+trailing_candidate_block.<locals>.<genexpr>C  s     >DKO>r   )swappedNameIdAt34swappedNameIdAt34Hex
field36Hexr}   r   )r   r|   rT   r;   r8   rA   r   rI   r*   rE   r   )r6   r\   r   r7   r   r   swapped_name_idr}   s           r,   trailing_candidate_blockr   #  s   #em&;;FD	)	$	Bu 4$/K{+OtV$E0  q 	 	f	
 	 	f'' 	!112 	  	Xk* 	(8$ 	Xk* 	r 	Br#h- 	8D&4-0 	8D&4-8=>  	!" 	bS 12#$ -"$_S$9 :8D&4-8=>88>>>- r.   c                l    t        |      D ]&  \  }}t        | t        |      |z   |      }|s"||d<   ( y )NtrailingCandidateBlock)	enumerater   rT   )r6   rr   	name_onlyoffset_indexr   r'   s         r,   attach_name_only_candidatesr   G  s@    &y1 6c,T3t9|3KSQ	,5C()6r.   c                   t        |       }t        | |      }|t        |      d  }t        | ||       t	        |D ch c]  }|j                  dd      dkD  r|d    c}      }i ddddgd	d
dt        t        t              t        t        t              t        dt        dt        dt        dt        t        t              d
dt        |      dt        |      dt        |      d|dt        dddt        t              dt        t         z
  d|d|d|dt        |      dk(  t        |      dk(  t        dk(  d d!d"d#d$d%d&|r|d   d   nd'|r|d(   d   nd'|D cg c]  }|d   	 c}|D cg c],  }|j                  d)      r|d   |d*   |d)   d+   |d)   d,   d-. c}d.d/g d0S c c}w c c}w c c}w )1Nr   r   r   rk   scopezmMonster stat HP-view rows recovered from Hwanse2.exe by HP/gold fields and the +0x34 name-id boundary marker.sourcer   promotionStatusz&enemy-stat-table-name-hp-gold-groundedtablerC   ry   rc   )
statTableFileOffsetstatTableFileOffsetHexstatTableVastatTableVaHexr   r   
nameIdBasenameIdBaseHexr   r   rowCountnamePointerCountnameOnlyCountduplicateNamesfieldLayoutconfirmedFieldCountPerRowr
   %candidateFrontNumericFieldCountPerRow!candidateRateByteFieldCountPerRowrr   namePointersnameOnlyEntrieschecksP   Q   r	   r   z*HP-view -0x02 / actual +0x06 / actor +0x06r   r    Tr"   r$   rK   r   re   r   r   )rj   re   r   r   )continuousStatRowsRecoverednamePointersRecoveredrowStrideIs0x38hpOffsetHexlevelOffsetHexgoldOffsetHexnameIdOffsetHex hpViewPlus36IsNextRowPrefixLevelcandidateFrontNumericRangeHexcandidateRateByteRangeHexfirstRowNamelastContinuousRowNametrailingNameOnlyEntriestrailingCandidateBlocksnotes)z<HP and gold are grounded from the recovered stat row layout.zIn this exported HP-view, the name id boundary marker is stored byte-swapped at +0x34, e.g. bytes 26 02 mean logical id 0x0226. The actor initializer uses an 8-byte wider row prefix, so +0x34 is not a current actor struct field.zMonster level is recovered from the actor row prefix at actual row +0x06. Because this export starts at actual row +0x08(HP), that is HP-view -0x02.zHP-view +0x36 crosses into the next actual row prefix and reads the next monster's level. It is kept only as a boundary diagnostic and must not be used as the current monster level.u  One trailing name pointer, 폭호 id 0x0255, is present after the continuous stat rows. The next raw 0x38-sized block starts with 2000, matching the known special 폭호 HP variant, but it is kept as a candidate because +0x34 is not the expected byte-swapped name id.zSprite binding, formation, encounter, reward routing, and combat formulas are separate tables and are not promoted by this report.z+0x04..+0x14 and +0x16..+0x27 are structured as candidate numeric/rate fields in this HP-view. Use battle_actor_stat_layout_review to map them to actor offsets.)rv   r   rT   r   sortedr   r   rI   STAT_TABLE_VArE   r|   rp   rl   FIELD_LAYOUTr   r   r   )r6   r   rr   r   r   duplicate_namess         r,   build_summaryr  N  sx   #D)E$&Dc$ij!IdI677'+a/ 	K O
=  A==/= 	C= 	#9&-.D&E($]3$}S12&!,s!34"7%+,A%B
	=  	CI!=" 	CJ#=$ 	Y%=& 	/'=( 	|)=* 	$Q+=, 	05P1Q-=. 	,]_-L/=0 	1=2 	3=4 	95=6 	+.t9?%(Z2%5,4"J$&04-;)748DGK0b>BT"Xk%:DM'NSK(8'N %	( 7734  ,!$[!1 !9:7C#&'?#@#O		(
7=h 	 
i= =Z (O	(s   !F2$F7 61F<"c                2    t        j                  | dd      S )NF,:ensure_ascii
separators)jsondumpsr?   s    r,   js_jsonr    s    ::e%JGGr.   c                   | d   }dj                  d | d   j                         D              }dj                  d | d   D              }dj                  d | d   D              }d	j                  d
ddddddddddt        j                  | d          ddt        j                  |d          dt        j                  |d          d| d    ddd| d d!d"| d d#d$| d d%d&dg      S )'Nr   rK   c           	   3     K   | ]?  \  }}d t        j                  |       dt        j                  t        |             d A yw)<tr><td></td><td><code></code></td></tr>N)htmlescaper   )r`   r   r@   s      r,   rb   zhtml_page.<locals>.<genexpr>  sO      
 C		{{3  ![[U,- .	s   AAr   c              3    K   | ]  }d |d    dt        j                  |d          d|d    d|d    d|d    dt        j                  |d          d	t        j                  |d
          d	t        j                  |d          d	t        j                  |d          d	t        j                  |d          d	t        j                  |d          d  yw)r!  r\   z	</td><td>rk   r   r   r   r"  r   z</code></td><td><code>re   r   r   r   r   r#  N)r$  r%  r   s     r,   rb   zhtml_page.<locals>.<genexpr>  s      	7|n {{3{+,- .7|n 4yk 6{m [[W./ 0[[[!123 4[[%:!;<= >[[%6!789 :[[[!123 4[[Y01 2	s   CCrr   c              3  `  K   | ]  }d t        j                  |d          dt        j                  |d          dt        j                  |d          dt        j                  |j                  d      r-dt        |j                  di       j                  d            z   nd	       d
	  yw)z<tr><td><code>re   z</code></td><td>rk   r"  ri   r   zHP candidate r   rK   z
</td></tr>N)r$  r%  r   r   r   s     r,   rb   zhtml_page.<locals>.<genexpr>  s       	[[[!123 4{{3{+,- .[[[!123 4{{nqnunu  wO  oP?S9QSU1V1Z1Z[h1i-jj  VX  Y  Z Z		s   B,B.r   
z<!doctype html>z<html lang="ko">z<head>z  <meta charset="utf-8">zF  <meta name="viewport" content="width=device-width, initial-scale=1">z!  <title>Enemy Stat Table</title>a@    <style>body{font-family:system-ui,sans-serif;margin:24px;line-height:1.45;max-width:1280px}table{border-collapse:collapse;width:100%;margin:16px 0}td,th{border:1px solid #ddd;padding:6px 8px;text-align:left;vertical-align:top}th{background:#f5f5f5}code{white-space:nowrap}td:last-child code{white-space:normal}</style>z</head>z<body>z  <h1>Enemy Stat Table</h1>z  <p>r   z</p>z  <p>Table <code>r   z</code>, row size <code>r   z</code>, rows r   z.</p>z  <h2>Checks</h2>zD  <table><thead><tr><th>check</th><th>value</th></tr></thead><tbody>z</tbody></table>z  <h2>Rows</h2>z  <table><thead><tr><th>#</th><th>name</th><th>Lv</th><th>HP</th><th>gold</th><th>row VA</th><th>name id</th><th>candidate front fields</th><th>candidate rate bytes</th><th>+0x36 next Lv</th><th>raw words</th></tr></thead><tbody>z  <h2>Name-Only Entries</h2>zg  <table><thead><tr><th>name id</th><th>name</th><th>name VA</th><th>candidate</th></tr></thead><tbody>z</body>z</html>)r   itemsr$  r%  )summaryr   
check_rowsrow_htmlname_only_rowss        r,   	html_pager.    s   GE 
 "(+113 J ww  6? H  WW  ,- N 99"P+ 	K%
GG,-.d3
DKK.>(?@AAYZ^ZeZefklxfyZzY{  |J  KR  S]  K^  J_  _d  	e
NzlZjk p  qy  pz  zJ  	K&
q  sA  rB  BR  	S
+  r.   c                   |j                  dd       |dz  j                  t        j                  | dd      dz   d	       |d
z  j                  t	        |       d	       |dz  j                  dt        | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   | d   d      z   dz   d	       y )NT)parentsexist_okzenemy_stat_table.jsonFr  r  r(  zutf-8)encodingenemy_stat_table.htmlzenemy_stat_table.jsz!window.HWANSE_ENEMY_STAT_TABLE = r   r   r   r   r   r   r   r   r   r   r   r   rr   r   r  )r   r   r   r   r   r   r   r   r   r   r   r   rr   r   r  z;
)mkdir
write_textr  r  r.  r  )r*  out_dirs     r,   write_outputsr7    s   MM$M.&&22

7:FM 3  &&229W3EPW2X$$00+
&'89W%W%
+ '(: ;$_5%&67"=1)01L)M5<=d5e189\1]h'FO&'89W%
 	$ %	& ) 1 r.   c                 t   t        j                  t              } | j                  dt        t
               | j                  dt        t               | j                         }|j                  j                         }t        |      }t        ||j                         t        d|j                  dz          y )N)descriptionz--exe)typedefaultz	--out-dirzwrote enemy stat table -> r3  )argparseArgumentParser__doc__add_argumentr   EXEOUT
parse_argsexe
read_bytesr  r7  r6  print)parserargsr6   r*  s       r,   mainrH    s    $$9F
dC8
$<D88 DD!G'4<<(	&t||6M'M&N
OPr.   __main__)r+   r3   returnr3   )r6   bytesr7   r3   rJ  r3   )r@   r3   rJ  r3   )r@   r3   rJ  r   )r@   r   rJ  r   )r6   rK  r+   r3   rJ  r   )r6   rK  rJ  list[dict[str, Any]])r6   rK  r7   r3   rJ  rL  )r6   rK  r7   r3   rJ  dict[str, Any])r6   rK  r   rL  rJ  rL  )r6   rK  r\   r3   r   rM  rJ  zdict[str, Any] | None)r6   rK  rr   rL  r   rL  rJ  None)r6   rK  rJ  rM  )r@   r   rJ  r   )r*  rM  rJ  r   )r*  rM  r6  r   rJ  rN  )rJ  rN  )0r>  
__future__r   r<  r$  r  pathlibr   typingr   __file__resolver0  ROOTrA  r@  r*   r   r  r|   rp   rl   rm   rn   r   r   r   r  r-   r8   r;   rA   rE   rI   rN   r]   rv   r   r   r   r   r   r  r  r.  r7  rH  __name__r>   r.   r,   <module>rV     s   #      H~''*
Ul
] # &)99"     T  5uwW=?(:E^_	 -;	
 -<	
 !B	
$ .)H=)H=1@!==0/ 6
<~IX!H6GTH8v>Q zF r.   