//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        serial.cpp
//
// Contents:    serial number string encode/decode implementation
//
//---------------------------------------------------------------------------

#include <pch.cpp>
#pragma hdrstop


HRESULT
ObsoleteMultiByteIntegerToWszBuf(
    IN BOOL fOctetString,
    IN DWORD cbIn,
    IN BYTE const *pbIn,
    IN OUT DWORD *pcbOut,
    OPTIONAL OUT WCHAR *pwszOut)
{
    return MultiByteIntegerToWszBuf(
                fOctetString,
                cbIn,
                pbIn,
                pcbOut,
                pwszOut);
}

HRESULT
ObsoleteMultiByteIntegerToBstr(
    IN BOOL fOctetString,
    IN DWORD cbIn,
    IN BYTE const *pbIn,
    OUT BSTR *pstrOut)
{
    return MultiByteIntegerToBstr(
                fOctetString,
                cbIn,
                pbIn,
                pstrOut);
}

BOOL
AsciiToNibble(
    IN WCHAR wc,
    BYTE *pb)
{
    BOOL fOk = TRUE;

    do
    {
	wc -= L'0';
	if (wc <= 9)
	{
	    break;
	}
	wc += (WCHAR) (L'0' - L'a' + 10);
	if (wc <= 15)
	{
	    break;
	}
	wc += L'a' - L'A';
	if (wc <= 15)
	{
	    break;
	}
	fOk = FALSE;
    } while (FALSE);

    *pb = (BYTE) wc;
    return(fOk);
}


__inline BOOL
IsMultiByteSkipChar(
    IN WCHAR wc)
{
    return(L' ' == wc || L'\t' == wc);
}


// WszToMultiByteIntegerBuf - convert a big endian null-terminated ascii-hex
// encoded WCHAR string of even length to a little-endian integer blob.
// If fOctetString is TRUE, preserve endian order, as in a hex dump

HRESULT
WszToMultiByteIntegerBuf(
    IN BOOL fOctetString,
    IN WCHAR const *pwszIn,
    IN OUT DWORD *pcbOut,
    OPTIONAL OUT BYTE *pbOut)
{
    HRESULT hr;
    WCHAR const *pwsz;
    DWORD cbOut;

    cbOut = 0;
    hr = E_INVALIDARG;

    if (fOctetString)
    {
	for (pwsz = pwszIn; L'\0' != *pwsz; )
	{
	    BYTE blo, bhi;

	    while (IsMultiByteSkipChar(*pwsz))
	    {
		pwsz++;
	    }
	    if (!AsciiToNibble(*pwsz, &bhi))
	    {
		_JumpError2(
			hr,
			error,
			"WszToMultiByteInteger: bad string",
			E_INVALIDARG);
	    }
	    pwsz++;

	    while (IsMultiByteSkipChar(*pwsz))
	    {
		pwsz++;
	    }
	    if (!AsciiToNibble(*pwsz, &blo))
	    {
		_JumpError(hr, error, "WszToMultiByteInteger: bad string");
	    }
	    pwsz++;

	    cbOut++;
	    if (NULL != pbOut)
	    {
		if (cbOut > *pcbOut)
		{
		    hr = TYPE_E_BUFFERTOOSMALL;
		    _JumpError(hr, error, "WszToMultiByteInteger: overflow");
		}
		*pbOut++ = blo | (bhi << 4);
	    }
	}
    }
    else
    {
	for (pwsz = &pwszIn[wcslen(pwszIn) - 1]; pwsz >= pwszIn; )
	{
	    BYTE blo, bhi;

	    while (pwsz >= pwszIn && IsMultiByteSkipChar(*pwsz))
	    {
		pwsz--;
	    }
	    if (pwsz < pwszIn)
	    {
		break;
	    }
	    if (!AsciiToNibble(*pwsz, &blo))
	    {
		_JumpError(hr, error, "WszToMultiByteInteger: bad string");
	    }
	    pwsz--;

	    while (pwsz >= pwszIn && IsMultiByteSkipChar(*pwsz))
	    {
		pwsz--;
	    }
	    if (pwsz < pwszIn || !AsciiToNibble(*pwsz, &bhi))
	    {
		_JumpError(hr, error, "WszToMultiByteInteger: bad string");
	    }
	    pwsz--;

	    cbOut++;
	    if (NULL != pbOut)
	    {
		if (cbOut > *pcbOut)
		{
		    hr = TYPE_E_BUFFERTOOSMALL;
		    _JumpError(hr, error, "WszToMultiByteInteger: overflow");
		}
		*pbOut++ = blo | (bhi << 4);
	    }
	}
    }
    *pcbOut = cbOut;
    hr = S_OK;

error:
    return(hr);
}


// WszToMultiByteInteger - convert a big endian null-terminated ascii-hex
// encoded WCHAR string of even length to a little-endian integer blob.
// If fOctetString is TRUE, preserve endian order, as in a hex dump

