#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "host_def.h"
#include "insignia.h"
#include <stdlib.h>
#include "xt.h"
#include "error.h"
#include "host_rrr.h"
#include "host_nls.h"
#include "nt_timer.h"



void CpuEnvInit(void);

typedef struct _CpuEnvironmentVariable {
    struct _CpuEnvironmentVariable *Next;
    char *Data;
    char Name[1];
} CPUENVVAR, *PCPUENVVAR;

PCPUENVVAR CpuEnvVarListHead=NULL;

#if DBG
BOOLEAN verboseGetenv;
#endif


INT host_main(INT argc, CHAR **argv);  // located in base\support\main.c

_CRTAPI1 main(int argc, CHAR ** argv)
{
   int ret=-1;

    /*
     *  Intialize synchronization events for the timer\heartbeat
     *  so that we can always suspend the heartbeat when an exception
     *  occurs.
     */
    TimerInit();



    try {

        CpuEnvInit();

        /*
         *  Load in the default system error message, since a resource load
         *  will fail when we are out of memory, if this fails we must exit
         *  to avoid confusion.
         */
        nls_init();

        ret = host_main(argc, argv);
        }
    except(VdmUnhandledExceptionFilter(GetExceptionInformation())) {
        ;  // we shouldn't arrive here
        }

    return ret;
}






//
// The following function is placed here, so build will resolve references to
// DbgBreakPoint here, instead of NTDLL.
//

VOID
DbgBreakPoint(
    VOID
    )
/*++

Routine Description:

    This routine is a substitute for the NT DbgBreakPoint routine.
    If a user mode debugger is atached we invoke the real DbgBreakPoint()
    thru the win32 api DebugBreak.

    If no usermode debugger is attached:
       - free build no effect
       - checked build raise an acces violation to invoke the system
         hard error popup which will give user a chance to invoke
         ntsd.

Arguments:

    None.

Return Value:

    None.

--*/
{
HANDLE      MyDebugPort;
DWORD       dw;

         // are we being debugged ??
     dw = NtQueryInformationProcess(
                  NtCurrentProcess(),
                  ProcessDebugPort,
                  &MyDebugPort,
                  sizeof(MyDebugPort),
                  NULL );
     if (!NT_SUCCESS(dw) || MyDebugPort == NULL)  {
#ifndef PROD
           RaiseException(STATUS_ACCESS_VIOLATION, 0L, 0L, NULL);
#endif
           return;
          }

     DebugBreak();
}





/*
 *  Softpc env variables are mapped to the registry
 *
 *  "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\WOW\CpuEnv"
 *
 * The string values for the CpuEnv key are read at initialization
 * into the CpuEnv linked list. The Environment variables are defined
 * as string key values, where the name of the value is equivalent to
 * the Cpu Environment Variable name, and the string value is equivalent
 * to the value of the environment variable value. This allows the
 * emulator defaults to be overridden, by adding the appropriate value
 * to CpuEnv subkey. Under standard retail setup there won't normally
 * be a CpuEnv subkey, and NO cpu env variables defined to minimize
 * code\data on a standard retail system.
 *
 */



/*
 *  Adds a CpuEnv KEY_VALUE_FULL_INFORMATION to the CpuEnvList
 */
BOOLEAN
AddToCpuEnvList(
   PKEY_VALUE_FULL_INFORMATION KeyValueInfo
   )
{
   NTSTATUS Status;
   ULONG BufferSize;
   PCPUENVVAR CpuEnvVar;
   UNICODE_STRING UnicodeString;
   ANSI_STRING ValueName;
   ANSI_STRING ValueData;
   char NameBuffer[MAX_PATH+sizeof(WCHAR)];
   char DataBuffer[MAX_PATH+sizeof(WCHAR)];


   /*
    *  Convert Value Name and Data strings from unicode to ansi
    */

   ValueName.Buffer = NameBuffer;
   ValueName.MaximumLength = sizeof(NameBuffer) - sizeof(WCHAR);
   ValueName.Length        = 0;
   UnicodeString.Buffer = (PWSTR)KeyValueInfo->Name;
   UnicodeString.MaximumLength =
   UnicodeString.Length        = (USHORT)KeyValueInfo->NameLength;
   Status = RtlUnicodeStringToAnsiString(&ValueName, &UnicodeString,FALSE);
   if (!NT_SUCCESS(Status)) {
       return FALSE;
       }

   ValueData.Buffer = DataBuffer;
   ValueData.MaximumLength = sizeof(DataBuffer) - sizeof(WCHAR);
   ValueData.Length        = 0;
   UnicodeString.Buffer = (PWSTR)((PBYTE)KeyValueInfo + KeyValueInfo->DataOffset);
   UnicodeString.MaximumLength =
   UnicodeString.Length        = (USHORT)KeyValueInfo->DataLength;
   Status = RtlUnicodeStringToAnsiString(&ValueData, &UnicodeString, FALSE);
   if (!NT_SUCCESS(Status)) {
       return FALSE;
       }


   /*
    * Allocate CPUENVLIST structure, with space for the ansi strings
    */
   CpuEnvVar = malloc(sizeof(CPUENVVAR)+    // list structure size
                      ValueName.Length +    // strlen Name
                      ValueData.Length +    // strlen Data
                      1                     // Null for Data
                      );
   if (!CpuEnvVar) {
       return FALSE;
       }


   /*
    *  Copy in the ansi strings, and link it into CpuEnvVar List
    */
   memcpy(CpuEnvVar->Name, ValueName.Buffer, ValueName.Length);
   *(CpuEnvVar->Name + ValueName.Length) = '\0';
   CpuEnvVar->Data = CpuEnvVar->Name + ValueName.Length + 1;
   memcpy(CpuEnvVar->Data, ValueData.Buffer, ValueData.Length);
   *(CpuEnvVar->Data + ValueData.Length) = '\0';
   CpuEnvVar->Next = CpuEnvVarListHead;
   CpuEnvVarListHead = CpuEnvVar;

   return TRUE;
}




