a
    6i                  $   @  s  U 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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 ddlmZ ddlmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZmZmZ ddlmZm Z m!Z! ddlm"Z"m#Z#m$Z$ ddl%m&Z& ddl'm'Z' ddl(m)Z) zddl*Z+W n$ e,yF   e&-d e.  Y n0 ddddZ/e!e0ef Z1G dd de Z2G dd deZ3G dd deZ4G dd deZ5eG dd dZ6G d d! d!Z7ej8d"dd#d$Z9ej8d"dd%d&Z:ej8d'd(d)d*d+Z;d
d-d.d/d0d1d2Z<d3dd4d5Z=d'd3d6d7d8Z>dd'd'd.d:d;d<Z?d'd.d6d=d>Z@d'd?d6d@dAZAd.ddBdCZBd'd.d6dDdEZCdd'd(d(d(d.d3dFdGdHZDejEfd'd'dId.dJdKdLZFd'd3dMdNdOZGdPd.dQdRdSZHdTdUdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdhdididjdkdldmdndodpdqdrdrdsdt#ZIeIZJduZKdvZLdwdxdydzZMi ZNd{eOd|< i ZPd}eOd~< i ZQd}eOd< ddd'd.d'd3dddZRi ZSdeOd< d'ddddZTdd'dddZUddd3dddZVddd3dddZWd'dddZXd'd3dddZYd'd3dddZZd'd3dddZ[d'd'd3dddZ\d'd3dddZ]d'ddddZ^d'dddZ_d'd'dd3dddZ`d'dd3dddZad'dd3dddZbd.dddZcd3dddZdd3dddZed3dddZfd3dddZgd3dddZhddd3dddĄZid'd'dŜddǄZjddd(dȜddʄZkd'd.d˜dd̈́Zlde3jmfd'dd'dd.dϜddфZnde3jmfd'd'dd'dd.dҜddԄZoddddׄZpdddڜdd܄Zqdd'd'dd3dޜddZrd3dddZsd3dddZteu fd'dd3dddZveu fd'd'dd3dddZwd'd3dddZxd.dddZyddddZzddd.d.dddZ{ddd.dd.dddZ|ddd.d.dddZ}d.d3dddZ~ddd'd.d.d ddZd(dddZde5jdfd'dd'd.ddd	ZdS (  z"Utilities to make gamefixes easier    )annotationsN)EnumPath)MutableMapping)	dataclass)datetimetimezone)TextIOWrapper)socketAF_INET
SOCK_DGRAM)AnyProtocolUnion)Mapping	GeneratorCallable   )log)configinstall_appz2Unable to hook into Proton main script environmentz_ProtonSession | Nonereturnc                   C  s   t tddS )a  Return Proton's Session (g_session) if it exists yet.

    ProtonFixes is imported very early in Proton's launcher script, before
    `g_session` is created. Gamefixes run later, after `g_session` exists,
    so this helper lets us safely grab it when available.
    	g_sessionN)getattr
protonmain r   r   ]/home/james-whalen/.local/share/Steam/compatibilitytools.d/GE-Proton10-32/protonfixes/util.py_get_proton_session&   s    r    c                   @  s"   e Zd ZU dZded< ded< dS )_ProtonSessionzEStructural type for the Proton session object (protonmain.g_session).MutableMapping[str, str]envzset[str]compat_configN__name__
__module____qualname____doc____annotations__r   r   r   r   r!   4   s   
r!   c                   @  s   e Zd ZdZdZdZdZdS )BasePathzEnum for base paths

    Attributes:
        USER: User's "My Documents" folder in the current prefix
        GAME: Game's install folder
    userZgameZappdata_localN)r&   r'   r(   r)   USERGAMEAPPDATA_LOCALr   r   r   r   r+   ;   s   r+   c                   @  s$   e Zd ZdZdZdZdZdZdZdS )OverrideOrdera  Enum for Wine dll/file override order

    Builtin means dlls/files that are provided by Wine.
    Native means dlls/files that are provided by the user or application.

    https://gitlab.winehq.org/wine/wine/-/wikis/Wine-User%27s-Guide#dll-overrides

    Files are usually resolved in the following order:

    1. The directory the program was started from.
    2. The current directory.
    3. The Windows system directory.
    4. The Windows directory.
    5. The PATH variable directories.

    Attributes:
        DISABLED: Disable file
        NATIVE: Load native file
        BUILTIN: Load builtin file
        NATIVE_BUILTIN: Load native file first, builtin second
        BUILTIN_NATIVE: Load builtin file first, native second
     nbzn,bzb,nN)	r&   r'   r(   r)   DISABLEDZNATIVEZBUILTINZNATIVE_BUILTINZBUILTIN_NATIVEr   r   r   r   r0   H   s   r0   c                   @  s    e Zd ZdZdZdZdZdZdS )	DosDevicezEnum for dos device types (mounted at 'prefix/dosdevices/')

    Attributes:
        NETWORK: A network device (UNC)
        FLOPPY: A floppy drive
        CD_ROM: A CD ROM drive
        HD: A hard disk drive

    networkZfloppyZcdromZhdN)r&   r'   r(   r)   ZNETWORKZFLOPPYCD_ROMZHDr   r   r   r   r5   g   s
   
r5   c                   @  s"   e Zd ZU dZded< ded< dS )ReplaceTypezUsed for replacementsstrZ
from_valueZto_valueNr%   r   r   r   r   r8   y   s   
r8   c                   @  s2   e Zd ZdZed dddZddddd	Zd
S )ProtonVersionz-Parses the proton version and build timestampr   c                 C  sP   t  d }z|jdd}| |W S  tyJ   td|  | d Y S 0 dS )zReturns the version of protonversionasciiencodingz"Proton version file not found in: z	0 UnknownN)	protondir	read_textOSErrorr   warn)clsfullpathversion_stringr   r   r   from_version_file   s    

