/********************************************************************/
/**               Copyright(c) 1989 Microsoft Corporation.	   **/
/********************************************************************/

//***
//
// Filename:	buffer.c
//
// Description: This module contains routines to manipulate cached
//		information. ie volume info, server properties and
//		ETC mappings info.
//
// History:
//		May 11,1992.	NarenG		Created original version.
//
#include "afpsvcp.h"

// This should be more than the size (in bytes) all the value names
// each AfpMultSzInfo structure. It will be used to calculate the amount
// of memory needed to create a multi-sz.
//
#define AFP_CUMULATIVE_VALNAME_SIZE	150

// This data structure will be used by AfpBufParseMultiSz and
// AfpBufMakeMultiSz.
//
typedef struct _AfpMultiSzInfo {

    DWORD	dwType;			// Type of data, string or DWORD
    DWORD	dwOffset;		// Offset of this field from the start
    LPWSTR	lpwsValueName;		// Value name for this field.
					// If this is NULL then it does not
					// have a value name. It is the
					// value name for this MULT_SZ.
    DWORD	fIsInPlace;		// If string, is it a pointer or a
					// buffer.
    DWORD	cch;			// If fIsInPlace is TRUE, then how
					// big (in UNICODE chars.) is the
					// buffer.

} AFP_MULTISZ_INFO, *PAFP_MULTISZ_INFO;

static AFP_MULTISZ_INFO AfpVolumeMultiSz[] = {

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_name ),
	NULL,
	FALSE,
	0,

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_password ),
	AFPREG_VALNAME_PASSWORD,
	FALSE,
	0,

	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_max_uses ),
	AFPREG_VALNAME_MAXUSES,
	FALSE,
	0,
	
	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_props_mask ),
	AFPREG_VALNAME_PROPS,
	FALSE,
	0,

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_path ),
	AFPREG_VALNAME_PATH,
	FALSE,
	0,

	REG_NONE, 0, 0, 0, 0
	};

static AFP_MULTISZ_INFO AfpExtensionMultiSz[] = {

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_EXTENSION, afpe_extension[0] ),
	NULL,
	TRUE,
	AFP_FIELD_SIZE( AFP_EXTENSION, afpe_extension ),

	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_EXTENSION, afpe_tcid ),
	AFPREG_VALNAME_ID,
	FALSE,
	0,

	REG_NONE, 0, 0, 0, 0
	};


static AFP_MULTISZ_INFO AfpTypeCreatorMultiSz[] = {

	REG_SZ,
	AFP_FIELD_OFFSET(AFP_TYPE_CREATOR, afptc_creator[0] ),
	AFPREG_VALNAME_CREATOR,
	TRUE,
	AFP_FIELD_SIZE(AFP_TYPE_CREATOR, afptc_creator ),

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_type[0] ),
	AFPREG_VALNAME_TYPE,
	TRUE,
	AFP_FIELD_SIZE( AFP_TYPE_CREATOR, afptc_type ),

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_comment[0] ),
	AFPREG_VALNAME_COMMENT,
	TRUE,
	AFP_FIELD_SIZE( AFP_TYPE_CREATOR, afptc_comment ),

	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_TYPE_CREATOR, afptc_id ),
	NULL,
	FALSE,
	0,

	REG_NONE, 0, 0, 0, 0
	};

static AFP_MULTISZ_INFO AfpIconMultiSz[] = {

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_type[0] ),
	AFPREG_VALNAME_TYPE,
	TRUE,
	AFP_FIELD_SIZE( AFP_ICON_INFO, afpicon_type ),

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_creator[0] ),
	AFPREG_VALNAME_CREATOR,
	TRUE,
	AFP_FIELD_SIZE( AFP_ICON_INFO, afpicon_creator ),

	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_icontype ),
	AFPREG_VALNAME_ICONTYPE,
	FALSE,
	0,

	REG_DWORD,
	AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_length ),
	AFPREG_VALNAME_LENGTH,
	FALSE,
	0,

	REG_SZ,
	AFP_FIELD_OFFSET( AFP_ICON_INFO, afpicon_data ),
	AFPREG_VALNAME_DATA,
	FALSE,
	0,

	REG_NONE, 0, 0, 0, 0
	};

// These arrays represents the byte offsets, from the beginning of the
// structure, of the LPWSTR fields.
//
static BYTE ServerOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_name ),
	AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_login_msg ),
	AFP_FIELD_OFFSET( AFP_SERVER_INFO, afpsrv_codepage ),
	0xFF
	};

static BYTE VolumeOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_name ),
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_password ),
	AFP_FIELD_OFFSET( AFP_VOLUME_INFO, afpvol_path ),
	0xFF
	};

static BYTE DirOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_path ),
	AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_owner ),
	AFP_FIELD_OFFSET( AFP_DIRECTORY_INFO, afpdir_group ),
	0xFF
	};

static BYTE SessionOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_SESSION_INFO, afpsess_ws_name ),
	AFP_FIELD_OFFSET( AFP_SESSION_INFO, afpsess_username ),
	0xFF
	};

