    /****************************************************************************/
    /*                                                                          */
    /*                 Copyright (C) 1987-1996 Microsoft Corp.                */
    /*                           All Rights Reserved                            */
    /*                                                                          */
    /****************************************************************************/
    /****************************** Module Header *******************************
    * Module Name: file.c
    *
    * Contains routines for handling files.
    *
    * History:
    *
    ****************************************************************************/
    
    #include "imagedit.h"
    #include "dialogs.h"
    
    #include <string.h>

	 #include <windowsx.h>
    #include <commdlg.h>
    
    
    #ifdef WIN16
    typedef BOOL (APIENTRY *LPOFNHOOKPROC) (HWND, UINT, WPARAM, LONG);
    #endif
    
    
    STATICFN VOID NEAR AddFilterString(PSTR pszBuf, PSTR pszType, PSTR pszExt,
        BOOL fFirst);
    STATICFN PSTR NEAR DefExtFromFilter(INT index, PSTR pszFilter);
    STATICFN BOOL NEAR LoadFile(PSTR pszFullFileName);
    STATICFN INT NEAR GetTypeFromExt(PSTR pszFileName);
    STATICFN VOID NEAR FileCat(PSTR pchName, PSTR pchCat);
    
    
    static OPENFILENAME ofn;
    
    
    
    /************************************************************************
    * SetFileName
    *
    * Updates the globals that contain the file name of the currently
    * loaded file.  This routine will also cause the title bar to
    * be udpated with the new name.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    VOID SetFileName(
        PSTR pszFullFileName)
    {
        CHAR szTitle[CCHMAXPATH];
        WIN32_FIND_DATA ffbuf;
        CHAR *pch;
    
        if (pszFullFileName) {
            HANDLE hfind;
            strcpy(gszFullFileName, pszFullFileName);
            gpszFileName = FileInPath(gszFullFileName);
    
            if((hfind = FindFirstFile( pszFullFileName, &ffbuf)) !=
                    INVALID_HANDLE_VALUE) {
    
                strcpy(gpszFileName, ffbuf.cFileName);
                FindClose(hfind);
            }
    
        }
        else {
            *gszFullFileName = '\0';
            gpszFileName = NULL;
        }
    
        strcpy(szTitle, ids(IDS_PGMTITLE));
        strcat(szTitle, " - ");
        pch = gpszFileName ? gpszFileName : ids(IDS_UNTITLED);
        strncat(szTitle, pch, sizeof(szTitle) - strlen(szTitle));
        szTitle[CCHMAXPATH-1] = '\0';
        SetWindowText(ghwndMain, szTitle);
    }
    
    
    
    /************************************************************************
    * FileInPath
    *
    * This function takes a path and returns a pointer to the file name
    * portion of it.  For instance, it will return a pointer to
    * "abc.res" if it is given the following path: "c:\windows\abc.res".
    *
    * Arguments:
    *   PSTR pstrPath - Path to look through.
    *
    * History:
    *
    ************************************************************************/
    
    PSTR FileInPath(
        PSTR pstrPath)
    {
        PSTR pstr;
    
        pstr = pstrPath + strlen(pstrPath);
        while (pstr > pstrPath) {
            pstr = FAR2NEAR(AnsiPrev(pstrPath, pstr));
            if (*pstr == '\\' || *pstr == ':' || *pstr == '/') {
                pstr = FAR2NEAR(AnsiNext(pstr));
                break;
            }
        }
    
        return pstr;
    }
    
    
    
    /************************************************************************
    * ClearResource
    *
    * Resets the editor back to a neutral state before editing any image.
    * This function can be called before starting to edit a new file
    * (but not just a new image).  Files should be saved before calling
    * this routine, because the entire image list is destroyed.
    *
    * History:
    *
    ************************************************************************/
    
    VOID ClearResource(VOID)
    {
        ImageLinkFreeList();
    
        SetFileName(NULL);
    
        gnImages = 0;
        fImageDirty = FALSE;
        fFileDirty = FALSE;
        gpImageCur = NULL;
    
        /*
         * Hide the workspace and view windows.
         */
        ShowWindow(ghwndWork, SW_HIDE);
        ViewShow(FALSE);
    
        /*
         * Destroy the image DC's.
         */
        ImageDCDelete();
    
        /*
         * Update the properties bar.
         */
        PropBarClearPos();
        PropBarClearSize();
        PropBarUpdate();
    }
    
    
    
    /************************************************************************
    * OpenDlg
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL OpenDlg(
        PSTR pszFileName,
        INT iType)
    {
        BOOL fGotName;
        INT idDlg;
        INT idPrevDlg;
        CHAR szFilter[CCHTEXTMAX];
    
        pszFileName[0] = '\0';
    
        switch (iType) {
            case FT_BITMAP:
            case FT_ICON:
            case FT_CURSOR:
                AddFilterString(szFilter, ids(IDS_BMPFILTER),
                        ids(IDS_BMPFILTEREXT), TRUE);
                AddFilterString(szFilter, ids(IDS_ICOFILTER),
                        ids(IDS_ICOFILTEREXT), FALSE);
                AddFilterString(szFilter, ids(IDS_CURFILTER),
                        ids(IDS_CURFILTEREXT), FALSE);
                AddFilterString(szFilter, ids(IDS_ALLFILTER),
                        ids(IDS_ALLFILTEREXT), FALSE);
    
                ofn.nFilterIndex = iType + 1;
                idDlg = DID_COMMONFILEOPEN;
    
                break;
    
            case FT_PALETTE:
                AddFilterString(szFilter, ids(IDS_PALFILTER),
                        ids(IDS_PALFILTEREXT), TRUE);
                AddFilterString(szFilter, ids(IDS_ALLFILTER),
                        ids(IDS_ALLFILTEREXT), FALSE);
    
                ofn.nFilterIndex = 1;
                idDlg = DID_COMMONFILEOPENPAL;
    
                break;
        }
    
        ofn.lStructSize = sizeof(OPENFILENAME);
        ofn.hwndOwner = ghwndMain;
        ofn.hInstance = NULL;
        ofn.lpstrFilter = szFilter;
        ofn.lpstrCustomFilter = NULL;
        ofn.nMaxCustFilter = 0;
        ofn.lpstrFile = pszFileName;
        ofn.nMaxFile = CCHMAXPATH;
        ofn.lpstrFileTitle = NULL;
        ofn.nMaxFileTitle = 0;
        ofn.lpstrInitialDir = NULL;
        ofn.lpstrTitle = NULL;
        ofn.Flags = OFN_HIDEREADONLY | OFN_SHOWHELP | OFN_FILEMUSTEXIST |
                OFN_ENABLEHOOK;
        ofn.nFileOffset = 0;
        ofn.nFileExtension = 0;
        ofn.lpstrDefExt = (LPCSTR)DefExtFromFilter((INT)ofn.nFilterIndex - 1,
                (PSTR)ofn.lpstrFilter);
        ofn.lCustData = 0;
        ofn.lpfnHook = (LPOFNHOOKPROC)MakeProcInstance(
                (FARPROC)GetOpenFileNameHook, ghInst);
        ofn.lpTemplateName = NULL;
    
        EnteringDialog(idDlg, &idPrevDlg, TRUE);
        fGotName = GetOpenFileName(&ofn);
        EnteringDialog(idPrevDlg, NULL, FALSE);
    
        FreeProcInstance((FARPROC)ofn.lpfnHook);
    
        return fGotName;
    }
    
    
    
    /************************************************************************
    * SaveAsDlg
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL SaveAsDlg(
        PSTR pszFileName,
        INT iType)
    {
        INT idDlg;
        BOOL fGotName;
        INT idPrevDlg;
        CHAR szFilter[CCHTEXTMAX];
    
        switch (iType) {
            case FT_BITMAP:
                AddFilterString(szFilter, ids(IDS_BMPFILTER),
                        ids(IDS_BMPFILTEREXT), TRUE);
    
                ofn.lpstrDefExt = ids(IDS_DEFEXTBMP);
                idDlg = DID_COMMONFILESAVE;
    
                break;
    
            case FT_ICON:
                AddFilterString(szFilter, ids(IDS_ICOFILTER),
                        ids(IDS_ICOFILTEREXT), TRUE);
    
                ofn.lpstrDefExt = ids(IDS_DEFEXTICO);
                idDlg = DID_COMMONFILESAVE;
    
                break;
    
            case FT_CURSOR:
                AddFilterString(szFilter, ids(IDS_CURFILTER),
                        ids(IDS_CURFILTEREXT), TRUE);
    
                ofn.lpstrDefExt = ids(IDS_DEFEXTCUR);
                idDlg = DID_COMMONFILESAVE;
    
                break;
    
            case FT_PALETTE:
                AddFilterString(szFilter, ids(IDS_PALFILTER),
                        ids(IDS_PALFILTEREXT), TRUE);
    
                ofn.lpstrDefExt = ids(IDS_DEFEXTPAL);
                idDlg = DID_COMMONFILESAVEPAL;
    
                break;
        }
    
        ofn.lStructSize = sizeof(OPENFILENAME);
        ofn.hwndOwner = ghwndMain;
        ofn.hInstance = NULL;
        ofn.lpstrFilter = szFilter;
        ofn.lpstrCustomFilter = NULL;
        ofn.nMaxCustFilter = 0;
        ofn.nFilterIndex = 1;
        ofn.lpstrFile = pszFileName;
        ofn.nMaxFile = CCHMAXPATH;
        ofn.lpstrFileTitle = NULL;
        ofn.nMaxFileTitle = 0;
        ofn.lpstrInitialDir = NULL;
        ofn.lpstrTitle = NULL;
        ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_SHOWHELP;
        ofn.nFileOffset = 0;
        ofn.nFileExtension = 0;
        ofn.lCustData = 0;
        ofn.lpfnHook = NULL;
        ofn.lpTemplateName = NULL;
    
        EnteringDialog(idDlg, &idPrevDlg, TRUE);
        fGotName = GetSaveFileName(&ofn);
        EnteringDialog(idPrevDlg, NULL, FALSE);
    
        return fGotName;
    }
    
    
    
    /************************************************************************
    * GetOpenFileNameHook
    *
    * This function is the hook function for the Common Dialogs
    * GetOpenFileName funtion.  It is used to be sure the default
    * extension that is used when the function exits is the same
    * as the image file type that the user specifies they want
    * opened.
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    DIALOGPROC GetOpenFileNameHook(
        HWND hwnd,
        UINT msg,
        WPARAM wParam,
        LPARAM lParam)
    {
        switch (msg) {
            case WM_INITDIALOG:
                /*
                 * Tell Windows to set the focus for me.
                 */
                return TRUE;
    
            case WM_COMMAND:
                /*
                 * Did they change the type of file from the File Type
                 * combo box?
                 */
                if (GET_WM_COMMAND_ID(wParam, lParam) == DID_COMMDLG_TYPECOMBO &&
                        GET_WM_COMMAND_CMD(wParam, lParam) == CBN_SELCHANGE) {
                    INT iSelect;
    
                    /*
                     * Get the selected file type, then change the default
                     * extension field of the ofn structure to match it.
                     * This ensures that the proper default extension
                     * gets added to the end of the file name if the user
                     * does not specify an extension explicitly.
                     */
                    if ((iSelect = (INT)SendDlgItemMessage(hwnd,
                            DID_COMMDLG_TYPECOMBO, CB_GETCURSEL, 0, 0L))
                            != CB_ERR) {
                        ofn.lpstrDefExt = (LPCSTR)DefExtFromFilter(
                                iSelect, (PSTR)ofn.lpstrFilter);
                    }
                }
    
                break;
        }
    
        /*
         * Process the message normally.
         */
        return FALSE;
    }
    
    
    
    /************************************************************************
    * AddFilterString
    *
    * This function adds a filter string pair to a filter string for
    * use by the common dialog open/save file functions.  The string
    * pair will be added to the end of the given filter string, unless
    * fFirst is TRUE, in which case it will be written out to the
    * start of the buffer.  A double null will always be written out
    * at the end of the filter string.
    *
    * Arguments:
    *   PSTR pszBuf  - Buffer to write to.
    *   PSTR pszType - Type string.  Something like "Icon files (*.ico)".
    *   PSTR pszExt  - Extension string.  Something like "*.ico".
    *   BOOL fFirst  - TRUE if this is the first filter string in the
    *                  buffer.  If FALSE, the new filter string pair will
    *                  be added to the end of pszBuf.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR AddFilterString(
        PSTR pszBuf,
        PSTR pszType,
        PSTR pszExt,
        BOOL fFirst)
    {
        PSTR psz;
    
        psz = pszBuf;
    
        /*
         * If this is not the first filter string pair, skip to the
         * terminating double null sequence.
         */
        if (!fFirst) {
            while (*psz || *(psz + 1))
                psz++;
    
            psz++;
        }
    
        strcpy(psz, pszType);
        psz += strlen(pszType) + 1;
        strcpy(psz, pszExt);
        psz += strlen(pszExt) + 1;
        *psz = '\0';
    }
    
    
    
    /************************************************************************
    * DefExtFromFilter
    *
    * This function returns the default extension for the given index
    * from the specified filter string chain.  The filter string chain
    * is in the format expected by the GetSaveFileName function.
    *
    * It will return NULL if the filter extension found is "*.*".
    *
    * Arguments:
    *   INT index      - Zero based index to the filter string.
    *   PSTR pszFilter - Pointer to the start of the filter chain.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN PSTR NEAR DefExtFromFilter(
        INT index,
        PSTR pszFilter)
    {
        if (!pszFilter)
            return NULL;
    
        /*
         * Skip to the specified filter string pair.
         */
        while (index--) {
            pszFilter += strlen(pszFilter) + 1;
            pszFilter += strlen(pszFilter) + 1;
        }
    
        /*
         * Skip the first string, then skip the '*' and the '.'.
         */
        pszFilter += strlen(pszFilter) + 1 + 1 + 1;
    
        /*
         * If the string found was "*.*", return NULL for the default
         * extension.
         */
        if (*pszFilter == '*')
            return NULL;
    
        /*
         * Return a pointer to the default extension.  This will be
         * something like "bmp" or "ico".
         */
        return pszFilter;
    }
    
    
    
    /************************************************************************
    * VerifySaveFile
    *
    * Prompts the user if they want to save the current file to disk.
    * If Yes, calls the appropriate save routine.
    *
    * Returns:
    *   Returns TRUE if either the file was not dirty (no save was done)
    *   or if it was and the user did not want to save it or the file
    *   was dirty and the user wanted to save it and the save was
    *   successful.
    *
    *   Returns FALSE if the user cancelled the operation, or an error
    *   occured with the save.
    *
    * History:
    *
    ************************************************************************/
    
    BOOL VerifySaveFile(VOID)
    {
        if (fImageDirty || fFileDirty) {
            switch (Message(MSG_SAVEFILE,
                    gpszFileName ? gpszFileName : ids(IDS_UNTITLED))) {
                case IDYES:
                    return SaveFile(FALSE);
    
                case IDNO:
                    fImageDirty = FALSE;
                    break;
    
                case IDCANCEL:
                    return FALSE;
            }
        }
    
        return TRUE;
    }
    
    
    
    /************************************************************************
    * SaveFile
    *
    * Does a save of the current file.  If the file is untitled, it
    * will ask the user for a file name.
    *
    * Arguments:
    *   BOOL fSaveAs - TRUE to force a Save As operation (always prompts
    *                  for the file name).
    *
    * Returns:
    *   Returns TRUE if the save was successful.
    *
    *   Returns FALSE if the user cancelled the operation, or an error
    *   occured with the save.
    *
    * History:
    *
    ************************************************************************/
    
    BOOL SaveFile(
        BOOL fSaveAs)
    {
        CHAR szFileName[CCHMAXPATH];
    
        if (gnImages == 0) {
            Message(MSG_NOIMAGES);
            return FALSE;
        }
    
        if (gpszFileName)
            strcpy(szFileName, gszFullFileName);
        else
            *szFileName = '\0';
    
        if (fSaveAs || !gpszFileName) {
            if (!SaveAsDlg(szFileName, giType))
                return FALSE;
        }
    
        switch (giType) {
            case FT_BITMAP:
                return SaveBitmapFile(szFileName);
    
            case FT_ICON:
            case FT_CURSOR:
                return SaveIconCursorFile(szFileName, giType);
        }
    
        return FALSE;
    }
    
    
    
    /************************************************************************
    * OpenAFile
    *
    * Prompts for a file name to open and then does the loading of it.
    *
    * History:
    *
    ************************************************************************/
    
    BOOL OpenAFile(VOID)
    {
        CHAR szFileName[CCHMAXPATH];
    
        if (OpenDlg(szFileName, giType)) {
            /*
             * Clear out the current resource.
             */
            ClearResource();
    
            LoadFile(szFileName);
    
            return TRUE;
        }
        else {
            return FALSE;
        }
    }
    
    
    
    /************************************************************************
    * LoadFile
    *
    * Loads the specified file for editing.
    *
    * Arguments:
    *   PSTR pszFullFileName - Full path name to the file to load.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN BOOL NEAR LoadFile(
        PSTR pszFullFileName)
    {
        switch (GetTypeFromExt(pszFullFileName)) {
            case FT_BITMAP:
                return LoadBitmapFile(pszFullFileName);
    
            case FT_ICON:
                return LoadIconCursorFile(pszFullFileName, TRUE);
    
            case FT_CURSOR:
                return LoadIconCursorFile(pszFullFileName, FALSE);
        }
    
        return FALSE;
    }
    
    
    
    /************************************************************************
    * GetTypeFromExt
    *
    * Returns the type of file based on it's file name extension.
    *
    * Arguments:
    *   PSTR pszFileName - File name to check.
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN INT NEAR GetTypeFromExt(
        PSTR pszFileName)
    {
        PSTR pszExt;
    
        pszExt = pszFileName + strlen(pszFileName) - 3;
    
        if (strcmpi(pszExt, ids(IDS_DEFEXTICO)) == 0)
            return FT_ICON;
        else if (strcmpi(pszExt, ids(IDS_DEFEXTCUR)) == 0)
            return FT_CURSOR;
        else
            return FT_BITMAP;
    }
    
    
    
    /************************************************************************
    * OpenCmdLineFile
    *
    * Handles opening of the file specified on the command line.
    *
    * History:
    * Nov 7, 1989   Byron Dazey - Created
    *
    ************************************************************************/
    
    VOID OpenCmdLineFile(
        PSTR pstrFileName)
    {
        CHAR szFullPath[CCHMAXPATH];
        OFSTRUCT OfStruct;
    
        strcpy(szFullPath, pstrFileName);
    
        /*
         * If the file name does not already have an extension,
         * assume it is a bitmap file and add a .BMP extension
         * to it.
         */
        FileCat(szFullPath, ids(IDS_DOTBMP));
    
        if ((HFILE)OpenFile(szFullPath, &OfStruct, OF_EXIST) == (HFILE)-1) {
            Message(MSG_CANTOPEN, pstrFileName);
        }
        else {
            LoadFile(OfStruct.szPathName);
        }
    }
    
    
    
    /************************************************************************
    * FileCat
    *
    * This function checks for an extension on the give file name.
    * If an extension is not found, the extension specified by
    * pchCat is added to the file name.
    *
    * Arguments:
    *     PSTR pch          = The file spec to "cat" the extension to.
    *     PSTR pchCat       = The extension to "cat" on to pch,
    *                         including the '.'
    *
    * History:
    *
    ************************************************************************/
    
    STATICFN VOID NEAR FileCat(
        PSTR pchName,
        PSTR pchCat)
    {
        PSTR pch;
    
        pch = pchName + strlen(pchName);
        pch = FAR2NEAR(AnsiPrev(pchName, pch));
    
        /* back up to '.' or '\\' */
        while (*pch != '.') {
            if (*pch == '\\' || pch <= pchName) {
                /* no extension, add one */
                strcat(pchName, pchCat);
                return;
            }
    
            pch = FAR2NEAR(AnsiPrev(pchName, pch));
        }
    }
    
    
    
    /************************************************************************
    * MyFileRead
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL MyFileRead(
        HFILE hf,
        LPSTR lpBuffer,
        UINT nBytes,
        PSTR pszFileName,
        INT iType)
    {
        register UINT cb;
    
        cb = _lread((HFILE)hf, lpBuffer, nBytes);
    
        if (cb == -1) {
            Message(MSG_READERROR, pszFileName);
            return FALSE;
        }
        else if (cb != nBytes) {
            Message((iType == FT_BITMAP) ? MSG_BADBMPFILE : MSG_BADICOCURFILE,
                pszFileName);
            return FALSE;
        }
        else {
            return TRUE;
        }
    }
    
    
    
    /************************************************************************
    * MyFileWrite
    *
    *
    *
    * Arguments:
    *
    * History:
    *
    ************************************************************************/
    
    BOOL MyFileWrite(
        HFILE hf,
        LPSTR lpBuffer,
        UINT nBytes,
        PSTR pszFileName)
    {
        register UINT cb;
    
        cb = _lwrite((HFILE)hf, lpBuffer, nBytes);
    
        if (cb == -1 || cb != nBytes) {
            Message(MSG_WRITEERROR, pszFileName);
            return FALSE;
        }
        else {
            return TRUE;
        }
    }