zProtonVersion.from_version_filer9   None)rE   r   c                 C  s^   |  }t|dks |d  s6td| d dS tjt|d tj	d| _
|d | _dS )z&Initialize from a given version string   r   zVersion string "z" is invalid!N)Ztzr   )splitlen	isnumericr   critr   Zfromtimestampintr	   Zutc
build_dateversion_name)selfrE   partsr   r   r   __init__   s    zProtonVersion.__init__N)r&   r'   r(   r)   classmethodrF   rR   r   r   r   r   r:      s   
r:   r   c                   C  s   t tjd jS )zReturns the path to protonr   )r   sysargvparentr   r   r   r   r?      s    r?   c                   C  s   t tjddd S )z(Returns wineprefix's path used by protonSTEAM_COMPAT_DATA_PATHr1   Zpfx)r   osenvirongetr   r   r   r   protonprefix   s    r[   r9   z
str | None)appnamer   c                 C  s\   t jd t jD ]4}t j|| }t j|rt |t jr|  S qt	
|  d dS )z/Returns the full path of an executable in $PATHPATHz not found in $PATHN)rX   rY   rI   pathseppathjoinexistsaccessX_OKr   rB   )r\   r_   rD   r   r   r   which   s    
rd   FzCallable | NoneboolzCallable[..., Any])funcretryr   c                   s.    du rt jtdS dd fdd}|S )aN  Decorator to use on functions which should only run once in a prefix.

    Error handling:
    By default, when an exception occurs in the decorated function, the
    function is not run again. To change that behavior, set retry to True.
    In that case, when an exception occurs during the decorated function,
    the function will be run again the next time the game is started, until
    the function is run successfully.
    Implementation:
    Uses a file (one per function) in PROTONPREFIX/drive_c/protonfixes/run/
    to track if a function has already been run in this prefix.
    N)rg   rG   r   c            	   
     s    j  d j }t }tj|d}tj||}tj|sJt| tj|rZd S d }z | i | W n0 ty } zr||}W Y d }~n
d }~0 0 t	|ddd}|
  W d    n1 s0    Y  |r|d S )N.zdrive_c/protonfixes/run/ar<   r=   )r'   r&   r[   rX   r_   r`   ra   makedirs	Exceptionopenclose)	argskwargsZfunc_idprefix	directoryfile	exceptionexctmprf   rg   r   r   wrapper   s(    
