/*++

Copyright (c) 1998-1999, Microsoft Corporation

Module Name:


    Hardware.cpp

Abstract:

--*/

#include "Hardware.h"

#include <stdio.h> // only needed for testing


#if defined(WIN32) || defined(_WIN32)

static inline BOOL IsPlatformNT()
{

    // always do it 'The NT Way'

    return TRUE;

/*/////////////////////////////////////////////////////////////////////////////

    OSVERSIONINFO osvInfo;
    BOOL          fNTPlatformFlag;

    osvInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );

    GetVersionEx( &osvInfo );

    switch( osvInfo.dwPlatformId )
    {
        case VER_PLATFORM_WIN32_NT:
            fNTPlatformFlag = TRUE;
            break;

        default:
            fNTPlatformFlag = FALSE;
            break;
    }

    return( fNTPlatformFlag );
*//////////////////////////////////////////////////////////////////////////////
}

#else

inline BOOL IsPlatformNT()
{
    return FALSE;
}

extern "C" extern WORD _C000H;
extern "C" extern WORD _F000H;

#endif

CHardware::CHardware()
#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
  : m_dwBiosCrc32(0),
    m_dwVolSer(0),
    m_dwTotalRamMegs(0),
    m_dwVideoBiosCrc32(0)
#endif
{
    uClassID = CHARDWARE_CLASSID;

    ZeroMemory( (LPVOID)m_szHardwareID, HARDWARE_ID_SIZE );

    SetBIOSDigit();
    SetHDSerialDigit();
    SetTotalRAMDigit();
    SetFDConfigDigit();
    SetVideoBIOSDigit();

#ifndef NO_HWID_GUID //////////////////////////////////////////////////////////
    CalculateHardwareGUID();
#endif ////////////////////////////////////////////////////////////////////////
}


CHardware::~CHardware()
{
}


DWORD CHardware::GetType()
{
    return(IsPlatformNT() ? 1 : 0);
}


LPSTR CHardware::GetID()
{
    return( m_szHardwareID );
}

#ifndef NO_HWID_GUID //////////////////////////////////////////////////////////
LPSTR CHardware::GetGUID()
{
    return( m_szHardwareGUID );
}
#endif ////////////////////////////////////////////////////////////////////////

VOID CHardware::SetBIOSDigit()
{
    DWORD dwBIOSChecksum;

#if defined(WIN32) || defined(_WIN32)
    if ( IsPlatformNT() )
    {
        dwBIOSChecksum  = CalculateRegKeyChecksum( "SystemBiosDate" );
        dwBIOSChecksum += CalculateRegKeyChecksum( "SystemBiosVersion" );
        m_dwBiosCrc32 = dwBIOSChecksum;
    } else
#endif
    {
        LPBYTE pbMemoryByte;

#if defined(WIN32) || defined(_WIN32)
        pbMemoryByte = (LPBYTE)0xF0000;
#else
        pbMemoryByte = (LPBYTE)MAKELONG(0, &_F000H);
#endif
        dwBIOSChecksum = CalculateMemoryRegionChecksum(pbMemoryByte, 2048);
#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
        m_dwBiosCrc32 = CRC_32(pbMemoryByte, 2048);
#endif
    }

    m_szHardwareID[ BIOS_DIGIT ] = (CHAR)( dwBIOSChecksum % 9 ) + '0';
}

#if defined(WIN32) || defined(_WIN32)

UINT CHardware::CalculateRegKeyChecksum(LPSTR lpszKey)
{
    LONG lStatus;
    HKEY hkSystem;
    UINT uChecksum;

    uChecksum = 0;

    lStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System", 0, KEY_QUERY_VALUE, &hkSystem );

    _ASSERT( lStatus == ERROR_SUCCESS );

    if ( lStatus == ERROR_SUCCESS )
    {
        DWORD dwValueType;
        DWORD dwBufferSize;
        BYTE  Buffer[ MAX_BIOS_KEY_LENGTH ];

        dwBufferSize = MAX_BIOS_KEY_LENGTH;

        lStatus = RegQueryValueEx( hkSystem, lpszKey, NULL, &dwValueType, Buffer, &dwBufferSize );

        // ASSERT( lStatus == ERROR_SUCCESS ); // Not all values are guarenteed to exist

        if ( lStatus == ERROR_SUCCESS )
        {
            UINT nCurrentByte;

            for ( nCurrentByte = 0; nCurrentByte < dwBufferSize; nCurrentByte++ )
            {
                uChecksum += Buffer[ nCurrentByte ];
            }
        }

        RegCloseKey( hkSystem );
    }

    return( uChecksum );
}
#endif

