; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Files manipulation library. Win32 part.
;
;  Target OS: Win32
;
;  Dependencies:
;
;  Notes:
;_________________________________________________________________________________________

uses kernel32

DIR_SLASH equ '\'

fsFromBegin     = FILE_BEGIN
fsFromEnd       = FILE_END
fsFromCurrent   = FILE_CURRENT

faReadOnly   = GENERIC_READ
faWriteOnly  = GENERIC_WRITE
faReadWrite  = GENERIC_READ or GENERIC_WRITE

faOpenAlways   = OPEN_ALWAYS      ; if the file does not exists it will be created.
faCreateAlways = CREATE_ALWAYS    ; Creates new file always. If the file exists, truncates it.
faCreateOnly   = CREATE_NEW       ; Creates new file only if not exists, else error.



if used STDIN | used STDOUT | used STDERR

  initialize InitStdHandles
  begin
    if used STDIN
          invoke  GetStdHandle, STD_INPUT_HANDLE
          mov     [__std_handle_in], eax
    end if

    if used STDOUT
          invoke  GetStdHandle, STD_OUTPUT_HANDLE
          mov     [__std_handle_out], eax
    end if

    if used STDERR
          invoke  GetStdHandle, STD_ERROR_HANDLE
          mov     [__std_handle_err], eax
    end if
          return
  endp

end if


body FileOpenAccess
begin
        push    edx ecx

        stdcall utf8ToWideChar, [.filename]
        push    eax

        mov     ecx, [.access]
        mov     edx, [.access]
        and     ecx, $ffff0000
        and     edx, $0000ffff
        jnz     @f
        mov     edx, OPEN_EXISTING
@@:
        invoke  CreateFileW, eax, ecx, FILE_SHARE_READ, 0, edx, 0, 0
        stdcall FreeMem ; from the stack

        cmp     eax, INVALID_HANDLE_VALUE
        je      .error

        clc
        pop     ecx edx
        return

.error:
        invoke  GetLastError

if defined options.DebugMode & options.DebugMode
        stdcall __FileErrorDisplay, eax, [.filename]
end if

        stc
        pop     ecx edx
        return
endp




body FileClose
begin
        push    edx ecx eax
        invoke  CloseHandle, [.handle]
        test    eax, eax
        jz      .error

        clc
        pop     eax ecx edx
        return

.error:
        pop     eax
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     ecx edx
        return
endp


; returns 32bit file size in eax
body FileSize
begin
        push    edx ecx

        xor     eax, eax
        invoke  GetFileSize, [.handle], eax
        cmp     eax, -1
        je      .error

        clc
        pop     ecx edx
        return

.error:
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     ecx edx
        return
endp



body FileRead
.read dd ?
begin
        push    edx ecx

        lea     eax, [.read]
        invoke  ReadFile, [.handle], [.buffer], [.count], eax, 0
        test    eax, eax
        jz      .error

        mov     eax, [.read]
        clc
        pop     ecx edx
        return

.error:
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     ecx edx
        return
endp




body FileWrite
.bytes dd ?
begin
        push    edx ecx

        lea     ecx, [.bytes]
        invoke  WriteFile, [.handle], [.buffer], [.count], ecx, NULL
        test    eax, eax
        jz      .error

        mov     eax, [.bytes]
        clc
        pop     ecx edx
        return

.error:
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     ecx edx
        return
endp




;----------------------------------------------------
; if CF = 0, eax is the new possition in the file
; if CF = 1, eax is error code.
;----------------------------------------------------
body FileSeek
begin
        push    edx ecx
        invoke  SetFilePointer, [.handle], [.dist], 0, [.direction]
        cmp     eax, -1
        je      .error

        clc
        pop     ecx edx
        return

.error:
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     ecx edx
        return
endp




body FileTruncate
begin
        invoke  SetEndOfFile, [.handle]
        return
endp



;----------------------------------------------------
; if CF = 0 - the file was deleted
; if CF = 1, eax is error code.
;----------------------------------------------------
body FileDelete
begin
        push    ecx edx eax

        stdcall utf8ToWideChar, [.filename]
        push    eax
        invoke  DeleteFileW, eax
        stdcall FreeMem ; from the stack

        test    eax, eax
        jz      .error
        clc
        pop     eax edx ecx
        return

.error:
        pop     eax
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     edx ecx
        return
endp





body GetCurrentDir
.buffer rb 1024
begin
        push    ecx edx esi

        lea     esi, [.buffer]
        invoke  GetCurrentDirectoryW, 1024, esi
        test    eax, eax
        jz      .error

        stdcall WideCharToUtf8, esi
        clc

