Skip to content

Commit 89c7972

Browse files
authored
v6.1 GUI options, key as password, BAT91, skip err
- skip inaccessible files - better test of non-writable dir with Desktop fallback - support for changed locations of default user folders - makecab single pass (2nd one did not reduce size much) - simplified expand function - fix no choices selected, now default - print total processing time
1 parent f46808e commit 89c7972

1 file changed

Lines changed: 67 additions & 71 deletions

File tree

Compressed 2 TXT.bat

Lines changed: 67 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
@(echo off% <#%) &color 07 &title Compressed2TXT v6.0 - Files/Folders "Send to" menu .bat ascii encoder by AveYo
1+
@(echo off% <#%) &color 07 &title Compressed2TXT v6.1 - Files/Folders "Send to" menu .bat ascii encoder by AveYo
22
chcp 65001 >nul &set "0=%~f0" &set 1=%*& powershell -nop -c iex ([io.file]::ReadAllText($env:0)) &pause &exit/b ||#>)[1]
33

4-
$env:1; if (!$env:1) {write-host "`n No input files or folders to encode! use 'Send to' context menu ...`n" -fore Yellow
5-
$s="$env:APPDATA\Microsoft\Windows\SendTo"; if ($(Split-Path $env:0) -ne $s){copy $env:0 "$s\Compressed 2 TXT.bat" -force} exit}
4+
$env:1; if (!$env:1) {write-host "`n No input files or folders to encode! use 'Send to' context menu ...`n" -fore Yellow}
5+
$SendTo = Join-Path $([Environment]::GetFolderPath("ApplicationData")) -Child 'Microsoft\Windows\SendTo'
6+
if (!$env:1) {if ($(Split-Path $env:0) -ne $SendTo){copy $env:0 "$SendTo\Compressed 2 TXT.bat" -force} exit}
67

78
$Main = {
89
# Choice text - &x is optional hotkey toggle # Choice value # Default:1
910
$c = @() ; $v = @() ; $d = @()
1011
$c += '&1 Input decoding key as password '; $v += 'password' ; $d += 0
1112
$c += '&2 Randomize decoding key (use with 1) '; $v += 'random' ; $d += 0
1213
$c += '&3 BAT91 encoder instead of BAT85 -1.7%'; $v += 'bat91' ; $d += 0
13-
$c += '&4 No long lines (adds more overhead) '; $v += 'nolonglines'; $d += 1
14-
$c += '&5 No LZX compression (full size) '; $v += 'nocompress' ; $d += 0
14+
$c += '&4 No long lines (adds more overhead) '; $v += 'nolonglines'; $d += 0
15+
$c += '&5 No LZX compression (for dense files)'; $v += 'nocompress' ; $d += 0
1516
$c += '&6 No txt encoding (cab archive only) '; $v += 'cabonly' ; $d += 0
1617

17-
# Show choices dialog - outputs $result with indexes like '1,2,4' - query via ($selected[number]) or ($choices -eq 'value')
18+
# Show Choices dialog snippet - outputs $result with indexes like '1,2,4'
1819
$all=$c -join ',';$def=(($d -split "`n")|Select-String 1).LineNumber -join ',';$choices=@();$selected=@($false)*($c.length+1)
19-
$result = Choices $all $def 'Compressed2TXT' 14; $result -split ',' |% {$selected[[int]$_] = $true;$choices += $v[[int]$_-1]}
20-
# Quit if canceled (allows no selection)
21-
if ($result -eq $null) {write-host "`n Canceled `n" -fore Yellow; exit} else {write-host $($choices -join ',')}
20+
$result = Choices $all $def 'Compressed2TXT' 14
21+
# Test individual choices presence via ($choices -eq 'value') or ($selected[number])
22+
if ($result) {$result -split ',' |% {$selected[$_-0] = $true; $choices += $v[$_-1]}}
23+
# Quit if canceled
24+
if ($result -eq $null) {write-host "`n Canceled `n" -fore Yellow; exit} else {write-host "$($choices -join ',')`n"}
2225

2326
# Choice 3: BAT91 encoder instead of BAT85 for ~1.7% less size, but will use some web-problematic chars <*`%\>
2427
$key = '.,;{-}[+](/)_|^=?O123456A789BCDEFGHYIeJKLMoN0PQRSTyUWXVZabcdfghijklmnpqrvstuwxz!@#$&~'; $chars = 85
@@ -44,23 +47,29 @@ $Main = {
4447
}
4548
}
4649

50+
# Start measuring total processing time
51+
$timer=new-object Diagnostics.StopWatch; $timer.Start()
4752
# Process command line arguments - supports multiple files and folders
4853
$arg = ([regex]'"[^"]+"|[^ ]+').Matches($env:1)
49-
$val = gi -force -lit $arg[0].Value.Trim('"')
50-
$fn1 = $val.Name
51-
# Setup work and output dirs
52-
$top = Split-Path $val; $dir = $top; $rng = [Guid]::NewGuid().Guid; $work = Join-Path $dir -Child $rng
53-
mkdir $work -force -ea 0|out-null
54-
if (!(Test-Path -lit $work)) {
55-
$dir = "$env:USERPROFILE\Desktop"; $work = Join-Path $dir -Child $rng; mkdir $work -force -ea 0|out-null
54+
$val = Get-Item -force -lit $arg[0].Value.Trim('"')
55+
$fn1 = $val.Name.replace('.','_')
56+
# Setup work and output dirs - if source not writable fallback to user Desktop
57+
$top = Split-Path $val; $dir = $top; $rng = [Guid]::NewGuid().Guid; $work = Join-Path $dir -Child $rng; $write = $work+'~'
58+
mkdir $work -force -ea 0|out-null; New-Item $write -item File -force -ea 0|out-null
59+
if (!(Test-Path -lit $work) -or !(Test-Path -lit $write)) {
60+
rmdir -lit $work -force -ea 0|out-null
61+
$dir = [Environment]::GetFolderPath("Desktop")
62+
$work = Join-Path $dir -Child $rng; mkdir $work -force -ea 0|out-null
5663
}
57-
sl -lit $work
64+
del -lit $write -force -ea 0|out-null
5865
# Grab target files names
5966
$files = @()
6067
foreach ($a in $arg) {
6168
$f = gi -force -lit $a.Value.Trim('"')
6269
if ($f.PSTypeNames -match 'FileInfo') {$files += $f} else {dir -lit $f -rec -force |? {!$_.PSIsContainer} |% {$files += $_}}
6370
}
71+
# Jump to work dir
72+
sl -lit $work
6473

6574
# Improved MakeCab ddf generator to handle localized and special characters file names better
6675
$ddf1 = @"
@@ -71,53 +80,43 @@ $Main = {
7180
.Set ReservePerCabinetSize=0`r`n.Set ReservePerDataBlockSize=0`r`n.Set ReservePerFolderSize=0`r`n.Set RptFileName=nul
7281
.Set UniqueFiles=ON`r`n.Set SourceDir=.`r`n
7382
"@
74-
$ddf2 = $ddf1+"1.cab`r`n"; $cabinet = "$work\1.cab"; [int]$renamed = 100; $rel = $top.Length + 1; if ($rel -eq 4) {$rel--}
75-
76-
# Makecab tool has issues with source filenames so just rename them, while keeping destination filenames the same
83+
# MakeCab tool has issues with source filenames so just rename them while keeping destination full; skip inaccesible files
84+
[int]$renamed = 100; $rel = $top.Length + 1; if ($rel -eq 4) {$rel--}
7785
foreach ($f in $files){
78-
copy -lit $f.FullName -dest "$work\$renamed" -force -ea 0|out-null
79-
$ddf1 += $("$renamed `""+$f.FullName.substring($rel)+"`"`r`n"); $renamed++
86+
try{ copy -lit $f.FullName -dest "$work\$renamed" -force -ea 0|out-null }catch{}
87+
if (Test-Path -lit "$work\$renamed") {$ddf1 += $("$renamed `""+$f.FullName.substring($rel)+"`"`r`n")}
88+
$renamed++
8089
}
81-
82-
# Choice 6: No text encoding (cab archive only) - single pass
83-
if ($choices -eq 'cabonly') {$comp = 'ON'} else {$comp = 'OFF'}
84-
# Choice 5: No LZX compression (full size)
85-
if ($choices -eq 'nocompress') {$comp = 'OFF'}
86-
# With default choices MakeCab is done in two passes, first just stores the files without compression
8790
[IO.File]::WriteAllText("$work\1.ddf", $ddf1, [Text.Encoding]::UTF8)
88-
write-host
91+
# Choice 5: No LZX compression (full size)
92+
if ($choices -eq 'nocompress') {$comp = 'OFF'} else {$comp = 'ON'}
93+
# Run MakeCab to either just store the files without compression or use LZX
8994
makecab.exe /F 1.ddf /D Compress=$comp /D CabinetNameTemplate=1.cab
90-
# Choice 6: No text encoding (cab archive only) - if selected script ends right after this
95+
96+
# Choice 6: No text encoding (cab archive only) - if selected, script ends right after this
9197
if ($choices -eq 'cabonly') {
92-
copy -lit $cabinet -dest $(Join-Path $dir -Child "$fn1~.cab") -force -ea 0
93-
sl -lit $top; rmdir -lit $work -rec -force -ea 0|out-null
98+
$output = Join-Path $dir -Child "$fn1~.cab"
99+
write-host "`nCAB archive only $output ..."
100+
move -lit "$work\1.cab" -dest $output -force -ea 0
101+
sl -lit $top; rmdir -lit $work -rec -force -ea 0|out-null; if (Test-Path -lit $work) {$null=cmd /c rmdir /s/q "`"$work`""}
102+
$timer.Stop()
103+
write-host "`nDone in $([math]::Round($timer.Elapsed.TotalSeconds,4)) sec" -fore Cyan
94104
exit
95105
}
96-
# With default choices MakeCab second pass LZX compresses the potentially large tree of filenames as well
97-
if ($choices -notcontains 'nocompress') {
98-
$cabinet = "$work\1.ca_"
99-
[IO.File]::WriteAllText("$work\2.ddf", $ddf2, [Text.Encoding]::UTF8)
100-
write-host
101-
makecab.exe /F 2.ddf /D Compress=ON /D CabinetNameTemplate=1.ca_
102-
}
103-
104-
# Text encoding - compact self-expanding batch file bundling ascii encoded with BAT91 cab archive of target files
105-
$HEADER = "@echo off& color 07& chcp 65001 >nul`r`n"
106-
$HEADER += 'set "0=%~f0"&powershell -nop -c cd -li(Split-Path $env:0);'
107-
$HEADER += '$f=[IO.File]::ReadAllText($env:0)-split'':bat2file\:.*'';iex($f[1]); X 1'
108-
$HEADER += "`r`n@pause& exit/b`r`n`r`n:bat2file: Compressed2TXT v6.0`r`n"
109106

107+
# Generate text decoding header - compact self-expanding batch file for bundled ascii encoded cab archive of target files
108+
$HEADER = "@echo off & color 07 & chcp 65001 >nul`r`n"
109+
$HEADER += 'set "0=%~f0" & powershell -nop -c $f=[IO.File]::ReadAllText($env:0)-split'':bat2file\:.*'';iex($f[1]); X 1'
110+
$HEADER += "`r`n@pause & exit/b`r`n`r`n:bat2file: Compressed2TXT v6.1`r`n"
110111
# Choice 4: No long lines (adds more overhead) - each line has 4 extra chars (cr lf ::) and short lines are ~8 times as many
111112
if ($choices -eq 'nolonglines') {$line = 124} else {$line = 1014}
112-
113113
# Choice 1: Input decoding key as password - or bundle it with the file for automatic extraction
114114
if ($choices -eq 'password') {
115115
$HEADER += '$b=''Microsoft.VisualBasic'';Add-Type -As $b;$k=iex "[$b.Interaction]::InputBox(''Key'','+$chars+')";'
116-
$HEADER += 'if($k.Length-ne'+$chars+'){exit} Add-Type -Ty @' + "'`r`n"
116+
$HEADER += 'if($k.Length-ne'+$chars+'){exit}Add-Type -Ty @' + "'`r`n"
117117
} else {
118118
$HEADER += '$k='''+$key+"'; Add-Type -Ty @'`r`n"
119119
}
120-
121120
# Generate text decoding C# snippet depending on Choice 3: BAT91 encoder instead of BAT85
122121
if ($choices -eq 'bat91') {
123122
$HEADER += @'
@@ -126,47 +125,44 @@ byte[] b91=new byte[256]; int n=0,c=255,v=91,q=0,z=f[x].Length; while (c>0) b91[
126125
using (FileStream o=new FileStream(fo,FileMode.Create)) { for (int i=0; i != z; i++) { c=b91[ f[x][i] ]; if (c == 91) continue;
127126
if (v == 91) {v = c;} else {v += c * 91; q |= v << n; if ((v & 8191) > 88) {n += 13;} else {n += 14;} v = 91;
128127
do {o.WriteByte((byte)q); q >>= 8; n -= 8;} while (n>7);} } if (v != 91) o.WriteByte((byte)(q | v << n)); } }}}
129-
'@ + "`r`n'@; " + 'function X([int]$x=1) {[BAT91]::Dec([ref]$f,$x+1,'
128+
'@ + "`r`n'@; "
130129
} else {
131130
$HEADER += @'
132131
using System.IO; public class BAT85 {public static void Dec (ref string[] f, int x, string fo, string key) { unchecked {
133132
byte[] b85=new byte[256];long n=0;int p=0,q=0,c=255,z=f[x].Length; while (c>0) b85[c--]=85; while (c<85) b85[key[c]]=(byte)c++;
134133
int[] p85={52200625,614125,7225,85,1}; using (FileStream o=new FileStream(fo,FileMode.Create)) { for (int i=0;i != z;i++) {
135134
c=b85[f[x][i]]; if (c==85) continue; n += c * p85[p++]; if (p==5) {p=0; q=4; while (q > 0) {q--; o.WriteByte((byte)(n>>8*q));}
136135
n=0;}} if (p>0) {for (int i=0;i<5-p;i++) {n += 84 * p85[p+i];} q=4; while (q > p-1) {q--;o.WriteByte((byte)(n>>8*q));} } } }}}
137-
'@ + "`r`n'@; " + 'function X([int]$x=1) {[BAT85]::Dec([ref]$f,$x+1,'
138-
}
139-
140-
# Generate expand function (must run twice if compressed in two passes)
141-
if ($choices -eq 'nocompress') {
142-
$HEADER += '"1.cab",$k); expand -R "1.cab" -F:* .; del "1.cab" -force}'
143-
} else {
144-
$HEADER += '"1.ca_",$k); @("1.ca_","1.cab") |% {expand -R $_ -F:* .; del $_ -force}}'
136+
'@ + "`r`n'@; "
145137
}
138+
# Generate expand function
139+
$HEADER += 'cd -lit (Split-Path $env:0); '
140+
$HEADER += 'function X([int]$x=1) {[BAT' + $chars + ']::Dec([ref]$f,$x+1,$x,$k); expand -R $x -F:* .; del $x -force}'
146141
$HEADER += "`r`n`r`n:bat2file:[ $fn1`r`n"
147142

148-
# BAT91 or BAT85 ascii encoding of cab file containing target files
143+
# BAT91 or BAT85 ascii encoding the cab archive of target files
149144
$output = Join-Path $dir -Child "$fn1~.bat"; $outputkey = Join-Path $dir -Child "$fn1~key.ini"
150145
[IO.File]::WriteAllText($output, $HEADER)
151-
write-host "`nBAT$chars encoding $output ..." -fore Green
152-
$timer=new-object Diagnostics.StopWatch; $timer.Start()
146+
write-host "`nBAT$chars encoding $output ... " -nonew
147+
$enctimer=new-object Diagnostics.StopWatch; $enctimer.Start()
153148
if ($choices -eq 'bat91') {
154-
[BAT91]::Enc($cabinet, $output, $key, $line)
149+
[BAT91]::Enc("$work\1.cab", $output, $key, $line)
155150
} else {
156-
[BAT85]::Enc($cabinet, $output, $key, $line)
151+
[BAT85]::Enc("$work\1.cab", $output, $key, $line)
157152
}
158-
$timer.Stop()
159-
write-host "$($timer.Elapsed.TotalSeconds) seconds `n"
153+
$enctimer.Stop()
154+
write-host "$([math]::Round($enctimer.Elapsed.TotalSeconds,4)) sec"
160155
[IO.File]::AppendAllText($output, "`r`n:bat2file:]`r`n")
161-
162156
# Choice 1: Input decoding key as password - saving decoding key externally
163157
if ($choices -eq 'password') {
164158
[IO.File]::WriteAllText($outputkey, $key)
165-
write-host "decoding key saved separately to $fn1~key.ini" -fore Yellow; write-host "$key`n"
159+
write-host "`ndecoding key saved separately to $fn1~key.ini" -fore Yellow; write-host "$key`n"
166160
} else {del $outputkey -force -ea 0|out-null}
167161

168-
# Done - cleanup $work dir
169-
sl -lit $top; rmdir -lit $work -rec -force -ea 0|out-null
162+
# Done - cleanup $work dir and write timer
163+
sl -lit $top; rmdir -lit $work -rec -force -ea 0|out-null; if (Test-Path -lit $work) {$null=cmd /c rmdir /s/q "`"$work`""}
164+
$timer.Stop()
165+
write-host "`nDone in $([math]::Round($timer.Elapsed.TotalSeconds,4)) sec" -fore Cyan
170166
}
171167

172168
############
@@ -208,7 +204,7 @@ public class BAT91 {
208204
using (FileStream o = new FileStream(fo, FileMode.Append)) {
209205
o.WriteByte(58); o.WriteByte(58);
210206
for (int i = 0; i != z; i++) {
211-
q |= (a[i] & 255) << n; n += 8;
207+
q |= (byte)a[i] << n; n += 8;
212208
if (n > 13) {
213209
v = q & 8191; if (v > 88) {q >>= 13; n -= 13;} else {v = q & 16383; q >>= 14; n -= 14;}
214210
o.WriteByte(b91[v % 91]); o.WriteByte(b91[v / 91]);
@@ -222,9 +218,9 @@ public class BAT91 {
222218
'@
223219

224220
# Choices dialog snippet - parameters: 1=allchoices, 2=default; [optional] 3=title, 4=textsize, 5=backcolor, 6=textcolor
225-
function Choices($all, $def, $n='Choices', [byte]$sz=12, $bc='MidnightBlue', $fc='Snow', $saved='HKCU:\Environment'){
221+
function Choices($all, $def, $n='Choices', [byte]$sz=12, $bc='MidnightBlue', $fc='Snow', $saved='HKCU:\Environment') {
226222
[void][Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); $f=new-object Windows.Forms.Form
227-
$a=$all.split(','); $s=$def.split(','); $reg=(gp $saved -ea 0).$n; if($reg.length) {$s=$reg.split(',')}
223+
$a=$all.split(','); $s=$def.split(','); $reg=(gp $saved -ea 0).$n; if($reg.length) {$s=$reg.Trim().split(',')}
228224
function rst(){ $cb | %{ $_.Checked=0; if($s -contains $_.Name){ $_.Checked=1 } } }; $f.Add_Shown({rst; $f.Activate()})
229225
$cb=@(); $i=1; $a | %{ $c=new-object Windows.Forms.CheckBox; $cb+=$c; $c.Text=$_; $c.AutoSize=1;
230226
$c.Margin='8,4,8,4'; $c.Location='64,'+($sz*3*$i-$sz); $c.Font='Tahoma,'+$sz; $c.Name=$i; $f.Controls.Add($c); $i++}

0 commit comments

Comments
 (0)