static BYTE FileOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_FILE_INFO, afpfile_path ),
	AFP_FIELD_OFFSET( AFP_FILE_INFO, afpfile_username ),
	0xFF
	};

static BYTE ConnectionOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_CONNECTION_INFO, afpconn_username ),
	AFP_FIELD_OFFSET( AFP_CONNECTION_INFO, afpconn_volumename ),
	0xFF
	};

static BYTE MessageOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_MESSAGE_INFO, afpmsg_text ),
	0xFF
	};

static BYTE FinderOffsetTable[] = {
	AFP_FIELD_OFFSET( AFP_FINDER_INFO, afpfd_path ),
	0xFF
	};


//**
//
// Call:	AfpBufStructureSize
//
// Returns:	The size (in bytes) of the data withing the structure.
//
// Description:	It will calculate the size of all the variable data and
//		add that to the fixed size of the structure.
//
DWORD
AfpBufStructureSize(
	IN AFP_STRUCTURE_TYPE	dwStructureType,
	IN LPBYTE		lpbStructure
)
{
DWORD	cbStructureSize;
DWORD	dwIndex;
DWORD	cbBufSize;
LPWSTR* plpwsStringField;
PBYTE	OffsetTable;

    switch( dwStructureType ) {

    case AFP_VOLUME_STRUCT:
	OffsetTable 	= VolumeOffsetTable;
	cbStructureSize = sizeof( AFP_VOLUME_INFO );
	break;

    case AFP_SERVER_STRUCT:
	OffsetTable 	= ServerOffsetTable;
	cbStructureSize = sizeof( AFP_SERVER_INFO );
	break;

    case AFP_DIRECTORY_STRUCT:
	OffsetTable 	= DirOffsetTable;
	cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
	break;

    case AFP_EXTENSION_STRUCT:
	return( sizeof(AFP_EXTENSION) );
	break;

    case AFP_TYPECREATOR_STRUCT:
	return( sizeof(AFP_TYPE_CREATOR) );
	break;

    case AFP_MESSAGE_STRUCT:
	OffsetTable 	= MessageOffsetTable;
	cbStructureSize = sizeof( AFP_MESSAGE_INFO );
	break;

    case AFP_ICON_STRUCT:
	return( sizeof(AFP_ICON_INFO) +
		((PAFP_ICON_INFO)lpbStructure)->afpicon_length );
	break;

    case AFP_FINDER_STRUCT:
	OffsetTable 	= FinderOffsetTable;
	cbStructureSize = sizeof( AFP_FINDER_INFO );
	break;

    default:
	return( 0 );
    }

    // First calculate the amount of memory that will be needed to
    // store all the string information.
    //
    for( dwIndex = 0, cbBufSize = 0;

	 OffsetTable[dwIndex] != 0xFF;

	 dwIndex++

	) {
	
   	plpwsStringField=(LPWSTR*)((ULONG_PTR)lpbStructure + OffsetTable[dwIndex]);

        cbBufSize += ( ( *plpwsStringField == NULL ) ? 0 :
		         STRLEN( *plpwsStringField ) + 1 );
    }

    // Convert to UNICODE size
    //
    cbBufSize *= sizeof( WCHAR );

    // Add size of fixed part of the structure
    //
    cbBufSize += cbStructureSize;

    return( cbBufSize );

}