&zonce.<locals>.wrapper)	functoolspartialonce)rf   rg   rw   r   rv   r   rz      s    rz   rG   c               	   C  s   t d dd tdD } dg}| D ]}zjttjd|dd@}| }|D ]"}|| v rRt	t
|tj qRW d   n1 s0    Y  W q( ty   Y q(Y q(0 q(dS )	z4Kills processes that hang when installing winetrickszKilling hanging wine processesc                 S  s   g | ]}|  r|qS r   )isdigit).0pidr   r   r   
<listcomp>       z _killhanging.<locals>.<listcomp>z/proczmscorsvw.execmdlinerbN)r   debugrX   listdirrl   r_   r`   readdecodekillrM   signalSIGKILLrA   )ZpidsZbadexesr}   Zproc_cmdr   exer   r   r   _killhanging   s    
6r   )verbr   c                 C  sR   t jt d}t|ddd }||  d W d   n1 sD0    Y  dS )z0Records verb into the winetricks.log.forced filewinetricks.log.forcedri   r<   r=   
N)rX   r_   r`   r[   rl   write)r   Z
forced_logZ	forcedlogr   r   r   _forceinstalled   s    r   winetricks.log)r   logfiler   c                 C  sN  t | tsdS tjt |}t| ddkr| dd d }| dd }d}zht|ddF}|	 D ],}t
d| | rlt| || k}qlW d   n1 s0    Y  |W S  ty   Y dS 0 z`t|dd>}| td	d
 |	 D v rW d   W dS W d   n1 s&0    Y  W n tyH   Y dS 0 dS )zBReturns True if the winetricks verb is found in the winetricks logF=r   r   r<   r=   ^Nc                 S  s   g | ]}|  qS r   )strip)r|   xr   r   r   r~     r   z#_checkinstalled.<locals>.<listcomp>T)
isinstancer9   rX   r_   r`   r[   rJ   rI   rl   	readlinesrefindallr   re   rA   reversed)r   r   Zwinetricks_logZwt_verbZwt_verb_paramZ	wt_is_setZtricklogxliner   r   r   _checkinstalled   s,    
46r   c                 C  s4   | dkrdS t d|  d t| dr,dS t| S )ziReturns True if the winetricks verb is found in the winetricks log or in the 'winetricks.log.forced' fileguiFzChecking if winetricks "z" is installedr   T)r   infor   )r   r   r   r   checkinstalled  s    
r   z
bool | strc                 C  s   | dkrdS | d }d}t jd| }t jt j||r\td|  t j||S t jt jt|}t jt j||rtd|  t j||S dS )z0Returns path to custom winetricks verb, if foundr   Fz.verbZverbsz!~/.config/protonfixes/localfixes/z)Using local custom winetricks verb from: z#Using custom winetricks verb from: )	rX   r_   
expanduserisfiler`   r   r   dirname__file__)r   Z	verb_nameZverb_dirZverbpathr   r   r   is_custom_verb#  s    r   c               
   C  sd   zDt tt$} | d | d W d   n1 s60    Y  W dS  tttfy^   Y dS 0 dS )zChecks for internet connection.   )z1.1.1.15   NTF)r   r   r   Z
settimeoutZconnectTimeoutErrorrA   PermissionError)Zsockr   r   r   check_internet:  s    
(r   c           	      C  s  t | st rntd dS td|   ttjj}tt	 |d< tj
j|d< tj
j|d< tj
j|d< d|d	< d
|d< d}|dg| d }| dkr|dg}d| v rd|d< t| }|r|d|g}|du rtd |durtd|  ttjD ]8\}}d|vr|dkrdtj|< tttj  q8qtd|  d tj|d dg|d tj||d}|  W d   n1 s0    Y  t  |j}|dkrtd|  d| d dS t | std |  d! t|  td" d#S d| v rd$|d< dS )%zRuns winetricks if availablez3No internet connection. Winetricks will be skipped.FzInstalling winetricks 
WINEPREFIXWINE
WINELOADER
WINESERVERdisabledZWINETRICKS_LATEST_VERSION_CHECKr1   
LD_PRELOAD
winetricksz--unattended r   Zdotnet0PROTON_USE_XALIANz No winetricks was found in $PATHzUsing winetricks command: waitforexitandrunrunzUsing winetricks verb ""z-wr#   r   z Winetricks failed running verb "z" with status rh   z&Not recorded as installed: winetricks z
, forcing!zWinetricks completeT1)r   r   r   r   dictr   r   r#   r9   r[   g_protonwine_binwineserver_binrI   r   rB   r   	enumeraterT   rU   
subprocesscallPopenwaitr   
returncoder   )	r   r#   Zwinetricks_binZwinetricks_cmdZcustom_verbidxargprocessZretcr   r   r   protontricksE  sb    





(



r   )foldernametypvaluearchr   c                 C  s  t tjj}tt |d< tjj|d< tjj|d< tjj|d< |dur|dur|dur|rxddd| d	d
|d|d|dg}nddd| d	d
|d|d|g}t	
d|   n6|durddd| d	dg}nddd| d	g}t	
d|   tj||d}|  W d   n1 s0    Y  dS )zAdd regedit keysr   r   r   r   NwineZregaddz/fz/vz/tz/dz/reg:64zAdding key: r   )r   r   r   r#   r9   r[   r   r   r   r   r   r   r   r   )r   r   r   r   r   r#   Zregedit_cmdr   r   r   r   regedit_add  sN    r   zre.RegexFlag)origreplmatch_flagsr   c                 C  s|   d}t tjD ]2\}}tj| |||d}||kr2q|tj|< d}q|r`td|  d| d ntd|  d| d |S )	zMake a commandline replacement in sys.argv

    Returns if there was any match.

    By default the search is case insensitive,
    you can override this behaviour with re.RegexFlag.NOFLAG
    F)flagsTz	Changed "" to "r   zCan not change "z", command not found)r   rT   rU   r   subr   r   rB   )r   r   r   foundr   r   replacedr   r   r   replace_command  s    

r   )argumentr   c                 C  s2   t d|   tj|  t dttj  dS )zAppend an argument to sys.argvzAdding argument zNew commandline: N)r   r   rT   rU   appendr   r9   )r   r   r   r   append_argument  s    r   object)valr   c                 C  sn   zT| d u rW dS t | tr | W S t | ttfr8| dkW S t| }t|dkoR|dkW S  tyh   Y dS 0 d S )NFr   r   )r   re   rM   floatr9   rJ   rk   )r   Zsvalr   r   r   _nonzero  s    