DWORD CHardware::CalculateMemoryRegionChecksum( LPBYTE pbChecksumArea, INT nNumberBytes )
{
    DWORD  dwRegionChecksum = 0;

    while (0 < nNumberBytes)
    {
        dwRegionChecksum += (UINT)( *pbChecksumArea );
        ++pbChecksumArea;
        --nNumberBytes;
    }

    return( dwRegionChecksum );
}

#if !defined(WIN32) && !defined(_WIN32)

#pragma pack(1)
   // Media ID
   typedef struct {
       WORD   wInfoLevel;
       DWORD  dwSerialNum;
       char   achVolLabel[11];
       BYTE   abFileSysType[8];
   } MID, *PMID, FAR* LPMID;
#pragma pack()

#endif


VOID CHardware::SetHDSerialDigit()
{
    m_szHardwareID[ HD_SERIAL_DIGIT ] = '?';
    BOOL  fInfoSuccess;
    DWORD dwVolumeSerialNumber;

#if defined(WIN32) || defined(_WIN32)

    DWORD dwFileSystemFlags;
    DWORD dwMaximumComponentLength;
    CHAR  szBootDrivePath[ MAX_PATH ];

    wsprintf( szBootDrivePath, "C:\\" );
    fInfoSuccess = GetVolumeInformation( szBootDrivePath, NULL, 0, &dwVolumeSerialNumber, &dwMaximumComponentLength, &dwFileSystemFlags, NULL, 0 );

    _ASSERT( fInfoSuccess );

#else

    LPMID  pmid;
    union  _REGS regs;
    struct _SREGS segregs;
    DWORD  dwMem;

    dwMem = GlobalDosAlloc(sizeof(MID));

    WORD wMidSelector = LOWORD(dwMem);
    WORD wMidSegment = HIWORD(dwMem);

    pmid = (LPMID)MAKELP(wMidSelector, 0);
    ZeroMemory(pmid, sizeof(MID));

    ZeroMemory(&regs, sizeof(regs));
    ZeroMemory(&segregs, sizeof(segregs));

    regs.x.ax = 0x440d;  // DOS Function 440Dh - IOCTL for Block Device
    regs.h.cl = 0x66;    // Minor Code 66h - Get Media ID
    regs.h.ch = 0x08;    // Device category (must be 08h)
    regs.x.bx = 3;       // Drive C:
    regs.x.dx = 0;       // pmid offset

    segregs.ds = wMidSelector; // wMidSegment;
    segregs.es = wMidSelector; // wMidSegment;

    _intdosx(&regs, &regs, &segregs);

    fInfoSuccess = !regs.x.cflag;

    dwVolumeSerialNumber = pmid->dwSerialNum;
    GlobalDosFree(wMidSelector);
#endif

    if ( fInfoSuccess )
    {
        m_szHardwareID[ HD_SERIAL_DIGIT ] = (CHAR)( dwVolumeSerialNumber % 9 ) + '0';

#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
        m_dwVolSer = dwVolumeSerialNumber;
#endif

    }
}



VOID CHardware::SetTotalRAMDigit()
{
    DWORD        dwTotalMegabytes;

    m_szHardwareID[ TOTAL_RAM_DIGIT ] = '?';

#if defined(WIN32) || defined(_WIN32)

    MEMORYSTATUS mStatus;

    mStatus.dwLength = sizeof( MEMORYSTATUS );

    GlobalMemoryStatus( &mStatus );

    dwTotalMegabytes  = (DWORD)( mStatus.dwTotalPhys / (1024 * 1024)); // convert to Megabytes
    dwTotalMegabytes += 1; // Add 1Mb to produce accurate result due to reserved space

#else
    BYTE abDpmiMemInfo[0x30];

    memset(abDpmiMemInfo, -1, sizeof(abDpmiMemInfo));

    __asm {
                push    di                      ;save regs

                push    ss
                pop     es                      ;make es point to stack
                lea     di,abDpmiMemInfo        ;Get offset of buffer
                mov     ax,0500h                ;DPMI -- Get Free Memory Info
                int     31h                     ;Call DPMI

                pop     di                      ;restore regs
    }

    DWORD dwTotalPages = *(LPDWORD)&abDpmiMemInfo[0x18];

    // check to see if the field is -1 (error) and just use 0
    // we're adding 1 to account for the memory below 1M (I think)
    dwTotalMegabytes = (dwTotalPages == -1) ? 0 : (1 + dwTotalPages/(1024/4));
#endif

    m_szHardwareID[ TOTAL_RAM_DIGIT ] = (CHAR)( dwTotalMegabytes % 9 ) + '0';

#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
    m_dwTotalRamMegs = dwTotalMegabytes;
#endif

}


