///////////////////////////////////////////////////////////////////////////////////////////////////
//
//  structs.cpp - extensions to dump well-known structs.
//
//  Copyright Microsoft Corporation.  All rights reserved.
//

#include "singx86.h"
#include <strsafe.h>

///////////////////////////////////////////////////////////////////////////////////////////////////
//
FieldType *ThreadContextFields = NULL;
StructType *ThreadContextStruct = NULL;

FieldType X86ThreadContextFields[] = {
    FIELD(ThreadContext, threadRecord.spill.mmx),       // "mmx" must be ThreadContextFields[0]
    FIELD(ThreadContext, threadRecord.spill.mmx.st0),   // "st0" must be ThreadContextFields[1]
    FIELD(ThreadContext, threadRecord.spill.mmx.st1),
    FIELD(ThreadContext, threadRecord.spill.mmx.st2),
    FIELD(ThreadContext, threadRecord.spill.mmx.st3),
    FIELD(ThreadContext, threadRecord.spill.mmx.st4),
    FIELD(ThreadContext, threadRecord.spill.mmx.st5),
    FIELD(ThreadContext, threadRecord.spill.mmx.st6),
    FIELD(ThreadContext, threadRecord.spill.mmx.st7),
    FIELD(ThreadContext, threadRecord.spill.mmx.fcw),
    FIELD(ThreadContext, threadRecord.spill.mmx.fsw),
    FIELD(ThreadContext, threadRecord.spill.mmx.ftw),
    FIELD(ThreadContext, threadRecord.spill.mmx.fop),
    FIELD(ThreadContext, threadRecord.spill.mmx.ip),
    FIELD(ThreadContext, threadRecord.spill.mmx.cs),
    FIELD(ThreadContext, threadRecord.spill.mmx.dp),
    FIELD(ThreadContext, threadRecord.spill.mmx.ds),
    FIELD(ThreadContext, threadRecord.spill.mmx.mxcsr),
    FIELD(ThreadContext, threadRecord.spill.mmx.mxcsrmask),
    FIELD(ThreadContext, threadRecord.spill.ax),
    FIELD(ThreadContext, threadRecord.spill.bx),
    FIELD(ThreadContext, threadRecord.spill.cx),
    FIELD(ThreadContext, threadRecord.spill.dx),
    FIELD(ThreadContext, threadRecord.spill.di),
    FIELD(ThreadContext, threadRecord.spill.si),
    FIELD(ThreadContext, threadRecord.spill.sp),
    FIELD(ThreadContext, threadRecord.spill.ip),
    FIELD(ThreadContext, threadRecord.spill.bp),
    FIELD(ThreadContext, threadRecord.spill.fl),
    // Common Fields
    FIELD(ThreadContext, threadRecord.activeStackLimit),
    FIELD(ThreadContext, regs),
    FIELD(ThreadContext, prev),
    FIELD(ThreadContext, next),
    FIELD(ThreadContext, stackBegin),
    FIELD(ThreadContext, stackLimit),
    FIELD(ThreadContext, processId),
    FIELD(ThreadContext, uncaughtFlag),
    FIELD(ThreadContext, suspendAlert),
    FIELD(ThreadContext, _thread),
    FIELD(ThreadContext, processThread),
    FIELD(ThreadContext, stackMarkers),
    FIELD(ThreadContext, processMarkers),
    FIELD(ThreadContext, threadIndex),
    FIELD(ThreadContext, processThreadIndex),
    FIELD(ThreadContext, gcStates),
    FIELDEND(),
};

