/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WGPRNSET.C
 *  WOW32 printer setup support routines
 *
 *  These routines help a Win 3.0 task to complete the printer set-up,
 *  when a user initiates the printer setup from the file menu of an
 *  application.
 *
 *  History:
 *  Created 18-Apr-1991 by Chandan Chauhan (ChandanC)
--*/


#include "precomp.h"
#pragma hdrstop

MODNAME(wgprnset.c);

DLLENTRYPOINTS  spoolerapis[WOW_SPOOLERAPI_COUNT] =  {"EXTDEVICEMODE", NULL,
                                    "DEVICEMODE", NULL,
                                    "DEVICECAPABILITIES", NULL,
                                    "OpenPrinterA", NULL,
                                    "StartDocPrinterA", NULL,
                                    "StartPagePrinter", NULL,
                                    "EndPagePrinter", NULL,
                                    "EndDocPrinter", NULL,
                                    "ClosePrinter", NULL,
                                    "WritePrinter", NULL,
                                    "DeletePrinter", NULL,
                                    "GetPrinterDriverDirectoryA", NULL,
                                    "AddPrinterA", NULL,
                                    "AddPrinterDriverA", NULL,
                                    "AddPortExA",NULL};


/****************************************************************************
*                                                                           *
*  ULONG FASTCALL   WG32DeviceMode (PVDMFRAME pFrame)                                *
*                                                                           *
*  (hWnd, hModule, lpDeviceName, lpOutPut)                                  *
*                                                                           *
*   This function passes WDevMode structure (which is per wow task) to      *
*   Win32 printer driver ExtDeviceMode API. This structure is then          *
*   initialized by the printer driver based on the user input.              *
*                                                                           *
*   Later on, when a WOW task creates a dc (by CreateDC API), the device    *
*   mode (WDevMode) structure associated with this wow task is passed along *
*   with the CreateDC API. Which contains the printer setup information     *
*   needed to print the document.                                           *
*                                                                           *
****************************************************************************/
ULONG FASTCALL   WG32DeviceMode (PVDMFRAME pFrame)
{

    register PDEVICEMODE16 parg16;
    PSZ      psz, psz3=NULL, psz4=NULL;
    HWND     hwnd32;

    GETARGPTR(pFrame, sizeof(DEVICEMODE16), parg16);

    // copy all 16-bit params now since 16-bit memory may move if this calls
    // into a 16-bit fax driver
    hwnd32 = HWND32(parg16->f1);

    GETPSZPTR(parg16->f3, psz);
    if(psz) {
        if(psz3 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz3, psz);
        }
    }
    FREEPSZPTR(psz);

    GETPSZPTR(parg16->f4, psz);
    if(psz) {
        if(psz4 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz4, psz);
        }
    }
    FREEPSZPTR(psz);

    // invalidate all flat ptrs to 16:16 memory now!
    FREEARGPTR(parg16);

    if (!(*spoolerapis[WOW_DEVICEMODE].lpfn)) {
        if (!LoadLibraryAndGetProcAddresses("WINSPOOL.DRV", spoolerapis, WOW_SPOOLERAPI_COUNT)) {
            return (0);
        }
    }

    // this can callback into a 16-bit fax driver!
    (*spoolerapis[WOW_DEVICEMODE].lpfn)(hwnd32, NULL, psz3, psz4);

    if(psz3) {
        free_w(psz3);
    }
    if(psz4) {
        free_w(psz4);
    }

    RETURN(1);  // DeviceMode returns void. Charisma checks the return value !
}