VOID CHardware::SetFDConfigDigit()
{
    DWORD dwFDConfig;

#if defined(WIN32) || defined(_WIN32)
    if ( IsPlatformNT() )
    {
        dwFDConfig  = CalculateDriveCapacityNT( 1 ) << 2;
        dwFDConfig += CalculateDriveCapacityNT( 2 );
    } else
#endif
    {
#ifndef _WIN64
        dwFDConfig  = CalculateDriveCapacity95( 1 ) << 2;
        dwFDConfig += CalculateDriveCapacity95( 2 );
#endif
    }

    m_szHardwareID[ FD_CONFIG_DIGIT ] = (CHAR)( dwFDConfig % 9 ) + '0';
}

#ifndef _WIN64
DWORD CHardware::CalculateDriveCapacity95( INT nDrive )
{
    DWORD   dwDriveCapacity = 0;
    BOOL    fOk;

    UINT    uNumberHeads;
    UINT    uNumberTracks;
    UINT    uBytesPerSector;
    UINT    uSectorsPerTrack;
    LPBYTE  pbDiskParamTable;


#if defined(WIN32) || defined(_WIN32)
    HANDLE         hDevice;
    BOOL           fResult;
    DIOC_REGISTERS DIOCRegs;
    DWORD          dwBytesReturned;

    // Open VWIN32 Device For Access To DOS Int 13h Functions

    hDevice = CreateFile( "\\\\.\\vwin32", 0, 0, NULL, 0, FILE_FLAG_DELETE_ON_CLOSE, NULL );
    fOk = (hDevice != INVALID_HANDLE_VALUE);


    if (fOk)
    {
        // Invoke Int 13h Function 08h - Get Drive Parameters

        DIOCRegs.reg_EAX = 0x0800; // Get Drive Parameters
        DIOCRegs.reg_EDX = nDrive - 1; // 0 = A:, 1 = B:

        fResult = DeviceIoControl( hDevice, VWIN32_DIOC_DOS_INT13, &DIOCRegs, sizeof( DIOC_REGISTERS ), &DIOCRegs, sizeof( DIOC_REGISTERS ), &dwBytesReturned, NULL );

        // Determine if Int 13h Call Succeeded
        fOk = (fResult == TRUE && 0 == (DIOCRegs.reg_Flags & FLAGS_CARRY));
    }

    if (fOk)
    {
        // Calculate Drive Capacity if Drive Number is Valid

        if ( ( DIOCRegs.reg_EDX & 0xFF ) >= (UINT)nDrive )
        {

            pbDiskParamTable = (UCHAR *)DIOCRegs.reg_EDI;

            uNumberHeads     = ( ( DIOCRegs.reg_EDX >> 8 ) & 0xFF ) + 1;
            uNumberTracks    = ( ( ( DIOCRegs.reg_ECX << 2 ) & 0x300 ) + ( ( DIOCRegs.reg_ECX >> 8 ) & 0xFF ) ) + 1;
            uSectorsPerTrack = ( DIOCRegs.reg_ECX & 0x3F );
            uBytesPerSector  = ( 128 << ( *( pbDiskParamTable + 3 ) ) );

            dwDriveCapacity = uNumberHeads * uNumberTracks * uSectorsPerTrack * uBytesPerSector;
        }
    }

    if (hDevice != INVALID_HANDLE_VALUE)
    {
        CloseHandle( hDevice );
    }

#else

    union _REGS regs;
    struct _SREGS segregs;

    ZeroMemory(&regs, sizeof(regs));
    ZeroMemory(&segregs, sizeof(segregs));

    regs.h.ah = 0x08;       // BIOS Function 08h - Get drive parameters
    regs.x.dx = nDrive - 1; // 0 = A:, 1 = B:

    _int86x(
        0x13, // BIOS Disk
        &regs,
        &regs,
        &segregs);


    fOk = (!regs.x.cflag);

    if (fOk)
    {
        uNumberHeads = regs.h.dh + 1;
        uNumberTracks = ((regs.h.cl & 0xC0) << 2) + regs.h.ch + 1;
        uSectorsPerTrack = regs.h.cl & 0x3F;

        pbDiskParamTable = (LPBYTE)MAKELP(segregs.es, regs.x.di);

        uBytesPerSector = (128 << pbDiskParamTable[3]);

        dwDriveCapacity = (DWORD)uNumberHeads * uNumberTracks * uSectorsPerTrack * uBytesPerSector;
    }

#endif

    dwDriveCapacity /= ( 1024L * 100L );


    return( dwDriveCapacity );
}
#endif

