/*@P************************************************************************
 * File           : EVMOV3.C
 * Project        : EVERLOCK EVMOV.OBJ Windows Interface DLL
 * Author         : M. Brendan Van Horn
 * Compiler       : Borland C++ v4.02
 * Description    : DLL Interface Code
 *
 * Copyright (c) 1995 Az-Tech Software, Inc.
 * ALL RIGHTS RESERVED
 ************************************************************************P@*/

#define STRICT                   /* force strict type checking */
#include <windows.h>
#include <windowsx.h>
#include <dos.h>
#include <string.h>
#include <memory.h>

#if defined( _EVMOV32 )          /* defined to support EVMOV & Win32S */
#define W32SUT_16                /* indicate 16-bit side of WIN32S interface */
#include <w32sut.h>
#endif

#include "evsb.h"
#include "azmemory.h"

/***************************************************************************
 * DPMI Registers
 ***************************************************************************/
typedef struct tagDPMI_REGS
{
   unsigned long  edi, esi, ebp, reserved, ebx, edx, ecx, eax;
   unsigned short flags, es, ds, fs, gs, ip, cs, sp, ss;
} DPMI_REGS;

/***************************************************************************
 * Module Global Variables.
 ***************************************************************************/
BOOL fDriver = FALSE;                  /* did we get the memory from AzMemory? */
LRESULT drvrStatus = 0;                /* status of last driver call. */
DPMI_REGS dpmiRegs;                    /* DPMI register structure. */
HDRVR hDriver = NULL;                  /* handle to the AzMemory driver. */
ALLOCINFO stAllocInfo;                 /* driver allocation information. */
const char szDriverName[] = "azmemory.drv";
extern char __far __pascal EVMOVD[];
extern char __far __pascal EVMOVDZ[];

/***************************************************************************
 * Internal Function Prototypes.
 ***************************************************************************/
#if defined( _EVMOV32 )
DWORD __far __pascal __export evmov_init( UT16CBPROC pfnCB, LPVOID data );
int __far __pascal __export evmov3( SB __far *lpUserSB );
DWORD __far __pascal __export evmov_proc( LPVOID data, DWORD dummy );
#endif

static BOOL RealAlloc( DWORD size );
static void RealFree( void );

//@PB
/***************************************************************************
 * This function gets called when the DLL is loaded.  At this point we need
 * to setup any special requirements for the DLL's operation.  If we can't
 * initialize the neccesary things, tell Windows that we can not load.
 ***************************************************************************/
#if defined( __BORLANDC__ )
#pragma argsused
#endif
int FAR PASCAL LibMain( HINSTANCE hInstance, WORD wDataSeg, WORD cbHeapSize,
                        LPSTR lpszCmdLine )
{
   /* if we have a local heap, unlock it for our use */
   if ( cbHeapSize != 0 )
      UnlockData( 0 );

   /* return success to Windows */
   return 1;
}

#if defined( _EVMOV32 )

//@PB
/***************************************************************************
 * Provide a dummy init function.  There is never any need to call this
 * function unless a given compiler can not handle a NULL initialization
 * function.
 ***************************************************************************/
#if defined( __BORLANDC__ )
#pragma argsused
#endif
DWORD __far __pascal __export evmov_init( UT16CBPROC pfnCB, LPVOID data )
{
   /* no initialization needed, just return success */
   return TRUE;
}

//@PB
/***************************************************************************
 * Just pass the given SB on to the internal 16-bit function.  Yeah, it's a
 * bit of a performance hit to just call the other function but the benefit
 * is that one DLL can be called from both 16-bit windows and 32-bit Win32S.
 ***************************************************************************/
#if defined( __BORLANDC__ )
#pragma argsused
#endif
DWORD __far __pascal __export evmov_proc( LPVOID data, DWORD dummy )
{
   return evmov3( data );
}

#endif