/*****************************************************************************
*                                                                            *
*  ULONG FASTCALL   WG32ExtDeviceMode (PVDMFRAME pFrame)                              *
*                                                                            *
*  INT     (hWnd, hDriver, lpDevModeOutput, lpDeviceName, lpPort,            *
*                     lpDevModeInput, lpProfile, wMode)                      *
*                                                                            *
*   This function is same as DeviceMode except that the wow task supplies    *
*   a DeviceMode structure. Apart from it, this API can be called in         *
*   different modes.                                                         *
*                                                                            *
*****************************************************************************/
ULONG FASTCALL   WG32ExtDeviceMode (PVDMFRAME pFrame)
{
    UINT      cb;
    LONG      l;
    HWND      hWnd1;
    WORD      wMode8;
    PSZ       psz;
    PSZ       psz4 = NULL, psz5 = NULL, psz7 = NULL;
    VPVOID    vpdm3, vpdm6;
    LPDEVMODE lpdmInput6;
    LPDEVMODE lpdmOutput3;
    register  PEXTDEVICEMODE16 parg16;


    GETARGPTR(pFrame, sizeof(EXTDEVICEMODE16), parg16);

    // copy the 16-bit parameters into local vars since this may callback
    // into a 16-bit fax driver and cause 16-bit memory to move
    hWnd1  = HWND32(parg16->f1);
    vpdm3  = FETCHDWORD(parg16->f3);
    vpdm6  = FETCHDWORD(parg16->f6);
    wMode8 = FETCHWORD(parg16->f8);

    GETPSZPTR(parg16->f4, psz);
    if(psz) {
        if(psz4 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz4, psz);
        }
    }
    FREEPSZPTR(psz);

    GETPSZPTR(parg16->f5, psz);
    if(psz) {
        if(psz5 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz5, psz);
        }
    }
    FREEPSZPTR(psz);

    GETPSZPTR(parg16->f7, psz);
    if(psz) {
        if(psz7 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz7, psz);
        }
    }
    FREEPSZPTR(psz);

    FREEARGPTR(parg16);
    // all flat ptrs to 16:16 memory are now invalid!!

    if (!(*spoolerapis[WOW_EXTDEVICEMODE].lpfn)) {
        if (!LoadLibraryAndGetProcAddresses("WINSPOOL.DRV", spoolerapis, WOW_SPOOLERAPI_COUNT)) {
            return (0);
        }
    }

    lpdmInput6 = ThunkDevMode16to32(FETCHDWORD(vpdm6));

    /* if they want output buffer size OR they want to fill output buffer */
    if( (wMode8 == 0) || (wMode8 & DM_OUT_BUFFER) ) {

        /* get required size for output buffer */
        l = (*spoolerapis[WOW_EXTDEVICEMODE].lpfn)(hWnd1,
                                                   NULL,
                                                   NULL,
                                                   psz4,
                                                   psz5,
                                                   lpdmInput6,
                                                   psz7,
                                                   0);

        // adjust size for WOW handling (see notes in wstruc.c)
        if(l > 0) {
            l += sizeof(WOWDM31);
            cb = (UINT)l;
        }

        /* if caller wants output buffer filled... */
        if( (wMode8 != 0) && (vpdm3 != 0L) && l > 0 ) {

            if( lpdmOutput3 = malloc_w(l) ) {

                l = (*spoolerapis[WOW_EXTDEVICEMODE].lpfn)(hWnd1,
                                                           NULL,
                                                           lpdmOutput3,
                                                           psz4,
                                                           psz5,
                                                           lpdmInput6,
                                                           psz7,
                                                           wMode8);

                /* Data in lpdmOutput3 is only valid with IDOK return. */
                if( l == IDOK ) {

                    // do our WOW magic on this before we give it to the app
                    ThunkDevMode32to16(vpdm3, lpdmOutput3, cb);
                }

                free_w(lpdmOutput3);
            }
            else {
                l = -1L;
            }
        }
    }

    /* else call for cases where they don't want to fill the output buffer */
    else {

        l = (*spoolerapis[WOW_EXTDEVICEMODE].lpfn)(hWnd1,
                                                   NULL,
                                                   NULL,
                                                   psz4,
                                                   psz5,
                                                   lpdmInput6,
                                                   psz7,
                                                   wMode8);
    }

    if( lpdmInput6 ) {
        free_w(lpdmInput6);
    }
    if(psz4) {
        free_w(psz4);
    }
    if(psz5) {
        free_w(psz5);
    }
    if(psz7) {
        free_w(psz7);
    }

    RETURN((ULONG)l);

}




