//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
 * commonUtils.cpp
 */

#include "commonUtils.h"

/*
 * Command line for tux dll.  Set in the tuxStandard file in the main
 * SHELLPROC processing function.
 */
LPCTSTR g_szDllCmdLine = NULL;

/***************************************************************************
 *
 *             Local Functions Not Exported Through the Header
 *
 ***************************************************************************\

/*
 * Exactly like the other circularShiftR, except this only shifts right one
 * bit.
 */
DWORD
circularShiftR (DWORD dwVal, DWORD dwLength);

/*
 * Exactly like the other circularShiftL, except this only shifts left one
 * bit.
 */
DWORD
circularShiftL (DWORD dwVal, DWORD dwLength);


/***************************************************************************
 *
 *                                Implementation
 *
 ***************************************************************************/

/*
 * For informational messages.  Treat this like printf.
 */
VOID Info(LPCTSTR szFormat, ...) {
   TCHAR szBuffer[1024] = TEXT("INFO: ");

   va_list pArgs; 
   va_start(pArgs, szFormat);

   /* 
    * six is the length of "INFO: " 
    * minus two accounts for the cat in the next line
    */   
   _vsntprintf_s(szBuffer + 6, countof(szBuffer) - 6 - 2, _TRUNCATE, 
		 szFormat, pArgs);

   va_end(pArgs);

   /*
    * if overrun buffer then not guaranteed to be null terminated, so
    * force it to be.  The - 1 grabs the location, since we index from
    * zero.  This leaves space for appending the end line in the next
    * statement.
    */
   szBuffer[countof(szBuffer) - 2 - 1] = '\0';

   const TCHAR szEndL[] = _T("\r\n");
   _tcsncat_s(szBuffer, countof(szBuffer), szEndL, _tcslen (szEndL));

   /* can't send directly in, because it will be reprocessed */
   g_pKato->Log(LOG_DETAIL, _T("%s"), szBuffer);
}

/*
 * For informational messages.  Treat this like printf.
 */
VOID InfoA(char * szFormat, ...) 
{
  char szBuffer[1024] = "INFO: ";

  va_list pArgs; 
  va_start(pArgs, szFormat);

  /* 
   * six is the length of "INFO: " 
   * minus two accounts for the cat in the next line
   */
  _vsnprintf_s(szBuffer + 6, countof(szBuffer) - 6 - 2, _TRUNCATE,
	     szFormat, pArgs);

  va_end(pArgs);

  /*
   * if overrun buffer then not guaranteed to be null terminated, so
   * force it to be.  The - 1 grabs the location, since we index from
   * zero.  This leaves space for appending the end line in the next
   * statement.
   */
  szBuffer[countof(szBuffer) - 2 - 1] = '\0';

  const char szEndL[] = "\r\n";
  strncat_s(szBuffer, countof(szBuffer), szEndL, strlen (szEndL));

  /* can't send directly in, because it will be reprocessed */
#ifdef _UNICODE
  /* 
   * if unicode is turned on then have the routine convert
   * for us
   */		
  g_pKato->Log(LOG_DETAIL, _T("%S"), szBuffer);
#else
  g_pKato->Log(LOG_DETAIL, "%s", szBuffer);
#endif
}

/*
 * For error messages (as opposed to informational messages).  Treat
 * this like printf.
 */
VOID Error(LPCTSTR szFormat, ...) {
   TCHAR szBuffer[1024] = TEXT("ERROR: ");

   va_list pArgs; 
   va_start(pArgs, szFormat);
   /*
    * seven is the length of "ERROR: " 
    * minus two accounts for the cat in the next line 
    */   
   _vsntprintf_s(szBuffer + 7, countof(szBuffer) - 7 - 2, _TRUNCATE,
	       szFormat, pArgs);

   va_end(pArgs);
   
   /*
    * if overrun buffer then not guaranteed to be null terminated, so
    * force it to be.  The - 1 grabs the location, since we index from
    * zero.  This leaves space for appending the end line in the next
    * statement.
    */
   szBuffer[countof(szBuffer) - 2 - 1] = '\0';

   const TCHAR szEndL[] = _T("\r\n");
   _tcsncat_s(szBuffer, countof(szBuffer), szEndL, _tcslen (szEndL));

   g_pKato->Log(LOG_DETAIL, _T("%s"), szBuffer);

}


