/**************************************************************************\
* uconvert.c -- convert to/from unicode using
*                MulitByteToWideChar & WideCharToMulitByte
*
*         Steve Firebaugh
*         Microsoft Developer Support
*         Copyright (c) 1992, 1993 Microsoft Corporation
*
\**************************************************************************/
#define UNICODE


#include <windows.h>
#include <commdlg.h>
#include "uconvert.h"
#include "install.h"






/**************************************************************************\
*  Global variables.
\**************************************************************************/

HANDLE hInst;

/* declare global HWNDs for the child windows.
*   They are created at WM_CREATE  of the main window.
*   Also used in the "View" dialogs.
*/
HWND hwndLabel0, hwndLabel1;
HWND hwndName0, hwndName1;
HWND hwndSize0, hwndSize1;
HWND hwndCodePage0, hwndCodePage1;
HWND hwndByteOrder0, hwndByteOrder1;
HWND hwndButton0, hwndButton1;

/* Global variables storing the source and destination "type" information.
*
* used to communicate between main wnd proc, and *OptionsProc.
*
* gTypeSource - stores the type interpretation of the source data
*       (and implicitly the destination data.)
*   TYPEUNKNOWN: indeterminant... not set. Can not do conversion.
*   TYPEUNICODE: source unicode & destination giDestinationCodePage.
*   TYPECODEPAGE: source giSourceCodePage & destination unicode.
*
* giSourceCodePage stores valid source code page iff gTypeSource == TRUE
* giDestinationCodePage stores valid destination code page iff gTypeSource == FALSE
*
*/
int gTypeSource;
UINT giSourceCodePage;
UINT giDestinationCodePage;

/* Pointers to the source and destination data, and the
 *  count of bytes in each of the buffers.
 */
#define NODATA 0
PBYTE pSourceData =       NULL;
PBYTE pDestinationData =  NULL;
int   nBytesSource =      NODATA;
int   nBytesDestination = NODATA;

/* Conversion Options variables. */
DWORD gMBFlags = MB_PRECOMPOSED;
DWORD gWCFlags = 0;
char  glpDefaultChar[4] = "";  // what is max size of multibyte character?
BOOL  gUsedDefaultChar = FALSE;

/* Handling the Byte Order Mark (BOM).
*
* If the input file begins with a BOM, then we know it is unicode,
*  we skip over the BOM and decrement the size of data by SIZEOFBOM.
*
*
* Before writing data that we know is unicode, write the szBOM string
*  to the file.
*
* Notice that this means that the file sizes we show in the window
*  do NOT include the BOM.
*/

char szBOM[] = "\377\376";  // 0xFF, 0xFE  // leave off TEXT() macro.
char szRBOM[] = "\376\377";  // 0xFF, 0xFE  // leave off TEXT() macro.
#define SIZEOFBOM 2

/* Title of main window */
TCHAR TitleMBToWC[]= TEXT("UConvert -- MultiByteToWideChar()");
TCHAR TitleWCToMB[]= TEXT("UConvert -- WideCharToMultiByte()");
TCHAR TitleUnknown[]= TEXT("UConvert.");

/* file name of the online help file */
TCHAR szHelpPathName[] = TEXT("uconvert.HLP");

/* Strings used to fill onscreen windows. */
TCHAR szBlank[] = TEXT("");
TCHAR szNBytes[] = TEXT("bytes: %d");
TCHAR szByteOrderReversed[]=TEXT("Byte Order Reversed.");

/* MessageBox() strings and flags. */
TCHAR MBTitle[]= TEXT("Application Warning");
UINT  MBFlags = MB_OK | MB_ICONEXCLAMATION;


/* misc. defines affecting size and placement of child windows */
#define BORDER    GetSystemMetrics (SM_CXFRAME)*4
#define WHEIGHT   GetSystemMetrics (SM_CYMENU)



/**************************************************************************\
*
*  function:  WinMain()
*
*
\**************************************************************************/
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow)
{
    MSG    msg;
    WNDCLASS  wc;
    HWND   hwndMain;
    HACCEL haccel;


    UNREFERENCED_PARAMETER( lpCmdLine );
    UNREFERENCED_PARAMETER(  nCmdShow );
    hInst = hInstance;


    /* Check for previous instance.  If none, then register class. */
    if (!hPrevInstance) {

      wc.style = 0;
      wc.lpfnWndProc = (WNDPROC)MainWndProc;

      wc.cbClsExtra = 0;
      wc.cbWndExtra = 0;
      wc.hInstance = hInstance;
      wc.hIcon = LoadIcon(hInstance, TEXT("uconvertIcon"));
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = GetStockObject (LTGRAY_BRUSH);
      wc.lpszMenuName =  TEXT("uconvertMenu");
      wc.lpszClassName = TEXT("uconvert");

      if (!RegisterClass(&wc)) return (FALSE);

    }  /* class registered o.k. */


    /* Create the main window.  Return false if CreateWindow() fails */
    hwndMain = CreateWindow(
        TEXT("uconvert"),
        TitleUnknown,
        (WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX)) | WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        512,  // Big enough for most of the text.
        16*WHEIGHT,
        NULL, NULL, hInst, NULL);

    if (!hwndMain) return (FALSE);


    /* Load the accelerator table that provides clipboard support. */
    haccel = LoadAccelerators (hInst, TEXT("uconvertAccel"));



    /* Loop getting messages and dispatching them. */
    while (GetMessage(&msg, NULL, 0,0)) {
      if (!TranslateAccelerator(hwndMain, haccel, &msg)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }

    return (msg.wParam);
}




