a
    V$cp                     @   s  d dl mZmZmZmZmZ d dlmZmZm	Z	m
Z
mZmZmZmZmZmZmZ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mZmZ d dlZejZdZ erd dl!Z!e!" Z#e#d7 Z#nd dl$m%Z# e#d7 Z#d	Z&d
Z'G dd de(Z)dd Z*G dd de+Z,dS )    )divisionabsolute_importwith_statementprint_functionunicode_literals)PY2
basestringbchrbordchropenpystrrangeroundstrtobytesunicodeN)loadsdumps   s   _v2.1)MAGIC_NUMBERs   _v3.1s
   RENPY RPC2zcache/bytecode.rpybc                   @   s   e Zd ZdZdS )ScriptErrorza
    Exception that is raised if the script is somehow inconsistent,
    or otherwise wrong.
    N)__name__
__module____qualname____doc__ r   r   renpy/script.pyr   F   s   r   c                 C   s   g }| D ]}| |j q|S )zO
    Returns a flat list containing every statement in the tree
    stmts.
    )get_childrenappend)stmtsrvir   r   r   collapse_stmtsM   s    r#   c                   @   s   e Zd 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d7ddZd8d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/d0 Zd1d2 Zd3d4 Zd5d6 ZdS )9Scriptak  
    This class represents a Ren'Py script, which is parsed out of a
    collection of script files. Once parsing and initial analysis is
    complete, this object can be serialized out and loaded back in,
    so it shouldn't change at all after that has happened.

    @ivar namemap: A map from the name of an AST node to the AST node
    itself.  This is used for jumps, calls, and to find the current
    node when loading back in a save. The names may be strings or
    integers, strings being explicit names provided by the user, and
    integers being names synthesised by renpy.

    @ivar initcode: A list of priority, Node tuples that should be
    executed in ascending priority order at init time.

    @ivar all_stmts: A list of all statements, that have been found
    in every file. Useful for lint, but tossed if lint is not performed
    to save memory.

    c                 C   s   | t j_tjt jjd r6tt jjd d	 | _
nd| _
i | _g | _g | _g | _g | _d| _i | _i | _d| _t j | _|   |   | j  d| _tt jd| _d| _ g | _!g | _"dS )z
        Loads the script by parsing all of the given files, and then
        walking the various ASTs to initialize this Script object.
        z	/lock.txtrbNTFr   zutf-8)#renpygamescriptospathexistsconfig
renpy_baser   readkeynamemap	all_stmts
all_pycode
all_pyexprneed_analysisrecord_pycodebytecode_oldcachebytecode_newcachebytecode_dirtytranslationScriptTranslator
translatorinit_bytecodescan_script_fileschain_translatesserialhashlibmd5version_onlyencodedigest
loaded_rpybackup_listduplicate_labelsselfr   r   r   __init__q   s,    
zScript.__init__c                 C   s   t jr
d S dD ]}t j|r d S qdd l}|t jjd}|d u rJd S tj	
t jj}t jj	t j|t j|}t jd| |S )N)zscript_version.txtzscript_version.rpyzscript_version.rpycr   backupszBacking up script files to %r:)r&   mobileloaderloadable__main__path_to_savesr,   gamedirr)   r*   basenamebasedirjoinexportsfsencode	write_log)rI   r"   rO   rK   rR   	backupdirr   r   r   choose_backupdir   s    
zScript.choose_backupdirc           
   	   C   sN  | j }g | _ tjdddkr"d S | js,d S tjr6d S |  }|d u rJd S |D ]\}}|tj	j
sfqNtj|stqNtj|ttj	j
d d  }tj|\}}tr|d d d}n|d d  }tj||d | | }	tj|	rqNzttj|	d W n ty    Y n0 zt||	 W qN tyF   Y qN0 qNd S )	NRENPY_DISABLE_BACKUPS zI take responsibility for this.r      hex.i  )rF   r)   environgetrE   r&   rL   rY   
startswithr,   rQ   r*   r+   rU   rV   lensplitextr   rC   r]   rT   makedirsdirname	Exceptionshutilcopy)
rI   rF   rX   fnchecksumshort_fnbaseexthex_checksum	target_fnr   r   r   make_backups   sF     zScript.make_backupsc                 C   s   t j }g | _g | _|D ]\}}|drJ|du r6q|dd }| j}nf|drh|dd }| j}nH|dr|du r|q|dd }| j}n |dr|dd }| j}nq||f|vr|||f qdS )	z8
        Scan the directories for script files.
        .rpyN.rpyc.rpym.rpymci)r&   rM   listdirfilesscript_filesmodule_filesendswithr   )rI   dirlistdirri   targetr   r   r   r=      s.    