//**
//
// Call:	AfpBufMakeFSDRequest
//
// Returns:	NO_ERROR	
//		ERROR_NOT_ENOUGH_MEMORY	
//
// Description: This routine is called by the worker routines for the client
//		API calls. The purpose of this routine is to convert a
//		AFP_XXX_INFO structure passed by the client API into a
//		contiguous self-relative buffer. This has to be done because
//		the FSD cannot reference pointers to user space.
//
//		This routine will allocate the required amount of memory to
//		store all the information in self relative form. It is
//		the reponsibility of the caller to free this memory.
//
//		All pointer fields will be converted to offsets from the
//		beginning of the structure.
//		
//		The cbReqPktSize parameter specifies how many bytes of space
//		should be allocated before the self relative data structure.
//		i.e.
//				|------------|
//				|cbReqPktSize|
//				|   bytes    |
//				|------------|
//				|   Self     |
//				|  relative  |
//				| structure  |
//				|------------|
//
DWORD
AfpBufMakeFSDRequest(

	// Buffer as received by the client API	
	//
	IN  LPBYTE  		pBuffer,

	// Size of FSD request packet.
	//
	IN  DWORD		cbReqPktSize,

	IN  AFP_STRUCTURE_TYPE dwStructureType,

	// Self-relative form of I/P buf
	//
	OUT LPBYTE 		*ppSelfRelativeBuf,

	// Size of self relative buf
	//
	OUT LPDWORD		lpdwSelfRelativeBufSize
)
{
LPBYTE		 lpbSelfRelBuf;
DWORD		 cbSRBufSize;
DWORD		 dwIndex;
LPWSTR		 lpwsVariableData;
LPWSTR *	 plpwsStringField;
LPWSTR *	 plpwsStringFieldSR;
PBYTE		 OffsetTable;
DWORD		 cbStructureSize;


    // Initialize the offset table and the structure size values
    //
    switch( dwStructureType ) {

    case AFP_VOLUME_STRUCT:
	OffsetTable     = VolumeOffsetTable;
	cbStructureSize = sizeof( AFP_VOLUME_INFO );
	break;

    case AFP_SERVER_STRUCT:
	OffsetTable     = ServerOffsetTable;
	cbStructureSize = sizeof( AFP_SERVER_INFO );
	break;

    case AFP_DIRECTORY_STRUCT:
	OffsetTable = DirOffsetTable;
	cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
	break;

    case AFP_MESSAGE_STRUCT:
	OffsetTable = MessageOffsetTable;
	cbStructureSize = sizeof( AFP_MESSAGE_INFO );
	break;

    case AFP_FINDER_STRUCT:
	OffsetTable = FinderOffsetTable;
	cbStructureSize = sizeof( AFP_FINDER_INFO );
	break;

    default:
	return( ERROR_INVALID_PARAMETER );
    }

    cbSRBufSize = cbReqPktSize + AfpBufStructureSize(dwStructureType, pBuffer);

    // Allocate space for self relative buffer
    //
    if ( ( lpbSelfRelBuf = (LPBYTE)LocalAlloc( LPTR, cbSRBufSize ) ) == NULL )
	return( ERROR_NOT_ENOUGH_MEMORY );

    *ppSelfRelativeBuf       = lpbSelfRelBuf;
    *lpdwSelfRelativeBufSize = cbSRBufSize;

    // Advance this pointer beyond the request packet
    //
    lpbSelfRelBuf += cbReqPktSize;

    // memcpy to fill in the non-string data
    //
    CopyMemory( lpbSelfRelBuf, pBuffer, cbStructureSize );

    // Now copy all the strings
    //
    for( dwIndex = 0,
	 lpwsVariableData = (LPWSTR)((ULONG_PTR)lpbSelfRelBuf + cbStructureSize);

	 OffsetTable[dwIndex] != 0xFF;

	 dwIndex++ ) {

	
	// This will point to a string pointer field in the non self-relative
	// structure.
	//
   	plpwsStringField = (LPWSTR*)((ULONG_PTR)pBuffer + OffsetTable[dwIndex]);

	// This will point to the corresponding string pointer field in the
	// self-relative structure
	//
   	plpwsStringFieldSR=(LPWSTR*)((ULONG_PTR)lpbSelfRelBuf+OffsetTable[dwIndex]);

	// If there is no string to be copied, then just set to NULL
  	//
    	if ( *plpwsStringField == NULL )
       	    *plpwsStringFieldSR = NULL;
	else {

	    // There is a string so copy it
	    //
            STRCPY( lpwsVariableData, *plpwsStringField );

	    // Store the pointer value
	    //
            *plpwsStringFieldSR = lpwsVariableData;

	    // Convert the pointer to this data to an offset
	    //
            POINTER_TO_OFFSET( *plpwsStringFieldSR, lpbSelfRelBuf );
	
	    // Update the pointer to where the next variable length data
	    // will be stored.
	    //
    	    lpwsVariableData += ( STRLEN( *plpwsStringField ) + 1 );

	}

    }

    return( NO_ERROR );

}

//**
//
// Call:	AfpBufOffsetToPointer
//
// Returns:	none.
//
// Description:	Will walk a list of structures, converting all offsets
//		within each structure to pointers.
//
VOID
AfpBufOffsetToPointer(
	IN OUT LPBYTE	          pBuffer,
	IN     DWORD		  dwNumEntries,
	IN     AFP_STRUCTURE_TYPE dwStructureType
)
{
PBYTE		OffsetTable;
DWORD		cbStructureSize;
LPWSTR 	       *plpwsStringField;
DWORD		dwIndex;


    // Initialize the offset table and the structure size values
    //
    switch( dwStructureType ) {

    case AFP_VOLUME_STRUCT:
	OffsetTable 	= VolumeOffsetTable;
	cbStructureSize = sizeof( AFP_VOLUME_INFO );
 	break;

    case AFP_SESSION_STRUCT:
	OffsetTable 	= SessionOffsetTable;
	cbStructureSize = sizeof( AFP_SESSION_INFO );
 	break;

    case AFP_CONNECTION_STRUCT:
	OffsetTable 	= ConnectionOffsetTable;
	cbStructureSize = sizeof( AFP_CONNECTION_INFO );
 	break;

    case AFP_FILE_STRUCT:
	OffsetTable 	= FileOffsetTable;
	cbStructureSize = sizeof( AFP_FILE_INFO );
 	break;

    case AFP_DIRECTORY_STRUCT:
	OffsetTable 	= DirOffsetTable;
	cbStructureSize = sizeof( AFP_DIRECTORY_INFO );
 	break;

    case AFP_MESSAGE_STRUCT:
	OffsetTable 	= MessageOffsetTable;
	cbStructureSize = sizeof( AFP_MESSAGE_INFO );
 	break;

    case AFP_SERVER_STRUCT:
	OffsetTable 	= ServerOffsetTable;
	cbStructureSize = sizeof( AFP_SERVER_INFO );
 	break;

    default:
	return;
    }

    // Walk the list and convert each structure.
    //
    while( dwNumEntries-- ) {

	// Convert every LPWSTR from an offset to a pointer
	//
        for( dwIndex = 0;  OffsetTable[dwIndex] != 0xFF;  dwIndex++ ) {
	
	    plpwsStringField = (LPWSTR*)( (ULONG_PTR)pBuffer
					  + (DWORD)OffsetTable[dwIndex] );

	    OFFSET_TO_POINTER( *plpwsStringField, pBuffer );
	
	}

	pBuffer += cbStructureSize;
	
    }

    return;
}

