EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

dirswinc.htm
Data
Data
Procedures
AddUnderscores
Arg2Gui
ByDate
ByName
ByVolume
DirVolume
EraseOlderFile
Error
ExcludeDir
LoadAndCompareScans
LoadNewer
LoadOlder
MainCon
OneLine2Dir
ParseToBuffer
ReadFile_ini
Scan
SetMaxDaysAgo
SetToday
StoreFileName
StoreIndex
StoreWideChar

This is a CON module of EuroTool program EuroDirs for Windows.


         EUROASM CPU=x64, SIMD=yes, Unicode=no
dirswinc PROGRAM Format=COFF,Width=64
          %DROPMACRO *
          INCLUDEHEAD1 argument.htm
          INCLUDE1 winabi.htm, cpuext64.htm, wins.htm, winscon.htm, winf64.htm, winsfile.htm
          INCLUDE1 memory64.htm, sort64.htm, string64.htm, status32.htm, time.htm,
↑ Data
SCAN_INDEX  STRUC     ; A structure which describes one scaned directory.
.Ptr         D DWORD  ; Relative pointer to the directory name in UTF-8. Related to [NamesInMemory].
.Size        D DWORD  ; Size of the directory name in bytes.
.Volume      D QWORD  ; Total amount of bytes in all files in this directory.
            ENDSTRUC SCAN_INDEX
CP_UTF8     EQU 65001
[.rodata]   ; Text constants.
Ext_names   DU ".names",0              ; The extension of temporary names file.
Ext_index   DU ".index",0              ; The extension of temporary index file.
Ext_scan    DU ".scan",0               ; The extension of scan file.
DateMaskW   DU "????.??.??_??.??",0    ; File date mask in unichars.
Root        DU "C:\",0                 ; Initialization of Dir.
EuroDirs::  DB "EuroDirs",0
Version::   DB " version %^Date",0
Help::      DB 0xEF,0xBB,0xBF       ; BOM UTF-8.
            DB "; EuroDirs default configuration:",13,10
            DB "/ScanNow=false     ; Do not scan if /SN=false, compare scans instead.",13,10
            DB "/ScanDir="".""     ; Directory for scan files.",13,10
            DB "/TimeZone=0        ; Time zone of scan files in hours. /TZ=0 for UTC.",13,10
            DB "/LeaveTemporary=no ; Do not erase *.name and *.index files (for debugging).",13,10
            DB "/MaxDays=35        ; Older scan files will be deleted when /SN=true.",13,10
            DB "/InputFile=        ; Old scan file to compare. Default is the oldest in ScanDir.",13,10
            DB "/OutputFile=       ; New scan file to compare. Default is the newest in ScanDir.",13,10
            DB "/ExcludeDirs=      ; Do not scan dirs beginning with it. Default is empty.",13,10
            DB "/IncludeDirs=      ; Scan those dirs only. Default is scan all.",13,10
            DB "/ViewDirs=Inc      ; Ins|Inc|Unch|Dec|Del - select one of 5 views.",13,10
            DB 0
Comparing   DB 13,10,"Comparing directories from two scan files:",13,10,0
Report1::   DB " new created directories,",0
Report2::   DB " increased directories,",0
Report3::   DB " directories which did not change,",0
Report4::   DB " decreased directories,",0
Report5::   DB " deleted directories.",0
[.data]     ; Text variables.
TodayW      DU "yyyy.mm.dd_hh.mm",0    ; Today's date during the scan in unichars.
MaxDaysAgoW DU "yyyy.mm.dd_hh.mm",0    ; Today - ArgMaxDays in unichars.
TodayA      DB "yyyy.mm.dd_hh.mm",0    ; Today's date during the scan in bytes.
MaxDaysAgoA DB "yyyy.mm.dd_hh.mm",0    ; Today - ArgMaxDays in bytes.
Disk        DB "B"                     ; Disk letters when ArgIncludeDirs is not specified.
[.bss]      ; Working memory variables.
SystemTime            DS SYSTEMTIME  ; A time structure.
FileTime              DS FILETIME    ; A time structure.
ScanIndex             DS SCAN_INDEX  ; A directory index.
File_ini              DS FILE64 ; Configuration FILE64.
File_names            DS FILE64 ; Temporary FILE64 for directory names (UTF-8).
File_index            DS FILE64 ; Temporary FILE64 for directory indexes.
File_scan             DS FILE64 ; Scan FILE64 (UTF-8), alphabetically sorted.
File_scanOlder::      DS FILE64 ; Older scan FILE64 to compare.
File_scanNewer::      DS FILE64 ; Newer scan FILE64 to compare.
Dir::                 D 1K * UNICHAR ; The working directory which is scanned. UTF-16.
DirUTF8::             D 4K * BYTE    ; The working directory translated to UTF-8.
InputFileNameUTF16::  D 264 * UNICHAR ; Input arguments translated to UTF-16.
OutputFileNameUTF16:: D 264 * UNICHAR
ScanDirUTF16::        D 264 * UNICHAR
IncludeDirsUTF16::    D  2K * UNICHAR
ExcludeDirsUTF16::    D  2K * UNICHAR
Total                 D QWORD   ; Total size of all files and subdirectories.
NamesInMemory         D QWORD   ; Pointer to the contents of File_names mapped in memory.
NamesSize             D QWORD   ; Size of the contents of File_names mapped in memory.
IndexInMemory         D QWORD   ; Pointer to the contents of File_index mapped in memory.
IndexEndInMemory      D QWORD   ; Size of the contents of File_index mapped in memory.
IndexNumber           D 24*B    ; Number of subdirectories.
IncludedDirsBuffer    D QWORD   ; BUFFER for the parsed directories of ArgIncludedDirs.
IncludedDirsPtr       D QWORD   ; Pointer to IncludedDirsBuffer contents.
IncludedDirsEnd       D QWORD   ; Pointer to the end of Includ edDirsBuffer contents.
ExcludedDirsBuffer    D QWORD   ; BUFFER for the parsed directories of ArgExcludedDirs.
ExcludedDirsPtr       D QWORD   ; Pointer to ExcludedDirsBuffer contents.
ExcludedDirsEnd       D QWORD   ; Pointer to the end of ExcludedDirsBuffer contents.
ScanDirPtr            D QWORD   ; Pointer to ScanDirBuffer contents.
ScanDirEnd            D QWORD   ; Pointer to the end of ScanDirBuffer contents.
ScanDirNr             D QWORD   ; Number of scans in ScanDir.
OlderPtr              D QWORD   ; Pointer to the old scan mapped in memory, variable.
OlderInMemory         D QWORD   ; Pointer to the beginning of old scan mapped in memory.
OlderEndInMemory      D QWORD   ; Pointer to the end of old scan mapped in memory.
NewerPtr              D QWORD   ; Pointer to the new scan mapped in memory, variable.
NewerInMemory         D QWORD   ; Pointer to the beginning of new scan mapped in memory.
NewerEndInMemory      D QWORD   ; Pointer to the end of new scan mapped in memory.
ScanDirBuffer         D QWORD   ; BUFFER for the dates of old scans
Buffers::             ; Array of buffers with SCAN_INDEXes.
Buffer1               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of inserted dirs.
Buffer2               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of increased dirs.
Buffer3               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of unchanged dirs.
Buffer4               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of decreased dirs.
Buffer5               D QWORD   ; Pointer to BUFFER with SCAN_INDEXes of deleted dirs.
BufferSelected::      D QWORD   ; One of Buffer1..Buffer5 selected by a button or /ViewDirs.
MaxLineNr::           D DWORD   ; Number of rows in the selected buffer.
ArgNr                 D DWORD   ; Binary number of the current cmd-line argument.
IndexPtr              D DWORD   ; On the fly pointer in File_index.
FatDate               D WORD    ; Used in SetMaxDaysAgo.
FatTime               D WORD    ; Used in SetMaxDaysAgo.
Number1W::            D 40*BYTE ; Decimal number (UTF-16) of inserted dirs in Unichars.
Number2W::            D 40*BYTE ; Decimal number (UTF-16) of increased dirs in Unichars.
Number3W::            D 40*BYTE ; Decimal number (UTF-16) of unchanged dirs in Unichars.
Number4W::            D 40*BYTE ; Decimal number (UTF-16) of decreased dirs in Unichars.
Number5W::            D 40*BYTE ; Decimal number (UTF-16) of deleted dirs in Unichars.
Number1A:             D 20*BYTE ; Decimal number (ANSI) of inserted dirs in bytes.
Number2A:             D 20*BYTE ; Decimal number (ANSI) of increased dirs in bytes.
Number3A:             D 20*BYTE ; Decimal number (ANSI) of unchanged dirs in bytes.
Number4A:             D 20*BYTE ; Decimal number (ANSI) of decreased dirs in bytes.
Number5A:             D 20*BYTE ; Decimal number (ANSI) of deleted dirs in bytes.
NumberA               D 20*BYTE ; A working space for numbers. Zero terminated, e. g. "1234567",0.
Number_A              D 26*BYTE ; Number same as NumberA but with separators '_', e. g. "1_234_567",0.
[.text]
MainCon
The main procedure executed both in console and in graphic mode.
It reads arguments and then it decides by /ScanNow=. If true, it scans all directories, writes a scan file to /ScanDir and terminates.
When /ScanNow=false (default), EuroDirs opens a graphic window and the user can browse directories, change the /ViewDirs mode or change older and newer scan files.
Calls
ReadFile_ini, ArgParse, Scan, LoadAndCompareScans.
MainCon:: PROC
    StdOutput EuroDirs, Version, Eol=yes, Unicode=off
    ; Try to load arguments from the configuration file "%AppData%\eurotool\eurodirs.ini".
    WinABI GetEnvironmentVariableW,=U'AppData',Dir,SIZE# Dir,Unicode=yes
    TEST RAX
    JZ .10:
    FileAssign File_ini,Dir,=U'\eurotool\eurodirs.ini',Unicode=yes
    FileExists? File_ini
    JNC .13:
    FileMkDir File_ini
    JC .10:
    LEA RDI,[Help]                     ; Online help and default configuration.
    GetLength$ RDI, Unicode=no
    FileStore File_ini, RDI,RCX
    JNC .13:
