refactor: replace configparser with manual file processing for launcher.ini and reorder initialization logic to support early secret configuration and background injection
This commit is contained in:
parent
58508465c7
commit
b1a25e20ee
1 changed files with 80 additions and 65 deletions
137
wrapper.py
137
wrapper.py
|
|
@ -156,45 +156,58 @@ def check_and_update_launcher_ini():
|
||||||
pass
|
pass
|
||||||
return
|
return
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.optionxform = str # Preserve case sensitivity
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config.read(ini_path)
|
with open(ini_path, 'r') as f:
|
||||||
except configparser.Error as e:
|
lines = f.readlines()
|
||||||
|
except Exception as e:
|
||||||
print(f"Warning: Could not read {ini_path}: {e}")
|
print(f"Warning: Could not read {ini_path}: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if section exists, if not we assume malformed or empty, so we proceed to check/fix
|
has_otp = False
|
||||||
if not config.has_section('Main'):
|
is_true = False
|
||||||
config.add_section('Main')
|
for line in lines:
|
||||||
|
if line.strip().lower().startswith('isotpserver='):
|
||||||
|
has_otp = True
|
||||||
|
if line.strip().lower().endswith('true'):
|
||||||
|
is_true = True
|
||||||
|
break
|
||||||
|
|
||||||
current_value = config.get('Main', 'IsOtpServer', fallback='false').lower()
|
if has_otp and is_true:
|
||||||
|
return
|
||||||
|
|
||||||
if current_value != 'true':
|
print(f"\nConfiguration Check: '{ini_path}'")
|
||||||
print(f"\nConfiguration Check: '{ini_path}'")
|
print("XIVLauncher requires 'IsOtpServer' to be enabled to accept OTP codes from this wrapper.")
|
||||||
print("XIVLauncher requires 'IsOtpServer' to be enabled to accept OTP codes from this wrapper.")
|
|
||||||
|
|
||||||
choice = 'n'
|
choice = 'n'
|
||||||
if sys.stdin.isatty():
|
if sys.stdin.isatty():
|
||||||
try:
|
try:
|
||||||
choice = input("Enable 'IsOtpServer' now? [Y/n]: ").strip().lower()
|
choice = input("Enable 'IsOtpServer' now? [Y/n]: ").strip().lower()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
choice = 'y'
|
|
||||||
else:
|
|
||||||
print("Non-interactive mode detected. Auto-enabling 'IsOtpServer'.")
|
|
||||||
choice = 'y'
|
choice = 'y'
|
||||||
|
else:
|
||||||
|
print("Non-interactive mode detected. Auto-enabling 'IsOtpServer'.")
|
||||||
|
choice = 'y'
|
||||||
|
|
||||||
if choice in ('', 'y', 'yes'):
|
if choice in ('', 'y', 'yes'):
|
||||||
config.set('Main', 'IsOtpServer', 'true')
|
try:
|
||||||
try:
|
new_lines = []
|
||||||
with open(ini_path, 'w') as f:
|
replaced = False
|
||||||
config.write(f)
|
for line in lines:
|
||||||
print("Updated launcher.ini successfully.")
|
if line.strip().lower().startswith('isotpserver='):
|
||||||
except OSError as e:
|
new_lines.append('IsOtpServer=true\n')
|
||||||
print(f"Error writing to {ini_path}: {e}")
|
replaced = True
|
||||||
else:
|
else:
|
||||||
print("Warning: OTP injection will likely fail without this setting.")
|
new_lines.append(line)
|
||||||
|
if not replaced:
|
||||||
|
new_lines.append('IsOtpServer=true\n')
|
||||||
|
|
||||||
|
with open(ini_path, 'w') as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
|
print("Updated launcher.ini successfully.")
|
||||||
|
except OSError as e:
|
||||||
|
print(f"Error writing to {ini_path}: {e}")
|
||||||
|
else:
|
||||||
|
print("Warning: OTP injection will likely fail without this setting.")
|
||||||
|
|
||||||
def detect_launch_command():
|
def detect_launch_command():
|
||||||
"""Detects available XIVLauncher installations."""
|
"""Detects available XIVLauncher installations."""
|
||||||
|
|
@ -371,6 +384,35 @@ def main():
|
||||||
# 1. Check and Update launcher.ini
|
# 1. Check and Update launcher.ini
|
||||||
check_and_update_launcher_ini()
|
check_and_update_launcher_ini()
|
||||||
|
|
||||||
|
# Retrieve Secret (Keyring refactor) - Moved up so users can configure secret before launch check fails
|
||||||
|
secret = get_secret(config, config_path, allow_prompt=not (args.prelaunch or args.inject_only))
|
||||||
|
|
||||||
|
if not secret:
|
||||||
|
if args.prelaunch or args.inject_only:
|
||||||
|
error_log = os.path.join(config_dir, 'error.log')
|
||||||
|
try:
|
||||||
|
with open(error_log, 'a') as f:
|
||||||
|
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Error: OTP secret not configured.\n")
|
||||||
|
f.write("Please run 'xivlauncher-wrapper' manually from your terminal or application menu to configure your secret before launching via Steam.\n\n")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
print("Error: No secret available.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if args.inject_only:
|
||||||
|
# Only run the OTP injection loop and exit
|
||||||
|
send_otp(secret)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if args.prelaunch:
|
||||||
|
print("Running in prelaunch mode. Spawning OTP injector in background...")
|
||||||
|
# Spawn ourselves in background with --inject-only
|
||||||
|
subprocess.Popen([sys.executable, os.path.realpath(__file__), '--inject-only'],
|
||||||
|
start_new_session=True,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
cmd = config.get("launcher_cmd")
|
cmd = config.get("launcher_cmd")
|
||||||
config_dirty = False
|
config_dirty = False
|
||||||
cmd_args = None
|
cmd_args = None
|
||||||
|
|
@ -388,9 +430,10 @@ def main():
|
||||||
available = detect_launch_command()
|
available = detect_launch_command()
|
||||||
|
|
||||||
if not available:
|
if not available:
|
||||||
print("Error: No XIVLauncher installation found (Flatpak or Native).")
|
print("\nError: No XIVLauncher installation found (Flatpak or Native).")
|
||||||
print("Please install XIVLauncher or manually set 'launcher_cmd' in config.json.")
|
print("If you are using XLM (Steam Compatibility Tool), you can ignore this error and just launch the game via Steam!")
|
||||||
sys.exit(1)
|
print("Otherwise, please install XIVLauncher or manually set 'launcher_cmd' in config.json.")
|
||||||
|
sys.exit(0) # Exit 0 instead of 1 so it's not treated as a failure if they just wanted to set up the secret
|
||||||
|
|
||||||
elif len(available) >= 1:
|
elif len(available) >= 1:
|
||||||
name, command = available[0]
|
name, command = available[0]
|
||||||
|
|
@ -438,34 +481,6 @@ def main():
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Warning: Could not save updated config: {e}")
|
print(f"Warning: Could not save updated config: {e}")
|
||||||
|
|
||||||
# Retrieve Secret (Keyring refactor)
|
|
||||||
secret = get_secret(config, config_path, allow_prompt=not (args.prelaunch or args.inject_only))
|
|
||||||
|
|
||||||
if not secret:
|
|
||||||
if args.prelaunch or args.inject_only:
|
|
||||||
error_log = os.path.join(config_dir, 'error.log')
|
|
||||||
try:
|
|
||||||
with open(error_log, 'a') as f:
|
|
||||||
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Error: OTP secret not configured.\n")
|
|
||||||
f.write("Please run 'xivlauncher-wrapper' manually from your terminal or application menu to configure your secret before launching via Steam.\n\n")
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
print("Error: No secret available.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if args.inject_only:
|
|
||||||
# Only run the OTP injection loop and exit
|
|
||||||
send_otp(secret)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
if args.prelaunch:
|
|
||||||
print("Running in prelaunch mode. Spawning OTP injector in background...")
|
|
||||||
# Spawn ourselves in background with --inject-only
|
|
||||||
subprocess.Popen([sys.executable, os.path.realpath(__file__), '--inject-only'],
|
|
||||||
start_new_session=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL)
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
print(f"Launch Command: {cmd}")
|
print(f"Launch Command: {cmd}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue