Translate registry-based AkelPad into the ini-based portable version at a glance (PowerShell script)
Posted: Fri May 02, 2025 4:48 pm
ini_fromRegistry_AkelPad.ps1
For me, the script reports as follows:
Code: Select all
# https://akelpad.sourceforge.net/forum/viewtopic.php?p=36803#p36803
$time = [diagnostics.stopwatch]::StartNew()
$unicode = [Text.Encoding]::Unicode
$n = [Environment]::NewLine
# basics
$AppBrand = 'HKCU:\SOFTWARE\Akelsoft'
$AppName = 'AkelPad'
$AppINI = 'AkelPad.ini'
$AppPath = 'c:\literal\path\AppName'
# addons
$AppFiles = 'AkelFiles'
$AppAddons = 'Plugs'
# items that require special treatment
$AppSubsection = 'QSearch';$subsection = 'FindHistory' # subsections
$AppExtra = 'Scripts';$extra = 'Settings' # keys with no value
$MultiString = 'Recent' # MultiString in Registry ~ Piped|String in ini
$StringBinary = 'Search' # Strings in Registry ~ Binary in ini
# placeholders
$options = 'Options' # default ini section name
$noname = 'Noname' # temporary section name for ini with no sections
$dummy = 'no_value_ini_key' # temporary value for keys with no values
# avoid ini within specified location, if needed
$avoid = 1 # pointer to avoid (1) or not (0)
switch ($avoid){
0 {$avoid = $null} # nothing to avoid
1 {$avoid = "*$AppAddons\$AppExtra\*"}} # avoid path like this
# get iniPath, trying consecutively: literal, current, parent, grandparent, great-grandparent paths
$literal=[IO.DirectoryInfo]$AppPath;$current=[IO.DirectoryInfo]$pwd.path
$try=$literal,$current,$current.Parent,$current.Parent.Parent,$current.Parent.Parent.Parent|foreach{if($_.exists){$_}}
switch ($try){{(Get-ChildItem -path $_ -file -recurse -force -filter $AppINI -EA:Silent|
sort LastWriteTime -bottom 1|Tee -var script:iniPath)}{break}}
if ($iniPath){$iniPath=$iniPath.DirectoryName}
# get registry paths containing data
$paths = Get-ChildItem $AppBrand -recurse|Where{$_.ValueCount -gt 0}
# if no registry data, nothing to do here; otherwise process
if ($paths.count -eq 0){
'no registry data, nothing to do here...'|Write-Host -f Yellow} else {
'processing...'|Write-Host -f Yellow
# if no iniPath, use current directory; display iniPath
if (-not ($iniPath)){$iniPath = [IO.Path]::combine($pwd,$AppName)};$iniPath|Write-Host -f Cyan
# process each registry path
$list = foreach ($path in $paths){
# get ini file and section names regarding app structure
$root = $path.Name.substring($path.Name.indexOf($AppName)+$AppName.length).trim('\')
$link = $root.split('\')
switch ($link.count){
0 {$ini = $section = $null}
1 {$ini = $AppIni;$section = $link[0]; break}
2 {$ini = [IO.Path]::combine($AppFiles,$link[0],$link[1]+'.ini');$section = $options}
3 {$ini = [IO.Path]::combine($AppFiles,$link[0],$link[1]+'.ini');$section = $link[2]}
4 {$ini = [IO.Path]::combine($AppFiles,$link[0],$link[1],$link[3]+'.ini');$section = $null}} # end of switch
# get keys within path
$keys = Get-Item -Path $path.PSPath
$names = $keys.GetValueNames()
# get ini data key by key
foreach ($key in $names){
# get key type and value
$type = $keys.GetValueKind($key)
$property = Get-ItemPropertyValue -LiteralPath "registry::$keys" -Name $key
# regular section: type, value
switch ($type){
None {$value=$dummy}
String {$value=[string]$property}
MultiString {$value=$property -join('|')}
Binary {$value=[BitConverter]::ToString($property).replace('-','')}
DWord {
if ($property -is [uint32]){$value=[uint32]$property}
if ($property -is [int32]){$value=[int]$property}}
} # end of key, type, value switch
# specific section: type=String, value=hexString via Binary bytes
if ($section -eq $StringBinary){
$type = 'String'
$bytes = $unicode.GetBytes($property)
$value = [BitConverter]::ToString($bytes) -replace '-'}
# data collection
[PSCustomObject][Ordered]@{
Ini = $ini
Section = $section
Path = $path
Key = $key
Type = $type
Value = $value}
}} # end of keys, paths loops
# ini contents
foreach ($ini in ($list|Select Ini,Section,Key,Value|Sort Ini,Key|Group Ini|Sort Name -desc)){$i=0
$content = foreach ($section in ($ini.group|Group Section)){
if ($section.Name){
if ($i -gt 0){''}
'[{0}]' -f $section.Name}
foreach ($line in $section.group){
switch -wildcard ($line.Value){
$dummy* {'{0}' -f $line.Key}
default {'{0}={1}' -f $line.Key,$line.Value}}} $i++}
# write ini contents to the file
$out = [IO.Path]::combine($iniPath,$ini.Name)
if (-not($out|Test-Path)){New-Item $out -type file -force|Out-Null}
[IO.File]::WriteAllLines($out,$content,$unicode)
} # end of ini contents loop
# display brief report
foreach ($ini in ($list|Select Ini,Section,Path|Sort * -unique|Group Ini|Sort Name -desc)){
''
$ini.Name|Write-Host -f Cyan
foreach ($section in $ini.group){
'[{0}] ' -f $section.Section|Write-Host -f DarkCyan -no;$section.Path|Write-Host -f Green}}
''
'{0} data entries written from the registry to {1} ini files ({2} sections)' -f
$list.count,($list|Group Ini).count,$paths.count|Write-Host -f DarkCyan
} # end of process
# finalizing
$time.Stop()
'{0:mm\:ss\.fff} by {1}' -f $time.Elapsed,
$MyInvocation.MyCommand.Name|Write-Host -f DarkCyan
''
if ($error){'errors {0}' -f $error.count;'';$error|foreach{'error {0}' -f ++$i;$_}}
sleep -s 33
Notes:488 data entries written from the registry to 16 ini files (21 sections)
00:01.302 by ini_fromRegistry_AkelPad.ps1
- The script can use a literal path to AkelPad.ini and/or define it by itself, from a location up to three levels below AkelPad.ini
- The script can also transfer data from the registry back to the AkelPad\AkelFiles\Plugs\Scripts\*.ini (if such data was written there by the sister script).
By default, this option is switched off.
If needed, a user can switch this option on in the corresponding script section (see $avoid switch). - The script is written for the 64-bit AkelPad under 64-bit Windows.
- The script is written for the cross-platform PowerShell (7+).
- The sister script:
PowerShell: translate ini-based portable AkelPad into the registry-based version at a glance
https://akelpad.sourceforge.net/forum/v ... 777#p36777