From d34c247e9dd8608294f35c394224296314207593 Mon Sep 17 00:00:00 2001
From: Gabriel Lima <44784408+gablm@users.noreply.github.com>
Date: Sat, 13 Jan 2024 20:59:14 +0000
Subject: [PATCH] URL Protocol support (#368)
- Registers collapse:// as a URL protocol for Collapse
- collapse://open -g 0 -r 1 == CollapseLauncher.exe open -g 0 -r 1
- The protocol can be used through Win + R, .url files and web browsers
- Only "tray" and "open" can be used, mainly for prevention of things like:
\One Billion Free Primogems here!\
- Fixes most "private" command line arguments not working.
- As in #360, commands are always added to the root command, the previous implementation of adding arguments to the root command stopped working. This PR fixes this by converting them in commands.
Commits:
* URL Protocol creation and parsing
* Fix empty protocol and broken non-public commands
* Limit commands allowed using protocols
* Better way to save the allowed commands
* Logging protocol activation
* Reorder imports and renaming variables (Code Review)
---
.../Classes/Properties/AppActivation.cs | 32 ++++++-
.../Classes/Properties/ArgumentParser.cs | 91 ++++++++++++++-----
2 files changed, 97 insertions(+), 26 deletions(-)
diff --git a/CollapseLauncher/Classes/Properties/AppActivation.cs b/CollapseLauncher/Classes/Properties/AppActivation.cs
index c7e469ade..c89397957 100644
--- a/CollapseLauncher/Classes/Properties/AppActivation.cs
+++ b/CollapseLauncher/Classes/Properties/AppActivation.cs
@@ -1,9 +1,11 @@
-using System;
+using Microsoft.Win32;
+using Microsoft.Windows.AppLifecycle;
+using System;
using System.Linq;
using System.Text.RegularExpressions;
-using Microsoft.Windows.AppLifecycle;
using Windows.ApplicationModel.Activation;
using static CollapseLauncher.InnerLauncherConfig;
+using static Hi3Helper.Logger;
using static Hi3Helper.Shared.Region.LauncherConfig;
namespace CollapseLauncher
@@ -13,6 +15,32 @@ public static class AppActivation
public static void Enable()
{
AppInstance.GetCurrent().Activated += App_Activated;
+
+ string protocolName = "collapse";
+ RegistryKey reg = Registry.ClassesRoot.OpenSubKey(protocolName + "\\shell\\open\\command", true);
+
+ if (reg != null)
+ {
+ if ((string)reg.GetValue("") == string.Format("\"{0}\" %1", AppExecutablePath))
+ {
+ LogWriteLine("The protocol is already activated.");
+ return;
+ }
+ }
+
+ LogWriteLine("Protocol does not exist or paths are different. Activating protocol...");
+
+ Registry.ClassesRoot.DeleteSubKeyTree(protocolName, false);
+
+ RegistryKey protocol = Registry.ClassesRoot.CreateSubKey(protocolName, true);
+
+ protocol.SetValue("", "CollapseLauncher protocol");
+ protocol.SetValue("URL Protocol", "");
+ protocol.SetValue("Version", AppCurrentVersionString);
+
+ RegistryKey command = protocol.CreateSubKey("shell\\open\\command", true);
+
+ command.SetValue("", string.Format("\"{0}\" %1", AppExecutablePath));
}
private static void App_Activated(object sender, AppActivationArguments e)
diff --git a/CollapseLauncher/Classes/Properties/ArgumentParser.cs b/CollapseLauncher/Classes/Properties/ArgumentParser.cs
index ad91c325f..3ea02536b 100644
--- a/CollapseLauncher/Classes/Properties/ArgumentParser.cs
+++ b/CollapseLauncher/Classes/Properties/ArgumentParser.cs
@@ -1,13 +1,20 @@
using System;
+using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
+using System.Linq;
+using System.Text.RegularExpressions;
using static CollapseLauncher.InnerLauncherConfig;
+using static Hi3Helper.Logger;
namespace CollapseLauncher
{
public static partial class ArgumentParser
{
- static RootCommand rootCommand = new RootCommand();
+ private static List allowedProtocolCommands = ["tray", "open"];
+
+ private static RootCommand rootCommand = new RootCommand();
+
public static void ParseArguments(params string[] args)
{
if (args.Length == 0)
@@ -16,6 +23,37 @@ public static void ParseArguments(params string[] args)
return;
}
+ if (args[0].StartsWith("collapse://"))
+ {
+ args[0] = args[0].Replace("collapse://", "");
+
+ if (args[0] == "/" || args[0] == "")
+ {
+ m_appMode = AppMode.Launcher;
+ return;
+ }
+
+ // Convert web browser format (contains %20 or %22 but no " or space)
+ if ((args[0].Contains("%20") || args[0].Contains("%22"))
+ && !(args[0].Contains(' ') || args[0].Contains('"')))
+ {
+ string convertedArg = args[0].Replace("%20", " ").Replace("%22", "\"");
+
+ args = Regex.Matches(convertedArg, @"[\""].+?[\""]|[^ ]+")
+ .Cast()
+ .Select(x => x.Value.Trim('"')).ToArray();
+ }
+
+ args = args.Select(x => x.Trim('/')).Where(x => x != "").ToArray();
+
+ if (allowedProtocolCommands.IndexOf(args[0]) == -1)
+ {
+ LogWriteLine("This command does not exist or cannot be activated using a protocol.", Hi3Helper.LogType.Error);
+ m_appMode = AppMode.Launcher;
+ return;
+ }
+ }
+
switch (args[0].ToLower())
{
case "hi3cacheupdate":
@@ -72,36 +110,39 @@ public static void ParseArguments(params string[] args)
public static void ParseHi3CacheUpdaterArguments(params string[] args)
{
- rootCommand.AddArgument(new Argument("hi3cacheupdate", "Update the app or change the Release Channel of the app") { HelpName = null });
- AddHi3CacheUpdaterOptions();
+ Command hi3cacheupdate = new Command("hi3cacheupdate", "Update the app or change the Release Channel of the app");
+ rootCommand.AddCommand(hi3cacheupdate);
+ AddHi3CacheUpdaterOptions(hi3cacheupdate);
}
public static void ParseUpdaterArguments(params string[] args)
{
- rootCommand.AddArgument(new Argument("update", "Update the app or change the Release Channel of the app") { HelpName = null });
- AddUpdaterOptions();
+ Command updater = new Command("update", "Update the app or change the Release Channel of the app");
+ rootCommand.AddCommand(updater);
+ AddUpdaterOptions(updater);
}
public static void ParseElevateUpdaterArguments(params string[] args)
{
- rootCommand.AddArgument(new Argument("elevateupdate", "Elevate updater to run as administrator") { HelpName = null });
- AddUpdaterOptions();
+ Command elevateUpdater = new Command("elevateupdate", "Elevate updater to run as administrator");
+ rootCommand.AddCommand(elevateUpdater);
+ AddUpdaterOptions(elevateUpdater);
}
- public static void AddHi3CacheUpdaterOptions()
+ public static void AddHi3CacheUpdaterOptions(Command command)
{
- rootCommand.SetHandler(() =>
+ command.SetHandler(() =>
{
});
}
- public static void AddUpdaterOptions()
+ public static void AddUpdaterOptions(Command command)
{
Option o_Input = new Option(new string[] { "--input", "-i" }, "App path") { IsRequired = true };
Option o_Channel = new Option(new string[] { "--channel", "-c" }, "App release channel") { IsRequired = true }.FromAmong();
- rootCommand.AddOption(o_Input);
- rootCommand.AddOption(o_Channel);
- rootCommand.Handler = CommandHandler.Create((string Input, AppReleaseChannel ReleaseChannel) =>
+ command.AddOption(o_Input);
+ command.AddOption(o_Channel);
+ command.Handler = CommandHandler.Create((string Input, AppReleaseChannel ReleaseChannel) =>
{
m_arguments.Updater = new ArgumentUpdater
{
@@ -129,32 +170,34 @@ public static void ParseTakeOwnershipArguments(params string[] args)
public static void ParseMigrateArguments(bool isBHI3L = false, params string[] args)
{
+ Command migrate;
if (!isBHI3L)
- rootCommand.AddArgument(new Argument("migrate", "Migrate Game from one installation to another location") { HelpName = null });
+ migrate = new Command("migrate", "Migrate Game from one installation to another location");
else
- rootCommand.AddArgument(new Argument("migratebhi3l", "Migrate Game from BetterHi3Launcher to another location") { HelpName = null });
- AddMigrateOptions(isBHI3L);
+ migrate = new Command("migratebhi3l", "Migrate Game from BetterHi3Launcher to another location");
+ AddMigrateOptions(isBHI3L, migrate);
+ rootCommand.AddCommand(migrate);
}
public static void ParseOOBEArguments(params string[] args)
{
- rootCommand.AddArgument(new Argument("oobesetup", "Starts Collapse in OOBE mode, to simulate first-time setup") { HelpName = null });
+ rootCommand.AddCommand(new Command("oobesetup", "Starts Collapse in OOBE mode, to simulate first-time setup"));
}
- private static void AddMigrateOptions(bool isBHI3L)
+ private static void AddMigrateOptions(bool isBHI3L, Command command)
{
var inputOption = new Option(new string[] { "--input", "-i" }, description: "Installation Source") { IsRequired = true };
var outputOption = new Option(new string[] { "--output", "-o" }, description: "Installation Target") { IsRequired = true };
var rootCommand = new RootCommand();
- rootCommand.AddOption(inputOption);
- rootCommand.AddOption(outputOption);
+ command.AddOption(inputOption);
+ command.AddOption(outputOption);
if (isBHI3L)
{
var gameVerOption = new Option(new string[] { "--gamever", "-g" }, description: "Game version string (Format: x.x.x)") { IsRequired = true };
var regLocOption = new Option(new string[] { "--regloc", "-r" }, description: "Location of game registry for BetterHI3Launcher keys") { IsRequired = true };
- rootCommand.AddOption(gameVerOption);
- rootCommand.AddOption(regLocOption);
- rootCommand.Handler = CommandHandler.Create(
+ command.AddOption(gameVerOption);
+ command.AddOption(regLocOption);
+ command.Handler = CommandHandler.Create(
(string Input, string Output, string GameVer, string RegLoc) =>
{
m_arguments.Migrate = new ArgumentMigrate
@@ -168,7 +211,7 @@ private static void AddMigrateOptions(bool isBHI3L)
});
return;
}
- rootCommand.Handler = CommandHandler.Create(
+ command.Handler = CommandHandler.Create(
(string Input, string Output) =>
{
m_arguments.Migrate = new ArgumentMigrate