/*
 * For internal errors.  If you ever see one of these then there is a
 * bug in the code somewhere.
 *
 * These are used like asserts.
 */
VOID IntErr(LPCTSTR szFormat, ...) {
   TCHAR szBuffer[1024] = TEXT("** INTERNAL ERROR **: ");

   va_list pArgs; 
   va_start(pArgs, szFormat);

   /*
    * seven is the length of "ERROR: " 
    * minus two accounts for the cat in the next line 
    */
   _vsntprintf_s(szBuffer + 21, countof(szBuffer) - 21 - 2, _TRUNCATE,
		 szFormat, pArgs);

   va_end(pArgs);
   
   /*
    * if overrun buffer then not guaranteed to be null terminated, so
    * force it to be.  The - 1 grabs the location, since we index from
    * zero.  This leaves space for appending the end line in the next
    * statement.
    */
   szBuffer[countof(szBuffer) - 2 - 1] = '\0';

   const TCHAR szEndL[] = _T("\r\n");

   _tcsncat_s(szBuffer, countof(szBuffer), szEndL, _tcslen (szEndL));

   g_pKato->Log(LOG_DETAIL, _T("%s"), szBuffer);

   DebugBreak();

}

/*
 * This can't use kato, since certian functions in the tux
 * initialization call this code before kato has been set up.  We use
 * standard debugging instead.
 *
 * Treat this just like printf.
 */
VOID Debug(LPCTSTR szFormat, ...) {
   TCHAR szBuffer[1024] = TEXT("DEBUG: ");

   va_list pArgs; 
   va_start(pArgs, szFormat);

   /*
    * seven is the length of "DEBUG: "
    * minus two accounts for the cat in the next line 
    */
   _vsntprintf_s(szBuffer + 7, countof(szBuffer) - 7 - 2, _TRUNCATE,
		 szFormat, pArgs);

   va_end(pArgs);
   
   /*
    * if overrun buffer then not guaranteed to be null terminated, so
    * force it to be.  The - 1 grabs the location, since we index from
    * zero.  This leaves space for appending the end line in the next
    * statement.
    */
   szBuffer[countof(szBuffer) - 2 - 1] = '\0';

   const TCHAR szEndL[] = _T("\r\n");
   _tcsncat_s(szBuffer, countof(szBuffer), szEndL, _tcslen (szEndL));

   OutputDebugString(szBuffer);

}


/*
 * Gets a random number in a given range, inclusive.
 *
 * lowEnd is the lowEnd of the range.  highEnd is the high end of the
 * range
 */
DWORD
getRandomInRange (DWORD lowEnd, DWORD highEnd)
{
  if (lowEnd >= highEnd)
    {
      IntErr (_T("getRandomInRange: lowEnd >= highEnd"));
      return BAD_VAL;
    }

  /* 
   * rand returns a value between 0 and RAND_MAX (0x7fff), inclusive.
   * We need a 32 bit random number, so calculate one.
   */
  DWORD bigRandom = 
    rand () |
    (rand () << 15) |
    (rand () << 30);

  /* scale the bigRandom number to the range that we need */
  DWORD result = (DWORD) (((double) bigRandom / (double) MAX_DWORD) * 
			  (double) (highEnd - lowEnd));

  /* move this into the range that we are looking for */
  result += lowEnd;

  return (result);
}


/*
 * Gets a random double between 0 and 1.
 *
 */
DOUBLE
getRandDoubleZeroToOne (VOID)
{
  /* 
   * rand returns a value between 0 and RAND_MAX (0x7fff), inclusive.
   * We need a 64 bit random number, so calculate one.
   */
  ULONGLONG bigRandom = 
    ((ULONGLONG)rand()) |
    ((ULONGLONG)rand() << 15) |
    ((ULONGLONG)rand() << 30) |
    ((ULONGLONG)rand() << 45) |
    ((ULONGLONG)rand() << 60);

  /* scale the bigRandom number to be between 0 and 1 */
  DOUBLE result = ((double)bigRandom / (double)ULONGLONG_MAX);

  return (result);
}


BOOL
AminusB (ULONGLONG & answer, ULONGLONG a, ULONGLONG b, ULONGLONG dwMaxSize)
{
  if ((a > dwMaxSize) || (b > dwMaxSize))
    {
      return (FALSE);
    }

  if (a < b) 
    {
      /* 
       * handle overflow
       * adding one accounts for the transition between the maximum dword
       * and zero.
       */
      answer = ((dwMaxSize - b) + a + 1);
    }
  else
    {
      answer = a - b;
    }
  
  return (TRUE);
}

