From bfe3f1d544bee6a3a2f4a1d188a531eb8f377099 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 31 Jan 2026 13:36:20 +0000 Subject: [PATCH] 14:36 --- core/__pycache__/obfuscator.cpython-311.pyc | Bin 9521 -> 10103 bytes core/__pycache__/parser.cpython-311.pyc | Bin 6125 -> 7073 bytes core/obfuscator.py | 92 ++++++++++---------- core/parser.py | 35 +++++--- parser_b64.txt | 1 - 5 files changed, 68 insertions(+), 60 deletions(-) delete mode 100644 parser_b64.txt diff --git a/core/__pycache__/obfuscator.cpython-311.pyc b/core/__pycache__/obfuscator.cpython-311.pyc index 11ea2ea393b0800db5024a92c71364f44e77de98..cea59a10923d75656cdd46f4fbe0e8c9ba7c6d74 100644 GIT binary patch delta 3152 zcma)8eM}q46`!$p*K1?I#%pW{5F0xQ=JIj*JWB3zfPo7fkTfv~U$$|)#*pw4t(^p@ zIZ`!J>?mrTPO2dFTKS~(%IWe4*Zk2`s#g5PJM-R~H*em1^L9V${!d@oPfJS;fV8CcgNc7veOBhl$zabR*iFo~xw%K=FVNDqI`pVHKgXaF$m+vgY_cAV?o*VEtH?>O0droE%D zqut^6Kb$M^YBXtxepTv0zbN}X`crv5I%b+cg4v9IX)?gm=nZoX`m@PRD3uVA*>_YP zHnZ>=nbo5w<_?t-tYG2nGfF-Bq=K$6b1q_d$o4Q_VSws<7o9`2Q}_m&7A9*QV+|A2 zvH0*I|6YeWIJ`MH;`g8uYlA%=ie1_tyBxYU-4Hz28|>-o?C=hHdpgkHt??cw(H5(M z09*%orVCauc{iwZ<#(w=pkf;YmDP7g9$WXUTlXw;vh{#sJ+K00>%qJi7Rhy^2bI<3 z?-!GN5IwD|D(^3*1ytHw!D8C@>$jLFmYapB*H&elE2gPNCb_&%gBEO##Q`kvV} z5{$@H5!V!&$X+5j0D$y|gl2&yJZ_7ZGF}DJo2imaK@{=!nw`2Wc7jbAUtCZH(9^1F zX8KU71VOv2k}nq4fRtVpN2j0-vDFpkI9pImk#O`?lyjsV_-!;V7q|tT1|+lzU4j)2 z^HheT_%Y#qs@hD~L2%n%*sZRlc(=HYatrOb?$LrC(GX=mCVBBGc?yka`3sRM_KWeE zU#5s(c-0O@EG^oB-)={O$uKBn7tn^m)F4$P`0d_#o9va)5EDwt;mEbRz4w`$%8S)* zcav2+S`d5Jv?V4&5k|G!&yXrf8B*ZlbFYf#54C53r=m}y?s_<1Jq6(v^tT;Xd&1BC zM;JVLEadrmp7`Sj%sr3*A}rcV^UM=7gs$D9rx=nQ2UA)SY12#X2Mj{Rojf?h!IC#p{Y1eU)HopHPOez_emY(>^ZiJ zR6U>6Nyd_%0tpiEXmZ?H-uG zpFk@+_AhQHC+O-w5_obQWXmn-&fAHlYwP8^ary47#kQoo`%b1CTlSNl6&#O+J?p}r zW#K{X%2ma4VpY33girf$|A=zhuXF@(Lr@k*6=4)__Kg?Td`s~xdRNb`dDcAGH-HC6 z6<}l`rpNBqVCPY6rafz_d2Dj6n_L;+egDc^%C6&&&aQe^ zJ!|dwn*(3=%56hR+YojS%cc>%1cazql8MdBKUxNcH9Ll4C+r zG$xT+2qgLe$#J1rY$7_X#xBRI?v~0Zmwrw>BrbI9r*ME0xUJwx?@($pft)rPi5K{# zYM64`U?>*P@zasnIE{%ErBclFj2OxBV^^lbD)AsC^28oBd^z3K(4F>g9nV@zOAFdj+qyVIIql>&KB0}oRj5qrv$m?FF>AFY4OxqjK(wtG$AZ(4d!wzw6GI}aH1ILsb6mhN7PXF5Mj$im*NP?Ldo zdebNGoP32R&aX|ZP2hnM><=pgqTClz`XabtOctVwz(lbSMH_WpW?CI+YJa^-3MwhZ zkd)WTho^TQhUG^WbfQ)SlZFLeWa2h-%TdFLY=X}i)z6MYSH}Dh{D?uzj#|i}-#MBp z)w(}VfABgaX+~;x{1+NLrO4=X!5Dhe>8Q);<5DObxey9plDf#QIm6hDG!=@IbPjv; z=)BWaGeyb#g}HbnOmiPbB()CF>*zpf=MmR^GK&3!fLfKlH-BqBmAIY2{J~6brZ=xK z=)DmA)p=!6Lk4-Ug;`?_-t1XR+fTHa){nUR9JaKPzU+D2cw)WrgxuJoG`19I*x0tM z(>s%AsY)`4xrq%+t$bS@_)YzBf}htJytUfT8ydZK?H3h#NG4y{S#K@+b$t^TBf`=R z^t9dyCA8D!^cmP7F|P>iJ&>);gCZQ!9A@cNE5eJ=$kJoI8qs%MG3O1klLty4kXMX; zBpqtimnd0^+hZ^cVWLK~8lI<`sZ9K(PRFm=HAJgs}%lW^=>SLnL$I$VCydk$Xp4F`hZU;sv= zL_Hdl61>t2uZR6zesN(jrU)}h9tLmo@g+r+WP2QnMmd=J=F~A-HlpRetFt&V9ynj1t1LkYK#&xtIbk zU4rfY_LF{yFP!6$sF2!X$>beKlHs{yAAG9JlYjK>+CSeQ_%T6JmT3C0biI6>m$3LKr2_!!4?Fd)dd)A2dne$s9C)tH3a%5xqxZE8P*O@{e| zAY(r;7LW6?j1K~)1!WG74-UhHm^?3}5>OTvmXi3q#1z^xS=o%j#Ux1nR@$B)sBA6I!R!^FBC$5#TwW4zu79vh(*J}(d)xu^u*Fk?V*Oc z^A;>CPZnuaNE8>*Yjtnb9CsNX1Nu!}tKmq1igj)>JR=GpOXBK}ZL1XA1267jI^~{S zN~x`PmruMr6Y>j?hnL_Yp2wtnYV6unXrMpnpYVr*WfY#Ul&C-%k_jUN>rKa`)XAy9 zfw88yta7)HU4wo}Py{mK{V^fAB=Nnl9eTY`5%Kxs6LIsNvUf5i_=g(n6(_{)mF*M& zZ-SC;ma5Z~z62T^r7x>2Q0X+!! zc4zcu`+DP3`HD^rp+{6RG_B}3{oPssGP?D%WoT>FQz)lXv_~NTAntKi_H+8XSjg!U znrD+(|LrqXRUcNxm3`YP(%{NzXt)|uDKaJiXauDM~b7AsUSoA^a7N=W{C7A*=kZ^ z7snqM<0AGoC2+S7^b(&?83yN}dXXg*saHa zIESMlj@bjl%pHY~i+D6BUN&F}`jO+T?nBJTe?otA*cqeIgPu5+&QnGY{?Iuj#d`OT znGN3ux(B*ky_-BW&pwawDlymq?KvCkHOwb&%jG`ilk=1x^L<*s27T^)SvQ9RNz3S* ztBp#c8?LsnQ9DJIKm~2MMQIyKpn{Gwc5M`cDrir&7H9WGg7#c;+cJ*fp`=Ap6S>Qv zbXJXeL_R{(9T#*9WGkZ&I_llTK=KmOLkK}XFXR7>PmD&i3bhiEvZk65$ diff --git a/core/__pycache__/parser.cpython-311.pyc b/core/__pycache__/parser.cpython-311.pyc index 5d032f5382b5869f59af87e9c0483ef21161be55..9b9da3ca1a76f404daa3dbab4da8bca0c76f3025 100644 GIT binary patch delta 2855 zcma)7T~Hg>72aR{AqkL#z`{80Lb1f3Vh1-d<|kmV>jcXX)Yue(Dti~97$o_w%wT!3 z+?hVOEf3(u6Oxb_{F0JsJ442Ka9$F>HJNEUyB^O}v(wJBeak~;3@l5 zZJB3XVSiV}TIPCSe@`*+qG!^oQ1Rz8C{klos6K)i+}Up@044zhfFl%DpuUY^F+bST zqulIKx_gw~9wpWRKe8yjy-Iht5^Ggr?Mkdg2`fKVVpB?^g1xZ1SBV9cSV)Q8P-2V9 zHm&6C%1Nb5X~V&W_0;)u=Y}HDJk#EuXFFQ@^1RaC-qB)M#-i_>jhxA|!9HhKp6gI1 zrt<7$s71ZYeAaC6VSY>TYvO_M#t_ zilWzt7uz!n<*r`#{QAPuh1<+OTvh+KSh7&waax<0)IF2hFD~hxOPVyJduFsNbGm0v z%Oc%_N)$Cu_cMhj6y@z_Zd-ATyY^#d-LcWRDcn1;iSG4o4e5<19t`M>y$?Nl%(4VjCb>olg#x>{9i>{ixXf?kfY+Sq7f+NCP1G?~&2Yy}Xdno9_;6tPfXTQd} zh&KAJF1)LaPw2vgHkHtYgeFew!n9UMrF9{#U6pk~*5(&&y*8ZUDK-yvag6Z?exQYJeSNpThDs=<0@nV8m{&bF3fYwZ-e%2tr0DY9 zy|nt?hF|vwANzxvGgPXy-T#XFH=RZvJP61MKpqHqF^BA^1}_FXE2YUOp#V^RE|J57 z<^$Q1$Y!OqXz)ptxt29pEQdH$k4-2Db}pTmlMEh7Nohgmum(mA32L>qfo@R4)~(~T z>rk=Ew`^Ort@W?CU@g^9mENV9kG*Tr_3;l5tsi>iKBBpgl>AhcZ|R0+3!o;|XKVE| z!c{sjd`h7vkOg(rCIoAFM~Qk4$DDk9i7E%2<0IRrZ@?zVCPR!_H7I200F((vxJtfq zB!OfJb;RSTbSf8*x2cM~fo@Viv@d(0MF_tzDAJJ-i|W;GM|~Xx8(dbBuH-9rqJg}s zW*j{@@{f)H^9iNK-K{*Uu*jx9bNtfRjAL*iU62gYLMzqZRJ=wD>X#J1R% z-Giq>mQ%@eG{hNP?s`^2ZNzClF?&ryr-@T=n+|Q$ZKxV791fnvg`LI3^yLyLDWAss zC%xzcrz%ovTw2H?38&Ab5o~8NWHE1CFZl2&QA+0w7GKG5q@{&ioV*^Cs$9dU0vXLu#D8d;7M1Bd<=2tNvh^}x}`fuqZ(icM`>(u4lbXTJ7o7bdj& z82o*)`(3_^^ue;GL&HhaqcAHS>fdX85rr0+16Xe!BD>Du4;g2E$jm#>n4NqJdxU5N z-P;{+=+d6?9#QN32Q5iZht)2BL!qu=EwOIfJib-^hc~p!PWan8$@7I%!GnQbhkSMv z2KiNhqX6)ne1EslAW-Nq8UZ*4U_PWE!6XxC#e%$u;nvPi{l*_Dyu$lR)N>f1S^jv5 zDhD$mz4w{6{X8S&bz)Qf(GU0|%`6owK@({z!t-y**f*G%J=Me{|1 dKZGX~wmb#EW(*^AAB{^YhbN$Wx{ra>`af4Zopb;I delta 1911 zcmZ`(O>7fK6yCLW{S$lbB;NQZHUS)Hya78RfC{M{N|R8bl0ceB0eKzQjW;31v8`Ey zsBtk!J>-x|Nr$SmO4ZOqX;f9BN)J^<5mHaRIVhE)Z4an74wZ~bFX^T4t?Lk)X6*fD z=FRusy!YnK+nAUB~h8>u;ZA^Q42deO2>XfO( zBu#a9(#!}WIfjS<1e&?;?BL+Q&}h*W4$E;l9_c(GCjd&}aHLZ+jf}o|dg%4ykznXV zk1R(@@$sN6C#23`N$v>kuFGUyCU=y!T96a8%zaxfMq{zk^;jtyEA_@oas+?Sm3n(i z(P$|yACSXxXV?Dm_Lv;)l|!;5Uz6VhJBe3%4(>@vW{sQ3r4`Lo$X!yhs%FgRRLwvX z%{-Bt$xmlsrT^^c$unm(o?Ol-s$|eORmn^eJ4B}%7zmQy<%b8qf$0rqUG&a!bJle? zcD8kw2%V*BVjP~&42unw_TxWx-#@JWp5P}~1(KCGpwo;QC969| znsLg{qzV%kiGX8MDxX)fX^o#ExyyOY2xEyOE?6RdT<5Z>83kfgrj)BHR_q>VM8qYF zo|@^RQgqM{8XRmhU2M1~8icmTjpDrJmgV-z1p)i2hrVTV+q_JpICrgLX(euY({`Y_ z1^3tsu*?uQjz#*w77W?>hAOiO0fF~dne6~JEcWGN7=j+6WT^R!0?0nPsXIwSB$LzG=|VCYq)+U-ST{8b^Uatb(u#n(l99ORvf%gX5psD&xm2|MtBsSF=x;&{ zG+Q05>=!ibJIEU8q~oUOLtO(2qaCz}Ii!C(4znWdat2JDAW~Cw*x6#6AD+8fvFuyo zmNdbH{N0A_Y#^F@FO51;-fjdC>Ni>#QN4{>GOS>rRgiByK*8@9p5pfXf- zdMZ0AaT-^6H?NT0I_XL(b6FuhIw`clN*nA!V#5^V7q0T<=36!)^s29bhjFX3+!$2bKj@FPyCDirQyZ4uMR)*MJm1s{ngvv{5|_! z=;26ZWUS&jkN?K=D?fP7v;J*q#2VAn_!?9}Uuc9OM?fO61jDRX<015GYc=-M2flzw zMsbin_U(GR=HRSTzX70)WP;TKS2u{`c7%Nh`hD5VBsqpMObY2o*pE=VMmXxI!(N(c z9WS@>VwKs1S6L(Ptuou8W&m~J9Gz$aZ+y+ziyLfMqBFTPJbb<6G*Ga>)QbRBBg?Yu pjP-{8t8xJru5VyW$F1;`ft6 1 then for i=1, c-1 do set_r(a+i-1, res[i]) end end end - -- RETURN dispatch[op_map[{self.opcodes.index("RETURN")+1}]] = function() vip = #insts + 1 end - -- Execution Loop while vip <= #insts do local inst = insts[vip] local f = dispatch[inst[1]] - if f then - f(inst[2], inst[3], inst[4]) - end + if f then f(inst[2], inst[3], inst[4]) end vip = vip + 1 end end --- Stealth Execution -task.spawn(function() - local s, e = pcall(_VM_LIFECYCLE) - if not s and _ENV.warn then - -- _ENV.warn("VM Critical Failure: " .. tostring(e)) - end -end) +task.spawn(_VM_LIFECYCLE) """ return vm_lua def compile_to_bytecode(self, ast): constants = [] instructions = [] + locals_map = {} + next_reg = 0 def add_const(val): enc = self.encrypt_constant(val) @@ -148,18 +121,42 @@ end) constants.append(enc) return len(constants) - 1 + def load_expr_to_reg(expr, reg): + if expr['type'] == 'IDENT': + if expr['value'] in locals_map: + instructions.append([self.op_to_id["MOVE"], reg, locals_map[expr['value']]]) + else: + instructions.append([self.op_to_id["GETGLOBAL"], reg, add_const(expr['value'])]) + elif expr['type'] in ['STRING', 'NUMBER']: + instructions.append([self.op_to_id["LOADK"], reg, add_const(expr['value'])]) + for node in ast: if node['type'] == 'call': - const_idx = add_const(node['name']) - instructions.append([self.op_to_id["GETGLOBAL"], 0, const_idx]) - reg_idx = 1 - for arg in node['args']: - instructions.append([self.op_to_id["LOADK"], reg_idx, add_const(arg)]) - reg_idx += 1 - instructions.append([self.op_to_id["CALL"], 0, len(node['args']) + 1, 1]) + func_reg = next_reg + # func_reg might be used, so we need to be careful if we have many locals + # For this simple obfuscator, we use registers above the current locals + + # Load function + if node['name'] in locals_map: + instructions.append([self.op_to_id["MOVE"], func_reg, locals_map[node['name']]]) + else: + instructions.append([self.op_to_id["GETGLOBAL"], func_reg, add_const(node['name'])]) + + # Load args + for i, arg_expr in enumerate(node['args']): + load_expr_to_reg(arg_expr, func_reg + 1 + i) + + instructions.append([self.op_to_id["CALL"], func_reg, len(node['args']) + 1, 1]) + elif node['type'] == 'assign': - instructions.append([self.op_to_id["LOADK"], 0, add_const(node['value'])]) - instructions.append([self.op_to_id["SETGLOBAL"], 0, add_const(node['name'])]) + val_reg = next_reg + load_expr_to_reg(node['value'], val_reg) + + if node.get('local'): + locals_map[node['name']] = val_reg + next_reg += 1 + else: + instructions.append([self.op_to_id["SETGLOBAL"], val_reg, add_const(node['name'])]) instructions.append([self.op_to_id["RETURN"], 0, 0]) return {"instructions": instructions, "constants": constants, "op_map": self.op_map} @@ -175,7 +172,8 @@ end) bytecode = self.compile_to_bytecode(ast) return self.generate_vm_source(json.dumps(bytecode)) except Exception as e: - return f"-- Error: {str(e)}" + import traceback + return f"-- Error: {str(e)}\n{traceback.format_exc()}" def obfuscate(code): - return LuauVMObfuscator().obfuscate(code) \ No newline at end of file + return LuauVMObfuscator().obfuscate(code) diff --git a/core/parser.py b/core/parser.py index 67f7e63..3676e12 100644 --- a/core/parser.py +++ b/core/parser.py @@ -5,14 +5,25 @@ class Lexer: self.code = code self.tokens = [] self.pos = 0 - # Correctly formatted rules with escaped backslashes for regex character classes + + # Using chr() to build regex parts and avoid tool-induced corruption. + LB = chr(91) + RB = chr(93) + DQ = chr(34) + SQ = chr(39) + BS = chr(92) + + # List every operator individually to avoid character set issues. + OP_LIST = '==|~=|<=|>=|\.\.\.|\.\.|>>|<<|\+|\-|\*|/|%|\^|#|=|\<|\>|\(|\)|\{|\}|' + BS + LB + '|' + BS + RB + '|;|:|,|\.' + self.rules = [ - ('COMMENT', re.compile(r'--\[\[.*?\].*?|--.*', re.DOTALL)), - ('STRING', re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\\\])*\'|\[\[.*?\].*?\]', re.DOTALL)), + ('COMMENT', re.compile('--' + LB + LB + '.*?' + RB + RB + '|--.*', re.DOTALL)), + ('STRING', re.compile(DQ + '(?:' + BS + BS + '.|[^' + DQ + BS + BS + '])*' + DQ + '|' + SQ + '(?:' + BS + BS + '.|[^' + SQ + BS + BS + '])*' + SQ + '|' + LB + LB + '.*?' + RB + RB, re.DOTALL)), ('NUMBER', re.compile(r'\d+\.?\d*')), ('KEYWORD', re.compile(r'\b(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b')), ('IDENT', re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')), - ('OP', re.compile(r'==|~=|<=|>=|\.\.\.|\.|>>|<<|[\+\-\*/%^#=\<>\(\)\{\}\[\];:, ]')) + ('OP', re.compile(OP_LIST)), + ('SPACE', re.compile(r'\s+')) ] def tokenize(self): @@ -26,7 +37,6 @@ class Lexer: self.pos = match.end() break if not match: - # Skip unknown characters self.pos += 1 return self.tokens @@ -41,7 +51,7 @@ class Parser: def consume(self, expected_type=None, expected_value=None): token = self.peek() - if not token[0]: return None + if not token or not token[0]: return None if expected_type and token[0] != expected_type: return None if expected_value and token[1] != expected_value: return None self.pos += 1 @@ -59,15 +69,16 @@ class Parser: def parse_statement(self): token = self.peek() - if not token[0]: return None + if not token or not token[0]: return None if token[1] == 'local': self.consume() ident = self.consume('IDENT') - if ident and self.peek()[1] == '=': - self.consume() - val = self.parse_expression() - return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True} + if ident: + if self.peek()[1] == '=': + self.consume() + val = self.parse_expression() + return {'type': 'assign', 'name': ident[1], 'value': val, 'local': True} return None if token[0] == 'IDENT': @@ -92,4 +103,4 @@ class Parser: def parse_expression(self): token = self.consume() if not token: return None - return token[1] \ No newline at end of file + return {'type': token[0], 'value': token[1]} diff --git a/parser_b64.txt b/parser_b64.txt deleted file mode 100644 index 2302f20..0000000 --- a/parser_b64.txt +++ /dev/null @@ -1 +0,0 @@ -aW1wb3J0IHJlCgpjbGFzcyBMZXhlcjoKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBjb2RlKToKICAgICAgICBzZWxmLmNvZGUgPSBjb2RlCiAgICAgICAgc2VsZi50b2tlbnMgPSBbXQogICAgICAgIHNlbGYucG9zID0gMAogICAgICAgIHNlbGYucnVsZXMgPSBbCiAgICAgICAgICAgICgnQ09NTUVOVCcsIHInLS1cW1xbLio/XF1cXXwtLS4qJyksCiAgICAgICAgICAgICgnU1RSSU5HJywgciciKD86XC58W14iXF0pKiJ8XCcoPzpcLnxbXlwnXF0pKlwnfFxbXFsuKj9cXVxdJyksCiAgICAgICAgICAgICgnTlVNQkVSJywgcidcZCtcLj9cZConKSwKICAgICAgICAgICAgKCdLRVlXT1JEJywgcidcYihhbmR8YnJlYWt8ZG98ZWxzZXxlbHNlaWZ8ZW5kfGZhbHNlfGZvcnxmdW5jdGlvbnxpZnxpbnxsb2NhbHxuaWx8bm90fG9yfHJlcGVhdHxyZXR1cm58dGhlbnx0cnVlfHVudGlsfHdoaWxlKVxiJyksCiAgICAgICAgICAgICgnSURFTlQnLCByJ1thLXpBLVpfXVthLXpBLVowLTlfXSonKSwKICAgICAgICAgICAgKCdPUCcsIHInPT18fj18PD18Pj18XC5cLlwufFwuXC58Pj58PDx8W1wrXC1cKi8lXiM9XDw+XChcKVx7XH1cW1xdOzosXC5dJyksCiAgICAgICAgICAgICgnU1BBQ0UnLCByJ1xzKycpCiAgICAgICAgXQoKICAgIGRlZiB0b2tlbml6ZShzZWxmKToKICAgICAgICB3aGlsZSBzZWxmLnBvcyA8IGxlbihzZWxmLmNvZGUpOgogICAgICAgICAgICBtYXRjaCA9IE5vbmUKICAgICAgICAgICAgZm9yIG5hbWUsIHBhdHRlcm4gaW4gc2VsZi5ydWxlczoKICAgICAgICAgICAgICAgIHJlZ2V4ID0gcmUuY29tcGlsZShwYXR0ZXJuLCByZS5ET1RBTEwpCiAgICAgICAgICAgICAgICBtYXRjaCA9IHJlZ2V4Lm1hdGNoKHNlbGYuY29kZSwgc2VsZi5wb3MpCiAgICAgICAgICAgICAgICBpZiBtYXRjaDoKICAgICAgICAgICAgICAgICAgICBpZiBuYW1lICE9ICdTUEFDRScgYW5kIG5hbWUgIT0gJ0NPTU1FTlQnOgogICAgICAgICAgICAgICAgICAgICAgICBzZWxmLnRva2Vucy5hcHBlbmQoKG5hbWUsIG1hdGNoLmdyb3VwKDApKSkKICAgICAgICAgICAgICAgICAgICBzZWxmLnBvcyA9IG1hdGNoLmVuZCgpCiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgaWYgbm90IG1hdGNoOgogICAgICAgICAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiBzZWxmLnRva2VucwoKY2xhc3MgUGFyc2VyOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHRva2Vucyk6CiAgICAgICAgc2VsZi50b2tlbnMgPSB0b2tlbnMKICAgICAgICBzZWxmLnBvcyA9IDAKCiAgICBkZWYgcGVlayhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi50b2tlbnNbc2VsZi5wb3NdIGlmIHNlbGYucG9zIDwgbGVuKHNlbGYudG9rZW5zKSBlbHNlIChOb25lLCBOb25lKQoKICAgIGRlZiBjb25zdW1lKHNlbGYsIGV4cGVjdGVkX3R5cGU9Tm9uZSk6CiAgICAgICAgdG9rZW4gPSBzZWxmLnBlZWsoKQogICAgICAgIGlmIGV4cGVjdGVkX3R5cGUgYW5kIHRva2VuWzBdICE9IGV4cGVjdGVkX3R5cGU6CiAgICAgICAgICAgIHJldHVybiBOb25lCiAgICAgICAgc2VsZi5wb3MgKz0gMQogICAgICAgIHJldHVybiB0b2tlbgoKICAgIGRlZiBwYXJzZShzZWxmKToKICAgICAgICBub2RlcyA9IFtdCiAgICAgICAgd2hpbGUgc2VsZi5wb3MgPCBsZW4oc2VsZi50b2tlbnMpOgogICAgICAgICAgICBub2RlID0gc2VsZi5wYXJzZV9zdGF0ZW1lbnQoKQogICAgICAgICAgICBpZiBub2RlOgogICAgICAgICAgICAgICAgbm9kZXMuYXBwZW5kKG5vZGUpCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICBzZWxmLnBvcyArPSAxCiAgICAgICAgcmV0dXJuIG5vZGVzCgogICAgZGVmIHBhcnNlX3N0YXRlbWVudChzZWxmKToKICAgICAgICB0b2tlbiA9IHNlbGYucGVlaygpCiAgICAgICAgaWYgdG9rZW5bMF0gPT0gJ0lERU5UJzoKICAgICAgICAgICAgaWRlbnQgPSBzZWxmLmNvbnN1bWUoKVsxXQogICAgICAgICAgICBuZXh0X3Rva2VuID0gc2VsZi5wZWVrKCkKICAgICAgICAgICAgaWYgbmV4dF90b2tlblsxXSA9PSAnKCc6CiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgYXJncyA9IFtdCiAgICAgICAgICAgICAgICB3aGlsZSBzZWxmLnBlZWsoKVsxXSAhPSAnKSc6CiAgICAgICAgICAgICAgICAgICAgYXJncy5hcHBlbmQoc2VsZi5wZWVrKClbMV0pCiAgICAgICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgICAgICBpZiBzZWxmLnBlZWsoKVsxXSA9PSAnLCc6CiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGYuY29uc3VtZSgpCiAgICAgICAgICAgICAgICBzZWxmLmNvbnN1bWUoKQogICAgICAgICAgICAgICAgcmV0dXJuIHsndHlwZSc6ICdjYWxsJywgJ25hbWUnOiBpZGVudCwgJ2FyZ3MnOiBhcmdzfQogICAgICAgICAgICBlbGlmIG5leHRfdG9rZW5bMV0gPT0gJz0nOgogICAgICAgICAgICAgICAgc2VsZi5jb25zdW1lKCkKICAgICAgICAgICAgICAgIHZhbHVlID0gc2VsZi5jb25zdW1lKClbMV0KICAgICAgICAgICAgICAgIHJldHVybiB7J3R5cGUnOiAnYXNzaWduJywgJ25hbWUnOiBpZGVudCwgJ3ZhbHVlJzogdmFsdWV9CiAgICAgICAgcmV0dXJuIE5vbmUK