r   nowmdecorationdxvkd3d8nod3d11nod3d10noesyncnofsyncnontsync
forcelgaddoldglstr	hidenvgpu	hidevggpuhideintelgpuhideapu	gamedrive
steamdrivenoximheapdelayfreeheapzeromemorydisablenvapi
forcenvapiwaylandsdlinputfsr3fsr4	fsr4rdna3fsr4huddlssdlsshudxess	nativeagshdr	writecopy)#PROTON_NO_WM_DECORATIONPROTON_DXVK_D3D8PROTON_NO_D3D11PROTON_NO_D3D10PROTON_NO_ESYNCPROTON_NO_FSYNCPROTON_NO_NTSYNC PROTON_FORCE_LARGE_ADDRESS_AWAREPROTON_OLD_GL_STRINGPROTON_HIDE_NVIDIA_GPUPROTON_HIDE_VANGOGH_GPUPROTON_HIDE_INTEL_GPUPROTON_HIDE_APUPROTON_SET_GAME_DRIVEPROTON_SET_STEAM_DRIVEPROTON_NO_XIMPROTON_HEAP_DELAY_FREEPROTON_HEAP_ZERO_MEMORYPROTON_DISABLE_NVAPIPROTON_FORCE_NVAPIPROTON_ENABLE_WAYLANDPROTON_USE_WAYLANDPROTON_PREFER_SDLPROTON_USE_SDLPROTON_FSR3_UPGRADEPROTON_FSR4_UPGRADEPROTON_FSR4_RDNA3_UPGRADEPROTON_FSR4_INDICATORPROTON_DLSS_UPGRADEPROTON_DLSS_INDICATORPROTON_XESS_UPGRADEPROTON_NATIVE_AGSPROTON_ENABLE_HDRPROTON_USE_HDRPROTON_USE_WRITECOPY)PROTON_USE_WINED3DPROTON_USE_WINED3D11wined3d)r  r  )r  r  )r  r   )r   r   r   zdict[str, bool]_COMPAT_FLAG_BASELINEzdict[str, set[str]]_ENVVAR_TO_FLAGS_TOUCHED_ADD_CONFIG_TOKENS_BY_ENVVARr   r"   )r#   keyenableenable_valuer   c                 C  s   |r|| |< n|  |d dS )z6Set env[key]=enable_value when enable else remove key.N)pop)r#   r(  r)  r*  r   r   r   _set_or_unset5  s    
r,  zdict[str, str | None]_WINE_DLL_BASELINEzdict[str, str])r   r   c                 C  sR   i }| s|S |  dD ]6}|rd|vr(q| dd\}}| }|r|||< q|S )N;r   r   )rI   r   )r   outpartdllsettingr   r   r   _parse_winedlloverrides@  s    
r3  )mappingr   c                 C  s   d dd |  D S )Nr.  c                 S  s   g | ]\}}| d | qS )r   r   r|   kvr   r   r   r~   N  r   z/_serialize_winedlloverrides.<locals>.<listcomp>)r`   items)r4  r   r   r   _serialize_winedlloverridesM  s    r9  r   )r#   updatesr   c                 C  sf   t | dd}| D ]<\}}|tvr6||t|< |d u rL||d  q|||< qt|| d< d S NWINEDLLOVERRIDESr1   )r3  rZ   r8  r-  r+  r9  )r#   r:  curr1  Znew_settingr   r   r   _update_winedlloverridesP  s    
r>  z	list[str])r#   dllsr   c                 C  sZ   t | dd}|D ]4}|tvr"qt| }|d u r@||d  q|||< qt|| d< d S r;  )r3  rZ   r-  r+  r9  )r#   r?  r=  r1  baseliner   r   r   _restore_winedlloverrides[  s    
rA  c                  C  sF   z.t tddd } t | dd }|r,|d W S W n ty@   Y n0 dS )N__main__r   dist_dirzshare/X11/localer1   )r   
__import__rk   )protonrC  r   r   r   _guess_xlocaledirg  s    rF  )changed_envvarr   c                   s  t    du rdS t j| }d)ddddd fdd}| d	kr~|rd jd
  jdd n jd
 d jd< dS | dkr|r jd  jdd n jd d jd< dS | dkr|r jd  jdd n jd d jd< dS | dkr<|r" jd n jd |d| dS | dkrt|rZ jd n jd |d| dS | dkr|r jd n jd dS | dkr|r jd n jd dS | dkr*|r jd n jd |rd jvrd jd< n jdd dS | d krb|rH jd! n jd! |d"| dS | d#kr|r jd$ n jd$ |d%| dS | d&kr|r jd' n jd' |d(| dS dS )*az  Mirror Proton's init_session() side effects for envvars changed by ProtonFixes.

    Proton reads many PROTON_* envvars inside Session.init_session(). ProtonFixes
    runs *after* init_session(), so when a gamefix changes a PROTON_* knob we
    replicate the meaningful changes here (compat_config updates + derived WINE_*
    envvars that Proton would normally set later).
    Nr   r9   re   rG   )r(  enabledr   r   c                   s   t  j| || d S N)r,  r#   )r(  rH  r   sessr   r   _set_or_clear_env  s    z:_apply_proton_knob_side_effects.<locals>._set_or_clear_envr  r   
WINENTSYNCr  r   	WINEESYNCr  r   	WINEFSYNCr  r   WINE_HEAP_DELAY_FREEr  r   WINE_HEAP_ZERO_MEMORYr  r   r  r   r  r   r   WINE_HIDE_NVIDIA_GPUr	  r   WINE_HIDE_VANGOGH_GPUr
  r   WINE_HIDE_INTEL_GPUr  r   WINE_HIDE_APU)r   )r    r   r#   rZ   r$   r   r+  discard)rG  rH  rL  r   rJ  r   _apply_proton_knob_side_effectsr  s    
















rW  c                 C  sf   t  }|du rdS z:| dkr0ttdr0t  n| dkrJttdrJt  W n ty`   Y dS 0 dS )zApply Proton session side-effects for a knob change.

    This mirrors Proton's `Session.init_session()` behavior for a subset of
    environment-variable "knobs" that ProtonFixes may toggle *after* the session has
    already been initialized.
    Nr  setup_game_dir_driver  setup_steam_dir_drive)r    hasattrr   rX  rY  rk   )rG  rK  r   r   r   _apply_proton_session_actions  s    
r[  c                 C  s   t jj}t jj}| tv rtd |v rLt|td  r@|t q~|t n2td |v r~t|td  rt|t n
|t dS t	
| }|sdS t|
| dr|| n
|| dS )zUpdate `compat_config` based on an envvar change.

    This mirrors Proton's `Session.check_environment()` behavior for options that
    are represented as entries in `compat_config`.
    r   r   Nr1   )r   r   r#   r$   _WINED3D_CHAINr   r   _WINED3D_FLAGrV  _ENV_TO_COMPATrZ   )rG  r#   Zcfgflagr   r   r   _apply_env_to_compat_config  s$    