DWORD 
AminusB (DWORD a, DWORD b)
{
  if (a < b)
    {
      return (MAX_DWORD - a + b + 1);
    }
  else
    {
      return (a - b);
    }
}

DWORD
circularShiftR (DWORD dwVal, DWORD dwLength, DWORD dwShift)
{
  if (!(dwShift <= dwLength))
    {
      IntErr (_T("circularShiftR: !(dwShift <= dwLength)"));
      return (BAD_VAL);
    }
  
  if (!((dwLength > 0) && (dwLength <= 32)))
    {
      IntErr (_T("circularShiftR: !((dwLength > 0) && (dwLength <= 32))"));
      return (BAD_VAL);
    }

  for (DWORD dwCount = 0; dwCount < dwShift; dwCount++)
    {
      dwVal = circularShiftR (dwVal, dwLength);
    }

  return (dwVal);
}

DWORD
circularShiftR (DWORD dwVal, DWORD dwLength)
{
  if (!((dwLength > 0) && (dwLength <= 32)))
    {
      IntErr (_T("circularShiftR: !((dwLength > 0) && (dwLength <= 32))"));
      return BAD_VAL;
    }

  DWORD dwEnd = dwVal & 0x1;

  dwVal >>= 1;

  if (dwEnd == 0)
    {
      CLR_BIT (dwVal, dwLength - 1);
    }
  else
    {
      SET_BIT (dwVal, dwLength - 1);
    }
  
  return (dwVal);
}
  
DWORD
circularShiftL (DWORD dwVal, DWORD dwLength, DWORD dwShift)
{
  if (!(dwShift <= dwLength))
    {
      IntErr (_T("circularShiftL: !(dwShift <= dwLength)"));
      return BAD_VAL;
    }

  if (!((dwLength > 0) && (dwLength <= 32)))
    {
      IntErr (_T("circularShiftL: !((dwLength > 0) && (dwLength <= 32))"));
      return BAD_VAL;
    }

  for (DWORD dwCount = 0; dwCount < dwShift; dwCount++)
    {
      dwVal = circularShiftL (dwVal, dwLength);
    }

  return (dwVal);
}

DWORD
circularShiftL (DWORD dwVal, DWORD dwLength)
{
  if (!((dwLength > 0) && (dwLength <= 32)))
    {
      IntErr (_T("circularShiftL: !((dwLength > 0) && (dwLength <= 32))"));
      return BAD_VAL;
    }

  DWORD dwEnd = dwVal & 0x1;

  dwVal >>= 1;

  if (dwEnd == 0)
    {
      CLR_BIT (dwVal, dwLength - 1);
    }
  else
    {
      SET_BIT (dwVal, dwLength - 1);
    }
  
  return (dwVal);
}
  


BOOL
handleThreadPriority (int iNew, int * iOld)
{ 
  if (!((iNew == DONT_CHANGE_PRIORITY) || ((iNew >= 0) && (iNew <= 255))))
    {
      IntErr (_T("handleThreadPriority: ")
	      _T("!((iNew == DONT_CHANGE_PRIORITY) || ")
	      _T("((iNew >= 0) && (iNew <= 255)))"));
      return FALSE;
    }

  INT returnVal = TRUE;
  
  /*
   * handle to the current thread.  This doesn't need to be 
   * closed when processing is complete.
   */
  HANDLE hThisThread = GetCurrentThread ();

  /* set the thread priority */
  if (iNew != DONT_CHANGE_PRIORITY)
    {
#ifdef UNDER_CE
      /*
       * save the old thread priority so that we can reset the 
       * priority to this after the test.
       */
      if (iOld != NULL)
	{
	  *iOld = CeGetThreadPriority (hThisThread);
	}

      if (CeSetThreadPriority (hThisThread, iNew) == FALSE)
	{
	  Error (_T("Could not set thread priority to %i."),
		 iNew);

	  returnVal = FALSE;
	  goto cleanAndExit;
	}
#else
      /* not implemented */
      returnVal = FALSE;
      goto cleanAndExit;
#endif
    }
 cleanAndExit:
  return (returnVal);
}




/****** Time Formatting **************************************************/


