diff --git a/.gitignore b/.gitignore
index e88c4d1..1824ab0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -451,5 +451,7 @@ $RECYCLE.BIN/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
-!.vscode/launch.json
!.vscode/extensions.json
+
+## Additions
+Wauncher/Properties/launchSettings.json
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..89dc443
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+ enable
+ 11.1.0
+
+
diff --git a/Launcher.sln b/Launcher.sln
index dc375c3..4ff6f00 100644
--- a/Launcher.sln
+++ b/Launcher.sln
@@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher", "Launcher\Launch
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wauncher", "Wauncher\Wauncher.csproj", "{6F2F5730-8F26-498D-BB71-F0068EB6870B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -17,6 +19,10 @@ Global
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6F2F5730-8F26-498D-BB71-F0068EB6870B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6F2F5730-8F26-498D-BB71-F0068EB6870B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6F2F5730-8F26-498D-BB71-F0068EB6870B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6F2F5730-8F26-498D-BB71-F0068EB6870B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj
index 16b3e92..1654e97 100644
--- a/Launcher/Launcher.csproj
+++ b/Launcher/Launcher.csproj
@@ -19,12 +19,17 @@
true
false
win-x64
+ assets\Launcher.ico
+
+
+
+
diff --git a/Launcher/Utils/Argument.cs b/Launcher/Utils/Argument.cs
index 29670fc..9b5332e 100644
--- a/Launcher/Utils/Argument.cs
+++ b/Launcher/Utils/Argument.cs
@@ -11,7 +11,8 @@ public static class Argument
"--patch-only",
"--gc",
"--disable-rpc",
- "--install-dependencies"
+ "--install-dependencies",
+ "--protocol-command"
};
private static List _additionalArguments = new();
@@ -39,7 +40,19 @@ public static List GenerateGameArguments(bool passLauncherArguments = fa
List gameArguments = new();
foreach (string arg in launcherArguments)
- if ((passLauncherArguments || !_launcherArguments.Contains(arg.ToLowerInvariant()))
+ if (arg.StartsWith("cc://"))
+ {
+ string protocolArgument = arg.Replace("cc://", "");
+ string[] protocolArguments = protocolArgument.Split('/');
+ switch (protocolArguments[0])
+ {
+ case "connect":
+ gameArguments.Add("+" + protocolArguments[0]);
+ gameArguments.Add(protocolArguments[1]);
+ break;
+ }
+ }
+ else if ((passLauncherArguments || !_launcherArguments.Contains(arg.ToLowerInvariant()))
&& !arg.EndsWith(".exe"))
gameArguments.Add(arg.ToLowerInvariant());
diff --git a/Launcher/Utils/Console.cs b/Launcher/Utils/Console.cs
index 5824787..5599cd7 100644
--- a/Launcher/Utils/Console.cs
+++ b/Launcher/Utils/Console.cs
@@ -10,7 +10,11 @@ public static class ConsoleManager
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
+
private const int SW_HIDE = 0;
+ private const uint MB_ICONERROR = 0x00000010;
private static IntPtr ConsoleHandle = GetConsoleWindow();
@@ -18,5 +22,14 @@ public static void HideConsole()
{
ShowWindow(ConsoleHandle, SW_HIDE);
}
+
+ public static void ShowError(string message)
+ {
+ if (OperatingSystem.IsWindows())
+ {
+ MessageBox(IntPtr.Zero, message, "ClassicCounter Error", MB_ICONERROR);
+ }
+ }
}
}
+
diff --git a/Launcher/Utils/Discord.cs b/Launcher/Utils/Discord.cs
index 188ce66..f6535b4 100644
--- a/Launcher/Utils/Discord.cs
+++ b/Launcher/Utils/Discord.cs
@@ -1,6 +1,7 @@
using DiscordRPC;
using DiscordRPC.Logging;
using DiscordRPC.Message;
+using System;
namespace Launcher.Utils
{
@@ -10,6 +11,8 @@ public static class Discord
private static DiscordRpcClient _client = new DiscordRpcClient(_appId);
private static RichPresence _presence = new RichPresence();
public static string? CurrentUserId { get; private set; } // ! DEPRECATED ! for whitelist check
+ public static string? CurrentUserAvatar { get; private set; }
+ public static string? CurrentUserUsername { get; private set; }
public static void Init()
{
@@ -58,9 +61,16 @@ public static void SetSmallArtwork(string? key)
private static void OnReady(object sender, ReadyMessage e)
{
CurrentUserId = e.User.ID.ToString(); // ! DEPRECATED ! for passing current uid to api
+ CurrentUserAvatar = e.User.GetAvatarURL(User.AvatarFormat.PNG);
+ CurrentUserUsername = e.User.Username;
+ OnAvatarUpdate?.Invoke(CurrentUserAvatar);
+ OnUsernameUpdate?.Invoke(CurrentUserUsername);
if (Debug.Enabled())
Terminal.Debug($"Discord RPC: User is ready => @{e.User.Username} ({e.User.ID})");
}
+
+ public static event Action? OnAvatarUpdate;
+ public static event Action? OnUsernameUpdate;
}
}
diff --git a/Launcher/Utils/Game.cs b/Launcher/Utils/Game.cs
index e1488e9..fc004e6 100644
--- a/Launcher/Utils/Game.cs
+++ b/Launcher/Utils/Game.cs
@@ -37,6 +37,7 @@ public static async Task Launch()
_listener.NewGameState += OnNewGameState;
_listener.Start();
+ try {
await File.WriteAllTextAsync(gameStatePath,
@"""ClassicCounter""
{
@@ -61,6 +62,11 @@ await File.WriteAllTextAsync(gameStatePath,
}
}"
);
+ }
+ catch
+ {
+ Terminal.Error($"(!) \"/csgo/cfg/gamestate_integration_cc.cfg\" not found in the current directory!");
+ }
}
else if (File.Exists(gameStatePath)) File.Delete(gameStatePath);
@@ -74,7 +80,15 @@ await File.WriteAllTextAsync(gameStatePath,
_process.StartInfo.FileName = $"{directory}\\{gameExe}";
_process.StartInfo.Arguments = string.Join(" ", arguments);
+ if (!File.Exists(_process.StartInfo.FileName))
+ {
+ Terminal.Error($"(!) {gameExe} not found in the current directory!");
+ ConsoleManager.ShowError($"{gameExe} not found in the current directory!\n\nPlease make sure the launcher and game files are in the same folder.");
+ return false;
+ }
+
return _process.Start();
+
}
public static async Task Monitor()
diff --git a/Launcher/Utils/Patch.cs b/Launcher/Utils/Patch.cs
index c05a410..472ee57 100644
--- a/Launcher/Utils/Patch.cs
+++ b/Launcher/Utils/Patch.cs
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using System.Collections.Concurrent;
using System.Security.Cryptography;
namespace Launcher.Utils
@@ -152,23 +153,31 @@ public static async Task ValidatePatches(bool validateAll = false)
needPak01Update = true;
}
- if (needPak01Update)
+ if (!needPak01Update)
{
patches.Remove(dirPatch);
}
}
- foreach (Patch patch in patches)
+ var concurrentMissing = new ConcurrentBag();
+ var concurrentOutdated = new ConcurrentBag();
+
+ var parallelOptions = new ParallelOptions
+ {
+ MaxDegreeOfParallelism = 4
+ };
+
+ await Parallel.ForEachAsync(patches, parallelOptions, async (patch, cancellationToken) =>
{
string originalFileName = GetOriginalFileName(patch.File);
// skip dir file (we already checked it)
if (originalFileName.Contains("pak01_dir.vpk"))
- continue;
+ return;
// are you a pak01 file?
bool isPak01File = originalFileName.Contains("pak01_");
- string path = $"{Directory.GetCurrentDirectory()}/{originalFileName}";
+ string path = Path.Combine(Directory.GetCurrentDirectory(), originalFileName);
if (isPak01File && !needPak01Update && !Argument.Exists("--validate-all"))
{
@@ -177,14 +186,14 @@ public static async Task ValidatePatches(bool validateAll = false)
if (Debug.Enabled())
Terminal.Debug($"Missing: {originalFileName}");
- missing.Add(patch);
- continue;
+ concurrentMissing.Add(patch);
+ return;
}
if (Debug.Enabled())
Terminal.Debug($"Skipping hash check for: {originalFileName} (pak01_dir.vpk up to date)");
- continue;
+ return;
}
if (!File.Exists(path))
@@ -192,8 +201,8 @@ public static async Task ValidatePatches(bool validateAll = false)
if (Debug.Enabled())
Terminal.Debug($"Missing: {originalFileName}");
- missing.Add(patch);
- continue;
+ concurrentMissing.Add(patch);
+ return;
}
if (Debug.Enabled())
@@ -206,9 +215,12 @@ public static async Task ValidatePatches(bool validateAll = false)
Terminal.Debug($"Outdated: {originalFileName}");
File.Delete(path);
- outdated.Add(patch);
+ concurrentOutdated.Add(patch);
}
- }
+ });
+
+ missing.AddRange(concurrentMissing);
+ outdated.AddRange(concurrentOutdated);
// if pak01_dir.vpk needs update, move it to end of lists
if (needPak01Update && dirPatch != null)
diff --git a/Launcher/assets/Launcher.ico b/Launcher/assets/Launcher.ico
new file mode 100644
index 0000000..d1657df
Binary files /dev/null and b/Launcher/assets/Launcher.ico differ
diff --git a/Wauncher/App.axaml b/Wauncher/App.axaml
new file mode 100644
index 0000000..19649f3
--- /dev/null
+++ b/Wauncher/App.axaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Wauncher/App.axaml.cs b/Wauncher/App.axaml.cs
new file mode 100644
index 0000000..ebaafb8
--- /dev/null
+++ b/Wauncher/App.axaml.cs
@@ -0,0 +1,75 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Data.Core.Plugins;
+using Avalonia.Markup.Xaml;
+using CommunityToolkit.Mvvm.Input;
+using Launcher.Utils;
+using Wauncher.Utils;
+using Wauncher.ViewModels;
+using Wauncher.Views;
+
+namespace Wauncher
+{
+ public partial class App : Application
+ {
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ Discord.Init();
+ ProtocolManager.RegisterURIHandler();
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
+ // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
+ DisableAvaloniaDataAnnotationValidation();
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ private void DisableAvaloniaDataAnnotationValidation()
+ {
+ // Get an array of plugins to remove
+ var dataValidationPluginsToRemove =
+ BindingPlugins.DataValidators.OfType().ToArray();
+
+ // remove each entry found
+ foreach (var plugin in dataValidationPluginsToRemove)
+ {
+ BindingPlugins.DataValidators.Remove(plugin);
+ }
+ }
+
+
+ [RelayCommand]
+ public void TrayIconClicked()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null)
+ {
+ desktop.MainWindow.Show();
+ desktop.MainWindow.Activate();
+ }
+ }
+
+ public void ExitApplication_Click(object? sender, System.EventArgs e)
+ {
+ switch (ApplicationLifetime)
+ {
+ case IClassicDesktopStyleApplicationLifetime desktopLifetime:
+ desktopLifetime.TryShutdown();
+ break;
+ case IControlledApplicationLifetime controlledLifetime:
+ controlledLifetime.Shutdown();
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wauncher/Assets/CS-logo.svg b/Wauncher/Assets/CS-logo.svg
new file mode 100644
index 0000000..42ade88
--- /dev/null
+++ b/Wauncher/Assets/CS-logo.svg
@@ -0,0 +1,3 @@
+
diff --git a/Wauncher/Assets/Styles.axaml b/Wauncher/Assets/Styles.axaml
new file mode 100644
index 0000000..c0d156b
--- /dev/null
+++ b/Wauncher/Assets/Styles.axaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wauncher/Assets/Wauncher.ico b/Wauncher/Assets/Wauncher.ico
new file mode 100644
index 0000000..cc20e3f
Binary files /dev/null and b/Wauncher/Assets/Wauncher.ico differ
diff --git a/Wauncher/Program.cs b/Wauncher/Program.cs
new file mode 100644
index 0000000..b17b8c2
--- /dev/null
+++ b/Wauncher/Program.cs
@@ -0,0 +1,60 @@
+using Avalonia;
+using Wauncher.Utils;
+
+namespace Wauncher
+{
+ internal sealed class Program
+ {
+ public static EventWaitHandle ProgramStarted;
+
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args)
+ {
+ if (OnStartup(args) == false)
+ {
+ Environment.Exit(0);
+ return;
+ }
+
+ BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+ }
+
+ // Reference (COPYPASTA)
+ // https://github.com/2dust/v2rayN/blob/d9843dc77502454b1ec48cec6244e115f1abd082/v2rayN/v2rayN.Desktop/Program.cs#L25-L52
+ private static bool OnStartup(string[]? Args)
+ {
+
+ if (Services.IsWindows())
+ {
+ var exePathKey = Services.GetMd5(Services.GetExePath());
+ var rebootas = (Args ?? []).Any(t => t == "rebootas");
+ ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew);
+ if (!rebootas && !bCreatedNew)
+ {
+ ProgramStarted.Set();
+ return false;
+ }
+ }
+ else
+ {
+ _ = new Mutex(true, "Wauncher", out var bOnlyOneInstance);
+ if (!bOnlyOneInstance)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+ }
+}
diff --git a/Wauncher/Utils/ProtocolManager.cs b/Wauncher/Utils/ProtocolManager.cs
new file mode 100644
index 0000000..b81f5ea
--- /dev/null
+++ b/Wauncher/Utils/ProtocolManager.cs
@@ -0,0 +1,49 @@
+using Microsoft.Win32;
+
+namespace Wauncher.Utils
+{
+ public class ProtocolManager
+ {
+ public static void RegisterURIHandler()
+ {
+ var appCurrentLocation = Path.Combine(new FileInfo(System.Environment.ProcessPath).Directory.FullName, "wauncher.exe");
+ EnsureKeyExists(Registry.CurrentUser, "Software/Classes/cc", "ClassicCounter");
+ SetValue(Registry.CurrentUser, "Software/Classes/cc", "URL Protocol", string.Empty);
+ EnsureKeyExists(Registry.CurrentUser, "Software/Classes/cc/DefaultIcon", $"{appCurrentLocation},1");
+ EnsureKeyExists(Registry.CurrentUser, "Software/Classes/cc/shell/open/command", $"\"{appCurrentLocation}\" --protocol-command \"%1\"");
+ }
+
+ private static void SetValue(RegistryKey rootKey, string keys, string valueName, string value)
+ {
+ var key = EnsureKeyExists(rootKey, keys);
+ key.SetValue(valueName, value);
+ }
+
+ private static RegistryKey EnsureKeyExists(RegistryKey rootKey, string keys, string defaultValue = null)
+ {
+ if (rootKey == null)
+ {
+ throw new Exception("Root key is (null)");
+ }
+
+ var currentKey = rootKey;
+ foreach (var key in keys.Split('/'))
+ {
+ currentKey = currentKey.OpenSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree)
+ ?? currentKey.CreateSubKey(key, RegistryKeyPermissionCheck.ReadWriteSubTree);
+
+ if (currentKey == null)
+ {
+ throw new Exception("Could not get or create key");
+ }
+ }
+
+ if (defaultValue != null)
+ {
+ currentKey.SetValue(string.Empty, defaultValue);
+ }
+
+ return currentKey;
+ }
+ }
+}
diff --git a/Wauncher/Utils/Services.cs b/Wauncher/Utils/Services.cs
new file mode 100644
index 0000000..bce55f6
--- /dev/null
+++ b/Wauncher/Utils/Services.cs
@@ -0,0 +1,39 @@
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Wauncher.Utils
+{
+ public class Services
+ {
+ public static string GetMd5(string str)
+ {
+ if (String.IsNullOrEmpty(str))
+ {
+ return string.Empty;
+ }
+ try
+ {
+ var byteOld = Encoding.UTF8.GetBytes(str);
+ var byteNew = MD5.HashData(byteOld);
+ StringBuilder sb = new(32);
+ foreach (var b in byteNew)
+ {
+ sb.Append(b.ToString("x2"));
+ }
+
+ return sb.ToString();
+ }
+ catch (Exception)
+ {
+ return string.Empty;
+ }
+ }
+ public static string GetExePath()
+ {
+ return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
+ }
+
+ public static bool IsWindows() => OperatingSystem.IsWindows();
+ }
+}
diff --git a/Wauncher/ViewLocator.cs b/Wauncher/ViewLocator.cs
new file mode 100644
index 0000000..79ee347
--- /dev/null
+++ b/Wauncher/ViewLocator.cs
@@ -0,0 +1,32 @@
+using Avalonia.Controls;
+using Avalonia.Controls.Templates;
+using System;
+using Wauncher.ViewModels;
+
+namespace Wauncher
+{
+ public class ViewLocator : IDataTemplate
+ {
+
+ public Control? Build(object? param)
+ {
+ if (param is null)
+ return null;
+
+ var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal);
+ var type = Type.GetType(name);
+
+ if (type != null)
+ {
+ return (Control)Activator.CreateInstance(type)!;
+ }
+
+ return new TextBlock { Text = "Not Found: " + name };
+ }
+
+ public bool Match(object? data)
+ {
+ return data is ViewModelBase;
+ }
+ }
+}
diff --git a/Wauncher/ViewModels/InfoWindowViewModel.cs b/Wauncher/ViewModels/InfoWindowViewModel.cs
new file mode 100644
index 0000000..be05980
--- /dev/null
+++ b/Wauncher/ViewModels/InfoWindowViewModel.cs
@@ -0,0 +1,35 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Wauncher.ViewModels
+{
+ public partial class InfoWindowViewModel : ViewModelBase
+ {
+ [RelayCommand]
+ private void OpenUrl(string? url)
+ {
+ if (string.IsNullOrWhiteSpace(url))
+ {
+ return;
+ }
+
+ try
+ {
+ Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
+ }
+ catch
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ Process.Start("xdg-open", url);
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ Process.Start("open", url);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wauncher/ViewModels/MainWindowViewModel.cs b/Wauncher/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..e64c68b
--- /dev/null
+++ b/Wauncher/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,45 @@
+using Avalonia.Threading;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Launcher.Utils;
+
+namespace Wauncher.ViewModels
+{
+ public partial class MainWindowViewModel : ViewModelBase
+ {
+ public string GameStatus { get; private set; } = "Game Status: ";
+
+ public string ProtocolManager { get; private set; } = "Selected server: ";
+
+ [ObservableProperty]
+ private string _profilePicture = "https://avatars.githubusercontent.com/u/75831703?v=4";
+
+ [ObservableProperty]
+ private string _usernameGreeting = "Hello, username";
+
+ public string WhitelistStatus { get; set; } = "Gray";
+
+ public MainWindowViewModel()
+ {
+ if (Argument.Exists("--protocol-command"))
+ {
+ ProtocolManager = ProtocolManager + "Ready to Launch!";
+ }
+
+ Discord.OnAvatarUpdate += (avatarUrl) =>
+ {
+ if (!string.IsNullOrEmpty(avatarUrl))
+ {
+ Dispatcher.UIThread.Post(() => ProfilePicture = avatarUrl);
+ }
+ };
+
+ Discord.OnUsernameUpdate += (username) =>
+ {
+ if (!string.IsNullOrEmpty(username))
+ {
+ Dispatcher.UIThread.Post(() => UsernameGreeting = $"Hello, {username}");
+ }
+ };
+ }
+ }
+}
diff --git a/Wauncher/ViewModels/ViewModelBase.cs b/Wauncher/ViewModels/ViewModelBase.cs
new file mode 100644
index 0000000..f39a365
--- /dev/null
+++ b/Wauncher/ViewModels/ViewModelBase.cs
@@ -0,0 +1,8 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Wauncher.ViewModels
+{
+ public class ViewModelBase : ObservableObject
+ {
+ }
+}
diff --git a/Wauncher/Views/InfoWindow.axaml b/Wauncher/Views/InfoWindow.axaml
new file mode 100644
index 0000000..d43fb24
--- /dev/null
+++ b/Wauncher/Views/InfoWindow.axaml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Thank You for playing ClassicCounter and using Wauncher.
+ Special thanks to h4rmy, heapy and eddies for maintaining this project.
+
+ Built using:
+
+ and
+
+
+
+ by
+
+
+
+ Suggest via
+
+ or
+
+
+ Something is not working? Read
+
+
+
+
+
+
+
+
+
diff --git a/Wauncher/Views/InfoWindow.axaml.cs b/Wauncher/Views/InfoWindow.axaml.cs
new file mode 100644
index 0000000..1d6a8a7
--- /dev/null
+++ b/Wauncher/Views/InfoWindow.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia.Controls;
+using Wauncher.ViewModels;
+
+namespace Wauncher;
+
+public partial class InfoWindow : Window
+{
+ public InfoWindow()
+ {
+ InitializeComponent();
+ DataContext = new InfoWindowViewModel();
+ }
+}
\ No newline at end of file
diff --git a/Wauncher/Views/MainWindow.axaml b/Wauncher/Views/MainWindow.axaml
new file mode 100644
index 0000000..a9e454f
--- /dev/null
+++ b/Wauncher/Views/MainWindow.axaml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wauncher/Views/MainWindow.axaml.cs b/Wauncher/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..d5a312f
--- /dev/null
+++ b/Wauncher/Views/MainWindow.axaml.cs
@@ -0,0 +1,35 @@
+using Avalonia.Controls;
+using Launcher.Utils;
+
+namespace Wauncher.Views
+{
+ public partial class MainWindow : Window
+ {
+ private InfoWindow? _infoWindow = null;
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+
+ private async void Button_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ await Game.Launch();
+ Discord.SetDetails("In Main Menu");
+ Discord.Update();
+ await Game.Monitor();
+ }
+ private void Button_Info(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
+ {
+ if (_infoWindow == null)
+ {
+ _infoWindow = new InfoWindow();
+ _infoWindow.Closed += (s, e) => _infoWindow = null;
+ _infoWindow.Show(this);
+ }
+ else
+ {
+ _infoWindow.Activate();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Wauncher/Wauncher.csproj b/Wauncher/Wauncher.csproj
new file mode 100644
index 0000000..27333d1
--- /dev/null
+++ b/Wauncher/Wauncher.csproj
@@ -0,0 +1,53 @@
+
+
+
+ WinExe
+ net8.0-windows7.0
+ win-x64
+ enable
+ enable
+ true
+ app.manifest
+ true
+
+ true
+ true
+ false
+ 3.0.0
+ $(Version)
+ $(Version)
+ wauncher
+ ClassicCounter Wauncher
+ ClassicCounter Team
+ koolych
+ Assets\Wauncher.ico
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
+
+
diff --git a/Wauncher/app.manifest b/Wauncher/app.manifest
new file mode 100644
index 0000000..9bc1866
--- /dev/null
+++ b/Wauncher/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/publish.bat b/publish.bat
index 72eef09..e3789b1 100644
--- a/publish.bat
+++ b/publish.bat
@@ -1,3 +1,15 @@
-dotnet publish -c Release
+@echo off
+for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
+echo =============================
+echo %ESC%[42mBuilding...%ESC%[0m
+dotnet publish Launcher -c Release
+dotnet publish Wauncher -c Release
+echo =============================
+echo %ESC%[41mHashing...%ESC%[0m
certutil -hashfile "Launcher\bin\Release\net8.0-windows7.0\win-x64\publish\launcher.exe" MD5
-pause
\ No newline at end of file
+certutil -hashfile "Wauncher\bin\Release\net8.0-windows7.0\win-x64\publish\wauncher.exe" MD5
+echo =============================
+echo %ESC%[1;43mCopying...%ESC%[0m
+set /p "destination=Copying destination (in quotations): "
+xcopy "Wauncher\bin\Release\net8.0-windows7.0\win-x64\publish\" %destination% /e /y
+timeout /t 5
\ No newline at end of file