FieldType X64ThreadContextFields[] = {
    FIELD(ThreadContext, threadRecord.spill.mmx),       // "mmx" must be ThreadContextFields[0]
    FIELD(ThreadContext, threadRecord.spill.mmx.st0),   // "st0" must be ThreadContextFields[1]
    FIELD(ThreadContext, threadRecord.spill.mmx.st1),
    FIELD(ThreadContext, threadRecord.spill.mmx.st2),
    FIELD(ThreadContext, threadRecord.spill.mmx.st3),
    FIELD(ThreadContext, threadRecord.spill.mmx.st4),
    FIELD(ThreadContext, threadRecord.spill.mmx.st5),
    FIELD(ThreadContext, threadRecord.spill.mmx.st6),
    FIELD(ThreadContext, threadRecord.spill.mmx.st7),
    FIELD(ThreadContext, threadRecord.spill.mmx.fcw),
    FIELD(ThreadContext, threadRecord.spill.mmx.fsw),
    FIELD(ThreadContext, threadRecord.spill.mmx.ftw),
    FIELD(ThreadContext, threadRecord.spill.mmx.fop),
    FIELD(ThreadContext, threadRecord.spill.mmx.ip),
    FIELD(ThreadContext, threadRecord.spill.mmx.cs),
    FIELD(ThreadContext, threadRecord.spill.mmx.dp),
    FIELD(ThreadContext, threadRecord.spill.mmx.ds),
    FIELD(ThreadContext, threadRecord.spill.mmx.mxcsr),
    FIELD(ThreadContext, threadRecord.spill.mmx.mxcsrmask),
    FIELD(ThreadContext, threadRecord.spill.ax),
    FIELD(ThreadContext, threadRecord.spill.bx),
    FIELD(ThreadContext, threadRecord.spill.cx),
    FIELD(ThreadContext, threadRecord.spill.dx),
    FIELD(ThreadContext, threadRecord.spill.di),
    FIELD(ThreadContext, threadRecord.spill.si),
    FIELD(ThreadContext, threadRecord.spill.sp),
    FIELD(ThreadContext, threadRecord.spill.ip),
    FIELD(ThreadContext, threadRecord.spill.bp),
    FIELD(ThreadContext, threadRecord.spill.r8),
    FIELD(ThreadContext, threadRecord.spill.r9),
    FIELD(ThreadContext, threadRecord.spill.r10),
    FIELD(ThreadContext, threadRecord.spill.r11),
    FIELD(ThreadContext, threadRecord.spill.r12),
    FIELD(ThreadContext, threadRecord.spill.r13),
    FIELD(ThreadContext, threadRecord.spill.r14),
    FIELD(ThreadContext, threadRecord.spill.r15),
    FIELD(ThreadContext, threadRecord.spill.fl),
    // Common Fields
    FIELD(ThreadContext, threadRecord.activeStackLimit),
    FIELD(ThreadContext, regs),
    FIELD(ThreadContext, prev),
    FIELD(ThreadContext, next),
    FIELD(ThreadContext, stackBegin),
    FIELD(ThreadContext, stackLimit),
    FIELD(ThreadContext, processId),
    FIELD(ThreadContext, uncaughtFlag),
    FIELD(ThreadContext, suspendAlert),
    FIELD(ThreadContext, _thread),
    FIELD(ThreadContext, processThread),
    FIELD(ThreadContext, stackMarkers),
    FIELD(ThreadContext, processMarkers),
    FIELD(ThreadContext, threadIndex),
    FIELD(ThreadContext, processThreadIndex),
    FIELD(ThreadContext, gcStates),
    FIELDEND(),
};

