- - Kopieren von Dateien und Ordnern (wahlweise mit Unterordnern)
- Fortschrittsanzeige mit kalkulierter Restzeit
- Direktes "Streamen" von Daten (gleichzeitiges Lesen & Schreiben, wie beim Disc-Brennen)
- Vorgefertigte Callbacks oder eigene nutzbar
- Anzeige der zu kopierenden Größe
- Wahlweise Übernahme von Dateidatum usw -attribute
- Fehlerausgabe
- Einstellbare Puffergröße
- Einstellbare Vergleichsroutine von Quell- und Zieldaten
- Problemlos als DLL oder PB-Include nutzbar 
Verbesserungsvorschläge sind natürlich wie immer gerne gesehen.
Code: Alles auswählen
;----------------------------------------
;  PureCopy 1.5 (c) Daniel Obermeier
;----------------------------------------
EnableExplicit
Enumeration 0 ; Error Messages
  #PURECOPY_OK
  
  #PURECOPY_USERCANCELED
  
  #PURECOPY_ERROR_NOTFOUND
  #PURECOPY_ERROR_NOREADFILE
  #PURECOPY_ERROR_NOWRITEFILE
  #PURECOPY_ERROR_READDENIED
  #PURECOPY_ERROR_WRITEDENIED
  #PURECOPY_ERROR_DIRECTORYDENIED
  #PURECOPY_ERROR_DIRECTORIESMATCH
EndEnumeration
Enumeration 0 ; Compare routines
  #PURECOPY_COMPARE_NONE
  
  #PURECOPY_COMPARE_DATE ; Simple filedate compare, recommend, fastest
  #PURECOPY_COMPARE_CRC ; Memory based CRC32 checksum compare
  #PURECOPY_COMPARE_SIZE ; Check the file sizes
  
  #PURECOPY_COMPARE_EXISTS ; Continue, if destination file already exists
  #PURECOPY_COMPARE_ASKCALLBACK ; Ask the Compare Callback for overwriting
  
  ; If compare routine not specified, files will always be overwritten
EndEnumeration
; Set Data Modes
#PURECOPY_PROPERTY_CREATED = 1
#PURECOPY_PROPERTY_ACCESSED = 2
#PURECOPY_PROPERTY_MODIFIED = 4
#PURECOPY_PROPERTY_ATTRIBUTES = 8
#PURECOPY_INTERNAL_COUNTSIZE = -1
#PURECOPY_EXPECTEDSLASH = "\"
#PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK = #True
#PURECOPY_COMPILE_WITH_INCLUDE_EXECUTABLE = #True
Structure PURECOPY_INFORMATION
  Root.s
  
  BytesProcessed.q
  BytesTotal.q
  
  FileBytesProcessed.q
  FileBytesTotal.q
  
  SubDirectories.l
  SetFileProperties.l
  CompareRoutine.l
  WaitOnAllErrors.l
  SimultaneousTransfer.l
  
  TransferBufferSize.l
  SimultaneousBufferSize.l
  SharedRead.l
  SharedWrite.l
  
  SimultaneousError.l
  
  SimultaneousCanceled.l
  SimultaneousPaused.l
  
  SkipFile.l
  
  FileIDRead.l
  FileIDWrite.l
  
  *ThreadRead
  *ThreadWrite
  
  *TransferBuffer
  *ProgressCallBack
  *ErrorCallBack
  *CompareCallBack
  *ReadOnlyCallBack