#if defined(WIN32) || defined(_WIN32)

DWORD CHardware::CalculateDriveCapacityNT(INT nDrive)
{
    BOOL   fDriveExists;
    DWORD  dwDriveCapacity;
    DWORD  dwBytesReturned;
    CHAR   szDrive[ MAX_PATH ];
    CHAR   szDriveAssignment[ MAX_PATH ];

    dwDriveCapacity = 0;

    // Determine if Logical Drive Exists

    fDriveExists = FALSE;

    wsprintf( szDrive, "%c:", 'A' + ( nDrive - 1 ) );   // Create DOS Drive Identifier (A: or B:)

    dwBytesReturned = QueryDosDevice( szDrive, szDriveAssignment, MAX_PATH );

    if ( dwBytesReturned != 0 )
    {
        LPTSTR lpszWalkString;

        // DBCS-Enabled Terminate String At 2nd Backslash (1st Backslash always at Position 0)

        lpszWalkString = szDriveAssignment;

        do
        {
            lpszWalkString = CharNext( lpszWalkString );

            switch( *lpszWalkString )
            {
                case '\\':
                    *lpszWalkString = 0;
                    break;
            }
        }
        while( *lpszWalkString != 0 );

        // Determine if Logical Drive is Physically Present

        if ( lstrcmp( szDriveAssignment, "\\Device" ) == 0 )
        {
            fDriveExists = TRUE;
        }
    }

    if ( fDriveExists == TRUE )
    {
        // Get All Supported Media Types for Drive

        HANDLE hDevice;
        BOOL   fResult;

        wsprintf( szDrive, "\\\\.\\%c:", 'A' + ( nDrive - 1 ) ); // Create NT Drive Identifier (\\.\A: or \\.\B:)

        hDevice = CreateFile( szDrive, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );

        _ASSERT( hDevice != INVALID_HANDLE_VALUE );

        if ( hDevice != INVALID_HANDLE_VALUE )
        {
            DISK_GEOMETRY dGeometry[ 10 ];

            fResult = DeviceIoControl( hDevice, IOCTL_DISK_GET_MEDIA_TYPES, NULL, 0, (LPVOID)&dGeometry, sizeof( DISK_GEOMETRY ) * 10, &dwBytesReturned, NULL );

            _ASSERT( fResult );

            if ( fResult == TRUE )
            {
                // Calculate Maximum Media Size of Drive in Bytes if No Errors

                INT  nMediaCount;
                INT  nCurrentMedia;
                UINT uCurrentMediaCapacity;

                nMediaCount = dwBytesReturned / sizeof( DISK_GEOMETRY );

                for ( nCurrentMedia = 0; nCurrentMedia < nMediaCount; nCurrentMedia++ )
                {
                    uCurrentMediaCapacity  = (UINT)dGeometry[ nCurrentMedia ].Cylinders.LowPart;
                    uCurrentMediaCapacity *= (UINT)dGeometry[ nCurrentMedia ].TracksPerCylinder;
                    uCurrentMediaCapacity *= (UINT)dGeometry[ nCurrentMedia ].SectorsPerTrack;
                    uCurrentMediaCapacity *= (UINT)dGeometry[ nCurrentMedia ].BytesPerSector;

                    if ( uCurrentMediaCapacity > dwDriveCapacity )
                    {
                        dwDriveCapacity = uCurrentMediaCapacity;
                    }
                }
            }

            CloseHandle( hDevice );
        }
    }

    dwDriveCapacity /= ( 1024 * 100 );

    return( dwDriveCapacity );
}
#endif