zScript.scan_script_filesc                 C   s   | j }|jdd d g }|D ]>\}}tjr>dd l}|d tjj  | dd||| qdd t	|D }|jd	d d d
d |D | _
| j  d S )Nc                 S   s   | d p
d| d pdfS )Nr   r[   r   r   )itemr   r   r   <lambda>      z$Script.load_script.<locals>.<lambda>)r/   r   rs   rq   c                 S   s   g | ]\}\}}|||fqS r   r   ).0indexpriocoder   r   r   
<listcomp>,  r   z&Script.load_script.<locals>.<listcomp>c                 S   s   | d | d fS )Nr   r   r   )r"   r   r   r   r   /  r   c                 S   s   g | ]\}}}||fqS r   r   )r   r   r   r   r   r   r   r   1  r   )rx   sortr&   
emscriptensleepdisplay	presplashpump_windowload_appropriate_file	enumerateinitcoder;   r>   )rI   rx   r   ri   r|   r   r   r   r   load_script  s    
zScript.load_scriptc                    s~    fdd| j D }|s$td  t|dkr<td  |d \}}g }| dd||| tj rptd	| j	  |S )
Nc                    s    g | ]\}}| kr||fqS r   r   )r   ri   r|   namer   r   r   7  r   z&Script.load_module.<locals>.<listcomp>zModule %s could not be loaded.   z-Module %s ambiguous, multiple variants exist.r   rv   ru   )
ry   rf   rb   r   r&   parserreport_parse_errors
SystemExitr;   r>   )rI   r   filesri   r|   r   r   r   r   load_module5  s    

zScript.load_modulec                 C   sH   t |}tt }|D ]*}|jd u r||| jf|_|  jd7  _qd S )Nr   )r#   inttimer   r?   )rI   r    ri   r1   versionsr   r   r   assign_namesK  s    
zScript.assign_namesc                 C   s   t |}t |}dd |D }dd |D }td ||}| D ]X\}}}	t|	D ]D}
|||
  }|||
  }|jd u rT|j|vrT|j|_||j qTqBd S )Nc                 S   s   g | ]}|  qS r   	diff_infor   r"   r   r   r   r   \  r   z&Script.merge_names.<locals>.<listcomp>c                 S   s   g | ]}|  qS r   r   r   r   r   r   r   ]  r   )r#   difflibSequenceMatcherget_matching_blocksr   r   add)rI   	old_stmts	new_stmts
used_namesold_infonew_infosmoldlnewlcountr"   oldnewr   r   r   merge_namesW  s    zScript.merge_namesr   c                 C   sN   t jj|||d}|du rdS | || | | g }| ||d}||fS )a9  
        Loads Ren'Py script from a string.

        `filename`
            The filename that's assigned to the data.

        `filedata`
            A unicode string to be loaded.

        Return the list of statements making up the root block, and a
        list of init statements that need to be run.
        )
linenumberNNNF)r&   r   parser   static_transformsfinish_load)rI   filenamefiledatar   r    r   r   r   r   load_stringj  s    
zScript.load_stringTNc                    sL  |s|S t j|d g }|D ]}||j q|D ],}t|t jjr4|jdkr4t jj	
|j q4j| |durt j|}|d j | s|d dkr|d7 }|D ]
}||_q fdd}  |D ]J}|j}	|| |j|	< |jr| }
|
r||
 |jr|  q؈jdur<j| j| |S )a  
        Given `stmts`, a list of AST nodes comprising the root block,
        finishes loading it.

        `initcode`
            A list we append init statements to.

        `check_names`
            If true, produce duplicate name errors.

        `filename`
            If given, a filename that overrides the filename found inside the
            file.

        Returns a list of statements that corresponds to the top-level block
        in initcode after transformation.
        N)python3r   r   cc                    s    sd S t jrd S d }d }d }| j}|jv r|}| }j| }t|tsntdt||j|j	|j|j	f nNt j
jrzd S jd||j|j	t j|j|j	|j|j	t j|j|j	 d S )Nz-Name %s is defined twice, at %s:%d and %s:%d.zRThe label {} is defined twice, at File "{}", line {}:
{}and File "{}", line {}:
{})r&   rL   r   r0   
isinstancer   r   reprr   r   r,   allow_duplicate_labelsrG   r   formatr   get_line_text)nodebad_namebad_nodeold_noder   check_namesrI   r   r   
check_name  s:    



z&Script.finish_load.<locals>.check_name)r&   astchain_blockr   r   r   RPYrestr   	py3_filesr   r   r;   take_translatesr   elide_filenamelowerrz   update_bytecoder   r0   get_initearly_executer1   extendr4   )rI   r    r   r   r   r1   r"   r   r   r   initr   r   r   r     sB    (