ULONG FASTCALL   WG32DeviceCapabilities (PVDMFRAME pFrame)
{
    LONG      l=0L, cb;
    WORD      fwCap3;
    PSZ       psz;
    PBYTE     pOutput4, pOutput32;
    VPVOID    vpOutput4;
    PSZ       psz1 = NULL;
    PSZ       psz2 = NULL;
    LPDEVMODE lpdmInput5;
    DWORD     dwDM5;
    register  PDEVICECAPABILITIES16 parg16;

    GETARGPTR(pFrame, sizeof(DEVICECAPABILITIES16), parg16);

    // copy the 16-bit parameters into local vars since this may callback
    // into a 16-bit fax driver and cause 16-bit memory to move
    GETPSZPTR(parg16->f1, psz);
    if(psz) {
        if(psz1 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz1, psz);
        }
    }
    FREEPSZPTR(psz);

    GETPSZPTR(parg16->f2, psz);
    if(psz) {
        if(psz2 = malloc_w(lstrlen(psz)+1)) {
            lstrcpy(psz2, psz);
        }
    }
    FREEPSZPTR(psz);

    fwCap3 = FETCHWORD(parg16->f3);

    vpOutput4 = FETCHDWORD(parg16->f4);

    dwDM5 = FETCHDWORD(parg16->f5);

    FREEARGPTR(parg16);
    // all flat ptrs to 16:16 memory are now invalid!!

    if (!(*spoolerapis[WOW_DEVICECAPABILITIES].lpfn)) {
        if (!LoadLibraryAndGetProcAddresses("WINSPOOL.DRV", 
                                            spoolerapis, 
                                            WOW_SPOOLERAPI_COUNT)) {
            return (0);
        }
    }

    lpdmInput5 = ThunkDevMode16to32(dwDM5);

    LOGDEBUG(LOG_TRACE, ("WG32DeviceCapabilities %d\n", fwCap3));

    switch (fwCap3) {

        // These ones do not fill up an output Buffer

        case DC_FIELDS:
        case DC_DUPLEX:
        case DC_SIZE:
        case DC_EXTRA:
        case DC_VERSION:
        case DC_DRIVER:
        case DC_TRUETYPE:
        case DC_ORIENTATION:
        case DC_COPIES:

            l = (*spoolerapis[WOW_DEVICECAPABILITIES].lpfn)(psz1,
                                                            psz2, 
                                                            fwCap3, 
                                                            NULL, 
                                                            lpdmInput5);

            LOGDEBUG(LOG_TRACE, ("WG32DeviceCapabilities simple case returned %d\n", l));

            // adjust for WOW handling of devmodes  // see notes in wstruc.c
            if(fwCap3 == DC_SIZE) {
            
                // we always convert NT DevModes to Win3.1 DevModes
                WOW32WARNMSGF((l==sizeof(DEVMODE)),
                              ("WG32DeviceCapabilities: Unexpected DevMode size: %d\n",l));
                if(l == sizeof(DEVMODE)) {
                    l = sizeof(DEVMODE31); 
                }
            }
            // adjust DriverExtra to allow for difference between NT devmodes
            // & Win3.1 devmodes + our secret WOW stuff at the end
            else if(fwCap3 == DC_EXTRA) {
                l += WOW_DEVMODEEXTRA;
            }
            // we tell them Win3.1 for the spec version too
            else if(fwCap3 == DC_VERSION) {
                l = WOW_DEVMODE31SPEC; // tell 'em the spec version is Win3.1
            }

            break;


        // These require an output buffer
        case DC_PAPERS:
        case DC_PAPERSIZE:
        case DC_MINEXTENT:
        case DC_MAXEXTENT:
        case DC_BINS:
        case DC_BINNAMES:
        case DC_ENUMRESOLUTIONS:
        case DC_FILEDEPENDENCIES:
        case DC_PAPERNAMES:
    
            LOGDEBUG(LOG_TRACE, ("WG32DeviceCapabilities more complicated:\n"));

            // We've got to figure out how much memory we will need
            GETMISCPTR(vpOutput4, pOutput4);
            if (pOutput4) { 

                cb = (*spoolerapis[WOW_DEVICECAPABILITIES].lpfn)(psz1, 
                                                                 psz2, 
                                                                 fwCap3, 
                                                                 NULL, 
                                                                 lpdmInput5);

                FREEPSZPTR(pOutput4); // invalidate -16bit memory may have moved

                LOGDEBUG(LOG_TRACE, ("we need %d bytes ", cb));

                if (cb > 0) {

                    switch (fwCap3) {

                        case DC_PAPERS:
                            cb *= 2;
                            break;

                        case DC_BINNAMES:
                            cb *= 24;
                            break;

                        case DC_BINS:
                            cb*=2;
                            break;
    
                        case DC_FILEDEPENDENCIES:
                        case DC_PAPERNAMES:
                            cb *= 64;
                            break;

                        case DC_MAXEXTENT:
                        case DC_MINEXTENT:
                        case DC_PAPERSIZE:
                            cb *= 8;

                            LOGDEBUG(LOG_TRACE, ("DC_PAPERSIZE called: Needed %d bytes\n", cb));

                            break;

                        case DC_ENUMRESOLUTIONS:
                            cb *= sizeof(LONG)*2;
                            break;

                    } // end switch

                    pOutput32 = malloc_w(cb);

                    if (pOutput32) {

                        l = (*spoolerapis[WOW_DEVICECAPABILITIES].lpfn)(psz1, psz2, fwCap3, pOutput32, lpdmInput5);

                        if (l >= 0) {

                            GETMISCPTR(vpOutput4, pOutput4);

                            switch (fwCap3) {

                                case DC_PAPERS:
                                    if (CURRENTPTD()->dwWOWCompatFlags &
                                        WOWCF_RESETPAPER29ANDABOVE) {

                                        // wordperfect for windows 5.2 GPs if
                                        // papertype is > 0x28. so reset such 
                                        // paper types to 0x1. In particular 
                                        // this happens if the selected printer
                                        // is Epson LQ-510.
                                        //                       - nanduri

                                        LONG i = l;
                                        while(i--) {
                                            if (((LPWORD)pOutput32)[i] > 0x28) {
                                                ((LPWORD)pOutput32)[i] = 0x1;
                                            }
                                        }
                                    } // end if

                                    RtlCopyMemory(pOutput4, pOutput32, cb);
                                    break;

                                case DC_MAXEXTENT:
                                case DC_MINEXTENT:
                                case DC_PAPERSIZE:
                                    LOGDEBUG(LOG_TRACE, ("Copying %d points from %0x to %0x\n", l, pOutput32, pOutput4));

                                    putpoint16(vpOutput4, l,(LPPOINT)pOutput32);
                                    break;

                                default:
                                    LOGDEBUG(LOG_TRACE, ("Copying %d bytes from %0x to %0x\n",cb, pOutput32, pOutput4));

                                    RtlCopyMemory(pOutput4, pOutput32, cb);
                                    break;

                            } // end switch

                            FLUSHVDMPTR(vpOutput4, (USHORT)cb, pOutput4);
                            FREEPSZPTR(pOutput4);

                        } // end if

                        free_w(pOutput32);

                    } else
                        l = -1;
                } else
                    l = cb;


            } else {


                l = (*spoolerapis[WOW_DEVICECAPABILITIES].lpfn)(psz1,
                                                                psz2, 
                                                                fwCap3, 
                                                                NULL, 
                                                                lpdmInput5);

                LOGDEBUG(LOG_TRACE, ("No Output buffer specified: Returning %d\n", l));
            }

            break;

        default:
            LOGDEBUG(LOG_TRACE, ("!!!! WG32DeviceCapabilities unhandled %d\n", fwCap3));
            l = -1L;
            break;

    } // end switch

    if (lpdmInput5) {
        free_w(lpdmInput5);
    }
    if(psz1) {
        free_w(psz1);
    }
    if(psz2) {
        free_w(psz2);
    }

    RETURN(l);
}