FieldType ArmThreadContextFields[] = {
    FIELD(ThreadContext, threadRecord.spill.r0),
    FIELD(ThreadContext, threadRecord.spill.r1),
    FIELD(ThreadContext, threadRecord.spill.r2),
    FIELD(ThreadContext, threadRecord.spill.r3),
    FIELD(ThreadContext, threadRecord.spill.r4),
    FIELD(ThreadContext, threadRecord.spill.r5),
    FIELD(ThreadContext, threadRecord.spill.r6),
    FIELD(ThreadContext, threadRecord.spill.r7),
    FIELD(ThreadContext, threadRecord.spill.r8),
    FIELD(ThreadContext, threadRecord.spill.r9),
    FIELD(ThreadContext, threadRecord.spill.r10),
    FIELD(ThreadContext, threadRecord.spill.r11),
    FIELD(ThreadContext, threadRecord.spill.r12),
    FIELD(ThreadContext, threadRecord.spill.sp),
    FIELD(ThreadContext, threadRecord.spill.lr),
    FIELD(ThreadContext, threadRecord.spill.pc),
    FIELD(ThreadContext, threadRecord.spill.cpsr),
    // Common Fields
    FIELD(ThreadContext, threadRecord.activeStackLimit),
    FIELD(ThreadContext, regs),
    FIELD(ThreadContext, prev),
    FIELD(ThreadContext, next),
    FIELD(ThreadContext, stackBegin),
    FIELD(ThreadContext, stackLimit),
    FIELD(ThreadContext, processId),
    FIELD(ThreadContext, uncaughtFlag),
    FIELD(ThreadContext, suspendAlert),
    FIELD(ThreadContext, _thread),
    FIELD(ThreadContext, processThread),
    FIELD(ThreadContext, stackMarkers),
    FIELD(ThreadContext, processMarkers),
    FIELD(ThreadContext, threadIndex),
    FIELD(ThreadContext, processThreadIndex),
    FIELD(ThreadContext, gcStates),
    FIELDEND(),
};

///////////////////////////////////////////////////////////////////////////////////////////////////
//
FieldType *ThreadFields = NULL;
StructType *ThreadStruct = NULL;

FieldType X86ThreadFields[] = {
    FIELD(Thread, context),                             // "context" must be ThreadFields[0]
    FIELD(Thread, context.threadRecord.spill.ax),
    FIELD(Thread, context.threadRecord.spill.bx),
    FIELD(Thread, context.threadRecord.spill.cx),
    FIELD(Thread, context.threadRecord.spill.dx),
    FIELD(Thread, context.threadRecord.spill.di),
    FIELD(Thread, context.threadRecord.spill.si),
    FIELD(Thread, context.threadRecord.spill.sp),
    FIELD(Thread, context.threadRecord.spill.ip),
    FIELD(Thread, context.threadRecord.spill.bp),
    FIELD(Thread, context.threadRecord.spill.fl),
    FIELD(Thread, context.threadRecord.spill.mmx.st0),
    FIELD(Thread, context.threadRecord.spill.mmx.st1),
    FIELD(Thread, context.threadRecord.spill.mmx.st2),
    FIELD(Thread, context.threadRecord.spill.mmx.st3),
    FIELD(Thread, context.threadRecord.spill.mmx.st4),
    FIELD(Thread, context.threadRecord.spill.mmx.st5),
    FIELD(Thread, context.threadRecord.spill.mmx.st6),
    FIELD(Thread, context.threadRecord.spill.mmx.st7),
    FIELD(Thread, context.threadRecord.spill.mmx.fcw),
    FIELD(Thread, context.threadRecord.spill.mmx.fsw),
    FIELD(Thread, context.threadRecord.spill.mmx.ftw),
    FIELD(Thread, context.threadRecord.spill.mmx.fop),
    FIELD(Thread, context.threadRecord.spill.mmx.ip),
    FIELD(Thread, context.threadRecord.spill.mmx.cs),
    FIELD(Thread, context.threadRecord.spill.mmx.dp),
    FIELD(Thread, context.threadRecord.spill.mmx.ds),
    FIELD(Thread, context.threadRecord.spill.mmx.mxcsr),
    FIELD(Thread, context.threadRecord.spill.mmx.mxcsrmask),
    // Common Fields
    FIELD(Thread, context.threadRecord.activeStackLimit),
    FIELD(Thread, context.regs),
    FIELD(Thread, context.prev),
    FIELD(Thread, context.next),
    FIELD(Thread, context.stackBegin),
    FIELD(Thread, context.stackLimit),
    FIELD(Thread, context.processId),
    FIELD(Thread, context.uncaughtFlag),
    FIELD(Thread, context.suspendAlert),
    FIELD(Thread, context._thread),
    FIELD(Thread, context.processThread),
    FIELD(Thread, context.stackMarkers),
    FIELD(Thread, context.processMarkers),
    FIELD(Thread, context.threadIndex),
    FIELD(Thread, context.processThreadIndex),
    FIELD(Thread, context.gcStates),
    FIELD(Thread, blocked),
    FIELD(Thread, blockedOn),
    FIELD(Thread, blockedOnCount),
    FIELD(Thread, blockedUntil),
    FIELD(Thread, process),
    FIELD(Thread, schedulerEntry),
    FIELDEND(),
};

