from pathlib import Path
import subprocess,sys,base64,shlex,tarfile,io,json,os
ROOT=Path('/home/jakub/devel/escapistsrereintroduced');sys.path.insert(0,str(ROOT/'tools'))
from source_manifest import addon_manifest
name=sys.argv[1];mode=sys.argv[2] if len(sys.argv)>2 else 'both';target='/tmp/erri-compat-'+name
buf=io.BytesIO()
with tarfile.open(fileobj=buf,mode='w:gz') as tar:
 for rel in addon_manifest(ROOT):
  if mode in ('reaper','neither') and not rel.endswith('/ERR_ReaperRHSCompat.c'):continue
  tar.add(ROOT/rel,arcname='source/'+str(Path(rel).relative_to('addons/core')))
 if mode in ('reaper','neither'):
  project=b'GameProject { ID "ERR_CompatFixture" GUID "2528466DE220D91C" TITLE "Private compatibility fixture" Dependencies { "58D0FB3206B6F859" } }'
  info=tarfile.TarInfo('source/addon.gproj');info.size=len(project);tar.addfile(info,io.BytesIO(project))
 probe=Path('/tmp/erri-compat-container-probe.c').read_bytes()
 if mode in ('both','rhs'):probe=probe.replace(b'// MISSION_READY',b'if (!ERR_GameMode.Get() || !ERR_GameMode.Get().Active()) return;')
 info=tarfile.TarInfo('source/Scripts/Game/ERR/00_Core/ERR_ZCompatProbe.c');info.size=len(probe);tar.addfile(info,io.BytesIO(probe))
code='''from pathlib import Path
import io,tarfile,base64,subprocess,os,time,signal,json
run=Path(TARGET);run.mkdir(mode=0o755)
with tarfile.open(fileobj=io.BytesIO(base64.b64decode(PAYLOAD)),mode='r:gz') as t:t.extractall(run)
runtime=Path('/home/amp/.ampdata/instances/RKSserver201/arma-reforger/1874900')
installed=runtime/'AReforgerMaster/addons';source=run/'source'
(source/'resourceDatabase.rdb').write_bytes((installed/'EscapistsReintroduced_2528466DE220D91C/resourceDatabase.rdb').read_bytes())
addons=run/'addons';addons.mkdir();(addons/'ERR_Core').symlink_to(source,target_is_directory=True)
allowed=['595F2BF2F44836FB','1337C0DE5DABBEEF','BADC0DEDABBEDA5E','629B2BA37EFFD577','5E389BB9F58B79A6','5E524E4FEECCA92B','65930CB4CD0237B2','5EB139459EBF5C16','61B8FA7B3BF8656B']
for addon in installed.iterdir():
 if any(addon.name.endswith('_'+key) for key in allowed):(addons/addon.name).symlink_to(addon,target_is_directory=True)
(run/'profile').mkdir()
engine=[str(runtime/'ArmaReforgerServer'),'-server',('{C1E20667DCE324ED}Missions/ERR_Everon.conf' if MODE in ('both','rhs') else 'Missions/21_GM_Eden.conf'),'-addonsDir',str(addons),'-addons',('2528466DE220D91C,5EB139459EBF5C16' if MODE in ('both','reaper') else '2528466DE220D91C'),'-profile',str(run/'profile'),'-noBackend','-noSound','-bindIP','127.0.0.1','-bindPort','2001','-maxFPS','20','-logconsole','-noSplash','-noThrow','-disableCrashReporter']
inner="import subprocess,os;subprocess.run(['mount','--make-rprivate','/'],check=True);subprocess.run(['mount','--bind',RUNTIME,RUNTIME],check=True);subprocess.run(['mount','-o','remount,bind,ro',RUNTIME],check=True);os.chdir(RUNTIME);os.nice(15);os.execv(ENGINE[0],ENGINE)".replace('RUNTIME',repr(str(runtime))).replace('ENGINE',repr(engine))
cmd=['unshare','--mount','--net','--pid','--fork','--mount-proc','python3','-c',inner]
with (run/'stdout.log').open('w') as log:
 p=subprocess.Popen(cmd,stdout=log,stderr=subprocess.STDOUT,start_new_session=True);start=time.monotonic();reason='timeout';text=''
 try:
  while time.monotonic()-start<150:
   files=sorted((run/'profile/logs').glob('logs_*/console.log'));text=files[-1].read_text(errors='replace') if files else (run/'stdout.log').read_text(errors='replace')
   if 'ERR COMPAT COMPLETE' in text:reason='complete';break
   if "Can't compile" in text:reason='compile-failed';break
   if p.poll() is not None:reason='exit';break
   time.sleep(.5)
 finally:
  if p.poll() is None:
   os.killpg(p.pid,signal.SIGTERM)
   try:p.wait(timeout=8)
   except subprocess.TimeoutExpired:os.killpg(p.pid,signal.SIGKILL);p.wait()
result={'reason':reason,'exit':p.returncode,'log':text,'stdout':(run/'stdout.log').read_text(errors='replace')};(run/'result.json').write_text(json.dumps(result));print(json.dumps(result))
'''.replace('MODE',repr(mode)).replace('TARGET',repr(target)).replace('PAYLOAD',repr(base64.b64encode(buf.getvalue()).decode()))
# Feed the Python program through stdin after sudo's password; no large command-line payload.
cmd='sudo -S -p "" python3 -'
p=subprocess.run(['ssh','-o','ControlPath=/tmp/erri-crash-check/socket','jakub@192.168.52.4',cmd],input=os.environ['ERR_TEST_SSH_PASSWORD']+'\n'+code,text=True,capture_output=True)
if p.returncode:print(p.stderr);raise SystemExit(p.returncode)
data=json.loads(p.stdout);local=Path('/tmp/erri-compat-results')/name;local.mkdir(parents=True,exist_ok=True)
(local/'result.json').write_text(json.dumps(data,indent=2));(local/'console.log').write_text(data['log'])
print(data['reason']);print('\n'.join(l for l in data['log'].splitlines() if any(t in l for t in ['ERR COMPAT','SCRIPT    (E)','cannot be combined'])))