BOOL LoadLibraryAndGetProcAddresses(char *name, DLLENTRYPOINTS *p, int num)
{
    int     i;
    HINSTANCE   hInst;

    if (!(hInst = SafeLoadLibrary (name))) {
        WOW32ASSERTMSGF (FALSE, ("WOW::LoadLibraryAndGetProcAddresses: LoadLibrary('%s') failed\n", name));
        return FALSE;
    }

    for (i = 0; i < num ; i++) {
        p[i].lpfn = (void *) GetProcAddress (hInst, (p[i].name));
        WOW32ASSERTMSGF (p[i].lpfn, ("WOW::LoadLibraryAndGetProcAddresses: GetProcAddress(%s, '%s') failed\n", name, p[i].name));
    }

    return TRUE;
}

#ifdef i386
/*
 * "Safe" version of LoadLibrary which preserves floating-point state
 * across the load.  This is critical on x86 because the FP state being
 * preserved is the 16-bit app's state.  MSVCRT.DLL is one offender which
 * changes the Precision bits in its Dll init routine.
 *
 * On RISC, this is an alias for LoadLibrary
 *
 */
HINSTANCE SafeLoadLibrary(char *name)
{
    HINSTANCE hInst;
    BYTE FpuState[108];

    // Save the 487 state
    _asm {
        lea    ecx, [FpuState]
        fsave  [ecx]
    }

    hInst = LoadLibrary(name);

    // Restore the 487 state
    _asm {
        lea    ecx, [FpuState]
        frstor [ecx]
    }

    return hInst;
}
#endif  //i386
