; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Files manipulation library.
;
;  Target OS: Any
;
;  Dependencies: Uses memory.asm for memory allocations.
;
;  Notes: This file contains OS independent part of the library and also include
;         the respective OS dependent part.
;_________________________________________________________________________________________
module "Files library"

struct TFileInfo
  .timeCreated   dq ?
  .timeAccessed  dq ?
  .timeModified  dq ?
ends

iglobal
__std_handle_in:
  var STDIN  = 0
__std_handle_out:
  var STDOUT = 1
__std_handle_err:
  var STDERR = 2
endg



interface FileOpenAccess, .filename, .access

interface FileClose, .handle

; returns 32bit file size in eax
interface FileSize, .handle

interface FileRead, .handle, .buffer, .count

interface FileWrite, .handle, .buffer, .count

;----------------------------------------------------
; if CF = 0, eax is the new possition in the file
; if CF = 1, eax is error code.
;----------------------------------------------------
interface FileSeek, .handle, .dist, .direction

interface FileTruncate, .handle

;----------------------------------------------------
; if CF = 0 - the file was deleted
; if CF = 1, eax is error code.
;----------------------------------------------------
interface FileDelete, .filename

interface GetCurrentDir

interface SetCurrentDir, .hDirectory

interface GetErrorString, .code

interface FreeErrorString, .ptrString

; 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;

interface DirectoryRead, .dir


interface GetFileInfo, .hFile, .ptrFileInfo






;--------------------------------------
; if CF = 0, eax is handle to the file.
; if CF = 1, eax is error code.
;--------------------------------------
proc FileOpen, .filename
begin
        stdcall FileOpenAccess, [.filename], faReadOnly
        return
endp


proc FileCreate, .filename
begin
        stdcall FileOpenAccess, [.filename], faReadWrite or faCreateAlways
        return
endp


;if defined StrLib
; strlib.asm depending procedures.


; reads the whole file content in a string and returns its handle.
proc StrLoadFromFile, .ptrFileName
begin
        push    ecx

        stdcall LoadBinaryFile, [.ptrFileName]
        jc      .finish

        push    eax
        stdcall StrDup, eax
        stdcall FreeMem ; from the stack

        clc

.finish:
        pop     ecx
        return
endp



proc StrSaveToFile, .ptrFileName, .hString
begin
        stdcall StrLen, [.hString]
        push    eax
        stdcall StrPtr, [.hString]
        push    eax
        stdcall SaveBinaryFile, [.ptrFileName] ; remaining already in the stack.
        return
endp


; reads a line from the text file and positions the file pointer to the begining of
; the next line.
; returns string handle with the line in EAX
; returns CF=1 and EAX=error code on error.

proc FileReadLine, .hfile
.result dd ?
.pbuffer dd ?
begin
        pushad

        stdcall StrNew
        mov     [.result], eax

        stdcall GetMem, 1028
        mov     [.pbuffer], eax

.read_loop:
        mov     esi, [.pbuffer]
        stdcall FileRead, [.hfile], esi, 1024
        jc      .error_read
        test    eax, eax
        jz      .end_of_file

        mov     ebx, eax
        mov     dword [esi+ebx], 0

        cmp     word [esi], $bbef
        jne     .proceed
        cmp     byte [esi+2], $bf
        jne     .proceed

        add     esi, 3

.proceed:
        mov     edi, esi        ; start of the line

.char_loop:
        mov     al, [esi]
        lea     esi, [esi+1]

        cmp     al, $0d
        je      .found
        cmp     al, $0a
        je      .found
        test    al, al
        jnz     .char_loop

.found:
        mov     byte [esi-1], 0
        stdcall StrCat, [.result], edi

        test    al, al
        jnz     .end_of_line
        cmp     ebx, 1024
        je      .read_loop
        jmp     .file_ok

.end_of_line:
        xor     al, $0d xor $0a
        cmp     byte [esi], al
        jne     .eol_ok
        inc     esi
.eol_ok:
        sub     esi, [.pbuffer]
        sub     esi, ebx
        jz      .file_ok

        stdcall FileSeek, [.hfile], esi, fsFromCurrent

.file_ok:
        stdcall FreeMem, [.pbuffer]
        clc

.finish:
        popad
        mov     eax, [.result]
        return

.error_read:
        stdcall StrDel, [.result]
        stdcall FreeMem, [.pbuffer]
        mov     [.result], eax
        stc
        jmp     .finish

.end_of_file:
        stdcall StrLen, [.result]
        test    eax, eax
        jnz     .file_ok

        stdcall StrDel, [.result]
        mov     [.result], 0
        jmp     .file_ok
endp



proc FileWriteString, .file, .hString
begin
        push    ecx

        stdcall StrLen, [.hString]
        mov     ecx, eax
        stdcall StrPtr, [.hString]
        stdcall FileWrite, [.file], eax, ecx

        pop     ecx
        return
endp


;end if






;--------------------------------------------------------------------
; Allocates needed memory and loads the entire file in this memory.
; Returns pointer to memory block or NULL if there is error.
; Returns size in ecx
;--------------------------------------------------------------------
proc LoadBinaryFile, .ptrFileName
begin
        push    esi edi ebx

        stdcall FileOpenAccess, [.ptrFileName], faReadOnly
        jc      .error

        mov     edi, eax

        stdcall FileSize, edi
        jc      .errorclose

        mov     ebx, eax
        lea     ecx, [eax+8]    ; It is for the case that the file is
                                ; text and we want to use it as null terminated
                                ; string.

        stdcall GetMem, ecx
        jc      .errorclose
        mov     esi, eax

        stdcall FileRead, edi, esi, ebx
        jc      .errorclosemem

        mov     ecx, eax

        stdcall FileClose, edi

                mov     eax, esi
        clc
        pop     ebx edi esi
        return

