Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Add-Type -AssemblyName System.Windows.Forms
- Add-Type -AssemblyName System.Drawing
- # Detect Windows System Dark Mode from Registry
- $RegPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"
- $IsDarkMode = $true
- if (Test-Path $RegPath) {
- if ((Get-ItemProperty -Path $RegPath).AppsUseLightTheme -eq 1) { $IsDarkMode = $false }
- }
- # Define Color Matrix based on Dark/Light mode status
- if ($IsDarkMode) {
- $ColorBg = [System.Drawing.Color]::FromArgb(32, 32, 32)
- $ColorText = [System.Drawing.Color]::White
- $ColorInputBg = [System.Drawing.Color]::FromArgb(45, 45, 45)
- $ColorGrpBg = [System.Drawing.Color]::FromArgb(40, 40, 40)
- } else {
- $ColorBg = [System.Drawing.Color]::FromArgb(243, 243, 243)
- $ColorText = [System.Drawing.Color]::Black
- $ColorInputBg = [System.Drawing.Color]::White
- $ColorGrpBg = [System.Drawing.Color]::FromArgb(255, 255, 255)
- }
- # ----------------------------------------------------
- # VISUAL FORM SETUP
- # ----------------------------------------------------
- $Form = New-Object System.Windows.Forms.Form
- $Form.Text = "Windows 11 Deskthemepack Compiler"
- $Form.Size = New-Object System.Drawing.Size(520, 660)
- $Form.StartPosition = "CenterScreen"
- $Form.FormBorderStyle = "FixedDialog"
- $Form.MaximizeBox = $false
- $Form.BackColor = $ColorBg
- $FontLabel = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
- $FontInput = New-Object System.Drawing.Font("Segoe UI", 9)
- # Theme Name Box
- $LblName = New-Object System.Windows.Forms.Label
- $LblName.Text = "Theme Name:"
- $LblName.Location = New-Object System.Drawing.Point(20, 20)
- $LblName.Size = New-Object System.Drawing.Size(120, 20)
- $LblName.Font = $FontLabel
- $LblName.ForeColor = $ColorText
- $Form.Controls.Add($LblName)
- $TxtName = New-Object System.Windows.Forms.TextBox
- $TxtName.Location = New-Object System.Drawing.Point(150, 18)
- $TxtName.Size = New-Object System.Drawing.Size(330, 20)
- $TxtName.Font = $FontInput
- $TxtName.BackColor = $ColorInputBg
- $TxtName.ForeColor = $ColorText
- $TxtName.Text = "MyCustomTheme"
- $Form.Controls.Add($TxtName)
- # Setup input boxes with global scope
- $Global:TxtWallBox = New-Object System.Windows.Forms.TextBox
- $Global:TxtCursBox = New-Object System.Windows.Forms.TextBox
- $Global:TxtSndBox = New-Object System.Windows.Forms.TextBox
- # Row 1: Wallpapers
- $LblW = New-Object System.Windows.Forms.Label
- $LblW.Text = "Wallpapers Folder:"
- $LblW.Location = New-Object System.Drawing.Point(20, 65)
- $LblW.Size = New-Object System.Drawing.Size(120, 20)
- $LblW.Font = $FontLabel
- $LblW.ForeColor = $ColorText
- $Form.Controls.Add($LblW)
- $Global:TxtWallBox.Location = New-Object System.Drawing.Point(150, 63)
- $Global:TxtWallBox.Size = New-Object System.Drawing.Size(240, 20)
- $Global:TxtWallBox.ReadOnly = $true
- $Global:TxtWallBox.Font = $FontInput
- $Global:TxtWallBox.BackColor = $ColorInputBg
- $Global:TxtWallBox.ForeColor = $ColorText
- $Form.Controls.Add($Global:TxtWallBox)
- $BtnW = New-Object System.Windows.Forms.Button
- $BtnW.Text = "Browse..."
- $BtnW.Location = New-Object System.Drawing.Point(400, 61)
- $BtnW.Size = New-Object System.Drawing.Size(80, 25)
- $BtnW.Font = $FontInput
- $BtnW.BackColor = $ColorInputBg
- $BtnW.ForeColor = $ColorText
- $BtnW.Add_Click({
- $FB = New-Object System.Windows.Forms.FolderBrowserDialog
- if ($FB.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $Global:TxtWallBox.Text = $FB.SelectedPath }
- })
- $Form.Controls.Add($BtnW)
- # Row 2: Cursors
- $LblC = New-Object System.Windows.Forms.Label
- $LblC.Text = "Cursors Folder:"
- $LblC.Location = New-Object System.Drawing.Point(20, 110)
- $LblC.Size = New-Object System.Drawing.Size(120, 20)
- $LblC.Font = $FontLabel
- $LblC.ForeColor = $ColorText
- $Form.Controls.Add($LblC)
- $Global:TxtCursBox.Location = New-Object System.Drawing.Point(150, 108)
- $Global:TxtCursBox.Size = New-Object System.Drawing.Size(240, 20)
- $Global:TxtCursBox.ReadOnly = $true
- $Global:TxtCursBox.Font = $FontInput
- $Global:TxtCursBox.BackColor = $ColorInputBg
- $Global:TxtCursBox.ForeColor = $ColorText
- $Form.Controls.Add($Global:TxtCursBox)
- $BtnC = New-Object System.Windows.Forms.Button
- $BtnC.Text = "Browse..."
- $BtnC.Location = New-Object System.Drawing.Point(400, 106)
- $BtnC.Size = New-Object System.Drawing.Size(80, 25)
- $BtnC.Font = $FontInput
- $BtnC.BackColor = $ColorInputBg
- $BtnC.ForeColor = $ColorText
- $BtnC.Add_Click({
- $FB = New-Object System.Windows.Forms.FolderBrowserDialog
- if ($FB.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $Global:TxtCursBox.Text = $FB.SelectedPath }
- })
- $Form.Controls.Add($BtnC)
- # Row 3: Sounds
- $LblS = New-Object System.Windows.Forms.Label
- $LblS.Text = "Sounds Folder:"
- $LblS.Location = New-Object System.Drawing.Point(20, 155)
- $LblS.Size = New-Object System.Drawing.Size(120, 20)
- $LblS.Font = $FontLabel
- $LblS.ForeColor = $ColorText
- $Form.Controls.Add($LblS)
- $Global:TxtSndBox.Location = New-Object System.Drawing.Point(150, 153)
- $Global:TxtSndBox.Size = New-Object System.Drawing.Size(240, 20)
- $Global:TxtSndBox.ReadOnly = $true
- $Global:TxtSndBox.Font = $FontInput
- $Global:TxtSndBox.BackColor = $ColorInputBg
- $Global:TxtSndBox.ForeColor = $ColorText
- $Form.Controls.Add($Global:TxtSndBox)
- $BtnS = New-Object System.Windows.Forms.Button
- $BtnS.Text = "Browse..."
- $BtnS.Location = New-Object System.Drawing.Point(400, 151)
- $BtnS.Size = New-Object System.Drawing.Size(80, 25)
- $BtnS.Font = $FontInput
- $BtnS.BackColor = $ColorInputBg
- $BtnS.ForeColor = $ColorText
- $BtnS.Add_Click({
- $FB = New-Object System.Windows.Forms.FolderBrowserDialog
- if ($FB.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $Global:TxtSndBox.Text = $FB.SelectedPath }
- })
- $Form.Controls.Add($BtnS)
- # Configurations Box Frame
- $GrpPersonal = New-Object System.Windows.Forms.GroupBox
- $GrpPersonal.Text = "Personalization Configurations"
- $GrpPersonal.Location = New-Object System.Drawing.Point(20, 205)
- $GrpPersonal.Size = New-Object System.Drawing.Size(460, 310)
- $GrpPersonal.Font = $FontLabel
- $GrpPersonal.BackColor = $ColorGrpBg
- $GrpPersonal.ForeColor = $ColorText
- # --- PANEL 1: Isolated System Mode Radio Group ---
- $PnlSys = New-Object System.Windows.Forms.Panel
- $PnlSys.Location = New-Object System.Drawing.Point(10, 25)
- $PnlSys.Size = New-Object System.Drawing.Size(440, 30)
- $GrpPersonal.Controls.Add($PnlSys)
- $LblSysMode = New-Object System.Windows.Forms.Label
- $LblSysMode.Text = "System Mode:"
- $LblSysMode.Location = New-Object System.Drawing.Point(10, 5)
- $LblSysMode.Size = New-Object System.Drawing.Size(100, 20)
- $LblSysMode.ForeColor = $ColorText
- $PnlSys.Controls.Add($LblSysMode)
- $RadSysDark = New-Object System.Windows.Forms.RadioButton
- $RadSysDark.Text = "Dark"
- $RadSysDark.Location = New-Object System.Drawing.Point(120, 3)
- $RadSysDark.Size = New-Object System.Drawing.Size(65, 20)
- $RadSysDark.Checked = $true
- $RadSysDark.Font = $FontInput
- $PnlSys.Controls.Add($RadSysDark)
- $RadSysLight = New-Object System.Windows.Forms.RadioButton
- $RadSysLight.Text = "Light"
- $RadSysLight.Location = New-Object System.Drawing.Point(200, 3)
- $RadSysLight.Size = New-Object System.Drawing.Size(65, 20)
- $RadSysLight.Font = $FontInput
- $PnlSys.Controls.Add($RadSysLight)
- # --- PANEL 2: Isolated App Mode Radio Group ---
- $PnlApp = New-Object System.Windows.Forms.Panel
- $PnlApp.Location = New-Object System.Drawing.Point(10, 60)
- $PnlApp.Size = New-Object System.Drawing.Size(440, 30)
- $GrpPersonal.Controls.Add($PnlApp)
- $LblAppMode = New-Object System.Windows.Forms.Label
- $LblAppMode.Text = "App Mode:"
- $LblAppMode.Location = New-Object System.Drawing.Point(10, 5)
- $LblAppMode.Size = New-Object System.Drawing.Size(100, 20)
- $LblAppMode.ForeColor = $ColorText
- $PnlApp.Controls.Add($LblAppMode)
- $RadAppDark = New-Object System.Windows.Forms.RadioButton
- $RadAppDark.Text = "Dark"
- $RadAppDark.Location = New-Object System.Drawing.Point(120, 3)
- $RadAppDark.Size = New-Object System.Drawing.Size(65, 20)
- $RadAppDark.Checked = $true
- $RadAppDark.Font = $FontInput
- $PnlApp.Controls.Add($RadAppDark)
- $RadAppLight = New-Object System.Windows.Forms.RadioButton
- $RadAppLight.Text = "Light"
- $RadAppLight.Location = New-Object System.Drawing.Point(200, 3)
- $RadAppLight.Size = New-Object System.Drawing.Size(65, 20)
- $RadAppLight.Font = $FontInput
- $PnlApp.Controls.Add($RadAppLight)
- # --- PANEL 3: Isolated Accent Color Radio Group ---
- $PnlColor = New-Object System.Windows.Forms.Panel
- $PnlColor.Location = New-Object System.Drawing.Point(10, 95)
- $PnlColor.Size = New-Object System.Drawing.Size(440, 95)
- $GrpPersonal.Controls.Add($PnlColor)
- $LblColor = New-Object System.Windows.Forms.Label
- $LblColor.Text = "Accent Color:"
- $LblColor.Location = New-Object System.Drawing.Point(10, 10)
- $LblColor.Size = New-Object System.Drawing.Size(100, 20)
- $LblColor.ForeColor = $ColorText
- $PnlColor.Controls.Add($LblColor)
- $RadColorAuto = New-Object System.Windows.Forms.RadioButton
- $RadColorAuto.Text = "Auto Colorization (Dynamic System Pick)"
- $RadColorAuto.Location = New-Object System.Drawing.Point(120, 8)
- $RadColorAuto.Size = New-Object System.Drawing.Size(300, 20)
- $RadColorAuto.Checked = $true
- $RadColorAuto.Font = $FontInput
- $PnlColor.Controls.Add($RadColorAuto)
- $RadColorBest = New-Object System.Windows.Forms.RadioButton
- $RadColorBest.Text = "Choose Best Color (Extract from Wallpaper)"
- $RadColorBest.Location = New-Object System.Drawing.Point(120, 35)
- $RadColorBest.Size = New-Object System.Drawing.Size(300, 20)
- $RadColorBest.Font = $FontInput
- $PnlColor.Controls.Add($RadColorBest)
- # --- PANEL 4: Slideshow Toggle & Intervals ---
- $PnlSlide = New-Object System.Windows.Forms.Panel
- $PnlSlide.Location = New-Object System.Drawing.Point(10, 195)
- $PnlSlide.Size = New-Object System.Drawing.Size(440, 100)
- $GrpPersonal.Controls.Add($PnlSlide)
- $ChkSlideshow = New-Object System.Windows.Forms.CheckBox
- $ChkSlideshow.Text = "Package all as Slideshow"
- $ChkSlideshow.Location = New-Object System.Drawing.Point(10, 5)
- $ChkSlideshow.Size = New-Object System.Drawing.Size(200, 20)
- $ChkSlideshow.ForeColor = $ColorText
- $ChkSlideshow.Font = $FontLabel
- $PnlSlide.Controls.Add($ChkSlideshow)
- $PnlInterval = New-Object System.Windows.Forms.Panel
- $PnlInterval.Location = New-Object System.Drawing.Point(30, 30)
- $PnlInterval.Size = New-Object System.Drawing.Size(400, 65)
- $PnlInterval.Enabled = $false
- $PnlSlide.Controls.Add($PnlInterval)
- $ChkSlideshow.Add_CheckedChanged({
- $PnlInterval.Enabled = $ChkSlideshow.Checked
- })
- $LblInterval = New-Object System.Windows.Forms.Label
- $LblInterval.Text = "Change picture every:"
- $LblInterval.Location = New-Object System.Drawing.Point(0, 5)
- $LblInterval.Size = New-Object System.Drawing.Size(150, 20)
- $LblInterval.ForeColor = $ColorText
- $PnlInterval.Controls.Add($LblInterval)
- $Rad30m = New-Object System.Windows.Forms.RadioButton
- $Rad30m.Text = "30 Min"; $Rad30m.Location = New-Object System.Drawing.Point(0, 25); $Rad30m.Size = New-Object System.Drawing.Size(70, 20); $Rad30m.Checked = $true; $Rad30m.Font = $FontInput
- $PnlInterval.Controls.Add($Rad30m)
- $Rad1h = New-Object System.Windows.Forms.RadioButton
- $Rad1h.Text = "1 Hr"; $Rad1h.Location = New-Object System.Drawing.Point(75, 25); $Rad1h.Size = New-Object System.Drawing.Size(60, 20); $Rad1h.Font = $FontInput
- $PnlInterval.Controls.Add($Rad1h)
- $Rad3h = New-Object System.Windows.Forms.RadioButton
- $Rad3h.Text = "3 Hr"; $Rad3h.Location = New-Object System.Drawing.Point(140, 25); $Rad3h.Size = New-Object System.Drawing.Size(60, 20); $Rad3h.Font = $FontInput
- $PnlInterval.Controls.Add($Rad3h)
- $Rad6h = New-Object System.Windows.Forms.RadioButton
- $Rad6h.Text = "6 Hr"; $Rad6h.Location = New-Object System.Drawing.Point(205, 25); $Rad6h.Size = New-Object System.Drawing.Size(60, 20); $Rad6h.Font = $FontInput
- $PnlInterval.Controls.Add($Rad6h)
- $Rad12h = New-Object System.Windows.Forms.RadioButton
- $Rad12h.Text = "12 Hr"; $Rad12h.Location = New-Object System.Drawing.Point(270, 25); $Rad12h.Size = New-Object System.Drawing.Size(70, 20); $Rad12h.Font = $FontInput
- $PnlInterval.Controls.Add($Rad12h)
- $Form.Controls.Add($GrpPersonal)
- $BtnBuild = New-Object System.Windows.Forms.Button
- $BtnBuild.Text = "Build Theme Pack"
- $BtnBuild.Location = New-Object System.Drawing.Point(20, 540)
- $BtnBuild.Size = New-Object System.Drawing.Size(460, 45)
- $BtnBuild.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
- $BtnBuild.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 215)
- $BtnBuild.ForeColor = [System.Drawing.Color]::White
- # ----------------------------------------------------
- # COMPILER CORE ARCHITECTURE
- # ----------------------------------------------------
- $BtnBuild.Add_Click({
- if ([string]::IsNullOrEmpty($TxtName.Text)) {
- [System.Windows.Forms.MessageBox]::Show("Please specify a valid theme name.", "Input Missing")
- return
- }
- # Clean incoming directory path strings to completely eliminate trailing backslash crash bugs
- $WallPath = $Global:TxtWallBox.Text.TrimEnd('\')
- $CursPath = $Global:TxtCursBox.Text.TrimEnd('\')
- $SndPath = $Global:TxtSndBox.Text.TrimEnd('\')
- if ([string]::IsNullOrEmpty($WallPath) -or !(Test-Path $WallPath)) {
- [System.Windows.Forms.MessageBox]::Show("A valid wallpaper source directory must be specified.", "Input Missing")
- return
- }
- $CleanThemeName = $TxtName.Text -replace '[\\/:*?"<>|]', ''
- $StagingDir = Join-Path $env:TEMP "ThemePackStaging_$(Get-Random)"
- New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null
- $ThemeFile = Join-Path $StagingDir "$CleanThemeName.theme"
- @("[Theme]", "DisplayName=$CleanThemeName", "") | Out-File $ThemeFile -Encoding UTF8
- @("[MasterThemeSelector]", "MTSM=RJSPBS", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- $SysValue = if ($RadSysDark.Checked) { "Dark" } else { "Light" }
- $AppValue = if ($RadAppDark.Checked) { "Dark" } else { "Light" }
- $AutoColorValue = if ($RadColorAuto.Checked) { 1 } else { 0 }
- $HexColorValue = "0XFF4B71B3"
- $CabinetFilesList = @()
- # 1. Wallpaper Path Configurations
- $Wallpapers = Get-ChildItem -Path $WallPath -File | Where-Object { $_.Extension -match '^\.(jpg|jpeg|png)$' }
- if ($Wallpapers.Count -gt 0) {
- $FirstWall = $Wallpapers[0]
- if ($RadColorBest.Checked) {
- try {
- $Bitmap = New-Object System.Drawing.Bitmap($FirstWall.FullName)
- $SamplePixel = $Bitmap.GetPixel([int]($Bitmap.Width / 2), [int]($Bitmap.Height / 2))
- $HexColorValue = "0XFF" + $SamplePixel.R.ToString("X2") + $SamplePixel.G.ToString("X2") + $SamplePixel.B.ToString("X2")
- $Bitmap.Dispose()
- } catch { $AutoColorValue = 1 }
- }
- if ($ChkSlideshow.Checked -and $Wallpapers.Count -gt 1) {
- # Package ALL wallpapers
- foreach ($Wall in $Wallpapers) {
- $CabinetFilesList += New-Object PSObject -Property @{ SourcePath = $Wall.FullName; CabinetSubdir = "DesktopBackground" }
- }
- @("[Control Panel\Desktop]", "Wallpaper=DesktopBackground\$($FirstWall.Name)", "TileWallpaper=0", "WallpaperStyle=10", "MultimonBackgrounds=0", "PicturePosition=4", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- # Map selected interval (milliseconds)
- $IntervalValue = 1800000 # default 30 min
- if ($Rad1h.Checked) { $IntervalValue = 3600000 }
- elseif ($Rad3h.Checked) { $IntervalValue = 10800000 }
- elseif ($Rad6h.Checked) { $IntervalValue = 21600000 }
- elseif ($Rad12h.Checked) { $IntervalValue = 43200000 }
- @("[Slideshow]", "Interval=$IntervalValue", "Shuffle=1", "ImagesRootPath=DesktopBackground\", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- } else {
- # Single Wallpaper
- $CabinetFilesList += New-Object PSObject -Property @{ SourcePath = $FirstWall.FullName; CabinetSubdir = "DesktopBackground" }
- @("[Control Panel\Desktop]", "Wallpaper=DesktopBackground\$($FirstWall.Name)", "TileWallpaper=0", "WallpaperStyle=10", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- }
- }
- # 2. Cursor Configuration Profile Keys
- @("[Control Panel\Cursors]") | Out-File $ThemeFile -Encoding UTF8 -Append
- if (![string]::IsNullOrEmpty($CursPath) -and (Test-Path $CursPath)) {
- $CursorsList = Get-ChildItem -Path $CursPath -File | Where-Object { $_.Extension -match '^\.(cur|ani)$' }
- # Use an ordered dictionary to prioritize longer/specific matches (like uparrow) before generic ones (like arrow)
- $CursorMap = [ordered]@{
- "appstarting" = "AppStarting"; "working" = "AppStarting"; "background" = "AppStarting";
- "uparrow" = "UpArrow"; "up" = "UpArrow";
- "crosshair" = "Crosshair"; "cross" = "Crosshair";
- "ibeam" = "IBeam"; "text" = "IBeam";
- "nwpen" = "NWPen"; "pen" = "NWPen";
- "sizenwse" = "SizeNWSE"; "sznwse" = "SizeNWSE";
- "sizenesw" = "SizeNESW"; "sznesw" = "SizeNESW";
- "sizeall" = "SizeAll"; "move" = "SizeAll";
- "sizens" = "SizeNS"; "vertical" = "SizeNS";
- "sizewe" = "SizeWE"; "horizontal" = "SizeWE";
- "unavailable" = "No"; "no" = "No";
- "wait" = "Wait"; "busy" = "Wait"; "loading" = "Wait";
- "help" = "Help";
- "hand" = "Hand"; "link" = "Hand";
- "pin" = "Pin";
- "arrow" = "Arrow"
- }
- foreach ($CurFile in $CursorsList) {
- $LowerName = $CurFile.BaseName.ToLower()
- foreach ($Key in $CursorMap.Keys) {
- if ($LowerName.Contains($Key)) {
- "$($CursorMap[$Key])=$($CurFile.Name)" | Out-File $ThemeFile -Encoding UTF8 -Append
- $CabinetFilesList += New-Object PSObject -Property @{ SourcePath = $CurFile.FullName; CabinetSubdir = "" }
- break
- }
- }
- }
- }
- @("") | Out-File $ThemeFile -Encoding UTF8 -Append
- # 3. Sound Audio Configurations
- @("[Sounds]", "SchemeName=$CleanThemeName Sounds", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- if (![string]::IsNullOrEmpty($SndPath) -and (Test-Path $SndPath)) {
- $SoundsList = Get-ChildItem -Path $SndPath -File | Where-Object { $_.Extension -eq '.wav' }
- $SoundMap = @{
- "start" = "SystemStart"; "boot" = "SystemStart"; "welcome" = "SystemStart";
- "connect" = "DeviceConnect"; "plug" = "DeviceConnect"; "insert" = "DeviceConnect";
- "disconnect" = "DeviceDisconnect"; "unplug" = "DeviceDisconnect";
- "fail" = "DeviceFail"; "devicefail" = "DeviceFail";
- "recycle" = "Explorer\EmptyRecycleBin"; "trash" = "Explorer\EmptyRecycleBin"; "empty" = "Explorer\EmptyRecycleBin";
- "notification" = "SystemNotification"; "notify" = "SystemNotification";
- "critical" = "SystemHand"; "error" = "SystemHand"; "stop" = "SystemHand";
- "warning" = "SystemExclamation"; "warn" = "SystemExclamation"; "exclamation" = "SystemExclamation";
- "default" = ".Default"; "beep" = ".Default";
- "criticalbattery" = "CriticalBatteryAlarm";
- "lowbattery" = "LowBatteryAlarm";
- "mail" = "MailBeep"; "email" = "MailBeep";
- "restoredown" = "RestoreDown"; "restoreup" = "RestoreUp";
- "asterisk" = "SystemAsterisk";
- "logoff" = "WindowsLogoff";
- "logon" = "WindowsLogon";
- "uac" = "WindowsUAC"; "admin" = "WindowsUAC";
- "theme" = "ChangeTheme"
- }
- foreach ($SndFile in $SoundsList) {
- $LowerName = $SndFile.BaseName.ToLower()
- foreach ($Key in $SoundMap.Keys) {
- if ($LowerName.Contains($Key)) {
- $EventPath = $SoundMap[$Key]
- if ($EventPath -match "^Explorer\\") {
- $Section = "[AppEvents\Schemes\Apps\$EventPath]"
- } else {
- $Section = "[AppEvents\Schemes\Apps\.Default\$EventPath]"
- }
- @("$Section", "DefaultValue=$($SndFile.Name)", "") | Out-File $ThemeFile -Encoding UTF8 -Append
- $CabinetFilesList += New-Object PSObject -Property @{ SourcePath = $SndFile.FullName; CabinetSubdir = "" }
- break
- }
- }
- }
- }
- # 4. Mandatory Coupling Section Closures
- @("[VisualStyles]", "Path=%SystemRoot%\resources\Themes\Aero\Aero.msstyles", "ColorStyle=NormalColor", "Size=NormalSize", "AutoColorization=$AutoColorValue", "ColorizationColor=$HexColorValue", "SystemMode=$SysValue", "AppMode=$AppValue", "VisualStyleVersion=10", "Transparency=1") | Out-File $ThemeFile -Encoding UTF8 -Append
- # 5. Generate DDF compilation blueprint instructions with strict absolute paths
- $DdfFile = Join-Path $StagingDir "pack.ddf"
- $DdfContent = @(
- ".OPTION EXPLICIT",
- ".Set CabinetNameTemplate=$CleanThemeName.deskthemepack",
- ".Set Cabinet=on",
- ".Set Compress=on",
- ".Set CompressionType=MSZIP",
- ".Set DiskDirectoryTemplate=.",
- ".Set CabinetFileCountThreshold=0",
- ".Set FolderFileCountThreshold=0",
- ".Set FolderSizeThreshold=0",
- ".Set MaxCabinetSize=0",
- ".Set MaxDiskFileCount=0",
- ".Set MaxDiskSize=0",
- "",
- ".Set DestinationDir=",
- "`"$ThemeFile`""
- )
- $RootFiles = $CabinetFilesList | Where-Object { $_.CabinetSubdir -eq "" }
- if ($RootFiles) {
- $DdfContent += ""
- $DdfContent += ".Set DestinationDir="
- foreach ($F in $RootFiles) { $DdfContent += "`"$($F.SourcePath)`"" }
- }
- $BgFiles = $CabinetFilesList | Where-Object { $_.CabinetSubdir -eq "DesktopBackground" }
- if ($BgFiles) {
- $DdfContent += ""
- $DdfContent += ".Set DestinationDir=DesktopBackground"
- foreach ($F in $BgFiles) { $DdfContent += "`"$($F.SourcePath)`"" }
- }
- $DdfContent | Out-File $DdfFile -Encoding ASCII
- # Open save file panel layout dialog
- $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
- $SaveFileDialog.Filter = "Windows Theme Pack (*.deskthemepack)|*.deskthemepack"
- $SaveFileDialog.FileName = "$CleanThemeName.deskthemepack"
- if ($SaveFileDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
- $FinalOutName = Split-Path $SaveFileDialog.FileName -Leaf
- # Explicitly patch directive text blocks without breaking nested string arrays
- (Get-Content $DdfFile) | ForEach-Object { $_ -replace "CabinetNameTemplate=.*", "CabinetNameTemplate=$FinalOutName" } | Out-File $DdfFile -Encoding ASCII
- Start-Process -FilePath "makecab.exe" -ArgumentList "/F `"$DdfFile`"" -WorkingDirectory $StagingDir -NoNewWindow -Wait
- if (Test-Path (Join-Path $StagingDir $FinalOutName)) {
- Copy-Item -Path (Join-Path $StagingDir $FinalOutName) -Destination $SaveFileDialog.FileName -Force
- [System.Windows.Forms.MessageBox]::Show("Theme Pack successfully compiled!", "Success")
- } else {
- [System.Windows.Forms.MessageBox]::Show("Compilation failed natively. Check asset properties.", "Error")
- }
- }
- Remove-Item -Path $StagingDir -Recurse -Force -ErrorAction SilentlyContinue
- })
- $Form.Controls.Add($BtnBuild)
- $Form.ShowDialog() | Out-Null
Advertisement
Comments
-
Comment was deleted
-
Comment was deleted
-
- Powershell Script using WinForms to build Windows 11 deskthemepacks using a graphical GUI. Allows for packaging wallpapers, cursors, and sounds. Simply provide the folders with the required assets, select the color modes, and optionally set the slideshow settings.
- A screenshot can be found here: https://previews.alekeagle.me/-mEhr1ZCIb.png
- --------------------------------------------------------------------------------------------------------------------------------------------
- Cursors and sounds need to be named properly for this script to map them to the correct paths.
- Here is a list of valid cursor file names:
- - appstarting.ani
- - wait.ani
- - arrow.cur
- - crosshair.cur
- - hand.cur
- - help.cur
- - ibeam.cur
- - no.cur
- - nwpen.cur
- - person.cur
- - pin.cur
- - sizeall.cur
- - sizenesw.cur
- - sizens.cur
- - sizenwse.cur
- - sizewe.cur
- - upparow.cur
- Here is a list of valid sound file names:
- - change-theme.wav
- - critical-battery-alarm.wav
- - critical-stop.wav
- - default-beep.wav
- - desktop-mail-notification.wav
- - device-connect.wav
- - device-failed-to-connect.wav
- - exclamation.wav
- - low-battery-alarm.wav
- - restore-down.wav
- - restore-up.wav
- - uac.wav
- - windows-logoff.wav
- - windows-logon.wav
- The names listed above are not the only names accepted. But they do give a general example of the naming schemes required to ensure the cursors and sounds are mapped correctly. The script uses the names of the files to determine the correct mapping. So the names need to be correct for each cursor and sound file.
Add Comment
Please, Sign In to add comment