//**
//
// Call:	AfpBufMakeMultiSz
//
// Returns:	NO_ERROR	- success
//		ERROR_NOT_ENOUGH_MEMORY
//
// Description: This routine will take a give structure and create a
//		REG_MULTI_SZ from it. This can then be set directly into the
//		registry. It is the caller's responsibility to free
//		the memory allocated for *ppbMultiSz.
//
DWORD
AfpBufMakeMultiSz(
	IN  AFP_STRUCTURE_TYPE  dwStructureType,
	IN  LPBYTE		lpbStructure,
	OUT LPBYTE *		ppbMultiSz,
	OUT LPDWORD		lpdwMultiSzSize
)
{
PAFP_MULTISZ_INFO	pAfpMultiSz;
PWCHAR			lpwchWalker;			
PVOID			pData;
DWORD			dwIndex;
DWORD			cbStructureSize;

    switch( dwStructureType ) {

    case AFP_VOLUME_STRUCT:
	pAfpMultiSz = AfpVolumeMultiSz;
	break;

    case AFP_EXTENSION_STRUCT:
	pAfpMultiSz = AfpExtensionMultiSz;
	break;

    case AFP_TYPECREATOR_STRUCT:
	pAfpMultiSz = AfpTypeCreatorMultiSz;
	break;

    case AFP_ICON_STRUCT:
	pAfpMultiSz = AfpIconMultiSz;
	break;

    default:
	return( ERROR_INVALID_PARAMETER );
    }

    // Allocate enough memory to create the multi-sz.
    // AFP_CUMULATIVE_VALNAME_SIZE should be greater than the sum of all the
    // value names of all the structures.
    //
    cbStructureSize = AfpBufStructureSize( dwStructureType, lpbStructure )
		      + AFP_CUMULATIVE_VALNAME_SIZE;

    if ( ( *ppbMultiSz = (LPBYTE)LocalAlloc( LPTR, cbStructureSize ) ) == NULL )
	return( ERROR_NOT_ENOUGH_MEMORY );

    ZeroMemory( *ppbMultiSz, cbStructureSize );

    // For every field, we create a string
    //
    for ( dwIndex = 0,
	  lpwchWalker = (PWCHAR)*ppbMultiSz;

	  pAfpMultiSz[dwIndex].dwType != REG_NONE;

	  dwIndex++

	){
	
	// This is the value name so do not put it in the buffer.
	//
	if ( pAfpMultiSz[dwIndex].lpwsValueName == NULL )
	    continue;

	STRCPY( lpwchWalker, pAfpMultiSz[dwIndex].lpwsValueName );
	STRCAT( lpwchWalker, TEXT("="));

	lpwchWalker += STRLEN( lpwchWalker );

	pData = lpbStructure + pAfpMultiSz[dwIndex].dwOffset;

	// Convert to string and concatenate
	//
	if ( pAfpMultiSz[dwIndex].dwType == REG_DWORD ) {

	    UCHAR chAnsiBuf[12];
	
	    _itoa( *((LPDWORD)pData), chAnsiBuf, 10 );
	
	    mbstowcs( lpwchWalker, chAnsiBuf, sizeof(chAnsiBuf) );
	}

	if ( pAfpMultiSz[dwIndex].dwType == REG_SZ ) {

	    // Check if this is a pointer or an in-place buffer
	    //
	    if ( pAfpMultiSz[dwIndex].fIsInPlace )
	     	STRCPY( lpwchWalker, (LPWSTR)pData );
	    else {

		if ( *(LPWSTR*)pData != NULL )
	    	    STRCPY( lpwchWalker, *((LPWSTR*)pData) );
	    }
	}

	lpwchWalker += ( STRLEN( lpwchWalker ) + 1 );
	
    }

    *lpdwMultiSzSize = (DWORD)((ULONG_PTR)lpwchWalker - (ULONG_PTR)(*ppbMultiSz) ) + sizeof(WCHAR);

    return( NO_ERROR );
}