FieldType X64ThreadFields[] = {
    FIELD(Thread, context),                             // "context" must be ThreadFields[0]
    FIELD(Thread, context.threadRecord.spill.ax),
    FIELD(Thread, context.threadRecord.spill.bx),
    FIELD(Thread, context.threadRecord.spill.cx),
    FIELD(Thread, context.threadRecord.spill.dx),
    FIELD(Thread, context.threadRecord.spill.di),
    FIELD(Thread, context.threadRecord.spill.si),
    FIELD(Thread, context.threadRecord.spill.sp),
    FIELD(Thread, context.threadRecord.spill.ip),
    FIELD(Thread, context.threadRecord.spill.bp),
    FIELD(Thread, context.threadRecord.spill.r8),
    FIELD(Thread, context.threadRecord.spill.r9),
    FIELD(Thread, context.threadRecord.spill.r10),
    FIELD(Thread, context.threadRecord.spill.r11),
    FIELD(Thread, context.threadRecord.spill.r12),
    FIELD(Thread, context.threadRecord.spill.r13),
    FIELD(Thread, context.threadRecord.spill.r14),
    FIELD(Thread, context.threadRecord.spill.r15),
    FIELD(Thread, context.threadRecord.spill.fl),
    FIELD(Thread, context.threadRecord.spill.mmx.st0),
    FIELD(Thread, context.threadRecord.spill.mmx.st1),
    FIELD(Thread, context.threadRecord.spill.mmx.st2),
    FIELD(Thread, context.threadRecord.spill.mmx.st3),
    FIELD(Thread, context.threadRecord.spill.mmx.st4),
    FIELD(Thread, context.threadRecord.spill.mmx.st5),
    FIELD(Thread, context.threadRecord.spill.mmx.st6),
    FIELD(Thread, context.threadRecord.spill.mmx.st7),
    FIELD(Thread, context.threadRecord.spill.mmx.fcw),
    FIELD(Thread, context.threadRecord.spill.mmx.fsw),
    FIELD(Thread, context.threadRecord.spill.mmx.ftw),
    FIELD(Thread, context.threadRecord.spill.mmx.fop),
    FIELD(Thread, context.threadRecord.spill.mmx.ip),
    FIELD(Thread, context.threadRecord.spill.mmx.cs),
    FIELD(Thread, context.threadRecord.spill.mmx.dp),
    FIELD(Thread, context.threadRecord.spill.mmx.ds),
    FIELD(Thread, context.threadRecord.spill.mmx.mxcsr),
    FIELD(Thread, context.threadRecord.spill.mmx.mxcsrmask),
    // Common Fields
    FIELD(Thread, context.threadRecord.activeStackLimit),
    FIELD(Thread, context.regs),
    FIELD(Thread, context.prev),
    FIELD(Thread, context.next),
    FIELD(Thread, context.stackBegin),
    FIELD(Thread, context.stackLimit),
    FIELD(Thread, context.processId),
    FIELD(Thread, context.uncaughtFlag),
    FIELD(Thread, context.suspendAlert),
    FIELD(Thread, context._thread),
    FIELD(Thread, context.processThread),
    FIELD(Thread, context.stackMarkers),
    FIELD(Thread, context.processMarkers),
    FIELD(Thread, context.threadIndex),
    FIELD(Thread, context.processThreadIndex),
    FIELD(Thread, context.gcStates),
    FIELD(Thread, blocked),
    FIELD(Thread, blockedOn),
    FIELD(Thread, blockedOnCount),
    FIELD(Thread, blockedUntil),
    FIELD(Thread, process),
    FIELD(Thread, schedulerEntry),
    FIELDEND(),
};