.10:StdOutput =U'Configuration "',File_ini.Name,=U'" could not be saved.',Eol=yes, Console=yes, Unicode=yes
    JMP .20:
.13:CALL ReadFile_ini
.20:; And finally read arguments from the command-line.
    INC [ArgNr]                        ; The next argument.
    GetArg [ArgNr],Unicode=yes ; RSI,RCX is the line with one argument in UTF-16, e. g. /ScanDir="C:\Eurodirs"
    JC .30:                            ; When they are no more arguments.
    SHR RCX,1
    LEA RDI,[DirUTF8]
    WinABI WideCharToMultiByte,CP_UTF8,0,RSI,RCX,RDI,SIZE#DirUTF8/2,0,0
    MOV RSI,RDI
    GetLengthUTF8 RDI
    CALL ArgParse::  ; Use ArgParse to translate UTF-8 string RSI,RCX to a public symbol Arg***.
    JNC .20:
    LEA RSI,[Help+3]
    StdOutput ErrorMessage::,RSI,Unicode=no  ; On error write help and exit.
    TerminateProgram 8
.30:; Create buffers for SCAN_INDEX objects.
    BufferCreate Size=16K
    JC ErrorBuffer
    MOV [ScanDirBuffer],RAX
    BufferCreate Size=4M
    JC ErrorBuffer
    MOV [Buffer1],RAX
    BufferCreate Size=4M
    JC ErrorBuffer
    MOV [Buffer2],RAX
    BufferCreate Size=8M
    JC ErrorBuffer
    MOV [Buffer3],RAX
    BufferCreate Size=4M
    JC ErrorBuffer
    MOV [Buffer4],RAX
    BufferCreate Size=4M
    JC ErrorBuffer
    MOV [Buffer5],RAX
    CALL Arg2Gui
    JSt [Status::],ArgScanNow,.80:     ; Should the computer be scanned now?
    CALL LoadAndCompareScans:          ; Compare older and newer scans, fill the five buffers.
    JMP MainGui::