.errorclosemem:
        stdcall FreeMem, esi

.errorclose:
        push    eax
        stdcall FileClose, edi
        pop     eax

.error:
        xor     ecx, ecx
        stc
        pop     ebx edi esi
        return
endp



;--------------------------------------------------------------------
; Saves to the file [size] bytes from given [ptr] address.
; returns CF=1 if error and CF=0 if OK.
;--------------------------------------------------------------------
proc SaveBinaryFile, .ptrFileName, .aptr, .size
begin
        push    edi

        stdcall FileCreate, [.ptrFileName]
        jc      .error

        mov     edi, eax
        stdcall FileWrite, edi, [.aptr], [.size]
        jc      .errorclose

        push    eax
        stdcall FileClose, edi
        pop     eax

        clc
        pop     edi
        return

.errorclose:
        push    eax
        stdcall FileClose, edi
        pop     eax

.error:
        stc
        pop     edi
        return
endp




; Returns:
; CF=1 if the file NOT EXISTS
; CF=0 if the file EXISTS

proc FileExists, .ptrFileName
begin
        push    eax

        stdcall FileOpen, [.ptrFileName]
        jc      .exit
        stdcall FileClose, eax
        clc
.exit:
        pop     eax
        return
endp




; This structure contains one directory item information.
;

struct TDirItem
  .hFilename dd ?       ; handle of string with the filename.
  .Type      dd ?       ; type of the file. Value of type ftXXXXXX (defined in the OS depending library).
ends



proc FreeDirArray, .pDirArray
begin
        push    eax ecx

        mov     eax, [.pDirArray]
        mov     ecx, [eax+TArray.count]

.freelist:
        dec     ecx
        js      .exit

        stdcall StrDel, [eax+TArray.array+sizeof.TDirItem*ecx+TDirItem.hFilename]
        jmp     .freelist
.exit:
        stdcall FreeMem, eax
        pop     ecx eax
        return
endp



fdsByName = 1
fdsByType = 2

fdsDescending = $80

proc SortDirArray, .pDirArray, .HowToSort
begin
        push    eax ebx ecx esi

        mov     esi, [.pDirArray]
        xor     ecx, ecx
.outer:

        mov     ebx, ecx
.inner:
        inc     ebx
        cmp     ebx, [esi+TArray.count]
        jae     .next_outer

        test    [.HowToSort], fdsByType
        jz      .by_name

        mov     eax, [esi+TArray.array+8*ecx+TDirItem.Type]
        cmp     eax, [esi+TArray.array+8*ebx+TDirItem.Type]
        ja      .swap
        jb      .inner

.by_name:
        test    [.HowToSort], fdsByName
        jz      .inner

        stdcall StrCompSort2, [esi+TArray.array+8*ecx+TDirItem.hFilename], [esi+TArray.array+8*ebx+TDirItem.hFilename], FALSE
        test    [.HowToSort], fdsDescending
        jnz     @f
        neg     eax
@@:
        test    eax, eax
        jns     .inner

.swap:
        push    [esi+TArray.array+8*ecx+TDirItem.hFilename]
        push    [esi+TArray.array+8*ecx+TDirItem.Type]

        push    [esi+TArray.array+8*ebx+TDirItem.hFilename]
        push    [esi+TArray.array+8*ebx+TDirItem.Type]

        pop     [esi+TArray.array+8*ecx+TDirItem.Type]
        pop     [esi+TArray.array+8*ecx+TDirItem.hFilename]

        pop     [esi+TArray.array+8*ebx+TDirItem.Type]
        pop     [esi+TArray.array+8*ebx+TDirItem.hFilename]
        jmp     .inner

.next_outer:
        inc     ecx
        mov     eax, [esi+TArray.count]
        dec     eax
        cmp     ecx, eax
        jb      .outer

        pop     esi ecx ebx eax
        return

endp




proc __FileErrorDisplay, .error_code, .filename
begin
        pushad

        stdcall NumToStr, [.error_code], ntsDec or ntsUnsigned
        mov     ebx, eax

        stdcall StrDupMem, .cErrorOpenString
        push    eax
        stdcall StrCat, eax, ebx
        stdcall StrCharCat, eax, $0a0d
        stdcall StrDel, ebx

        stdcall StrPtr, eax
        stdcall Output, eax
        stdcall StrDel ; from the stack.

        stdcall GetErrorString, [.error_code]
        push    eax
        stdcall StrPtr, eax
        stdcall Output, eax
        stdcall Output, .ccrlf
        stdcall FreeErrorString ; from the stack

        cmp     [.filename], 0
        je      .finish

        stdcall Output, .cErrorFilename
        stdcall StrPtr, [.filename]
        stdcall Output, eax
        stdcall Output, .ccrlf

.finish:
        popad
        return

        .cErrorOpenString db "Error code:", 0
        .cErrorFilename   db "Filename: ", 0
        .ccrlf            db 13, 10, 0
endp


include '%TargetOS%/files.asm'

endmodule