FieldType ArmThreadFields[] = {
    FIELD(Thread, context),                             // "context" must be ThreadFields[0]
    FIELD(Thread, context.threadRecord.spill.r0),
    FIELD(Thread, context.threadRecord.spill.r1),
    FIELD(Thread, context.threadRecord.spill.r2),
    FIELD(Thread, context.threadRecord.spill.r3),
    FIELD(Thread, context.threadRecord.spill.r4),
    FIELD(Thread, context.threadRecord.spill.r5),
    FIELD(Thread, context.threadRecord.spill.r6),
    FIELD(Thread, context.threadRecord.spill.r7),
    FIELD(Thread, context.threadRecord.spill.r8),
    FIELD(Thread, context.threadRecord.spill.r9),
    FIELD(Thread, context.threadRecord.spill.r10),
    FIELD(Thread, context.threadRecord.spill.r11),
    FIELD(Thread, context.threadRecord.spill.r12),
    FIELD(Thread, context.threadRecord.spill.sp),
    FIELD(Thread, context.threadRecord.spill.lr),
    FIELD(Thread, context.threadRecord.spill.pc),
    FIELD(Thread, context.threadRecord.spill.cpsr),
    // Common Fields.
    FIELD(Thread, context.threadRecord.activeStackLimit),
    FIELD(Thread, context.regs),
    FIELD(Thread, context.prev),
    FIELD(Thread, context.next),
    FIELD(Thread, context.stackBegin),
    FIELD(Thread, context.stackLimit),
    FIELD(Thread, context.processId),
    FIELD(Thread, context.uncaughtFlag),
    FIELD(Thread, context.suspendAlert),
    FIELD(Thread, context._thread),
    FIELD(Thread, context.processThread),
    FIELD(Thread, context.stackMarkers),
    FIELD(Thread, context.processMarkers),
    FIELD(Thread, context.threadIndex),
    FIELD(Thread, context.processThreadIndex),
    FIELD(Thread, context.gcStates),
    FIELD(Thread, blocked),
    FIELD(Thread, blockedOn),
    FIELD(Thread, blockedOnCount),
    FIELD(Thread, blockedUntil),
    FIELD(Thread, process),
    FIELD(Thread, schedulerEntry),
    FIELDEND(),
};

///////////////////////////////////////////////////////////////////////////////////////////////////
//
FieldType ThreadEntryFields[] = {
    FIELD(ThreadEntry, queue),
    FIELDEND(),
};

// kernel name is potentially fixed up in StructType::Initialize
StructType ThreadEntryStruct
= StructType(kernel_ThreadEntry,
             sizeof(ThreadEntry), ThreadEntryFields);

FieldType StringFields[] = {
    FIELD(String, m_firstChar),                         // Accessed directly as Field[0]
    FIELD(String, m_stringLength),
    FIELDEND(),
};

// kernel name is potentially fixed up in StructType::Initialize
StructType StringStruct = StructType(kernel_String,
                                     sizeof(String), StringFields);

FieldType LogEntryFields[] = {
    FIELD(LogEntry, _cycleCount),
    FIELD(LogEntry, _eip),
    FIELD(LogEntry, _cpuId),
    FIELD(LogEntry, _threadId),
    FIELD(LogEntry, _processId),
    FIELD(LogEntry, _tag),
    FIELD(LogEntry, _severity),
    FIELD(LogEntry, _strings),
    FIELD(LogEntry, _text),
    FIELD(LogEntry, _arg0),
    FIELD(LogEntry, _arg1),
    FIELD(LogEntry, _arg2),
    FIELD(LogEntry, _arg3),
    FIELD(LogEntry, _arg4),
    FIELD(LogEntry, _arg5),
    FIELDEND(),
};

// kernel name is potentially fixed up in StructType::Initialize
StructType LogEntryStruct = StructType(kernel_Struct_Microsoft_Singularity_Tracing_LogEntry,
                                       sizeof(LogEntry), LogEntryFields);