HRESULT
WszToMultiByteInteger(
    IN BOOL fOctetString,
    IN WCHAR const *pwszIn,
    OUT DWORD *pcbOut,
    OUT BYTE **ppbOut)
{
    HRESULT hr = S_OK;

    *pcbOut = 0;
    *ppbOut = NULL;

    while (TRUE)
    {
	hr = WszToMultiByteIntegerBuf(fOctetString, pwszIn, pcbOut, *ppbOut);
	if (S_OK != hr)
	{
	    if (NULL != *ppbOut)
	    {
		LocalFree(*ppbOut);
		*ppbOut = NULL;
	    }
	    _JumpError2(hr, error, "WszToMultiByteIntegerBuf", E_INVALIDARG);
	}
	if (NULL != *ppbOut)
	{
	    break;
	}
	*ppbOut = (BYTE *) LocalAlloc(LMEM_FIXED, *pcbOut);
	if (NULL == *ppbOut)
	{
	    hr = E_OUTOFMEMORY;
	    _JumpError(hr, error, "LocalAlloc");
	}
    }

error:
    return(hr);
}


HRESULT
caTranslateFileTimePeriodToPeriodUnits(
    IN FILETIME const *pftGMT,
    IN BOOL fExact,
    OUT DWORD *pcPeriodUnits,
    OUT PERIODUNITS **prgPeriodUnits)
{
    HRESULT hr;
    LLFILETIME llft;
    LONGLONG llRemain;
    DWORD i;
    DWORD cPeriodUnits;
    PERIODUNITS *rgPeriodUnits;
#define IC_YEARS	0
#define IC_MONTHS	1
#define IC_WEEKS	2
#define IC_DAYS		3
#define IC_HOURS	4
#define IC_MINUTES	5
#define IC_SECONDS	6
#define IC_MAX		7
    LONG alCount[IC_MAX];
    static const enum ENUM_PERIOD s_aenumPeriod[] =
    {
	ENUM_PERIOD_YEARS,
	ENUM_PERIOD_MONTHS,
	ENUM_PERIOD_WEEKS,
	ENUM_PERIOD_DAYS,
	ENUM_PERIOD_HOURS,
	ENUM_PERIOD_MINUTES,
	ENUM_PERIOD_SECONDS,
    };
    
    llft.ft = *pftGMT;
    if (0 <= llft.ll)
    {
	hr = E_INVALIDARG;
	_JumpError(hr, error, "Not a time period");
    }
    llft.ll = -llft.ll;
    llft.ll /= CVT_BASE; // now in seconds

    ZeroMemory(alCount, sizeof(alCount));
    alCount[IC_DAYS] = (LONG) (llft.ll / (60 * 60 * 24));

    llRemain = llft.ll - (LONGLONG) alCount[IC_DAYS] * (60 * 60 * 24);
    if (fExact || 4 > alCount[IC_DAYS])	// if less than 96 hrs
    {
	alCount[IC_HOURS] = (LONG) llRemain / (60 * 60);
	if (fExact || 2 > alCount[IC_HOURS])	// if less than 120 mins
	{
	    alCount[IC_MINUTES] = ((LONG) llRemain / 60) % 60;
	    if (fExact || 2 > alCount[IC_MINUTES]) // if less than 120 secs
	    {
		alCount[IC_SECONDS] = (LONG) llRemain % 60;
	    }
	}
    }

    if (0 != alCount[IC_DAYS])
    {
	if (0 == (alCount[IC_DAYS] % 365))
	{
	    alCount[IC_YEARS] = alCount[IC_DAYS] / 365;
	    alCount[IC_DAYS] = 0;
	}
	else if (0 == (alCount[IC_DAYS] % 30))
	{
	    alCount[IC_MONTHS] = alCount[IC_DAYS] / 30;
	    alCount[IC_DAYS] = 0;
	}
	else if (0 == (alCount[IC_DAYS] % 7))
	{
	    alCount[IC_WEEKS] = alCount[IC_DAYS] / 7;
	    alCount[IC_DAYS] = 0;
	}
    }
    cPeriodUnits = 0;
    for (i = 0; i < IC_MAX; i++)
    {
	if (0 != alCount[i])
	{
	    cPeriodUnits++;
	}
    }
    if (0 == cPeriodUnits)
    {
	cPeriodUnits++;
    }
    rgPeriodUnits = (PERIODUNITS *) LocalAlloc(
				LMEM_FIXED,
				cPeriodUnits * sizeof(rgPeriodUnits[0]));
    if (NULL == rgPeriodUnits)
    {
	hr = E_OUTOFMEMORY;
	_JumpError(hr, error, "LocalAlloc");
    }
    *pcPeriodUnits = cPeriodUnits;
    *prgPeriodUnits = rgPeriodUnits;

    cPeriodUnits = 0;
    for (i = 0; i < IC_MAX; i++)
    {
	if (0 != alCount[i] || (0 == cPeriodUnits && i + 1 == IC_MAX))
	{
	    rgPeriodUnits[cPeriodUnits].lCount = alCount[i];
	    rgPeriodUnits[cPeriodUnits].enumPeriod = s_aenumPeriod[i];
	    cPeriodUnits++;
	}
    }
    CSASSERT(cPeriodUnits == *pcPeriodUnits);
    hr = S_OK;

error:
    return(hr);
}