/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/Win32/pnUtW32Misc.cpp
*   
***/

#include "../../Pch.h"
#pragma hdrstop

/*****************************************************************************
*
*   Private
*
***/
static MEMORYSTATUSEX s_memstatus;

/*****************************************************************************
*
*   Exports
*
***/

//============================================================================
const wchar * AppGetCommandLine () {
    return GetCommandLineW();
}

//============================================================================
void MachineGetName (wchar *computerName, unsigned int length) {
    DWORD len = length;
    GetComputerNameW(computerName, &len);
}

/*****************************************************************************
*
*   System status
*
***/

//============================================================================
void MemoryGetStatus (MemoryStatus * status) {
    MEMORYSTATUSEX mem;
    mem.dwLength = sizeof(mem);
    GlobalMemoryStatusEx(&mem);

    const qword BYTES_PER_MB = 1024 * 1024;
    status->totalPhysMB         = unsigned(mem.ullTotalPhys     / BYTES_PER_MB);
    status->availPhysMB         = unsigned(mem.ullAvailPhys     / BYTES_PER_MB);
    status->totalPageFileMB     = unsigned(mem.ullTotalPageFile / BYTES_PER_MB);
    status->availPageFileMB     = unsigned(mem.ullAvailPageFile / BYTES_PER_MB);
    status->totalVirtualMB      = unsigned(mem.ullTotalVirtual  / BYTES_PER_MB);   
    status->availVirtualMB      = unsigned(mem.ullAvailVirtual  / BYTES_PER_MB);
    status->memoryLoad          = mem.dwMemoryLoad;
}

//============================================================================
void DiskGetStatus (ARRAY(DiskStatus) * disks) {
    for (;;) {
        DWORD length = GetLogicalDriveStrings(0, NULL);
        if (!length || length > 2048)
            break;

        wchar * buffer = ALLOCA(wchar, length + 1);
        if (!GetLogicalDriveStringsW(length, buffer))
            break;

        for (; *buffer; buffer += StrLen(buffer) + 1) {
            UINT driveType = GetDriveTypeW(buffer);
            if (driveType != DRIVE_FIXED)
                continue;

            ULARGE_INTEGER freeBytes;
            ULARGE_INTEGER totalBytes;
            if (!GetDiskFreeSpaceExW(buffer, &freeBytes, &totalBytes, NULL))
                continue;

            DiskStatus status;
            StrCopy(status.name, buffer, arrsize(status.name));

            const qword BYTES_PER_MB = 1024 * 1024;
            status.totalSpaceMB = unsigned(totalBytes.QuadPart / BYTES_PER_MB);
            status.freeSpaceMB  = unsigned(freeBytes.QuadPart  / BYTES_PER_MB);

            disks->Add(status);
        }
        break;
    }
}

//============================================================================
// Loosely taken from MS's cpuid code sample
void CpuGetInfo (
    word *  cpuCaps,
    dword * cpuVendor,
    word *  cpuSignature
) {
    dword signature = 0;
    dword extended  = 0;
    dword flags[2]  = { 0, 0 };
    cpuVendor[0]    = 0;

    _asm {
        // Detect if cpuid instruction is supported by attempting
        // to change the ID bit of EFLAGS
        pushfd
        pop eax             // get EFLAGS
        mov ecx, eax        // store copy of original EFLAGS
        xor eax, 0x200000   // flip ID bit 
        push eax
        popfd               // replace EFLAGS
        pushfd              // get EFLAGS
        pop eax
        xor eax, ecx
        je DONE

        // Get processor id (GenuineIntel, AuthenticAMD, etc)
        xor eax, eax
        cpuid
        mov edi, cpuVendor
        mov [edi + 0], ebx
        mov [edi + 4], edx
        mov [edi + 8], ecx

        // Check if capability flags are supported
        cmp eax, 1
        jl DONE

        // Get processor capability flags and signature
        mov eax, 1
        cpuid
        mov signature, eax
        mov [flags + 0], edx
        mov [flags + 4], ecx

        // Check for extended capabilities
        mov eax, 0x80000000
        cpuid
        cmp eax, 0x80000001
        jl DONE

        // Get extended capabilities
        mov eax, 0x80000001
        cpuid
        mov extended, edx

DONE:
    }

    // Decode capability flags
    const static struct CpuCap {
        word    cpuFlag;
        byte    field;
        byte    bit;
    } s_caps[] = {
        // feature      field   bit
        // -------      -----   ---
        { kCpuCapCmov,  0,      15  },
        { kCpuCapEst,   1,      7   },
        { kCpuCapHtt,   0,      28  },
        { kCpuCapMmx,   0,      23  },
        { kCpuCapPsn,   0,      18  },
        { kCpuCapSse,   0,      25  },
        { kCpuCapSse2,  0,      26  },
        { kCpuCapSse3,  1,      0   },
        { kCpuCapTsc,   0,      4   },
    };
    for (unsigned i = 0; i < arrsize(s_caps); ++i) {
        const CpuCap & cap = s_caps[i];
        if (flags[cap.field] & (1 << cap.bit))
            *cpuCaps |= cap.cpuFlag;
    }

    // Copy signature
    *cpuSignature = word(signature & 0xfff);

    // If this is an AMD CPU, check for 3DNow support
    const char * vendorAmd = "AuthenticAMD";
    if (!MemCmp(vendorAmd, cpuVendor, 12)) {
        if (extended & (1 << 31))
            *cpuCaps |= kCpuCap3dNow;
    }
}