/*
 * struct to store the values that we will return to the user.
 *
 * doConversion is the conversion value to go from the given line
 * to the line one less precise.  The values are organized from
 * most precise at 0 to least precise.
 */
struct
{
  double doConversion;
  _TCHAR * unitsSing;
  _TCHAR * unitsPlural;
} conversionValues[] =
  {1000.0, _T("nano-second"), _T("nano-seconds"),
   1000.0, _T("micro-second"), _T("micro-seconds"),
   1000.0, _T("millisecond"), _T("milliseconds"),   
   60.0, _T("second"), _T("seconds"),    
   60.0, _T("minute"), _T("minutes"),
   24.0, _T("hour"), _T("hours"),
   0.0, _T("day"), _T("days")};

/*
 * goes hand in hand with above.  If you add stuff before seconds you
 * need to change this.
 */
#define SECONDS_POS 3

/*
 * convert a time into a time that is more realistic to print.  This
 * allows us to easily pretty print times.  120000 seconds isn't much
 * help to the normal user without a calculator.  This makes the
 * output nicer.
 *
 * doTime is the value to convert, in seconds.  doFormatted time is
 * the resulting number, and szUnits in the units that should be
 * printed.  The units account for singular and plural.
 *
 */ 
void
realisticTime (double doTime, double & doFormattedTime, _TCHAR * szUnits[])
{
  DWORD dwCurrentSpot = SECONDS_POS;

  doFormattedTime = doTime;

  if (doTime == 0.0)
    {
      *szUnits = conversionValues[SECONDS_POS].unitsPlural;
      return;
    }

  if (doFormattedTime < 1.0)
    {
      /* we are going to have to move to more precise units */
      
      while ((doFormattedTime < 1.0) && (dwCurrentSpot > 0))
	{
	  doFormattedTime = 
	    doFormattedTime * conversionValues[dwCurrentSpot - 1].doConversion;
	  dwCurrentSpot--;
	}

      /* done */
    }
  else
    {
      /* going to have to go up */
      
      while ((doFormattedTime > (1.1 * conversionValues[dwCurrentSpot].doConversion)) && 
	     (dwCurrentSpot < (countof(conversionValues) - 1)))
	{
	  doFormattedTime /= conversionValues[dwCurrentSpot].doConversion;
	  dwCurrentSpot++;
	}

      /* done */
    }
      
  if (doFormattedTime == 1.0)
    {
      *szUnits = conversionValues[dwCurrentSpot].unitsSing;
    }
  else
    {
      *szUnits = conversionValues[dwCurrentSpot].unitsPlural;
    }
}

/*
 * return true if the given DWORD is a power of two and false if it is not.
 */
BOOL
isPowerOfTwo (DWORD val)
{
  if (val == 0)
    {
      return (FALSE);
    }

  /* 
   * the previous statement forces there to be at least one bit set in
   * val.  Bail out on that bit.
   */
  while ((val & 1) != 1)
    {
      val >>= 1;
    }

  /* 
   * at this point the one that we are looking for is sitting in the
   * lowest position.  Shift it off.
   */
  val >>= 1;

  if (val == 0)
    {
      /* only one set bit, so power of two */
      return (TRUE);
    }
  else
    {
      return (FALSE);
    }
}

/*
 * get the number of bits needed to represent a number which is a power of two
 *
 * This doesn't include the number val.  In other words, count the
 * number of zeros to the right of the one in a given number.
 */
DWORD
getNumBitsToRepresentNum (DWORD val)
{
  if (val == 0)
    {
      IntErr (_T("getNumBitsToRepresentNum: val == 0"));
      return BAD_VAL;
    }

  if (isPowerOfTwo (val) != TRUE)
    {
      IntErr (_T("getNumBitsToRepresentNum: isPowerOfTwo (val) != TRUE"));
      return BAD_VAL;
    }

  DWORD dwCount = 0;

  /* 
   * constraints force there to be at least one bit set in val
   */
  while ((val & 1) != 1)
    {
      val >>= 1;
      dwCount++;
    }

  /* don't count the last bit, because we are not inclusive. */
  return (dwCount);

}



/****** String Stuff ***************************************************/

/*
 * convert string to ULONGLONG
 * 
 * see header for more info.
 */
