#!/usr/bin/env python3
"""Restore DEFER's corresponding source from its open JSON distribution."""
import base64,hashlib,json,sys
from pathlib import Path
from urllib.request import urlopen
from urllib.parse import urljoin
base=sys.argv[1] if len(sys.argv)>1 else 'https://defer-core.neocities.org/source/'
out=Path('DEFER-2.3.3-source')
if out.exists():raise SystemExit('Output folder exists; choose an empty working directory.')
manifest=json.load(urlopen(urljoin(base,'manifest.json')))
out.mkdir()
for number,chunk in enumerate(manifest['chunks'],1):
 data=urlopen(urljoin(base,chunk['path'])).read()
 if hashlib.sha256(data).hexdigest()!=chunk['sha256']:raise SystemExit('Corrupt chunk: '+chunk['path'])
 for record in json.loads(data)['records']:
  relative=Path(record['path'])
  if relative.is_absolute() or '..' in relative.parts:raise SystemExit('Invalid path')
  path=out/relative;path.parent.mkdir(parents=True,exist_ok=True)
  content=record['content'].encode('utf-8') if record['encoding']=='utf-8' else base64.b64decode(record['content'],validate=True)
  with path.open('ab') as f:f.write(content)
 print(f'{number}/{len(manifest["chunks"])}',flush=True)
for item in manifest['files']:
 path=out/item['path'];data=path.read_bytes()
 if len(data)!=item['size'] or hashlib.sha256(data).hexdigest()!=item['sha256']:raise SystemExit('Corrupt file: '+item['path'])
 if item['executable']:path.chmod(path.stat().st_mode|0o111)
print('Verified source restored:',out.resolve())