.finish:
        pop     esi edx ecx
        return

.error:
if used GetErrorString
        invoke  GetLastError
end if

        stc
        jmp     .finish

endp



body SetCurrentDir
begin
        push    ecx edx eax
        stdcall utf8ToWideChar, [.hDirectory]
        push    eax
        invoke  SetCurrentDirectoryW, eax
        stdcall FreeMem ; from the stack

        test    eax, eax
        jz      .error

        pop     eax edx ecx
        return

.error:
        pop     eax
if used GetErrorString
        invoke  GetLastError
end if
        stc
        pop     edx ecx
        return
.finish:

endp



body GetErrorString
.buff dd ?
begin
        push    edx ecx
        lea     eax, [.buff]
        invoke  FormatMessageW, FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM, 0, [.code], LANG_NEUTRAL or SUBLANG_DEFAULT, eax, 0, 0

        stdcall WideCharToUtf8, [.buff]
        push    eax

        invoke  LocalFree, [.buff]

        pop     eax
        clc
        pop     ecx edx
        return
endp



body FreeErrorString
begin
        stdcall StrDel, [.ptrString]
        return
endp



ftUnknown    =  0
ftDirectory  =  1
ftFile       =  2



; reads all items of the directory and fills them in the TArray of type TDirItem.
; arguments:
;   dir - string with the path to the directory.
;
; returns:
;   if not error: CF=0; eax = pointer to TArray of TDirItem;
;   if error:     CF=1; eax = error code;

body DirectoryRead
.finddata FINDDATAW
.size   dd ?
begin
        push    ebx ecx edx edi

; open the file
        stdcall StrDup, [.dir]
        push    eax
        stdcall StrCat, eax, .searchall

        stdcall utf8ToWideChar, eax
        stdcall StrDel ; from the stack

        push    eax

        lea     ecx, [.finddata]
        invoke  FindFirstFileW, eax, ecx
        stdcall FreeMem ; from the stack

        cmp     eax, INVALID_HANDLE_VALUE
        je      .error

        mov     ebx, eax

        stdcall CreateArray, sizeof.TDirItem
        mov     edi, eax

.addloop:
        lea     eax, [.finddata.cFileName]
        stdcall WideCharToUtf8, eax
        push    eax

        stdcall AddArrayItems, edi, 1
        mov     edi, edx

        pop     [eax+TDirItem.hFilename]

        mov     ecx, ftDirectory
        test    [.finddata.dwFileAttributes], FILE_ATTRIBUTE_DIRECTORY
        jnz     .typeok
        mov     ecx, ftFile
.typeok:
        mov     [eax+TDirItem.Type], ecx

        lea     eax, [.finddata]
        invoke  FindNextFileW, ebx, eax
        test    eax, eax
        jnz     .addloop

        invoke  GetLastError
        push    eax

.end_of_directory:

        invoke  FindClose, ebx

        pop     eax
        cmp     eax, ERROR_NO_MORE_FILES
        jne     .finish_error

        mov     eax, edi
        pop     edi edx ecx ebx
        clc
        return

.error:
        invoke  GetLastError

.finish_error:
        pop     edi edx ecx ebx
        stc
        return

.searchall db '\*.*', 0
endp




body GetFileInfo
begin
        pushad

        mov     esi, [.ptrFileInfo]
        lea     ecx, [esi+TFileInfo.timeAccessed]
        lea     edx, [esi+TFileInfo.timeModified]

        invoke  GetFileTime, [.hFile], esi, ecx, edx
        test    eax, eax
        jz      .error

        mov     eax, dword [esi+TFileInfo.timeCreated]
        mov     edx, dword [esi+TFileInfo.timeCreated+4]
        call    FileTimeToUnixTime
        mov     dword [esi+TFileInfo.timeCreated], eax
        mov     dword [esi+TFileInfo.timeCreated+4], edx

        mov     eax, dword [esi+TFileInfo.timeAccessed]
        mov     edx, dword [esi+TFileInfo.timeAccessed+4]
        call    FileTimeToUnixTime
        mov     dword [esi+TFileInfo.timeAccessed], eax
        mov     dword [esi+TFileInfo.timeAccessed+4], edx

        mov     eax, dword [esi+TFileInfo.timeModified]
        mov     edx, dword [esi+TFileInfo.timeModified+4]
        call    FileTimeToUnixTime
        mov     dword [esi+TFileInfo.timeModified], eax
        mov     dword [esi+TFileInfo.timeModified+4], edx

        clc
        popad
        return

.error:
        stc
        popad
        return

endp