BOOL
strToULONGLONG(const TCHAR *const szStr, ULONGLONG * ullVal)
{
  if ((szStr == NULL) || (ullVal == NULL))
    {
      return (FALSE);
    }

  if (_stscanf (szStr, _T("%I64u"), ullVal) != 1)
    {
      return (FALSE);
    }

  return (TRUE);
}

/*
 * szTime is the time to be converted.  *pullTimeS is the converted value.
 */
BOOL
strToSeconds (const TCHAR *const szTime, ULONGLONG * pullTimeS)
{  
  BOOL returnVal = FALSE;

  if (!szTime || !pullTimeS)
    {
      return FALSE;
    }

  /* szTimeValue now points to the string representing the argument */
  
  TCHAR * endPtr;

  /* use strtoul to convert the numerical part, if possible */
  /* 10 is the base */
  unsigned long lNumericalPart = _tcstoul (szTime, &endPtr, 10);

  if (!endPtr)
    {
      /* something went wrong */
      goto cleanAndReturn;
    }
  
  if (endPtr == szTime)
    {
      /* no numerical part given */
      Error (_T("no numerical part found in %s"), szTime);
      goto cleanAndReturn;
    }

  DWORD dwConversionFactor = 0;

  switch (*endPtr)
    {
    case _T('\0'):		/* no units denotes seconds */
    case _T('s'):
      dwConversionFactor = 1;
      break;
    case _T('m'):
      dwConversionFactor = 60;
      break;
    case _T('h'):
      dwConversionFactor = 60 * 60;
      break;
    case _T('d'):
      dwConversionFactor = 60 * 60 * 24;
      break;
    default:
      Error (_T("Can't parse the unit identifier \"%s\""), endPtr);
      goto cleanAndReturn;
    }

  *pullTimeS = lNumericalPart * dwConversionFactor;

  returnVal = TRUE;

 cleanAndReturn:

  return (returnVal);

}

/*
 * convert a string to a double.
 *
 * This uses the TCHAR version of strtod, which is pretty good
 * concerning error checking.
 */
BOOL
strToDouble (const TCHAR *const szStr, double * doN)
{
  if ((szStr == NULL) || (doN == NULL))
    {
      return FALSE;
    }
  
  TCHAR * szEndPtr = NULL;

  *doN = _tcstod (szStr, &szEndPtr);

  if (*szEndPtr == _T('\0'))
    {
      /* we read all of the way to the null term, which denotes success */
      return TRUE;
    }
  else
    {
      return FALSE;
    }
}


/****** Debugging *****************************************************/

void breakIfUserDesires ()
{
  CClParse cmdLine(g_szDllCmdLine);

  if (cmdLine.GetOpt(_T("b")))
    {
      DebugBreak();
    }
}



BOOL
getDeviceType (__out_ecount(dwLen) TCHAR * szDeviceName,
	       DWORD dwLen)
{
  BOOL returnVal = FALSE;
  if (!szDeviceName || dwLen == 0)
    {
      return (FALSE);
    }

  char * szDevId = (char*) malloc (dwLen);

  if (!szDevId)
    {
      Error (_T("Couldn't allocate mem."));
      goto cleanAndReturn;
    }
  
  if (!getDeviceId(szDevId, dwLen))
    {
      Error (_T("Couldn't get device id."));
      goto cleanAndReturn;
    }

  size_t iStringLength;

  /* get needed length */
  mbstowcs_s(&iStringLength, NULL, 0, szDevId, dwLen);  

  if (iStringLength > (size_t) dwLen)
    {
      Error (_T("Not enough room in device name."));
      goto cleanAndReturn;
    }

  /* convert string */
  mbstowcs_s(&iStringLength, szDeviceName, dwLen, szDevId, dwLen);

  if (iStringLength < 0)
    {
      Error (_T("Error converting to wide char."));
      goto cleanAndReturn;
    }

  /* force null term after string conversion */
  szDeviceName [dwLen - 1] = 0;

  /* for now we are going to snip the string at character 4 */
  if (dwLen > 3)
    {
      szDeviceName [3] = 0;
    }

  returnVal = TRUE;

 cleanAndReturn:

  if (szDevId)
    {
      free (szDevId);
    }

  return (returnVal);
}


