Something went wrong. Try again.
Native control, inspection, UI automation and evidence capture for a running Witcher 3 process.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104"""Rebuild the native adapter from pinned local upstream/toolchain sources."""import hashlibimport jsonimport osfrom pathlib import Pathimport subprocessimport sysimport tempfile
ROOT=Path(__file__).resolve().parents[1]sys.path.insert(0,str(ROOT))from witcher_devkit.game_paths import tool_pathSDK=tool_path('repos/WitcherNativeBridge')COMPILER=tool_path('toolchains/llvm-mingw-20261006-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang++')BUILD=ROOT/'native/build'PIN='415e1c1e79a9d5ab78629c70cd3a76131ad98941'PATCH=ROOT/'native/patches/wnb-logchannel-register-args.patch'BRIDGE_SOURCE='src/NativeBridge.cpp'BRIDGE_SOURCE_SHA256='02e853f6f9bbc805fd0eee53750b27f6645cc39ccf2856953efe5f16c33fc7d3'
def verified_sources(sdk, pin): """Snapshot exact tracked bytes, including files hidden by Git index flags.""" git=['git','-C',str(sdk)] revision=subprocess.check_output(git+['rev-parse','HEAD'],text=True).strip() if revision!=pin: raise RuntimeError('Upstream SDK revision differs from inspected source') if subprocess.run(git+['diff-index','--cached','--quiet',pin,'--']).returncode: raise RuntimeError('Upstream SDK has staged tracked changes') tree=subprocess.check_output(git+['ls-tree','-rz',pin]) sources={} for entry in tree.split(b'\0'): if not entry: continue metadata,name=entry.split(b'\t',1) mode,kind,oid=metadata.split() relative=os.fsdecode(name) path=sdk/relative if kind!=b'blob' or mode not in (b'100644',b'100755') or path.is_symlink(): raise RuntimeError('Unsupported upstream tracked file: '+relative) expected=subprocess.check_output(git+['cat-file','blob',oid.decode()]) if not path.is_file() or path.read_bytes()!=expected: raise RuntimeError('Upstream SDK tracked contents differ: '+relative) if bool(path.stat().st_mode & 0o111)!=(mode==b'100755'): raise RuntimeError('Upstream SDK tracked mode differs: '+relative) sources[relative]=(expected,mode==b'100755') return sources
def prepare_source(destination, sources, patch_bytes): """Copy only verified bytes; no untracked includes or edits reach the compiler.""" if hashlib.sha256(sources[BRIDGE_SOURCE][0]).hexdigest()!=BRIDGE_SOURCE_SHA256: raise RuntimeError('Patch input differs from inspected NativeBridge.cpp') for name,(data,executable) in sources.items(): path=destination/name path.parent.mkdir(parents=True,exist_ok=True) path.write_bytes(data) path.chmod(0o755 if executable else 0o644) # Do not discover a containing repository, even when TMPDIR is in a worktree. env=dict(os.environ,GIT_CEILING_DIRECTORIES=str(destination.parent)) for options in (['--check'],[]): subprocess.run(['git','apply','--whitespace=error',*options,'-'], cwd=destination,env=env,input=patch_bytes,check=True) return hashlib.sha256((destination/BRIDGE_SOURCE).read_bytes()).hexdigest()
def timestamp_flags(compiler, directory): """Probe the actual compiler/linker pair, not a guessed linker executable.""" command=[str(compiler),'-shared','-x','c++','-','-o',str(directory/'timestamp-probe.dll')] flag='-Wl,--no-insert-timestamp' probe=subprocess.run(command+[flag],input='int probe;\n',text=True,capture_output=True) if probe.returncode==0: return [flag] # A missing/broken toolchain is not an unsupported-flag result. subprocess.run(command,input='int probe;\n',text=True,capture_output=True,check=True) print('warning: linker does not support --no-insert-timestamp; builds may vary', file=sys.stderr) return []
def build(): sources=verified_sources(SDK,PIN) patch_bytes=PATCH.read_bytes() BUILD.mkdir(exist_ok=True) with tempfile.TemporaryDirectory(prefix='w3dev-native-') as temporary: source=Path(temporary) patched_hash=prepare_source(source,sources,patch_bytes) flags=timestamp_flags(COMPILER,source) common=[str(COMPILER),'-shared','-O2','-std=c++17','-static', '-I'+str(source/'include'),*flags] subprocess.run(common+['-fms-extensions','-DWITCHER_NATIVE_BRIDGE_EXPORTS',*[str(p) for p in sorted((source/'src').glob('*.cpp'))], '-lversion','-o',str(BUILD/'WitcherNativeBridge.asi'),'-Wl,--out-implib,'+str(BUILD/'libWitcherNativeBridge.a')],check=True) subprocess.run(common+['-Wall','-Wextra','-Werror',str(ROOT/'native/PathInput.cpp'),str(BUILD/'libWitcherNativeBridge.a'),'-lws2_32','-o',str(BUILD/'PathInput.asi')],check=True) result={'ok':True,'upstream':PIN,'compiler':str(COMPILER), 'patch':{'path':PATCH.relative_to(ROOT).as_posix(), 'sha256':hashlib.sha256(patch_bytes).hexdigest(), 'source_sha256':BRIDGE_SOURCE_SHA256,'patched_source_sha256':patched_hash}, 'linker_flags':flags,'files':{ name:hashlib.sha256((BUILD/name).read_bytes()).hexdigest() for name in ('WitcherNativeBridge.asi','PathInput.asi')}} (BUILD/'manifest.json').write_text(json.dumps(result,indent=2)+'\n') print(json.dumps(result))
if __name__=='__main__':build()