FieldType ProcessorContextFields[] = {
    FIELD(ProcessorContext, cpuRecord.id),
    FIELD(ProcessorContext, cpuRecord.interruptStackBegin),
    FIELD(ProcessorContext, cpuRecord.interruptStackLimit),
    FIELD(ProcessorContext, _processor),
    FIELD(ProcessorContext, exception),
    FIELD(ProcessorContext, nextProcessorContext),
    FIELDEND(),
};

// kernel name is potentially fixed up in StructType::Initialize
StructType ProcessorContextStruct
= StructType(kernel_ProcessorContext,
             sizeof(ProcessorContext), ProcessorContextFields);

FieldType ProcessorFields[] = {
    FIELD(Processor, context),
    FIELD(Processor, pic),
    FIELD(Processor, timer),
    FIELD(Processor, clock),
    FIELD(Processor, timerInterrupt),
    FIELD(Processor, clockInterrupt),
    FIELD(Processor, inInterruptContext),
    FIELD(Processor, halted),
    FIELD(Processor, NumExceptions),
    FIELD(Processor, NumInterrupts),
    FIELD(Processor, NumContextSwitches),
    FIELD(Processor, interruptCounts),
    FIELDEND(),
};

// kernel name is potentially fixed up in StructType::Initialize
StructType ProcessorStruct
= StructType(kernel_Processor,
             sizeof(Processor), ProcessorFields);

///////////////////////////////////// Remote to Local conversions for structs.
//
StructType * StructType::registered = NULL;

StructType::StructType(PSTR name, ULONG localSize, struct FieldType *fields)
{
    this->next = registered;
    registered = this;

    this->name = name;
    this->localSize = localSize;
    this->fields = fields;

    this->fieldCount = 0;
    this->module = 0;
    this->type = 0;
    this->size = 0;
    this->temp = NULL;
}

HRESULT StructType::InitializeRegistered()
{
    if (TargetMachine == IMAGE_FILE_MACHINE_I386) {
        ThreadContextFields = X86ThreadContextFields;
        ThreadFields = X86ThreadFields;
        ExtVerb("Selected X86 fields.\n");
    }
    else if (TargetMachine == IMAGE_FILE_MACHINE_AMD64) {
        ThreadContextFields = X64ThreadContextFields;
        ThreadFields = X64ThreadFields;
        ExtVerb("Selected X64 fields.\n");
    }
    else if (TargetMachine == IMAGE_FILE_MACHINE_ARM) {
        ThreadContextFields = ArmThreadContextFields;
        ThreadFields = ArmThreadFields;
        ExtVerb("Selected Arm fields.\n");
    }
    else {
        ExtErr("Selected *NO* fields. [target:%08x]\n", TargetMachine);
    }

    // kernel name is potentially fixed up in StructType::Initialize
    ThreadContextStruct = new StructType(kernel_ThreadContext,
                                         sizeof(ThreadContext),
                                         ThreadContextFields);

    // kernel name is potentially fixed up in StructType::Initialize
    ThreadStruct = new StructType(kernel_Thread,
                                  sizeof(Thread),
                                  ThreadFields);

    for (StructType *next = registered; next != NULL; next = next->next) {
        next->Initialize();
    }
    return S_OK;
}

HRESULT StructType::Initialize()
{
    HRESULT status = S_OK;

    ExtVerb("Initializing: %s [%p]\n", name, fields);
    EXT_CHECK(g_ExtSymbols->GetSymbolTypeId(name, &type, &module));
    EXT_CHECK(g_ExtSymbols->GetTypeSize(module, type, &size));
    ExtVerb("Initializing: %s [size=%d]\n", name, size);
    if (temp != NULL) {
        delete[] temp;
    }
    temp = new BYTE [size];
    ZeroMemory(temp, size);

    fieldCount = 0;
    for (FieldType *field = fields; field->name != NULL; field++) {
        CHAR fieldName[512];

        EXT_CHECK(StringCbPrintf(fieldName, sizeof(fieldName), "%s.%s", name, field->name));
        status = g_ExtSymbols->GetSymbolTypeId(fieldName, &field->type, &field->module);
        if (status == S_OK) {
            EXT_CHECK(g_ExtSymbols->GetTypeSize(field->module, field->type, &field->size));
            EXT_CHECK(g_ExtSymbols->GetFieldOffset(module, type, field->name, &field->offset));
            ExtVerb("Initializing: %s [offset=%d,size=%d]\n", fieldName, field->offset, field->size);
        }
        else {
            ExtErr("Can't find: %s\n", fieldName);
            field->size = 0;
            field->offset = 0;
        }

        field->parent = this;
        fieldCount++;
    }

  Exit:
    ExtVerb("** Exited with %08x\n", status);
    return status;
}