BOOL
getDeviceId (__out_ecount(dwLen) char * szDevId, DWORD dwLen)
{
  /* assume error until proven otherwise */
  BOOL returnVal = FALSE;

  /* null means no memory alloced, non-null means need to free */
  DEVICE_ID * devId = NULL;             /* the device id */

  DWORD devIdSize = 0;

  /*
   * we need a junk DEVICE_ID to set the dwSize to null to query for the
   * size
   */
  DEVICE_ID queryId;

  /* set the size part to zero for the query function */
  queryId.dwSize = 0;

  BOOL bRet;

  /* figure out how large the devId needs to be for a standard, correct call */
  bRet = KernelIoControl (IOCTL_HAL_GET_DEVICEID, NULL, 0,
                          &queryId, sizeof (queryId), 0);

  DWORD getLastErrorVal = GetLastError ();

  /* we want to get an ERROR_INSUFFICIENT_BUFFER and FALSE */
  if ((bRet == FALSE) &&
      (getLastErrorVal == ERROR_INSUFFICIENT_BUFFER))
    {
      /* what we want */
    }
  else
    {
      /* something went wrong */
      Error (_T("We called"));
      Error (_T("KernelIoControl (IOCTL_HAL_GET_BUFFER, NULL, 0, NULL, 0, ")
             _T("&dwCorrectDevIdSizeBytes)"));
      Error (_T("and didn't get FALSE and ERROR_INSUFFICIENT_DEVID list"));
      Error (_T("expected.  Instead we got a return value of %u and a ")
             _T("GetLastError"), bRet);
      Error (_T("of %u."), getLastErrorVal);
      Error (_T("Maybe the ioctl isn't implemented?"));

      goto cleanAndReturn;
    }

  DWORD dwDevIdStructSize = queryId.dwSize;

  /* allocate memory for the devId */
  devId = (DEVICE_ID *) malloc (dwDevIdStructSize);

  if (devId == NULL)
    {
      Error (_T("Couldn't allocate %u bytes for the devId."),
	     dwDevIdStructSize);

      goto cleanAndReturn;
    }
  
  /* set this to non-zero so that we don't do another query */
  devId->dwSize = dwDevIdStructSize;
  
  DWORD checkSize;

  /* try to get the DEVICE_ID */
  bRet = KernelIoControl (IOCTL_HAL_GET_DEVICEID, NULL, 0,
                          devId, dwDevIdStructSize, &checkSize);

  if (bRet != TRUE)
    {
      /* something went wrong... */
      Error (_T("Couldn't get the device id.  It might not be supported."));
      Error (_T("GetLastError returned %u."), GetLastError ());
      goto cleanAndReturn;
    }

  if (checkSize != dwDevIdStructSize)
    {
      Error (_T("We calculated that the DEVICE_ID structure should be %u bytes"),
             dwDevIdStructSize);
      Error (_T("and the ioctl is now reporting %u bytes."), checkSize);
      goto cleanAndReturn;
    }

  if (devId->dwSize != dwDevIdStructSize)
    {
      Error (_T("The dwSize value of the DEVICE_ID structure (%u byes) does not"),
             devId->dwSize);
      Error (_T("match the value returned by the ioctl (%u bytes)."),
             dwDevIdStructSize);
      goto cleanAndReturn;
    }

  /* confirm that we won't walk off the end, causing a memory access violation */
  DWORD dwMemAccessSize;

  if (DWordAdd (devId->dwPlatformIDOffset, devId->dwPlatformIDBytes, 
		&dwMemAccessSize) != S_OK)
    {
      Error (_T("Overflow calculating the last byte to be accessed."));
      goto cleanAndReturn;
    }

  if (dwMemAccessSize > dwDevIdStructSize)
    { 
      Error (_T("Platform ID extends beyond the memory allocated for it. ")
	     _T("Accessing it risks a memory access violation."));
      goto cleanAndReturn;
    }

  DWORD dwDevIdFromIoctlLength;

  const char *const szDevIdFromIoctl = 
    ((char *) devId) + devId->dwPlatformIDOffset;
  dwDevIdFromIoctlLength = devId->dwPlatformIDBytes;

  /* 
   * code below will underflow if length is 0.  This shouldn't happen unless
   * something else is wrong.
   */
  if (dwDevIdFromIoctlLength == 0)
    {
      Error (_T("Device id length is zero (including the null terminator)."));
      goto cleanAndReturn;
    }

  /* 
   * we need to confirm that we have room for the string.  Since the
   * deviceId isn't necessarily null terminated we have to account for 
   * both cases.
   *
   * The code below says that if it is null terminated use the entire
   * length and if it isn't allow one space at the end of the null
   * terminator.
   */
  /*
   * prefast is looking for a check on the high side, which we do above.
   * The devId->dwPlatformIdBytes line confuses it
   */
  PREFAST_SUPPRESS (12008, "PREFAST noise");
  if (
      ((szDevIdFromIoctl[dwDevIdFromIoctlLength - 1] == '\0') && 
       (dwLen < dwDevIdFromIoctlLength)) ||
      ((szDevIdFromIoctl[dwDevIdFromIoctlLength - 1] != '\0') && 
       (dwLen < (dwDevIdFromIoctlLength - 1))))
    {
      Error (_T("The device id from the ioctl is too large. %u > %u"),
	     dwDevIdFromIoctlLength, dwLen);
      goto cleanAndReturn;
    }

  /* 
   * only copy the number of bytes from the ioctl call.  We will stop
   * at a null terminator if we find it, but it isn't necessarily
   * there.  If the null terminator is the last thing in the string this 
   * won't copy it, but we will catch all cases next.
   */
  strncpy_s (szDevId, dwLen, szDevIdFromIoctl, dwDevIdFromIoctlLength - 1);

  /* terminate the string */
  szDevId[dwDevIdFromIoctlLength - 1] = '\0';

  returnVal = TRUE;

 cleanAndReturn:

  if (devId) free (devId);

  return (returnVal);

}