//**
//
// Call:	AfpBufParseMultiSz
//
// Returns:	NO_ERROR		- success
//		ERROR_INVALID_PARAMETER
//
// Description: This routine will parse a REG_MULTI_SZ and fill in the
//		appropriate data structure. All pointers will point to
//		the pbMultiSz input parameter.
//
DWORD
AfpBufParseMultiSz(
	IN  AFP_STRUCTURE_TYPE  dwStructureType,
	IN  LPBYTE		pbMultiSz,
	OUT LPBYTE		pbStructure
)
{
PAFP_MULTISZ_INFO	pAfpMultiSz;
DWORD			dwIndex;
DWORD			cbStructSize;
LPWSTR			lpwchWalker;
PVOID			pData;
UCHAR           chAnsiBuf[12];
DWORD           dwDisableCatsearch=0;

    switch( dwStructureType ) {

    case AFP_VOLUME_STRUCT:
	pAfpMultiSz  = AfpVolumeMultiSz;
	cbStructSize = sizeof( AFP_VOLUME_INFO );

    //
    // The following "quick fix" is for Disabling CatSearch support.  Read in the
    // DisableCatsearch parameter if it's put in.  In most cases, this parm won't
    // be there.  If it is, the server disables CatSearch
    //
    for ( (lpwchWalker = (LPWSTR)pbMultiSz);
          (*lpwchWalker != TEXT('\0') );
          (lpwchWalker += ( STRLEN( lpwchWalker ) + 1 ) ))
    {
	    if ( STRNICMP( AFPREG_VALNAME_CATSEARCH,
			           lpwchWalker,
			           STRLEN( AFPREG_VALNAME_CATSEARCH ) ) == 0 )
        {
	        lpwchWalker += ( STRLEN( AFPREG_VALNAME_CATSEARCH ) + 1 );
           	wcstombs( chAnsiBuf, lpwchWalker, sizeof(chAnsiBuf) );
	    	dwDisableCatsearch = atoi( chAnsiBuf );
            break;
        }
    }

	break;

    case AFP_EXTENSION_STRUCT:
	pAfpMultiSz = AfpExtensionMultiSz;
	cbStructSize = sizeof( AFP_EXTENSION );
	break;

    case AFP_TYPECREATOR_STRUCT:
	pAfpMultiSz = AfpTypeCreatorMultiSz;
	cbStructSize = sizeof( AFP_TYPE_CREATOR );
	break;

    case AFP_ICON_STRUCT:
	pAfpMultiSz = AfpIconMultiSz;
	cbStructSize = sizeof( AFP_ICON_INFO );
	break;

    default:
	return( ERROR_INVALID_PARAMETER );
    }

    ZeroMemory( pbStructure, cbStructSize );

    // For every field in the structure
    //
    for ( dwIndex = 0; pAfpMultiSz[dwIndex].dwType != REG_NONE; dwIndex++ ){
	
	// This is the value name so do not try to retrieve it from the
	// buffer.
	//
	if ( pAfpMultiSz[dwIndex].lpwsValueName == NULL )
	    continue;

	// Search for valuename for this field
	//
        for (  lpwchWalker = (LPWSTR)pbMultiSz;

	       ( *lpwchWalker != TEXT('\0') )
	       &&
	       ( STRNICMP( pAfpMultiSz[dwIndex].lpwsValueName,
			   lpwchWalker,
			   STRLEN(pAfpMultiSz[dwIndex].lpwsValueName) ) != 0 );

	       lpwchWalker += ( STRLEN( lpwchWalker ) + 1 ) );

	// Could not find parameter
	//
	if ( *lpwchWalker == TEXT('\0') )
	    return( ERROR_INVALID_PARAMETER );

	// Otherwise we found it so get the value
	//
	lpwchWalker += ( STRLEN( pAfpMultiSz[dwIndex].lpwsValueName ) + 1 );

	pData = pbStructure + pAfpMultiSz[dwIndex].dwOffset;

	// If there is no value after the value name then ignore this field
	// It defaults to zero.
	//
        if ( *lpwchWalker != TEXT( '\0' ) ) {

	    // Convert to integer
	    //
	    if ( pAfpMultiSz[dwIndex].dwType == REG_DWORD ) {
	
            	wcstombs( chAnsiBuf, lpwchWalker, sizeof(chAnsiBuf) );

	    	*((LPDWORD)pData) = atoi( chAnsiBuf );
	
	    }

        //
        // CatSearch hack continued: if we are looking at the volume mask
        // parameter, see if we must turn the bit off.
        //
        if( dwStructureType == AFP_VOLUME_STRUCT && dwDisableCatsearch )
        {
	        if ( STRNICMP( pAfpMultiSz[dwIndex].lpwsValueName,
			              AFPREG_VALNAME_PROPS,
			              STRLEN(pAfpMultiSz[dwIndex].lpwsValueName) ) == 0 )
            {
                *((LPDWORD)pData) |= AFP_VOLUME_DISALLOW_CATSRCH;
            }
        }

	    if ( pAfpMultiSz[dwIndex].dwType == REG_SZ ) {

	    	// Check if this is a pointer or an in-place buffer
	    	//
	    	if ( pAfpMultiSz[dwIndex].fIsInPlace ) {

		    if ( STRLEN( lpwchWalker ) > pAfpMultiSz[dwIndex].cch )
    			return( ERROR_INVALID_PARAMETER );

		    STRCPY( (LPWSTR)pData, lpwchWalker );
		}
	    	else
		    *((LPWSTR*)pData) = lpwchWalker;
	    }

	}

    }

    return( NO_ERROR );

}