VOID CHardware::SetVideoBIOSDigit()
{
    DWORD dwVideoBIOSChecksum;

#if defined(WIN32) || defined(_WIN32)
    if ( IsPlatformNT() )
    {
        dwVideoBIOSChecksum  = CalculateRegKeyChecksum( "VideoBiosDate" );
        dwVideoBIOSChecksum += CalculateRegKeyChecksum( "VideoBiosVersion" );

#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
        m_dwVideoBiosCrc32 = dwVideoBIOSChecksum;
#endif

    } else
#endif
    {

        LPBYTE pbMemoryByte;

#if defined(WIN32) || defined(_WIN32)
        pbMemoryByte = (LPBYTE)0xC0000;
#else
        pbMemoryByte = (LPBYTE)MAKELONG(0, &_C000H);
#endif
        dwVideoBIOSChecksum = CalculateMemoryRegionChecksum(pbMemoryByte, 2048);

#ifdef HWID_DETAIL ////////////////////////////////////////////////////////////
        m_dwVideoBiosCrc32 = CRC_32(pbMemoryByte, 2048);
#endif

    }

    m_szHardwareID[ VIDEO_BIOS_DIGIT ] = (CHAR)( dwVideoBIOSChecksum % 9 ) + '0';
}

#ifndef NO_HWID_GUID //////////////////////////////////////////////////////////

VOID CHardware::CalculateHardwareGUID()
{
    ULONG   uCRC;
    INT     nIndex;
    CCrc32 *lpCCrc32;
    CHAR    szCRCTemp[ 20 ];

    // Create Empty Template for GUID

    lstrcpy( m_szHardwareGUID, "{30303030-30DA-0000-0000-0020AFC36E79}" );

    // Add ASCII HWID to GUID

    for ( nIndex = 0; nIndex < lstrlen( m_szHardwareID ); nIndex++ )
    {
        switch( nIndex )
        {
            case 0:
            case 1:
            case 2:
            case 3:
                m_szHardwareGUID[ 2 + ( nIndex * 2 ) ] = m_szHardwareID[ nIndex ];
                break;

            case 4:
                m_szHardwareGUID[ 11 ] = m_szHardwareID[ nIndex ];
                break;

            default:
                _ASSERT( FALSE );
                break;
        }
    }

    // Calculate GUID CRC

    lpCCrc32 = new CCrc32();
    _ASSERT( lpCCrc32->uClassID == CCRC32_CLASSID );

    uCRC = lpCCrc32->CalculateBlockCRC( m_szHardwareGUID, lstrlen( m_szHardwareGUID ) );

    delete lpCCrc32;

    // Add CRC Result To GUID

    wsprintf( szCRCTemp, "%08X", uCRC );

    for ( nIndex = 0; nIndex < lstrlen( szCRCTemp ); nIndex++ )
    {
        switch( nIndex )
        {
            case 0:
            case 1:
            case 2:
            case 3:
                m_szHardwareGUID[ 15 + nIndex ] = szCRCTemp[ nIndex ];
                break;

            case 4:
            case 5:
            case 6:
            case 7:
                m_szHardwareGUID[ 16 + nIndex ] = szCRCTemp[ nIndex ];
                break;

            default:
                _ASSERT( FALSE );
                break;
        }
    }
}
#endif ////////////////////////////////////////////////////////////////////////


#if 0 /////////////////////////////////////////////////////////////////////////

// Test main() function

int PASCAL WinMain(
    HINSTANCE, // hInstance,  // handle to current instance
    HINSTANCE, // hPrevInstance,  // handle to previous instance
    LPSTR, // lpCmdLine,      // pointer to command line
    int // nCmdShow          // show state of window)
)
{

    CHardware hwid;

    MessageBox(
        NULL,
        (char *)hwid.GetGUID(),
        (char *)hwid.GetID(),
        MB_OK);

    return 0;
}

#endif ////////////////////////////////////////////////////////////////////////
