├── LEShortcutCreator.cmd
├── LICENSE
├── NOTICE
├── README.md
├── screenshot-win10.png
├── screenshot-win11.png
├── screenshot-win7.png
└── screenshot-win81.png
/LEShortcutCreator.cmd:
--------------------------------------------------------------------------------
1 | <# ::: Batch code starts here :::::::::::::::::::::::::::::::::::::::::::::::::
2 | :
3 | : Batch (*.BAT/*.CMD) wrapper for Powershell scripts
4 | :
5 | : Inspiration:
6 | : - https://stackoverflow.com/questions/29645/#8597794
7 | : - https://stackoverflow.com/questions/9074476/#9074483
8 | :
9 | : Batch code explained:
10 | : setlocal New variables only exists for this script, and do not affect Windows.
11 | :
12 | : PowerShell arguments explained:
13 | : -Sta Enables support for file drag'n'drop in Windows forms.
14 | : -NoProfile Makes sure no PowerShell settings disrupts script execution.
15 | : -ExecutionPolicy Bypass Makes sure no PowerShell settings disrupts script execution.
16 | : -WindowStyle hidden Hides the console window.
17 |
18 | @echo off
19 | setlocal
20 |
21 | :: Test if PowerShell can be found
22 | :: Verify that the PowerShell command works before continuing.
23 | SET PowerShell_default_location=%systemroot%\System32\WindowsPowerShell\v1.0\powershell.exe
24 | where /Q PowerShell
25 | if %ERRORLEVEL% == 0 (
26 | FOR /f "delims=" %%F IN ('where PowerShell') DO (SET PowerShell=%%F)
27 | ) else (
28 | if exist "%PowerShell_default_location%" (
29 | SET PowerShell=%PowerShell_default_location%
30 | ) else (
31 | REM mshta javascript:alert^("ERROR: PowerShell not found."^);close^(^);
32 | mshta vbscript:Execute^("msgbox ""PowerShell not found."",0,""ERROR"":close"^)
33 | EXIT /B
34 | )
35 | )
36 |
37 | :: Run :: Execute this file directly as a PowerShell script
38 | :: No temporary files needed, but errors messages is harder to understand.
39 | set POWERSHELL_BAT_ARGS=%0 %*
40 | if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
41 | if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:$=`$%
42 | "%PowerShell%" -Sta -NoProfile -ExecutionPolicy Bypass -WindowStyle hidden -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$(Get-Content '%~f0')))
43 | EXIT /B
44 |
45 | :: Run DEBUG :: Execute a temporary copy of this file with a *.ps1 extension
46 | :: By running a real *.ps1 file errors messages gets easier to understand.
47 | set TMPFILE=%~d0%~p0%~n0.debug.ps1
48 | COPY "%~f0" "%TMPFILE%" >NUL
49 | "%PowerShell%" -Sta -NoProfile -ExecutionPolicy Bypass -File "%TMPFILE%" %*
50 | DEL "%TMPFILE%" >NUL
51 | PAUSE
52 | EXIT /B
53 | #>
54 |
55 |
56 |
57 | ## ::: PowerShell Code Starts here ::::::::::::::::::::::::::::::::::::::::::::
58 |
59 | ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ###
60 | ### ###
61 | ### ============== Locale Emulator Shortcut Creator =============== ###
62 | ### ###
63 | ### Usage Instructions ###
64 | ### ------------------ ###
65 | ### Either launch the script normally to get a Graphical User Interface. ###
66 | ### The interface will let you create shortcuts that uses Locale ###
67 | ### Emulator. ###
68 | ### ###
69 | ### OR drag'n'drop files onto the script file to create shortcut files ###
70 | ### automatically without any graphical interface interaction. These ###
71 | ### shortcut files will be created in the same directory as the script ###
72 | ### file. ###
73 | ### ###
74 | ### ###
75 | ### Locale Emulator ###
76 | ### --------------- ###
77 | ### A software that can run applications (that has no Unicode support) ###
78 | ### with a different locale than the systems default. ###
79 | ### Usually used to run Japanese Games on non-Japanese versions of ###
80 | ### Windows. ###
81 | ### ###
82 | ### Exellent software, highly recommended. ###
83 | ### URL: https://pooi.moe/Locale-Emulator/ ###
84 | ### ###
85 | ### ###
86 | ### This Script ###
87 | ### ----------- ###
88 | ### Creates shortcut files for applications that will run them through ###
89 | ### Locale Emulator. ###
90 | ### ###
91 | ### ###
92 | ### Why? ###
93 | ### ---- ###
94 | ### Shortcut files created by Locale Emulator each requires their own ###
95 | ### config file. This config file is created simultaneously and stored ###
96 | ### in the targeted applications install directory. ###
97 | ### ###
98 | ### This script creates shortcut files that can use Locale Emulator ###
99 | ### without these extra config files. They instead uses Locale Emulator's ###
100 | ### global config file inside Locale Emulator's install directory. ###
101 | ### ###
102 | ### The script can also be used as a launcher for Locale Emulator. ###
103 | ### Applications can be launched directly from the Graphical User ###
104 | ### Interface. ###
105 | ### ###
106 | ### ###
107 | ### Author ###
108 | ### ------ ###
109 | ### Svintoo, 2018-10-11 ###
110 | ### ###
111 |
112 |
113 |
114 | # Stop script on any error
115 | $ErrorActionPreference = "Stop"
116 |
117 |
118 | #REGION BEGIN Script File Location {
119 |
120 | # Figure out the absolute path to this script
121 | if (Test-Path -LiteralPath $MyInvocation.MyCommand.Definition) {
122 | # Script file is run as a PowerShell (*.ps1) script.
123 | # This method of finding the script file path is compatible with PowerShell V2 and up.
124 | $script_path = $MyInvocation.MyCommand.Definition
125 | $file_targets = $Args
126 | } else {
127 | # Script file is run as a Batch (*.cmd/*.bat) script.
128 | # Script file path should be in the first argument.
129 | $script_path, $file_targets = $Args
130 | }
131 |
132 | $script_file = Split-Path $script_path -Leaf
133 | $script_dir = Split-Path $script_path -Parent
134 | #ENDREGION Script File Location }
135 |
136 |
137 |
138 | #REGION BEGIN Files {
139 | $Files = New-Object PSObject -Property @{
140 | Script = New-Object PSObject -Property @{
141 | Config = Join-Path $script_dir ([IO.Path]::GetFileNameWithoutExtension($script_file) + ".config.xml")
142 | }
143 | LE = New-Object PSObject -Property @{
144 | Runner = "LEProc.exe"
145 | Config = "LEConfig.xml"
146 | Editor = "LEGUI.exe"
147 | }
148 | }
149 | #ENDREGION Files }
150 |
151 |
152 |
153 | #REGION BEGIN Create Shortcut {
154 | function Create-LEShortcutFile {
155 | Param (
156 | <## No Alias ##>[String]$Target, # Mandatory
157 | [Alias("Args") ][String]$TargetArgs,
158 | [Alias("LEPath") ][String]$LocaleEmulatorPath, # Mandatory
159 | [Alias("LangName")][String]$LanguageName,
160 | [Alias("LangID") ][String]$LanguageID, # Mandatory
161 | [Alias("Path") ][String]$ShortcutFilePath, # Mandatory
162 | [Alias("WorkDir") ][String]$WorkingDirectory
163 | )
164 |
165 | if (-Not $Target -Or -Not $LocaleEmulatorPath -Or -Not $LanguageID -Or -Not $ShortcutFilePath) {
166 | throw "Create-LEShortcutFile: Missing arguments "
167 | }
168 |
169 | if (-Not $WorkingDirectory) { $WorkingDirectory = Split-Path $Target -Parent }
170 |
171 | if ($LanguageName) {
172 | $Description = "\(^-^)/ $LanguageName with Locale Emulator \(^-^)/"
173 | } else {
174 | $Description = "\(^-^)/ Run with Locale Emulator \(^-^)/"
175 | }
176 |
177 | if (-Not (Test-Path -LiteralPath $ShortcutFilePath)) {
178 | New-Item -ItemType file $ShortcutFilePath | Out-Null
179 | }
180 |
181 | $Shell = New-Object -ComObject Shell.Application
182 |
183 | $ShortcutDirectory = Split-Path $ShortcutFilePath -Parent
184 | $ShortcutFilename = Split-Path $ShortcutFilePath -Leaf
185 |
186 | $Shortcut = $Shell.NameSpace($ShortcutDirectory).ParseName($ShortcutFilename).GetLink
187 | $Shortcut.Description = $Description
188 | $Shortcut.Path = $LocaleEmulatorPath
189 | $Shortcut.Arguments = "-runas `"$LanguageID`" `"$Target`" $TargetArgs".Trim()
190 | $Shortcut.WorkingDirectory = $WorkingDirectory
191 | $Shortcut.SetIconLocation($Target, 0)
192 |
193 | $Shortcut.Save() | Out-Null
194 | }
195 | #ENDREGION Create Shortcut }
196 |
197 |
198 |
199 | #REGION BEGIN Get Config {
200 | <#
201 |
202 |
203 | $Language
204 | $DefaultSaveDirectory
205 | $LELocation
206 |
207 | #>
208 | function Get-ConfigFile {
209 | $ConfigPath = $Files.Script.Config
210 |
211 | if (Test-Path -LiteralPath $ConfigPath -PathType Leaf -ErrorAction SilentlyContinue) {
212 | $config = try{ ([Xml](Get-Content $ConfigPath)).Config } catch {}
213 | }
214 |
215 | # Return $Null if config file is not found or can not be parsed
216 | return $config
217 | }
218 | #ENDREGION Get Config }
219 |
220 |
221 |
222 | #REGION BEGIN FileSystem Paths {
223 | <# # Explanation to why a Regular Expression solution is used for normalizing paths # #
224 | # (Join-Path $path1 '') -Eq (Join-Path $path2 '')
225 | # o Handles paths that doesn't exists: "C:\fake\" == "C:\fake\"
226 | # o Handles both \ and /: "C:\asdf\" == "C:/asdf/"
227 | # o Handles missing \ at end of line: "C:\asdf\" == "C:\asdf"
228 | # o Handles multiple \ at end of line: "C:\asdf\" == "C:\asdf\\"
229 | # x Doesn't handle multiple \ anywhere else: "C:\\asdf" != "C:\asdf"
230 | # x Doesn't handle spaces before \ and end: "C: \asdf" != "C:\asdf" #NOTE: Only in PowerShell.
231 | # "C:\asdf " != "C:\asdf" Not valid in Batch (cmd).
232 | # (Get-ItemProperty -LiteralPath $path1).FullName -Eq (Get-ItemProperty -LiteralPath $path2).FullName
233 | # x Doesn't handle paths that doesn't exists: "C:\fake\" != "C:\fake\"
234 | # o Handles both \ and /: "C:\asdf\" == "C:/asdf/"
235 | # x Doesn't handle missing \ at end of line: "C:\asdf\" != "C:\asdf"
236 | # o Handles multiple \ at end of line: "C:\asdf\" == "C:\asdf\\"
237 | # o Handles multiple \ anywhere else: "C:\\asdf" != "C:\asdf"
238 | # o Handles spaces before \ and end: "C: \asdf" == "C:\asdf" #NOTE: Only in PowerShell.
239 | # "C:\asdf " != "C:\asdf" Not valid in Batch (cmd).
240 | # -Not (Compare-Object (Get-ItemProperty -LiteralPath $path1) (Get-ItemProperty -LiteralPath $path2))
241 | # NO! Returns $Null if EQUAL. Code is hard to understand.
242 | # AND doesn't handle paths that doesn't exists.
243 | #>
244 |
245 | # All strings that resolve to the same file system path are here normalized to the same string
246 | function Normalize-Path([String]$path) {
247 | # C:/path/somewhere => C:\path\somewhere
248 | # C:\path\\somewhere => C:\path\somewhere
249 | # C:\path\somewhere\ => C:\path\somewhere
250 | # C:\path \somewhere => C:\path\somewhere # PowerShell ignores whitespaces at end of file/folder names
251 | $path -Replace "/", "\" `
252 | -Replace "\\\\+", "\" `
253 | -Replace "\\$", "" `
254 | -Replace " +\\", "\"
255 | }
256 |
257 | # Compare two or more file system paths
258 | function Equal-Paths {
259 | if ($Args.Count -LT 2) {
260 | throw "Equal-Paths: At least two paths are needed for comparison"
261 | }
262 |
263 | $reference_path = Normalize-Path $Args[0]
264 |
265 | for ($i = 1 ; $i -LT $Args.Count ; $i++) {
266 | $path = Normalize-Path $Args[$i]
267 |
268 | if ($path -NE $reference_path) {
269 | return $False
270 | }
271 | }
272 |
273 | return $True
274 | }
275 | #ENDREGION FileSystem Paths }
276 |
277 |
278 |
279 | #REGION BEGIN Locale Emulator Functions {
280 | function Verify-LEDirectory([String]$Directory) {
281 | # Test-Path arguments '-ErrorAction SilentlyContinue' is ignored when:
282 | # - using arguments '-PathType Container', OR
283 | # - receiving an empty string ("") as path.
284 | # Hence the try-catch.
285 | $dir_exists = try { Test-Path -LiteralPath $Directory -PathType Container } catch { $False }
286 |
287 | if (-Not $dir_exists) { return $False }
288 | if (-Not (Test-Path -LiteralPath (Join-Path -Path $Directory -ChildPath $Files.LE.Runner))) { return $False }
289 |
290 | return $True
291 | }
292 |
293 | function Get-LEDirectories {
294 | Param (
295 | [String]$ConfigLEDirectory = ""
296 | )
297 |
298 | $LEDirs = New-Object System.Collections.ArrayList
299 |
300 | # Config directory
301 | if ($ConfigLEDirectory) {
302 | $DirectoryPath = $ConfigLEDirectory
303 | $Valid = Verify-LEDirectory $DirectoryPath
304 | $LEDirs.Add((New-Object PSObject -Property @{Path = $DirectoryPath; Valid = $Valid})) | Out-Null
305 | }
306 |
307 | # Script directory
308 | $DirectoryPath = $script_dir
309 | $Valid = Verify-LEDirectory $DirectoryPath
310 | if ($Valid) {
311 | $LEDirs.Add((New-Object PSObject -Property @{Path = $DirectoryPath; Valid = $Valid})) | Out-Null
312 | }
313 |
314 | # Install directory
315 | if (-Not (Test-Path "HKCR:")) { New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null }
316 | $regKeyName = "HKCR:\CLSID\{C52B9871-E5E9-41FD-B84D-C5ACADBEC7AE}\InprocServer32"
317 | $regValueName = "CodeBase"
318 | if (Test-Path $regKeyName) {
319 | $regKey = Get-Item $regKeyName
320 | $LE_DLLPath = [String](Get-ItemProperty $regKey.PSPath).$regValueName
321 | if ($LE_DLLPath) {
322 | #Ex: $LE_DLLPath = "file:///C:/path/to/Locale Emulator/LEContextMenuHandler.DLL"
323 | $DirectoryPath = $LE_DLLPath -Replace '^file:///','' -Replace '/[^/]+$','' -Replace '/','\'
324 | $Valid = Verify-LEDirectory $DirectoryPath
325 | $LEDirs.Add((New-Object PSObject -Property @{Path = $DirectoryPath; Valid = $Valid})) | Out-Null
326 | }
327 | }
328 |
329 | return ,$LEDirs
330 | }
331 |
332 | # Returns a list of the profiles in Locale Emulator's config file.
333 | function Get-LELanguages([String]$Directory) {
334 | # ArrayList is used instead of Powershell arrays because:
335 | # - Faster when changing the number of elements in the array.
336 | # - required when used with ComboBoxes in Windows Forms.
337 | $Languages = New-Object System.Collections.ArrayList
338 |
339 | [XML]$Config = try {
340 | Get-Content (Join-Path -Path $Directory -ChildPath $Files.LE.Config)
341 | } catch {
342 | # Return empty language list if the LE config file doesn't exist
343 | return ,$Languages
344 | }
345 |
346 | if ($Config.LEConfig.Profiles.Profile -Is [Xml.XmlLinkedNode]) {
347 | # Only one element in XML document
348 | $Profiles = @($Config.LEConfig.Profiles.Profile)
349 | } elseif ($Config.LEConfig.Profiles.Profile -Is [Array]) {
350 | # Multiple elements in XML document
351 | $Profiles = $Config.LEConfig.Profiles.Profile
352 | }
353 |
354 | $Profiles | % {
355 | if ($_.Name -And $_.Guid) {
356 | $Language = New-Object PSObject -Property @{Name = $_.Name; Guid = $_.Guid}
357 | $Languages.Add($Language) | Out-Null
358 | }
359 | }
360 |
361 | return ,$Languages
362 | }
363 | #ENDREGION Locale Emulator Functions }
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 | ### ### ### ### ### The Automatic Part ### ### ### ### ###
374 | ### ###
375 | ### No user interaction here. ###
376 | ### ###
377 | ### File paths are received through script arguments ###
378 | ### (by drag'n'drop) and shortcuts are created ###
379 | ### automatically. ###
380 | ### ###
381 | ### NOTE: Settings in the config file are used here ###
382 | ### ###
383 |
384 | #REGION BEGIN NoGUI {
385 | Add-Type -AssemblyName PresentationFramework
386 |
387 | if ($file_targets.Count -GT 0) {
388 | # Verify that all targets exists
389 | ForEach ($Target in $file_targets) {
390 | $TargetExists = Test-Path -LiteralPath $Target -PathType Leaf -ErrorAction SilentlyContinue
391 | if (-Not $TargetExists) {
392 | [System.Windows.MessageBox]::Show("File not found: $Target")
393 | Exit
394 | }
395 | }
396 |
397 | # Parse config file (if it exists)
398 | $Config = Get-ConfigFile
399 |
400 | # Find Locale Emulator
401 | if ($Config) {
402 | $LEDirectories = Get-LEDirectories -ConfigLEDirectory $Config.LELocation
403 | $LanguageName = $Config.Language
404 | } else {
405 | $LEDirectories = Get-LEDirectories
406 | }
407 |
408 | if ($LEDirectories.Count -Eq 0) {
409 | [System.Windows.MessageBox]::Show("Can't find Locale Emulator")
410 | Exit
411 | }
412 |
413 | $LEDirectory = $LEDirectories[0]
414 |
415 | if (-Not $LEDirectory.Valid) {
416 | [System.Windows.MessageBox]::Show("Configured path to Locale Emulator is invalid: ${$LEDirectory.Path}")
417 | Exit
418 | }
419 |
420 | $LocaleEmulatorPath = Join-Path $LEDirectory.Path $Files.LE.Runner
421 |
422 | # Fetch all available Languages (Profiles) configured in Locale Emulator
423 | $Languages = Get-LELanguages $LEDirectory.Path
424 |
425 | if ($Languages.Count -Eq 0) {
426 | [System.Windows.MessageBox]::Show("Locale Emulator has no configured profiles: ${Join-Path $LEDirectory.Path $Files.LE.Config}")
427 | Exit
428 | }
429 |
430 | # Select one language
431 | if ($LanguageName) {
432 | $Language = $Languages | Where { $_.Name -Eq $LanguageName } | Select -First 1
433 | }
434 |
435 | if (-Not $Language) { $Language = $Languages[0] }
436 |
437 | # Create all shortcut files and save them to the script directory
438 | ForEach ($Target in $file_targets) {
439 | $ShortcutFilename = [IO.Path]::GetFileNameWithoutExtension($Target)
440 | $ShortcutFilename = "(LE)" + $ShortcutFilename + ".lnk"
441 | $ShortcutFilePath = Join-Path $script_dir $ShortcutFilename
442 |
443 | Create-LEShortcutFile -Target $Target `
444 | -LocaleEmulatorPath $LocaleEmulatorPath `
445 | -LanguageName $Language.Name `
446 | -LanguageID $Language.Guid `
447 | -ShortcutFilePath $ShortcutFilePath
448 | }
449 |
450 | Exit
451 | }
452 | #ENDREGION NoGUI }
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 | ### ### ### ### ### The Interactive Part ### ### ### ### ###
462 | ### ###
463 | ### Displays a Graphical User Interface (GUI) ###
464 | ### ###
465 | ### A user can here select files and configure settings ###
466 | ### before deciding to either create shortcuts OR launch ###
467 | ### a selected file directly. ###
468 | ### ###
469 | ### Selected settings can be saved and remembered. ###
470 | ### ###
471 |
472 | #REGION BEGIN GUI {
473 | <# This form was created using POSHGUI.com a free online gui designer for PowerShell #>
474 | Add-Type -AssemblyName System.Windows.Forms
475 | [System.Windows.Forms.Application]::EnableVisualStyles()
476 |
477 |
478 |
479 | #REGION BEGIN GUI Colors {
480 | $TextBoxColors = New-Object PSObject -Property @{
481 | None = [System.Drawing.Color]::Empty
482 | Invalid = "255,235,238" # Light Pink (textbox bgcolor, used when text is invalid)
483 | }
484 | #ENDREGION GUI Colors }
485 |
486 |
487 |
488 | #REGION BEGIN GUI Elements {
489 | $Form = New-Object System.Windows.Forms.Form
490 | $Form.ClientSize = New-Object System.Drawing.Size(400,400) #'400,400'
491 | $Form.Text = "LE Shortcut Creator"
492 | $Form.TopMost = $False
493 | $Form.MaximizeBox = $False
494 | $Form.SizeGripStyle = "Hide"
495 | $Form.StartPosition = "CenterScreen" # Manual, CenterScreen, WindowsDefaultLocation, WindowsDefaultBounds, CenterParent
496 | $Form.FormBorderStyle = 'FixedDialog' # None, FixedSingle, Fixed3D, FixedDialog, Sizable, FixedToolWindow, SizableToolWindow
497 |
498 | $labels_x = 9
499 | $browse_buttons_width = 74
500 | $browse_buttons_height = 24
501 | $browse_buttons_x = 316
502 | $browse_textbox_width = 304
503 | $browse_textbox_height = 22
504 | $lang_buttons_width = 88
505 | $lang_buttons_height = 26
506 | $bottom_buttons_y = 360
507 |
508 | $Target_Label = New-Object System.Windows.Forms.Label
509 | $Target_Label.Text = "Target(s)"
510 | $Target_Label.AutoSize = $True
511 | $Target_Label.Width = 25
512 | $Target_Label.Height = 10
513 | $Target_Label.Location = New-Object System.Drawing.Point($labels_x,5)
514 |
515 | $Target_TextBox = New-Object System.Windows.Forms.TextBox
516 | $Target_TextBox.Multiline = $False
517 | $Target_TextBox.Width = $browse_textbox_width
518 | $Target_TextBox.Height = $browse_textbox_height
519 | $Target_TextBox.AutoSize = $False #Win11
520 | $Target_TextBox.Location = New-Object System.Drawing.Point(11,24)
521 | Add-Member -InputObject $Target_TextBox -MemberType "NoteProperty" -Name "Targets" -Value @()
522 | # Note that the initial directory fallback below is modified during execution
523 | Add-Member -InputObject $Target_TextBox -MemberType "NoteProperty" -Name "FileBrowserInitialDirectoryFallback" `
524 | -Value $script_dir
525 |
526 | $Target_Button = New-Object System.Windows.Forms.Button
527 | $Target_Button.Text = "Browse..."
528 | $Target_Button.Width = $browse_buttons_width
529 | $Target_Button.Height = $browse_buttons_height
530 | $Target_Button.Location = New-Object System.Drawing.Point($browse_buttons_x,23)
531 |
532 | $Args_Label = New-Object System.Windows.Forms.Label
533 | $Args_Label.Text = "Target Arguments (optional)"
534 | $Args_Label.AutoSize = $True
535 | $Args_Label.Width = 25
536 | $Args_Label.Height = 10
537 | $Args_Label.Location = New-Object System.Drawing.Point($labels_x,59)
538 |
539 | $Args_TextBox = New-Object System.Windows.Forms.TextBox
540 | $Args_TextBox.Multiline = $False
541 | $Args_TextBox.Width = $browse_textbox_width + $browse_buttons_width
542 | $Args_TextBox.Height = $browse_textbox_height
543 | $Args_TextBox.AutoSize = $False #Win11
544 | $Args_TextBox.Location = New-Object System.Drawing.Point(11,78)
545 |
546 | $Lang_Groupbox = New-Object System.Windows.Forms.Groupbox
547 | $Lang_Groupbox.Height = 56
548 | $Lang_Groupbox.Width = 378
549 | $Lang_Groupbox.Text = "Language"
550 | $Lang_Groupbox.Location = New-Object System.Drawing.Point(11,119)
551 |
552 | $Lang_ComboBox = New-Object System.Windows.Forms.ComboBox
553 | $Lang_ComboBox.Width = 266
554 | $Lang_ComboBox.Height = 24
555 | $Lang_ComboBox.Location = New-Object System.Drawing.Point(9,19)
556 | $Lang_ComboBox.DropDownStyle = "DropDownList"
557 | $Lang_ComboBox.DisplayMember = "Name"
558 | $Lang_ComboBox.ValueMember = "Guid"
559 | $Lang_ComboBox.DataSource = New-Object System.Collections.ArrayList(@(,@(New-Object PSObject -Property @{Name="";Guid=""})))
560 |
561 | $Lang_Button_Edit = New-Object System.Windows.Forms.Button
562 | $Lang_Button_Edit.Text = "Edit List"
563 | $Lang_Button_Edit.Width = $lang_buttons_width
564 | $Lang_Button_Edit.Height = $lang_buttons_height
565 | $Lang_Button_Edit.Location = New-Object System.Drawing.Point(282,18)
566 | $Lang_Button_Edit.Enabled = $False
567 |
568 | $SaveSettings_Checkbox = New-Object System.Windows.Forms.Checkbox
569 | $SaveSettings_Checkbox.Text = "Remember Settings"
570 | $SaveSettings_Checkbox.AutoSize = $True
571 | $SaveSettings_Checkbox.AutoCheck = $True
572 | $SaveSettings_Checkbox.Location = New-Object System.Drawing.Point(($labels_x+2),187)
573 |
574 | $SaveDir_Label = New-Object System.Windows.Forms.Label
575 | $SaveDir_Label.Text = "Default Save directory"
576 | $SaveDir_Label.AutoSize = $True
577 | $SaveDir_Label.Width = 25
578 | $SaveDir_Label.Height = 10
579 | $SaveDir_Label.Location = New-Object System.Drawing.Point($labels_x,223)
580 |
581 | $SaveDir_TextBox = New-Object System.Windows.Forms.TextBox
582 | $SaveDir_TextBox.Multiline = $False
583 | $SaveDir_TextBox.Width = $browse_textbox_width
584 | $SaveDir_TextBox.Height = $browse_textbox_height
585 | $SaveDir_TextBox.AutoSize = $False #Win11
586 | $SaveDir_TextBox.Location = New-Object System.Drawing.Point(11,242)
587 | $SaveDir_TextBox.text = [Environment]::GetFolderPath("Desktop")
588 | Add-Member -InputObject $SaveDir_TextBox -MemberType "NoteProperty" -Name "TextWas" -Value $SaveDir_TextBox.text
589 |
590 | $SaveDir_Button = New-Object System.Windows.Forms.Button
591 | $SaveDir_Button.Text = "Browse..."
592 | $SaveDir_Button.Width = $browse_buttons_width
593 | $SaveDir_Button.Height = $browse_buttons_height
594 | $SaveDir_Button.Location = New-Object System.Drawing.Point($browse_buttons_x,241)
595 |
596 | $LELocation_Label = New-Object System.Windows.Forms.Label
597 | $LELocation_Label.Text = "Locale Emulator location"
598 | $LELocation_Label.AutoSize = $True
599 | $LELocation_Label.Width = 25
600 | $LELocation_Label.Height = 10
601 | $LELocation_Label.Location = New-Object System.Drawing.Point($labels_x,277)
602 |
603 | $LELocation_TextBox = New-Object System.Windows.Forms.TextBox
604 | $LELocation_TextBox.Multiline = $False
605 | $LELocation_TextBox.Width = $browse_textbox_width
606 | $LELocation_TextBox.Height = $browse_textbox_height
607 | $LELocation_TextBox.AutoSize = $False #Win11
608 | $LELocation_TextBox.BackColor = $TextBoxColors.Invalid
609 | $LELocation_TextBox.Location = New-Object System.Drawing.Point(11,294)
610 | Add-Member -InputObject $LELocation_TextBox -MemberType "NoteProperty" -Name "TextWas" -Value ""
611 | Add-Member -InputObject $LELocation_TextBox -MemberType "NoteProperty" -Name "Valid" -Value $False
612 | Add-Member -InputObject $LELocation_TextBox -MemberType "NoteProperty" -Name "NewValid" -Value $Null
613 |
614 | $LELocation_Button = New-Object System.Windows.Forms.Button
615 | $LELocation_Button.Text = "Browse..."
616 | $LELocation_Button.Width = $browse_buttons_width
617 | $LELocation_Button.Height = $browse_buttons_height
618 | $LELocation_Button.Location = New-Object System.Drawing.Point($browse_buttons_x,293)
619 |
620 | $Run_Button = New-Object System.Windows.Forms.Button
621 | $Run_Button.Text = "Run Target"
622 | $Run_Button.Width = 115
623 | $Run_Button.Height = 30
624 | $Run_Button.Location = New-Object System.Drawing.Point(10,$bottom_buttons_y)
625 | $Run_Button.Enabled = $False
626 |
627 | $Save_Button = New-Object System.Windows.Forms.Button
628 | Add-Member -InputObject $Save_Button -MemberType "NoteProperty" -Name "TextSingular" -Value "Save Shortcut"
629 | Add-Member -InputObject $Save_Button -MemberType "NoteProperty" -Name "TextPlural" -Value "Save Shortcuts"
630 | $Save_Button.Text = $Save_Button.textSingular
631 | $Save_Button.Width = 115
632 | $Save_Button.Height = 30
633 | $Save_Button.Location = New-Object System.Drawing.Point(142,$bottom_buttons_y)
634 | $Save_Button.Enabled = $False
635 |
636 | $Quit_Button = New-Object System.Windows.Forms.Button
637 | $Quit_Button.Text = "Quit"
638 | $Quit_Button.Width = 115
639 | $Quit_Button.Height = 30
640 | $Quit_Button.Location = New-Object System.Drawing.Point(275,$bottom_buttons_y)
641 |
642 | $form_controls = @($Target_TextBox,$Target_Button,$Target_Label,$Args_TextBox,$Args_Label,$Lang_Groupbox,$SaveSettings_Checkbox,$SaveDir_TextBox,$SaveDir_Button,$SaveDir_Label,$LELocation_TextBox,$LELocation_Button,$LELocation_Label,$Run_Button,$Save_Button,$Quit_Button)
643 | $lang_controls = @($Lang_ComboBox,$Lang_Button_Edit)
644 | $controls = $form_controls + $lang_controls
645 |
646 | $Form.controls.AddRange($form_controls)
647 | $Lang_Groupbox.controls.AddRange($lang_controls)
648 | #ENDREGION GUI Elements }
649 |
650 |
651 |
652 | #REGION BEGIN GUI LELocations Elements }
653 | $Form_LELocations = New-Object System.Windows.Forms.Form
654 | $Form_LELocations.ClientSize = New-Object System.Drawing.Size(400,182)
655 | $Form_LELocations.Text = "LE Locations"
656 | $Form_LELocations.TopMost = $False
657 | $Form_LELocations.MaximizeBox = $False
658 | $Form_LELocations.SizeGripStyle = "Hide"
659 | $Form_LELocations.StartPosition = "CenterScreen" # Manual, CenterScreen, WindowsDefaultLocation, WindowsDefaultBounds, CenterParent
660 | $Form_LELocations.FormBorderStyle = 'FixedDialog' # None, FixedSingle, Fixed3D, FixedDialog, Sizable, FixedToolWindow, SizableToolWindow
661 | Add-Member -InputObject $Form_LELocations -MemberType "NoteProperty" -Name "ButtonAction" -Value ""
662 |
663 | $LELocations_Label = New-Object System.Windows.Forms.Label
664 | $LELocations_Label.Text = "Which Locale Emultor do you want to use?"
665 | $LELocations_Label.AutoSize = $True
666 | $LELocations_Label.Height = 10
667 | $LELocations_Label.Location = New-Object System.Drawing.Point(9,8)
668 |
669 | $LELocations_ListBox = New-Object System.Windows.Forms.ListBox
670 | $LELocations_ListBox.SelectionMode = "one"
671 | $LELocations_ListBox.Sorted = $False
672 | $LELocations_ListBox.HorizontalScrollbar = $True
673 | $LELocations_ListBox.Width = 378
674 | $LELocations_ListBox.Height = 100
675 | $LELocations_ListBox.Location = New-Object System.Drawing.Point(11,32)
676 | $LELocations_ListBox.DisplayMember = "Name"
677 | $LELocations_ListBox.ValueMember = "Value"
678 | $LELocations_ListBox.DataSource = New-Object System.Collections.ArrayList(@(,@(New-Object PSObject -Property @{Name=""; Value=""})))
679 | Add-Member -InputObject $LELocations_ListBox -MemberType "NoteProperty" -Name "LastSelection" -Value ""
680 |
681 | $LELocations_ButtonCancel = New-Object System.Windows.Forms.Button
682 | $LELocations_ButtonCancel.Text = "Cancel"
683 | $LELocations_ButtonCancel.width = 115
684 | $LELocations_ButtonCancel.Height = 30
685 | $LELocations_ButtonCancel.Location = New-Object System.Drawing.Point(10,142)
686 |
687 | $LELocations_ButtonBrowse = New-Object System.Windows.Forms.Button
688 | $LELocations_ButtonBrowse.Text = "Browse"
689 | $LELocations_ButtonBrowse.Width = 115
690 | $LELocations_ButtonBrowse.Height = 30
691 | $LELocations_ButtonBrowse.Location = New-Object System.Drawing.Point(142,142)
692 |
693 | $LELocations_ButtonSelect = New-Object System.Windows.Forms.Button
694 | $LELocations_ButtonSelect.Text = "Select"
695 | $LELocations_ButtonSelect.Width = 115
696 | $LELocations_ButtonSelect.Height = 30
697 | $LELocations_ButtonSelect.Location = New-Object System.Drawing.Point(275,142)
698 |
699 | $lelocation_controls = @($LELocations_ListBox,$LELocations_Label,$LELocations_ButtonCancel,$LELocations_ButtonBrowse,$LELocations_ButtonSelect)
700 | $Form_LELocations.controls.AddRange($lelocation_controls)
701 | #ENDREGION GUI LELocations Elements }
702 |
703 |
704 |
705 | #REGION BEGIN GUI Functions {
706 |
707 | # Tries to find a valid absolute path in a string (best effort).
708 | # Should return an empty string on failure.
709 | function Find-ValidPath([String]$String) {
710 | $Path = $String.Trim(' "') -Replace '^[^a-zA-Z]+',''
711 | $Path = Normalize-Path $Path
712 |
713 | #if ($Path.Length -LT 2 -Or $Path -Match '^[a-zA-Z][^:]') {
714 | if ($Path.Length -LT 2 -Or -Not ($Path -Match '^[a-zA-Z]+:')) {
715 | return ""
716 | }
717 |
718 | if ($Path) {
719 | # Shorten the path until a valid one is found
720 | while ($Path -NE "") {
721 | # Test-Path arguments '-ErrorAction SilentlyContinue' is ignored when:
722 | # - using arguments '-PathType Container', OR
723 | # - receiving an empty string ("") as path.
724 | # Hence the try-catch.
725 | $valid_path = try { Test-Path -LiteralPath $Path -PathType Container } catch { $False }
726 | if ($valid_path) { break }
727 | $Path = Split-Path $Path -Parent
728 | }
729 | }
730 |
731 | return $Path
732 | }
733 |
734 | function Show-SaveFileDialog {
735 | Param (
736 | [String]$Title = "",
737 | [String]$InitialFilename = "",
738 | [String]$InitialDirectory = "",
739 | [String]$InitialDirectoryFallback = "",
740 | [String]$Filter = "",
741 | [Int] $FilterIndex = 0
742 | )
743 |
744 | $SaveFileDialog = New-Object -Typename System.Windows.Forms.SaveFileDialog
745 | $SaveFileDialog.Title = $Title
746 | $SaveFileDialog.Filter = $Filter
747 | $SaveFileDialog.FilterIndex = $FilterIndex
748 | $SaveFileDialog.FileName = $InitialFilename
749 | $SaveFileDialog.ShowHelp = $True
750 |
751 | if ($InitialDirectory -And ($InitialDirectory = Find-ValidPath $InitialDirectory)) {
752 | $SaveFileDialog.InitialDirectory = $InitialDirectory
753 | } elseif ($InitialDirectoryFallback) {
754 | $SaveFileDialog.InitialDirectory = $InitialDirectoryFallback
755 | }
756 |
757 | $dialog_result = $SaveFileDialog.ShowDialog()
758 |
759 | if ($dialog_result -Eq [System.Windows.Forms.DialogResult]::OK) {
760 | return $SaveFileDialog.Filename
761 | } else {
762 | return $Null
763 | }
764 | }
765 |
766 | function Show-OpenFileDialog {
767 | Param (
768 | [String]$InitialDirectory = "",
769 | [String]$InitialDirectoryFallback = "",
770 | [String]$Filter = "",
771 | [Int] $FilterIndex = 0,
772 | [Switch]$CheckFileExists,
773 | [Switch]$Multiselect
774 | )
775 |
776 | $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
777 | $OpenFileDialog.Filter = $Filter #"All files (*.*)| *.*|Applications (*.exe)| *.exe"
778 | $OpenFileDialog.FilterIndex = $FilterIndex
779 | $OpenFileDialog.CheckFileExists = $CheckFileExists
780 | $OpenFileDialog.Multiselect = $Multiselect
781 | $OpenFileDialog.ShowHelp = $True # OpenFileDialog won't show unless the help button is displayed
782 |
783 | if ($InitialDirectory -And ($InitialDirectory = Find-ValidPath $InitialDirectory)) {
784 | $OpenFileDialog.InitialDirectory = $InitialDirectory
785 | } elseif ($InitialDirectoryFallback) {
786 | $OpenFileDialog.InitialDirectory = $InitialDirectoryFallback
787 | }
788 |
789 | $dialog_result = $OpenFileDialog.ShowDialog()
790 |
791 | if ($dialog_result -NE [System.Windows.Forms.DialogResult]::OK) {
792 | return $Null
793 | } elseif ($OpenFileDialog.FileNames.Count -Eq 1) {
794 | return $OpenFileDialog.FileName
795 | } else {
796 | return '"' + ($OpenFileDialog.FileNames -Join '" "') + '"'
797 | }
798 | } #END Function Show-OpenFileDialog
799 |
800 |
801 | function Show-FolderBrowserDialog([String]$Description, [bool]$ShowNewFolderButton = $False, [String]$InitialDirectory = "") {
802 | $FolderBrowserDialog = New-Object System.Windows.Forms.FolderBrowserDialog
803 | $FolderBrowserDialog.Description = $Description
804 | $FolderBrowserDialog.RootFolder = "Desktop" # Desktop, MyComputer, MyDocuments, Favorites, Personal, DesktopDirectory
805 | $FolderBrowserDialog.ShowNewFolderButton = $ShowNewFolderButton
806 |
807 | if ($InitialDirectory -And ($InitialDirectory = Find-ValidPath $InitialDirectory)) {
808 | $FolderBrowserDialog.SelectedPath = $InitialDirectory
809 | } else {
810 | $FolderBrowserDialog.SelectedPath = [Environment]::GetFolderPath("Desktop")
811 | }
812 |
813 | $dialog_result = $FolderBrowserDialog.ShowDialog()
814 |
815 | if ($dialog_result -NE [System.Windows.Forms.DialogResult]::OK) {
816 | return $Null
817 | } else {
818 | return $FolderBrowserDialog.SelectedPath
819 | }
820 | } #END Function Show-FolderBrowserDialog
821 |
822 |
823 | function Show-LELocationDialog($LEPaths, $DefaultSelection) {
824 | $Options = New-Object System.Collections.ArrayList
825 | if (-Not $DefaultSelection) { $DefaultSelection = $LELocations_ListBox.LastSelection }
826 |
827 | ForEach ($LEPath in $LEPaths) {
828 | $Options.Add((New-Object PSObject -Property @{Name=$LEPath; Value=$LEPath})) | Out-Null
829 | }
830 |
831 | $LELocations_ListBox.DataSource = $Options
832 |
833 | $Option = $LELocations_ListBox.DataSource | Where { $_.Value -Eq $DefaultSelection } | Select -First 1
834 | if ($Option) { $LELocations_ListBox.SelectedItem = $Option }
835 |
836 | #$Form_LELocations.Location = Get-NewGUIPosition $Form_LELocations
837 | $Form_LELocations.ShowDialog() | Out-Null
838 |
839 | Switch -Exact ($Form_LELocations.ButtonAction) {
840 | "Select" { return $LELocations_ListBox.SelectedValue }
841 | "Browse" { return "browse" }
842 | "Cancel" { return $Null }
843 | default { Throw "Fatal Error: This should never happen" }
844 | }
845 | }
846 |
847 | function GUI-Update-LE {
848 | GUI-Update-Languages
849 | GUI-Update-RunCreate
850 | }
851 |
852 | function GUI-Disable-LE {
853 | GUI-Disable-Languages
854 | GUI-Disable-RunCreate
855 | }
856 |
857 | function GUI-Update-RunCreate {
858 | if (-Not $LELocation_TextBox.Valid -Or -Not $Lang_ComboBox.Enabled) {
859 | $Run_Button.Enabled = $False
860 | $Save_Button.Enabled = $False
861 | return
862 | }
863 |
864 | if ($Target_TextBox.Targets.Count -Eq 1) {
865 | $Run_Button.Enabled = $True
866 | $Save_Button.Enabled = $True
867 | $Save_Button.Text = $Save_Button.TextSingular
868 | } elseif ($Target_TextBox.Targets.Count -ge 2) {
869 | $Run_Button.Enabled = $False
870 | $Save_Button.Enabled = $True
871 | $Save_Button.Text = $Save_Button.TextPlural
872 | }
873 | }
874 |
875 | function GUI-Disable-RunCreate {
876 | $Run_Button.Enabled = $False
877 | $Save_Button.Enabled = $False
878 | }
879 |
880 | function GUI-Update-Languages {
881 | $Languages = Get-LELanguages -Directory $LELocation_TextBox.Text
882 |
883 | if ($Languages.Count -Eq 0) {
884 | # ComboBox.DataSource must at all times be a
885 | # System.Collections.ArrayList containing at least one PSObject.
886 | # Otherwise it will start displaying the elements incorrectly in the GUI.
887 | $empty_language = New-Object PSObject -Property @{Name=""; Guid=""}
888 | $Languages.Add($empty_language) | Out-Null
889 | $Lang_ComboBox.DataSource = $Languages
890 | $Lang_ComboBox.Enabled = $False
891 | return
892 | }
893 |
894 | $Lang_ComboBox.Enabled = $True
895 |
896 | $language_differences = Compare-Object -Property Name,Guid $Languages $Lang_ComboBox.DataSource
897 | $current_selection_name = $Lang_ComboBox.SelectedItem.Name
898 |
899 | if ($language_differences) {
900 | $Lang_ComboBox.DataSource = $Languages
901 |
902 | # If possible, change the selection to the same language as before
903 | $Language = $Lang_ComboBox.DataSource | Where { $_.Name -Eq $current_selection_name } | Select -First 1
904 | if ($Language) { $Lang_ComboBox.SelectedItem = $Language }
905 | }
906 | }
907 |
908 | function GUI-Disable-Languages {
909 | $Lang_ComboBox.Enabled = $False
910 | }
911 |
912 | function Save-ConfigFile {
913 | $Language = $Lang_ComboBox.SelectedItem.Name
914 | $DefaultSaveDirectory = $SaveDir_TextBox.Text
915 | $LELocation = $LELocation_TextBox.Text
916 |
917 | $Config = @"
918 |
919 |
920 | $Language
921 | $DefaultSaveDirectory
922 | $LELocation
923 |
924 | "@
925 |
926 | $ConfigPath = $Files.Script.Config
927 | $Config | Out-File $ConfigPath
928 | }
929 |
930 | function Quit-Application {
931 | if ($SaveSettings_Checkbox.Checked) {
932 | Save-ConfigFile
933 | } elseif (Test-Path -LiteralPath $Files.Script.Config -PathType Leaf -ErrorAction SilentlyContinue) {
934 | Remove-Item -Path $Files.Script.Config
935 | }
936 |
937 | $Form.Close()
938 | }
939 | #ENDREGION GUI Functions }
940 |
941 |
942 |
943 | #REGION BEGIN GUI Events {
944 | # Here there be event code for the main window
945 |
946 | # Mouse drop support
947 | #NOTE: This requires that PowerShell was executed with: -Sta
948 | $Form.AllowDrop = $True
949 | $Form.Add_DragEnter({ $_.Effect = 'Copy' })
950 | $Form.Add_DragDrop({
951 | if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::Text)) {
952 | # If the dropped data is: plain text
953 | $Target_TextBox.Text = $_.Data.GetData([Windows.Forms.DataFormats]::Text)
954 | } elseif ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) {
955 | # If the dropped data is: one or more files
956 | $Filenames = $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop)
957 |
958 | if ($Filenames.Count -Eq 1) {
959 | $Target_TextBox.Text = $Filenames[0]
960 | } else {
961 | $Target_TextBox.Text = '"' + ($Filenames -Join '" "') + '"'
962 | }
963 | }
964 | })
965 |
966 |
967 | $Target_TextBox.Add_TextChanged({
968 | # Normalize targets text string
969 | $targets_text = $Target_TextBox.Text
970 | $targets_text = $targets_text.Trim() # Remove surrounding whitespaces
971 |
972 | # This script block is run if targets are deemed invalid
973 | $targets_invalid_scriptblock = {
974 | $Target_TextBox.Targets = @()
975 | $Target_TextBox.BackColor = $TextBoxColors.Invalid
976 | GUI-Disable-RunCreate
977 | }
978 |
979 | # If empty string
980 | if ($targets_text -Eq "") {
981 | Invoke-Command -ScriptBlock $targets_invalid_scriptblock
982 | $Target_TextBox.BackColor = $TextBoxColors.None # No error color on empty string
983 | return
984 | }
985 |
986 | # If NOT inside quotation marks
987 | if ($targets_text -Match '^[^"]' -And $targets_text -Match '[^"]$') {
988 | $targets_text = '"' + $targets_text + '"'
989 | }
990 |
991 | #TODO: Maybe this isn't needed?
992 | if ($targets_text -Eq '""') {
993 | Invoke-Command -ScriptBlock $targets_invalid_scriptblock
994 | return
995 | }
996 |
997 | # If uneven number of double quotes (")
998 | if ( ([Regex]::Matches($targets_text,'"')).Count % 2 -NE 0 ) {
999 | Invoke-Command -ScriptBlock $targets_invalid_scriptblock
1000 | return
1001 | }
1002 |
1003 | # Convert $targets_text to an array of strings
1004 | $Targets = [Regex]::Matches($targets_text,'"([^"])+"') | ForEach {$_.Value -Replace '"',''} | Select -Uniq
1005 | # Restore the array wrapping that PowerShell removes if it only contains one element
1006 | if ($Targets.GetType() -Eq [String]) {
1007 | $Targets = @($Targets)
1008 | }
1009 |
1010 | # Check that all targets can be found
1011 | ForEach ( $Target in $Targets ) {
1012 | $valid_path = Test-Path -LiteralPath $Target -PathType Leaf -ErrorAction SilentlyContinue
1013 |
1014 | if (-Not $valid_path) {
1015 | Invoke-Command -ScriptBlock $targets_invalid_scriptblock
1016 | return
1017 | }
1018 | }
1019 |
1020 | # Save targets
1021 | $Target_TextBox.FileBrowserInitialDirectoryFallback = $Targets[0]
1022 | $Target_TextBox.Targets = $Targets
1023 |
1024 | # Clear Background Color
1025 | $Target_TextBox.BackColor = $TextBoxColors.None
1026 |
1027 | # Try to activate the "Run" and "Create Shortcut" buttons
1028 | GUI-Update-RunCreate
1029 | })
1030 |
1031 |
1032 | $Target_Button.Add_Click({
1033 | $Filenames = Show-OpenFileDialog -InitialDirectory $Target_TextBox.Text `
1034 | -InitialDirectoryFallback $Target_TextBox.FileBrowserInitialDirectoryFallback `
1035 | -Filter "All files (*.*)| *.*|Applications (*.exe)| *.exe" `
1036 | -FilterIndex 2 `
1037 | -CheckFileExists `
1038 | -Multiselect
1039 | if ($Filenames -NE $Null) { $Target_TextBox.Text = $Filenames }
1040 | })
1041 |
1042 |
1043 | $Lang_Button_Edit.Add_Click({
1044 | $Path = Join-Path $LELocation_TextBox.Text $Files.LE.Editor
1045 |
1046 | if (-Not (Test-Path $Path)) {
1047 | $Title = "Unable to start editor"
1048 | $Message = "The Locale Emulator GUI seems to be missing.$([Environment]::NewLine
1049 | )File not found:$([Environment]::NewLine
1050 | )$Path"
1051 | $Buttons = "OK"
1052 | $Icon = "Error"
1053 | [System.Windows.MessageBox]::Show($Message,$Title,$Buttons,$Icon)
1054 | return
1055 | }
1056 |
1057 | # Launch the LE Editor
1058 | #TODO: Prevent the GUI from being used until the LE Editor has exited
1059 | #$Form.Enabled = $False
1060 | $PS = New-Object System.Diagnostics.Process
1061 | $PS.StartInfo.Filename = $Path
1062 | $PS.Start()
1063 | while (-Not $PS.HasExited) {
1064 | [System.Windows.Forms.Application]::DoEvents() # IMPORTANT!!!
1065 | Start-Sleep -Milliseconds 100
1066 | }
1067 | #$Form.Enabled = $True
1068 |
1069 | GUI-Update-Languages
1070 | })
1071 |
1072 |
1073 | $SaveDir_TextBox.Add_LostFocus({
1074 | # Test-Path arguments '-ErrorAction SilentlyContinue' is ignored when:
1075 | # - using arguments '-PathType Container', OR
1076 | # - receiving an empty string ("") as path.
1077 | # Hence the try-catch.
1078 | $dir_exists = try { Test-Path -LiteralPath $SaveDir_TextBox.Text -PathType Container } catch { $False }
1079 |
1080 | if ($dir_exists) {
1081 | $SaveDir_TextBox.TextWas = $SaveDir_TextBox.Text
1082 | } else {
1083 | $SaveDir_TextBox.Text = $SaveDir_TextBox.TextWas
1084 | }
1085 | })
1086 |
1087 |
1088 | $SaveDir_Button.Add_Click({
1089 | $Directory = Show-FolderBrowserDialog -Description "Choose the default directory where to save shortcuts" `
1090 | -ShowNewFolderButton $True `
1091 | -InitialDirectory $SaveDir_TextBox.Text
1092 | if ($Directory -NE $Null) { $SaveDir_TextBox.Text = $Directory }
1093 | })
1094 |
1095 |
1096 | $LELocation_TextBox.Add_TextChanged({
1097 | $path = $LELocation_TextBox.Text
1098 | $path_was = $LELocation_TextBox.TextWas
1099 | $pre_verification_result = $LELocation_TextBox.NewValid
1100 |
1101 | $LELocation_TextBox.TextWas = $LELocation_TextBox.Text
1102 | $LELocation_TextBox.NewValid = $Null
1103 |
1104 | if (Equal-Paths $path $path_was) {
1105 | return
1106 | }
1107 |
1108 | if ($pre_verification_result -NE $Null) {
1109 | $path_is_valid = $pre_verification_result
1110 | } else {
1111 | $path_is_valid = Verify-LEDirectory $path
1112 | }
1113 |
1114 | if ($path_is_valid) {
1115 | $LELocation_TextBox.Valid = $True
1116 | $LELocation_TextBox.BackColor = $TextBoxColors.None
1117 | $Lang_Button_Edit.Enabled = Test-Path -LiteralPath (Join-Path $LELocation_TextBox.Text $Files.LE.Editor)
1118 | GUI-Update-LE
1119 | } else {
1120 | $LELocation_TextBox.Valid = $False
1121 | $LELocation_TextBox.BackColor = $TextBoxColors.Invalid
1122 | $Lang_Button_Edit.Enabled = $False
1123 | GUI-Disable-LE
1124 | }
1125 | })
1126 |
1127 |
1128 | ## Select a folder containing Locale Emulator (LE)
1129 | # Normally show a Folder Browser Dialog.
1130 | # BUT: If one or more LE directories has been autodetected (that is not the
1131 | # same as $LELocation_TextBox.Text), then show a selection list instead.
1132 | $LELocation_Button.Add_Click({
1133 | # Fetch a list of autodetected LE directories (as a list of strings)
1134 | $Directories = $LEDirectories | Where { $_.Valid } | Select -ExpandProperty Path
1135 | # Restore the array wrapping that PowerShell removes if an array only contains one element
1136 | if (-Not($Directories -Is [Array])) {
1137 | $Directories = @($Directories)
1138 | }
1139 |
1140 | $OnlyShowFolderBrowser = $Directories.Count -Eq 0 -Or `
1141 | ($Directories.Count -Eq 1 -And (Equal-Paths $Directories[0] $LELocation_TextBox.Text))
1142 |
1143 | # Sets the default selection in the Selection List dialog window
1144 | $DefaultSelection = $LELocation_TextBox.Text
1145 |
1146 | while ($True) {
1147 | # Show-LELocationDialog
1148 | if (-Not($OnlyShowFolderBrowser)) {
1149 | $Directory = Show-LELocationDialog $Directories $DefaultSelection
1150 | $DefaultSelection = $Null # Do not use more than once.
1151 | # The Dialog Window will remember the last selection used automatically.
1152 |
1153 | if ($Directory -Eq $Null) { break } # User cancelled
1154 | elseif ($Directory -NE "browse") { break } # User selected an LE directory
1155 | # User chose "browse": The execution continues
1156 | }
1157 |
1158 | # Show-FolderBrowserDialog
1159 | while ($True) {
1160 | $Directory = Show-FolderBrowserDialog -Description "Where is Locale Emulator?" `
1161 | -initialDirectory $LELocation_TextBox.Text
1162 |
1163 | if ($Directory -Eq $Null) { break } # User cancelled
1164 | if (Verify-LEDirectory $Directory) { break } # User selected a valid LE directory
1165 | # User selected an invalid directory: Display error message and try again
1166 | $Title = "Locale Emulator not found"
1167 | $Message = "Locale Emulator was not found in the selected folder."
1168 | $Buttons = "OK"
1169 | $Icon = "Error"
1170 | [System.Windows.MessageBox]::Show($Message,$Title,$Buttons,$Icon)
1171 | }
1172 |
1173 | # If only FolderBrowserDialog is used: always break
1174 | # Else if user cancelled the FolderBrowserDialog: Return to LELocationDialog
1175 | if ($OnlyShowFolderBrowser -Or $Directory) { break }
1176 | }
1177 |
1178 | if (-Not($Directory)) { return }
1179 |
1180 | # Below code TRIGGERS: $LELocation_TextBox.Add_TextChanged()
1181 | $LELocation_TextBox.NewValid = $True
1182 | $LELocation_TextBox.Text = $Directory
1183 | })
1184 |
1185 |
1186 | # Click this button to run the selected executable
1187 | $Run_Button.Add_Click({
1188 | $LEProgram = Join-Path $LELocation_TextBox.Text $Files.LE.Runner
1189 | $LanguageID = $Lang_ComboBox.SelectedValue
1190 | $Target = $Target_TextBox.Targets[0]
1191 | $TargetArgs = $Args_TextBox.Text
1192 |
1193 | Start-Process -FilePath $LEProgram `
1194 | -ArgumentList "-runas `"$LanguageID`" `"$Target`" $TargetArgs"
1195 | })
1196 |
1197 |
1198 | # Click here to create a shortcut file to the executable
1199 | $Save_Button.Add_Click({
1200 | $LEProgram = Join-Path $LELocation_TextBox.Text $Files.LE.Runner
1201 | $LanguageName = $Lang_ComboBox.SelectedItem.Name
1202 | $LanguageID = $Lang_ComboBox.SelectedValue
1203 | $TargetArgs = $Args_TextBox.Text
1204 | $DefaultSaveDir = $SaveDir_TextBox.Text
1205 |
1206 | if ($Target_TextBox.Targets.Count -Eq 1) {
1207 | # One Target #
1208 | # Let the user decide both the directory and the filename.
1209 | $Target = $Target_TextBox.Targets[0]
1210 |
1211 | # Generate a filename as a suggestion to the user
1212 | $InitialFilename = [IO.Path]::GetFileNameWithoutExtension($Target)
1213 | $InitialFilename = "(LE)" + $InitialFilename
1214 |
1215 | $ShortcutFilePath = Show-SaveFileDialog -InitialFilename $InitialFilename `
1216 | -InitialDirectory $DefaultSaveDir `
1217 | -InitialDirectoryFallback [Environment]::GetFolderPath("Desktop") `
1218 | -Filter "Shortcuts| *.lnk"
1219 |
1220 | if (-Not $ShortcutFilePath) {
1221 | return
1222 | }
1223 |
1224 | if (-Not ($ShortcutFilePath -Match '\.lnk$')) {
1225 | $ShortcutFilePath += '.lnk'
1226 | }
1227 |
1228 | Create-LEShortcutFile -Target $Target `
1229 | -Args $TargetArgs `
1230 | -LEPath $LEProgram `
1231 | -LangID $LanguageID `
1232 | -Path $ShortcutFilePath
1233 | } else {
1234 | # Multiple Targets #
1235 | # Only let the user decide the directory. Filenames are automatically generated.
1236 | $Targets = $Target_TextBox.Targets
1237 |
1238 | $ShortcutDirectory = Show-FolderBrowserDialog -Description "Select the directory to where you want to save the shortcut files." `
1239 | -initialDirectory $DefaultSaveDir
1240 |
1241 | if (-Not $ShortcutDirectory) {
1242 | return
1243 | }
1244 |
1245 | ForEach ($Target in $Targets) {
1246 | $ShortcutFilename = [IO.Path]::GetFileNameWithoutExtension($Target)
1247 | $ShortcutFilename = "(LE)" + $ShortcutFilename + ".lnk"
1248 |
1249 | $ShortcutFilePath = Join-Path $ShortcutDirectory $ShortcutFilename
1250 |
1251 | Create-LEShortcutFile -Target $Target `
1252 | -Args $TargetArgs `
1253 | -LEPath $LEProgram `
1254 | -LangName $LanguageName `
1255 | -LangID $LanguageID `
1256 | -Path $ShortcutFilePath
1257 | }
1258 | }
1259 | })
1260 |
1261 |
1262 | # Escape key quits application
1263 | ForEach ($control in $controls) {
1264 | $control.Add_KeyDown({
1265 | if ($_.KeyCode -Eq "Escape") {
1266 | Quit-Application
1267 | }
1268 | })
1269 | }
1270 |
1271 | # Quit button quits application
1272 | $Quit_Button.Add_Click({
1273 | Quit-Application
1274 | })
1275 | #ENDREGION GUI Events }
1276 |
1277 |
1278 |
1279 | #REGION BEGIN GUI LELocations Events {
1280 | # Here there be event code for the LELocation window
1281 | # For more info, check out: $LELocation_Button.Add_Click
1282 |
1283 | $Form_LELocations.Add_Shown({
1284 | $LELocations_ListBox.LastSelection = $LELocations_ListBox.SelectedValue
1285 | $LELocations_ListBox.Focus() # Give default focus to the ListBox
1286 | })
1287 |
1288 | $LELocations_ListBox.Add_SelectedIndexChanged({
1289 | $LELocations_ListBox.LastSelection = $LELocations_ListBox.SelectedValue
1290 | })
1291 |
1292 | # Escape key clicks the Cancel button
1293 | ForEach ($lelocation_control in $lelocation_controls) {
1294 | $lelocation_control.Add_KeyDown({
1295 | if ($_.KeyCode -Eq "Escape") {
1296 | $LELocations_ButtonCancel.PerformClick()
1297 | }
1298 | })
1299 | }
1300 |
1301 | # Enter Key clicks the Select button (only if ListBox is in focus)
1302 | $LELocations_ListBox.Add_KeyDown({
1303 | if ($_.KeyCode -Eq "Enter") {
1304 | $LELocations_ButtonSelect.PerformClick()
1305 | }
1306 | })
1307 |
1308 | $LELocations_ButtonCancel.Add_Click({
1309 | $Form_LELocations.ButtonAction = "Cancel"
1310 | $Form_LELocations.Close()
1311 | })
1312 |
1313 | $LELocations_ButtonBrowse.Add_Click({
1314 | $Form_LELocations.ButtonAction = "Browse"
1315 | $Form_LELocations.Close()
1316 | })
1317 |
1318 | $LELocations_ButtonSelect.Add_Click({
1319 | $Form_LELocations.ButtonAction = "Select"
1320 | $Form_LELocations.Close()
1321 | })
1322 | #ENDREGION GUI LELocations Events }
1323 |
1324 |
1325 |
1326 | #REGION BEGIN GUI Show {
1327 | # Here we finally initialize the main window and display it
1328 |
1329 | $Config = Get-ConfigFile
1330 |
1331 | if ($Config) {
1332 | $SaveSettings_Checkbox.Checked = $True
1333 | $LEDirectories = Get-LEDirectories -ConfigLEDirectory $Config.LELocation
1334 |
1335 | $dir_exists = try { Test-Path -LiteralPath $Config.DefaultSaveDirectory -PathType Container } catch { $False }
1336 | if ($dir_exists) { $SaveDir_TextBox.text = $Config.DefaultSaveDirectory }
1337 | } else {
1338 | $SaveSettings_Checkbox.Checked = $False
1339 | $LEDirectories = Get-LEDirectories
1340 | }
1341 |
1342 | if ($LEDirectories.Count -GT 0) {
1343 | $LEDirectory = $LEDirectories[0]
1344 | # Below code TRIGGERS: $LELocation_TextBox.Add_TextChanged()
1345 | $LELocation_TextBox.NewValid = $LEDirectory.Valid
1346 | $LELocation_TextBox.Text = $LEDirectory.Path
1347 | }
1348 |
1349 | if ($Config) {
1350 | #NOTE: $Lang_ComboBox.DataSource was maybe populated by triggering $LELocation_TextBox.Add_TextChanged() above
1351 | # If possible, change the selection to the same language as in the config
1352 | $Language = $Lang_ComboBox.DataSource | Where { $_.Name -Eq $Config.Language } | Select -First 1
1353 | if ($Language) { $Lang_ComboBox.SelectedItem = $Language }
1354 | }
1355 |
1356 | # Show Application Window
1357 | #[void]$Form.ShowDialog()
1358 | [System.Windows.Forms.Application]::Run($Form)
1359 | #ENDREGION GUI Show }
1360 |
1361 | #ENDREGION GUI }
1362 |
1363 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Initial creator and author
2 | Name: Svintoo
3 | Date: 2018-10-11
4 | URL: https://github.com/Svintooo/
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LE Shortcut Creator
2 | Create shortcuts for games/applications that will run them through Locale Emulator.
3 |
4 | Written in PowerShell (don't let the \*.cmd extention fool you).
5 | Compatible with Windows 7, Windows 8.1, Windows 10, and Windows 11.
6 |
7 | 



8 |
9 | ## Features
10 | - File drag'n'drop: Drop files on the user interface.
11 | - Batch mode: Add multiple files at once and create shortcut files for all of them.
12 | - Non-GUI mode: Give the script a list of files as parameters and shortcut files will be created without any user interaction.
13 | - Config file: Can remember last used settings between executions.
14 | - Launcher: Can be used to directly launch applications through Locale Emulator without creating a shortcut file first.
15 |
16 | ## Install Instructions
17 | Download `LEShortcutCreator.cmd` from the [releases page](https://github.com/Svintooo/LEShortcutCreator/releases).
18 |
19 | If Locale Emulator is installed it will autodetect its location. Alternatively
20 | it can autodetect an uninstalled Locale Emulator if `LEShortcutCreator.cmd` is
21 | placed in the same directory.
22 |
23 | **IMPORTANT:** Make sure to at least once run `LEInstaller.exe` from Locale Emulator (even if you do not want to have it installed). This creates some necessary dll-files that Locale Emulator needs to function.
24 |
25 | ## Usage Instructions
26 | Either double click `LEShortcutCreator.cmd` to get a Graphical User Interface.
27 | The interface will let you create shortcuts that uses Locale Emulator.
28 | Also supports file drag'n'drop.
29 |
30 | OR drag'n'drop files onto the `LEShortcutCreator.cmd` file directly to create
31 | shortcut files automatically without any graphical interface interaction. These
32 | shortcut files will be created in the same directory as the script file.
33 |
34 |
35 | ## Locale Emulator
36 | A software that can run applications (that has no Unicode support)
37 | with a different locale than the systems default.
38 | It is usually used to run Japanese Games on non-Japanese versions of
39 | Windows.
40 |
41 | Excellent software, highly recommended.
42 | URL: https://pooi.moe/Locale-Emulator/
43 |
44 |
45 | ## This Script
46 | Creates shortcut files for applications that will run them through
47 | Locale Emulator.
48 |
49 |
50 | ## Why?
51 | Shortcut files created by Locale Emulator itself each require their own
52 | config file. This config file is created simultaneously and stored
53 | in the targeted applications install directory.
54 |
55 | This script creates shortcut files that can use Locale Emulator
56 | without these extra config files. They instead uses Locale Emulator's
57 | global config file inside Locale Emulator's install directory (`LEConfig.xml`).
58 |
59 |
60 | ## PowerShell in a \*.cmd file?
61 | Yes. The script has a header written in BATCH, but everything else in the file is PowerShell code.
62 | The only thing the BATCH code does is to execute the rest of the file as a PowerShell script.
63 |
64 | I want the script to be executed by double clicking it.
65 | PowerShell files (\*.ps1) does not allow double clicking for security reasons.
66 | But BATCH files (\*.bat, \*.cmd) does not have this restriction.
67 | So by wrapping the PowerShell code in a BATCH script I circumvent this restriction.
68 |
69 |
70 | ## Common Problems
71 |
72 | ### The "Locale Emulator location" text field is automatically filled on start. But the text field is pink, and the "Edit List" button is greyed out
73 | **Solution:** Either reinstall Locale Emulator (run `LEInstaller.exe`), or put the `LEShortcutCreator.cmd` file in the same directory as Locale Emulator.
74 | **Reason:** This can happen if Locale Emulator was once installed, and later the Locale Emulator files were moved/deleted without uninstalling it first. The path to Locale Emulator then still exists in the Windows Registry, which is read by LE Shortcut Creator.
75 |
76 | ### No languages are listed in the dropdown menu
77 | **Solution:** Click the "Edit List" button. This will start the Locale Emulator GUI.
78 | Just close this GUI (you do not need to use it).
79 | The default languages should now show up in the dropdown menu.
80 | **Reason:** The Locale Emulator file `LEConfig.xml` is not included in the download. But a default config is created after running the GUI the first time.
81 |
82 | ### Clicking the "Edit List" button gives a weird error message "Could not load file or assembly..."
83 | **Solution:** Just run and immediately close `LEInstaller.exe` from Locale Emulator folder (you do not need to perform the actual install).
84 | **Reason:** Locale Emulator needs some dll-files to function that is not included in the download. Instead, these dll-files are created when running `LEInstaller.exe` for the first time.
85 |
--------------------------------------------------------------------------------
/screenshot-win10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Svintooo/LEShortcutCreator/9705e69a4cac751e4bb2180f19aa5e229417af73/screenshot-win10.png
--------------------------------------------------------------------------------
/screenshot-win11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Svintooo/LEShortcutCreator/9705e69a4cac751e4bb2180f19aa5e229417af73/screenshot-win11.png
--------------------------------------------------------------------------------
/screenshot-win7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Svintooo/LEShortcutCreator/9705e69a4cac751e4bb2180f19aa5e229417af73/screenshot-win7.png
--------------------------------------------------------------------------------
/screenshot-win81.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Svintooo/LEShortcutCreator/9705e69a4cac751e4bb2180f19aa5e229417af73/screenshot-win81.png
--------------------------------------------------------------------------------