r`  )envvarr   r   c                 C  s   t d|  d|  |tj| < |tjj| < t }| tv rF|	t
 n| tv r\|	t|   |r|D ]}t||tjjv  qdt| t | t|  t|  t|  dS )a  Add or override an environment value.

    Extended behavior:
    - If envvar is a PROTON_* knob that Proton normally translates into compat_config flags,
      update protonmain.g_session.compat_config immediately.
    - Apply a small set of important late-binding side effects (esync/fsync/ntsync, hdr, etc.)
      so that user protonfix scripts behave as expected even though init_session() already ran.
    zAdding env: r   N)r   r   rX   rY   r   r   r#   setr\  r   r]  r^  r%  
setdefaultr$   r&  updater`  rW  r[  )ra  r   r   fr   r   r   set_environment  s    	
rf  )ra  r   c                   s  t j| d t   du r dS  j| d t|  t| t }|D ]V}|tkrlt	 fddt
D rlqHt|}|du rqH|r j| qH j| qHt| }t|o| jv }d&ddddd fd	d
}| dkr|d|  n| dkr|d|  n| dkr|d|  n| dkr4|d| n| dkrJ|d| n| dkr`|d| n| dkr|d|d |d|d nl| dkr|d| nV| dkr|d| n@| d kr|d!| n*| d"kr|d#| n| d$kr|d%| t|  dS )'zDDelete an environment variable and re-apply relevant Proton effects.Nc                 3  s   | ]}| j v V  qd S rI  r   )r|   r6  rJ  r   r   	<genexpr>S  r   z"del_environment.<locals>.<genexpr>r   r9   re   rG   )r(  onr   r   c                   s"   |r| j | < n j | d  d S rI  )r#   r+  )r(  rh  r   rJ  r   r   _seta  s    zdel_environment.<locals>._setr  rN  r  rO  r  rM  r   WINE_NO_WM_DECORATIONr  rP  r  rQ  r  MESA_EXTENSION_MAX_YEAR2003__GL_ExtensionStringVersion17700r  rR  r	  rS  r
  rT  r  rU  r!  WINE_SIMULATE_WRITECOPY)r   )rX   rY   r+  r    r#   r`  r&  rb  r]  anyr\  r%  rZ   r$   r   rV  _PROTON_ENV_TO_COMPATre   r[  )ra  Ztouchedr_  r@  Zcompat_flagrH  ri  r   rJ  r   del_environmentA  sZ    













rr  zbool | None)r_  r   c                   s   t jj tkrTtd  v r0t td dS td  v rPt td dS d S tv rd }t D ]}| v rh|}qh|d u rd S t |dS  fddt	 D }|sd S t
|d }t |dS )Nr   r1   r   c                   s$   g | ]\}}|kr| v r|qS r   r   r5  r#   r_  r   r   r~     r   z,_recompute_flag_from_env.<locals>.<listcomp>)r   r   r#   r]  r\  r   rZ   _FLAG_ENV_ORDERr^  r8  sorted)r_  winnerr6  Zcontrollingr   rs  r   _recompute_flag_from_env  s(    rx  c                  C  s&   t jdt  } td|   | S )zGame installation pathSTEAM_COMPAT_INSTALL_PATHzDetected path to game: )rX   rY   rZ   getcwdr   r   )Zinstall_pathr   r   r   get_game_install_path  s    r{  )targetfiletypedtyper   c                 C  s`   t d|  d| d|j  |dkr*| n|  d| }| d|j }ttjjd|d dS )	zAdd WINE file overridezOverriding rh   z = r1  r   r<  r.  N)r   r   r   r   append_to_env_strr   r#   )r|  r}  r~  Ztarget_filer2  r   r   r   _winepe_override  s    r  )r1  r~  r   c                 C  s   t | d|d dS )zAdd WINE dll overrider1  r|  r}  r~  Nr  )r1  r~  r   r   r   winedll_override  s    r  )r   r~  r   c                 C  s   t | d|d dS )zAdd WINE executable overrider   r  Nr  )r   r~  r   r   r   wineexe_override  s    r  c               
   C  s"  zt d} | s"td W dS z^tj| dgddd}z|jd}W n4 ty|   dd	l	}|
d}|jj|d
d}Y n0 W n: tjy } z td|  W Y d	}~W dS d	}~0 0 d	}| D ]X}d|v rd|v r| d}t|dkr|d  }	tj|	rtj|	} q"q|s8td W dS td|  ddlm}
 t|
 tj| }zt| }W n: ty } z td|  W Y d	}~W dS d	}~0 0 | }|dd}t|}z(| | |!d t"d|  W n@ ty< } z&td| d|  W Y d	}~W dS d	}~0 0 td|  t#|j$t%j&j'(dg}d)t*d	|t%j&j'd< tdt%j&j'(d  t#|t%j&j'(d g}d)t*d	|t%j&j'd < td!t%j&j'(d   W dS  t+y } ztd"|  W Y d	}~dS d	}~0 0 d	S )#ax  Patches libcuda to work around games that crash when initializing libcuda and are using DLSS.

    We will replace specific bytes in the original libcuda.so binary to increase the allowed memory allocation area.

    The patched library is overwritten at every launch and is placed in .cache

    Returns true if the library was patched correctly. Otherwise returns false
    Zldconfigzldconfig not found in PATH.Fz-pT)capture_outputcheckutf-8r   Nreplace)errorszError running ldconfig: z
libcuda.sozx86-64z => rH   r   z<libcuda.so not found as a 64-bit library in ldconfig output.zFound 64-bit libcuda.so at: )mkdtempzUnable to read libcuda.so: Z000000f8ff000000Z000000f8ffff0000i  z!Permissions set to rwxr-xr-x for z&Unable to write patched libcuda.so to z: zPatched libcuda.so saved to: LD_LIBRARY_PATH:zLD_LIBRARY_PATH updated to r   zLD_PRELOAD updated to zUnexpected error occurred: ),shutilrd   r   rB   r   r   stdoutr   UnicodeDecodeErrorlocaleZgetpreferredencodingCalledProcessError
splitlinesr   rI   rJ   rX   r_   ra   abspathr   Ztempfiler  r   basename
read_bytesrA   rL   hexr  bytesfromhexwrite_byteschmodr   r9   rV   r   r   r#   rZ   r`   filterrk   )Zldconfig_pathresultoutputr  r>   eZlibcuda_pathlinerQ   r_   r  Zpatched_libraryZbinary_dataZhex_dataZpatched_binary_dataZld_pathsr   r   r   patch_libcuda  s~    	







