73 lines
3.9 KiB
Python
73 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Prepare a self-contained Mac game app from the user's own files."""
|
|
import argparse
|
|
from datetime import datetime
|
|
import json
|
|
import os
|
|
import plistlib
|
|
from pathlib import Path
|
|
import platform
|
|
import sys
|
|
from build_ios import REPO, build_lock, load_builder, select_keys
|
|
|
|
|
|
def main(argv=None):
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('game_dir',type=Path)
|
|
parser.add_argument('--keys-folder',type=Path,required=True)
|
|
parser.add_argument('--package-metadata',type=Path)
|
|
parser.add_argument('--dry-run',action='store_true')
|
|
args=parser.parse_args(argv)
|
|
game=args.game_dir.expanduser().resolve(strict=True)
|
|
if not game.is_dir() or game==REPO or REPO in game.parents:
|
|
raise ValueError('Keep game folders outside the source repository.')
|
|
packages=[p for p in game.iterdir() if p.is_file() and p.suffix.lower() in {'.nsp','.xci','.nsz','.xcz'}]
|
|
if not packages: raise ValueError('No NSP/XCI/NSZ/XCZ files directly in this game folder.')
|
|
state=game/'nxnative-mac.json'
|
|
previous=json.loads(state.read_text()) if state.exists() else {}
|
|
if not isinstance(previous,dict) or previous.get('schema',1)!=1:raise ValueError('Unsupported nxnative-mac.json; previous record was preserved.')
|
|
old_info={}
|
|
if isinstance(previous.get('app_path'),str):
|
|
info_path=Path(previous['app_path'])/'Contents/Info.plist'
|
|
if info_path.is_file():old_info=plistlib.loads(info_path.read_bytes())
|
|
if not isinstance(old_info,dict):raise ValueError('Invalid previous Mac app metadata; preserved without changes.')
|
|
old_id=previous.get('bundle_id') or old_info.get('CFBundleIdentifier')
|
|
if old_id is not None and (not isinstance(old_id,str) or not old_id.strip()):raise ValueError('Invalid previous Mac bundle identity.')
|
|
old_title=previous.get('title_id') or old_info.get('DMTitleID')
|
|
output=game/('nxnative-mac-'+datetime.now().strftime('%Y%m%d-%H%M%S-%f')+'.app')
|
|
command=[str(game),'--platform','mac-app','--mac-sign','-','--team-id','0000000000',
|
|
'--progress',os.environ.get('NXE_PROGRESS','lines'),'-o',str(output)]
|
|
if old_id:command+=['--bundle-id',old_id]
|
|
if args.dry_run:
|
|
print('Mac command:',command)
|
|
return 0
|
|
if sys.platform!='darwin' or platform.machine()!='arm64':
|
|
raise ValueError('Mac games require an Apple silicon Mac.')
|
|
args.keys=None
|
|
keys=select_keys(args)
|
|
with build_lock():
|
|
if any(p.suffix.lower() in {'.nsz','.xcz'} for p in packages):
|
|
import subprocess
|
|
subprocess.run([sys.executable,'-m','pip','install','nsz'],check=True)
|
|
builder=load_builder(args.package_metadata) if args.package_metadata else load_builder()
|
|
code=builder.cli(command+['-k',str(keys)])
|
|
if code: return code
|
|
romfs=output/'Contents/Resources/romfs'
|
|
if not (output/'Contents/MacOS/dmhost').is_file() or not romfs.is_dir() or romfs.is_symlink():
|
|
raise ValueError('Build finished without a complete Mac game app (embedded game data required).')
|
|
info=plistlib.loads((output/'Contents/Info.plist').read_bytes())
|
|
if old_id and info.get('CFBundleIdentifier')!=old_id:raise ValueError('Mac bundle identity changed; previous app record was preserved.')
|
|
if old_title and info.get('DMTitleID')!=old_title:raise ValueError('Mac Title ID changed; use a separate folder for another game.')
|
|
record={'schema':1,'app_path':str(output),'bundle_id':info.get('CFBundleIdentifier'),
|
|
'title_id':info.get('DMTitleID'),'version':info.get('CFBundleShortVersionString')}
|
|
temporary=state.with_suffix('.json.tmp')
|
|
temporary.write_text(json.dumps(record,indent=2)+'\n')
|
|
temporary.replace(state)
|
|
print('Mac app:',output,flush=True)
|
|
return 0
|
|
|
|
if __name__=='__main__':
|
|
try: sys.exit(main())
|
|
except (ValueError,OSError) as error:
|
|
print('Error:',error,file=sys.stderr);sys.exit(1)
|