//**
//
// Call:	AfpBufMakeFSDETCMappings
//
// Returns:	NO_ERROR	
//		ERROR_NOT_ENOUGH_MEMORY	
//
// Description: This routine will convert all the mappings in the
//		form stored in AfpGlobals.AfpETCMapInfo to the form
//		required by the FSD, ie. the ETCMAPINFO structure.
//		It is the responsibility for the caller to free
//		allocated memory.
//
DWORD
AfpBufMakeFSDETCMappings(
	OUT PSRVETCPKT		*ppSrvSetEtc,
	OUT LPDWORD		lpdwSrvSetEtcBufSize
)
{
DWORD			dwIndex;
PETCMAPINFO2		pETCMapInfo;
PAFP_EXTENSION		pExtensionWalker;
PAFP_TYPE_CREATOR	pTypeCreator;
AFP_TYPE_CREATOR	AfpTypeCreatorKey;
DWORD			dwNumTypeCreators;


    // Allocate space to hold the ETCMaps in the form required by the FSD.
    //
    *ppSrvSetEtc = (PSRVETCPKT)LocalAlloc( LPTR,
	  AFP_FIELD_SIZE( SRVETCPKT, retc_NumEtcMaps ) +
          (AfpGlobals.AfpETCMapInfo.afpetc_num_extensions*sizeof(ETCMAPINFO2)));

    if ( *ppSrvSetEtc == NULL )
	return( ERROR_NOT_ENOUGH_MEMORY );

    // Walk through the extension list
    //
    for( dwIndex 	   = 0,
	 pETCMapInfo       = (*ppSrvSetEtc)->retc_EtcMaps,
	 pExtensionWalker  = AfpGlobals.AfpETCMapInfo.afpetc_extension,
	 pTypeCreator      = AfpGlobals.AfpETCMapInfo.afpetc_type_creator,
	 dwNumTypeCreators = AfpGlobals.AfpETCMapInfo.afpetc_num_type_creators,
    	 (*ppSrvSetEtc)->retc_NumEtcMaps = 0;

	 dwIndex < AfpGlobals.AfpETCMapInfo.afpetc_num_extensions;

	 dwIndex++,
	 dwNumTypeCreators = AfpGlobals.AfpETCMapInfo.afpetc_num_type_creators,
	 pExtensionWalker++

	) {

	
	// Ignore any extensions that are associated with the default
	// type/creator. They shouldnt be in the registry to begin with.
	//
	if ( pExtensionWalker->afpe_tcid == AFP_DEF_TCID )
	    continue;
	
	// Find the type/creator associated with this extension.
	//
  	AfpTypeCreatorKey.afptc_id = pExtensionWalker->afpe_tcid;

    	pTypeCreator = _lfind(  &AfpTypeCreatorKey,
			       AfpGlobals.AfpETCMapInfo.afpetc_type_creator,
			       (unsigned int *)&dwNumTypeCreators,
			       sizeof(AFP_TYPE_CREATOR),
			       AfpLCompareTypeCreator );
	

	// If there is a type/creator associated with this extension
	//
	if ( pTypeCreator != NULL ) {

	    AfpBufCopyFSDETCMapInfo( pTypeCreator,
				     pExtensionWalker,
				     pETCMapInfo );

	    pETCMapInfo++;
    	    (*ppSrvSetEtc)->retc_NumEtcMaps++;
	}

    }

    *lpdwSrvSetEtcBufSize = AFP_FIELD_SIZE( SRVETCPKT, retc_NumEtcMaps ) +
    	    ((*ppSrvSetEtc)->retc_NumEtcMaps * sizeof(ETCMAPINFO2));

    return( NO_ERROR );
}