//@PB
int __far __pascal __export evmov3( SB __far *lpUserSB )
{
   DWORD dwTotalSize;
   WORD wCodeSize;
   WORD comp_status;
   WORD segSB;
   DPMI_REGS __far *lpdpmiRegs;
   char __far *lpRealSB;

   /* sanity check!  make sure we have the proper SB signature before continuing */
   if ( 0 != _fstrncmp( lpUserSB->id, "AZSB", 4 ) )

      /* invalid signature, abort back to our caller */
      return 0x7240;

   /* calculate the size of the real-mode code */
   wCodeSize = (WORD) ( EVMOVDZ - EVMOVD );

   /* calculate the amount of real-mode memory we require */
   dwTotalSize = wCodeSize + sizeof( SB );

   /* allocate the real-mode memory we require */
   if ( FALSE == RealAlloc( dwTotalSize ) )
   {
      /* set the return codes for this failure */
      lpUserSB->stat1 = 0x7002;
      lpUserSB->stat2 = (short) drvrStatus;
      lpUserSB->stat3 = (short) dwTotalSize;

      /* return to our caller with simulated EVMOV3 error */
      return 0;
   }

   /* create the pointer to the real-mode SB area */
   lpRealSB = stAllocInfo.lpProtBuff + wCodeSize;

   /* setup invalid return code in the SB structure */
   lpUserSB->stat1 = -1;
   lpUserSB->stat2 = 0;
   lpUserSB->stat3 = 0;

   /* copy the protected mode SB to the real mode SB */
   _fmemcpy( lpRealSB, lpUserSB, sizeof( SB ) );

   /* copy the EVMOV code from the data segment to the real mode buffer */
   _fmemcpy( stAllocInfo.lpProtBuff, EVMOVD, (size_t) wCodeSize );

   /* clear out the DPMI register block */
   _fmemset( &dpmiRegs, 0, sizeof( DPMI_REGS ) );

   /* CS:IP of call is SEG:0 of DOS memory */
   dpmiRegs.cs = stAllocInfo.wBuffSeg;

   /* setup pointer to DPMI registers */
   lpdpmiRegs = (DPMI_REGS __far *) &dpmiRegs;

   /* get the segment of the SB buffer */
   segSB = stAllocInfo.wBuffSeg;

   /* default to no error conditions */
   comp_status = 0;

   __asm mov   bh,0             /* flags = 0                               */
   __asm mov   cx,2             /* two words to copy to real mode stack    */
   __asm les   di,lpdpmiRegs    /* pointer to regs struct                  */
   __asm mov   ax,0301h         /* call real mode procedure                */
   __asm push  segSB            /* SEGMENT of the Real SB                  */
   __asm push  wCodeSize        /* OFFSET of the Real SB                   */
                                /* DPMI will xfer this to real mode stack  */
   __asm int   31h
   __asm add   sp,4             /* clean up what we pushed                 */
   __asm jnc   NOERROR          /* if no error code, skip getting status   */
   __asm mov   comp_status,ax   /* get error code from DPMI server         */
   __asm jmp   CONT             /* skip getting error code from EVMOV.OBJ  */

NOERROR:

   /* get any error codes from EVMOV */
   comp_status = LOWORD( dpmiRegs.eax );

CONT:

   /* copy the results to the protected mode SB */
   _fmemcpy( lpUserSB, lpRealSB, sizeof( SB ) );

   /* release the real memory buffer */
   RealFree();

   /* return our results */
   return comp_status;
}

//@PB
static BOOL RealAlloc( DWORD size )
{
   DWORD dwBuffer;
   HGLOBAL hBuffer;

   /* mark that the driver is not available until we know otherwise */
   drvrStatus = -1;

   /* setup the default contents of the allocation information struct */
   memset( &stAllocInfo, 0, sizeof( ALLOCINFO ) );
   stAllocInfo.wStructSize = sizeof( ALLOCINFO );

   /* attempt to open the AzMemory driver for our use */
   hDriver = OpenDriver( szDriverName, NULL, NULL );

   /* verify that the driver has been opened */
   if ( NULL != hDriver )
   {
      /* mark that the driver has been opened by us */
      fDriver = TRUE;

      /* try to allocate the buffer from the driver */
      drvrStatus = SendDriverMessage( hDriver, DRV_ALLOCBUFFER, size,
                                      (LPARAM) (LPALLOCINFO) &stAllocInfo );

      /* if we allocated the memory, return that everything's hunky-dorey */
      if ( ERR_OK == drvrStatus )
         return TRUE;
   } else
      fDriver = FALSE;

   /* attempt to allocate the memory */
   dwBuffer = GlobalDosAlloc( size );

   /* if we got the buffer */
   if ( 0 != dwBuffer )
   {
      /* save the values in the allocation information structure */
      stAllocInfo.dwBuffSize = size;
      stAllocInfo.wBuffSel   = LOWORD( dwBuffer );
      stAllocInfo.wBuffSeg   = HIWORD( dwBuffer );
      stAllocInfo.lpProtBuff = MK_FP( stAllocInfo.wBuffSel, 0 );
      stAllocInfo.lpRealBuff = MK_FP( stAllocInfo.wBuffSeg, 0 );

      /* set the real-mode buffer to a known state */
      _fmemset( stAllocInfo.lpProtBuff, 0xAA, (size_t) stAllocInfo.dwBuffSize );

      /* return success to our caller */
      return TRUE;
   }

   /* if we get here there is no driver and no memory available */
   return FALSE;
}

//@PB
static void RealFree( void )
{
   /* was the driver used to allocate the memory buffer? */
   if ( NULL != hDriver )
   {
      /* yes, was a buffer allocated from the driver? */
      if ( ERR_OK == drvrStatus )
      {
         /* clear the contents of the real-mode buffer */
         _fmemset( stAllocInfo.lpProtBuff, 0xBB, (size_t) stAllocInfo.dwBuffSize );

         /* yes, so free the buffer from the driver */
         drvrStatus = SendDriverMessage( hDriver, DRV_FREEBUFFER, 0, 0 );

         /* clear the contents of the memory allocation info structure */
         _fmemset( &stAllocInfo, 0, sizeof( ALLOCINFO ) );
      }

      /* close the driver since we are done with it */
      CloseDriver( hDriver, 0, 0 );
   }

   /* is there still an allocated buffer? */
   if ( 0 != stAllocInfo.wBuffSel )
   {
      /* clear the contents of the real-mode buffer */
      _fmemset( stAllocInfo.lpProtBuff, 0xBB, (size_t) stAllocInfo.dwBuffSize );

      /* yes, so we allocated the buffer with GlobalDosAlloc, free it now */
      GlobalDosFree( stAllocInfo.wBuffSel );

      /* clear the contents of the memory allocation info structure */
      _fmemset( &stAllocInfo, 0, sizeof( ALLOCINFO ) );
   }
}
