"""Build local UI introspection overrides from this installation's assets. Uses pyflashkit (MIT), zopfli and the locally checked out Conjunction packer (own-use license). Original game bundles are read only. No game assets are redistributed. Unknown GFx tags and CR2W payloads are preserved verbatim. """ from pathlib import Path import hashlib import json import struct import sys import zlib ROOT=Path(__file__).resolve().parent.parent # Index bundle locations are relative to the configured game installation. def indexed_bundle(value): path=Path(value) if path.is_absolute() or '..' in path.parts:raise ValueError('Nonportable UI index bundle path') return game_paths.GAME/path from . import game_paths sys.path[:0]=[str(game_paths.tool_path('repos/pyflashkit')),str(game_paths.tool_path('repos/Conjunction'))] import zopfli.zlib from flashkit.swf.parser import parse_swf from flashkit.abc.parser import parse_abc,write_u30 from flashkit.abc.writer import serialize_abc from flashkit.abc.types import MultinameInfo,NamespaceInfo,MethodInfo,MethodBodyInfo,TraitInfo from conjunction import packer,metastore from . import support def patch(original): offset=original.index(b'CFX') capacity=struct.unpack_from('>6==82: start=raw.index(b'\0',p+hs+4)+1 before=raw[start:p+hs+size];a=parse_abc(before) def qualified(mn): m=a.multiname_pool[mn];ns=a.namespace_name(m.ns) return (ns+'.' if ns else '')+a.string(m.name) inst=next((i for i in a.instances if qualified(i.name)==root),None) if inst: assert hs==6 and serialize_abc(a)==before break p+=hs+size else:raise ValueError('Document class not defined in this movie') def string(v): if v not in a.string_pool:a.string_pool.append(v) return a.string_pool.index(v) def qname(v,package=''): ns=next((i for i,n in enumerate(a.namespace_pool) if n.kind==0x16 and a.string(n.name)==package),None) if ns is None: ns=len(a.namespace_pool);a.namespace_pool.append(NamespaceInfo(0x16,string(package))) a.multiname_pool.append(MultinameInfo(kind=7,ns=ns,name=string(v))) return len(a.multiname_pool)-1 def op(code,*args):return bytes([code])+b''.join(write_u30(i) for i in args) scope=next(b.init_scope_depth for b in a.method_bodies if b.method==inst.iinit) def method(name,code,return_type='Object',params=(),getter=True,extra_locals=0): idx=len(a.methods) a.methods.append(MethodInfo(param_count=len(params),return_type=qname(return_type),param_types=[qname(t) for t in params],name=string(name),flags=0)) a.method_bodies.append(MethodBodyInfo(method=idx,max_stack=16 if extra_locals else 8,local_count=1+len(params)+extra_locals,init_scope_depth=scope,max_scope_depth=scope+1,code=b'\xd0\x30'+code+b'\x48')) inst.traits.append(TraitInfo(name=qname(name),kind=2 if getter else 1,method_idx=idx)) parent,index,other=qname('pdParent'),qname('pdIndex'),qname('pdOther') inst.traits.extend([TraitInfo(name=parent,kind=0,type_name=qname('Object')),TraitInfo(name=index,kind=0,type_name=qname('int')),TraitInfo(name=other,kind=0,type_name=qname('Object'))]) getparent=b'\xd0'+op(0x66,parent) method('pdProbe',op(0x2c,string('PathDevkitUIv3')),'String') method('pdSame',getparent+b'\xd0'+op(0x66,other)+b'\xac','Boolean') method('pdAttached',getparent+op(0x66,qname('stage'))+b'\xd0'+op(0x66,qname('stage'))+b'\xac','Boolean') method('pdChild',getparent+b'\xd0'+op(0x66,index)+op(0x46,qname('getChildAt'),1)) method('pdBounds',getparent+b'\xd0'+op(0x66,qname('stage'))+op(0x46,qname('getBounds'),1)) classname=qname('getQualifiedClassName','flash.utils') method('pdClass',op(0x5d,classname)+getparent+op(0x46,classname,1),'String') event=qname('MouseEvent','flash.events') method('pdDispatch',getparent+op(0x5d,event)+b'\xd1\x26\x27'+op(0x4a,event,3)+op(0x46,qname('dispatchEvent'),1),'Boolean',('String',),False) if root == 'red.game.witcher3.hud.Hud': from .overlay_flash import LAYER, methods def uint(value): if value not in a.uint_pool:a.uint_pool.append(value) return a.uint_pool.index(value) inst.traits.append(TraitInfo(name=qname(LAYER),kind=0,type_name=qname('Object'))) for declaration in methods(op,qname,string,uint): method(*declaration) after=serialize_abc(a) raw=raw[:p+2]+struct.pack('capacity:raise ValueError('Patched stream exceeds fixed CR2W allocation') out=bytearray(original);out[offset:offset+capacity]=replacement+bytes(capacity-len(replacement)) table,count,_=struct.unpack_from('