//**
//
// Call:	AfpBufMakeFSDIcon
//
// Returns:	none.
//
// Description: This routine will copy the icon information from the
//		AFP_ICON_INFO data structure to an SRVICONINFO data
//		structure viz. the form that the FSD needs.
//
VOID
AfpBufMakeFSDIcon(
	IN  PAFP_ICON_INFO pIconInfo,
	OUT LPBYTE	   lpbFSDIcon,
	OUT LPDWORD	   lpcbFSDIconSize
)
{
UCHAR	chBuffer[sizeof(AFP_ICON_INFO)]; // Need enough space to translate

    // Blank out the whole structure so that type and creator will
    // be padded with blanks
    //
    memset( lpbFSDIcon, ' ', sizeof(SRVICONINFO) );

    // Convert to ANSI and copy type
    //
    wcstombs(chBuffer,pIconInfo->afpicon_type,sizeof(chBuffer));

    CopyMemory( ((PSRVICONINFO)lpbFSDIcon)->icon_type,
	    	chBuffer,
	    	STRLEN(pIconInfo->afpicon_type));

    // Convert to ANSI copy creator
    //
    wcstombs(chBuffer,pIconInfo->afpicon_creator,sizeof(chBuffer));

    CopyMemory( ((PSRVICONINFO)lpbFSDIcon)->icon_creator,
	      	chBuffer,
	    	STRLEN(pIconInfo->afpicon_creator));

    // Set icon type
    //
    ((PSRVICONINFO)lpbFSDIcon)->icon_icontype = pIconInfo->afpicon_icontype;

    // Set icon data length
    //
    ((PSRVICONINFO)lpbFSDIcon)->icon_length = pIconInfo->afpicon_length;

    CopyMemory( lpbFSDIcon + sizeof(SRVICONINFO),
	        pIconInfo->afpicon_data,
	    	((PSRVICONINFO)lpbFSDIcon)->icon_length );

    *lpcbFSDIconSize = sizeof(SRVICONINFO) + pIconInfo->afpicon_length;

    return;
}

//**
//
// Call:	AfpBufCopyFSDETCMapInfo
//
// Returns:	none
//
// Description: This routine will copu information from the AFP_TYPE_CREATOR
//		and AFP_EXTENSION data structures into a ETCMAPINFO data
//		structure viz. in the form as required by the FSD.
//
VOID
AfpBufCopyFSDETCMapInfo( 	
	IN  PAFP_TYPE_CREATOR 	pAfpTypeCreator,
	IN  PAFP_EXTENSION	pAfpExtension,
	OUT PETCMAPINFO2         pFSDETCMapInfo
)
{
    CHAR	Buffer[sizeof(AFP_TYPE_CREATOR)];


    // Insert blanks which will be used to pad type/creators less
    // than their max. lengths.
    //
    memset( (LPBYTE)pFSDETCMapInfo, ' ', sizeof(ETCMAPINFO2) );
    ZeroMemory( (LPBYTE)(pFSDETCMapInfo->etc_extension),
            	AFP_FIELD_SIZE( ETCMAPINFO2, etc_extension ) );

    CopyMemory( pFSDETCMapInfo->etc_extension,
                pAfpExtension->afpe_extension,
	    	    wcslen(pAfpExtension->afpe_extension) * sizeof(WCHAR));

    wcstombs( Buffer, pAfpTypeCreator->afptc_type, sizeof(Buffer) );
    CopyMemory( pFSDETCMapInfo->etc_type,
	      	Buffer,
	    	STRLEN(pAfpTypeCreator->afptc_type));

    wcstombs( Buffer, pAfpTypeCreator->afptc_creator, sizeof(Buffer) );
    CopyMemory( pFSDETCMapInfo->etc_creator,
	    	Buffer,
	    	STRLEN(pAfpTypeCreator->afptc_creator));

    return;

}

//**
//
// Call:	AfpBufUnicodeToNibble
//
// Returns:	NO_ERROR
//		ERROR_INVALID_PARAMETER
//
// Description: This routine will take a pointer to a UNCODE string and
//		convert each UNICODE char to a the corresponding nibble.
//		it char. 'A' will be converted to a nibble having value 0xA
//		This conversion is done in-place.
//
DWORD
AfpBufUnicodeToNibble(
	IN OUT LPWSTR	lpwsData
)
{
DWORD 	dwIndex;
BYTE	bData;
LPBYTE  lpbData = (LPBYTE)lpwsData;

    // Convert each UNICODE character to nibble. (in place)
    //
    for ( dwIndex = 0; *lpwsData != TEXT('\0'); dwIndex++, lpwsData++ ) {

	if ( iswalpha( *lpwsData ) ) {
	
	    if ( iswupper( *lpwsData ) )
            	bData = *lpwsData - TEXT('A');
	    else
            	bData = *lpwsData - TEXT('a');

	    bData += 10;
	
	    if ( bData > 0x0F )
		return( ERROR_INVALID_PARAMETER );
	}
	else if ( iswdigit( *lpwsData ) )
	    bData = *lpwsData - TEXT('0');
	else
	    return( ERROR_INVALID_PARAMETER );

	// Multipy so that data is in the most significant nibble.
	// Do this every other time.
	//
	if ( ( dwIndex % 2 ) == 0 )
	    *lpbData = bData * 16;
	else {
	    *lpbData += bData;
	    lpbData++;
	}
				
				
    }

    return( NO_ERROR );
}

