diff --git a/.gitignore b/.gitignore index a1b9553..4c5d6c6 100644 --- a/.gitignore +++ b/.gitignore @@ -329,3 +329,6 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ /Paths.cmd +_AppxTemp/ +AppxBuild/ +*.msix diff --git a/AppxManifest.xml b/AppxManifest.xml new file mode 100644 index 0000000..724416b --- /dev/null +++ b/AppxManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + UnityLauncherPro + unitycoder.com + Installer/icon.png + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BuildMsix.cmd b/BuildMsix.cmd new file mode 100644 index 0000000..38879ca --- /dev/null +++ b/BuildMsix.cmd @@ -0,0 +1,69 @@ +@echo off +setlocal +pushd "%~dp0" + +:: ----------------------------- +:: Generate date-based version +:: ----------------------------- +for /f %%a in ('powershell -NoProfile -Command "Get-Date -Format yyyyMMdd"') do set DATE=%%a + +:: ----------------------------- +:: Configuration +:: ----------------------------- +set APPNAME=UnityLauncherPro +set OUTPUT=%APPNAME%_%DATE%.msix +set BUILD_DIR=_AppxTemp +set MANIFEST=AppxManifest.xml + +:: ----------------------------- +:: Clean previous build +:: ----------------------------- +if exist "%OUTPUT%" del /f /q "%OUTPUT%" +if exist "%BUILD_DIR%" rmdir /s /q "%BUILD_DIR%" +mkdir "%BUILD_DIR%" + +:: ----------------------------- +:: Copy required files +:: ----------------------------- +echo Copying files to %BUILD_DIR%... + +xcopy /y "UnityLauncherPro\\bin\\Release\\UnityLauncherPro.exe" "%BUILD_DIR%\\" +xcopy /y "UnityLauncherPro\\Images\\icon.ico" "%BUILD_DIR%\\" +copy /y "AppxManifest.xml" "%BUILD_DIR%\AppxManifest.xml" +xcopy /y /s /e "Installer" "%BUILD_DIR%\Installer\" >nul + +:: if exist "UnityLauncherPro\\Scripts" xcopy /y /s /e "UnityLauncherPro\\Scripts" "%BUILD_DIR%\\Scripts\\" +:: if exist "UnityLauncherPro\\Images" xcopy /y /s /e "UnityLauncherPro\\Images" "%BUILD_DIR%\\Images\\" + +:: ----------------------------- +:: Validate AppxManifest.xml +:: ----------------------------- +if not exist "%BUILD_DIR%\\AppxManifest.xml" ( + echo ❌ ERROR: AppxManifest.xml not found in %BUILD_DIR%. + pause + goto :end +) + +:: ----------------------------- +:: Build MSIX +:: ----------------------------- +echo Building MSIX package... +makeappx pack /d "%BUILD_DIR%" /p "%OUTPUT%" +if %errorlevel% neq 0 ( + echo ❌ MSIX build failed. + pause + goto :end +) + +:: ----------------------------- +:: Done +:: ----------------------------- +echo MSIX created: %OUTPUT% +echo Run this to install: +echo powershell -Command "Add-AppxPackage '%OUTPUT%'" +echo. +pause + +:end +popd +endlocal diff --git a/Installer/SplashScreen620x300.png b/Installer/SplashScreen620x300.png new file mode 100644 index 0000000..ccc7f52 Binary files /dev/null and b/Installer/SplashScreen620x300.png differ diff --git a/Installer/Square150x150Logo.png b/Installer/Square150x150Logo.png new file mode 100644 index 0000000..80d70e1 Binary files /dev/null and b/Installer/Square150x150Logo.png differ diff --git a/Installer/Square44x44Logo.png b/Installer/Square44x44Logo.png new file mode 100644 index 0000000..8c06d6d Binary files /dev/null and b/Installer/Square44x44Logo.png differ diff --git a/Installer/Wide310x150Logo.png b/Installer/Wide310x150Logo.png new file mode 100644 index 0000000..9f902b8 Binary files /dev/null and b/Installer/Wide310x150Logo.png differ diff --git a/Installer/icon.png b/Installer/icon.png new file mode 100644 index 0000000..ab332e9 Binary files /dev/null and b/Installer/icon.png differ diff --git a/UnityLauncherPro/MainWindow.xaml.cs b/UnityLauncherPro/MainWindow.xaml.cs index 1ad883f..6422b35 100644 --- a/UnityLauncherPro/MainWindow.xaml.cs +++ b/UnityLauncherPro/MainWindow.xaml.cs @@ -43,7 +43,28 @@ public partial class MainWindow : Window public static readonly string projectNameFile = "ProjectName.txt"; public static string preferredVersion = null; public static int projectNameSetting = 0; // 0 = folder or ProjectName.txt if exists, 1=ProductName - public static readonly string initScriptFileFullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts", "InitializeProject.cs"); + + // required setup for exe and msix builds + public static string InitScriptFileFullPath + { + get + { + string basePath; + if (AppDomain.CurrentDomain.BaseDirectory.Contains(@"\WindowsApps\")) + { + // MSIX: Use user-writable folder + basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnityLauncherPro", "Scripts"); + } + else + { + // EXE: Use app folder + basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Scripts"); + } + // Ensure directory exists + if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath); + return Path.Combine(basePath, "InitializeProject.cs"); + } + } const string contextRegRoot = "Software\\Classes\\Directory\\Background\\shell"; const string githubURL = "https://github.com/unitycoder/UnityLauncherPro"; @@ -2131,7 +2152,7 @@ void CreateNewEmptyProject(string targetFolder = null) Console.WriteLine("Create project " + NewProject.newVersion + " : " + rootFolder); if (string.IsNullOrEmpty(rootFolder)) return; - var p = Tools.FastCreateProject(NewProject.newVersion, rootFolder, NewProject.newProjectName, NewProject.templateZipPath, NewProject.platformsForThisUnity, NewProject.selectedPlatform, (bool)chkUseInitScript.IsChecked, initScriptFileFullPath, NewProject.forceDX11); + var p = Tools.FastCreateProject(NewProject.newVersion, rootFolder, NewProject.newProjectName, NewProject.templateZipPath, NewProject.platformsForThisUnity, NewProject.selectedPlatform, (bool)chkUseInitScript.IsChecked, InitScriptFileFullPath, NewProject.forceDX11); // add to list (just in case new project fails to start, then folder is already generated..) if (p != null) AddNewProjectToList(p); @@ -2154,7 +2175,7 @@ void CreateNewEmptyProject(string targetFolder = null) { newVersion = GetSelectedUnity().Version == null ? preferredVersion : GetSelectedUnity().Version; } - var p = Tools.FastCreateProject(newVersion, rootFolder, null, null, null, null, (bool)chkUseInitScript.IsChecked, initScriptFileFullPath); + var p = Tools.FastCreateProject(newVersion, rootFolder, null, null, null, null, (bool)chkUseInitScript.IsChecked, InitScriptFileFullPath); if (p != null) AddNewProjectToList(p); } @@ -3472,16 +3493,31 @@ public int Compare(Object a, Object b) private void btnExploreScriptsFolder_Click(object sender, RoutedEventArgs e) { - if (Tools.LaunchExplorer(Path.GetDirectoryName(initScriptFileFullPath)) == false) + // handle exe vs msix locations + try { - // if failed, open parent folder (probably path was using URL or no scripts yet) - var parentPath = Directory.GetParent(Path.GetDirectoryName(initScriptFileFullPath)).FullName; - if (Tools.LaunchExplorer(parentPath) == false) + string scriptFolder = Path.GetDirectoryName(InitScriptFileFullPath); + + if (!Tools.LaunchExplorer(scriptFolder)) { - // if still failed, open exe folder - Tools.LaunchExplorer(AppDomain.CurrentDomain.BaseDirectory); + // fallback: try parent of script folder + string parentPath = Directory.GetParent(scriptFolder)?.FullName; + + if (string.IsNullOrEmpty(parentPath) || !Tools.LaunchExplorer(parentPath)) + { + // final fallback: show %LOCALAPPDATA% for MSIX, or exe dir for EXE builds + string fallback = AppDomain.CurrentDomain.BaseDirectory.Contains(@"\WindowsApps\") + ? Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + : AppDomain.CurrentDomain.BaseDirectory; + + Tools.LaunchExplorer(fallback); + } } } + catch (Exception ex) + { + Console.WriteLine("Failed to open folder: " + ex.Message); + } } private void txtCustomInitFileURL_PreviewKeyDown(object sender, KeyEventArgs e) @@ -3823,7 +3859,7 @@ private void tabControl_PreviewKeyDown(object sender, KeyEventArgs e) private void btnFetchLatestInitScript_Click(object sender, RoutedEventArgs e) { - Tools.DownloadInitScript(initScriptFileFullPath, txtCustomInitFileURL.Text); + Tools.DownloadInitScript(InitScriptFileFullPath, txtCustomInitFileURL.Text); } private void btnHubLogs_Click(object sender, RoutedEventArgs e) @@ -3910,26 +3946,23 @@ private void OnPipeConnection(IAsyncResult result) private void CheckCustomIcon() { - string customIconPath = Path.Combine(Environment.CurrentDirectory, "icon.ico"); - //Console.WriteLine(customIconPath); + // Use app-specific writable folder (same as used for themes/scripts) + string baseFolder = AppDomain.CurrentDomain.BaseDirectory.Contains(@"\WindowsApps\") + ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnityLauncherPro") + : AppDomain.CurrentDomain.BaseDirectory; + + string customIconPath = Path.Combine(baseFolder, "icon.ico"); if (File.Exists(customIconPath)) { try { - // Load the custom icon using System.Drawing.Icon - using (var icon = new System.Drawing.Icon(customIconPath)) + using (var icon = new Icon(customIconPath)) { - // Convert the icon to a BitmapSource and assign it to the WPF window's Icon property - this.Icon = Imaging.CreateBitmapSourceFromHIcon( - icon.Handle, - Int32Rect.Empty, - BitmapSizeOptions.FromEmptyOptions() // Use BitmapSizeOptions here - ); - - // window icon + // Set WPF window icon + this.Icon = Imaging.CreateBitmapSourceFromHIcon(icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); IconImage.Source = this.Icon; - // tray icon + // Tray icon notifyIcon.Icon = new Icon(customIconPath); } } @@ -3939,13 +3972,14 @@ private void CheckCustomIcon() Debug.WriteLine($"Failed to load custom icon: {ex.Message}"); } } - else // no custom icon found + else { + // Fallback to default embedded icon notifyIcon.Icon = new Icon(Application.GetResourceStream(new Uri("pack://application:,,,/Images/icon.ico")).Stream); - //Debug.WriteLine("Custom icon not found. Using default."); } } + private void chkCheckSRP_Checked(object sender, RoutedEventArgs e) { if (this.IsActive == false) return; // dont run code on window init diff --git a/UnityLauncherPro/ThemeEditor.xaml.cs b/UnityLauncherPro/ThemeEditor.xaml.cs index a5da45a..c1fae82 100644 --- a/UnityLauncherPro/ThemeEditor.xaml.cs +++ b/UnityLauncherPro/ThemeEditor.xaml.cs @@ -109,7 +109,7 @@ private void GridThemeColors_SelectionChanged(object sender, SelectionChangedEve private void BtnSaveTheme_Click(object sender, RoutedEventArgs e) { - var themeFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Themes"); + var themeFolder = GetThemeFolder(); if (Directory.Exists(themeFolder) == false) Directory.CreateDirectory(themeFolder); @@ -287,5 +287,24 @@ private void TxtColorField_PreviewKeyDown(object sender, KeyEventArgs e) break; } } + + private string GetThemeFolder() + { + string exeDir = AppDomain.CurrentDomain.BaseDirectory; + + // If running inside MSIX (installed to Program Files\WindowsApps), use local app data + if (exeDir.Contains(@"\WindowsApps\")) + { + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + return Path.Combine(localAppData, "UnityLauncherPro", "Themes"); + } + else + { + // Standalone .exe build, use app folder + return Path.Combine(exeDir, "Themes"); + } + } + + } // class } // namespace \ No newline at end of file diff --git a/UnityLauncherPro/Tools.cs b/UnityLauncherPro/Tools.cs index 0e5d484..9069caa 100644 --- a/UnityLauncherPro/Tools.cs +++ b/UnityLauncherPro/Tools.cs @@ -854,6 +854,22 @@ public static async Task DownloadInitScript(string currentInitScriptFullPath, st } } + public static string GetInitScriptFolder() + { + string exeDir = AppDomain.CurrentDomain.BaseDirectory; + + // Check for MSIX install + if (exeDir.Contains(@"\WindowsApps\")) + { + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + return Path.Combine(localAppData, "UnityLauncherPro", "Scripts"); + } + else + { + return Path.Combine(exeDir, "Scripts"); + } + } + static void DeleteTempFile(string path) { if (File.Exists(path) == true)