/*******************************************************************************
 *                   Get Platform Name
 ******************************************************************************/

// This function returns the Platform Name for a given dev board.
//
// Arguments: 
// 		Input: Buffer for Platform Name, Length of buffer
//  	       Output: Platform Name in the input buffer
//
// Return value:
//    	TRUE if successful, FALSE otherwise

BOOL GetPlatformName(__out_ecount(dwLen) TCHAR * szPlatName, DWORD dwLen)
{
  // Assume FALSE, until proven everything works.
  BOOL returnVal = FALSE;

   if(!szPlatName || dwLen == 0)
   {
	goto cleanAndExit;
   }
	
  TCHAR *pOutBuf = NULL;
  DWORD dwOutBufSize = 0;
  DWORD dwBytesRet = 0; 
  DWORD dwInputSpi = SPI_GETPLATFORMNAME;
	
  Info (_T("")); //Blank line
  Info (_T("Get the Platform Name of the device by calling the Device Info Ioctl."));

  // Use the Device Info Ioctl to get the PlatformName of the device
  
  // Call the IOCTL with null output buffer and get the required output size
  KernelIoControl (IOCTL_HAL_GET_DEVICE_INFO, &dwInputSpi, 
   	        sizeof(dwInputSpi), NULL, 0, &dwBytesRet);
  	
  if(!dwBytesRet)
  {
  	Error (_T("Could not get the output size."));
	goto cleanAndExit;
  }

    dwOutBufSize = dwBytesRet;

  // Now call the Ioctl with correct output buffer and get the Platform Name
  pOutBuf = (TCHAR*)malloc(dwOutBufSize * sizeof(BYTE));      	  
  if (!pOutBuf)
  {
      Error (_T(""));
      Error (_T("Error allocating memory for the output buffer."));
      goto cleanAndExit;
  }  
   
  if (!KernelIoControl (IOCTL_HAL_GET_DEVICE_INFO, &dwInputSpi, 
   	        sizeof(dwInputSpi), (VOID*)pOutBuf, dwOutBufSize, &dwBytesRet))
  {
      Error (_T("")); //Blank line
      Error (_T("Called the Device Info Ioctl function with all correct parameters."));
      Error (_T(" The function returned FALSE, while the expected value is TRUE."));     
      goto cleanAndExit;
  }

  // Copy the output from the Ioctl into the given buffer
  // But first check if the length of the buffer is big enough

  if(dwLen < dwOutBufSize)
  {
    Error (_T("The given buffer is insufficient for the Platform Name of the device."));
  }
  
    _tcsncpy_s (szPlatName, dwLen, pOutBuf, dwOutBufSize);

  // Make sure we terminate the string with a null char at the end
  szPlatName[(dwOutBufSize/sizeof(TCHAR)) - 1] = L'\0';

     Info (_T("The Platform Name is %s"),szPlatName);

    returnVal = TRUE;

cleanAndExit:
	if(pOutBuf)
       {
  	    free(pOutBuf);
       }
	return returnVal;
}


