//=======================================================================
//
//  Copyright (c) 1999 Microsoft Corporation.  All Rights Reserved.
//
//  File:    filecrc.h
//
//  Purpose: Calculating and using CRC for files
//
//=======================================================================

#include <windows.h>
#include <objbase.h>
#include <filecrc.h>
#include <search.h>   // for bsearch
#include <tchar.h>
#include <atlconv.h>


HRESULT GetCRCNameFromList(int iNo, PBYTE pmszCabList, PBYTE pCRCList, LPTSTR pszCRCName, int cbCRCName, LPTSTR pszCabName)
{
	USES_CONVERSION;
	
	int i = 0;
	WUCRC_HASH* pCRC = (WUCRC_HASH*)pCRCList;

	if ( (NULL == pmszCabList) || (NULL == pCRCList) )
		return E_INVALIDARG;

	for (LPSTR pszFN = (LPSTR)pmszCabList; *pszFN; pszFN += strlen(pszFN) + 1)
	{
		if (i == iNo)
		{
			lstrcpy(pszCabName, A2T(pszFN));
			
			return MakeCRCName(A2T(pszFN), pCRC, pszCRCName, cbCRCName);
		}
		pCRC++;
		i++;
	}

	// if we got here that means we did not find the request element
	return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
}


HRESULT StringFromCRC(const WUCRC_HASH* pCRC, LPTSTR pszBuf, int cbBuf)
{
	LPTSTR p = pszBuf;
	BYTE b;
	
	//check the input argument, to see that it is not NULL
	if (NULL == pCRC)
	{
		return E_INVALIDARG;
	}

    if (cbBuf < ((WUCRC_HASH_SIZE * 2) + 1))
	{
		return TYPE_E_BUFFERTOOSMALL;
	}
	
	for (int i = 0; i < WUCRC_HASH_SIZE; i++)
	{
		b = pCRC->HashBytes[i] >> 4;
		if (b <= 9)
			*p = '0' + (TCHAR)b;
		else
			*p = 'A' + (TCHAR)(b - 10);
		p++;

		b = pCRC->HashBytes[i] & 0x0F;
		if (b <= 9)
			*p = '0' + (TCHAR)b;
		else
			*p = 'A' + (TCHAR)(b - 10);
		p++;
	}
	*p = _T('\0');
	
	return S_OK;
}



static BYTE hex2dec(char ch)
{
    if (ch >= '0' && ch <= '9')
    {
	    return (ch - '0');
    }

    if (ch >= 'A' && ch <= 'F')
    {
	    return (ch - 'A' + 0xA);
    }

    if (ch >= 'a' && ch <= 'f')
    {
	    return (ch - 'a' + 0xA);
    }

	// we do not expect illegal values here
	return 0;
}



HRESULT CRCFromString(LPCSTR pszCRC, WUCRC_HASH* pCRC)
{
	if (strlen(pszCRC) != (2 * WUCRC_HASH_SIZE))
	{
		return E_INVALIDARG;
	}
	
	LPCSTR p = pszCRC;
    
    
	for (int i = 0; i < WUCRC_HASH_SIZE; i++)
	{
        // broken into two lines because the optimizer was doing the wrong thing when on one line
		pCRC->HashBytes[i] = (hex2dec(*p++) << 4);
        pCRC->HashBytes[i] += hex2dec(*p++);
	}
	
	return S_OK;
}




HRESULT MakeCRCName(LPCTSTR pszFromName, const WUCRC_HASH* pCRC, LPTSTR pszToName, int cbToName)
{
	int iLen = lstrlen(pszFromName);
	LPTSTR pDot;
	TCHAR szCRC[WUCRC_HASH_SIZE * 2 + 1];
	HRESULT hr = S_OK;

	// make sure we have enough space for orignal file name + hash + a '_' + null terminator
	if (cbToName < (WUCRC_HASH_SIZE * 2 + iLen + 2))
	{
		return TYPE_E_BUFFERTOOSMALL;
	}

	hr = StringFromCRC(pCRC, szCRC, sizeof(szCRC));
	if (FAILED(hr))
	{
		return hr;
	}

	lstrcpy(pszToName, pszFromName);

	// find the extension in the new copy
	pDot = _tcschr(pszToName, _T('.'));
	if (pDot != NULL)
	{
		*pDot = _T('\0');
	}
	lstrcat(pszToName, _T("_"));
	lstrcat(pszToName, szCRC);

	// copy the extension from the original name
	pDot = _tcschr(pszFromName, _T('.'));
	if (pDot != NULL)
	{
		lstrcat(pszToName, pDot);
	}

	return hr;
}