HRESULT StructType::RemoteOffsetFromLocal(ULONG localOffset, ULONG *remoteOffset)
{
    for (ULONG f = 0; f < fieldCount; f++) {
        FieldType *field = &fields[f];

        if (field->localOffset == localOffset) {
            if (remoteOffset != NULL) {
                *remoteOffset = field->offset;
                return S_OK;
            }
            return S_FALSE;
        }
    }
    return E_FAIL;
}

HRESULT StructType::RawAccess(ULONG remoteOffset, PVOID *raw)
{
    *raw = temp + remoteOffset;
    return S_OK;
}

HRESULT StructType::Read(ULONG64 address, PVOID local)
{
    HRESULT status = S_OK;

    ZeroMemory(temp, size);
    ZeroMemory(local, localSize);

    EXT_CHECK(TraceRead(address, temp, size));

    for (ULONG f = 0; f < fieldCount; f++) {
        FieldType *field = &fields[f];

        PBYTE pbLocal = ((PBYTE)local) + field->localOffset;
        PBYTE pbTemp = &temp[field->offset];

        switch (field->size) {
          case 1:
            *(ULONG64 *)pbLocal = *(BYTE *)pbTemp;
            break;
          case 2:
            *(ULONG64 *)pbLocal = *(USHORT *)pbTemp;
            break;
          case 4:
            *(ULONG64 *)pbLocal = *(ULONG *)pbTemp;
            break;
          case 8:
            *(ULONG64 *)pbLocal = *(ULONG64 *)pbTemp;
            break;
          default:
#if 0
              // We allow bigger sizes, for raw access only.
            ExtOut("Unknown size: %d, in field %s\n", field->size, field->name);
#endif
            break;
        }
    }

  Exit:
    return status;
}

HRESULT StructType::Clear()
{
    HRESULT status = S_OK;

    ZeroMemory(temp, size);

    return status;
}

HRESULT StructType::Update(PVOID local)
{
    HRESULT status = S_OK;

    for (ULONG f = 0; f < fieldCount; f++) {
        FieldType *field = &fields[f];

        PBYTE pbLocal = ((PBYTE)local) + field->localOffset;
        PBYTE pbTemp = &temp[field->offset];

        switch (field->size) {
          case 1:
            *(BYTE *)pbTemp = (BYTE)*(ULONG64 *)pbLocal;
            break;
          case 2:
            *(USHORT *)pbTemp = (USHORT)*(ULONG64 *)pbLocal;
            break;
          case 4:
            *(ULONG *)pbTemp = (ULONG)*(ULONG64 *)pbLocal;
            break;
          case 8:
            *(ULONG64 *)pbTemp = *(ULONG64 *)pbLocal;
            break;
        }
    }

    return status;
}

HRESULT StructType::Flush(ULONG64 address)
{
    HRESULT status = S_OK;

    EXT_CHECK(g_ExtData->WriteVirtual(address, temp, size, NULL));

  Exit:
    return status;
}

/////////////////////////////////////////////////////////// KnownStructOutput.
//
const char* KnownStructs[] = {"String",
                              "Thread",
                              "System_String",
                              "System_Threading_Thread",
#if 0
                              "ProcessorContext",
                              "Microsoft_Singularity_X86_ProcessorContext"
#endif
};