EndStructure
CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
  #PURECOPY_CHR10 = Chr(10)
  #PURECOPY_CHR34 = Chr(34)
  
  
  
  Structure PURECOPY_DEFAULTPROGRESSWINDOW
    InitStatus.l
    
    ProgressWindow.l
    Font.l
    
    Text_Input.l
    Text_Output.l
    Text_Status.l
    
    LabelRemain.l
    Text_RemainingTime.l
    
    LabelBuffer.l
    ProgressBuffer.l
      Text1Buffer.l
      Text2Buffer.l
    LabelFile.l
    ProgressFile.l
      TextFile.l
    ProgressTotal.l
    
    Pause.l
    Cancel.l
    SkipFile.l
    
    ErrorFrame.l
    ErrorList.l
    ErrorContinue.l
    ErrorContinueAlways.l
    
    ErrorsDisplayed.l
    
    CompareWindow.l
    CompareYes.l
    CompareNo.l
    CompareRemember.l
    CompareRememberedChoice.l
    
    ReadOnlyWindow.l
    ReadOnlyYes.l
    ReadOnlyNo.l
    ReadOnlyRemember.l
    ReadOnlyRememberedChoice.l
    
    LastSecond.l
    LastUpdate.l
    LastBytesProcessed.l
    RemainingSeconds.l
    CurrentSpeed.l
  EndStructure
  
  
  
  Procedure.l PureCopy_DefaultCallback_CreateProgressWindow(*PureCopy_Information.PURECOPY_INFORMATION, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    *PureCopy_DefaultProgressWindow\Font = LoadFont(#PB_Any, "Tahoma", 8)
    
    *PureCopy_DefaultProgressWindow\ProgressWindow = OpenWindow(#PB_Any, 0, 0, 400, 210, "PureCopy", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered)
    
    If IsFont(*PureCopy_DefaultProgressWindow\Font)
      SetGadgetFont(#PB_Default, FontID(*PureCopy_DefaultProgressWindow\Font))
    EndIf
    
    
    If *PureCopy_DefaultProgressWindow\ProgressWindow
      ;If CreateGadgetList(WindowID(*PureCopy_DefaultProgressWindow\ProgressWindow))
        TextGadget(#PB_Any, 10, 10, 40, 20, "From:")
        TextGadget(#PB_Any, 10, 30, 40, 20, "To:")
        
        *PureCopy_DefaultProgressWindow\Text_Input = TextGadget(#PB_Any, 50, 10, 340, 15, "")
        *PureCopy_DefaultProgressWindow\Text_Output = TextGadget(#PB_Any, 50, 30, 340, 15, "")
        
        Frame3DGadget(#PB_Any, 10, 55, 380, 115, "Progress")
        
        TextGadget(#PB_Any, 20, 75, 50, 15, "Status:")
        *PureCopy_DefaultProgressWindow\Text_Status = TextGadget(#PB_Any, 70, 75, 310, 15, "")
        
        *PureCopy_DefaultProgressWindow\LabelRemain = TextGadget(#PB_Any, 20, 95, 50, 15, "Remain:")
        *PureCopy_DefaultProgressWindow\Text_RemainingTime = TextGadget(#PB_Any, 70, 95, 310, 15, "")
        
        If *PureCopy_Information\SimultaneousTransfer
          *PureCopy_DefaultProgressWindow\LabelBuffer = TextGadget(#PB_Any, 20, 115, 50, 15, "Buffer:")
          *PureCopy_DefaultProgressWindow\ProgressBuffer = ProgressBarGadget(#PB_Any, 70, 115, 190, 10, 1, 100)
          *PureCopy_DefaultProgressWindow\Text1Buffer = TextGadget(#PB_Any, 270, 115, 60, 15, "", #PB_Text_Right)
          *PureCopy_DefaultProgressWindow\Text2Buffer = TextGadget(#PB_Any, 330, 115, 50, 15, "", #PB_Text_Right)
        EndIf
        
        *PureCopy_DefaultProgressWindow\LabelFile = TextGadget(#PB_Any, 20, 130, 50, 15, "File:")
        *PureCopy_DefaultProgressWindow\ProgressFile = ProgressBarGadget(#PB_Any, 70, 130, 190, 10, 1, 100)
        *PureCopy_DefaultProgressWindow\TextFile = TextGadget(#PB_Any, 270, 130, 110, 15, "", #PB_Text_Right)
        
        TextGadget(#PB_Any, 20, 145, 50, 15, "Total:")
        *PureCopy_DefaultProgressWindow\ProgressTotal = ProgressBarGadget(#PB_Any, 70, 145, 310, 15, 1, 100)
        
        *PureCopy_DefaultProgressWindow\SkipFile = ButtonGadget(#PB_Any, 70, 178, 100, 24, "Skip file")
        *PureCopy_DefaultProgressWindow\Pause = ButtonGadget(#PB_Any, 180, 178, 100, 24, "Pause", #PB_Button_Toggle)
        *PureCopy_DefaultProgressWindow\Cancel = ButtonGadget(#PB_Any, 290, 178, 100, 24, "Cancel")
        
        *PureCopy_DefaultProgressWindow\ErrorFrame = Frame3DGadget(#PB_Any, 10, 205, 380, 200, "Errors")
          HideGadget(*PureCopy_DefaultProgressWindow\ErrorFrame, 1)
        
        *PureCopy_DefaultProgressWindow\ErrorList = ListIconGadget(#PB_Any, 20, 225, 360, 140, "Error code", 70)
          AddGadgetColumn(*PureCopy_DefaultProgressWindow\ErrorList, 1, "Description", 120)
          AddGadgetColumn(*PureCopy_DefaultProgressWindow\ErrorList, 2, "Path", 140)
          HideGadget(*PureCopy_DefaultProgressWindow\ErrorList, 1)
          
        *PureCopy_DefaultProgressWindow\ErrorContinue = ButtonGadget(#PB_Any, 280, 372, 100, 24, "Continue")
          HideGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
          
          *PureCopy_DefaultProgressWindow\ErrorContinueAlways = CheckBoxGadget(#PB_Any, 170, 377, 100, 16, "Continue always")
            If *PureCopy_Information\WaitOnAllErrors
              SetGadgetState(*PureCopy_DefaultProgressWindow\ErrorContinueAlways, 0)
            Else
              SetGadgetState(*PureCopy_DefaultProgressWindow\ErrorContinueAlways, 1)
            EndIf
            HideGadget(*PureCopy_DefaultProgressWindow\ErrorContinueAlways, 1)
        
        *PureCopy_DefaultProgressWindow\ErrorsDisplayed = 0
      ;EndIf
    EndIf
    
    ProcedureReturn #PURECOPY_OK
  EndProcedure
  
  
  
  
  Procedure.s PureCopy_DefaultCallback_ReturnBestTime(Size.l)
    If Size < 60
      ProcedureReturn Str(Size) + " seconds"
    ElseIf Size < 3600
      If Size / 60 = 1
        ProcedureReturn Str(Size / 60) + " minute"
      Else
        ProcedureReturn Str(Size / 60) + " minutes"
      EndIf
    Else
      If Size / 3600 = 1
        ProcedureReturn Str(Size / 3600) + " hour, " + Str(Size / 60 - Int(Size / 3600) * 60) + " minutes"
      Else
        ProcedureReturn Str(Size / 3600) + " hours, " + Str(Size / 60 - Int(Size / 3600) * 60) + " minutes"
      EndIf
    EndIf
  EndProcedure
  
  
  Procedure.s PureCopy_DefaultCallback_ReturnBestSize(Size.q)
    If Size >= 1073741824
      ProcedureReturn StrD(Size / 1073741824, 2) + " GB"
    ElseIf Size >= 1048576
      ProcedureReturn StrD(Size / 1048576, 1) + " MB"
    ElseIf Size >= 1024
      ProcedureReturn StrD(Size / 1024, 1) + " KB"
    Else
      ;ProcedureReturn StrQ(Size) + " Byte"
      ProcedureReturn Str(Size) + " Byte"
    EndIf
  EndProcedure
  
  
  Procedure.s PureCopy_DefaultCallback_ShortenPath(Path.s, Pixel.l, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    Protected LastSlash.l
    Protected NewPath.s
    Protected a.l
    Protected TempString.s
    Protected Size.SIZE
    
    Protected *hDC
    
    If IsFont(*PureCopy_DefaultProgressWindow\Font)
      *hDC = GetDC_(WindowID(*PureCopy_DefaultProgressWindow\ProgressWindow))
      
      If *hDC
        SelectObject_(*hDC, FontID(*PureCopy_DefaultProgressWindow\Font))
        
        For a = 0 To Len(Path) - 3
          If Mid(Path, Len(Path) - a, 1) = #PURECOPY_EXPECTEDSLASH
            TempString.s = Left(Path, 3) + Right(Path, a)
            
            GetTextExtentPoint32_(*hDC, TempString, Len(TempString), Size)
            
            If Size\cx > Pixel
              Break
            Else
              LastSlash = a
            EndIf
          EndIf
        Next
        
        If a <> Len(Path) - 2
          NewPath = Left(Path, 3) + "..." + #PURECOPY_EXPECTEDSLASH + Right(Path, LastSlash)
        Else
          NewPath = Path
        EndIf
        
        ReleaseDC_(WindowID(*PureCopy_DefaultProgressWindow\ProgressWindow), *hDC)
        
        ProcedureReturn NewPath
      Else
        ProcedureReturn Path
      EndIf
    Else
      ProcedureReturn Path
    EndIf
  EndProcedure
  
  
  
  
  
  Procedure.l PureCopy_DefaultCallback_ProgressCallback(InputPath.s, OutputPath.s, *PureCopy_Information.PURECOPY_INFORMATION, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    Protected RemainingBytes.q
    Protected BytesPerSecond.l
    Protected TimeDifference.l
    Protected FreeBytes.q
    Protected EventID.l
    Protected BufferFilled.l
    
    If Date() <> *PureCopy_DefaultProgressWindow\LastSecond
      *PureCopy_DefaultProgressWindow\RemainingSeconds - 1
      *PureCopy_DefaultProgressWindow\LastSecond = Date()
    EndIf
    
    
    InputPath.s = PureCopy_DefaultCallback_ShortenPath(InputPath, 330, *PureCopy_DefaultProgressWindow)
    OutputPath.s = PureCopy_DefaultCallback_ShortenPath(OutputPath, 330, *PureCopy_DefaultProgressWindow)
    
    
    If *PureCopy_Information\BytesProcessed = #PURECOPY_INTERNAL_COUNTSIZE
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_Input, InputPath)
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_Status, PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_Information\BytesTotal))
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_RemainingTime, "Counting files ...")
    Else
      TimeDifference = Abs(ElapsedMilliseconds() - *PureCopy_DefaultProgressWindow\LastUpdate)
      If TimeDifference >= 10000
        RemainingBytes = *PureCopy_Information\BytesTotal - *PureCopy_Information\BytesProcessed
        BytesPerSecond.l = IntQ(*PureCopy_Information\BytesProcessed - *PureCopy_DefaultProgressWindow\LastBytesProcessed)
        BytesPerSecond / (TimeDifference / 1000)
        
        If BytesPerSecond
          *PureCopy_DefaultProgressWindow\RemainingSeconds = (RemainingBytes / BytesPerSecond)
          *PureCopy_DefaultProgressWindow\CurrentSpeed = BytesPerSecond
        EndIf
        
        *PureCopy_DefaultProgressWindow\LastBytesProcessed = *PureCopy_Information\BytesProcessed
        *PureCopy_DefaultProgressWindow\LastUpdate = ElapsedMilliseconds()
      EndIf
      
      
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_Input, InputPath)
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_Output, OutputPath)
      
      GetDiskFreeSpaceEx_(*PureCopy_Information\Root, @FreeBytes, 0, 0)
      
      SetGadgetText(*PureCopy_DefaultProgressWindow\Text_Status, "Copied " + PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_Information\BytesProcessed) + " of " + PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_Information\BytesTotal) + " (remaining space: " + PureCopy_DefaultCallback_ReturnBestSize(FreeBytes) + ")")
      If *PureCopy_DefaultProgressWindow\RemainingSeconds > 0
        SetGadgetText(*PureCopy_DefaultProgressWindow\Text_RemainingTime, PureCopy_DefaultCallback_ReturnBestTime(*PureCopy_DefaultProgressWindow\RemainingSeconds) + " (" + PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_DefaultProgressWindow\CurrentSpeed) + "/s)")
      Else
        SetGadgetText(*PureCopy_DefaultProgressWindow\Text_RemainingTime, "")
      EndIf
      
      If *PureCopy_Information\SimultaneousTransfer
        BufferFilled = *PureCopy_Information\SharedRead - *PureCopy_Information\SharedWrite
        If BufferFilled < 0
          BufferFilled + *PureCopy_Information\SimultaneousBufferSize
        EndIf
        
        SetGadgetState(*PureCopy_DefaultProgressWindow\ProgressBuffer, Round(BufferFilled / *PureCopy_Information\SimultaneousBufferSize * 100, #PB_Round_Nearest))
        SetGadgetText(*PureCopy_DefaultProgressWindow\Text1Buffer, StrF(BufferFilled / 1000 / 1000, 1) + " MB")
        SetGadgetText(*PureCopy_DefaultProgressWindow\Text2Buffer, "(" + Str(Round(BufferFilled / *PureCopy_Information\SimultaneousBufferSize * 100, #PB_Round_Nearest)) + "%)")
      EndIf
      
      SetGadgetState(*PureCopy_DefaultProgressWindow\ProgressFile, Int(*PureCopy_Information\FileBytesProcessed / *PureCopy_Information\FileBytesTotal * 100))
      SetGadgetText(*PureCopy_DefaultProgressWindow\TextFile, PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_Information\FileBytesProcessed) + " / " + PureCopy_DefaultCallback_ReturnBestSize(*PureCopy_Information\FileBytesTotal))
      SetGadgetState(*PureCopy_DefaultProgressWindow\ProgressTotal, Int(*PureCopy_Information\BytesProcessed / *PureCopy_Information\BytesTotal * 100))
    EndIf
    
    
    Repeat
      EventID = WindowEvent()
      
      Select EventID
      Case #PB_Event_Gadget
        Select EventGadget()
        Case *PureCopy_DefaultProgressWindow\SkipFile
          *PureCopy_Information\SkipFile = #True
        
        Case *PureCopy_DefaultProgressWindow\Pause
          *PureCopy_Information\SimultaneousPaused = #True - *PureCopy_Information\SimultaneousPaused
        
        Case *PureCopy_DefaultProgressWindow\Cancel
          DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
          ProcedureReturn #False
          
        EndSelect
        
      Case #PB_Event_CloseWindow
        ProcedureReturn #False
        
      Case 0
        If GetGadgetState(*PureCopy_DefaultProgressWindow\Pause) = #False
          ProcedureReturn #True
        EndIf
        
      EndSelect
    ForEver
  EndProcedure
  
  
  Procedure PureCopy_DefaultCallback_ErrorCallback(ErrorCode.l, Path.s, *PureCopy_Information.PURECOPY_INFORMATION, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    Protected Message.s
    Protected EventID.l
    
    Select ErrorCode
    Case #PURECOPY_USERCANCELED
      Message = "PureCopy canceled by the user"
      
    Case #PURECOPY_ERROR_NOTFOUND
      Message = "Source file doesn't exist"
      
    Case #PURECOPY_ERROR_NOREADFILE
      Message = "Source file can't be opened"
      
    Case #PURECOPY_ERROR_NOWRITEFILE
      Message = "Destination file can't be created"
      
    Case #PURECOPY_ERROR_READDENIED
      Message = "Source file can't be read"
      
    Case #PURECOPY_ERROR_WRITEDENIED
      Message = "Destination file can't be written"
      
    Case #PURECOPY_ERROR_DIRECTORYDENIED
      Message = "Directory can't be examined"
      
    Case #PURECOPY_ERROR_DIRECTORIESMATCH
      Message = "Directory file names are equal"
      
    Default
      Message = "Unknown error code"
      
    EndSelect
    
    AddGadgetItem(*PureCopy_DefaultProgressWindow\ErrorList, -1, "0x" + Hex(ErrorCode) + #PURECOPY_CHR10 + Message + #PURECOPY_CHR10 + Path)
    
    If *PureCopy_DefaultProgressWindow\ErrorsDisplayed = 0
      ResizeWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, #PB_Ignore, #PB_Ignore, #PB_Ignore, 415)
      HideGadget(*PureCopy_DefaultProgressWindow\ErrorFrame, 0)
      HideGadget(*PureCopy_DefaultProgressWindow\ErrorList, 0)
      
      HideGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 0)
      HideGadget(*PureCopy_DefaultProgressWindow\ErrorContinueAlways, 0)
    EndIf
    
    DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 0)
      
    *PureCopy_DefaultProgressWindow\ErrorsDisplayed = #True
    
    
    
    *PureCopy_Information\WaitOnAllErrors = 1 - GetGadgetState(*PureCopy_DefaultProgressWindow\ErrorContinueAlways)
    
    
    Repeat
      EventID = WindowEvent()
      
      Select EventID
      Case #PB_Event_Gadget
        Select EventGadget()
        Case *PureCopy_DefaultProgressWindow\ErrorContinue
          *PureCopy_Information\WaitOnAllErrors = 1 - GetGadgetState(*PureCopy_DefaultProgressWindow\ErrorContinueAlways)
          DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
          ProcedureReturn #True
          
        Case *PureCopy_DefaultProgressWindow\Cancel
          DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
          CallDebugger
          ProcedureReturn #False
          
        EndSelect
          
      Case #PB_Event_CloseWindow
        DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
        ProcedureReturn #False
        
      Case 0
        If *PureCopy_Information\WaitOnAllErrors = 0
          If GetGadgetState(*PureCopy_DefaultProgressWindow\Pause) = 0
            DisableGadget(*PureCopy_DefaultProgressWindow\ErrorContinue, 1)
            ProcedureReturn #True
          EndIf
        EndIf
        
      EndSelect
    ForEver
  EndProcedure
  
  
  
  Procedure PureCopy_DefaultCallback_CompareCallback(DestinationFile.s, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    Protected EventID.l
    
    If *PureCopy_DefaultProgressWindow\CompareRememberedChoice <> -1
      ProcedureReturn *PureCopy_DefaultProgressWindow\CompareRememberedChoice
    EndIf
    
    *PureCopy_DefaultProgressWindow\CompareWindow = OpenWindow(#PB_Any, 0, 0, 360, 100, "PureCopy", #PB_Window_TitleBar | #PB_Window_ScreenCentered, WindowID(*PureCopy_DefaultProgressWindow\ProgressWindow))
    
    If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
      DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 1)
    EndIf
    
    If *PureCopy_DefaultProgressWindow\CompareWindow; And CreateGadgetList(WindowID(*PureCopy_DefaultProgressWindow\CompareWindow))
      If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
        DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 1)
      EndIf
      
      MessageBeep_(48)
      
      TextGadget(#PB_Any, 10, 10, 340, 50, "The file " + #PURECOPY_CHR34 + DestinationFile + #PURECOPY_CHR34 + " already exists.")
      TextGadget(#PB_Any, 10, 65, 70, 20, "Overwrite?")
      
      *PureCopy_DefaultProgressWindow\CompareYes = ButtonGadget(#PB_Any, 80, 60, 60, 25, "Yes")
      *PureCopy_DefaultProgressWindow\CompareNo = ButtonGadget(#PB_Any, 160, 60, 60, 25, "No")
      *PureCopy_DefaultProgressWindow\CompareRemember = CheckBoxGadget(#PB_Any, 240, 60, 100, 25, "Remember choice")
      
      Repeat
        EventID = WaitWindowEvent()
        
        Select EventID
        Case #PB_Event_Gadget
          Select EventGadget()
          Case *PureCopy_DefaultProgressWindow\CompareYes
            If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
              DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
            EndIf
            
            If GetGadgetState(*PureCopy_DefaultProgressWindow\CompareRemember)
              *PureCopy_DefaultProgressWindow\CompareRememberedChoice = #True
            EndIf
            
            CloseWindow(*PureCopy_DefaultProgressWindow\CompareWindow)
            
            ProcedureReturn #True
            
            
          Case *PureCopy_DefaultProgressWindow\CompareNo
            If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
              DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
            EndIf
            
            If GetGadgetState(*PureCopy_DefaultProgressWindow\CompareRemember)
              *PureCopy_DefaultProgressWindow\CompareRememberedChoice = #False
            EndIf
            
            CloseWindow(*PureCopy_DefaultProgressWindow\CompareWindow)
            
            ProcedureReturn #False
            
          EndSelect
        EndSelect
        
      ForEver
      
      If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
        DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
      EndIf
      
      CloseWindow(*PureCopy_DefaultProgressWindow\CompareWindow)
    Else
      ProcedureReturn #True
    EndIf
    
  EndProcedure
  
  
  Procedure PureCopy_DefaultCallback_ReadOnlyCallback(DestinationFile.s, *PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW)
    Protected EventID.l
    
    If *PureCopy_DefaultProgressWindow\ReadOnlyRememberedChoice <> -1
      ProcedureReturn *PureCopy_DefaultProgressWindow\ReadOnlyRememberedChoice
    EndIf
    
    *PureCopy_DefaultProgressWindow\ReadOnlyWindow = OpenWindow(#PB_Any, 0, 0, 360, 100, "PureCopy", #PB_Window_TitleBar | #PB_Window_ScreenCentered, WindowID(*PureCopy_DefaultProgressWindow\ProgressWindow))
    
    If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
      DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 1)
    EndIf
    
    If *PureCopy_DefaultProgressWindow\ReadOnlyWindow; And CreateGadgetList(WindowID(*PureCopy_DefaultProgressWindow\ReadOnlyWindow))
      If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
        DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 1)
      EndIf
      
      MessageBeep_(48)
      
      TextGadget(#PB_Any, 10, 10, 340, 50, "The file " + #PURECOPY_CHR34 + DestinationFile + #PURECOPY_CHR34 + " is read-only.")
      TextGadget(#PB_Any, 10, 65, 70, 20, "Overwrite?")
      
      *PureCopy_DefaultProgressWindow\ReadOnlyYes = ButtonGadget(#PB_Any, 80, 60, 60, 25, "Yes")
      *PureCopy_DefaultProgressWindow\ReadOnlyNo = ButtonGadget(#PB_Any, 160, 60, 60, 25, "No")
      *PureCopy_DefaultProgressWindow\ReadOnlyRemember = CheckBoxGadget(#PB_Any, 240, 60, 100, 25, "Remember choice")
      
      Repeat
        EventID = WaitWindowEvent()
        
        Select EventID
        Case #PB_Event_Gadget
          Select EventGadget()
          Case *PureCopy_DefaultProgressWindow\ReadOnlyYes
            If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
              DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
            EndIf
            
            If GetGadgetState(*PureCopy_DefaultProgressWindow\ReadOnlyRemember)
              *PureCopy_DefaultProgressWindow\ReadOnlyRememberedChoice = #True
            EndIf
            
            CloseWindow(*PureCopy_DefaultProgressWindow\ReadOnlyWindow)
            
            ProcedureReturn #True
            
            
          Case *PureCopy_DefaultProgressWindow\ReadOnlyNo
            If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
              DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
            EndIf
            
            If GetGadgetState(*PureCopy_DefaultProgressWindow\ReadOnlyRemember)
              *PureCopy_DefaultProgressWindow\ReadOnlyRememberedChoice = #False
            EndIf
            
            CloseWindow(*PureCopy_DefaultProgressWindow\ReadOnlyWindow)
            
            ProcedureReturn #False
            
          EndSelect
        EndSelect
        
      ForEver
      
      If IsWindow(*PureCopy_DefaultProgressWindow\ProgressWindow)
        DisableWindow(*PureCopy_DefaultProgressWindow\ProgressWindow, 0)
      EndIf
      
      CloseWindow(*PureCopy_DefaultProgressWindow\ReadOnlyWindow)
    Else
      ProcedureReturn #True
    EndIf
    
  EndProcedure
CompilerEndIf
Procedure PureCopy_Internal_SharedRead(*PureCopy_Information.PURECOPY_INFORMATION)
  Protected BufferFilled.l, BufferEmpty.l, ReadSize.l
  
  While Eof(*PureCopy_Information\FileIDRead) = 0 And *PureCopy_Information\SimultaneousCanceled = #False
    BufferFilled = *PureCopy_Information\SharedRead - *PureCopy_Information\SharedWrite
    If BufferFilled < 0
      BufferFilled + *PureCopy_Information\SimultaneousBufferSize
    EndIf
    BufferEmpty = *PureCopy_Information\SimultaneousBufferSize - BufferFilled
    
    If BufferEmpty <> *PureCopy_Information\TransferBufferSize
      ReadSize = ReadData(*PureCopy_Information\FileIDRead, *PureCopy_Information\TransferBuffer + *PureCopy_Information\SharedRead, *PureCopy_Information\TransferBufferSize)
      *PureCopy_Information\SharedRead + ReadSize
      
      If ReadSize
        If *PureCopy_Information\SharedRead = *PureCopy_Information\SimultaneousBufferSize
          *PureCopy_Information\SharedRead = 0
        EndIf
      Else
        *PureCopy_Information\SimultaneousError = #PURECOPY_ERROR_READDENIED
        ProcedureReturn
      EndIf
    EndIf
    
    While *PureCopy_Information\SimultaneousPaused And *PureCopy_Information\SimultaneousCanceled = #False
      Delay(100)
    Wend
  Wend
EndProcedure
Procedure PureCopy_Internal_SharedWrite(*PureCopy_Information.PURECOPY_INFORMATION)
  Protected BufferFilled.l, BufferWrite.l
  
  Repeat
    BufferFilled = *PureCopy_Information\SharedRead - *PureCopy_Information\SharedWrite
    If BufferFilled < 0
      BufferFilled + *PureCopy_Information\SimultaneousBufferSize
    EndIf
    
    If (BufferFilled <> *PureCopy_Information\TransferBufferSize Or Eof(*PureCopy_Information\FileIDRead)) And BufferFilled <> 0
      BufferWrite = BufferFilled
      If BufferWrite > *PureCopy_Information\TransferBufferSize
        BufferWrite = *PureCopy_Information\TransferBufferSize
      EndIf
      
      If WriteData(*PureCopy_Information\FileIDWrite, *PureCopy_Information\TransferBuffer + *PureCopy_Information\SharedWrite, BufferWrite)
        *PureCopy_Information\SharedWrite + BufferWrite
        *PureCopy_Information\BytesProcessed + BufferWrite
        *PureCopy_Information\FileBytesProcessed + BufferWrite
        
        If *PureCopy_Information\SharedWrite = *PureCopy_Information\SimultaneousBufferSize
          *PureCopy_Information\SharedWrite = 0
        EndIf
      Else
        *PureCopy_Information\SimultaneousError = #PURECOPY_ERROR_WRITEDENIED
        ProcedureReturn
      EndIf
    EndIf
    
    While *PureCopy_Information\SimultaneousPaused And *PureCopy_Information\SimultaneousCanceled = #False
      Delay(100)
    Wend
  Until (BufferFilled <> 0 And BufferFilled < *PureCopy_Information\TransferBufferSize) Or (BufferFilled = 0 And Eof(*PureCopy_Information\FileIDRead)) Or *PureCopy_Information\SimultaneousCanceled = #True
EndProcedure
Procedure.l PureCopy_Internal_DoFile(InputPath.s, OutputPath.s, *PureCopy_Information.PURECOPY_INFORMATION, *UserInformation)
  Protected ReallyReadSize.l
  Protected TimeCounter.l
  Protected CRCFirstValue.l
  Protected CRCInitValue.l
  Protected ContentDifferent.l
  Protected Attributes.l
  
  CRCInitValue = 0
  ContentDifferent = 0
  
  
  If FileSize(OutputPath) >= 0 And FileSize(InputPath) = FileSize(OutputPath)
    If *PureCopy_Information\ProgressCallBack
      If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputPath, @OutputPath, *PureCopy_Information, *UserInformation) = #False
        ProcedureReturn #PURECOPY_USERCANCELED
      EndIf
    EndIf
    
    
    Select *PureCopy_Information\CompareRoutine
    Case #PURECOPY_COMPARE_DATE
      If GetFileDate(InputPath, #PB_Date_Modified) = GetFileDate(OutputPath, #PB_Date_Modified)
        *PureCopy_Information\BytesProcessed + FileSize(InputPath)
        ProcedureReturn #PURECOPY_OK
      EndIf
      
    Case #PURECOPY_COMPARE_CRC
      *PureCopy_Information\FileIDRead = ReadFile(#PB_Any, InputPath)
      
      If *PureCopy_Information\FileIDRead
        *PureCopy_Information\FileIDWrite = ReadFile(#PB_Any, OutputPath)
        
        If *PureCopy_Information\FileIDWrite
          While Eof(*PureCopy_Information\FileIDRead) = #False
            ReallyReadSize = ReadData(*PureCopy_Information\FileIDRead, *PureCopy_Information\TransferBuffer, *PureCopy_Information\TransferBufferSize)
            CRCFirstValue = CRC32Fingerprint(*PureCopy_Information\TransferBuffer, ReallyReadSize, CRCInitValue)
            
            ReallyReadSize = ReadData(*PureCopy_Information\FileIDWrite, *PureCopy_Information\TransferBuffer, *PureCopy_Information\TransferBufferSize)
            If CRC32Fingerprint(*PureCopy_Information\TransferBuffer, ReallyReadSize, CRCInitValue) <> CRCFirstValue
              ContentDifferent = 1
              Break
            EndIf
            
            CRCInitValue = CRCFirstValue
          Wend
          
          
          CloseFile(*PureCopy_Information\FileIDWrite)
        EndIf
        
        CloseFile(*PureCopy_Information\FileIDRead)
      EndIf
      
      If ContentDifferent = 0
        *PureCopy_Information\BytesProcessed + FileSize(InputPath)
        ProcedureReturn #PURECOPY_OK
      EndIf
      
      
    Case #PURECOPY_COMPARE_SIZE
      *PureCopy_Information\BytesProcessed + FileSize(InputPath)
      ProcedureReturn #PURECOPY_OK
      
    Case #PURECOPY_COMPARE_EXISTS
      If FileSize(OutputPath) >= 0
        *PureCopy_Information\BytesProcessed + FileSize(InputPath)
        ProcedureReturn #PURECOPY_OK
      EndIf
      
      
    Case #PURECOPY_COMPARE_ASKCALLBACK
      If *PureCopy_Information\CompareCallBack
        If CallFunctionFast(*PureCopy_Information\CompareCallBack, @OutputPath, *UserInformation) = #False
          *PureCopy_Information\BytesProcessed + FileSize(InputPath)
          ProcedureReturn #PURECOPY_OK
        EndIf
      EndIf
    EndSelect
  EndIf
  
  
  Attributes = GetFileAttributes_(OutputPath)
  
  If Attributes <> -1 And Attributes & #FILE_ATTRIBUTE_READONLY
    If CallFunctionFast(*PureCopy_Information\ReadOnlyCallBack, @OutputPath, *UserInformation) = #False
      *PureCopy_Information\BytesProcessed + FileSize(InputPath)
      ProcedureReturn #PURECOPY_OK
    Else
      SetFileAttributes_(OutputPath, Attributes &~ #FILE_ATTRIBUTE_READONLY)
    EndIf
  EndIf
  
  
  If *PureCopy_Information\ProgressCallBack
    If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputPath, @OutputPath, *PureCopy_Information, *UserInformation) = #False
      ProcedureReturn #PURECOPY_USERCANCELED
    EndIf
  EndIf
  
  
  
  TimeCounter = ElapsedMilliseconds()
  *PureCopy_Information\FileIDRead = ReadFile(#PB_Any, InputPath)
  
  
  If *PureCopy_Information\FileIDRead
    FileBuffersSize(*PureCopy_Information\FileIDRead, 0)
    *PureCopy_Information\FileIDWrite = CreateFile(#PB_Any, OutputPath)
    
    If *PureCopy_Information\FileIDWrite
      FileBuffersSize(*PureCopy_Information\FileIDWrite, 0)
      
      *PureCopy_Information\FileBytesProcessed = 0
      *PureCopy_Information\FileBytesTotal = Lof(*PureCopy_Information\FileIDRead)
      
      If *PureCopy_Information\ProgressCallBack
        If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputPath, @OutputPath, *PureCopy_Information, *UserInformation) = #False
          CloseFile(*PureCopy_Information\FileIDRead)
          CloseFile(*PureCopy_Information\FileIDWrite)
          
          DeleteFile(OutputPath)
          
          ProcedureReturn #PURECOPY_USERCANCELED
        EndIf
      EndIf
      
      If FileSize(InputPath) <> 0
        *PureCopy_Information\SkipFile = #False
        
        If *PureCopy_Information\SimultaneousTransfer
          *PureCopy_Information\SharedRead = 0
          *PureCopy_Information\SharedWrite = 0
          
          *PureCopy_Information\SimultaneousCanceled = #False
          *PureCopy_Information\SimultaneousError = #PURECOPY_OK
          
          *PureCopy_Information\ThreadRead = CreateThread(@PureCopy_Internal_SharedRead(), *PureCopy_Information)
          *PureCopy_Information\ThreadWrite = CreateThread(@PureCopy_Internal_SharedWrite(), *PureCopy_Information)
          
          Repeat
            If *PureCopy_Information\ProgressCallBack
              If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputPath, @OutputPath, *PureCopy_Information, *UserInformation) = #False
                *PureCopy_Information\SimultaneousCanceled = #True
                
                WaitThread(*PureCopy_Information\ThreadWrite, 10000)
                WaitThread(*PureCopy_Information\ThreadRead, 10000)
                
                CloseFile(*PureCopy_Information\FileIDRead)
                CloseFile(*PureCopy_Information\FileIDWrite)
                
                DeleteFile(OutputPath)
                
                ProcedureReturn #PURECOPY_USERCANCELED
              EndIf
              
              TimeCounter = ElapsedMilliseconds()
            EndIf
          Until WaitThread(*PureCopy_Information\ThreadWrite, 100) Or *PureCopy_Information\SkipFile
          
          If *PureCopy_Information\SkipFile
            *PureCopy_Information\SimultaneousCanceled = #True
            *PureCopy_Information\BytesProcessed + (*PureCopy_Information\FileBytesTotal -  *PureCopy_Information\FileBytesProcessed)
            
            WaitThread(*PureCopy_Information\ThreadWrite, 10000)
            WaitThread(*PureCopy_Information\ThreadRead, 10000)
            
            CloseFile(*PureCopy_Information\FileIDRead)
            CloseFile(*PureCopy_Information\FileIDWrite)
            
            DeleteFile(OutputPath)
            
            ProcedureReturn #PURECOPY_OK
          ElseIf *PureCopy_Information\SimultaneousError <> #PURECOPY_OK
            *PureCopy_Information\SimultaneousCanceled = #True
            *PureCopy_Information\BytesProcessed + (*PureCopy_Information\FileBytesTotal -  *PureCopy_Information\FileBytesProcessed)
            
            WaitThread(*PureCopy_Information\ThreadWrite, 10000)
            WaitThread(*PureCopy_Information\ThreadRead, 10000)
            
            CloseFile(*PureCopy_Information\FileIDRead)
            CloseFile(*PureCopy_Information\FileIDWrite)
            
            DeleteFile(OutputPath)
            
            If *PureCopy_Information\ErrorCallBack
              Select *PureCopy_Information\SimultaneousError
              Case #PURECOPY_ERROR_READDENIED
                If CallFunctionFast(*PureCopy_Information\ErrorCallBack, *PureCopy_Information\SimultaneousError, @InputPath, *PureCopy_Information, *UserInformation) = #False
                  ProcedureReturn #PURECOPY_USERCANCELED
                EndIf
                
              Case #PURECOPY_ERROR_WRITEDENIED
                If CallFunctionFast(*PureCopy_Information\ErrorCallBack, *PureCopy_Information\SimultaneousError, @OutputPath, *PureCopy_Information, *UserInformation) = #False
                  ProcedureReturn #PURECOPY_USERCANCELED
                EndIf
                
              EndSelect
            EndIf
            
            ProcedureReturn *PureCopy_Information\SimultaneousError
          Else
            WaitThread(*PureCopy_Information\ThreadRead, 10000)
          EndIf
        Else
          While Eof(*PureCopy_Information\FileIDRead) = #False And *PureCopy_Information\SkipFile = #False
            ReallyReadSize = ReadData(*PureCopy_Information\FileIDRead, *PureCopy_Information\TransferBuffer, *PureCopy_Information\TransferBufferSize)
            
            If ReallyReadSize
              If WriteData(*PureCopy_Information\FileIDWrite, *PureCopy_Information\TransferBuffer, ReallyReadSize)
                *PureCopy_Information\BytesProcessed + ReallyReadSize
                *PureCopy_Information\FileBytesProcessed + ReallyReadSize
              Else
                *PureCopy_Information\BytesProcessed + (*PureCopy_Information\FileBytesTotal -  *PureCopy_Information\FileBytesProcessed)
                
                CloseFile(*PureCopy_Information\FileIDRead)
                CloseFile(*PureCopy_Information\FileIDWrite)
                
                DeleteFile(OutputPath)
                
                If *PureCopy_Information\ErrorCallBack
                  If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_WRITEDENIED, @OutputPath, *PureCopy_Information, *UserInformation) = #False
                    ProcedureReturn #PURECOPY_USERCANCELED
                  EndIf
                EndIf
                
                ProcedureReturn #PURECOPY_ERROR_WRITEDENIED
              EndIf
            Else
              *PureCopy_Information\BytesProcessed + (*PureCopy_Information\FileBytesTotal -  *PureCopy_Information\FileBytesProcessed)
              
              CloseFile(*PureCopy_Information\FileIDRead)
              CloseFile(*PureCopy_Information\FileIDWrite)
              
              DeleteFile(OutputPath)
              
              If *PureCopy_Information\ErrorCallBack
                If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_READDENIED, @InputPath, *PureCopy_Information, *UserInformation) = #False
                  ProcedureReturn #PURECOPY_USERCANCELED
                EndIf
              EndIf
              
              ProcedureReturn #PURECOPY_ERROR_READDENIED
            EndIf
            
            
            If Abs(ElapsedMilliseconds() - TimeCounter) > 100 And *PureCopy_Information\ProgressCallBack
              If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputPath, @OutputPath, *PureCopy_Information, *UserInformation) = #False
                CloseFile(*PureCopy_Information\FileIDRead)
                CloseFile(*PureCopy_Information\FileIDWrite)
                
                DeleteFile(OutputPath)
                
                ProcedureReturn #PURECOPY_USERCANCELED
              EndIf
              
              TimeCounter = ElapsedMilliseconds()
            EndIf
          Wend
          
          If *PureCopy_Information\SkipFile
            *PureCopy_Information\BytesProcessed + (*PureCopy_Information\FileBytesTotal -  *PureCopy_Information\FileBytesProcessed)
            
            CloseFile(*PureCopy_Information\FileIDRead)
            CloseFile(*PureCopy_Information\FileIDWrite)
            
            DeleteFile(OutputPath)
            
            ProcedureReturn #PURECOPY_OK
          EndIf
        EndIf
      EndIf
      
      CloseFile(*PureCopy_Information\FileIDRead)
      CloseFile(*PureCopy_Information\FileIDWrite)
    Else
      CloseFile(*PureCopy_Information\FileIDRead)
      
      If *PureCopy_Information\ErrorCallBack
        If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_NOWRITEFILE, @OutputPath, *PureCopy_Information, *UserInformation) = #False
          ProcedureReturn #PURECOPY_USERCANCELED
        EndIf
      EndIf
      
      ProcedureReturn #PURECOPY_ERROR_NOWRITEFILE
    EndIf
  Else
    If *PureCopy_Information\ErrorCallBack
      If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_NOREADFILE, @InputPath, *PureCopy_Information, *UserInformation) = #False
        ProcedureReturn #PURECOPY_USERCANCELED
      EndIf
    EndIf
    
    ProcedureReturn #PURECOPY_ERROR_NOREADFILE
  EndIf
  
  If *PureCopy_Information\SetFileProperties & #PURECOPY_PROPERTY_CREATED
    SetFileDate(OutputPath, #PB_Date_Created, GetFileDate(InputPath, #PB_Date_Created))
  EndIf
  If *PureCopy_Information\SetFileProperties & #PURECOPY_PROPERTY_ACCESSED
    SetFileDate(OutputPath, #PB_Date_Accessed, GetFileDate(InputPath, #PB_Date_Accessed))
  EndIf
  If *PureCopy_Information\SetFileProperties & #PURECOPY_PROPERTY_MODIFIED
    SetFileDate(OutputPath, #PB_Date_Modified, GetFileDate(InputPath, #PB_Date_Modified))
  EndIf
  If *PureCopy_Information\SetFileProperties & #PURECOPY_PROPERTY_ATTRIBUTES
    SetFileAttributes_(OutputPath, GetFileAttributes_(InputPath)) ; Has to be replaced by another Procedure on Non-Windows OS!
  EndIf
  
  ProcedureReturn #PURECOPY_OK
EndProcedure
Procedure.l PureCopy_Internal_DoDirectory(InputDirectory.s, OutputDirectory.s, *PureCopy_Information.PURECOPY_INFORMATION, *UserInformation)
  Protected Result.l
  Protected Directory.l
  Protected DirectoryName.s
  
  
  InputDirectory + #PURECOPY_EXPECTEDSLASH
  OutputDirectory + #PURECOPY_EXPECTEDSLASH
  
  MakeSureDirectoryPathExists_(OutputDirectory) ; Has to be replaced by another Procedure on Non-Windows OS!
  
  Directory = ExamineDirectory(#PB_Any, InputDirectory, "*.*")
  If Directory
    While NextDirectoryEntry(Directory)
      DirectoryName = DirectoryEntryName(Directory)
      
      If DirectoryName = "." Or DirectoryName = ".."
        Continue
      EndIf
      
      Select DirectoryEntryType(Directory)
      Case #PB_DirectoryEntry_File
        Result = PureCopy_Internal_DoFile(InputDirectory + DirectoryName, OutputDirectory + DirectoryName, *PureCopy_Information, *UserInformation)
        
        If Result = #PURECOPY_USERCANCELED
          FinishDirectory(Directory)
          ProcedureReturn Result
        EndIf
        
      Case #PB_DirectoryEntry_Directory
        If *PureCopy_Information\SubDirectories
          Result = PureCopy_Internal_DoDirectory(InputDirectory + DirectoryName, OutputDirectory + DirectoryName, *PureCopy_Information, *UserInformation)
          If Result = #PURECOPY_USERCANCELED
            FinishDirectory(Directory)
            ProcedureReturn Result
          EndIf
        EndIf
        
      EndSelect
      
    Wend
  Else
    If *PureCopy_Information\ErrorCallBack
      If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_DIRECTORYDENIED, @InputDirectory, *PureCopy_Information, *UserInformation) = #False
        ProcedureReturn #PURECOPY_USERCANCELED
      EndIf
    EndIf
    
    ProcedureReturn #PURECOPY_ERROR_DIRECTORYDENIED
  EndIf
  
  
  FinishDirectory(Directory)
  
  
  
  InputDirectory = Left(InputDirectory, Len(InputDirectory) - 1)
  OutputDirectory = Left(OutputDirectory, Len(OutputDirectory) - 1)
  
  If *PureCopy_Information\SetFileProperties & #PURECOPY_PROPERTY_ATTRIBUTES
    SetFileAttributes_(OutputDirectory, GetFileAttributes_(InputDirectory)) ; Has to be replaced by another Procedure on Non-Windows OS!
  EndIf
  
  ProcedureReturn #PURECOPY_OK
EndProcedure
Procedure.l PureCopy_Internal_CountSize(InputDirectory.s, *PureCopy_Information.PURECOPY_INFORMATION, *UserInformation)
  Protected Result.l
  Protected Directory.l
  Protected DirectoryName.s
  
  InputDirectory + #PURECOPY_EXPECTEDSLASH
  
  If *PureCopy_Information\ProgressCallBack
    If CallFunctionFast(*PureCopy_Information\ProgressCallBack, @InputDirectory, @"", *PureCopy_Information, *UserInformation) = #False
      ProcedureReturn #PURECOPY_USERCANCELED
    EndIf
  EndIf
  
  
  Directory = ExamineDirectory(#PB_Any, InputDirectory, "*.*")
  If Directory
    While NextDirectoryEntry(Directory)
      DirectoryName = DirectoryEntryName(Directory)
      
      If DirectoryName = "." Or DirectoryName = ".."
        Continue
      EndIf
      
      Select DirectoryEntryType(Directory)
      Case #PB_DirectoryEntry_File
        *PureCopy_Information\BytesTotal + DirectoryEntrySize(Directory)
        
      Case #PB_DirectoryEntry_Directory
        If *PureCopy_Information\SubDirectories
          Result = PureCopy_Internal_CountSize(InputDirectory + DirectoryName, *PureCopy_Information, *UserInformation)
          If Result <> #PURECOPY_OK And Result <> #PURECOPY_ERROR_DIRECTORYDENIED
            FinishDirectory(Directory)
            ProcedureReturn Result
          EndIf
        EndIf
        
      EndSelect
      
    Wend
  Else
    If *PureCopy_Information\ErrorCallBack
      If CallFunctionFast(*PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_DIRECTORYDENIED, @InputDirectory, *PureCopy_Information, *UserInformation) = #False
        ProcedureReturn #PURECOPY_USERCANCELED
      EndIf
    EndIf
    
    ProcedureReturn #PURECOPY_ERROR_DIRECTORYDENIED
  EndIf
  
  FinishDirectory(Directory)
  
  ProcedureReturn #PURECOPY_OK
EndProcedure
Procedure.l PureCopy(SourcePathComplete.s, DestinationDirectory.s, *UserInformation, SubDirectories.l = #PB_Ignore, SetFileProperties.l = #PB_Ignore, CompareRoutine.l = #PB_Ignore, TransferBufferSize.l = #PB_Ignore, SimultaneousBufferCount.l = #PB_Ignore, WaitOnError.l = #PB_Ignore, SimultaneousTransfer.l = #PB_Ignore, *ProgressCallback.l = #PB_Ignore, *ErrorCallback.l = #PB_Ignore, *CompareCallback.l = #PB_Ignore, *ReadOnlyCallback.l = #PB_Ignore)
  Protected Result.l
  Protected EventID.l
  Protected PureCopy_Information.PURECOPY_INFORMATION
  
  Protected SourcePath.s
  Protected FilesCount.l
  Protected FileIndex.l
  
  
  CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
    Protected PureCopy_DefaultProgressWindow.PURECOPY_DEFAULTPROGRESSWINDOW
    PureCopy_DefaultProgressWindow\CompareRememberedChoice = -1
    PureCopy_DefaultProgressWindow\ReadOnlyRememberedChoice = -1
  CompilerEndIf
  
  If Left(DestinationDirectory, 2) = "\\"
    PureCopy_Information\Root.s = Left(DestinationDirectory, FindString(DestinationDirectory, #PURECOPY_EXPECTEDSLASH, 3))
  Else
    PureCopy_Information\Root = Left(DestinationDirectory, 3)
  EndIf
  
  If SubDirectories = #PB_Ignore
    PureCopy_Information\SubDirectories = 1
  Else
    PureCopy_Information\SubDirectories = SubDirectories
  EndIf
  
  If SetFileProperties = #PB_Ignore
    PureCopy_Information\SetFileProperties = #PURECOPY_PROPERTY_CREATED | #PURECOPY_PROPERTY_ACCESSED | #PURECOPY_PROPERTY_MODIFIED | #PURECOPY_PROPERTY_ATTRIBUTES
  Else
    PureCopy_Information\SetFileProperties = SetFileProperties
  EndIf
  
  If CompareRoutine = #PB_Ignore
    PureCopy_Information\CompareRoutine = 0
  Else
    PureCopy_Information\CompareRoutine = CompareRoutine
  EndIf
  
  If WaitOnError = #PB_Ignore Or WaitOnError = 0
    PureCopy_Information\WaitOnAllErrors = #False
  Else
    PureCopy_Information\WaitOnAllErrors = #True
  EndIf
  
  If SimultaneousTransfer = #PB_Ignore Or SimultaneousTransfer = 0
    PureCopy_Information\SimultaneousTransfer = #False
  Else
    PureCopy_Information\SimultaneousTransfer = #True
  EndIf
  
  
  If *ProgressCallback = #PB_Ignore
    CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
      PureCopy_Information\ProgressCallBack = @PureCopy_DefaultCallback_ProgressCallback()
      
      PureCopy_DefaultProgressWindow\InitStatus = PureCopy_DefaultCallback_CreateProgressWindow(PureCopy_Information, PureCopy_DefaultProgressWindow)
    CompilerElse
      PureCopy_Information\ProgressCallBack = 0
    CompilerEndIf
  Else
    PureCopy_Information\ProgressCallBack = *ProgressCallback
  EndIf
  
  
  If *ErrorCallback = #PB_Ignore
    CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
      If PureCopy_DefaultProgressWindow\InitStatus = #PURECOPY_OK
        PureCopy_Information\ErrorCallBack = @PureCopy_DefaultCallback_ErrorCallback()
      Else
        PureCopy_Information\ErrorCallBack = 0
      EndIf
    CompilerElse
      PureCopy_Information\ErrorCallBack = 0
    CompilerEndIf
  Else
    PureCopy_Information\ErrorCallBack = *ErrorCallback
  EndIf
  
  
  If *CompareCallback = #PB_Ignore
    CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
      If PureCopy_DefaultProgressWindow\InitStatus = #PURECOPY_OK
        PureCopy_Information\CompareCallBack = @PureCopy_DefaultCallback_CompareCallback()
      Else
        PureCopy_Information\CompareCallBack = 0
      EndIf
    CompilerElse
      PureCopy_Information\CompareCallBack = 0
    CompilerEndIf
  Else
    PureCopy_Information\CompareCallBack = *CompareCallback
  EndIf
  
  If *ReadOnlyCallback = #PB_Ignore
    CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
      If PureCopy_DefaultProgressWindow\InitStatus = #PURECOPY_OK
        PureCopy_Information\ReadOnlyCallBack = @PureCopy_DefaultCallback_ReadOnlyCallback()
      Else
        PureCopy_Information\ReadOnlyCallBack = 0
      EndIf
    CompilerElse
      PureCopy_Information\ReadOnlyCallBack = 0
    CompilerEndIf
  Else
    PureCopy_Information\ReadOnlyCallBack = *ReadOnlyCallback
  EndIf
  
  
  
  If TransferBufferSize = #PB_Ignore
    TransferBufferSize = 4096 * 64
  EndIf
  
  If SimultaneousBufferCount = #PB_Ignore
    SimultaneousBufferCount = 32
  EndIf
  
  If PureCopy_Information\SimultaneousTransfer = #True
    PureCopy_Information\TransferBuffer = AllocateMemory(TransferBufferSize * SimultaneousBufferCount)
  Else
    PureCopy_Information\TransferBuffer = AllocateMemory(TransferBufferSize)
  EndIf
  PureCopy_Information\TransferBufferSize = TransferBufferSize
  PureCopy_Information\SimultaneousBufferSize = TransferBufferSize * SimultaneousBufferCount
  
  PureCopy_Information\BytesProcessed = #PURECOPY_INTERNAL_COUNTSIZE
  PureCopy_Information\BytesTotal = 0
  
  
  
  FilesCount = CountString(SourcePathComplete, #LF$) + 1
  
  For FileIndex = 1 To FilesCount
    SourcePath = StringField(SourcePathComplete, FileIndex, #LF$)
    
    
    Select FileSize(SourcePath)
    Case -2
      If Right(SourcePath, 1) = #PURECOPY_EXPECTEDSLASH
        SourcePath = Left(SourcePath, Len(SourcePath) - 1)
      EndIf
      
      CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
        Result = PureCopy_Internal_CountSize(SourcePath, PureCopy_Information, PureCopy_DefaultProgressWindow)
      CompilerElse
        Result = PureCopy_Internal_CountSize(SourcePath, PureCopy_Information, *UserInformation)
      CompilerEndIf
      
      
    Default
      PureCopy_Information\BytesTotal + FileSize(SourcePath)
      
    EndSelect
  Next
  
  
  If Result <> #PURECOPY_USERCANCELED
    PureCopy_Information\BytesProcessed = 0
    MakeSureDirectoryPathExists_(GetPathPart(DestinationDirectory)) ; Has to be replaced by another Procedure on Non-Windows OS!
    
    If Right(DestinationDirectory, 1) = #PURECOPY_EXPECTEDSLASH
      DestinationDirectory = Left(DestinationDirectory, Len(DestinationDirectory) - 1)
    EndIf
    
    
    
    For FileIndex = 1 To FilesCount
      SourcePath = StringField(SourcePathComplete, FileIndex, #LF$)
      
      Select FileSize(SourcePath)
      Case -1
        Result = #PURECOPY_ERROR_NOTFOUND
        
        If PureCopy_Information\ErrorCallBack
          CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
            If CallFunctionFast(PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_NOTFOUND, @SourcePath, PureCopy_Information, PureCopy_DefaultProgressWindow) = #False
          CompilerElse
            If CallFunctionFast(PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_NOTFOUND, @SourcePath, PureCopy_Information, *UserInformation) = #False
          CompilerEndIf
            
            Result = #PURECOPY_USERCANCELED
          EndIf
        EndIf
        
        
      Case -2
        If Right(SourcePath, 1) = #PURECOPY_EXPECTEDSLASH
          SourcePath = Left(SourcePath, Len(SourcePath) - 1)
        EndIf
        
        
        If FindString(LCase(DestinationDirectory), LCase(SourcePath), 1)
          Result = #PURECOPY_ERROR_DIRECTORIESMATCH
          
          If PureCopy_Information\ErrorCallBack
            CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
              If CallFunctionFast(PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_DIRECTORIESMATCH, @SourcePath, PureCopy_Information, PureCopy_DefaultProgressWindow) = #False
            CompilerElse
              If CallFunctionFast(PureCopy_Information\ErrorCallBack, #PURECOPY_ERROR_DIRECTORIESMATCH, @SourcePath, PureCopy_Information, *UserInformation) = #False
            CompilerEndIf
              
              Result = #PURECOPY_USERCANCELED
            EndIf
          EndIf
        Else
          CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
            If GetPathPart(SourcePath) = DestinationDirectory + #PURECOPY_EXPECTEDSLASH
              Result = PureCopy_Internal_DoDirectory(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + "Copy of " + RemoveString(GetFilePart(SourcePath), ":"), PureCopy_Information, PureCopy_DefaultProgressWindow)
            Else
              Result = PureCopy_Internal_DoDirectory(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + RemoveString(GetFilePart(SourcePath), ":"), PureCopy_Information, PureCopy_DefaultProgressWindow)
            EndIf
          CompilerElse
            If SourcePath = DestinationDirectory
              Result = PureCopy_Internal_DoDirectory(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + "Copy of " + RemoveString(GetFilePart(SourcePath), ":"), PureCopy_Information, *UserInformation)
            Else
              Result = PureCopy_Internal_DoDirectory(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + RemoveString(GetFilePart(SourcePath), ":"), PureCopy_Information, *UserInformation)
            EndIf
          CompilerEndIf
        EndIf
        
        
      Default
        CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
          If SourcePath = DestinationDirectory + #PURECOPY_EXPECTEDSLASH +  GetFilePart(SourcePath)
            Result = PureCopy_Internal_DoFile(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + "Copy of " + GetFilePart(SourcePath), PureCopy_Information, PureCopy_DefaultProgressWindow)
          Else
            Result = PureCopy_Internal_DoFile(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + GetFilePart(SourcePath), PureCopy_Information, PureCopy_DefaultProgressWindow)
          EndIf
        CompilerElse
          If SourcePath = DestinationDirectory + #PURECOPY_EXPECTEDSLASH +  GetFilePart(SourcePath)
            Result = PureCopy_Internal_DoFile(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + "Copy of " + GetFilePart(SourcePath), PureCopy_Information, *UserInformation)
          Else
            Result = PureCopy_Internal_DoFile(SourcePath, DestinationDirectory + #PURECOPY_EXPECTEDSLASH + GetFilePart(SourcePath), PureCopy_Information, *UserInformation)
          EndIf
        CompilerEndIf
        
      EndSelect
      
      If Result = #PURECOPY_USERCANCELED
        Break
      EndIf
    Next
    
    FreeMemory(PureCopy_Information\TransferBuffer)
  EndIf
  
  
  CompilerIf #PURECOPY_COMPILE_WITH_DEFAULT_CALLBACK
    If *ProgressCallback = #PB_Ignore
      If PureCopy_DefaultProgressWindow\InitStatus = #PURECOPY_OK
        If CountGadgetItems(PureCopy_DefaultProgressWindow\ErrorList) = 0
          CloseWindow(PureCopy_DefaultProgressWindow\ProgressWindow)
        Else
          SetGadgetText(PureCopy_DefaultProgressWindow\Cancel, "Close")
          
          HideGadget(PureCopy_DefaultProgressWindow\SkipFile, 1)
          HideGadget(PureCopy_DefaultProgressWindow\Pause, 1)
          
          HideGadget(PureCopy_DefaultProgressWindow\ErrorContinue, 1)
          HideGadget(PureCopy_DefaultProgressWindow\ErrorContinueAlways, 1)
          ResizeGadget(PureCopy_DefaultProgressWindow\ErrorList, #PB_Ignore, #PB_Ignore, #PB_Ignore, 170)
          
          SetGadgetText(PureCopy_DefaultProgressWindow\Text_Input, SourcePath)
          SetGadgetText(PureCopy_DefaultProgressWindow\Text_Output, DestinationDirectory)
          SetGadgetText(PureCopy_DefaultProgressWindow\Text_Status, "Finished")
          
          HideGadget(PureCopy_DefaultProgressWindow\LabelRemain, 1)
          HideGadget(PureCopy_DefaultProgressWindow\Text_RemainingTime, 1)
          
          HideGadget(PureCopy_DefaultProgressWindow\LabelFile, 1)
          HideGadget(PureCopy_DefaultProgressWindow\ProgressFile, 1)
          
          SetGadgetState(PureCopy_DefaultProgressWindow\ProgressTotal, 100)
          SetGadgetState(PureCopy_DefaultProgressWindow\ProgressFile, 100)
          
          SetGadgetText(PureCopy_DefaultProgressWindow\TextFile, "Total: " + PureCopy_DefaultCallback_ReturnBestSize(PureCopy_Information\BytesTotal))
          If PureCopy_Information\SimultaneousTransfer
            HideGadget(PureCopy_DefaultProgressWindow\LabelBuffer, 1)
            HideGadget(PureCopy_DefaultProgressWindow\ProgressBuffer, 1)
            HideGadget(PureCopy_DefaultProgressWindow\Text1Buffer, 1)
            HideGadget(PureCopy_DefaultProgressWindow\Text2Buffer, 1)
          EndIf
          
          Repeat
            EventID = WaitWindowEvent()
            
            Select EventID
            Case #PB_Event_CloseWindow
              Break
              
            Case #PB_Event_Gadget
              Select EventGadget()
              Case PureCopy_DefaultProgressWindow\Cancel
                Break
                
              EndSelect
              
            EndSelect
            
          ForEver
          
          CloseWindow(PureCopy_DefaultProgressWindow\ProgressWindow)
        EndIf
      EndIf
    EndIf
  CompilerEndIf
  
  ProcedureReturn Result
EndProcedure
Procedure OnErrorProcedure()
  MessageRequester("Error", ErrorMessage() + "(" + GetFilePart(ErrorFile()) + " line " + Str(ErrorLine()) + ")")
EndProcedure
OnErrorCall(@OnErrorProcedure())
CompilerIf #PURECOPY_COMPILE_WITH_INCLUDE_EXECUTABLE
  Define Parameter.s = ""
  Define a.l
  
  For a = 1 To CountProgramParameters()
    Parameter + ProgramParameter(a - 1) + " "
  Next
  
  Parameter = Trim(Parameter)
  
  Define InputFiles.s = StringField(Parameter, 1, Chr(1))
  Define OutputFilepath.s = StringField(Parameter, 2, Chr(1))
  Define SubDirectories.l = Val(StringField(Parameter, 3, Chr(1)))
  Define SetFileProperties.l = Val(StringField(Parameter, 4, Chr(1)))
  Define CompareRoutines.l = Val(StringField(Parameter, 5, Chr(1)))
  Define WaitOnError.l = Val(StringField(Parameter, 6, Chr(1)))
  Define SimultaneousTransfer.l = Val(StringField(Parameter, 7, Chr(1)))
  
  PureCopy(InputFiles, OutputFilepath, 0, SubDirectories, SetFileProperties, CompareRoutines, #PB_Ignore, #PB_Ignore, WaitOnError, SimultaneousTransfer)
CompilerEndIf