.80:CALL Scan:                         ; Compute and save the Scan and then terminate.
.Terminate::
    FileClose File_scanOlder, File_scanNewer
    BufferDestroy [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
    TerminateProgram 0
  ENDP MainCon::
Scan

Procedure Scan is called when an argument /ScanNow=yes was detected. EuroDirs is usually called with this argument from TaskScheduler daily or weekly and it creates a scan file "eurodirs_YYYY.MM.DD_HH.MM.scan" in the directory specified by /ScanDir=

Called by
MainCon.
Scan: PROC                            ; Scan now.
    CALL SetToday
    CALL SetMaxDaysAgo
    FileAssign File_scan,ScanDirUTF16::,DateMaskW,Ext_scan,Unicode=yes
    FileEach File_scan,EraseOlderFile ; Erase files older than ArgMaxDays.
    ; Open temporary files with extensions .names, .index, .scan.
    LEA RBX,[File_names]
    FileAssign RBX,ScanDirUTF16,TodayW,Ext_names,Unicode=yes
    FileCreate RBX
    JC ErrorFile:
    LEA RBX,[File_index]
    FileAssign RBX,ScanDirUTF16,TodayW,Ext_index,Unicode=yes
    FileCreate RBX
    JC ErrorFile:
    LEA RBX,[File_scan]
    FileAssign RBX,ScanDirUTF16,TodayW,Ext_scan,Unicode=yes
    FileCreate RBX
    JC ErrorFile:
    ; Prepare excluded directories.
    BufferCreate Size=4K
    MOV [ExcludedDirsBuffer],RAX
    LEA RSI,[ExcludeDirsUTF16]
    CALL ParseToBuffer
    BufferRetrieve [ExcludedDirsBuffer]
    LEA RDX,[RSI+RCX]
    MOV [ExcludedDirsPtr],RSI
    MOV [ExcludedDirsEnd],RDX
    ; Scan included dirs (if not empty) or all directories otherwise. Omit excluded dirs.
    LEA RSI,[IncludeDirsUTF16]
    CMPW [RSI],0
    JZ .20:                  ; Included directories are not specified; jump to scan all.
    BufferCreate Size=4K
    MOV [IncludedDirsBuffer],RAX
    CALL ParseToBuffer
    BufferRetrieve [IncludedDirsBuffer]
    LEA RDX,[RSI+RCX]
    MOV [IncludedDirsPtr],RSI
    MOV [IncludedDirsEnd],RDX
.10:MOV RSI,[IncludedDirsPtr]; Scan the next directory from IncludedDirs.
    CMP RSI,[IncludedDirsEnd]
    JNB .40:
    LODSQ                    ; 1st QWORD is a size, 2nd QWORD is a pointer to a directory name.
    MOV ECX,EAX
    LODSQ
    MOV [IncludedDirsPtr],RSI
    MOV RSI,RAX
    LEA RDI,[Dir]
    JRCXZ .10:
    REP MOVSW                ; Included directory will be put to Dir.
    XOR EAX,EAX
    STOSD
    CALL ExcludeDir          ; Skip if Dir is one of the excluded directories.
    JZ .10:
    LEA RDI,[Dir]
    StdOutput =U"Scanning directory """,RDI,=U""", please wait...",Eol=yes,Console=yes,Unicode=yes
    CALL DirVolume           ; The main recursive procedure will scan Dir and all its subdirectories.
    JZ .10:                  ; ZF=1 when the directory is excluded.
    ADD [Total],RAX          ; Otherwise RAX is the total volume of Dir.
    CALL StoreIndex          ; Write Dir to File_names and File_index.
    JMP .10:
.20:; IncludedDirs are empty, scan directories from all disks.
    MOV RAX,[Root]           ; DU "C:\",0
    LEA RDI,[Dir]
    MOV [RDI],RAX
.30:MOV AL,[Disk]            ; DB "B"
    INC AL
    CMP AL,'Z'
    JA .40:
    LEA RDI,[Dir]
    MOV [Disk],AL             ; Rewrite "C:\" to "D:\" etc.
    MOV [RDI],AL
    WinABI GetDriveTypeW,RDI
    CMP AL,DRIVE_FIXED
    JNE .30:
    CALL ExcludeDir
    JZ .30:
    LEA RDI,[Dir]
    StdOutput =U"Scanning directory """,RDI,=U"""",Eol=yes,Console=yes,Unicode=yes
    CALL DirVolume           ; The main recursive procedure will scan Dir and all its subdirectories.
    JZ .30:                  ; ZF if Dir does not exist or is excluded.
    ADD [Total],RAX          ; Volume of the Dir.
    CALL StoreIndex
    JMP .30:                 ; Go to the next disk.
.40:MOV RBX,[IncludedDirsBuffer]
    BufferDestroy RBX
    MOV RBX,[ExcludedDirsBuffer]
    BufferDestroy RBX
    FileClose File_index,File_names  ; DirVolume was now scanned and the result put into File_names and File_index.
    LEA RBX,[File_names]
    FileMapOpen RBX
    JC ErrorMap:
    MOV [NamesInMemory],RSI
    MOV [NamesSize],RAX
    LEA RBX,[File_index]
    FileMapCreate RBX
    JC ErrorMap:
    MOV [IndexInMemory],RDI
    ADD RDI,RAX
    MOV [IndexEndInMemory],RDI
    SHR RAX,4                ; SIZE# SCAN_INDEX = 16.
    MOV [IndexNumber],RAX    ; Number of scanned directories.
    LEA RDI,[NumberA]
    MOV RBX,RDI
    StoD RDI
    MOVB [RDI],0
    StdOutput =B'Sorting ',RBX,=B' directories...',Unicode=off,Eol=yes
    LEA RDX,[ByName:]
    MOV RSI,[IndexInMemory]
    MOV RAX,[IndexNumber]
    ShellSort RSI,RAX,SIZE#SCAN_INDEX,RDX
    StdOutput =B'Writing scan "', Unicode=no
    StdOutput File_scan.Name, =U'"', Unicode=yes, Console=yes, Eol=yes
.60:CMP RSI,[IndexEndInMemory]
    JAE .80:
    MOV RAX,[RSI+SCAN_INDEX.Volume]
    LEA RDI,[NumberA]
    MOV RBX,RDI
    StoD RDI
    SUB RDI,RBX
    MOV EDX,[RSI+SCAN_INDEX.Ptr]
    MOV ECX,[RSI+SCAN_INDEX.Size]
    ADD RDX,[NamesInMemory]
    FileWrite File_scan,RDX,RCX,=B(9),1,RBX,RDI,=B(10),1 ; DirNameInUTF8, HT, DirVolume, LF.
    LEA RBX,[File_scan]
    JC ErrorWrite
    ADD RSI,SIZE# SCAN_INDEX
    JMP .60:
.80:FileClose File_names, File_index, File_scan
    JSt [Status::],ArgLeaveTemporary,.90:
    FileDelete File_names, File_index
.90:StdOutput =B"Scan terminated.", Unicode=off, Eol=yes
    RET
  ENDP Scan:
DirVolume
This is a recursive procedure which will scan a single directory.
The directory name in Dir may or may be not terminated with backslash. It is zero terminated. The procedure will reuse Dir and temporarilly append file names to it to find the file size.
Input
Dir contains name of the examined folder, encoded in unichars.
Output
ZF=0, RAX=total data volume of Dir's files and subdirectories.
Error
ZF=1, RAX=0 if the directory does not exist or if it is excluded.
Called by
Scan.
Calls
StoreIndex, ExcludeDir.
Clobbers
RCX,RDX,RSI,RDI,R8,R11
DirVolume: PROC                                 ; Recursively called procedure which scans one directory.
    XOR EAX,EAX
    PUSH RBX,R12,RBP,RAX
     SUB RSP,SIZE# WIN32_FIND_DATAW
     MOV RBP,RSP                             ; RBP: ^WIN32_FIND_DATAW.
     LEA RDI,[Dir]
     MOV ECX,SIZE# Dir
     XOR EAX,EAX
     REPNE SCASW                             ; Search for the terminating zero.
     SUB RDI,2
     MOV AX,'\'
     CMP [RDI-2],AX
     JE .10:
     STOSW                                   ; Make the folder name always backslash terminated.
.10: MOV R12,RDI                             ; R12: behind the last \ in Dir.
     MOV EAX,'*'
     STOSD
     LEA RCX,[Dir]
     WinABI FindFirstFileW,RCX,RBP
     MOV RBX,RAX                             ; RBX=FindHandle.
     CMP RAX,INVALID_HANDLE_VALUE
     JE .90:
     MOVW [R12],0                            ; Erase the asterix.
.20: JSt [RBP+WIN32_FIND_DATAW.FileAttributes],FILE_ATTRIBUTE_DIRECTORY,.40:
     ;  The name is a file.
     MOV EAX,[RBP+WIN32_FIND_DATAW.FileSizeLow]
     MOV EDX,[RBP+WIN32_FIND_DATAW.FileSizeHigh]
     SAL RDX,32
     ADD RAX,RDX
     ADD [RBP+SIZE# WIN32_FIND_DATAW],RAX
.30: WinABI FindNextFileW,RBX,RBP
     TEST RAX,RAX
     JNZ .20:
     JMP .90:
.40: ;  The name is a directory.
     LEA RSI,[RBP+WIN32_FIND_DATAW.FileName] ; ReturnRAX (Total) on machine stack.
     MOV EAX,'.'
     CMP [RSI],EAX
     JE .30:                                 ; Ignore the directory "."
     SHL EAX,16
     MOV AL,'.'
     CMP [RSI],EAX
     JNE .50:
     CMPW [RSI+4],0
     JE .30:                                 ; Ignore the directory ".."
.50: GetLength$ RSI, Unicode=yes
     MOV RDI,R12
     REP MOVSB
     SUB RAX,RAX
     STOSW                                   ; Let Dir be the new directory.
     CALL ExcludeDir:
     JNZ .60:
     MOVW [R12],0
     JMP .30:
.60: CALL DirVolume                          ; Recursive call itself.
     ADD [RBP+SIZE# WIN32_FIND_DATAW],RAX    ; Returned RAX (Total) on machine stack.
     CALL StoreIndex:
     JMP .30:
.90: MOVW [R12],0
     WinABI FindClose,RBX,Fastmode=enabled
     ADD RSP,SIZE# WIN32_FIND_DATAW
     CMP RBX,INVALID_HANDLE_VALUE
    POP RAX,RBP,R12,RBX
    RET
  ENDP DirVolume:
StoreIndex
StoreIndex writes the directory name from Dir to the File_names and its index to File_index.
The name in Dir is encoded in UTF-16.
The name written to File_names will be encoded in UTF-8. It is not zero-terminated.
Input
RAX=the volume of files and subdirectories in bytes.
Output
File_names and File_index are incremented.
Clobbers
RAX,RDX,RSI,RDI,R8,R11
Called by
Scan and DirVolume.
StoreIndex: PROC
    MOV [ScanIndex.Volume],RAX
    LEA RSI,[Dir]
    LEA RDI,[DirUTF8]
    XOR EAX,EAX
.20:LODSW
    CMP AX,0
    JE .80:
    CMP AX,0xD800
    JB .50:
    CMP AX,0xE000
    JAE .50:
    SUB AX,0xD800                      ; High surrogate character.
    SHL EAX,10
    MOV EDX,EAX
    LODSW
    CMP AX,0
    JE .80:
    SUB AX,0xDC00
    JNA .40:
    CMP AX,0x03FF
    JA .40:
    ADD EAX,EDX
    EncodeUTF8                         ; Use macro EncodeUTF8.
    XOR EAX,EAX
    JMP .20:
.40:MOV EAX,'?'
.50:EncodeUTF8
    JMP .20:
.80:LEA RDX,[DirUTF8]
    MOV R8,RDI
    SUB R8,RDX
    MOV [ScanIndex.Size],R8D
    MOV EAX,[IndexPtr]
    MOV [ScanIndex.Ptr],EAX
    ADD [IndexPtr],R8D
    MOV R11,RBX
    LEA RBX,[File_names]
    FileWrite RBX,RDX,R8               ; Write the directory name (UTF8) to the File_names.
    JC ErrorWrite:
    LEA RBX,[File_index]
    FileWrite RBX,ScanIndex,SIZE#SCAN_INDEX ; Write Index to the nxdFile.
    JC ErrorWrite:
    MOV RBX,R11
    RET
  ENDP StoreIndex:
LoadAndCompareScans

Global procedure LoadAndCompareScans checks arguments.
If /ScanDir is empty it fills it with ..
If /InputFile is empty (default), it assigns it with the oldest scan from /ScanDir.
If /OutputFile is empty (default), it assigns it with the newest scan from /ScanDir.
If the name of oldest scan is greater then the name of newest scan, it will swap them.
If less than two scans are in /ScanDir, LoadAndCompareScans ends with CF=1.

By default it selects the oldest and the newest scan in ScanDir.

The procedure fills five buffers of ScanFiles indexes with differences between those two scans.

Input
-
Output
CF=0, Buffer1,Buffer2,Buffer3,Buffer4,Buffer5 are loaded with the differences and sorted.
Error
CF=1 less than two scans were found.
Called by
DirVolume.
LoadAndCompareScans:: PROC
    CALL Arg2Gui
    ; Clear buffers from previous run of LoadAndCompareScans procedure.
    BufferClear [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
    ; Load dates of all scans which are in ScanDirUTF16 into a ScanDirBuffer.
    FileAssign File_scan,ScanDirUTF16,DateMaskW,Ext_scan,Unicode=yes
    FileEach File_scan,StoreFileName ; Store the date from file names to ScanDirBuffer.
    FileClose File_scan
    BufferRetrieve [ScanDirBuffer]
    CMP ECX,2*16*2                   ; At least two scans are required.
    JAE .20:
    StdOutput =B"At least two scan files are required in /ScanDir=""",Unicode=no
    StdOutput ScanDirUTF16,=U"""",Unicode=yes,Console=yes,Eol=yes
    STC
    JMP .90:
.20:MOV RAX,RCX
    ADD RCX,RSI
    SHR RAX,4+1                      ; Each item in ScanDirBuffer has 16*2 bytes.
    MOV [ScanDirPtr],RSI
    MOV [ScanDirEnd],RCX
    MOV [ScanDirNr],RAX
    ShellSort RSI, RAX, 2*16, ByDate
    FileClose File_scanOlder,File_scanNewer ; Close files, if opened by previous comparison.
    ; If InputFileNameUTF16 is specified, it will be used as File_scanOlder.
    LEA RBX,[File_scanOlder]
    LEA RDI,[InputFileNameUTF16]
    MOV RDX,RDI
    CMPW [RDI],0
    JNE .25:
    MOV RSI,[ScanDirPtr]
    MOV ECX,16*2
    REP MOVSB
    SUB EAX,EAX
    STOSW
    FileAssign RBX,ScanDirUTF16::,RDX,Ext_scan,Unicode=yes ; The first (oldest) scan.
    JMP .30:
.25:FileAssign RBX,RDX,Unicode=yes
.30:FileMapOpen RBX
    JC ErrorMap:
    TEST RAX
    JZ ErrorZero:
    MOV [OlderPtr],RSI
    MOV [OlderInMemory],RSI
    ADD RAX,RSI
    MOV [OlderEndInMemory],RAX
    ; If OutputFileNameUTF16 is specified, it will be used as File_scanNewer.
    LEA RBX,[File_scanNewer]
    LEA RDI,[OutputFileNameUTF16]
    MOV RDX,RDI
    CMPW [RDI],0
    JNE .35:
    MOV RSI,[ScanDirEnd]
    SUB RSI,16*2
    MOV ECX,16*2
    REP MOVSB
    SUB EAX,EAX
    STOSW
    FileAssign RBX,ScanDirUTF16::,RDX,Ext_scan,Unicode=yes ; The last (newest) scan.
    JMP .40:
.35:FileAssign RBX,RDX,Unicode=yes
.40:FileMapOpen RBX
    JC ErrorMap:
    TEST RAX
    JZ ErrorZero:
    MOV [NewerPtr],RSI
    MOV [NewerInMemory],RSI
    ADD RAX,RSI
    MOV [NewerEndInMemory],RAX
    ; Check if newer > older.
    RstSt [Status::],ArgSwappedDates
    LEA RSI,[File_scanNewer.Name]
    LEA RDI,[File_scanOlder.Name]
    GetLength$ RDI,Unicode=yes
    MOV EDX,ECX
    GetLength$ RSI,Unicode=yes
    CMP ECX,EDX
    JNE .45:
    SHR ECX,1
    REPE CMPSW
    JGE .45:
    SetSt [Status::],ArgSwappedDates
.45:; Comparing two scan files starts.
    StdOutput Comparing,Unicode=no
    StdOutput =U"Old file: ",File_scanOlder.Name,Eol=yes,Console=yes, Unicode=yes
    StdOutput =U"New file: ",File_scanNewer.Name,Eol=yes,Console=yes, Unicode=yes
    JNSt [Status::],ArgSwappedDates,.50:
    StdOutput =B"Warning: old scan is younger than the new scan.",Eol=yes,Unicode=no
.50:; Start to compare File_scanOlder and File_scanNewer.
.LoadBoth:CALL LoadNewer: ; Output: R8=Volume,RBP=RSI=Pointer to a dir-name,RCX=R12=Size or -1 when at EOF.
.LoadOld: CALL LoadOlder: ; Output: R9=Volume,RBX=RDI=Pointer to a dir-name,RDX=Size or -1 when at EOF.
.Compare: CMP RCX,-1
    JNE .55:
    ; File_scanNewer is over or higher. Keep loading File_scanOlder, store it to Buffer5.
    CMP RDX,-1
    JE .EndCompare:         ; Both file are at end.
    JMP .NewIsHigher:
.55:CMP RDX,-1
    JE .OldIsHigher: ; File_scanOlder is over or higher. Keep loading File_scanNewer, store it to Buffer1.
    CMP ECX,EDX
    JBE .60:
    MOV ECX,EDX
.60:REPE CMPSB       ; Compare at the matching size.
    MOV RDI,RBX
    JB .OldIsHigher:
    JA .NewIsHigher:
    CMP R12,RDX      ; When names match, look at their sizes.
    JB .OldIsHigher:
    JA .NewIsHigher:
; NoneIsHigher; Directory names are equal.
; ScanIndex goes to Buffer2,3 or 4.  SCAN_INDEX.Ptr is relative to [NewerInMemory].
    SUB RBP,[NewerInMemory]
    MOV [ScanIndex.Ptr],EBP
    MOV [ScanIndex.Size],EDX
    SUB R8,R9        ; Volume difference is in R8.
    MOV [ScanIndex.Volume],R8
    JNE .75:
    MOV RBX,[Buffer3]
    MOV [ScanIndex.Volume],R9
    JMP .80:
.75:MOV RBX,[Buffer4]
    JB .80:
    MOV RBX,[Buffer2]
.80:BufferStore RBX,ScanIndex,SIZE# ScanIndex
    JC ErrorBuffer
    JMP .LoadBoth:
.NewIsHigher:  ; Line retrieved from File_scanNewer is higher or it already ended.
; Keep the line (in R8,RBP,R12) and store the index of line retrieved from File_scanOlder (R9,RBX=RDI,RDX).
; ScanIndex goes to Buffer5, SCAN_INDEX.Ptr is relative to [OlderInMemory].
    SUB RBX,[OlderInMemory]
    MOV [ScanIndex.Ptr],EBX
    MOV [ScanIndex.Size],EDX
    NEG R9
    MOV [ScanIndex.Volume],R9
    MOV RBX,[Buffer5]
    BufferStore RBX,ScanIndex,SIZE# ScanIndex
    JC ErrorBuffer
    MOV RSI,RBP
    MOV RCX,R12
    JMP .LoadOld:
.OldIsHigher:; Line retrieved from File_scanOlder is higher or it already ended.
; Keep the line (in R9,RBX=RDI,RDX) and store the index of line retrieved from File_scanNewer (R8,RBP=RSI,R12=RCX).
; ScanIndex goes to Buffer1, SCAN_INDEX.Ptr is relative to [NewerInMemory],
    SUB RBP,[NewerInMemory]
    MOV [ScanIndex.Ptr],EBP
    MOV [ScanIndex.Size],R12D
    MOV [ScanIndex.Volume],R8
    MOV R10,RBX
    MOV RBX,[Buffer1]
    BufferStore RBX,ScanIndex,SIZE# ScanIndex
    JC ErrorBuffer
    MOV RBX,R10
    CALL LoadNewer:
    JMP .Compare:
.EndCompare:
Nr  %FOR 1..5                           ; Set the lengths of five buffers to NumberxA and NumberxW.
       BufferRetrieve [Buffer%Nr]
          MOV EAX,ECX
          SHR EAX,4
          LEA RDI,[Number%Nr{}A]
          MOV RSI,RDI
          StoD RDI
          XOR EAX,EAX
          STOSB
          LEA RDI,[Number%Nr{}W]
   .8%Nr: LODSB
          STOSW
          CMP AL,0
          JNE .8%Nr:
    %ENDFOR Nr
    SetSt [Status::],ArgDescending
    MOV R8,[NewerInMemory]
    BufferRetrieve [Buffer1]
    SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
    ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
    BufferRetrieve [Buffer2]
    SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
    ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
    BufferRetrieve [Buffer3]
    SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
    ShellSort RSI,RCX,16,ByVolume    ; ArgDescending, R8=NewerInMemory
    RstSt [Status::],ArgDescending
    BufferRetrieve [Buffer4]
    SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
    ShellSort RSI,RCX,16,ByVolume    ; ArgAscending, R8=NewerInMemory
    MOV R8,[OlderInMemory]
    BufferRetrieve [Buffer5]
    SHR ECX,4                        ; Let RCX=number of SCAN_INDEX records (16 bytes each).
    ShellSort RSI,RCX,16,ByVolume    ; ArgAscending, R8=OlderInMemory
    StdOutput EuroDirs, =B" has found", Eol=yes, Unicode=no
Nr  %FOR 1..5
      StdOutput Number%Nr{}A, Report%Nr, Eol=yes, Unicode=no
    %ENDFOR Nr
    CLC
.90:RET
   ENDP LoadAndCompareScans
Arg2Gui
Copy arguments Arg** from UTF-8 to UTF-16 used by WinGUI module.
Arg2Gui PROC   ; Convert string arguments Arg*** from UTF-8 to ***UTF16.
    LEA RSI,[ArgScanDir::]
    LEA RDI,[ScanDirUTF16]
    WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#ScanDirUTF16/2
    LEA RSI,[ArgInputFile::]
    LEA RDI,[InputFileNameUTF16]
    WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#InputFileNameUTF16/2
    LEA RSI,[ArgOutputFile::]
    LEA RDI,[OutputFileNameUTF16]
    WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#OutputFileNameUTF16/2
    LEA RSI,[ArgIncludeDirs::]
    LEA RDI,[IncludeDirsUTF16]
    WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#IncludeDirsUTF16/2
    LEA RSI,[ArgExcludeDirs::]
    LEA RDI,[ExcludeDirsUTF16]
    WinABI MultiByteToWideChar,CP_UTF8,0,RSI,-1,RDI,SIZE#ExcludeDirsUTF16/2
    ; Check /ScanDir=
    LEA RDI,[ScanDirUTF16]
    CMPW [RDI],0
    JNE .10:
    MOV EAX,'.'                      ; /ScanDir= was not specified, use ".".
    MOV [RDI],EAX
.10:XOR ECX,ECX
    XOR EAX,EAX
    DEC RCX
    REPNE SCASW
    MOV AX,'\'
    SUB RDI,2
    CMP [RDI-2],AX
    JE .90:
    STOSD                            ; Terminate the scan directory with backslash.
.90:RET
 ENDP Arg2Gui
StoreFileName
Procedure StoreFileName is called as a callback from FileEach macro in LoadAndCompareScans.
It stores the 16*unichar string such as "2025.09.06_09.59" from the scan file name to ScanDirBuffer.
Input
RDI=^WIN32_FIND_DATAW
containing the name such as eurodirs_2025.09.06_09.59.scan.
ScanDirBuffer must be created.
Output
CF=0, 16 UNICHARs are stored to ScanDirBuffer.
Error
ErrorBuffer
Called by
LoadAndCompareScans.
StoreFileName PROC
   LEA RSI,[RDI+WIN32_FIND_DATAW.FileName] ; Let RSI point to unichar "2024.09.06_09.59".
   BufferStore [ScanDirBuffer],RSI,16*2
   JC ErrorBuffer
   RET
  ENDP StoreFileName
OneLine2Dir

Construct a line from the [BufferSelected] into Dir. The line looks like dir_size directory\name\in\UTF16, it is encoded in UTF-16 and zero terminated.
When the input line number RBX is 0, an empty line is returned (CF=1. RCX=0).
When the input line number RBX is 1, first line is returned (CF=0. RCX>0).
When the input line number RBX is 2, second line is returned etc.
When the input line number RBX is bigger than the [MaxLineNr], an empty line is returned (CF=1. RCX=0).

Input
RBX= line number (0,1,2,,,).
[ArgViewDirs::]= selected view (1..5 alias ArgViewDirsIns..ArgViewDirsDel).
Output
RSI=Dir is filled with the line containing the differential size and name in WIDE encoding.
RCX= is size of the output line in bytes.
Calls
StoreWideChar, AddUnderscores
Called by
LoadAndCompareScans
Clobbers
RAX,RDX
OneLine2Dir:: PROC
    PUSH RBX,RDI
     MOV EAX,[ArgViewDirs::]
     MOV RDX,[Buffers+8*RAX-8]
     BufferRetrieve RDX    ; Load the buffer contents to RSI,RCX.
     ADD RCX,RSI           ; The end of buffer contents.
     SAL EBX,4             ; Each SCAN_INDEX in the buffer contents has size=16.
     JZ .10:
     LEA RBX,[RBX+RSI-SIZE# SCAN_INDEX] ; Let RBX point at the selected SCAN_INDEX.
     CMP RBX,RCX           ; Is it out of contents?
     JB .20:
.10: XOR ECX,ECX
     LEA RSI,[Dir]
     STC
     JMP .90:              ; Abort when the requested line number exceeded the buffer and return CF=1 and empty line.
.20: MOV RDX,[NewerInMemory]
     CMP AL,ArgViewDirsDel
     JNE .30:
     MOV RDX,[OlderInMemory] ; Select the mapping of File_scanOlder or File_scanNewer.
.30: MOV RAX,[RBX+SCAN_INDEX.Volume]
     LEA RSI,[NumberA]
     StoD RSI,Size=20      ; Store the directory volume as a decimal ANSI
     CALL AddUnderscores   ;  and complete it with separators.
     LEA RSI,[Number_A]
     CMPB [ArgViewDirs::],ArgViewDirsDel
     JNE .50:
     CMPB [RSI],'0'
     JE .50:
     INC RSI               ; Skip the minus sign when viewing deleted direcotires.
.50: LEA RDI,[Dir]
     CMPB [ArgViewDirs::],ArgViewDirsInc
     JNE .60:
     MOV AL,'+'            ; Add a plus sign when viewing incremented directories.
     JMP .70:
.60: LODSB                 ; Convert the number from ANSI to WIDE.
     CMP AL,0
     JE .80:
.70: STOSW
     JMP .60:
.80: MOV ECX,[RBX+SCAN_INDEX.Size]
     MOV EAX,[RBX+SCAN_INDEX.Ptr]
     ADD RDX,RAX           ; Let RDX point to the directory name.
     DecodeUTF8 RDX,StoreWideChar,Size=RCX,Width=16
     MOV RCX,RDI           ; At the end of directory name.
     MOVW [RDI],0          ; Zero terminate.
     LEA RSI,[Dir]
     SUB RCX,RSI           ; CF=0, RSI,RCX is the target line.
.90:POP RDI,RBX
    RET
  ENDP OneLine2Dir
AddUnderscores

Complete an ASCII decadic number with underscores _ per each three digits.

Input
RSI= is pointer to the beginning of the number.
RDI= is pointer at the end of the number.
Output
Number_A is filled with the number and separators, space and zero terminated.
Called by
LoadAndCompareScans
Clobbers
RAX,RCX,RDI
AddUnderscores PROC
   PUSH RDX
    SUB RDI,RSI
    MOV ECX,3
    MOV EAX,EDI
    XOR EDX,EDX
    LEA RDI,[Number_A]
    DIV RCX
    CMP EDX,0
    JNE .20:
    REP MOVSB
    DEC EAX
    JMP .40:
.20:CMP EDX,1
    JNE .30:
    MOVSB
    CMPB [RSI-1],'-'
    JNE .40:
    XCHG EAX,EDX
    DEC EDX
    JS .80:
    JMP .60:
.30:MOVSW
.40:XCHG EAX,EDX
.50:DEC RDX
    JS .80:
    MOV AL,'_'
    STOSB
.60:MOV CL,3
    REP MOVSB
    JMP .50:
.80:MOV AX,' '
    STOSW
   POP RDX
   RET
 ENDP AddUnderscores
StoreWideChar
Callback procedure StoreWideChar from DecodeUTF8.
Called by
ReadFile_ini, OneLine2Dir.
Input
AX=a character to write.
RDI=position to write.
Output
RDI=incremented by 2.
StoreWideChar:: PROC
  STOSW
  RET
 ENDP StoreWideChar:
LoadOlder
Load one directory name and volume from File_scanOlder mapped at OlderInMemory..OlderEndInMemory.
Each record has variable size: /dir_name 0x09 dir_volume_decimal 0x0A
Input
OlderPtr is pointer to one record,
OlderEndInMemory points to the end of records.
Output
R9=volume,
RBX=RDI=pointer to a directory name,
RDX=directory name size or -1 when at EOF.
OlderPtr is updated.
Preserves
R8,RCX,RSI,RBP
Clobbers
RAX,R10,R11
LoadOlder PROC
    MOV R10,RCX                     ; Save preserved RCX.
    MOV RDI,[OlderPtr]
    MOV RCX,[OlderEndInMemory]
    MOV RBX,RDI
    MOV RDX,RDI
    SUB RCX,RDI
    JNA .90:
    MOV AL,9
    REPNE SCASB
    MOV R11,RSI
    MOV RSI,RDI
    DEC RDI                         ; Go at HT.
    SUB RDI,RDX
    XCHG RDI,RDX
    LodD RSI
    MOV R9,RAX
    INC RSI                         ; Skip LF.
    MOV [OlderPtr],RSI
    MOV RSI,R11
    MOV RCX,R10                     ; Restore preserved RCX.
    RET                             ; Normal end.
.90:MOV RDX,-1
    MOV RCX,R10                     ; Restore preserved RCX.
    RET                             ; End at EOF.
  ENDP LoadOlder
LoadNewer
Load one directory name and volume from File_scanNewer mapped at NewerInMemory..NewerEndInMemory.
Each record has variable size: /dir_name 0x09 dir_volume_decimal 0x0A
Input
NewerPtr is pointer to one record,
NewerEndInMemory points to the end of records.
Output
R8=volume,
RBP=RSI=pointer to a directory name,
RCX=R12=directory name size or -1 when at EOF.
NewerPtr is updated.
Preserves
R9,RBX,RDX,RDI
Clobbers
RAX,R10
LoadNewer PROC
    MOV R10,RDI                     ; Save preserved RDI.
    MOV RDI,[NewerPtr]
    MOV RCX,[NewerEndInMemory]
    MOV RBP,RDI
    SUB RCX,RDI
    JNA .90:
    MOV AL,9
    REPNE SCASB
    MOV RSI,RDI
    DEC RDI
    SUB RDI,RBP
    MOV ECX,EDI
    LodD RSI
    MOV R8,RAX
    INC RSI                         ; Skip LF.
    MOV [NewerPtr],RSI
    MOV RDI,R10                     ; Restore preserved RDI.
    MOV RSI,RBP                     ; Normal end.
    MOV R12,RCX
    RET                             ; Normal end.
.90:MOV RDI,R10                     ; Restore preserved RDI.
    MOV RCX,-1
    MOV R12,RCX
    RET                             ; End at EOF.
  ENDP LoadNewer
ExcludeDir
Procedure ExcludeDir returns ZF=1 when the directory name in Dir is it the ArgExcludeDirs.
Called by
Scan.
Input
ExcludeDirsPtr, ExcludeDirsEnd are pointers to an array of 2*QWORDs.
Clobbers
RAX,RCX,RDX,RSI,RDI
ExcludeDir PROC
    MOV RSI,[ExcludedDirsPtr]
.10:CMP RSI,[ExcludedDirsEnd]
    JAE .90:
    LODSQ
    MOV RCX,RAX
    LODSQ
    JRCXZ .10:
    XCHG RSI,RAX
    LEA RDI,[Dir]
    REPE CMPSW
    XCHG RAX,RSI
    JNE .10:
    RET        ; ZF=1.
.90:CMP ESI,1
    RET        ; ZF=0.
   ENDP ExcludeDir
ParseToBuffer
Procedure ParseToBuffer will parse a multiple semicolon-separated pathes from RSI and for each path it stores two QWORDs to the buffer: length of dir in unichars and pointer.
Called by
Scan.
Input
RAX= output empty BUFFER.
RSI= pointer to IncludeDirsUTF16 or ExcludeDirsUTF16.
ParseToBuffer PROC
    MOV RBX,RAX
.10:MOV RDX,RSI
.20:LODSW
    CMP AX,0
    JE .50:
    CMP AX,';'            ; ; is the folder names separator.
    JNE .20:
    CALL .50:             ; Store to buffer and continue.
    JMP .10:
.50: ; Store size RSI-2-RDX and pointer RDX to the buffer RBX.
    LEA RAX,[RSI-2]
    SUB RAX,RDX
    SHR RAX,1             ; Convert bytes to unichars.
    JZ .90:
    PUSH RAX              ; Save folder name size on stack.
      MOV RAX,RSP
      BufferStore RBX,RAX,8 ; Dir name size in unichars.
    POP RAX
    JC ErrorBuffer
    PUSH RDX              ; Save pointer to folder name on stack.
      MOV RAX,RSP
      BufferStore RBX,RAX,8 ; Dir ptr.
    POP RAX
    JC ErrorBuffer
.90:RET
   ENDP ParseToBuffer
ReadFile_ini
Procedure ReadFile_ini reads lines from File_ini in UTF-8 and parses its each line into configuration variables Arg*** in UTF-8, too.
The File_ini.Name is in UTF-16. Procedure writes information about the file name and whether it was found.
Called by
MainCon.
Input
File_ini is assigned with the name.
Clobbers
RAX,RCX,RDX,RSI,RDI
ReadFile_ini PROC
    SetSt [Status::],ArgFromFile       ; Tell ArgParse that arguments may not begin with / or -.
    StdOutput =B"Configuration """,Unicode=no
    StdOutput File_ini.Name,Console=yes,Unicode=yes
    FileStreamOpen File_ini,BufSize=4K
    JNC .10:
    StdOutput =B""" was not found.",Eol=yes,Unicode=no
    JMP .90:
.10:FileStreamReadLn File_ini
    JBE .80:
    MOV ECX,EAX
    ; The first line may begin with BOM.
    MOV AX,[RSI]
    CMPW AX,0xBBEF   ; UTF-8 BOM?
    JNE .50:
    ADD RSI,3        ; Skip the BOM.
    SUB ECX,3
    JB .80:
    JMP .50:
.30:FileStreamReadLn File_ini
    JBE .80:
    MOV ECX,EAX
.50:CALL ArgParse::
    JNC .30:
    LEA RSI,[Help+3]
    StdOutput RSI,Unicode=no
    TerminateProgram 8
.80:FileClose File_ini
    StdOutput =B""" was accepted.",Eol=yes,Unicode=off
.90:RstSt [Status::],ArgFromFile
    RET
  ENDP ReadFile_ini
ByName
Procedure ByName is a callback function to sort by directory name.
Input
RSI,RDI point to SCAN_INDEX records to compare.
Clobbers
RAX,RDX,RSI,RDI,R09,R10,R12
Called by
Scan.
ByName: PROC
    MOV R8,RCX              ; Save RCX,RSI,RDI.
    MOV R9,RSI
    MOV R10,RDI
    MOV EDX,[RDI+SCAN_INDEX.Size]
    MOV ECX,[RSI+SCAN_INDEX.Size]
    CMP ECX,EDX
    JBE .10:
    MOV ECX,EDX              ; Compare dirnames by the shorter of both sizes.
.10:MOV ESI,[RSI+SCAN_INDEX.Ptr]
    MOV EDI,[RDI+SCAN_INDEX.Ptr]
    ADD RSI,[NamesInMemory]
    ADD RDI,[NamesInMemory]
    DEC ECX
    XCHG RSI,RDI             ; Ascending.
    REPE CMPSB
    JA .90:
    JB .50:
    CMP EDX,[R9+SCAN_INDEX.Size] ; When the dirnames are the same, compare by dirname size.
    JAE .90
.50:MOV RAX,[R9+SCAN_INDEX.Ptr]  ; Records are in wrong order. Swap them.
    MOV RDX,[R9+SCAN_INDEX.Volume]
    XCHG RAX,[R10+SCAN_INDEX.Ptr]
    XCHG RDX,[R10+SCAN_INDEX.Volume]
    MOV [R9+SCAN_INDEX.Ptr],RAX
    MOV [R9+SCAN_INDEX.Volume],RDX
.90:MOV RCX,R8               ; Restore RCX.
    RET
  ENDP ByName:
ByDate
Procedure ByDate is a callback function to sort a table of DateMaskW records.
Input
RSI,RDI point to DateMaskW records to compare.
Clobbers
RAX,RDX,RSI,RDI
Called by
Scan.
ByDate: PROC
    XCHG RSI,RDI
    PUSH RCX,RSI,RDI
     MOV ECX,SIZE# DateMaskW /2
     REPE CMPSW
    POP RDI,RSI,RCX
    JAE .90:
    MOV  RAX,[RSI+00]
    MOV  RDX,[RSI+08]
    XCHG [RDI+00],RAX
    XCHG [RDI+08],RDX
    MOV  [RSI+00],RAX
    MOV  [RSI+08],RDX
    MOV  RAX,[RSI+16]
    MOV  RDX,[RSI+24]
    XCHG [RDI+16],RAX
    XCHG [RDI+24],RDX
    MOV  [RSI+16],RAX
    MOV  [RSI+24],RDX
.90:RET
   ENDP ByDate
ByVolume
Procedure ByVolume is a callback function to sort a table of SCAN_INDEX records by volume and then by directory name.
Input
RSI,RDI point to SCAN_INDEX records to compare.
R8 contains [NewerInMemory] or [OlderInMemory].
ArgDescending specifies the sort direction.
Clobbers
RAX,RDX,RSI,RDI
ByVolume:PROC
    JSt [Status::],ArgDescending,.10:
    XCHG RSI,RDI
.10:MOV RAX,[RSI+SCAN_INDEX.Volume]
    MOV RDX,[RDI+SCAN_INDEX.Volume]
    CMP RDX,RAX
    CLC
    JG .90:
    MOV R9,RCX
    MOV R10,RSI
    MOV R11,RDI
    XCHG RSI,RDI
    JL .60:
    MOV ECX,[RSI+SCAN_INDEX.Size]
    MOV ESI,[RSI+SCAN_INDEX.Ptr]
    MOV EDX,[RDI+SCAN_INDEX.Size]
    MOV EDI,[RDI+SCAN_INDEX.Ptr]
    ADD RSI,R8
    ADD RDI,R8
    CMP ECX,EDX
    JBE .20:
    MOV ECX,EDX
.20:REPE CMPSB
    JA .80:
    JB .50:
    CMP EDX,[R10+SCAN_INDEX.Size]
    JAE .80:
.50:MOV RAX,[R10+SCAN_INDEX.Volume]
    MOV RDX,[R11+SCAN_INDEX.Volume]
.60:MOV [R11+SCAN_INDEX.Volume],RAX
    MOV [R10+SCAN_INDEX.Volume],RDX
    MOV RAX,[R10+SCAN_INDEX.Ptr]
    MOV RDX,[R11+SCAN_INDEX.Ptr]
    MOV [R11+SCAN_INDEX.Ptr],RAX
    MOV [R10+SCAN_INDEX.Ptr],RDX
    STC
.80:MOV RCX,R9
.90:RET
 ENDP ByVolume:
SetToday
Procedure SetToday will set a nominal local time of a scan
Called by
MainCon.
Clobbers
RAX,RBX,RCX,RSI,RDI
SetToday PROC
    LEA RBX,[SystemTime]
    WinABI GetLocalTime,RBX
    LEA RDI,[TodayA]
    MOVZXW EAX,[RBX+SYSTEMTIME.wYear]
    StoD RDI,Size=4,Signed=no,Align=right,LeadingZeroes=yes
    MOV AL,'.'
    STOSB
    MOVZXW EAX,[RBX+SYSTEMTIME.wMonth]
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    MOV AL,'.'
    STOSB
    MOVZXW EAX,[RBX+SYSTEMTIME.wDay]
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    MOV AL,'_'
    STOSB
    MOVZXW EAX,[RBX+SYSTEMTIME.wHour]
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    MOV AL,'.'
    STOSB
    MOVZXW EAX,[RBX+SYSTEMTIME.wMinute]
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    XOR EAX,EAX
    STOSB
    LEA RSI,[TodayA]
    LEA RDI,[TodayW]
    MOV ECX,SIZE# TodayA
.50:LODSB
    STOSW
    LOOP .50:
    RET
  ENDP SetToday
SetMaxDaysAgo
Procedure SetMaxDaysAgo will set a current local time stored by SetToday into TodayA and TodayW minus /MaxDays= into string variables MaxDaysAgoA and MaxDaysAgoW.
Called by
MainCon.
Clobbers
RAX,RBX,RCX,RSI,RDI
SetMaxDaysAgo PROC
    LEA RSI,[TodayA]
    LEA RDI,[MaxDaysAgoA]
    MOV ECX,SIZE# MaxDaysAgoA
    REP MOVSB
    LEA RBX,[FileTime]
    WinABI GetSystemTimeAsFileTime,RBX
    MOV RBX,[RBX]  ; RBX is the number of 100-nanosecond intervals since January 1, 1601.
    MOV RAX,24*60*60*10_000_000
    MOV ECX,[ArgMaxDays::]
    MUL RCX
    SUB RBX,RAX
    LEA RDI,[FileTime]
    MOV [RDI],RBX
    WinABI FileTimeToDosDateTime,RDI,FatDate,FatTime
    MOV AX,[FatDate]
    SHR AX,9
    ADD AX,1980
    LEA RDI,[MaxDaysAgoA]
    StoD RDI,Size=4,Signed=no,Align=right,LeadingZeroes=yes
    MOV AX,[FatDate]
    AND AX,01E0h
    SHR AX,5
    INC RDI
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    MOV AX,[FatDate]
    AND AX,001Fh
    INC RDI
    StoD RDI,Size=2,Signed=no,Align=right,LeadingZeroes=yes
    LEA RSI,[MaxDaysAgoA]
    LEA RDI,[MaxDaysAgoW]
    MOV ECX,SIZE# MaxDaysAgoA
.50:LODSB
    STOSW
    LOOP .50:
    RET
  ENDP SetMaxDaysAgo
EraseOlderFile
Procedure EraseOld is called as a callback from FileEach
Called by
Scan.
Input
RBX=^FILE64 with the name.
RDI=^WIN32_FIND_DATAW
Clobbers
RAX,RCX,RSI,RDI
EraseOlderFile: PROC
    LEA RSI,[RDI+WIN32_FIND_DATAW.FileName+18]
    LEA RDI,[MaxDaysAgoW]
    MOV ECX,16
    REPE CMPSW                ; Compare datetime encoded in the file name.
    JAE .90:
    LEA RAX,[RBX+FILE64.Name]
    StdOutput =U"Old scan """,RAX,=U""" was deleted.",Eol=yes,Console=yes,Unicode=yes
    FileDelete RBX
.90:RET
  ENDP EraseOlderFile:
Error
Simplified error handling will abort the program with errorlevel 8.
Input
RBX=^FILE64 which caused the error.
Called by
Scan
ErrorZero: ; RBX=^FILE64
     StdOutput =B'Error: zero size of the file "',Unicode=no
      LEA RAX,[RBX+FILE64.Name]
      JMP Write
ErrorMap:  ; RBX=^FILE64
      StdOutput =B'Error: memory-mapping the file "',Unicode=no
      LEA RAX,[RBX+FILE64.Name]
      JMP Write
ErrorFile: ; RBX=^FILE64
      StdOutput =B'Error: could not open the file "',Unicode=no
      LEA RAX,[RBX+FILE64.Name]
      JMP Write
ErrorWrite:  ; RBX=^FILE64
      StdOutput =B'Error writing to the file "',Unicode=no
      LEA RAX,[RBX+FILE64.Name]
Write:StdOutput RAX,Eol=no,Console=yes,Unicode=yes
      StdOutput =B'".', Unicode=no, Eol=yes
      JMPS .Terminate:
ErrorBuffer:
      StdOutput =B"Error allocating memory.",Eol=yes,Unicode=no
.Terminate:
      FileClose File_scan, File_index, File_names
      BufferDestroy [ScanDirBuffer],[Buffer1],[Buffer2],[Buffer3],[Buffer4],[Buffer5]
      TerminateProgram 8
      TerminateProgram 8
    ENDPROGRAM dirswinc

▲Back to the top▲