#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=C:\Program Files (x86)\AutoIt3\Icons\MyAutoIt3_Blue.ico #AutoIt3Wrapper_Outfile=..\..\..\Desktop\zzz\Zip27z.Exe #AutoIt3Wrapper_Compression=4 #AutoIt3Wrapper_UseUpx=y #AutoIt3Wrapper_Res_Comment=Zip to 7zip #AutoIt3Wrapper_Res_Description=Converts all zip files recursively into a 7Zip file, with max compression #AutoIt3Wrapper_Res_Fileversion=1.0.1.0 #AutoIt3Wrapper_Res_ProductName=AutoIT #AutoIt3Wrapper_Res_LegalCopyright=(c)2020 Louis Horvath #AutoIt3Wrapper_Res_Language=1033 #AutoIt3Wrapper_Run_Tidy=y #Tidy_Parameters=/gd #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ; Zip to 7zip ================================================================= ; Converts all zip files recursively into a 7Zip file, with max compression ; REQUIRES AUTOIT Version 3.3.14.5 ; + Exact copy with attributes and folder structure ; + Extensive error checking, including for files blocked by anti-virus ; + Can stop process and restart later with the use of a log file ; + Creates a .CSV file that can be used to check on compression ratios ; + Dynamic GUI that enables user to see console while 7zip is working ; + Gui can be stretched horizontally for long filenames ; + Up to 5 retries while transfering compressed files ; + Checks free space (at startup) to make sur you don't kill your OS LOL ; + Have converted 10,000+ files with no issues in file integrity. ; + Open to suggestions and program free to modify to your liking ; + Will eventually be fully modular and configurable (if there is interest for this) ; + Exit codes so you can have an idea where program went wrong (if it did) ; + Fully commented so users can tinker away. ; - Cannot save archive comments. ; - Not FULLY tested yet, do not use on .zip files that have no backups ;============================================================================== ; Written by Louis Horvath, 2016-2020 ; CREDITS ; Zedna Ressource.zip to be used in GUI translation [https://www.autoitscript.com/forum/topic/51103-resources-udf/] ; Subz Function being called with a string, returns "" [https://www.autoitscript.com/forum/topic/202896-cannot-define-default-string-value-in-func] ; Musashi Extra help regarding previous issue ; NOTES ; Is basically a re-tooled version of 7Crush (unpublished personnal test) ; TO DO ; -! Investigate rare lock-up during file renaming process (possible lost files) (possible memory leak) ; -! Investigate slowdowns during file renaming process since we had retry added. Is it going through ALL the retries? Sure feels like it. Perhaps a flag consolewrite whenever a retry is attempted? ; - Fuctions: reuse same variable name for legibility ; - Lock show / hide console --> Lock button --> Check GUI space left ; - Add retry to log and stat files? ; - Investigate why all explorer windows flash continuously when program runs (and left-click is interrupted). Is it GUI-related? ; - "Dump-all-vars" if crash / error? ; - Investigate why program stops when windows is moved or resized. Is this by design? ; - Is _ArrayConcatenate() any faster? --> showdown between my technique and _arrayconcatenate() + _arraysort() ; - Does recycling files force an explorer refresh? ; - Create a versatile "what to what" compression system ; - Ask community to help debug and increase speed? ; - Prepare to translate and create a text array $aLang[Item][Language]. Load array from file? Integrate at compilation! (see Zedna's resource.zip) ; Done 1.01 ; OK TEST to see if grouping buttons helps flashing or helps keeping them together when screen is stretched. ; OK Create "lock" checkmark ; OK Integrate retries for the random file-relate hiccups (x5), 1 second between retries ; OK Change seperator comma for semi colon since many files use coma in filename. Furthermore, semicolon seperator is universal ; OK At _Add_stat(), replace semi-colons in filename by question mark ; NO Keep comments? [NO. 7-zip does not offer command line handling of comments. However I've asked the 7-zip team for a workaround] [https://sourceforge.net/p/sevenzip/discussion/45798/thread/0bdee22857/] ; NO Make a console version? [There is no practical usage and would not be significantly faster anyways. Would add programming overhead also] ; Done 1.02 ; OK Bypass integration of "previous.log" and force starting from scratch with LEFT SHIFT during startup ; OK Do space check to avoid HD full (< 5GB on system drive?) ; OK Detect when file is flagged as virus and advise? [Yes, list archive and get internal file / folder count, unzip and compare. Throw error if number is different] ; OK Errors: return with number AND text? [No need since we already have _Error()] ; NO When extracting 7-zip tells you at lines -3 and -4 the number of files and folders. Use that instead of "7z.exe l" ? [Adds complexity and archive is in cache anyways. Might ressurect if necessary] ; OK Map error messages [See below] ; OK Pause button becomes "Restart" when pressed OR is depressed? ; OK F1 or ? brings up "about" window (rather not have a button for GUI space) [TESTING] ; OK Give credits in AU3 file ; RETURN CODES ================================================================ ; Code Proc Text / reason ; #0 No error ; #1 _7z_EXTRACT Cannot create folder ; #2 _7z_EXTRACT Error #2 while attempting to extract files with "$sLine" ; #3 _7Z_COMPRESS Error #3 while attempting to compress files with "$sLine" ; #4 _File_Enum Files missing after extraction. Deleted by antivirus? ; #5 _Replace New 7zip file cannot be found ; #6 ; #7 _Replace Could not send file to recycle bin ; #8 _Replace Cannot move compressed file! ; #9 _Load_PATH Error loading files. Error #9 ; #10 _Integrate_PRE Cannot load results.log. Error #10 ; #11 _Add_LOG Impossible to create or write to previous.log. Error #11 ; #12 _VerifBASE (folderselect) X pressed or bad folder, return error code 12 ; #13 _VerifBASE (folderselect) quit while selecting folder ; #14 _7zip_WEB Cannot find 7-zip on this computer Opt("MustDeclareVars", 1) ; Annoying but good habit to have. Compiler will complain if you haven't declared a variable as being local or global (or constant!) Opt("GUIOnEventMode", 1) ; Buttons call a function #include ; Needed To process arrays(you'd think it'd be array.au3 LOL) #include ; Constants In order To increase code legibility(the varaiable names are more significant than simply numbers) #include ; Required For _IsPressed() ; VARIABLES SETUP ============================================================= Global $aGUI[9] ; Create a matrix in order to store the GUI values (slightly slower but so much neater! cell [0] contains window handle Global $bPause = False ; Bolean value in order to toggle pause Global $sVer = "Zip27z 1.00 [English]" ; Actual program name, used to stop running twice and written at the top of the window Global $i = 0 ; Generic variable (integer), used in for / next loops Global $sPre_File = "previous.log" ; Points to a file containing the list of previously or currently compressed files. Can be opened and modified by notepad or scite. Global $aPre_Path ; Matrix containing all the paths found in "previous.log" Global $sStat = "stat.csv" ; String containing the name of the file where compression statistics will be exported to Global $sBase = @ScriptDir ; String that points to directory where compression will be done Global $aREP ; Matrix containing the final paths of all the files in $sBase, minus all the paths in "previous.log". Sorted. Global $sPath ; String containing the current work path Global $sFile ; String containing the current work filename Global $sFile7z ; String containing the renamed filename in 7-zip format Global $sTemp ; String containing the current temporary FOLDER (not file) Global $sTemp7z ; String containing new compressed 7zip file in it's temporary folder Global $sTemp7z_LIST = FileGetShortName(@TempDir & "\" & "s7z_list.txt") ; Short path to text file that will contain archive listing Global $iPct ; Integer indicating the current percentage Global $iSaved ; Integer indicating how many bytes or megabytes have been saved Global $sDetail ; String holding information to be displayed in the GUI Global $iOSize ; Integer containing the original file size Global $pConsole ; Integer pointing to the PID of the 7-zip console while compressing or decompressing Global $hConsole ; Integer pointing to the WINDOW HANDLE of the 7-zip console while compressing or decompressing Global $bShow ; Bolean indicating if the console should be hidden or shown Global Const $iRetry = 5 ; Integer & constant indicating how many times a specific function must be retried before calling up an error Global $iFiles ; Integer containing the number of files contained in a given .zip file (for detecting antivirus interference) Global $iFolders ; Integer containing the number of folders contained in a given .zip file (for detecting antivirus interference) Global $hDLL = DllOpen("user32.dll") ; Clunky shit. Hate it. Used for _IsPressed(). Must unload after use. Global $s7z = _7zip() ; Path to 7-zip, function contains extensive path checking. ; 7-Zip commands ; Chr(162) & Chr (163) = source ; Chr(164) & Chr (165) = destination Global $s7Z_Extract = " x " & Chr(162) & Chr(163) & " -o" & Chr(164) & Chr(165) & " -aoa -r" ; Extract, recurse and ignore all warnings Global $s7Z_Comp = " a " & Chr(162) & Chr(163) & " * -m0=lzma2 -mx=9 -mfb=273 -md=64m -ms=on -sdel -r" ; Compress recursively and delete sourcefiles Global $s7z_List = " l " & Chr(162) & Chr(163) & " > " & $sTemp7z_LIST ; List all files in archive, pipe them in "s7z_list.txt" ; PROGRAM SETUP =============================================================== _VerifPROG() ; First of all make sure this is the only instance running _FreeSpace() ; Make sure there is enough free space to extract files (at least 5 gigabytes) _VerifBASE() ; Check for 'previous.log', if it does not exist, ask for a new path or propose to use the current one _Interface() ; Put up the GUI _Progress(0, "Loading files recursively", " ") ; Tell user what's going on. _Load_PATH($sBase, $aREP) If FileExists($sPre_File) Then ; If second time running, integrate previous file _Progress(0, "Integration of 'previous.log'", " ") ; Once again tell user what's going on _Integrate_PRE() ; Let's do it! EndIf If Not FileExists($sStat) Then ; Statistics: create the header for a .CSV file if it doesn't exist. FileWriteLine($sStat, "Path;Source;Destination;Zip size;7zip size;Difference") ; The first line in a .CSV file must be the name of each of the columns, separated by commas. EndIf ; MAIN LOOP =================================================================== For $i = 1 To $aREP[0] ; Main loop! Go through all the cells of $aRep!! $sPath = StringLeft($aREP[$i], StringInStr($aREP[$i], "\", 0, -1)) ; Isolate folder name $sFile = StringRight($aREP[$i], StringLen($aREP[$i]) - StringInStr($aREP[$i], "\", 0, -1)) ; ... and the filename $sFile7z = StringTrimRight($sFile, 4) & ".7z" ; Convert the filename to a 7zip file $sTemp = @TempDir & "\" & $sFile & "\" ; And determine the temporary folder $sTemp7z = @TempDir & "\" & $sFile & "\" & $sFile7z ; New compressed 7zip file (in temp folder) $iPct = Int(($i / $aREP[0]) * 100) ; Now calculate what percentage of the job is actually done $sDetail = "[" & $i & " of " & $aREP[0] & "][" & "S:" & _Saved($iSaved) & "]" & @TAB ; Build the text line to display in the GUI $iOSize = FileGetSize($aREP[$i]) ; Find the original size of the file (for statistics) If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Enumerating Zip ...", $iPct & "% - " & $sVer) ; Tell user we're extracting files _7z_LIST($aREP[$i]) ; Start by finding out how many files/folders are in the zip file file If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Extract Zip ...", $iPct & "% - " & $sVer) ; Tell user we're extracting files _7z_EXTRACT($aREP[$i], $sTemp) ; From the original file to the temporary folder If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Enumerating files ...", $iPct & "% - " & $sVer) ; Tell user we're extracting files _File_ENUM($sTemp) ; Start by finding out how many files/folders are in the zip file file If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Compress 7zip ...", $iPct & "% - " & $sVer) ; Advise user we're compressing files _7Z_COMPRESS($sTemp, $sFile7z) ; Compress temporary folder ($sTemp) and give it the proper name ($sFile7z) If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Remplacement ...", $iPct & "% - " & $sVer) ; Now replacing file. Tell user about it. _Replace($aREP[$i], $sTemp7z) ; Replace file if smaller. Old file, new file If $bPause Then _Pause() ; Need a pause? _Progress($iPct, $aREP[$i], $sDetail & $iPct & "% Maintenance ...", $iPct & "% - " & $sVer) _Cleanup($sTemp) ; Erase all temporary files If $bPause Then _Pause() ; Need a pause? _Progress($iPct, "Writing to 'previous.log'", $aREP[$i], $iPct & "% - " & $sVer) ; Now writing line to 'previous.log'. Let user know. _Add_LOG($aREP[$i]) ; Do it. Next ; Continue main loop until all files are passed. MsgBox(64, "COMPLETED!", "Compression completed!" & @CRLF & "Compressed " & $aREP[0] & " files and saved " & @CRLF & _Saved($iSaved) & " bytes / megs total") _xit(0) ; Exit with a 0 error code. Nothing after this will be run unless it is called in the previous lines. Exit 0 ; Should not get here but just in case. It ends here. ; GUI Functions =============================================================== Func _Interface() ; Create GUI $aGUI[0] = GUICreate($sVer, 513, 129, -1, -1, BitOR($GUI_SS_DEFAULT_GUI, $WS_SIZEBOX)) ; [0] contains Window handle. DEFAULT_GUI and SIZEBOX are for resizing GUISetOnEvent($GUI_EVENT_CLOSE, "_GUI_Quit") ; Quit if GUI is closed $aGUI[1] = GUICtrlCreateLabel("", 16, 14, 480, 20) ; Setup first label (to contain filename) GUICtrlSetFont(-1, 10, 800, 0, "MS Sans Serif") ; Set font $aGUI[2] = GUICtrlCreateLabel("", 16, 56, 480, 17) ; Setup second label (process message) $aGUI[3] = GUICtrlCreateProgress(16, 36, 480, 16) ; Create a progress bar $aGUI[4] = GUICtrlCreateButton("Pause", 16, 88, 128, 25) ; Create a pause button GUICtrlSetOnEvent($aGUI[4], "_GUI_Pause") ; If button is pressed, call _GUI_Pause $aGUI[5] = GUICtrlCreateButton("Quit", 152, 88, 129, 25) ; Create a quit button GUICtrlSetOnEvent($aGUI[5], "_GUI_Quit") ; If button is pressed, call _GUI_Quit $aGUI[6] = GUICtrlCreateButton("Show console", 288, 88, 129, 25) ; Create a "Show console" button GUICtrlSetOnEvent($aGUI[6], "_ShowCONSOLE") ; If button is pressed, show or hide console $aGUI[7] = GUICtrlCreateCheckbox("Lock", 424, 88, 49, 25) ; Create a checkbox to lock the "show CONSOLE" setting (default is reset for each file) $aGUI[8] = GUICtrlCreateGroup("", 8, 75, 464, 46) ; Group together all buttons to see if they get spread apart when window stretched. GUICtrlCreateGroup("", -99, -99, 1, 1) ; required, apparently GUICtrlSetState(-1, $GUI_HIDE) GUISetState(@SW_SHOW) ; Diplay GUI GUIRegisterMsg($WM_GETMINMAXINFO, "_Width") ; Limit vertical height HotKeySet("?", "_About") EndFunc ;==>_Interface Func _GUI_Quit() _xit(0) ; Do the exit routine Exit ; Should not get there but you never know. EndFunc ;==>_GUI_Quit Func _GUI_Pause() $bPause = Not $bPause ; Boleean: Toggle the pause EndFunc ;==>_GUI_Pause Func _Pause($i = False) ; Time to pause or unpause Local $bFlash = True If $i Then $bPause = True ; Forced pause if main loop is active While $bPause ; Hey we're paused! If $bFlash Then ControlSetText($aGUI[0], "", $aGUI[4], "Restart") _Progress($iPct, "", "", $iPct & "% - " & $sVer & " [PAUSE]") ; Display line WITH "[PAUSE]" Else ControlSetText($aGUI[0], "", $aGUI[4], "Pause") _Progress($iPct, "", "", $iPct & "% - " & $sVer) ; Display regular line EndIf $bFlash = Not $bFlash ; Toggle the flashing Sleep(500) ; Wait for half a second WEnd _Progress($iPct, "", "", $iPct & "% - " & $sVer) ; Reset line to default EndFunc ;==>_Pause Func _ShowCONSOLE() ; If the "Show console" button is pressed show console! If $bShow Then ; Bolean : what's the current status of the console? Shown or hidden? $bShow = Not $bShow ; Toggle it's value WinSetState($s7z, "", @SW_SHOW) ; Show the console! WinActivate($aGUI[0]) ; But put the GUI on top so user can press "Hide console" ControlSetText($aGUI[0], "", $aGUI[6], "Hide console") ; Change button to indicate pressing button will hide console Else ; Since this is a bolean variable, if it's not true then it is necessarily false $bShow = Not $bShow ; Toggle it's value WinSetState($s7z, "", @SW_HIDE) ; Hide the console ControlSetText($aGUI[0], "", $aGUI[6], "Show console") ; Change button to indicate pressing button will show console EndIf EndFunc ;==>_ShowCONSOLE Func _Width($hWnd, $Msg, $wParam, $lParam) ; Establishes GUI size ; Source : https://www.autoitscript.com/forum/topic/75958-gui-resize/ Local $minmaxinfo = DllStructCreate("int;int;int;int;int;int;int;int;int;int", $lParam) ; Get system info about the window DllStructSetData($minmaxinfo, 7, 513) ; min X DllStructSetData($minmaxinfo, 8, 168) ; min Y. 168 is pixel-perfect. DllStructSetData($minmaxinfo, 9, 1920) ; max X. As per a 1920x1080 screen. DllStructSetData($minmaxinfo, 10, 168) ; max Y EndFunc ;==>_Width Func _Progress($iP = 0, $sMess1 = "", $sMess2 = "", $sTitle = "") ; Update information in GUI If $sMess1 Then ControlSetText($aGUI[0], "", $aGUI[1], $sMess1) ; Modifying first line If $sMess2 Then ControlSetText($aGUI[0], "", $aGUI[2], $sMess2) ; Modifying second line GUICtrlSetData($aGUI[3], $iP) ; Progress, as percentage If WinGetTitle($aGUI[0]) <> $sTitle Then WinSetTitle($aGUI[0], "", $sTitle) ; Changes window title but only if it changed EndFunc ;==>_Progress ; 7-zip Functions ============================================================= Func _7zip() ; Returns path and executable to 7-zip or shoots error Local $sReg If @OSArch = "X86" Then ; Windows 32 bits $sReg = RegRead("HKLM\SOFTWARE\7-Zip", "Path") ; Read registry value If @error Then ; Not found! _7zip_WEB() ; This function will write a generic message and invite user to visit https://7zip.org EndIf Else ; It's a 64 bit OS $sReg = RegRead("HKLM64\SOFTWARE\7-Zip", "Path64") ; Read registry value If @error Then ; Not found! _7zip_WEB() ; This function will write a generic message and invite user to visit https://7zip.org EndIf EndIf $sReg &= "7z.exe" ; Append executable filename $sReg = FileGetShortName($sReg) ; Get short name so you don't need quotes around executable on the command line If @error Then ; 7z.exe cannot be found or another error _7zip_WEB("Cannot find 7z.exe on this computer.") ; This function will write a generic message and invite user to visit https://7zip.org EndIf Return $sReg ; Return proper path and filename EndFunc ;==>_7zip Func _7zip_WEB($sMess = "Cannot find 7zip on this computer.") ; Generic 7-zip missing message. Call _7zip_WEB() or else $sMess will come out blank Local $i $i = MsgBox(308, "Error _7zip()", $sMess & @CRLF & "Would you like to visit https://7-zip.org?") If $i = 6 Then ShellExecute("https://7-zip.org", "", "", "", @SW_MAXIMIZE) _xit(14) ; Exit program with error code 14 EndFunc ;==>_7zip_WEB Func _7z_EXTRACT($sSource, $sDest) ; Extract files from archive, from source to temp destination. Local $sLine = $s7z & StringReplace($s7Z_Extract, Chr(162) & Chr(163), Chr(34) & $sSource & Chr(34)), $i = "" ; Replace special characters by source folder between quotes $sLine = StringReplace($sLine, Chr(164) & Chr(165), Chr(34) & $sDest & Chr(34)) ; Replace special characters by destination folder between quotes $i = DirCreate($sDest) ; Create the folder in order to extract the files into it If Not $i Then ; Error! _Error("Error _7z_EXTRACT", "Cannot create folder! " & $sDest & @CRLF & "Exiting program") _xit(1) ; Exit program with error code 1 EndIf $bShow = True ; When it's true, the console is hidden. $pConsole = Run($sLine, $sDest, @SW_HIDE) ; Run that line! $pConsole should enable us to show it if need it. If @error Then ; Error! _Error("Error _7z_EXTRACT", "Error #" & @error & " while attempting to extract files with " & $sLine & @CRLF & "Exiting program") _xit(2) ; Exit program with error code 2 EndIf ControlSetText($aGUI[0], "", $aGUI[6], "Show console") ; Reset button if it's been changed While ProcessExists($pConsole) ; Idle while compression ends (required so that GUI is responsive) Sleep(100) WEnd $hConsole = 0 ; Too late, 7zip has run and console is closed. $pConsole = 0 ; Too late, 7zip has run and PID is no longer valid. EndFunc ;==>_7z_EXTRACT Func _7Z_COMPRESS($sSource, $sDest) Local $sLine, $i = "" $sLine = $s7z & StringReplace($s7Z_Comp, Chr(162) & Chr(163), Chr(34) & $sDest & Chr(34)) ; Replace special characters by destination folder between quotes $bShow = True ; When it's true, the console is hidden. $pConsole = Run($sLine, $sSource, @SW_HIDE) ; Run that line! If @error Then ; Error! _Error("Error _7Z_COMPRESS", "Error #" & @error & " while attempting to compress files with " & $sLine & @CRLF & "Exiting program") _xit(3) ; Exit program with error code 3 EndIf ControlSetText($aGUI[0], "", $aGUI[6], "Show console") ; Reset button if it's been changed While ProcessExists($pConsole) ; Idle while compression ends (required so that GUI is responsive) Sleep(100) WEnd $hConsole = 0 ; Too late, 7zip has run and console is closed. $pConsole = 0 ; Too late, 7zip has run and PID is no longer valid. EndFunc ;==>_7Z_COMPRESS Func _7z_LIST($sFile) Local $sLine = $s7z & StringReplace($s7z_List, Chr(162) & Chr(163), Chr(34) & $sFile & Chr(34)) ; Assemble the "run" line RunWait(@ComSpec & " /c " & $sLine, "", @SW_HIDE) ; Run it _7z_ENUM($sTemp7z_LIST) ; Get the data ;~ MsgBox(0, "_7z_LIST", $iFiles & @CRLF & $iFolders) ; Since a function can't return more than one variable, global $iFiles and $iFolders have been set. EndFunc ;==>_7z_LIST Func _7z_ENUM($sFile) ; Read archive listout and extract number of files and folders Local $sLine, $hFile $hFile = FileOpen($sFile, 0) ; Handle to open file (or error number) $sLine = FileReadLine($hFile, -1) ; Read the last line FileClose($hFile) ; We got what we needed, closing file If StringRight($sLine, 7) = "folders" Then ; The listing contains folders and files numbers $iFolders = Number(StringMid($sLine, StringInStr($sLine, ",") + 2)) ; Extract folder text and make it a number $iFiles = Number(StringMid($sLine, StringInStr($sLine, " ", 0, -1) + 2)) ; Extract file text and make it a number ElseIf StringRight($sLine, 5) = "files" Then ; The listing contains only files $iFolders = 0 ; Send back zero folders. $iFiles = Number(StringMid($sLine, StringInStr($sLine, " ", 0, -1) + 2)) ; Extract file text and make it a number Else ; Error, expressions not found ; Drop error here since last line did not contain what we were looking for EndIf EndFunc ;==>_7z_ENUM Func _File_ENUM($sTemp) ; Verify how many files and folders have been extracted and compare with results from _7z_ENUM() Local $aSize $aSize = DirGetSize($sTemp, 1) ;~ _ArrayDisplay($aSize, "_File_ENUM") If $aSize[1] <> $iFiles Then ; Something's wrong. Number of files within archive and files extracted from archive is different. Antivirus? _Error("Error _File_Enum", "Files missing after extraction. Deleted by antivirus? " & @CRLF & $sTemp & @CRLF & "Exiting program") _xit(4) ; Exit program with error code 4 EndIf EndFunc ;==>_File_ENUM ; Functions =================================================================== Func _VerifPROG() ; Check if an instance is already running ; Source: https://www.autoitscript.com/forum/topic/32301-start-script-check-if-script-is-running-if-it-is-kill-it-and-continue/ Local $g_szVersion = $sVer ; Give this program a name If WinExists($g_szVersion) Then Exit (255) ; If a window has this name, close this program. Return code 255. AutoItWinSetTitle($g_szVersion) ; Set windows title accordingly EndFunc ;==>_VerifPROG Func _VerifBASE() ; Check existence of previous.log. If it exists then return. Else ask what folder to use. Local $iMsgBoxAnswer, $sPath If Not _IsPressed("10", $hDLL) Then ; Shift key is NOT pressed. If FileExists($sPre_File) Or FileExists($sStat) Then ; Is there a previous file or a statistiscs file? Return ; If true, return EndIf EndIf DllClose($hDLL) ; Shift was pressed and action taken. Unload DLL $iMsgBoxAnswer = MsgBox(51 + 262144, "First run", "Zip27z starts in a new folder. Keep this folder? :" & @CRLF & "[ " & $sBase & " ]" & @CRLF & "Click NO to choose another folder or Cancel to quit.", "") Select Case $iMsgBoxAnswer = 6 ;Yes Return ; Keep the current folder and go on Case $iMsgBoxAnswer = 7 ;No, ask for a new folder $sPath = FileSelectFolder("Choose a base folder for Zip27z", "", 0, "") If @error Then _xit(12) ; X pressed or bad folder, return error code 12 $sBase = $sPath ; Proper folder sected. Set base folder and return. Return Case $iMsgBoxAnswer = 2 ; Quit with cancel or X _xit(13) ; Exit with error code 13 EndSelect EndFunc ;==>_VerifBASE Func _xit($iErr = 0) ; Close all threads and clean before exiting If $pConsole Then ProcessClose($pConsole) ; Close 7-zip if it is still running _Cleanup($sTemp) ; Erase all temporary files Exit $iErr ; Now exit! And send error code if required EndFunc ;==>_xit Func _Integrate_PRE() ; Integrating data within previous.log _FileReadToArray($sPre_File, $aPre_Path) ; Load the previous file into array $aPre_Path If @error Then ; Something went wrong! _Error("Error _Integrate_PRE()", "Cannot load results.log. Error " & @error & @CRLF & "Exiting program") _xit(10) ; Exiting program with error code 10 EndIf _ArraySort($aPre_Path) ; Sort. This is required for "magic" to happen _ArrayDel($aREP, $aPre_Path) ; Substract the integrated previous file from the current path. EndFunc ;==>_Integrate_PRE Func _Error($sT1 = "", $sC1 = "") ; Generic error message MsgBox(48, $sT1, $sC1) EndFunc ;==>_Error Func _Load_PATH($sBase, ByRef $aREP) ; Load all .7z files in $sBase and put them in $aREP $aREP = _FileListToArrayRec($sBase, "*.zip", 1, 1, 1, 2) ; List all *.zip files and put them in an array If @error Then If @extended = 9 Then _Error("Error _Load_PATH", "Zip files not found. Error #" & @error & @TAB & @extended & @CRLF & "Exiting program") Else _Error("Error _Load_PATH", "Error loading files. Error #" & @error & @TAB & @extended & @CRLF & "Exiting program") EndIf _xit(9) ; Exiting program with error code 9 Exit ; Should not get here but just in case EndIf _ArraySort($aREP) ; Sort. This is required for "magic" to happen EndFunc ;==>_Load_PATH Func _ArrayDel(ByRef $aSource, $aDel) ; Substract one array from another. REQUIRED: sorted arrays Local $aTemp[$aSource[0] + 2], $i, $j, $k = 1, $m = 1 For $i = 1 To $aDel[0] ; Go through all items in main array _Progress(Int($i / $aDel[0] * 100), "Integration ...", " ") ; Show where we're at. If $bPause Then _Pause() ; Need a pause (yes, this is where we check to see if button is pressed) For $j = $k To $aSource[0] ; And go through all items in preceding array If $bPause Then _Pause() ; Need a pause? If $aDel[$i] == $aSource[$j] Then ; EQUAL! $k = $j + 1 ; Increment ContinueLoop 2 ; Pass both For/Next loops ElseIf $aDel[$i] < $aSource[$j] Then ; TOO FAR! We passed all the items $k = $j ; Reset counter ContinueLoop 2 ; Pass both For/Next loops Else ; No match found. $aTemp[$m] = $aSource[$j] ; Transfer item $m += 1 ; Increment item counter EndIf Next Next If $m - 1 < $aSource[0] Then ; NOT DONE! For $i = $j + 1 To $aSource[0] $aTemp[$m] = $aSource[$i] ; Assign this item to $aTemp $m += 1 ; Increment counter Next EndIf ReDim $aTemp[$m] ; Chop it down to size, getting rid of extraneous values $aTemp[0] = $m - 1 ; Put size of array in cell [0] $aSource = $aTemp ; Send back the properly substracted array EndFunc ;==>_ArrayDel Func _Replace($sOld, $sNew) ; Finds out which file is smaller and replaces the larger one with the smaller one Local $i, $iOld, $iNew, $iRetryC If Not FileExists($sNew) Then ; The new 7z file doesn't exist! _Error("Error _Replace", "New 7zip file cannot be found: " & @CRLF & $sFile7z & @CRLF & "Exiting program") _xit(5) ; Exit program with error code 5 EndIf $iOld = FileGetSize($sOld) ; Find size of new compressed file $iNew = FileGetSize($sNew) ; Find size of original file _Add_STAT($sPath, $sFile, $sFile7z, $iOld, $iNew) ; Stat-it-up! ; Original path, Old filename, New filename, Old size, New size If $iNew < $iOld Then ; New file is actually smaller $iSaved += ($iOld - $iNew) ; File is therefore smaller. Increment saved bytes counter ; Recycle loop ======================================================== $iRetryC = 0 Do $i = FileRecycle($sOld) ; Send the larger file to the recycle bin If $i Then ExitLoop ; If return code <> 0 then all is OK $iRetryC = +1 ; Increment by one If $iRetryC = $iRetry Then ; Have we maxed out on retries? _Error("Error _Replace", "Could not send file to recycle bin" & @CRLF & $sOld & @CRLF & "Exiting program") _xit(7) ; Exiting program with error code 7 EndIf Sleep(1000) ; Wait a second to give system a chance to free up this file If $pConsole Then ProcessClose($pConsole) ; 7-zip might still be hanging on to this file. Let's kill the process Until $i = 1 $sOld = StringTrimRight($sOld, 4) & ".7z" ; Rename the file also since if not, it will have a .zip extension ; Move loop =========================================================== $iRetryC = 0 Do $i = FileMove($sNew, $sOld) ; Now let's move that smaller file If $i Then ExitLoop ; If return code <> 0 then all is OK $iRetryC = +1 ; Increment by one If $iRetryC = $iRetry Then ; Have we maxed out on retries? _Error("Error _Replace", "Cannot move the compressed file!" & @CRLF & $sNew & @CRLF & "Exiting program") _xit(8) ; Exiting program with error code 8 EndIf Sleep(1000) ; Wait a second to give system a chance to free up this file If $pConsole Then ProcessClose($pConsole) ; 7-zip might still be hanging on to this file. Let's kill the process Until $i = 1 EndIf EndFunc ;==>_Replace Func _Cleanup($sRep) DirRemove($sRep, 1) ; Erase all temporary files. EndFunc ;==>_Cleanup Func _Add_LOG($sFile) ; CANDIDATE FOR RETRY FileWriteLine($sPre_File, $sFile) If @error Then ; Oh no something went wrong! _Error("Error _Add_LOG", "Impossible to create or write to previous.log. Error #" & @error & @CRLF & "Exiting program") _xit(11) ; Exiting program with error code 11 EndIf EndFunc ;==>_Add_LOG Func _Add_STAT($sPath, $sFile, $sFile7z, $iOld, $iNew) ; Add stats: Path, Name, Original size, Final size and Difference ; CANDIDATE FOR RETRY Local $iDiff = $iOld - $iNew ; Difference is old - new $sFile = StringReplace($sFile, ";", "?") ; Some filenames may contain semicolons. Replace by "?" to preserve .CSV integrity $sFile7z = StringReplace($sFile7z, ";", "?") ; Some filenames may contain semicolons. Replace by "?" to preserve .CSV integrity FileWriteLine($sStat, $sPath & ";" & $sFile & ";" & $sFile7z & ";" & $iOld & ";" & $iNew & ";" & $iDiff) ; Write a line to the .CSV file EndFunc ;==>_Add_STAT Func _Saved($i) ; Return how many bytes or megs have been saved so far If $i < 1048575 Then Return $i & " B" ; Less than a meg. Give back bytes Return Int($i / 1048576) & " MB" ; If not, return megs. EndFunc ;==>_Saved Func _FreeSpace() ; Is there enough free space to extract files? Local $iFree = DriveSpaceFree(StringLeft(@TempDir, 3)) If $iFree < 5000 Then ; Less than 5 gigbytes _Error("Error _FreeSpace", "Less than 5 gigabytes free space on drive " & StringLeft(@TempDir, 3) & @CRLF & "Exiting program") _xit(13) ; Exiting program with error code 13 EndIf EndFunc ;==>_FreeSpace Func _About() Local $iMsgBoxAnswer $iMsgBoxAnswer = MsgBox(321, $sVer, "This program will recurse all .ZIP files in a folder and convert them to .7Z format using the best compression possible. Press OK to visit the AutoIT forum where this script is posted." & @CRLF & @CRLF & "https://www.autoitscript.com/forum/topic/20595-e-mail-url-hyperlink/") If $iMsgBoxAnswer = 1 Then ;OK ShellExecute("https://www.autoitscript.com/forum/topic/20595-e-mail-url-hyperlink/", "", "", "", @SW_MAXIMIZE) EndIf EndFunc ;==>_About ; DEBUG ======================================================================= ;~ Func _cw($sFile, $sLine, $sTemp7z_LIST, $iFiles, $iFolders) ;~ ConsoleWrite("$sFile = " & $sFile & @CRLF & "$sLine = " & $sLine & @CRLF & "$sTemp7z_LIST = " & $sTemp7z_LIST & @CRLF & "$iFiles = " & $iFiles & @CRLF & "$iFolders = " & $iFolders & @CRLF) ;~ EndFunc ;==>_cw