iglobal
  cOpenFileTitle    du 'Open file:', 0
  cSaveProjectTitle du 'Save project as:', 0
  cSaveFileTitle    du 'Save file as:', 0
  cBackSlash        text '\'

  ProjFilter:
        du 'Fresh project (*.fpr)', 0, '*.fpr',0
        du 'All files (*.*)', 0, '*.*',0
        du 0

  AllFilter:
        du 'All Fresh files (*.fpr, *.asm, *.inc, *.frm)', 0, '*.fpr;*.asm;*.inc;*.frm',0
        du 'Fresh project (*.fpr)', 0, '*.fpr',0
        du 'Form (*.frm)', 0, '*.frm',0
        du 'Assembler files (*.asm, *.inc)', 0, '*.asm;*.inc', 0
;        du 'Bitmaps (*.bmp)', 0, '*.bmp',0
        du 'All files (*.*)', 0, '*.*',0
        du 0

  FileFilter:
        du 'Assembler files (*.asm, *.inc)', 0, '*.asm;*.inc', 0
        du 'All files (*.*)', 0, '*.*',0
        du 0

  FormFilter:
        du 'Form (*.frm)', 0, '*.frm',0
        du 'All files (*.*)', 0, '*.*', 0
        du 0

;  GraphicFilter:
;        du 'Bitmap (*.bmp)', 0, '*.bmp',0
;        du 'All files (*.*)', 0, '*.*', 0
;        du 0

;  WatchFilter:
;        du 'Memory watch table (*.mwt)', 0, '*.mwt',0
;        du 'All files (*.*)', 0, '*.*', 0
;        du 0
endg


cFilenameBufferSize = 4096


proc OpenFilesDialog, .hOwner, .ptrTitle, .MultiSelect, .ptrFilter, .ptrCallBack, .hFileName, .lParam
.opn OPENFILENAME
.errcnt dd ?

.pStr dd ?
begin
        pushad

        xor     eax, eax
        mov     [esp+4*regEAX], eax

        mov     [.errcnt], eax

; Allocate memory for filename buffer.
        stdcall GetMem, cFilenameBufferSize*2
        mov     edi, eax

        mov     [.opn.lpstrInitialDir], 0

        cmp     [.hFileName], 0
        je      .setstructure

; process the filename/path

        stdcall GetFullPathNameUtf8, [.hFileName]
        mov     esi, eax

        stdcall StrSplitFilename, esi
        mov     ebx, eax

        stdcall utf8ToWideChar, esi
        stdcall StrDel, esi
        mov     [.opn.lpstrInitialDir], eax     ; the directory.

        stdcall utf8ToWideChar, ebx     ; the filename
        stdcall StrDel, ebx
        mov     esi, eax

; copy the filename
        push    edi esi

        rep movsw

        xor     eax, eax
        stosd

        pop     esi edi

        stdcall FreeMem, esi

.setstructure:
        xor     edx, edx
        mov     [.opn.lStructSize], sizeof.OPENFILENAME
        mov     eax, [.hOwner]
        mov     [.opn.hwndOwner], eax
        mov     [.opn.hInstance], edx
        mov     eax, [.ptrFilter]
        mov     [.opn.lpstrFilter], eax
        mov     [.opn.lpstrCustomFilter], edx
        mov     [.opn.nFilterIndex], 1
        mov     [.opn.lpstrFile], edi
        mov     [.opn.nMaxFile], cFilenameBufferSize
        mov     [.opn.lpstrFileTitle], edx
        mov     [.opn.nMaxFileTitle], edx
        mov     eax, [.ptrTitle]
        mov     [.opn.lpstrTitle], eax
        mov     [.opn.Flags], OFN_HIDEREADONLY or OFN_EXPLORER or    \
                              OFN_NOCHANGEDIR or OFN_FILEMUSTEXIST or OFN_LONGNAMES

        cmp     [.MultiSelect], FALSE
        je      @f
        or      [.opn.Flags], OFN_ALLOWMULTISELECT
@@:
        mov     [.opn.lpstrDefExt], edx
        mov     [.opn.lCustData], edx
        mov     [.opn.lpfnHook], edx

.retry:
        lea     eax, [.opn]
        invoke  GetOpenFileNameW, eax
        test    eax, eax
        jnz     .process_no_error

; Error open dialog or simply cancel...
        invoke  CommDlgExtendedError
        test    eax, eax
        jz      .finish

        cmp     eax, $3002
        jne     .errbox

        cmp     [.errcnt], 0
        jne     .errbox

        stdcall FreeMem, [.opn.lpstrInitialDir]
        mov     [.opn.lpstrInitialDir], 0
        inc     [.errcnt]
        jmp     .retry

.errbox:
        stdcall ErrorMessage, errOpenDialog
        jmp     .finish

.process_no_error:
; Get file(s) path.

        movzx   esi, [.opn.nFileOffset]
        lea     esi, [edi+2*esi]

        cmp     word [esi-2], 0                 ; if 0 then more than one file is selected.
        je      .multyfile                      ; The ebx string contains the filename.