/*
 * Reads the CpuEnv values from the registry, into CpuEnvList
 */
void
CpuEnvInit(
   void
   )
{
    int Index;
    NTSTATUS Status;
    HANDLE CpuEnvKey = NULL;
    ULONG ResultLength;
    UNICODE_STRING UnicodeString;
    OBJECT_ATTRIBUTES ObjectAttributes;
    PKEY_VALUE_FULL_INFORMATION KeyValueInfo;
    BYTE NameDataBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + MAX_PATH*2*sizeof(WCHAR)];



    KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION) NameDataBuffer;

#ifndef MONITOR
/*
 *  BUGBUG temp hack code to add two env var, which aren't properly
 *  defaulted to in the risc cpu emulator
 *
 *  THIS is to be removed before SUR ship 19-Dec-1995 Jonle
 */
     {
     PWCHAR Data;

     wcscpy(KeyValueInfo->Name, L"Soft486Buffers");
     KeyValueInfo->NameLength = wcslen(KeyValueInfo->Name) * sizeof(WCHAR);
     Data = (PWCH)((PBYTE)KeyValueInfo->Name + KeyValueInfo->NameLength + sizeof(WCHAR));
     wcscpy(Data, L"511");
     KeyValueInfo->DataLength = wcslen(Data) * sizeof(WCHAR);
     KeyValueInfo->DataOffset =  (PBYTE)Data - (PBYTE)KeyValueInfo;
     AddToCpuEnvList(KeyValueInfo);

     wcscpy(KeyValueInfo->Name, L"LCIF_FILENAME");
     KeyValueInfo->NameLength = wcslen(KeyValueInfo->Name) * sizeof(WCHAR);
     Data = (PWCH)((PBYTE)KeyValueInfo->Name + KeyValueInfo->NameLength + sizeof(WCHAR));
     wcscpy(Data, L"R lcif");
     KeyValueInfo->DataLength = wcslen(Data) * sizeof(WCHAR);
     KeyValueInfo->DataOffset = (PBYTE)Data - (PBYTE)KeyValueInfo;
     AddToCpuEnvList(KeyValueInfo);
     }

#endif



    RtlInitUnicodeString(
        &UnicodeString,
        L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Wow\\CpuEnv"
        );

    InitializeObjectAttributes(&ObjectAttributes,
                               &UnicodeString,
                               OBJ_CASE_INSENSITIVE,
                               (HANDLE)NULL,
                               NULL
                               );

    Status = NtOpenKey(&CpuEnvKey,
                       KEY_READ,
                       &ObjectAttributes
                       );

    //
    // If there is no CpuEnv key, CpuEnvList is empty.
    //
    if (!NT_SUCCESS(Status)) {
        return;
        }

    Index = 0;
    while (TRUE) {
         Status = NtEnumerateValueKey(CpuEnvKey,
                                      Index,
                                      KeyValueFullInformation,
                                      KeyValueInfo,
                                      sizeof(NameDataBuffer),
                                      &ResultLength
                                      );

         if (!NT_SUCCESS(Status) || !AddToCpuEnvList(KeyValueInfo)) {
             break;
             }

       Index++;
       };

    NtClose(CpuEnvKey);

#if DBG
    {
    char *pEnvStr;
    pEnvStr = getenv("VERBOSE_GETENV");
    verboseGetenv = pEnvStr && !_stricmp(pEnvStr, "TRUE");
    }
#endif

}



/*
 * In order to catch all references, we define our own
 * version of the CRT getenv, which does the mapping.
 */
char * _CRTAPI1 getenv(const char *Name)
{
  PCPUENVVAR CpuEnvVar;
  char *Value = NULL;

  CpuEnvVar = CpuEnvVarListHead;
  while (CpuEnvVar) {
     if (!_stricmp(CpuEnvVar->Name, Name)) {
         Value = CpuEnvVar->Data;
         break;
         }
     CpuEnvVar = CpuEnvVar->Next;
     }

#if DBG
   if (verboseGetenv) {
       DbgPrint("getenv %s:<%s>\n", Name, Value);
       }
#endif

  return Value;
}