zScript.finish_loadc              	   C   s2   | t tdD ]}| tdddd qdS )zU
        Writes an empty version 2 .rpyc header to the open binary file `f`.
           IIIr   N)writeRPYC2_HEADERr   structpack)rI   f_ir   r   r   write_rpyc_header  s    
zScript.write_rpyc_headerc              	   C   sp   | dd | }t|d}|| | ttd|d   d |td||t| | dd dS )z
        Writes data into `slot` of a .rpyc file. The data should be a binary
        string, and is compressed before being written.
        r   r   r      r   r   N)	seektellzlibcompressr   rb   r   r   r   )rI   r   slotdatastartr   r   r   write_rpyc_data  s    
zScript.write_rpyc_datac                 C   s   | dd || dS )z<
        Writes the md5 to the end of a .rpyc file.
        r   r   N)r   r   )rI   r   rD   r   r   r   write_rpyc_md5  s    zScript.write_rpyc_md5c           	      C   s   | d}|dtt tkrF|dkr*dS |d |  }t|S tt}td|||d  \}}}||krvq|dkrdS |d7 }qN|| | |}t|S )z
        Reads the binary data from `slot` in a .rpyc (v1 or v2) file. Returns
        the data if the slot exists, or None if the slot does not exist.
        i   Nr   r   r   r   )r.   rb   r   r   r   
decompressr   unpack)	rI   r   r   header_datar   posheader_slotr   lengthr   r   r   read_rpyc_data  s"    





zScript.read_rpyc_datac                 C   s   t j| dS )z
        This performs transformations on the script that can be performed
        statically. When possible, these transforms are stored in slot 2
        of the rpyc file.
        N)r&   r9   restructure)rI   r    r   r   r   r   G  s    zScript.static_transformsc              
   C   s  | ds| drP|s&td| |d\}}}|d | }|d | }|d }|d | d }	tj|}
i }t|d< | jpd|d	< |
d u r|g fS t }|	|fD ]}zzhd
| _	t
|d}| |d}W d    n1 s0    Y  |d urt|\}}| ||
| ~~W n ty,   Y n0 W d| _	qd| _	0 q| |
tj| t||
f}| |
 t||
f}tjsFzt
|d}| | | |d| | |d| t
|d"}t|  }W d    n1 s0    Y  | || W d    n1 s0    Y  W n$ tyD   dd l}|  Y n0 d| _nZ| dsh| drd }d }
tj|}dD ]P}z*| ||}|rt|\}}
W  qW n ty   Y n0 |d qW d    dS |d u r
t d| W d    dS t!|t"s&W d    dS | jrR|#d	d| jkrRW d    dS |d tkrpW d    dS |dk r| |
 W d    n1 s0    Y  ndS ||