r  c                   C  sV   t d tdtj tdtj tdtj tdtj tdtj tdtj dS )	zDisable WINE nv* dllszDisabling NvAPInvapinvapi64nvcudaZnvcuvidZnvencodeapiZnvencodeapi64N)r   r   r  r0   r4   r   r   r   r   disable_nvapi$  s    
r  c                   C  s   t d  tdd dS )zDisabling EsyncrN  r1   Nr   r   rf  r   r   r   r   disable_esync/  s    
r  c                   C  s   t d  tdd dS )zDisabling FSyncrO  r1   Nr  r   r   r   r   disable_fsync5  s    
r  c                   C  s   t d  tdd dS )zDisabling NTSyncrM  r1   Nr  r   r   r   r   disable_ntsync;  s    
r  c                   C  s6   t d  tdd tdd tdd tdd dS )z Disabling Proton Media ConverterZPROTON_AUDIO_CONVERTr   ZPROTON_AUDIO_CONVERT_BINZPROTON_VIDEO_CONVERTZPROTON_DEMUXNr  r   r   r   r   disable_protonmediaconverterA  s
    



r  StrPathzMapping[str, Mapping[str, Any]])	conf_file	conf_dictr   c                 C  s`   t | t jrdS t }|| t| ddd}|| W d   n1 sR0    Y  dS )zCreate DOSBox configuration file.

    DOSBox accepts multiple configuration files passed with -conf
    option;, each subsequent one overwrites settings defined in
    previous files.
    Nwr<   r=   )rX   rb   F_OKconfigparserConfigParserZ	read_dictrl   r   )r  r  confrr   r   r   r   create_dosbox_confJ  s    	
r  )r_   r   c           	      C  s  t j| r| S | }t j|s2t j|d }q|t|d  dvrP|t j }| |dt j}t|}d}|D ]p}t j|s qt |}d}|D ].}| | krt j	||}|d7 }d}q|stt j	||}|d7 }qt||krt j	|t j	||d }|S )z|Find potentially differently-cased location

    e.g /path/to/game/system/gothic.ini -> /path/to/game/System/GOTHIC.INI
    r   r   )/\r1   FTN)
rX   r_   ra   rI   rJ   sepr  r   lowerr`   )	r_   rootZs_working_dirZpaths_to_findZpaths_foundrq   Zdir_listr   Zexisting_dirr   r   r   _get_case_insensitive_name[  s4    



r  )cfile	base_pathr   c                 C  s   |t jkrtjt d| }n>|t jkr:tjt | }n"|t jkrXtjt d| }n| }t	t
|}tj|rt|tjrtd|  |S td|  dS )zFind game's config filez$drive_c/users/steamuser/My Documentsz%drive_c/users/steamuser/AppData/LocalzFound config file: zConfig file not found: N)r+   r-   rX   r_   r`   r[   r.   r{  r/   r  r9   ra   rb   r  r   r   rB   )r  r  cfg_pathr   r   r   _get_config_full_path  s"    


r  )r  r   c                 C  s2   t j| d s.td t| | d  dS dS )zCreate backup config filez.protonfixes.bakzCreating backup for config fileTF)rX   r_   ra   r   r   r  copyfile)r  r   r   r   create_backup_config  s
    
r  r  )ini_optsr  r>   r  r   c                 C  s   t ||}|sdS t|sdS tjdddd}dd |_||| td| d|   ||  t	|d|d	}|
| W d
   n1 s0    Y  dS )zEdit game's INI config fileFT)Zempty_lines_in_valuesZallow_no_valuestrictc                 S  s   | S rI  r   Z	optionstrr   r   r   <lambda>  r   z!set_ini_options.<locals>.<lambda>zAddinging INI options into "z":
r  r=   N)r  r  r  r  optionxformr   r   r   Zread_stringrl   r   )r  r  r>   r  r  r  
configfiler   r   r   set_ini_options  s    


(r  )base_attibuttexml_liner  r>   r  r   c              	   C  s   t ||}|sdS t|sdS t|}d}|| }|D ]B}	|d7 }| |	vrRq<td| d| d|  |||d  q<t	d|}
|
|
| td d	S )
zEdit game's XML config fileFr   r   zAdding XML options into "z", line z:
r   zXML config patch appliedT)r  r  r   r@   r  r   r   insertr9   r`   
write_text)r  r  r  r>   r  Zxml_pathZxml_path_objicontentsr  datar   r   r   set_xml_options  s$    

r  ztuple[int, int | None]c            	      C  s   t d} | std dS tjds6td dS t| dg	d}|
 D ]f}d|v rR| d	 }|d
}|d d}|d d
 |d  }|d
\}}t|t|f  S qRdS )z-Returns screen res width, height using xrandrZxrandrzBxrandr not found in PATH, skipping screen resolution determination)r   NZDISPLAYz@DISPLAY does not exist, skipping screen resolution determinationz	--currentr  Zprimary   r   r   +r   )r   r   )r  rd   r   r   rX   rY   rZ   r   check_outputr   r  rI   rM   )	Z
xrandr_binZxrandr_outputr  Z
resolutionZwidth_heightZoffset_valuesZclean_resolutionZscreenxZscreenyr   r   r   get_resolution  s"    



r  r
   zGenerator[str, None, None])cfpr   c                 c  s"   dt  j dV  | E dH  dS )z'Add fake [DEFAULT] section to dxvk.conf[]N)r  r  default_section)r  r   r   r   read_dxvk_conf  s    r  /tmp/protonfixes_dxvk.conf)optr   r  r   c                 C  sX  t  }dd |_|j}tjtjd d}|| |	|drV|
|dt krtd tdt| t  }dd |_||dtt  t|tjrt|d	d
}|t| W d   n1 s0    Y  t||  td|  d| d ||| t| t|dd	d
}|| W d   n1 sJ0    Y  dS )zmCreate custom DXVK config file

    See https://github.com/doitsujin/dxvk/wiki/Configuration for details
    c                 S  s   | S rI  r   r  r   r   r   r    r   z!set_dxvk_option.<locals>.<lambda>PWDz	dxvk.confZsessionzCreating new DXVK configZDXVK_CONFIG_FILEc                 S  s   | S rI  r   r  r   r   r   r  '  r   r<   r=   NzAddinging DXVK option: "z" = "r   r  )r  r  r  r  rX   r_   r`   rY   r   Z
