Page 1 of 1

Translate registry-based AkelPad into the ini-based portable version at a glance (PowerShell script)

Posted: Fri May 02, 2025 4:48 pm
by ewild
ini_fromRegistry_AkelPad.ps1

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

For me, the script reports as follows:
488 data entries written from the registry to 16 ini files (21 sections)
00:01.302 by ini_fromRegistry_AkelPad.ps1
Notes:
  • 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

Re: PowerShell: translate registry-based AkelPad to the ini-based portable one at a glance

Posted: Fri May 02, 2025 5:02 pm
by ewild
A useful neighboring script: to backup AkelPad ini files into a regular .zip archive:

AkelPad_iniBackup.ps1

Code: Select all

# https://akelpad.sourceforge.net/forum/viewtopic.php?p=36803#p36803
$time = [diagnostics.stopwatch]::StartNew()
$stamp = Get-Date -format "yyyyMMdd_HHmmss"

# basics
$AppBrand   = 'HKCU:\SOFTWARE\Akelsoft'
$AppName    = 'AkelPad'
$AppINI     = 'AkelPad.ini'
$backupRoot = 'Backup'
$namePrefix = 'iniBackup'

# get $AppINI root location
$iniRoot = 2 # pointer to locate $AppINI (0,1,2,3):
switch ($iniRoot){ # $root path where from to look for $AppINI:
0 {$root = $PSScriptRoot}                       # script is next to or above $AppINI
1 {$root = $PSScriptRoot|Split-Path}            # script is one level below $AppINI
2 {$root = $PSScriptRoot|Split-Path|Split-Path} # script is two levels below $AppINI
3 {$root = 'eg:\literal\path\App\AppName.ini'}} # valid literal $iniPath to $AppINI

# get $AppName ini paths; if more than one found, use the first one to work with
$iniPath = (Get-ChildItem -path $root -file -recurse -force -filter $AppINI).DirectoryName|Select -first 1

# display $iniPath
$iniPath | Write-Host -f Cyan

# backup target and temporary naming and locations
$backupPath = [IO.Path]::combine($pwd,$backupRoot)
$backupFile = [IO.FileInfo][IO.Path]::combine($backupPath,$namePrefix+'_'+$AppName+'_'+$stamp+'.zip')
$backupTemp = [IO.Path]::combine([IO.Path]::GetTempPath(),[IO.Path]::GetRandomFileName())
if (-not ($backupTemp|Test-Path)){New-Item -path $backupTemp -type directory -force|Out-null}
if (-not ($backupPath|Test-Path)){New-Item -path $backupPath -type directory -force|Out-null}

# copy archived files into temporary destination 
Copy-Item $iniPath -destination $backupTemp -force -recurse -filter *.ini

# define latest timestamp 
$files = Get-ChildItem -path $backupTemp -file -recurse -force
$last = $files|Sort -property LastWriteTime|Select -Last 1

# create backup and date it after the latest file in archive 
Compress-Archive -path "$backupTemp\*" -destination $backupFile -compression Fastest
$backupFile.LastWriteTime = $last.LastWriteTime

# clean temp
$backupTemp|Remove-Item -recurse -force -EA:Silent

# finalizing
$time.Stop()
''
'{0} ini files archived for {1:mm}:{1:ss}.{1:fff}' -f $files.count,$time.Elapsed|Write-Host -f DarkCyan
''
if ($error){'errors {0}' -f $error.count;'';$error|foreach{'error {0}' -f ++$i;$_}}

sleep -s 7

Re: PowerShell: translate registry-based AkelPad into the ini-based portable one at a glance

Posted: Wed Jul 09, 2025 11:37 am
by ewild
The main script has been rewritten.