//**
//
// Call:	AfpBCompareTypeCreator
//
// Returns:	< 0  if pAfpTypeCreator1 comes before pAfpTypeCreator2
// 		> 0  if pAfpTypeCreator1 comes before pAfpTypeCreator2
//		== 0 if pAfpTypeCreator1 is equal to  pAfpTypeCreator2
//
// Description: This routine is called by qsort to sort the list of
//		type creators in the cache. The list is sorted in
//		ascending alphabetical order of the concatenation of the
//		creator and type. This list is sorted to facilitate quick
//		lookup (binary search). This routine is also called by
//		bsearch to do a binary search on the list.
//
int
_cdecl
AfpBCompareTypeCreator(
	IN const void * pAfpTypeCreator1,
	IN const void * pAfpTypeCreator2
)
{
WCHAR	wchTypeCreator1[ sizeof( AFP_TYPE_CREATOR )];
WCHAR	wchTypeCreator2[ sizeof( AFP_TYPE_CREATOR )];
		

    STRCPY(wchTypeCreator1,
	   ((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_creator);

    if (STRLEN(((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_creator) == 0)
        wchTypeCreator1[0]=L'\0';

    STRCAT(wchTypeCreator1,((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_type );

    STRCPY(wchTypeCreator2,
	   ((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_creator);

    STRCAT(wchTypeCreator2,((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_type );

    return( STRCMP( wchTypeCreator1, wchTypeCreator2 ) );
}

//**
//
// Call:	AfpLCompareTypeCreator
//
// Returns:	< 0  if pAfpTypeCreator1 comes before pAfpTypeCreator2
// 		> 0  if pAfpTypeCreator1 comes before pAfpTypeCreator2
//		== 0 if pAfpTypeCreator1 is equal to  pAfpTypeCreator2
//
// Description: This routine is called by lfind to do a linear search of
//		the type/creator list.
//
int
_cdecl
AfpLCompareTypeCreator(
	IN const void * pAfpTypeCreator1,
	IN const void * pAfpTypeCreator2
)
{

    return( ( ((PAFP_TYPE_CREATOR)pAfpTypeCreator1)->afptc_id ==
    	      ((PAFP_TYPE_CREATOR)pAfpTypeCreator2)->afptc_id ) ? 0 : 1 );
}

//**
//
// Call:	AfpBCompareExtension
//
// Returns:	< 0  if pAfpExtension1 comes before pAfpExtension2
// 		> 0  if pAfpExtension1 comes before pAfpExtension2
//		== 0 if pAfpExtension1 is equal to  pAfpExtension2
//
// Description: This is called by qsort to sort the list of extensions in the
//		cache. The list is sorted by ID. This routine is also called
//		by bserach to do a binary lookup of this list.
//
int
_cdecl
AfpBCompareExtension(
	IN const void * pAfpExtension1,
	IN const void * pAfpExtension2
)
{
    return((((PAFP_EXTENSION)pAfpExtension1)->afpe_tcid ==
    	    ((PAFP_EXTENSION)pAfpExtension2)->afpe_tcid ) ? 0 :
          ((((PAFP_EXTENSION)pAfpExtension1)->afpe_tcid <
    	    ((PAFP_EXTENSION)pAfpExtension2)->afpe_tcid ) ? -1 : 1 ));

}

//**
//
// Call:	AfpLCompareExtension
//
// Returns:	< 0  if pAfpExtension1 comes before pAfpExtension2
// 		> 0  if pAfpExtension1 comes before pAfpExtension2
//		== 0 if pAfpExtension1 is equal to  pAfpExtension2
//
// Description: This routine is called by lfind to do a linear lookup of the
//		list of extensions in the cache.
//
int
_cdecl
AfpLCompareExtension(
	IN const void * pAfpExtension1,
	IN const void * pAfpExtension2
)
{
    return( STRICMP( ((PAFP_EXTENSION)pAfpExtension1)->afpe_extension,
    		     ((PAFP_EXTENSION)pAfpExtension2)->afpe_extension ) );
}

//**
//
// Call:	AfpBinarySearch
//
// Returns:	Pointer to first occurance of element that matches pKey.
//
// Description: This is a wrapper around bsearch. Since bsearch does not
//		return the first occurance of an element within the array,
//		this routine will back up to point to the first occurance
//		of a record with a particular key is reached.
//
void *
AfpBinarySearch(
	IN const void * pKey,
	IN const void * pBase,
	IN size_t num,
	IN size_t width,
	IN int (_cdecl *compare)(const void * pElem1, const void * pElem2 )
)
{
void * pCurrElem = bsearch( pKey, pBase, num, width, compare);


    if ( pCurrElem == NULL )
	return( NULL );

    // Backup until first occurance is reached
    //
    while ( ( (ULONG_PTR)pCurrElem > (ULONG_PTR)pBase )
	    &&
	    ( (*compare)( pKey, (void*)((ULONG_PTR)pCurrElem - width) ) == 0 ) )

	pCurrElem = (void *)((ULONG_PTR)pCurrElem - width);

    return( pCurrElem );

}