has_optionZgetintgetpidr   r   rf  r9   rb  rb   r  rl   Z	read_filer  r   r8  r   )r  r   r  r  ZsectionZ	dxvk_confZdxvkr  r   r   r   set_dxvk_option  s,    




,r  c                   C  s   t d dS )z$Install Proton Easyanticheat RuntimeZ1826330Nr   r   r   r   r   install_eac_runtime7  s    r  c                   C  s   t d dS )zInstall Proton BattlEye RuntimeZ1161040Nr   r   r   r   r   install_battleye_runtime<  s    r  )urlr_   r   c                 C  s   t jjjddd tj| }t jj| }| sNtd|  t	j
| | t|d0}td| d|  || W d   n1 s0    Y  dS )z*Install all files from a downloaded tar.gzTparentsexist_okzDownloading zr:gzzExtracting z to N)r   r_   	cache_dirmkdirrX   r  is_filer   r   urllibrequesturlretrievetarfilerl   
extractall)r  r_   Ztgz_file_nameZtgz_file_pathZtgz_objr   r   r   install_all_from_tgzA  s    r  )r  filenamer_   r   c                 C  s   |t |v r*td| d| d dS tjjjddd t j| }tjj| }|	 std| d| d t
j| | t|d	6}td
| d| d |j||d W d   n1 s0    Y  dS )z$Install a file from a downloaded zipzFile "z" found in "r   NTr  zDownloading "r   rzExtracting ")r_   )rX   r   r   r   r   r_   r  r  r  r  r  r  r  zipfileZipFileextract)r  r  r_   Zzip_file_nameZzip_file_pathZzip_objr   r   r   install_from_zipQ  s    r  )textr   c                 C  sv   zddl m} |d|  W nT typ   ztjdd| gdd W n( tjtjfyj   t	d|   Y n0 Y n0 d	S )
zTrys to show a message box with an error

    1. Try importing tkinter and show messagebox
    2. Try executing process 'notify-send'
    3. Failed, output info to log
    r   )
messageboxzProton Fixesznotify-sendprotonfixesT)r  z6Failed to show error message with the following text: N)
Ztkinterr  Z	showerrorImportErrorr   r   SubprocessErrorr  r   r   )r  r  r   r   r   try_show_gui_errore  s    r  c               
   C  s   zBt ddd"} |   dkW  d   W S 1 s60    Y  W nR ty^   td Y n8 ty } z td|j d W Y d}~n
d}~0 0 d	S )
zUReturns whether SMT is enabled.

    If the check has failed, False is returned.
    z"/sys/devices/system/cpu/smt/activer<   r=   r   Nz No permission to read SMT statusz/SMT status not supported by the kernel (errno: )F)rl   r   r   r   r   rB   rA   errno)Zsmt_fileexr   r   r   is_smt_enabledw  s    4*r  rM   c                  C  s&   t  } | r| dkr"td dS | S )z_Returns the cpu core count, provided by the OS.

    If the request failed, 0 is returned.
    r   z'Can not read count of logical cpu cores)rX   	cpu_countr   rB   )	cpu_coresr   r   r   get_cpu_count  s
    
r  )
core_countignore_user_settingr   c                 C  s|   t d}|r&|s&td|  dS | r2| dkr@td dS |  ddttt|  }t	d| td|  d	S )
zThis sets the cpu topology to a fixed core count.

    By default, a user provided topology is prioritized.
    You can override this behavior by setting `ignore_user_setting`.
    WINE_CPU_TOPOLOGYz)Using WINE_CPU_TOPOLOGY set by the user: Fr   z9Only positive core_counts can be used to set cpu topologyr  ,zUsing WINE_CPU_TOPOLOGY: T)