fS )Nrq   ru   z4Cannot load rpy/rpym file %s from inside an archive./z/old-r   r   unlockedr/   Fr%   r   Twbr   r   rs   rv   )r   r   r   zFailed to load)$rz   rf   
rpartitionr&   r   r   script_versionr/   setr5   r   r   r   r   r   r   r   r   macappr   r   r@   rA   r.   rD   r   	traceback	print_excrE   rM   loadr   printr   dictr`   )rI   r|   ri   rl   _r'   olddirfullfnrpycfn	oldrpycfnr    r   r   mergefnrpycfbindataold_datar   $pickle_data_before_static_transforms#pickle_data_after_static_transformsr   fullf	rpydigestr   r   r   r   r   	load_fileQ  s    *


20



,zScript.load_filec                 C   sZ  d }|d u r|| }|| }|  ||| \}}	|d u rFtd|f tj|| 4}
|
t j d |
	t j}W d    n1 s0    Y  nR|d | | }|d | | }tj
| tj|rt|d"}
t|
	  }W d    n1 s0    Y  nd }zhtj|r~t|d4}
|
t j d |
	t j}W d    n1 sr0    Y  nd }W n ty   d }Y n0 d }tj|rtj|rtjjj}|}d\}}	z>||kr|s|  ||| \}}	|d u rtd|  W nH tyZ   tjjd| tjj  dtjv rVtd	|  Y n0 |d u rz|  ||| \}}	|}nVtj|r|}|  ||| \}}	|}n*tj|r|}|  ||| \}}	|}|d ur| j||f |d u rtd
| | jd u r|d | _n| j|d kr:t|d | j|	||d | j| d S )NzCould not load from archive %s.r   r   r%   r   zCould not load zWhile loading %rRENPY_RPYC_EXCEPTIONSzWhile loadingzCould not load file %s.r/   z{ does not share a key with at least one .rpyc file. To fix, delete all .rpyc files, or rerun Ren'Py with the --lock option.)r   )r	  rf   r&   rM   r   r   r@   rA   digest_sizer.   add_autor)   r*   r+   r   rD   r'   argscompiler   r   logr   	exceptionr_   rF   r   r/   r   update)rI   compiledsourcer|   ri   r   r   rpyfnlastfnr    r   rD   r   r  
rpycdigestforce_compiler   r   r   r     s|    242






zScript.load_appropriate_filec                 C   sl   zTt jt4}tt| \}}|tkr4|| _	W d   n1 sH0    Y  W n t
yf   Y n0 dS )z0
        Init/Loads the bytecode cache.
        N)r&   rM   r   BYTECODE_FILEr   r   r   r.   BYTECODE_VERSIONr6   rf   )rI   r   r   cacher   r   r   r<   -  s    (zScript.init_bytecodec                 C   s  | j D ]*}ztj|d W q ty.   Y q0 qg | _ | jD ]}| t }|jd tjj	v rj|d7 }| j
|d}|du rd| _tjj}d|jd |jd f tj_z|jdkrtjj|j|jd |jd |jd	}nb|jd
krtjj|j|jd |jd |jd	}n0|jdkr@tjj|j|jd |jd |jd	}W nt ty } zZ|j}|du rhd}tjj|j|j|j||jd}tjj|j W Y d}~q>W Y d}~n
d}~0 0 |tj_|| j |< t!"||_#q>g | _dS )zy
        Compiles the PyCode objects in self.all_pycode, updating the
        cache. Clears out self.all_pycode.
        evalr   s   _py3NTz7While compiling python block starting at line %d of %s.r   exec)r   linenopyhider[   )r   numbermsgliner   )$r3   r&   r   
py_compilerf   r2   get_hashMAGIClocationr   r6   r`   r8   r'   exception_infomodepy_compile_exec_bytecoder  r  py_compile_hide_bytecodepy_compile_eval_bytecodeSyntaxErrortextr   
ParseErrorr   r  r!  offsetparse_errorsr   messager7   marshalr   bytecode)rI   r"   r/   r   old_eier-  pemr   r   r   r   <  sN    


&&(
"
zScript.update_bytecodec                 C   s   t jr
d S | jr~zZt jt}t|d0}t| jf}|	t
t|d W d    n1 s^0    Y  W n ty|   Y n0 d S )Nr   r   )r&   r   r8   rM   get_pathr  r   r  r7   r   r   r   r   rf   )rI   ri   r   r   r   r   r   save_bytecode~  s    
8zScript.save_bytecodec                 C   s   t |tjjr|jd j}tjj||}|}| j	|d}|du rjtjj
durjtj
|}| j	|d}|du rtdt| | j	| S )zp
        Looks up the given label in the game. If the label is not found,
        raises a ScriptError.
        r   Nzcould not find label '%s'.)r   r&   r   SubParseblockr   r,   label_overridesr`   r0   missing_label_callbackr   r   )rI   labeloriginalr!   r   r   r   lookup  s    zScript.lookupc                 C   s>   t |tjjr$|jsdS |jd j}tjj||}|| j	v S )zG
        Returns true if the label exists, or false otherwise.
        Fr   )
r   r&   r   r9  r:  r   r,   r;  r`   r0   rI   r=  r   r   r   	has_label  s    zScript.has_labelc                 C   s$   |du rdS |  |sdS | |S )zR
        Looks up the label if it exists, or returns None if it does not.
        N)rA  r?  r@  r   r   r   lookup_or_none  s
    
zScript.lookup_or_nonec                 C   s   | j D ]}|  qg | _ dS )z=
        Analyzes all statements that need analysis.
        N)r4   analyze)rI   r"   r   r   r   rC    s    

zScript.analyzec                 C   s8   t jjsd S t jjrd S | jt j_t j r4tdd S )Nr   )	r&   r,   	developerignore_duplicate_labelsrG   r   r0  r   r   rH   r   r   r   report_duplicate_labels  s    

zScript.report_duplicate_labels)r   )TN)r   r   r   r   rJ   rY   rp   r=   r   r   r   r   r   r   r   r   r   r   r   r	  r   r<   r   r8  r?  rA  rB  rC  rF  r   r   r   r   r$   [   s4   .5)!

v
(
siB
r$   )-
__future__r   r   r   r   r   renpy.compatr   r   r	   r
   r   r   r   r   r   r   r   r   r&   r@   r)   r   r   r2  r   r   renpy.compat.pickler   r   rg   r   r  imp	get_magicr%  Zimportlib.utilr   r   r  rf   r   r#   objectr$   r   r   r   r   <module>   s0   8