// splits abc_12345.cab into  abc.cab and 12345 returned as CRC
HRESULT SplitCRCName(LPCSTR pszCRCName, WUCRC_HASH* pCRC, LPSTR pszName)
{
// YANL - unreferenced local variable
//	char szCRC[WUCRC_HASH_SIZE * 2 + 1];
	char szTmp[MAX_PATH];
	int l = strlen(pszCRCName);
	int i;
	LPSTR pszExt = NULL;
	LPSTR pszHash = NULL;

	pszName[0] = '\0';
	if (l < (2 * WUCRC_HASH_SIZE))
	{
		// cannot be a valid name if it does not have atleast 2*WUCRC_HASH_SIZE characters
		return E_INVALIDARG;
	}

	strcpy(szTmp, pszCRCName);

	// start at the end, set pointers to put nulls at last period and last underscore
	// record the starting position of the extension and hash code
	i = l - 1;
	while (i >= 0)
	{
		if ((szTmp[i] == '.') && (pszExt == NULL))
		{
			pszExt = &(szTmp[i + 1]);
			szTmp[i] = '\0';
		}
		else if ((szTmp[i] == '_') && (pszHash == NULL))
		{
			pszHash = &(szTmp[i + 1]);
			szTmp[i] = '\0';
		}
		i--;
	}

	if (pszHash == NULL)
	{
		return E_INVALIDARG;
	}

	// copy original cab name
	strcpy(pszName, szTmp);
	if (pszExt != NULL)
	{
		strcat(pszName, ".");
		strcat(pszName, pszExt);
	}


	return CRCFromString(pszHash, pCRC);
}



int __cdecl CompareWUCRCMAP(const void* p1, const void* p2)
{
	//check if the input arguments are not NULL
	if (NULL == p1 || NULL == p2)
	{
		return 0;
	}

	DWORD d1 = ((WUCRCMAP*)p1)->dwKey;
	DWORD d2 = ((WUCRCMAP*)p2)->dwKey;

	if (d1 > d2)
		return +1;
	else if (d1 < d2)
		return -1;
	else
		return 0;
}


//
// CCRCMapFile class
//


// Constructs an object to search the CRC index file data passed in 
// with pMemData.  
//
// NOTE: The memory pointed by pMemData buffer must stay valid
//       for the lifetime of this object
//
// structure for map file:
//   DWORD count
//   WUCRCMAP[0]
//   WUCRCMAP[1]
//   WUCRCMAP[count - 1]
//
CCRCMapFile::CCRCMapFile(const BYTE* pMemData, DWORD dwMemSize)
{
	//check the input argument for NULLs
	if (NULL == pMemData) 
	{
		m_pEntries = NULL;
		m_cEntries = 0;
		return;
	}

	// get the count
	m_cEntries = *((DWORD*)pMemData);

	// validate the memory buffer size
	if ((sizeof(DWORD) + m_cEntries * sizeof(WUCRCMAP)) != dwMemSize)
	{
		// invalid size is passed, we cannot process it
		m_pEntries = NULL;
		m_cEntries = 0;
	}
	else
	{
		// set the pointer to begining of the map entries
		m_pEntries = (WUCRCMAP*)(pMemData + sizeof(DWORD));
	}
}


HRESULT CCRCMapFile::GetValue(DWORD dwKey, WUCRC_HASH* pCRC)
{
	WUCRCMAP* pEntry;
	WUCRCMAP key;

	if (m_cEntries == 0)
	{
		// memory buffer passed to us was invalid
		return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
	}

	// fill the key field need for compare function in the structure
	key.dwKey = dwKey;

	// binary search to find the item
	pEntry = (WUCRCMAP*)bsearch((void*)&key, (void*)m_pEntries, m_cEntries, sizeof(WUCRCMAP), CompareWUCRCMAP);

	if (pEntry == NULL)
	{
		// not found
		return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
	}

	// found the entry
	memcpy(pCRC, &pEntry->CRC, sizeof(WUCRC_HASH));

	return S_OK;
}


HRESULT CCRCMapFile::GetCRCName(DWORD dwKey, LPCTSTR pszFromName, LPTSTR pszToName, int cbToName)
{
	WUCRC_HASH CRC;

	HRESULT hr = GetValue(dwKey, &CRC);

	if (SUCCEEDED(hr))
	{
		hr = MakeCRCName(pszFromName, &CRC, pszToName, cbToName);
	}

	return hr;
}