rX   getenvr   r   rB   r`   mapr9   rangerf  )r  r  Z	user_topoZcpu_topologyr   r   r   set_cpu_topology  s    


r  rH   )
core_limitr  threads_per_corer   c                 C  s<   t  du rtd dS t | }t|t|| }t||S )zThis sets the cpu topology to the count of physical cores.

    If SMT is enabled, eg. a 4c8t cpu is limited to 4 logical cores.
    You can limit the core count to the `core_limit` argument.
    FzSMT is not active, skipping fix)r  r   r   r  maxminr  )r  r  r  r  r   r   r   set_cpu_topology_nosmt  s    	


r
  )r  r  r   c                 C  s4   t  }| |kr*td| d|  d dS t| |S )zThis sets the cpu topology to a limited number of logical cores.

    A limit that exceeds the available cores, will be ignored.
    zThe count of logical cores (z+) is lower than or equal to the set limit (z), skipping fixF)r  r   r   r  )r  r  r  r   r   r   set_cpu_topology_limit  s    r  )rH  r   c                 C  s&   | rt jjd nt jjd dS )aM  Enable or disable the game drive setting.

    This function modifies the `compat_config` to include or exclude
    the "gamedrive" option based on the `enabled` parameter.

    Args:
        enabled (bool):
            If True, add "gamedrive" to `compat_config`.
            If False, remove "gamedrive" from `compat_config`.

    r   N)r   r   r$   r   rV  )rH  r   r   r   set_game_drive  s    r  )
from_appidrelative_pathis_demor   c           
      C  s  ddl m} g }| dks$| d dkr:td|  d dS z`ttjd  d8}| D ]}d	|v rZ||d
d  qZW d   n1 s0    Y  W n t	y   t
d Y dS 0 |D ]d}|| d|   r|| d|  d|  }| st
d| d|  d  dS  q:qt
d|  d dS |t  d|  }| s|jjddd |s||d t
d| d| d n$t|| t
d| d| d dS | s|r| rd}	| D ]}||j  s| r:t|||j  t
d| d||j  d d}	n>| rt|||j  t
d| d||j  d d}	q|	S dS nB|  s|  ||d t
d| d| d dS dS dS )a  Creates a symlink in the prefix for the game you're trying to play, to a folder from another game's prefix (it needs to be in a Steam library folder).

    You can use this for games that have functionality that depends on having save data for another game or demo,
        which does NOT store its save data in its steamapps/common folder.
    This will only work for Steam games.

    from_appid: Steam App ID of the game you're trying to get the saves folder from
        You can find this number in the URL for the game's store page, or through looking up the game in SteamDB.
    relative_path: Path to reach the target folder that has the save data, starting from the .../pfx/drive_c/users/steamuser folder
        For example for the "My Documents/(Game name)" folder, just use "My Documents/(Game name)".
    is_demo: If True, will copy files from the folder in the other game's prefix instead of symlinking the whole folder.
        This is good for obtaining save data from demos that save in the same location as the full game, in case the full game already made the target folder.
        This also means you won't lose the files if you delete the demo's prefix.
    r   r   
   z)Invalid ID used for import_saves_folder (z).FSTEAM_BASE_FOLDERz/steamapps/libraryfolders.vdfz"path"   Nz/Could not find Steam's libraryfolders.vdf file.z/steamapps/compatdata/z/pfx/drive_c/users/steamuser/z2Skipping save data folder import due to location `z!` not existing in the prefix for rh   zGSkipping save data folder import, due to not finding a prefix for game z to get the folder from.z/drive_c/users/steamuser/Tr  zCreated a symlink at `z` to go to ``zCopied folder from `z` to `zCopied file from `zReplaced symlink at `)pathlibr   r   rB   rl   rX   rY   r   r   FileNotFoundErrorr   ra   resolver[   rV   r  
symlink_tor  copytree
is_symlinkis_diriterdirr   r  r  readlinkunlink)
r  r  r  r   pathsre  r  rp   ZgoalZchangesMader   r   r   import_saves_folder  sz    6






r   c                  C  s   t tjd  dx} d}|  D ]Z}t|dkrP|dd  rP|dd }q"|dtjd  d	kr"|  W  d   S q"W d   n1 s0    Y  dS )
z&Returns your 17-digit Steam account IDr  z/config/loginusers.vdfNr   rH   r  z		"AccountName"		"Z	SteamUserz"
)rl   rX   rY   r   rJ   r{   )re  ZlastFoundIdr  r   r   r   get_steam_account_idC  s    6r!  r  z/tmp)letterdev_typer_   r   c                 C  sV   t | dksJ t d|  d }| r.dS ||d td|  dd|jd dS )a  Create a symlink to <path> in the dosdevices folder of the prefix and register it

    Args:
        letter (str, optional): Letter that the device gets assigned to, must be len = 1
        dev_type (DosDevice, optional): The device's type which will be registered to wine
        path (str, optional): The link target path, defaults to `/tmp`

    Returns:
        bool: True, if device was created

    r   zdosdevices/r  FTzHKLM\Software\Wine\DrivesZREG_SZ)rJ   r[   ra   r  r   r   )r"  r#  r_   Z	dosdevicer   r   r   create_dos_deviceP  s    r$  )NF)r   )NNNF)r   )r  )F)r   FrH   )F)F)r)   Z
__future__r   r  rX   rT   r   r  r   r  r  r   Zurllib.requestr  rx   enumr   r  r   collections.abcr   Zdataclassesr   r   r	   ior
   r   r   r   typingr   r   r   r   r   r   loggerr   r   Zsteamhelperr   rB  r   r  rL   exitr    r9   r  r!   r+   r0   r5   r8   r:   	lru_cacher?   r[   rd   rz   r   r   r   r   r   r   r   r   
IGNORECASEr   r   r   r^  rq  r\  r]  ru  r%  r*   r&  r'  r,  r-  r3  r9  r>  rA  rF  rW  r[  r`  rf  rr  rx  r{  r  r  r  r  r  r  r  r  r  r  r  r  r  r.   r  r  r  r  r  r  r  rz  r  r  r  r  r  r  r
  r  r  r   r!  r7   r$  r   r   r   r   <module>   sB  

-I   ?&	r"'A
i	*$!*c