HRESULT OnGetKnownStructNames(PDEBUG_CLIENT client,
                              PSTR buffer,
                              PULONG bufferSize)
{
    EXT_ENTER();    // Defines: HRESULT status = S_OK;

    //
    // Return names of known structs in multi string
    //
    ULONG sizeRemaining = *bufferSize, SizeNeeded = 0, Length;
    PCHAR copyAt = buffer;

    for (ULONG i = 0; i < arrayof(KnownStructs); i++) {
        if (sizeRemaining > (Length = (ULONG)strlen(KnownStructs[i]) + 1) &&
            status == S_OK) {
            status = StringCbCopy(copyAt, sizeRemaining, KnownStructs[i]);

            sizeRemaining -= Length;
            copyAt += Length;
        }
        else {
            status = S_FALSE;
        }
        SizeNeeded += Length;
    }
    // Terminate multistring and return size copied
    *copyAt = 0;
    *bufferSize = SizeNeeded+1;

    EXT_LEAVE();    // Macro includes: return status;
}

HRESULT OnGetSingleLineOutput(PDEBUG_CLIENT client,
                              ULONG64 address,
                              PSTR structName,
                              PSTR buffer,
                              PULONG bufferSize)
{
    EXT_ENTER();    // Defines: HRESULT status = S_OK;

    ExtVerb("OnGetSingleLineOutput [%s]\n", structName);

    if (strcmp(structName, "System_String") == 0 ||
        strcmp(structName, "String") == 0) {
        String str;
        ExtVerb("OnGetSingleLineOutput [%s] is string\n", structName);
        EXT_CHECK(StringStruct.Read(address, &str));
        ExtVerb("OnGetSingleLineOutput [%s] is still string\n", structName);

        WCHAR data[256];
        ULONG len;

        len = (ULONG)str.m_stringLength;
        if (len > arrayof(data)) {
            len = arrayof(data);
        }
        if (len > *bufferSize - 20) {
            len = *bufferSize - 20;
        }

        if (len > 0) {
            EXT_CHECK(TraceRead(address + StringFields[0].offset,
                                data,
                                sizeof(data[0]) * len));
            ExtVerb("OnGetSingleLineOutput [%s] read %d of %d chars to %p\n",
                    structName, len, *bufferSize, buffer);
        }

        status = StringCbPrintf(buffer, *bufferSize, " { [%d] \"%.*ls\" }",
                                (ULONG)str.m_stringLength, len, data);
        ExtVerb("OnGetSingleLineOutput [%s] status = %x\n",
                structName, status);
    }
    else if (!strcmp(structName, "System_Threading_Thread") ||
             !strcmp(structName, "Thread")) {
        Thread thread;
        EXT_CHECK(ThreadStruct->Read(address, &thread));

        status = StringCbPrintf(buffer, *bufferSize, " { eip=%I64x ebp=%I64x esp=%I64x }",
                                thread.context.threadRecord.spill.ip,
                                thread.context.threadRecord.spill.bp,
                                thread.context.threadRecord.spill.sp);
    }
#if 0
    else if (!strcmp(structName, "Microsoft_Singularity_ProcessorContext") ||
             !strcmp(structName, "ProcessorContext")) {
        ProcessorContext pc;
        EXT_CHECK(ProcessorContext.Read(address, &pc));

        status = StringCbPrintf(buffer, *bufferSize, " { eip=%I64x ebp=%I64x esp=%I64x }",
                                thread.context.threadRecord.spill.ip,
                                thread.context.threadRecord.spill.bp,
                                thread.context.threadRecord.spill.sp);
    }
#endif
    else {
        status = E_INVALIDARG;
    }

    EXT_LEAVE();    // Macro includes: return status;
}

HRESULT OnGetSuppressTypeName(PDEBUG_CLIENT client, PSTR structName)
{
    UNREFERENCED_PARAMETER(structName);

    EXT_ENTER();    // Defines: HRESULT status = S_OK;
    EXT_LEAVE();    // Macro includes: return status;
}