/**************************************************************************\
*
*  function:  MainWndProc()
*
*
* On WM_CREATE create all of the child windows.
* On WM_DESTROY make sure that all dynamically allocated memory is freed.
* On WM_PAINT, outline many of the child windows.
* On WM_COMMAND, respond to the command messages properly.
*
\**************************************************************************/
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{


/* misc. variables used in multiple messages cases. */
RECT clientrect;
RECT rect;
TCHAR buffer[50];
static TCHAR szFilter[MAX_PATH];

  switch (message) {

    /**********************************************************************\
    *  WM_CREATE
    *
    * Create all of the child windows used on this main window.
    *  Assign the HWNDs to the correct static variables.
    *
    \**********************************************************************/
    case WM_CREATE: {
      GetClientRect (hwnd, &clientrect);

      /* Create Source Windows. */
      CopyRect (&rect, &clientrect);
      rect.right = (clientrect.right - clientrect.left) /2;
      InflateRect (&rect, -2*BORDER, -BORDER);
      createwindows(&rect,
                   hwnd,
                   &hwndLabel0,
                   &hwndName0,
                   &hwndSize0,
                   &hwndCodePage0,
                   &hwndByteOrder0,
                   &hwndButton0);

      /* Create Destination Windows. */
      CopyRect (&rect, &clientrect);
      rect.left = (clientrect.right - clientrect.left) /2;
      InflateRect (&rect, -2*BORDER, -BORDER);
      createwindows(&rect,
                   hwnd,
                   &hwndLabel1,
                   &hwndName1,
                   &hwndSize1,
                   &hwndCodePage1,
                   &hwndByteOrder1,
                   &hwndButton1);

      /* fill in window information that is different for source/destination */
      SetWindowText (hwndLabel0, TEXT("Source:"     ));
      SetWindowText (hwndLabel1, TEXT("Destination:"));

      SetWindowText (hwndButton0, TEXT("View &Source..."     ));
      SetWindowText (hwndButton1, TEXT("View &Destination..."));
      SetWindowLong (hwndButton0, GWL_ID, BID_VIEWSOURCE      );
      SetWindowLong (hwndButton1, GWL_ID, BID_VIEWDESTINATION );

      gTypeSource = TYPEUNKNOWN;
      giSourceCodePage =      GetACP();  // Just some reasonable initializer.
      giDestinationCodePage = GetACP();  // Just some reasonable initializer.

      /* initialize source & destination data correctly */
      SendMessage (hwnd, WM_COMMAND, MID_CLEARSOURCE,      0);
      SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

      /* Build up the correct filter strings for OPENFILENAME structure
       *  Do it here so that we only have to do it once.
       */
      {
        TCHAR *p;

        p = szFilter;
        lstrcpy (buffer,TEXT("All Files (*.*)"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;
        lstrcpy (buffer,TEXT("*.*"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;

        lstrcpy (buffer,TEXT("Text Files (*.txt)"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;
        lstrcpy (buffer,TEXT("*.txt"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;

        lstrcpy (buffer,TEXT("Unicode Files (*.utf)"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;
        lstrcpy (buffer,TEXT("*.utf"));
        lstrcpy (p,buffer);
        p += lstrlen (buffer) +1;

        lstrcpy (p,TEXT("\0"));
      }
    } break; /* end WM_CREATE */



    /**********************************************************************\
    *  WM_DESTROY
    *
    * Release the Online help, and free allocated memory if any.
    \**********************************************************************/
    case WM_DESTROY:
      WinHelp( hwnd, szHelpPathName, (UINT) HELP_QUIT, (DWORD) NULL );
      ManageMemory (MMFREE, MMSOURCE,      0, pSourceData);
      ManageMemory (MMFREE, MMDESTINATION, 0, pDestinationData);
      PostQuitMessage(0);
    break;


    /**********************************************************************\
    *  WM_CTLCOLOR*
    *
    * Set the background of the child controls to be gray here.
    \**********************************************************************/
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORSTATIC: {
      HDC hdc;

      hdc = (HDC) wParam;
      SetBkMode (hdc, TRANSPARENT);
      return (LRESULT)GetStockObject (LTGRAY_BRUSH);
    } break;




    /**********************************************************************\
    *  WM_PAINT
    *
    * Simply draw the two vertical divider lines, and 3D frame the children.
    *
    \**********************************************************************/
    case WM_PAINT: {
      HDC hdc;
      PAINTSTRUCT ps;

      hdc = BeginPaint(hwnd, &ps);
      GetClientRect (hwnd, &clientrect);

      /* draw vertical separator line in the center */
      rect.left = (clientrect.right - clientrect.left ) /2 -1;
      rect.top = clientrect.top;
      rect.right = rect.left +1;;
      rect.bottom = clientrect.bottom;
      FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
      SelectObject (hdc, GetStockObject (WHITE_PEN));
      MoveToEx (hdc, rect.right, rect.top, NULL);
      LineTo (hdc,rect.right, rect.bottom);

      /* draw 3D outlines of child windows. */
      framechildwindow (hdc, hwnd, hwndName0);
      framechildwindow (hdc, hwnd, hwndSize0);
      framechildwindow (hdc, hwnd, hwndCodePage0);
      framechildwindow (hdc, hwnd, hwndByteOrder0);

      framechildwindow (hdc, hwnd, hwndName1);
      framechildwindow (hdc, hwnd, hwndSize1);
      framechildwindow (hdc, hwnd, hwndCodePage1);
      framechildwindow (hdc, hwnd, hwndByteOrder1);

      /* underline the labels */
      underlinechildwindow (hdc, hwnd, hwndLabel0);
      underlinechildwindow (hdc, hwnd, hwndLabel1);

      EndPaint (hwnd, &ps);
    } break; /* end WM_PAINT */




    /**********************************************************************\
    *  WMU_ADJUSTFORNEWSOURCE
    *
    * lParam - szName of source (file, clipboard, ...)
    *
    * global - nBytesSource
    *
    * "user message."  Set the text of the Source windows
    \**********************************************************************/
    case WMU_ADJUSTFORNEWSOURCE: {
      LPVOID szName;

      szName = (LPVOID) lParam;

      /* Set Window text appropriately */
      wsprintf (buffer, szNBytes, nBytesSource);
      SetWindowText (hwndSize0, buffer);
      SetWindowText (hwndName0, szName);
      SetWindowText (hwndByteOrder0, szBlank);

      /* Clear the destination data if any to avoid user confusion. */
      SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

      /* Reset the "type strings" based on new gTypeSource. */
      SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
    } break;




    /**********************************************************************\
    *  WMU_SETTYPESTRINGS
    *
    * "user message."  Set the text of the "type" windows to reflect
    *  the state stored in gTypeSource and gi*CodePage.
    *
    \**********************************************************************/
    case WMU_SETTYPESTRINGS:
      switch (gTypeSource) {
        case TYPEUNICODE:
          SetWindowText (hwndCodePage0, TEXT("Unicode"));
          wsprintf (buffer, TEXT("CodePage: %d"), giDestinationCodePage);
          SetWindowText (hwndCodePage1, buffer);
          SetWindowText (hwnd, TitleWCToMB);
        break;
        case TYPECODEPAGE:
          wsprintf (buffer, TEXT("CodePage: %d"), giSourceCodePage);
          SetWindowText (hwndCodePage0, buffer);
          SetWindowText (hwndCodePage1, TEXT("Unicode"));
          SetWindowText (hwnd, TitleMBToWC);
        break;
        case TYPEUNKNOWN:
          SetWindowText (hwndCodePage0, szBlank);
          SetWindowText (hwndCodePage1, szBlank);
          SetWindowText (hwnd, TitleUnknown);
        break;
      } /* end switch gTypeSource */
    break;


    /**********************************************************************\
    *  WM_INITMENU
    *
    * Manage the enabled state of all of the menus.
    *  Notice that the button enabled state is taken care of in ManageMemory().
    *
    * In general, this is dependent upon pSourceData & pDestinationData.
    *  They are either NULL or non-NULL, and menu items are dependent upon
    *  this state.
    *
    * One exception is the "Paste from Clipboard menu" which is enabled
    *  conditional upon there being text data in the clipboard.
    *
    \**********************************************************************/
    case WM_INITMENU:

      /* Adjust the "Paste from Clipboard menu" */
      OpenClipboard (hwnd);
      if (IsClipboardFormatAvailable (CF_UNICODETEXT) ||
          IsClipboardFormatAvailable (CF_OEMTEXT) ||
          IsClipboardFormatAvailable (CF_TEXT))
        EnableMenuItem (GetMenu (hwnd),MID_PASTESOURCE,      MF_ENABLED);
      else
        EnableMenuItem (GetMenu (hwnd),MID_PASTESOURCE,      MF_GRAYED);
      CloseClipboard ();


      /* Adjust the source data dependent menus. */
      if (pSourceData != NULL) {
        EnableMenuItem (GetMenu (hwnd),MID_SOURCEOPT,        MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_SWAPSOURCE,       MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_CLEARSOURCE,      MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_CONVERTNOW,       MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_CONVERSIONOPT,    MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_DESTINATIONOPT,   MF_ENABLED);
      } else {
        EnableMenuItem (GetMenu (hwnd),MID_SOURCEOPT,        MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_SWAPSOURCE,       MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_CLEARSOURCE,      MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_CONVERTNOW,       MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_CONVERSIONOPT,    MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_DESTINATIONOPT,   MF_GRAYED);
      }


      /* Adjust the destination data dependent menus. */
      if (pDestinationData != NULL) {
        EnableMenuItem (GetMenu (hwnd),MID_SAVEAS,           MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_SWAPDESTINATION,  MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_COPYDESTINATION,  MF_ENABLED);
        EnableMenuItem (GetMenu (hwnd),MID_CLEARDESTINATION, MF_ENABLED);
      } else {
        EnableMenuItem (GetMenu (hwnd),MID_SAVEAS,           MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_SWAPDESTINATION,  MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_COPYDESTINATION,  MF_GRAYED);
        EnableMenuItem (GetMenu (hwnd),MID_CLEARDESTINATION, MF_GRAYED);
      }

    break;





    /**********************************************************************\
    *  WM_COMMAND
    *
    * Just switch() on the command ID.  Notice that menu and button
    *  command messages are treated the same.
    *
    \**********************************************************************/
    case WM_COMMAND:
      switch (LOWORD(wParam)) {


        /******************************************************************\
        *  WM_COMMAND, MID_OPEN
        *
        * Put up common dialog, try to open & read file.
        *  Fill windows with correct text & fill pSourceData.
        \******************************************************************/
        case MID_OPEN  : {
          HANDLE hFile;
          DWORD nBytesRead;
          TCHAR szFile[MAX_PATH],szFileTitle[MAX_PATH];

          /* First set up the structure for the GetOpenFileName
           *  common dialog.
           */
          {
            OPENFILENAME OpenFileName;
            /* buffers for the file names. */

            wsprintf (szFile, szBlank);
            wsprintf (szFileTitle, szBlank);


            OpenFileName.lStructSize       = sizeof(OPENFILENAME);
            OpenFileName.hwndOwner         = hwnd;
            OpenFileName.hInstance         = (HANDLE) hInst;
            OpenFileName.lpstrFilter       = szFilter; // built in WM_CREATE
            OpenFileName.lpstrCustomFilter = NULL;
            OpenFileName.nMaxCustFilter    = 0L;
            OpenFileName.nFilterIndex      = 1L;
            OpenFileName.lpstrFile         = szFile;
            OpenFileName.nMaxFile          = MAX_PATH;
            OpenFileName.lpstrFileTitle    = szFileTitle;
            OpenFileName.nMaxFileTitle     = MAX_PATH;
            OpenFileName.lpstrInitialDir   = NULL;
            OpenFileName.lpstrTitle        = TEXT("Open File");

            OpenFileName.nFileOffset       = 0;
            OpenFileName.nFileExtension    = 0;
            OpenFileName.lpstrDefExt       = NULL;

            OpenFileName.lCustData         = 0;
            OpenFileName.lpfnHook          = NULL;
            OpenFileName.lpTemplateName    = NULL;

            OpenFileName.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;

            if (!GetOpenFileName(&OpenFileName)) return 0;
          }


          /* User has filled in the file information.
           *  Try to open that file for reading.
           */
          hFile = CreateFile(szFile,
                       GENERIC_READ,
                       0,
                       NULL,
                       OPEN_EXISTING,
                       FILE_ATTRIBUTE_NORMAL,
                       NULL);
          if (hFile == INVALID_HANDLE_VALUE) {
            MessageBox (hwnd, TEXT("open file failed."),MBTitle, MBFlags);
            return 0;
          }


          /* make sure file is not too big... i.e. > 2^32
           *  If it is OK, write the file size in hwndSize0
           */
          {
            BY_HANDLE_FILE_INFORMATION bhfi;

            GetFileInformationByHandle (hFile, &bhfi);
            if (bhfi.nFileSizeHigh != 0) {
              MessageBox (hwnd, TEXT("File too big, > 2^32."),MBTitle, MBFlags);
              CloseHandle (hFile);
              return 0;
            }

            nBytesSource= bhfi.nFileSizeLow;

          }

          /* Allocate space for string, including potential UNICODE_NULL */
          pSourceData = ManageMemory (MMALLOC, MMSOURCE, nBytesSource +2, pSourceData);
          if (pSourceData == NULL) {
            CloseHandle (hFile);
            return 0;
          }

          /* first read two bytes and look for BOM */
          if (!ReadFile (hFile, pSourceData,SIZEOFBOM, &nBytesRead, NULL)) {
            MessageBox (hwnd, TEXT("ReadFile() failed."),MBTitle, MBFlags);
            CloseHandle (hFile);
            pSourceData = ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
            return 0;
          }



          /* If file begins with BOM, then we know the type,
           *  we'll decrement the number of bytes by two,
           *  and read the rest of the data.
           */
          if (IsBOM (pSourceData)) {
            gTypeSource = TYPEUNICODE;
            nBytesSource -=SIZEOFBOM;

          /* If file begins with Reverse BOM, then we know the type,
           *  we'll decrement the number of bytes by two,
           *  and read the rest of the data, and post a message so
           *  that we know to swap the order later.
           */
          } else if (IsRBOM (pSourceData)) {
            gTypeSource = TYPEUNICODE;
            nBytesSource -=SIZEOFBOM;
            MessageBox (hwnd, TEXT("Reverse byte order mark detected.\n Automatically swapping byte order."),MBTitle, MBFlags);
            PostMessage (hwnd, WM_COMMAND, MID_SWAPSOURCE, 0);

          /* Oops, does not begin with BOM.
           *  Reset file pointer, and read data.
           */
          } else {
            gTypeSource = TYPEUNKNOWN;
            SetFilePointer (hFile, -SIZEOFBOM, NULL, FILE_CURRENT);
          }



          /* try to read all of it into memory */
          if (!ReadFile (hFile, pSourceData,nBytesSource, &nBytesRead, NULL)) {
            MessageBox (hwnd, TEXT("ReadFile() failed."),MBTitle, MBFlags);
            CloseHandle (hFile);
            pSourceData = ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
            return 0;
          }

          CloseHandle (hFile);

          /* If we don't know the file type at this point,
           *  try to determine if it is unicode.
           */
          if (gTypeSource == TYPEUNKNOWN) {
            if (IsUnicode (pSourceData)) {
              gTypeSource = TYPEUNICODE;
              pSourceData[nBytesSource]   = 0;  // UNICODE_NULL
              pSourceData[nBytesSource+1] = 0;
            } else {
              gTypeSource = TYPECODEPAGE;
              pSourceData[nBytesSource] = 0;
            }
          }

          SendMessage (hwnd, WMU_ADJUSTFORNEWSOURCE, 0, (LPARAM)szFile);



        } break;  /* end case MID_OPEN */



        /******************************************************************\
        *  WM_COMMAND, MID_SAVEAS
        *
        * Put up common dialog, try to open file, and write data to it.
        \******************************************************************/
        case MID_SAVEAS: {
          HANDLE hFile;
          DWORD nBytesRead;
          TCHAR szFile[MAX_PATH],szFileTitle[MAX_PATH];

          if (nBytesDestination == NODATA ) {
            MessageBox (hwnd, TEXT("No text to save"),MBTitle, MBFlags);
            return 0;
          }


          /* Set up the structure for the GetSaveFileName
           *  common dialog.
           */
          {
            OPENFILENAME OpenFileName;
            /* buffers for the file names. */

            wsprintf (szFile, szBlank);
            wsprintf (szFileTitle, szBlank);

            OpenFileName.lStructSize       = sizeof(OPENFILENAME);
            OpenFileName.hwndOwner         = hwnd;
            OpenFileName.hInstance         = (HANDLE) hInst;
            OpenFileName.lpstrFilter       = szFilter;
            OpenFileName.lpstrCustomFilter = NULL;
            OpenFileName.nMaxCustFilter    = 0L;
            OpenFileName.nFilterIndex      = 1L;
            OpenFileName.lpstrFile         = szFile;
            OpenFileName.nMaxFile          = MAX_PATH;
            OpenFileName.lpstrFileTitle    = szFileTitle;
            OpenFileName.nMaxFileTitle     = MAX_PATH;
            OpenFileName.lpstrInitialDir   = NULL;
            OpenFileName.lpstrTitle        = TEXT("Save As");

            OpenFileName.nFileOffset       = 0;
            OpenFileName.nFileExtension    = 0;
            OpenFileName.lpstrDefExt       = NULL;

            OpenFileName.lCustData         = 0;
            OpenFileName.lpfnHook          = NULL;
            OpenFileName.lpTemplateName    = NULL;

            OpenFileName.Flags = OFN_HIDEREADONLY;

            if (!GetSaveFileName(&OpenFileName)) return 0;
          }


          /* User has filled in the file information.
           *  Try to open that file for writing.
           */
          hFile = CreateFile(szFile,
                      GENERIC_WRITE,
                      0,
                      NULL,
                      CREATE_ALWAYS,
                      FILE_ATTRIBUTE_NORMAL,
                      NULL);

          if (hFile == INVALID_HANDLE_VALUE) {
            MessageBox (hwnd, TEXT("CreateFile() failed."),MBTitle, MBFlags);
            return 0;
          }


          /* if destination is unicode, try to write BOM first.
           *  unless the bytes have been swapped
           *  (criterion: hwndByteOrder contains text)
           *  in which case, write a Reverse Byte Order Mark.
           */
          if (gTypeSource == TYPECODEPAGE) {
            if (GetWindowTextLength (hwndByteOrder1) == 0) {

              if (!WriteFile (hFile, szBOM, SIZEOFBOM, &nBytesRead, NULL)) {
                MessageBox (hwnd, TEXT("WriteFile() failed."),MBTitle, MBFlags);
                CloseHandle (hFile);
                return 0;
              }

            }else {
              if (!WriteFile (hFile, szRBOM, SIZEOFBOM, &nBytesRead, NULL)) {
                MessageBox (hwnd, TEXT("WriteFile() failed."),MBTitle, MBFlags);
                CloseHandle (hFile);
                return 0;
              }

            }
          }


          /* try to write all of it into memory */
          if (!WriteFile (hFile, pDestinationData,nBytesDestination, &nBytesRead, NULL)) {
            MessageBox (hwnd, TEXT("WriteFile() failed."),MBTitle, MBFlags);
            CloseHandle (hFile);
            return 0;
          }

          SetWindowText (hwndName1, szFile);
          CloseHandle (hFile);

        } break;


        /**********************************************************************\
        *  WM_COMMAND, MID_PASTESOURCE
        *
        * Paste the clipboard's prefered data format into the source.
        *  Fills pSourceData.
        \**********************************************************************/
        case MID_PASTESOURCE: {
          UINT  iFormat;
          PVOID pData;

          OpenClipboard (hwnd);

          iFormat = 0;
          while (iFormat = EnumClipboardFormats(iFormat))
            if ((iFormat == CF_UNICODETEXT) || (iFormat == CF_OEMTEXT) || (iFormat == CF_TEXT)) {

              pData = GetClipboardData (iFormat);

              switch (iFormat) {
                case CF_UNICODETEXT:
                  nBytesSource = lstrlenW (pData) *2;
                  pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+2, pSourceData);
                  lstrcpyW ((LPVOID)pSourceData, pData);
                  gTypeSource = TYPEUNICODE;
                break;

                case CF_OEMTEXT:
                  nBytesSource = lstrlenA (pData);
                  pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+1, pSourceData);
                  lstrcpyA (pSourceData, pData);
                  gTypeSource = TYPECODEPAGE;
                  giSourceCodePage = GetOEMCP();
                break;

                case CF_TEXT:
                  nBytesSource = lstrlenA (pData);
                  pSourceData= ManageMemory (MMALLOC, MMSOURCE, nBytesSource+1, pSourceData);
                  lstrcpyA (pSourceData, pData);
                  gTypeSource = TYPECODEPAGE;
                  giSourceCodePage = GetACP();
                break;

                default: break;  // shouldn't get here
              } /* end switch (iFormat) */

              SendMessage (hwnd, WMU_ADJUSTFORNEWSOURCE, 0,
                            (LPARAM)TEXT("<from Clipboard>"));

            break;  /* break out of while loop. */
            } /* end if iFormat */


          CloseClipboard ();

        } break;



        /**********************************************************************\
        *  WM_COMMAND, MID_COPYDESTINATION
        *
        * Copy destination data to the clipboard.
        \**********************************************************************/
        case MID_COPYDESTINATION:
          if (pDestinationData == NULL) return FALSE;

          OpenClipboard (hwnd);
          EmptyClipboard();

          /* if source NOT unicode, then destination is, else look at dest CP */
          if (gTypeSource != TYPEUNICODE)
            SetClipboardData (CF_UNICODETEXT, pDestinationData);
          else if (giDestinationCodePage == GetOEMCP())
            SetClipboardData (CF_OEMTEXT, pDestinationData);
          else
            SetClipboardData (CF_TEXT, pDestinationData);

          CloseClipboard ();

        break;



        /******************************************************************\
        *  WM_COMMAND, MID_CONVERTNOW
        *
        * This is where the conversion actually takes place.
        *  In either case, make the call twice.  Once to determine how
        *  much memory is needed, allocate space, and then make the call again.
        *
        *  If conversion succeeds, it fills pDestinationData.
        \******************************************************************/
        case MID_CONVERTNOW: {
          int nBytesNeeded, nWCharNeeded, nWCharSource;


          if (nBytesSource == NODATA ) {
            MessageBox (hwnd, TEXT("Load Source File"),MBTitle, MBFlags);
            return 0;
          }


          /* Converting UNICODE -> giDestinationCodePage*/
          if (gTypeSource == TYPEUNICODE) {

            nWCharSource = nBytesSource/2;

            /* Query the number of bytes required to store the Dest string */
            nBytesNeeded = WideCharToMultiByte(giDestinationCodePage, gWCFlags,
                             (LPWSTR)pSourceData, nWCharSource,
                             NULL, 0,
                             glpDefaultChar, &gUsedDefaultChar);

            /* Allocate the required amount of space, including trailing NULL */
            pDestinationData= ManageMemory (MMALLOC, MMDESTINATION, nBytesNeeded +1, pDestinationData);

            /* Do the conversion */
            nBytesDestination = WideCharToMultiByte(giDestinationCodePage, gWCFlags,
                             (LPWSTR)pSourceData, nWCharSource,
                             pDestinationData, nBytesNeeded, glpDefaultChar, &gUsedDefaultChar);

            /* Null terminate string. */
            pDestinationData[nBytesDestination] = 0;
          }


          /* converting giSourceCodePage -> UNICODE */
          else if (gTypeSource == TYPECODEPAGE) {

            /* Query the number of WChar required to store the Dest string */
            nWCharNeeded = MultiByteToWideChar(giSourceCodePage, gMBFlags,
                             pSourceData, nBytesSource, NULL, 0 );

            /* Allocate the required amount of space, including trailing NULL */
            pDestinationData= ManageMemory (MMALLOC, MMDESTINATION, (nWCharNeeded +1)*2, pDestinationData);

            /* Do the conversion */
            nWCharNeeded = MultiByteToWideChar(giSourceCodePage, gMBFlags,
                             pSourceData, nBytesSource,
                             (LPWSTR)pDestinationData, nWCharNeeded);

            /* MultiByteToWideChar returns # WCHAR, so multiply by 2 */
            nBytesDestination = 2*nWCharNeeded ;

            /* Null terminate string. */
            pDestinationData[nBytesDestination]   = 0;  // UNICODE_NULL
            pDestinationData[nBytesDestination+1] = 0;
          } else {
            MessageBox (hwnd, TEXT("Source type unknown.\n Specify Source Options"),MBTitle, MBFlags);
            return 0;
          }


          /* code common to all conversions... */
          SetWindowText (hwndName1, TEXT("Data not saved yet!!"));
          wsprintf (buffer, szNBytes, nBytesDestination);
          SetWindowText (hwndSize1, buffer);
          SetWindowText (hwndByteOrder1, szBlank);


          /* Throw up "Save as" dialog to help the user along.
           *  They can always <esc> if need be.
           */
          SendMessage (hwnd, WM_COMMAND, MID_SAVEAS, 0);

        } break; /* end  case BID_CONVERT */



        /******************************************************************\
        *  WM_COMMAND, BID_VIEWSOURCE
        *
        \******************************************************************/
        case BID_VIEWSOURCE:
          if (gTypeSource == TYPEUNICODE)
            DialogBoxW (hInst, L"ShowTextDlg", hwnd, (DLGPROC)ViewSourceProc);
          else
            DialogBoxA (hInst, "ShowTextDlg", hwnd, (DLGPROC)ViewSourceProc);
        break;

        /******************************************************************\
        *  WM_COMMAND, BID_VIEWDESTINATION
        *
        \******************************************************************/
        case BID_VIEWDESTINATION:
          if (gTypeSource == TYPEUNICODE)
            DialogBoxA (hInst, "ShowTextDlg", hwnd, (DLGPROC)ViewDestinationProc);
          else
            DialogBoxW (hInst, L"ShowTextDlg", hwnd, (DLGPROC)ViewDestinationProc);
        break;



        /******************************************************************\
        *  WM_COMMAND, MID_SOURCEOPT
        *
        * Allows user to change interpretation options for the source data.
        *
        *  Put up appropriate dialog box, and reset window text in response.
        \******************************************************************/
        case MID_SOURCEOPT:
          if (DialogBox (hInst, TEXT("DataOptionsDlg"), hwnd, (DLGPROC)SourceOptionsProc)) {
            SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
            SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
          }
        break;

        /******************************************************************\
        *  WM_COMMAND, MID_DESTINATIONOPT
        *
        * Allows user to change options for destination data.
        *
        *  Put up appropriate dialog box, and reset window text in response.
        \******************************************************************/
        case MID_DESTINATIONOPT:
          if (DialogBox (hInst, TEXT("DataOptionsDlg"), hwnd, (DLGPROC)DestinationOptionsProc)) {
            SendMessage (hwnd, WMU_SETTYPESTRINGS, 0,0);
            SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
          }
        break;

        /******************************************************************\
        *  WM_COMMAND, MID_CONVERSIONOPT
        *
        \******************************************************************/
        case MID_CONVERSIONOPT:
          if (DialogBox (hInst, TEXT("ConversionOptionsDlg"), hwnd, (DLGPROC)ConversionOptionsProc)) {
            SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);
          }
        break;



        /******************************************************************\
        *  WM_COMMAND, MID_SWAPSOURCE
        *
        * Allows user to reverse byte order of data.
        *
        \******************************************************************/
        case MID_SWAPSOURCE: {
          int i, end;
          BYTE temp;

          if (pSourceData == NULL) return FALSE;

          end =  nBytesSource - 2;
          for (i = 0; i<= end; i+=2) {
            temp             = pSourceData[i];
            pSourceData[i]   = pSourceData[i+1];
            pSourceData[i+1] = temp;
          }

          if (GetWindowTextLength (hwndByteOrder0) == 0)
            SetWindowText (hwndByteOrder0, szByteOrderReversed);
          else
            SetWindowText (hwndByteOrder0, szBlank);

          /* Since source is different, invalidate Destination data. */
          SendMessage (hwnd, WM_COMMAND, MID_CLEARDESTINATION, 0);

        } break;



        /******************************************************************\
        *  WM_COMMAND, MID_SWAPDESTINATION
        *
        * Allows user to reverse byte order of data.
        *
        \******************************************************************/
        case MID_SWAPDESTINATION: {
          int i, end;
          BYTE temp;

          if (pDestinationData == NULL) return FALSE;

          end =  nBytesDestination - 2;
          for (i = 0; i<= end; i+=2) {
            temp             = pDestinationData[i];
            pDestinationData[i]   = pDestinationData[i+1];
            pDestinationData[i+1] = temp;
          }

          if (GetWindowTextLength (hwndByteOrder1) == 0)
            SetWindowText (hwndByteOrder1, szByteOrderReversed);
          else
            SetWindowText (hwndByteOrder1, szBlank);

        } break;


        /**********************************************************************\
        *  WM_COMMAND, MID_CLEARDESTINATION
        *
        * Clear the destination information.  May cause data to be lost.
        \**********************************************************************/
        case MID_CLEARDESTINATION:
          SetWindowText (hwndSize1, szBlank);
          SetWindowText (hwndName1, szBlank);
          SetWindowText (hwndByteOrder1, szBlank);
          pDestinationData= ManageMemory (MMFREE, MMDESTINATION, 0, pDestinationData);
        break;


        /**********************************************************************\
        *  WM_COMMAND, MID_CLEARSOURCE
        *
        * Clear the SOURCE information.  May cause data to be lost.
        \**********************************************************************/
        case MID_CLEARSOURCE:
          SetWindowText (hwndSize0, szBlank);
          SetWindowText (hwndName0, szBlank);
          SetWindowText (hwndByteOrder0, szBlank);
          pSourceData= ManageMemory (MMFREE, MMSOURCE, 0, pSourceData);
        break;





        /******************************************************************\
        *  WM_COMMAND, MID_INSTALLTABLES
        *
        \******************************************************************/
        case MID_INSTALLTABLES:
          DialogBox (hInst, TEXT("InstallTableDlg"), hwnd, (DLGPROC)InstallTableProc);
        break;




        /* Simply call WinHelp to display the OnLine help file. */
        case MID_HELP:
          WinHelp( hwnd, szHelpPathName, HELP_INDEX, (DWORD) NULL );
        break;


        /* No-op Window procedure to simply display the dialog box. */
        case MID_ABOUT:
          DialogBox (hInst, TEXT("aboutBox"), hwnd, (DLGPROC) AboutProc);
        break;

        /* Just close the window. */
        case MID_EXIT:
          PostMessage (hwnd, WM_CLOSE, 0,0);
        break;




      } /* end switch (LOWORD(wParam)) */
    break;  /* end WM_COMMAND */



    default: break;
  } /* end switch */

  return (DefWindowProc(hwnd, message, wParam, lParam));
}





/**************************************************************************\
*
*  function:  IsUnicode()
*
* HACK... eventually use a proper function for IsUnicode
*  Use function from unipad?
*
\**************************************************************************/
BOOL IsUnicode (PBYTE pb)
{
  return (IsBOM (pb));
}



/**************************************************************************\
*
*  function:  IsBOM()
*
* true iff pb points to a Byte Order Mark.
*
\**************************************************************************/
BOOL IsBOM (PBYTE pb)
{
  if ((*pb == 0xFF) & (*(pb+1) == 0xFE))  // BOM
    return TRUE;
  else
    return FALSE;
}


/**************************************************************************\
*
*  function:  IsRBOM()
*
* true iff pb points to a reversed Byte Order Mark.
*
\**************************************************************************/
BOOL IsRBOM (PBYTE pb)
{
  if ((*pb == 0xFE) & (*(pb+1) == 0xFF))  // RBOM
    return TRUE;
  else
    return FALSE;
}




/**************************************************************************\
*
*  function:  framechildwindow()
*
* Simply draw a 3D frame around child window.
*
\**************************************************************************/
VOID framechildwindow (HDC hdc, HWND hwndParent, HWND hwndChild)
{
RECT rect;

      GetWindowRect (hwndChild, &rect);

      /* minor hack... assumes RECT is two points, right field starting first */
      ScreenToClient (hwndParent, (LPPOINT)&rect);
      ScreenToClient (hwndParent, (LPPOINT)&(rect.right));

      InflateRect (&rect, 1, 1);
      FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
      InflateRect (&rect, -1, -1);
      SelectObject (hdc, GetStockObject (WHITE_PEN));
      MoveToEx (hdc, rect.right, rect.top, NULL);
      LineTo (hdc,rect.right, rect.bottom);
      LineTo (hdc,rect.left, rect.bottom);

    return;
}


/**************************************************************************\
*
*  function:  underlinechildwindow()
*
* Underline child window.
*
\**************************************************************************/
VOID underlinechildwindow (HDC hdc, HWND hwndParent, HWND hwndChild)
{
RECT rect;

      GetWindowRect (hwndChild, &rect);

      /* minor hack... assumes RECT is two points, right field starting first */
      ScreenToClient (hwndParent, (LPPOINT)&rect);
      ScreenToClient (hwndParent, (LPPOINT)&(rect.right));

      InflateRect (&rect, 1, 1);
      rect.top = rect.bottom-1;
      FrameRect (hdc, &rect, GetStockObject (GRAY_BRUSH));
      SelectObject (hdc, GetStockObject (WHITE_PEN));
      MoveToEx (hdc, rect.right, rect.bottom, NULL);
      LineTo (hdc,rect.left, rect.bottom);

    return;
}







/**************************************************************************\
*
*  function:  createwindows()
*
* Create the child windows and pass the handles back in parameters.
*  Each Window is created relative to (inside of) prect.
*  top is a spacial pointer to the Y coordinate of the next window.
*
\**************************************************************************/
VOID createwindows(PRECT prect,
                   HWND  hwndParent,
                   HWND* hwndLabel,
                   HWND* hwndName,
                   HWND* hwndSize,
                   HWND* hwndCodePage,
                   HWND* hwndByteOrder,
                   HWND* hwndButton)
{
int top;

  top = prect->top;
  *hwndLabel = CreateWindow(
          TEXT("STATIC"),
          szBlank,
          WS_CHILD | WS_VISIBLE | SS_CENTER,
          prect->left,
          top,
          prect->right - prect->left,
          WHEIGHT,
          hwndParent, NULL, hInst, 0);

  top += WHEIGHT*5/2;
  *hwndName = CreateWindow(
          TEXT("STATIC"),
          szBlank,
          WS_CHILD | WS_VISIBLE | SS_RIGHT,
          prect->left,
          top,
          prect->right - prect->left,
          WHEIGHT,
          hwndParent, NULL, hInst, 0);

  top += WHEIGHT*2;
  *hwndSize = CreateWindow(
          TEXT("STATIC"),
          szBlank,
          WS_CHILD | WS_VISIBLE | SS_LEFT,
          prect->left,
          top,
          (prect->right - prect->left) *3/4,
          WHEIGHT,
          hwndParent, NULL, hInst, 0);

  top += WHEIGHT*2;
  *hwndCodePage = CreateWindow(
          TEXT("STATIC"),
          szBlank,
          WS_CHILD | WS_VISIBLE | SS_LEFT,
          prect->left,
          top,
          (prect->right - prect->left) *3/4,
          WHEIGHT,
          hwndParent, NULL, hInst, 0);

  top += WHEIGHT*2;
  *hwndByteOrder = CreateWindow(
          TEXT("STATIC"),
          szBlank,
          WS_CHILD | WS_VISIBLE | SS_LEFT,
          prect->left,
          top,
          (prect->right - prect->left) *3/4,
          WHEIGHT,
          hwndParent, NULL, hInst, 0);

  top += WHEIGHT*2;
  *hwndButton = CreateWindow(
          TEXT("BUTTON"),
          TEXT("ViewText"),
          WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
          prect->left,
          top,
          prect->right - prect->left,
          WHEIGHT*9/7,
          hwndParent, NULL, hInst, 0);

  return;
}


/**************************************************************************\
*
*  function:  ManageMemory()
*
* Do all memory management here for the source and destination pointers.
*  We also enable/disable the "View..." buttons to reflect existence of data.
*
*
* PARAMETERS
*
*  message : {MMALLOC, MMFREE}
*    Alloc when requested by MMALLOC, and free the existing, passed in, pointer.
*    Free when requested by MMFREE.
*  sourcedestination : {MMSOURCE, MMDESTINATION}
*  nBytes - number to alloc on MMALLOC messages.
*  p - old pointer to be freed.
*
*
* GLOBALS
*
*  hwndButton0, hwndButton1
*
\**************************************************************************/
LPVOID ManageMemory (UINT message, UINT sourcedestination, DWORD nBytes, LPVOID p)
{
  switch (message) {
    case MMFREE :
      if (sourcedestination == MMSOURCE)
        EnableWindow (hwndButton0, FALSE);
      else if (sourcedestination == MMDESTINATION)
        EnableWindow (hwndButton1, FALSE);

      if (p != NULL) GlobalFree (GlobalHandle (p));
      return NULL;
    break;

    case MMALLOC :
      if (sourcedestination == MMSOURCE)
        EnableWindow (hwndButton0, TRUE);
      else if (sourcedestination == MMDESTINATION)
        EnableWindow (hwndButton1, TRUE);

      if (p != NULL) GlobalFree (GlobalHandle (p));
      p = (LPVOID) GlobalAlloc (GPTR, nBytes);
      return p;
    break;

  } /* end switch (message) */
  return NULL;
}