; only one file to read:

        stdcall WideCharToUtf8, edi

        cmp     [.ptrCallBack], 0
        jne     .callback

        mov     [esp+4*regEAX], eax
        jmp     .finish


.callback:
        push    eax
        stdcall [.ptrCallBack], eax, [.lParam]
        stdcall StrDel ; from the stack
        jmp     .finish

.multyfile:
        cmp     word [esi], 0
        je      .endfiles

        stdcall WideCharToUtf8, edi
        mov     ebx, eax

        stdcall StrCat, ebx, cBackSlash

        stdcall WideCharToUtf8, esi
        stdcall StrCat, ebx, eax
        stdcall StrDel, eax

        stdcall [.ptrCallBack], ebx, [.lParam]

        stdcall StrDel, ebx

.findend:
        lodsw
        test    ax, ax
        jnz     .findend

        jmp     .multyfile

.endfiles:

.finish:
;Free filename buffer
        stdcall FreeMem, [.opn.lpstrInitialDir]
        stdcall FreeMem, edi

        popad
        return

endp






proc SaveFileDialog, .hOwner, .ptrTitle, .ptrFilter, .hFileName, .hInitialDir
.opn OPENFILENAME
begin
        push    esi edi ebx

        stdcall GetMem, cFilenameBufferSize
        mov     edi, eax

        stdcall utf8ToWideChar, [.hFileName]
        mov     esi, eax

        push    edi

        rep movsw
        xor eax, eax
        stosd

        pop     edi

        mov     eax, [.hInitialDir]
        test    eax, eax
        jz      .initial_dir_ok

        stdcall FreeMem, esi
        stdcall utf8ToWideChar, eax
        mov     esi, eax

.initial_dir_ok:
        mov     [.opn.lpstrInitialDir], eax

        xor     edx, edx
        mov     [.opn.lStructSize], sizeof.OPENFILENAME

        mov     eax, [.hOwner]
        mov     [.opn.hwndOwner], eax

        mov     [.opn.hInstance], edx
        mov     eax, [.ptrFilter]
        mov     [.opn.lpstrFilter], eax
        mov     [.opn.lpstrCustomFilter], edx
        mov     [.opn.nFilterIndex], 1
        mov     [.opn.lpstrFile], edi
        mov     [.opn.nMaxFile], cFilenameBufferSize/2
        mov     [.opn.lpstrFileTitle], edx
        mov     [.opn.nMaxFileTitle], edx
        mov     eax, [.ptrTitle]
        mov     [.opn.lpstrTitle], eax
        mov     [.opn.Flags], OFN_EXPLORER or           \
                              OFN_HIDEREADONLY or       \
                              OFN_NOREADONLYRETURN or   \
                              OFN_NOCHANGEDIR

        mov     [.opn.lpstrDefExt], edx
        mov     [.opn.lCustData], edx
        mov     [.opn.lpfnHook], edx

        lea     eax, [.opn]
        invoke  GetSaveFileNameW, eax
        test    eax, eax
        jz      .finish

        stdcall WideCharToUtf8, edi

.finish:
        stdcall FreeMem, esi
        stdcall FreeMem, edi
        pop     ebx edi esi
        return
endp



proc SelectPathDialog, .hOwner, .pTitle, .hPath
.bi  BROWSEINFO
.str  rw 256
.path rw 1024
.IMalloc dd ?
begin
        push    esi edi

        mov     eax, [.hOwner]
        lea     esi, [.str]
        mov     [.bi.hwndOwner], eax
        mov     [.bi.pidlRoot], 0

        mov     [.bi.pszDisplayName], esi

        mov     eax, [.pTitle]
        mov     [.bi.lpszTitle], eax

        mov     [.bi.ulFlags], BIF_RETURNONLYFSDIRS or $40

        mov     [.bi.lpfn], _BrowseFolderProc

        lea     eax, [.hOwner]
        mov     [.bi.lParam], eax

        lea     eax, [.bi]
        invoke  SHBrowseForFolderW, eax
        test    eax, eax
        jz      .finish

        push    eax

        lea     edi, [.path]
        xor     esi, esi
        invoke  SHGetPathFromIDListW, eax, edi
        test    eax, eax
        jz      .memfree

        stdcall WideCharToUtf8, edi
        mov     esi, eax

.memfree:
        invoke  CoTaskMemFree ; From the stack
        mov     eax, esi

.finish:
        pop     edi esi
        return
endp


proc _BrowseFolderProc, .hwnd, .wmsg, .lparam, .lpData
begin
        push    esi
        cmp     [.wmsg], BFFM_INITIALIZED
        jne     .finish

        mov     esi, [.lpData] ; This is pointer to arguments of SelectPathDialog

        invoke  SendMessageW, [.hwnd], WM_SETTEXT, 0, [esi+4]

        stdcall utf8ToWideChar, [esi+8]  ; .hPath
        push    eax

        invoke  SendMessageW, [.hwnd], BFFM_SETSELECTIONW, TRUE, eax

        stdcall FreeMem ; from the stack

.finish:
        xor     eax, eax
        pop     esi
        return
endp