#pragma warning(disable:4996)
// TODO: Remove for 64-bit
#pragma warning(disable:4244)
#pragma warning(disable:4267)

#define NTDDI_VERSION 0x06020000

#include "WinDebugger.h"
#include "CPU.h"
#include "DbgModule.h"
#include "DebugVisualizers.h"
#include "MiniDumpDebugger.h"
#include "X86.h"
#include "BeefySysLib/Common.h"
#include "BeefySysLib/util/PerfTimer.h"
#include "BeefySysLib/util/BeefPerf.h"
#include "BeefySysLib/util/CritSect.h"
#include "BeefySysLib/util/UTF8.h"
#include "BeefySysLib/FileStream.h"
#include "BeefySysLib/FileHandleStream.h"
#include "BeefySysLib/util/FileEnumerator.h"
#include <inttypes.h>
#include <windows.h>
#include "DbgExprEvaluator.h"
#include "Compiler/BfSystem.h"
#include "Compiler/BfParser.h"
#include "Compiler/BfReducer.h"
#include "Compiler/BfDemangler.h"
#include "Compiler/BfPrinter.h"
#include <Shlobj.h>
#include "NetManager.h"
#include "DebugManager.h"
#include "X86Target.h"
#include "HotHeap.h"
#include "HotScanner.h"
#include "Profiler.h"
#include <float.h>

#include <psapi.h>

#if !defined BF32 || !defined BF_DBG_64

#define STATUS_WX86_CONTINUE             0x4000001DL
#define STATUS_WX86_SINGLE_STEP          0x4000001EL
#define STATUS_WX86_BREAKPOINT           0x4000001FL
#define STATUS_WX86_EXCEPTION_CONTINUE   0x40000020L

#pragma pack(push, 1)
struct HotJumpOp
{
	uint8 mOpCode;
	int32 mRelTarget;
};
#pragma pack(pop)

#include "BeefySysLib/util/AllocDebug.h"

#include <limits>

USING_NS_BF_DBG;

static void FilterThreadName(String& name)
{
	for (int i = 0; i < (int)name.length(); i++)
	{
		uint8 c = name[i];
		if (c == 0)
		{
			name.RemoveToEnd(i);
			return;
		}

		if (c < 32)
		{
			name.Remove(i);
			i--;
			continue;
		}
	}
}

static bool IsHandleValid(HANDLE handle)
{
	return (handle != NULL) && (handle != INVALID_HANDLE_VALUE);
}

//////////////////////////////////////////////////////////////////////////

WdBreakpointCondition::~WdBreakpointCondition()
{
	delete mDbgEvaluationContext;
}

//////////////////////////////////////////////////////////////////////////

DbgEvaluationContext::DbgEvaluationContext(WinDebugger* winDebugger, DbgModule* dbgModule, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
{
	Init(winDebugger, dbgModule, expr, formatInfo, contextValue);
}

DbgEvaluationContext::DbgEvaluationContext(WinDebugger* winDebugger, DbgCompileUnit* dbgCompileUnit, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
{
	DbgModule* dbgModule = NULL;
	if (dbgCompileUnit != NULL)
		dbgModule = dbgCompileUnit->mDbgModule;
	Init(winDebugger, dbgModule, expr, formatInfo, contextValue);
}

void DbgEvaluationContext::Init(WinDebugger* winDebugger, DbgModule* dbgModule, const StringImpl& expr, DwFormatInfo* formatInfo, DbgTypedValue contextValue)
{
	if (expr.empty())
	{
		mParser = NULL;
		mReducer = NULL;
		mPassInstance = NULL;
		mDbgExprEvaluator = NULL;
		mExprNode = NULL;
		return;
	}

	mParser = new BfParser(winDebugger->mBfSystem);
	mParser->mCompatMode = true;
	mPassInstance = new BfPassInstance(winDebugger->mBfSystem);
	auto terminatedExpr = expr + ";";
	mParser->SetSource(terminatedExpr.c_str(), terminatedExpr.length());
	mParser->Parse(mPassInstance);

	mReducer = new BfReducer();
	mReducer->mAlloc = mParser->mAlloc;
	mReducer->mSystem = winDebugger->mBfSystem;
	mReducer->mPassInstance = mPassInstance;
	mReducer->mVisitorPos = BfReducer::BfVisitorPos(mParser->mRootNode);
	mReducer->mVisitorPos.MoveNext();
	mReducer->mCompatMode = mParser->mCompatMode;
	mReducer->mSource = mParser;
	mExprNode = mReducer->CreateExpression(mParser->mRootNode->GetFirst());
	mParser->Close();
	mDbgExprEvaluator = new DbgExprEvaluator(winDebugger, dbgModule, mPassInstance, -1, -1);

	if ((formatInfo != NULL) && (mExprNode != NULL) && (mExprNode->GetSrcEnd() < (int) expr.length()))
	{
		String formatFlags = expr.Substring(mExprNode->GetSrcEnd());
		String errorString = "Invalid expression";
		if (!winDebugger->ParseFormatInfo(dbgModule, formatFlags, formatInfo, mPassInstance, NULL, NULL, &errorString, contextValue))
		{
			mPassInstance->FailAt(errorString, mParser->mSourceData, mExprNode->GetSrcEnd(), (int)expr.length() - mExprNode->GetSrcEnd());
			formatFlags = "";
		}
	}

	if (formatInfo != NULL)
	{
		mDbgExprEvaluator->mExplicitThis = formatInfo->mExplicitThis;
		mDbgExprEvaluator->mCallStackIdx = formatInfo->mCallStackIdx;
		mDbgExprEvaluator->mLanguage = formatInfo->mLanguage;
	}
}

bool DbgEvaluationContext::HasExpression()
{
	return mExprNode != NULL;
}

DbgEvaluationContext::~DbgEvaluationContext()
{
	delete mParser;
	delete mReducer;
	delete mDbgExprEvaluator;
	delete mPassInstance;
}

DbgTypedValue DbgEvaluationContext::EvaluateInContext(DbgTypedValue contextTypedValue)
{
	if (mExprNode == NULL)
		return DbgTypedValue();
	mPassInstance->ClearErrors();
	if (contextTypedValue)
	{
		mDbgExprEvaluator->mExplicitThis = contextTypedValue;
		if ((mDbgExprEvaluator->mExplicitThis.mType->IsPointer()) && (mDbgExprEvaluator->mExplicitThis.mType->mTypeParam->WantsRefThis()))
		{
			mDbgExprEvaluator->mExplicitThis.mType = mDbgExprEvaluator->mExplicitThis.mType->mTypeParam;
			mDbgExprEvaluator->mExplicitThis.mSrcAddress = mDbgExprEvaluator->mExplicitThis.mPtr;
			mDbgExprEvaluator->mExplicitThis.mPtr = 0;
		}

		if ((mDbgExprEvaluator->mExplicitThis.mType->IsCompositeType()) && (!mDbgExprEvaluator->mExplicitThis.mType->WantsRefThis()))
		{
			if (mDbgExprEvaluator->mExplicitThis.mSrcAddress != 0)
			{
				mDbgExprEvaluator->mExplicitThis.mType = mDbgExprEvaluator->mDbgModule->GetPointerType(mDbgExprEvaluator->mExplicitThis.mType);
				mDbgExprEvaluator->mExplicitThis.mPtr = mDbgExprEvaluator->mExplicitThis.mSrcAddress;
				mDbgExprEvaluator->mExplicitThis.mSrcAddress = 0;
			}
		}
	}
	if (contextTypedValue.mType != NULL)
		mDbgExprEvaluator->mDbgCompileUnit = contextTypedValue.mType->mCompileUnit;
	DbgTypedValue exprResult;
	auto result = mDbgExprEvaluator->Resolve(mExprNode);
	return result;
}

bool DbgEvaluationContext::HadError()
{
	return mPassInstance->mFailedIdx != 0;
}

String DbgEvaluationContext::GetErrorStr()
{
	String errorStr = mPassInstance->mErrors[0]->mError;
	if (mExprNode != NULL)
	{
		errorStr += ": ";
		errorStr += mExprNode->ToString();
	}
	return errorStr;
}

//////////////////////////////////////////////////////////////////////////

typedef HRESULT(WINAPI* SetThreadDescription_t)(HANDLE hThread, PCWSTR lpThreadDescription);
typedef HRESULT(WINAPI* GetThreadDescription_t)(HANDLE hThread, PWSTR* lpThreadDescription);
static SetThreadDescription_t gSetThreadDescription = NULL;
static GetThreadDescription_t gGetThreadDescription = NULL;
static HMODULE gKernelDll = NULL;

static void ImportKernel()
{
	if (gKernelDll != NULL)
		return;

	WCHAR path[MAX_PATH];
	GetSystemDirectory(path, MAX_PATH);
	wcscat(path, L"\\kernel32.dll");
	gKernelDll = GetModuleHandle(path);
	if (gKernelDll == NULL)
	{
		return;
	}

	gSetThreadDescription = (SetThreadDescription_t)GetProcAddress(gKernelDll, "SetThreadDescription");
	gGetThreadDescription = (GetThreadDescription_t)GetProcAddress(gKernelDll, "GetThreadDescription");
}

void WinDebugger::TryGetThreadName(WdThreadInfo* threadInfo)
{
	if (threadInfo->mHThread == NULL)
		return;

	ImportKernel();

	PWSTR wStr = NULL;
	if (gGetThreadDescription != NULL)
	{
		gGetThreadDescription(threadInfo->mHThread, &wStr);
		if (wStr == NULL)
			return;

		threadInfo->mName = UTF8Encode(wStr);
		FilterThreadName(threadInfo->mName);
		LocalFree(wStr);
	}
}

static void CreateFilterName(String& name, DbgType* type)
{
	CreateFilterName(name, type->mParent);

	switch (type->mTypeCode)
	{
	case DbgType_Namespace:
	case DbgType_Struct:
	case DbgType_Class:
		name += type->mName;
		break;
	}
}

static void CreateFilterName(String& name, const char* srcStr, DbgLanguage language)
{
	int chevronDepth = 0;
	const char* cPtr = srcStr;
	for (; true; cPtr++)
	{
		char c = *cPtr;
		if (c == 0)
			break;
		if (c == '>')
			chevronDepth--;
		bool inGeneric = chevronDepth > 0;
		if (c == '<')
			chevronDepth++;
		if (inGeneric) // Bundle all generic instances together
			continue;
		if (c == '[') // Bundle all arrays together
			name.clear();
		if (c == '(')
			return; // Start of params
		if ((c == ':') && (cPtr[1] == ':') && (language == DbgLanguage_Beef))
		{
			name.Append('.');
			cPtr++;
		}
		else
			name.Append(c);
	}
}

static void CreateFilterName(String& name, DbgSubprogram* subprogram)
{
	auto language = subprogram->GetLanguage();
	if (subprogram->mName == NULL)
	{
		if (subprogram->mLinkName[0] == '<')
		{
			name += subprogram->mLinkName;
			return;
		}
		name = BfDemangler::Demangle(subprogram->mLinkName, language);
		// Strip off the params since we need to generate those ourselves
		int parenPos = (int)name.IndexOf('(');
		if (parenPos != -1)
			name.RemoveToEnd(parenPos);
		return;
	}
	else if (subprogram->mHasQualifiedName)
	{
		const char* cPtr = subprogram->mName;
		if (strncmp(cPtr, "_bf::", 5) == 0)
		{
			CreateFilterName(name, cPtr + 5, DbgLanguage_Beef);
			name.Replace(".__BfStaticCtor", ".this$static");
			name.Replace(".__BfCtorClear", ".this$clear");
			name.Replace(".__BfCtor", ".this");
		}
		else
			CreateFilterName(name, subprogram->mName, language);

		return;
	}
	else
	{
		if (subprogram->mParentType != NULL)
		{
			String parentName = subprogram->mParentType->ToString();
			CreateFilterName(name, parentName.c_str(), language);
			if (!name.empty())
			{
				if (language == DbgLanguage_Beef)
					name += ".";
				else
					name += "::";
			}
		}

		if ((language == DbgLanguage_Beef) && (subprogram->mParentType != NULL) && (subprogram->mParentType->mTypeName != NULL) &&
			(strcmp(subprogram->mName, subprogram->mParentType->mTypeName) == 0))
			name += "this";
		else if ((language == DbgLanguage_Beef) && (subprogram->mName[0] == '~'))
			name += "~this";
		else if (strncmp(subprogram->mName, "_bf::", 5) == 0)
		{
			CreateFilterName(name, subprogram->mName + 5, DbgLanguage_Beef);
		}
		else
		{
			CreateFilterName(name, subprogram->mName, language);
		}
	}

	if (name.empty())
		name += "`anon";
	if ((name[name.length() - 1] == '!') || (name[0] == '<'))
	{
		if (language == DbgLanguage_Beef)
		{
			// It's a mixin - assert that there's no params
			//BF_ASSERT(subprogram->mParams.Size() == 0);
		}
		return;
	}
}

//////////////////////////////////////////////////////////////////////////

DbgPendingExpr::DbgPendingExpr()
{
	mThreadId = -1;
	mCallStackIdx = -1;
	mParser = NULL;
	mCursorPos = -1;
	mExprNode = NULL;
	mIdleTicks = 0;
	mExplitType = NULL;
	mExpressionFlags = DwEvalExpressionFlag_None;
	mUsedSpecifiedLock = false;
	mStackIdxOverride = -1;
}

DbgPendingExpr::~DbgPendingExpr()
{
	delete mParser;
}

// conversion logic based on table at http://en.wikipedia.org/wiki/Extended_precision
//CDH TODO put this somewhere more general
static double ConvertFloat80ToDouble(const byte fp80[10])
{
	uint16 e = *((uint16*)&fp80[8]);
	uint64 m = *((uint64*)&fp80[0]);
	uint64 bit63 = (uint64)1 << 63;
	uint64 bit62 = (uint64)1 << 62;

	bool isNegative = (e & 0x8000) != 0;
	double s = isNegative ? -1.0 : 1.0;
	e &= 0x7fff;

	if (!e)
	{
		// the high bit and mantissa content will determine whether it's an actual zero, or a denormal or
		// pseudo-denormal number with an effective exponent of -16382.  But since that exponent is so far
		// below anything we can handle in double-precision (even accounting for denormal bit shifts), we're
		// effectively still dealing with zero.
		return s * 0.0;
	}
	else if (e == 0x7fff)
	{
		if (m & bit63)
		{
			if (m & bit62)
			{
				return std::numeric_limits<double>::quiet_NaN();
			}
			else
			{
				if (m == bit63)
					return s * std::numeric_limits<double>::infinity();
				else
					return std::numeric_limits<double>::signaling_NaN();
			}
		}
		else
		{
			return std::numeric_limits<double>::quiet_NaN();
		}
	}
	else
	{
		if (!(m & bit63))
			return std::numeric_limits<double>::quiet_NaN(); // unnormal (we don't handle these since 80387 and later treat them as invalid operands anyway)

		// else is a normalized value
	}

	int useExponent = (int)e - 16383;
	if (useExponent < -1022)
		return s * 0.0; // we could technically support e from -1023 to -1074 as denormals, but don't bother with that for now.
	else if (useExponent > 1023)
		return s * HUGE_VAL;

	useExponent += 1023;

	BF_ASSERT((useExponent > 0) && (useExponent < 0x7ff)); // assume we've filtered for valid exponent range
	BF_ASSERT(m & bit63); // assume we've filtered out values that aren't normalized by now

	uint64 result = 0;
	if (isNegative)
		result |= bit63;
	result |= (uint64)useExponent << 52;
	result |= (m & ~bit63) >> 11;

	return *reinterpret_cast<double*>(&result);
}

addr_target NS_BF_DBG::DecodeTargetDataPtr(const char*& strRef)
{
	addr_target val = (addr_target)stouln(strRef, sizeof(intptr_target) * 2);
	strRef += sizeof(intptr_target) * 2;
	return val;
}

WinDebugger::WinDebugger(DebugManager* debugManager) : mDbgSymSrv(this)
{
	ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));

	mActiveHotIdx = -1;
	mGotStartupEvent = false;
	mIsContinuingFromException = false;
	mDestroying = false;
	mDebugManager = debugManager;
	mNeedsRehupBreakpoints = false;
	mStepInAssembly = false;
	mStepSP = 0;
	mStepIsRecursing = false;
	mStepStopOnNextInstruction = false;
	mDebugTarget = NULL;
	mShuttingDown = false;
	mBfSystem = new BfSystem();
	mAtBreakThread = NULL;
	mActiveThread = NULL;
	mActiveBreakpoint = NULL;
	mSteppingThread = NULL;
	mExplicitStopThread = NULL;
	mStepSwitchedThreads = false;
	mIsDebuggerWaiting = false;
	mWantsDebugContinue = false;
	mContinueFromBreakpointFailed = false;
	mIsStepIntoSpecific = false;
	mDbgBreak = false;
	mDebuggerWaitingThread = NULL;
	mStepType = StepType_None;
	mOrigStepType = StepType_None;
	mLastValidStepIntoPC = 0;
	mActiveSymSrvRequest = NULL;
	mStdInputPipe = INVALID_HANDLE_VALUE;
	mStdOutputPipe = INVALID_HANDLE_VALUE;
	mStdErrorPipe = INVALID_HANDLE_VALUE;

	mStoredReturnValueAddr = 0;
#ifdef BF_DBG_32
	mCPU = gX86Target->mX86CPU;
#else
	mCPU = gX86Target->mX64CPU;
#endif
	mRunState = RunState_NotStarted;
	mIsRunning = false;
	mSavedAtBreakpointAddress = 0;
	mSavedBreakpointAddressContinuing = 0;
	mRequestedStackFrameIdx = 0;
	mShowPCOverride = 0;
	mCurNoInfoStepTries = 0;
	mDbgAttachFlags = BfDbgAttachFlag_None;
	mDbgProcessHandle = 0;
	mDbgThreadHandle = 0;
	mDbgProcessId = 0;
	mDbgHeapData = NULL;
	mIsPartialCallStack = true;
	mHotSwapEnabled = false;
	mOpenFileFlags = DbgOpenFileFlag_None;

	for (int i = 0; i < 4; i++)
	{
		mFreeMemoryBreakIndices.push_back(i);
	}
	mMemoryBreakpointVersion = 0;

	SYSTEM_INFO systemInfo;
	GetSystemInfo(&systemInfo);
	mPageSize = systemInfo.dwPageSize;

	mEmptyDebugTarget = new DebugTarget(this);
	mEmptyDebugTarget->CreateEmptyTarget();
	mEmptyDebugTarget->mIsEmpty = true;
	mDebugTarget = mEmptyDebugTarget;
	mDebugPendingExpr = NULL;
	mDebugEvalThreadInfo = WdThreadInfo();

	mMemCacheAddr = 0;
	mDebuggerThreadId = 0;
}

WinDebugger::~WinDebugger()
{
	mDestroying = true;

	delete gDbgPerfManager;
	gDbgPerfManager = NULL;

	if ((mDebugTarget != NULL) && (mDebugTarget != mEmptyDebugTarget))
		Detach();

	for (auto breakpoint : mBreakpoints)
	{
		auto checkBreakpoint = breakpoint->mLinkedSibling;
		while (checkBreakpoint != NULL)
		{
			auto nextBreakpoint = checkBreakpoint->mLinkedSibling;
			delete checkBreakpoint;
			checkBreakpoint = nextBreakpoint;
		}

		delete breakpoint;
	}

	delete mEmptyDebugTarget;

	delete mBfSystem;
	for (auto kv : mPendingProfilerMap)
		delete kv.mValue;
	for (auto profiler : mNewProfilerList)
		delete profiler;
	delete mDebugPendingExpr;
}

void WinDebugger::Fail(const StringImpl& error)
{
	if (mIsRunning)
		mDebugManager->mOutMessages.push_back(StrFormat("error %s", error.c_str()));
}

// Leave active thread unpaused
void WinDebugger::ThreadRestorePause(WdThreadInfo* onlyPauseThread, WdThreadInfo* dontPauseThread)
{
	BfLogDbg("ThreadRestorePause %d %d\n", (onlyPauseThread != NULL) ? onlyPauseThread->mThreadId : 0, (dontPauseThread != NULL) ? dontPauseThread->mThreadId : 0);
	for (auto threadInfo : mThreadList)
	{
		if (((threadInfo != dontPauseThread) && (!threadInfo->mIsBreakRestorePaused)) &&
			((onlyPauseThread == NULL) || (threadInfo == onlyPauseThread)))
		{
			BF_ASSERT(!threadInfo->mIsBreakRestorePaused);

			BfLogDbg("SuspendThread %d\n", threadInfo->mThreadId);
			::SuspendThread(threadInfo->mHThread);

			threadInfo->mIsBreakRestorePaused = true;
		}
	}
}

void WinDebugger::ThreadRestoreUnpause()
{
	BfLogDbg("ThreadRestoreUnpause\n");
	for (auto threadInfo : mThreadList)
	{
		if (threadInfo->mIsBreakRestorePaused)
		{
			BfLogDbg("ResumeThread %d\n", threadInfo->mThreadId);
			::ResumeThread(threadInfo->mHThread);
			threadInfo->mIsBreakRestorePaused = false;
		}
	}
}

void WinDebugger::UpdateThreadDebugRegisters(WdThreadInfo* threadInfo)
{
	if (threadInfo->mMemoryBreakpointVersion == mMemoryBreakpointVersion)
		return;

	auto threadId = threadInfo->mHThread;

	BF_CONTEXT lcContext;
	lcContext.ContextFlags = BF_CONTEXT_DEBUG_REGISTERS;
	BF_GetThreadContext(threadId, &lcContext);
	for (int memoryBreakIdx = 0; memoryBreakIdx < 4; memoryBreakIdx++)
	{
		WdMemoryBreakpointBind memoryBreakpointBind = mMemoryBreakpoints[memoryBreakIdx];

		WdBreakpoint* wdBreakpoint = memoryBreakpointBind.mBreakpoint;
		if (wdBreakpoint == NULL)
		{
			*(&lcContext.Dr0 + memoryBreakIdx) = 0;
			lcContext.Dr7 &= ~((1 << (memoryBreakIdx * 2)) | (1 << (16 + memoryBreakIdx * 4)) | (3 << (18 + memoryBreakIdx * 4)));
		}
		else
		{
			int sizeCode = 0;
			if (memoryBreakpointBind.mByteCount == 2)
				sizeCode = 1;
			else if (memoryBreakpointBind.mByteCount == 4)
				sizeCode = 3;
			else if (memoryBreakpointBind.mByteCount == 8)
				sizeCode = 2;

			addr_target calcAddr = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress + memoryBreakpointBind.mOfs;
			BF_ASSERT(calcAddr == memoryBreakpointBind.mAddress);

			*(&lcContext.Dr0 + memoryBreakIdx) = calcAddr;
			lcContext.Dr7 |= (1 << (memoryBreakIdx * 2)) | (1 << (16 + memoryBreakIdx * 4)) | (sizeCode << (18 + memoryBreakIdx * 4));
		}
	}
	bool worked = BF_SetThreadContext(threadId, &lcContext) != 0;
	BF_ASSERT(worked || (mRunState == RunState_Terminating) || (mRunState == RunState_Terminated));
	threadInfo->mMemoryBreakpointVersion = mMemoryBreakpointVersion;
}

void WinDebugger::UpdateThreadDebugRegisters()
{
	for (auto threadInfo : mThreadList)
	{
		::SuspendThread(threadInfo->mHThread);
		UpdateThreadDebugRegisters(threadInfo);
		::ResumeThread(threadInfo->mHThread);
	}
}

void WinDebugger::PhysSetBreakpoint(addr_target address)
{
	BfLogDbg("PhysSetBreakpoint %p\n", address);

	uint8 newData = 0xCC;

	// This ensure that we have the orig image data cached
	DbgMemoryFlags flags = mDebugTarget->ReadOrigImageData(address, NULL, 1);
	if ((flags & DbgMemoryFlags_Execute) == 0)
	{
		BfLogDbg("Breakpoint ignored - execute flag NOT set in breakpoint address\n", address);

		BfLogDbg("Memory Flags = %d\n", gDebugger->GetMemoryFlags(address));

		return;
	}

	// Replace it with Breakpoint
	SIZE_T dwReadBytes;
	BOOL worked = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, &newData, 1, &dwReadBytes);
	if (!worked)
	{
		int err = GetLastError();
		BfLogDbg("SetBreakpoint FAILED %p\n", address);
	}
	FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, 1);

	{
		uint8 mem = ReadMemory<uint8>(address);
		BfLogDbg("Breakpoint byte %X\n", mem);
	}
}

void WinDebugger::SetBreakpoint(addr_target address, bool fromRehup)
{
	int* countPtr = NULL;
	if (mPhysBreakpointAddrMap.TryAdd(address, NULL, &countPtr))
	{
		BfLogDbg("SetBreakpoint %p\n", address);
		*countPtr = 1;
	}
	else
	{
		if (fromRehup)
		{
			BfLogDbg("SetBreakpoint %p Count: %d. Rehup (ignored).\n", address, *countPtr);
			return;
		}

		(*countPtr)++;
		BfLogDbg("SetBreakpoint %p Count: %d\n", address, *countPtr);
		return;
	}

	PhysSetBreakpoint(address);
}

void WinDebugger::SetTempBreakpoint(addr_target address)
{
	BfLogDbg("SetTempBreakpoint %p\n", address);
	mTempBreakpoint.push_back(address);
	SetBreakpoint(address);
}

void WinDebugger::PhysRemoveBreakpoint(addr_target address)
{
	BfLogDbg("PhysRemoveBreakpoint %p\n", address);

	uint8 origData;
	DbgMemoryFlags flags = mDebugTarget->ReadOrigImageData(address, &origData, 1);
	if ((flags & DbgMemoryFlags_Execute) == 0)
	{
		//BF_ASSERT("Failed" == 0);
		return;
	}

	SIZE_T dwReadBytes;
	if (!WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, &origData, 1, &dwReadBytes))
	{
		int err = GetLastError();
		BfLogDbg("RemoveBreakpoint FAILED %p\n", address);
	}
	FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, 1);
}

void WinDebugger::RemoveBreakpoint(addr_target address)
{
	int* countPtr = NULL;
	mPhysBreakpointAddrMap.TryGetValue(address, &countPtr);

	// This can happen when we shutdown and we're continuing from a breakpoint
	//BF_ASSERT(*countPtr != NULL);
	if (countPtr == NULL)
	{
		BfLogDbg("RemoveBreakpoint %p FAILED\n", address);
		return;
	}

	BfLogDbg("RemoveBreakpoint %p count: %d\n", address, *countPtr);
	if (*countPtr > 1)
	{
		(*countPtr)--;
		return;
	}
	mPhysBreakpointAddrMap.Remove(address);
	PhysRemoveBreakpoint(address);
}

void WinDebugger::SingleStepX86()
{
	// In what cases did this catch bugs?
	// This caused other failures (caught in tests)
// 	if (mActiveThread->mIsAtBreakpointAddress != 0)
// 	{
// 		ContinueFromBreakpoint();
// 		return;
// 	}

	BfLogDbg("Setup SingleStepX86 ActiveThread: %d\n", (mActiveThread != NULL) ? mActiveThread->mThreadId : -1);

	BF_CONTEXT lcContext;
	lcContext.ContextFlags = BF_CONTEXT_ALL;

	BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
	lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
	BF_SetThreadContext(mActiveThread->mHThread, &lcContext);
}

bool WinDebugger::IsInRunState()
{
	return (mRunState == RunState_Running) || (mRunState == RunState_Running_ToTempBreakpoint);
}

bool WinDebugger::ContinueFromBreakpoint()
{
	if (mDebuggerWaitingThread->mFrozen)
	{
		BfLogDbg("ContinueFromBreakpoint bailout on frozen thread\n");
		mDebuggerWaitingThread->mStoppedAtAddress = 0;
		mDebuggerWaitingThread->mIsAtBreakpointAddress = 0;
		return true;
	}

	mActiveThread = mDebuggerWaitingThread;
	mActiveBreakpoint = NULL;

	BfLogDbg("ContinueFromBreakpoint. ActiveThread: %d\n", (mActiveThread != NULL) ? mActiveThread->mThreadId : -1);

	BfLogDbg("ResumeThread %d\n", mActiveThread->mThreadId);
	BOOL success = ::ResumeThread(mActiveThread->mHThread);
	if (success)
	{
		// It's possible the active thread is suspended - possibly by the GC, so we would deadlock if we
		//  attempted to pause the other threads
		BfLogDbg("SuspendThread %d\n", mActiveThread->mThreadId);
		BfLogDbg("Thread already paused!\n");
		::SuspendThread(mActiveThread->mHThread);
		return false;
	}

	ThreadRestorePause(NULL, mActiveThread);

	PhysRemoveBreakpoint(mActiveThread->mIsAtBreakpointAddress);

	BF_CONTEXT lcContext;
	lcContext.ContextFlags = BF_CONTEXT_ALL;

	BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
	lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
	BF_SetThreadContext(mActiveThread->mHThread, &lcContext);

	mActiveThread->mStoppedAtAddress = 0;
	mActiveThread->mBreakpointAddressContinuing = mActiveThread->mIsAtBreakpointAddress;
	mActiveThread->mIsAtBreakpointAddress = 0;
	BfLogDbg("ContinueFromBreakpoint set mIsAtBreakpointAddress = 0\n");
	return true;
}

void WinDebugger::ValidateBreakpoints()
{
	HashSet<addr_target> usedBreakpoints;

	std::function<void(WdBreakpoint*)> _AddBreakpoint = [&](WdBreakpoint* breakpoint)
	{
		if (breakpoint->mAddr != 0)
		{
			usedBreakpoints.Add(breakpoint->mAddr);

			WdBreakpoint* foundBreakpoint = NULL;
			auto itr = mBreakpointAddrMap.Find(breakpoint->mAddr);
			bool found = false;
			while (itr != mBreakpointAddrMap.end())
			{
				WdBreakpoint* foundBreakpoint = itr->mValue;
				found |= foundBreakpoint == breakpoint;
				itr.NextWithSameKey(breakpoint->mAddr);
			}

			BF_ASSERT(found);
		}

		auto checkSibling = (WdBreakpoint*)breakpoint->mLinkedSibling;
		while (checkSibling != NULL)
		{
			_AddBreakpoint(checkSibling);
			checkSibling = (WdBreakpoint*)checkSibling->mLinkedSibling;
		}
	};

	for (auto breakpoint : mBreakpoints)
		_AddBreakpoint(breakpoint);

	for (auto& entry : mBreakpointAddrMap)
	{
		BF_ASSERT(usedBreakpoints.Contains(entry.mKey));
	}
}

Breakpoint* WinDebugger::FindBreakpointAt(intptr address)
{
#ifdef _DEBUG
	//ValidateBreakpoints();
#endif

	WdBreakpoint* breakpoint = NULL;
	mBreakpointAddrMap.TryGetValue(address, &breakpoint);
	return breakpoint;
}

Breakpoint* WinDebugger::GetActiveBreakpoint()
{
	if ((mActiveBreakpoint != NULL) && (mActiveBreakpoint->mHead != NULL))
		return mActiveBreakpoint->mHead;
	return mActiveBreakpoint;
}

void WinDebugger::DebugThreadProc()
{
	BpSetThreadName("DebugThread");
	BfpThread_SetName(NULL, "DebugThread", NULL);

	mDebuggerThreadId = GetCurrentThreadId();

	if (!IsMiniDumpDebugger())
	{
		if (!DoOpenFile(mLaunchPath, mArgs, mWorkingDir, mEnvBlock, mOpenFileFlags))
		{
			if (mDbgProcessId != 0)
				OutputRawMessage("error Unable to attach to process");
			else
				OutputRawMessage(StrFormat("error Failed to launch: %s", mLaunchPath.c_str()));
			mShuttingDown = true;
			mRunState = RunState_Terminated;
		}
	}

	while (!mShuttingDown)
	{
		DoUpdate();
	}

	mIsRunning = false;

	for (int i = 0; i < (int) mBreakpoints.size(); i++)
	{
		WdBreakpoint* wdBreakpoint = mBreakpoints[i];

		if (wdBreakpoint->mAddr != 0)
			mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);

		wdBreakpoint->mAddr = 0;
		wdBreakpoint->mLineData = DbgLineDataEx();
		wdBreakpoint->mSrcFile = NULL;
		if (wdBreakpoint->mLinkedSibling != NULL)
		{
			DeleteBreakpoint(wdBreakpoint->mLinkedSibling);
			wdBreakpoint->mLinkedSibling = NULL;
		}
	}

	if (!IsMiniDumpDebugger())
	{
		while (true)
		{
			if (!mIsDebuggerWaiting)
			{
				if (!WaitForDebugEvent(&mDebugEvent, 0))
					break;
			}
			if (mDebuggerWaitingThread != NULL)
			{
				BF_ASSERT_REL((mDebuggerWaitingThread->mIsAtBreakpointAddress == 0) || (mShuttingDown));
				::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_CONTINUE);
				BfLogDbg("::ContinueDebugEvent startup ThreadId:%d\n", mDebuggerWaitingThread->mThreadId);
			}
			mIsDebuggerWaiting = false;
			mDebuggerWaitingThread = NULL;
		}
	}

	mDebuggerThreadId = 0;
}

static void DebugThreadProcThunk(void* winDebugger)
{
	((WinDebugger*) winDebugger)->DebugThreadProc();
}

int WinDebugger::GetAddrSize()
{
	return sizeof(addr_target);
}

bool WinDebugger::CanOpen(const StringImpl& fileName, DebuggerResult* outResult)
{
	FILE* fp = fopen(fileName.c_str(), "rb");
	if (fp == NULL)
	{
		*outResult = DebuggerResult_CannotOpen;
		return false;
	}

	FileStream fs;
	fs.mFP = fp;

	*outResult = DebuggerResult_Ok;
	bool canRead = DbgModule::CanRead(&fs, outResult);
	fclose(fp);
	return canRead;
}

void WinDebugger::OpenFile(const StringImpl& launchPath, const StringImpl& targetPath, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, bool hotSwapEnabled, DbgOpenFileFlags openFileFlags)
{
	BF_ASSERT(!mIsRunning);
	mLaunchPath = launchPath;
	mTargetPath = targetPath;
	mArgs = args;
	mWorkingDir = workingDir;
	mEnvBlock = envBlock;
	mHotSwapEnabled = hotSwapEnabled;
	mOpenFileFlags = openFileFlags;
	mDebugTarget = new DebugTarget(this);
}

bool WinDebugger::Attach(int processId, BfDbgAttachFlags attachFlags)
{
	BF_ASSERT(!mIsRunning);

	mDbgAttachFlags = attachFlags;
	mDbgProcessHandle = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, (DWORD)processId);
	if (mDbgProcessHandle == 0)
		return false;
	BOOL is32Bit = false;
	if (!IsWow64Process(mDbgProcessHandle, &is32Bit))
	{
		mDbgProcessHandle = 0;
		::CloseHandle(mDbgProcessHandle);
		return false;
	}

	bool want32Bit = sizeof(intptr_target) == 4;
	if (want32Bit != (is32Bit != 0))
	{
		mDbgProcessHandle = 0;
		::CloseHandle(mDbgProcessHandle);
		return false;
	}

	HMODULE mainModule = 0;
	DWORD memNeeded = 0;
	::EnumProcessModules(mDbgProcessHandle, &mainModule, sizeof(HMODULE), &memNeeded);

	WCHAR fileName[MAX_PATH] = {0};
	GetModuleFileNameExW(mDbgProcessHandle, mainModule, fileName, MAX_PATH);
	mLaunchPath = UTF8Encode(fileName);
	mTargetPath = mLaunchPath;

	mDbgProcessId = processId;
	mDbgProcessHandle = 0;
	::CloseHandle(mDbgProcessHandle);

	mDebugTarget = new DebugTarget(this);

	return true;
}

void WinDebugger::GetStdHandles(BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((outStdIn != NULL) && (IsHandleValid(mStdInputPipe)))
	{
		*outStdIn = BfpFile_GetFromHandle((intptr)mStdInputPipe, NULL);
		mStdInputPipe = 0;
	}

	if ((outStdOut != NULL) && (IsHandleValid(mStdOutputPipe)))
	{
		*outStdOut = BfpFile_GetFromHandle((intptr)mStdOutputPipe, NULL);
		mStdOutputPipe = 0;
	}

	if ((outStdErr != NULL) && (IsHandleValid(mStdErrorPipe)))
	{
		*outStdErr = BfpFile_GetFromHandle((intptr)mStdErrorPipe, NULL);
		mStdErrorPipe = 0;
	}
}

void WinDebugger::Run()
{
	mIsRunning = true;
	DWORD localThreadId;
	HANDLE hThread = ::CreateThread(NULL, 64 * 1024, (LPTHREAD_START_ROUTINE) &DebugThreadProcThunk, (void*)this, 0, &localThreadId);
	CloseHandle(hThread);
}

bool WinDebugger::HasLoadedTargetBinary()
{
	if (mDebugTarget == NULL)
		return false;
	return mDebugTarget->mTargetBinary != NULL;
}

void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (mDebugTarget->mTargetBinary == NULL)
	{
		Fail("Hot swapping failed because the hot target binary has not yet been loaded.");
		return;
	}

	if (mDebugTarget->mHotHeap == NULL)
	{
		Fail("There is no hot heap space available for hot swapping.");
		return;
	}

	BfLogDbg("WinDebugger::HotLoad Start %d\n", hotIdx);

	SetAndRestoreValue<int> prevHotIdx(mActiveHotIdx, hotIdx);

	BF_ASSERT(mHotThreadStates.empty());
	mHotThreadStates.Resize(mThreadList.size());
	for (int threadIdx = 0; threadIdx < (int)mThreadList.size(); threadIdx++)
	{
		WdThreadInfo* threadInfo = mThreadList[threadIdx];
		SetAndRestoreValue<WdThreadInfo*> prevActiveThread(mActiveThread, threadInfo);
		BfLogDbg("SuspendThread %d\n", threadInfo->mThreadId);
		::SuspendThread(threadInfo->mHThread);
		mHotThreadStates[threadIdx].mThreadId = threadInfo->mThreadId;
		PopulateRegisters(&mHotThreadStates[threadIdx].mRegisters);
	}

	for (auto address : mTempBreakpoint)
		RemoveBreakpoint(address);
	mTempBreakpoint.Clear();
	mStepBreakpointAddrs.Clear();
	for (auto breakpoint : mBreakpoints)
	{
		DetachBreakpoint(breakpoint);
	}

	int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();

	bool hasHotVData = false;

	bool failed = false;
	for (auto fileName : objectFiles)
	{
		if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
			hasHotVData = true;

		BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
		DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
		if ((newBinary != NULL) && (newBinary->mFailed))
			failed = true;
	}

	for (int moduleIdx = startingModuleIdx; moduleIdx < (int)mDebugTarget->mDbgModules.size(); moduleIdx++)
	{
		auto dbgModule = mDebugTarget->mDbgModules[moduleIdx];
		BF_ASSERT(dbgModule->IsObjectFile());
		BF_ASSERT(dbgModule->mHotIdx == hotIdx);
		dbgModule->FinishHotSwap();
	}

	for (auto dwarf : mDebugTarget->mDbgModules)
		dwarf->RevertWritingEnable();

	int blockAllocSinceClean = mDebugTarget->mHotHeap->mBlockAllocIdx - mDebugTarget->mLastHotHeapCleanIdx;
	// Clean up the hot heap every 64MB
	int blocksBetweenCleans = (64 * 1024 * 1024) / HotHeap::BLOCK_SIZE;

#ifdef _DEBUG
	//TODO: This is just for testing
	blocksBetweenCleans = 1;
#endif

	//TODO: Put this back after we fix the cleanup
	if (blockAllocSinceClean >= blocksBetweenCleans)
		CleanupHotHeap();

	mDebugTarget->RehupSrcFiles();

	if (hasHotVData)
		mDebugTarget->mVDataHotIdx = hotIdx;

	for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
	{
		auto breakpoint = mBreakpoints[breakIdx];
		CheckBreakpoint(breakpoint);
	}

	for (int hotThreadIdx = 0; hotThreadIdx < (int)mHotThreadStates.size(); hotThreadIdx++)
	{
		auto& hotThreadState = mHotThreadStates[hotThreadIdx];
		WdThreadInfo* threadInfo = NULL;
		if (!mThreadMap.TryGetValue((uint32)hotThreadState.mThreadId, &threadInfo))
			continue;

		BfLogDbg("ResumeThread %d\n", threadInfo->mThreadId);
		::ResumeThread(threadInfo->mHThread);
	}

	mHotThreadStates.Clear();

	if (IsPaused())
	{
		ClearCallStack();
		UpdateCallStack();
	}
}

void WinDebugger::InitiateHotResolve(DbgHotResolveFlags flags)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	delete mHotResolveData;
	mHotResolveData = NULL;

	mHotResolveData = new DbgHotResolveData();
	DbgHotScanner* hotScanner = new DbgHotScanner(this);
	hotScanner->Scan(flags);
	delete hotScanner;
}

intptr WinDebugger::GetDbgAllocHeapSize()
{
	if (mDbgHeapData == NULL)
	{
		Beefy::String memName = StrFormat("BFGC_stats_%d", mProcessInfo.dwProcessId);

		mDbgHeapData = new WinDbgHeapData();
		mDbgHeapData->mFileMapping = ::OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, memName.c_str());
		if (mDbgHeapData->mFileMapping == 0)
		{
			delete mDbgHeapData;
			mDbgHeapData = NULL;
			return 0;
		}

		mDbgHeapData->mStats = (WinDbgHeapData::Stats*)MapViewOfFile(mDbgHeapData->mFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(WinDbgHeapData::Stats));
	}

	if (mDbgHeapData->mStats == NULL)
		return 0;

	return mDbgHeapData->mStats->mHeapSize;
}

String WinDebugger::GetDbgAllocInfo()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	for (auto threadInfo : mThreadList)
		::SuspendThread(threadInfo->mHThread);

	delete mHotResolveData;
	mHotResolveData = NULL;

	mHotResolveData = new DbgHotResolveData();
	DbgHotScanner* hotScanner = new DbgHotScanner(this);
	hotScanner->Scan((DbgHotResolveFlags)(DbgHotResolveFlag_Allocations | DbgHotResolveFlag_KeepThreadState));
	delete hotScanner;

	String result;

	if (mHotResolveData != NULL)
	{
		DbgExprEvaluator exprEvaluator(this, NULL, NULL, -1, -1);
		exprEvaluator.mDebugTarget = mDebugTarget;

		String typeName;

		result += ":types\n";

		for (int typeId = 0; typeId < mHotResolveData->mTypeData.size(); typeId++)
		{
			auto& typeData = mHotResolveData->mTypeData[typeId];
			if (typeData.mCount > 0)
			{
				auto type = exprEvaluator.GetBeefTypeById(typeId);
				typeName.Clear();
				exprEvaluator.BeefTypeToString(type, typeName);
				if (typeName.IsEmpty())
					typeName = StrFormat("Type #%d", typeId);
				result += StrFormat("type\t%d\t%s\t%lld\t%lld\n", typeId, typeName.c_str(), typeData.mCount, typeData.mSize);
			}
		}
	}

	for (auto threadInfo : mThreadList)
		::ResumeThread(threadInfo->mHThread);

	return result;
}

static bool CreatePipeWithSecurityAttributes(HANDLE& hReadPipe, HANDLE& hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, int32 nSize)
{
	hReadPipe = 0;
	hWritePipe = 0;
	bool ret = ::CreatePipe(&hReadPipe, &hWritePipe, lpPipeAttributes, nSize);
	if (!ret || (hReadPipe == INVALID_HANDLE_VALUE) || (hWritePipe == INVALID_HANDLE_VALUE))
		return false;
	return true;
}

static bool CreatePipe(HANDLE& parentHandle, HANDLE& childHandle, bool parentInputs)
{
	SECURITY_ATTRIBUTES securityAttributesParent = { 0 };
	securityAttributesParent.bInheritHandle = 1;

	HANDLE hTmp = INVALID_HANDLE_VALUE;
	if (parentInputs)
		CreatePipeWithSecurityAttributes(childHandle, hTmp, &securityAttributesParent, 0);
	else
		CreatePipeWithSecurityAttributes(hTmp, childHandle, &securityAttributesParent, 0);

	HANDLE dupHandle = 0;

	// Duplicate the parent handle to be non-inheritable so that the child process
	// doesn't have access. This is done for correctness sake, exact reason is unclear.
	// One potential theory is that child process can do something brain dead like
	// closing the parent end of the pipe and there by getting into a blocking situation
	// as parent will not be draining the pipe at the other end anymore.
	if (!::DuplicateHandle(GetCurrentProcess(), hTmp,
		GetCurrentProcess(), &dupHandle,
		0, false, DUPLICATE_SAME_ACCESS))
	{
		return false;
	}

	parentHandle = dupHandle;

	if (hTmp != INVALID_HANDLE_VALUE)
		::CloseHandle(hTmp);

	return true;
}

bool WinDebugger::DoOpenFile(const StringImpl& fileName, const StringImpl& args, const StringImpl& workingDir, const Array<uint8>& envBlock, DbgOpenFileFlags openFileFlags)
{
	BP_ZONE("WinDebugger::DoOpenFile");

	AutoCrit autoCrit(mDebugManager->mCritSect);
	//gDbgPerfManager->StartRecording();

	STARTUPINFOW si;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));

	DWORD flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | CREATE_DEFAULT_ERROR_MODE;
	BOOL inheritHandles = false;

	// set up the streams
	if ((openFileFlags & (DbgOpenFileFlag_RedirectStdInput | DbgOpenFileFlag_RedirectStdOutput | DbgOpenFileFlag_RedirectStdError)) != 0)
	{
		if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
			CreatePipe(mStdInputPipe, si.hStdInput, true);
		else if (::GetConsoleWindow() != NULL)
			si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
		else
			si.hStdInput = INVALID_HANDLE_VALUE;

		if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
			CreatePipe(mStdOutputPipe, si.hStdOutput, false);
		else
			si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);

		if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
			CreatePipe(mStdErrorPipe, si.hStdError, false);
		else
			si.hStdError = GetStdHandle(STD_ERROR_HANDLE);		
		flags |= CREATE_NO_WINDOW;
		si.dwFlags = STARTF_USESTDHANDLES;
		inheritHandles = true;
	}	

	if (mDbgProcessId != 0)
	{
		BOOL success = ::DebugActiveProcess(mDbgProcessId);
		if (!success)
			return false;

		mProcessInfo.dwProcessId = mDbgProcessId;
	}
	else
	{
		BP_ZONE("DoOpenFile_CreateProcessW");

		UTF16String envW;
		
		void* envPtr = NULL;
		if (!envBlock.IsEmpty())
		{
			//UTF16?
			if (envBlock[1] == 0)
			{
				envPtr = (void*)&envBlock[0];
				flags |= CREATE_UNICODE_ENVIRONMENT;
			}
			else
			{
				String str8((char*)&envBlock[0], (int)envBlock.size());
				envW = UTF8Decode(str8);

				envPtr = (void*)envW.c_str();
				flags |= CREATE_UNICODE_ENVIRONMENT;
			}
		}

		String cmdLine = "\"";
		cmdLine += fileName;
		cmdLine += "\"";

		if (!args.IsEmpty())
		{
			cmdLine += " ";
			cmdLine += args;
		}

		BOOL worked = CreateProcessW(NULL, (WCHAR*)UTF8Decode(cmdLine).c_str(), NULL, NULL, inheritHandles,
			flags, envPtr, (WCHAR*)UTF8Decode(workingDir).c_str(), &si, &mProcessInfo);

		if ((openFileFlags & DbgOpenFileFlag_RedirectStdInput) != 0)
			::CloseHandle(si.hStdInput);
		if ((openFileFlags & DbgOpenFileFlag_RedirectStdOutput) != 0)
			::CloseHandle(si.hStdOutput);
		if ((openFileFlags & DbgOpenFileFlag_RedirectStdError) != 0)
			::CloseHandle(si.hStdError);

		if (!worked)
		{
			if (IsHandleValid(mStdInputPipe))
			{
				::CloseHandle(mStdInputPipe);
				mStdInputPipe = 0;
			}
			if (IsHandleValid(mStdOutputPipe))
			{
				::CloseHandle(mStdOutputPipe);
				mStdOutputPipe = 0;
			}
			if (IsHandleValid(mStdErrorPipe))
			{
				::CloseHandle(mStdErrorPipe);
				mStdErrorPipe = 0;
			}

			auto lastError = ::GetLastError();
			if (lastError == ERROR_DIRECTORY)
			{
				mDebugManager->mOutMessages.push_back(StrFormat("error Unable to locate specified working directory '%s'", SlashString(workingDir, false, false).c_str()));
			}

			return false;
		}

		WdThreadInfo* threadInfo = new WdThreadInfo();
		threadInfo->mProcessId = mProcessInfo.dwProcessId;
		threadInfo->mThreadId = mProcessInfo.dwThreadId;
		threadInfo->mHThread = mProcessInfo.hThread;
		threadInfo->mThreadLocalBase = NULL;
		threadInfo->mStartAddress = NULL;
		mThreadMap[mProcessInfo.dwThreadId] = threadInfo;
		mThreadList.push_back(threadInfo);
	}

	mRunState = RunState_Running;

	while (true)
	{
		BP_ZONE("DoOpenFile_WaitForImageBase");
		autoCrit.mCritSect->Unlock();
		DoUpdate();
		autoCrit.mCritSect->Lock();

		ContinueDebugEvent();

		if ((mDebugTarget->mLaunchBinary != NULL) && (mDebugTarget->mLaunchBinary->mOrigImageData != NULL))
			break;
	}

	RehupBreakpoints(true);

	//gDbgPerfManager->StopRecording();
	//gDbgPerfManager->DbgPrint();

	return true;
}

void WinDebugger::StopDebugging()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	BfLogDbg("WinDebugger::Terminate\n");
	if (mActiveSymSrvRequest != NULL)
		mActiveSymSrvRequest->Cancel();
	if ((mRunState == RunState_NotStarted) || (mRunState == RunState_Terminated) || (mRunState == RunState_Terminating))
		return;

	if ((mDbgProcessId != 0) && ((mDbgAttachFlags & BfDbgAttachFlag_ShutdownOnExit) == 0))
	{
		for (auto address : mTempBreakpoint)
			RemoveBreakpoint(address);
		for (auto breakpoint : mBreakpoints)
			DetachBreakpoint(breakpoint);

		BfLogDbg("StopDebugging\n");
		::DebugActiveProcessStop(mDbgProcessId);
		mRunState = RunState_Terminated;
		BfLogDbg("mRunState = RunState_Terminated\n");
	}
	else
	{
		TerminateProcess(mProcessInfo.hProcess, 0);
		mRunState = RunState_Terminating;
		BfLogDbg("mRunState = RunState_Terminating\n");
	}
}

void WinDebugger::Terminate()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	BfLogDbg("WinDebugger::Terminate\n");
	if (mActiveSymSrvRequest != NULL)
		mActiveSymSrvRequest->Cancel();
	if ((mRunState == RunState_NotStarted) || (mRunState == RunState_Terminated) || (mRunState == RunState_Terminating))
		return;
	TerminateProcess(mProcessInfo.hProcess, 0);
	mRunState = RunState_Terminating;
	BfLogDbg("mRunState = RunState_Terminating\n");
}

static int gDebugUpdateCnt = 0;

void WinDebugger::Detach()
{
	BfLogDbg("Debugger Detach\n");

	mDebugManager->mNetManager->CancelAll();
	while ((mIsRunning) || (mDebuggerThreadId != 0))
	{
		mShuttingDown = true;
		Sleep(1);
	}

	for (auto profiler : mProfilerSet)
		profiler->Stop();

	BfLogDbg("Debugger Detach - thread finished\n");

	mPendingProfilerMap.Clear();
	for (auto profiler : mNewProfilerList)
		delete profiler;
	mNewProfilerList.Clear();

	mPendingImageLoad.Clear();
	mPendingDebugInfoLoad.Clear();

	RemoveTempBreakpoints();
	mContinueEvent.Reset();
	if (mDebugTarget != mEmptyDebugTarget)
		delete mDebugTarget;
	mDebugTarget = mEmptyDebugTarget;

	mShuttingDown = false;
	mStepSP = 0;
	ClearCallStack();
	mRunState = RunState_NotStarted;
	mStepType = StepType_None;
	mHadImageFindError = false;
	mIsPartialCallStack = true;

	delete mDebugPendingExpr;
	mDebugPendingExpr = NULL;

	for (auto threadPair : mThreadMap)
	{
		auto threadInfo = threadPair.mValue;
		delete threadInfo;
	}
	mThreadMap.Clear();
	mThreadList.Clear();
	mHotTargetMemory.Clear();

	// We don't need to close the hThread when we have attached to a process
	if (mDbgProcessId == 0)
	{
		CloseHandle(mProcessInfo.hThread);
		CloseHandle(mProcessInfo.hProcess);
	}

	for (auto breakpoint : mBreakpoints)
	{
		if (!mDestroying)
		{
			BF_FATAL("Breakpoints should be deleted already");
		}

		if (breakpoint->mMemoryBreakpointInfo != NULL)
		{
			DetachBreakpoint(breakpoint);
		}
	}

	ZeroMemory(&mProcessInfo, sizeof(mProcessInfo));
	mStepBreakpointAddrs.Clear();
	mIsRunning = false;

	mDbgAttachFlags = BfDbgAttachFlag_None;
	mDbgProcessId = 0;
	delete mDbgHeapData;
	mDbgHeapData = NULL;
	mDbgProcessHandle = 0;
	ClearCallStack();
	mWantsDebugContinue = false;
	mAtBreakThread = NULL;
	mActiveThread = NULL;
	mActiveBreakpoint = NULL;
	mSteppingThread = NULL;
	mExplicitStopThread = NULL;
	mIsContinuingFromException = false;
	mGotStartupEvent = false;

	mIsDebuggerWaiting = false;
	mPhysBreakpointAddrMap.Clear();
	mBreakpointAddrMap.Clear();

	gDebugUpdateCnt = 0;

	if (IsHandleValid(mStdInputPipe))
		::CloseHandle(mStdInputPipe);
	mStdInputPipe = INVALID_HANDLE_VALUE;

	if (IsHandleValid(mStdOutputPipe))
		::CloseHandle(mStdOutputPipe);
	mStdOutputPipe = INVALID_HANDLE_VALUE;
	
	if (IsHandleValid(mStdErrorPipe))
		::CloseHandle(mStdErrorPipe);
	mStdErrorPipe = INVALID_HANDLE_VALUE;
}

Profiler* WinDebugger::StartProfiling()
{
	return new DbgProfiler(this);
}

Profiler* WinDebugger::PopProfiler()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	if (mNewProfilerList.IsEmpty())
		return NULL;
	auto profiler = (DbgProfiler*)mNewProfilerList[0];
	mNewProfilerList.erase(mNewProfilerList.begin());
	return profiler;
}

void WinDebugger::AddProfiler(DbgProfiler * profiler)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	mProfilerSet.Add(profiler);
}

void WinDebugger::RemoveProfiler(DbgProfiler * profiler)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	mProfilerSet.Remove(profiler);
}

void WinDebugger::ReportMemory(MemReporter* memReporter)
{
	mEmptyDebugTarget->ReportMemory(memReporter);
	if (mDebugTarget != mEmptyDebugTarget)
		mDebugTarget->ReportMemory(memReporter);
}

bool WinDebugger::GetEmitSource(const StringImpl& filePath, String& outText)
{
	if (!filePath.StartsWith("$Emit"))
		return false;

	int dollarPos = filePath.IndexOf('$', 1);
	String numStr = filePath.Substring(5, dollarPos - 5);
	int id = atoi(numStr.c_str());

	for (auto dbgModule : mDebugTarget->mDbgModules)
	{
		if (dbgModule->mId == id)
			return dbgModule->GetEmitSource(filePath, outText);
	}

	return false;
}

void WinDebugger::ModuleChanged(DbgModule* dbgModule)
{
	mDebugManager->mOutMessages.push_back(String("dbgInfoLoaded ") + dbgModule->mFilePath);
}

bool WinDebugger::DoUpdate()
{
	if ((mDbgProcessId != 0) && ((mDbgAttachFlags & BfDbgAttachFlag_ShutdownOnExit) == 0))
		::DebugSetProcessKillOnExit(FALSE);
	else
		::DebugSetProcessKillOnExit(TRUE);

	//
	{
		AutoCrit autoCrit(mDebugManager->mCritSect);
		auto _ModuleChanged = [&](DbgModule* dbgModule)
		{
			ModuleChanged(dbgModule);
			ClearCallStack(); // We may have actual dbgSubprograms and stuff now...
		};

		for (auto dbgModule : mPendingImageLoad)
		{
			dbgModule->PreCacheImage();
		}

		for (auto kv : mPendingDebugInfoLoad)
		{
			kv.mKey->PreCacheDebugInfo();
		}

		while (!mPendingImageLoad.IsEmpty())
		{
			auto dbgModule = mPendingImageLoad.back();
			mPendingImageLoad.pop_back();
			dbgModule->RequestImage();
			_ModuleChanged(dbgModule);
		}

		if (!mPendingDebugInfoLoad.IsEmpty())
		{
			Array<DbgPendingDebugInfoLoad> pendingList;
			for (auto kv : mPendingDebugInfoLoad)
				pendingList.Add(kv.mValue);
			mPendingDebugInfoLoad.Clear();

			for (auto& entry : pendingList)
			{
				auto dbgModule = entry.mModule;
				entry.mModule->RequestDebugInfo(entry.mAllowRemote);
				// We do a "_ModuleChanged" even if the load failed, so we rehup the callstack and stop
				//  saying "<Loading...>"
				_ModuleChanged(entry.mModule);
			}
		}
	}

	if (IsMiniDumpDebugger())
	{
		//
		{
			AutoCrit autoCrit(mDebugManager->mCritSect);
			if (mRunState == RunState_Terminating)
			{
				mRunState = RunState_Terminated;
				return false;
			}
		}

		Sleep(20);
		return false;
	}

	if (mIsDebuggerWaiting)
	{
		if ((IsInRunState()) || (mRunState == RunState_Terminating) || (mRunState == RunState_DebugEval))
			ContinueDebugEvent();
		if (mContinueEvent.WaitFor(8))
		{
			BF_ASSERT(!mWantsDebugContinue); // mWantsDebugContinue should already been reset
			BfLogDbg("::ContinueDebugEvent 1 ThreadId:%d\n", mDebuggerWaitingThread->mThreadId);
			BF_ASSERT_REL(mDebuggerWaitingThread->mIsAtBreakpointAddress == 0);
			::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, mIsContinuingFromException ? DBG_EXCEPTION_NOT_HANDLED : DBG_CONTINUE);
			mIsContinuingFromException = false;
			mIsDebuggerWaiting = false;
			mDebuggerWaitingThread = NULL;
		}
		else
			return false;
	}

	if (!WaitForDebugEvent(&mDebugEvent, 8))
		return false;

	gDebugUpdateCnt++;

	static const char* eventNames[] = { "DBG_EVENT ?",
		"EXCEPTION_DEBUG_EVENT",
		"CREATE_THREAD_DEBUG_EVENT",
		"CREATE_PROCESS_DEBUG_EVENT",
		"EXIT_THREAD_DEBUG_EVENT",
		"EXIT_PROCESS_DEBUG_EVENT",
		"LOAD_DLL_DEBUG_EVENT",
		"UNLOAD_DLL_DEBUG_EVENT",
		"OUTPUT_DEBUG_STRING_EVENT",
		"RIP_EVENT"};

	BfLogDbg("WaitForDebugEvent %s ThreadId:%d\n", eventNames[mDebugEvent.dwDebugEventCode], mDebugEvent.dwThreadId);

	BP_ZONE(eventNames[mDebugEvent.dwDebugEventCode]);

	AutoCrit autoCrit(mDebugManager->mCritSect);

	mActiveBreakpoint = NULL;
	mIsDebuggerWaiting = true;
	mWantsDebugContinue = true;
	mRequestedStackFrameIdx = 0;
	mBreakStackFrameIdx = 0;
	mShowPCOverride = 0;

	WdThreadInfo* threadInfo = NULL;

	mThreadMap.TryGetValue(mDebugEvent.dwThreadId, &threadInfo);

	mDebuggerWaitingThread = threadInfo;
	mExplicitStopThread = mDebuggerWaitingThread;

	switch (mDebugEvent.dwDebugEventCode)
	{
	case CREATE_PROCESS_DEBUG_EVENT:
		{
			if (threadInfo == NULL)
			{
				BF_ASSERT(mThreadMap.size() == 0);

				WdThreadInfo* newThreadInfo = new WdThreadInfo();
				newThreadInfo->mProcessId = mDebugEvent.dwProcessId;
				newThreadInfo->mThreadId = mDebugEvent.dwThreadId;
				newThreadInfo->mHThread = mDebugEvent.u.CreateProcessInfo.hThread;
				newThreadInfo->mThreadLocalBase = mDebugEvent.u.CreateProcessInfo.lpThreadLocalBase;
				newThreadInfo->mStartAddress = (void*)mDebugEvent.u.CreateProcessInfo.lpStartAddress;

				BF_CONTEXT lcContext;
				lcContext.ContextFlags = BF_CONTEXT_CONTROL;
				BF_GetThreadContext(newThreadInfo->mHThread, &lcContext);
				newThreadInfo->mStartSP = BF_CONTEXT_SP(lcContext);

				mThreadMap[mDebugEvent.dwThreadId] = newThreadInfo;
				mDebuggerWaitingThread = newThreadInfo;
				mThreadList.push_back(mDebuggerWaitingThread);
				UpdateThreadDebugRegisters();

				if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadCreateMessages))
					OutputMessage(StrFormat("Creating thread from CREATE_PROCESS_DEBUG_EVENT %d\n", mDebugEvent.dwThreadId));

				threadInfo = mDebuggerWaitingThread;
				mProcessInfo.dwThreadId = threadInfo->mThreadId;
				mProcessInfo.hThread = threadInfo->mHThread;
				mProcessInfo.hProcess = mDebugEvent.u.CreateProcessInfo.hProcess;
			}
			else
			{
				threadInfo->mThreadLocalBase = mDebugEvent.u.CreateProcessInfo.lpThreadLocalBase;
				threadInfo->mStartAddress = (void*)mDebugEvent.u.CreateProcessInfo.lpStartAddress;
			}

			BF_CONTEXT lcContext;
			lcContext.ContextFlags = BF_CONTEXT_CONTROL;
			BF_GetThreadContext(threadInfo->mHThread, &lcContext);
			threadInfo->mStartSP = BF_CONTEXT_SP(lcContext);

			DbgModule* launchBinary = mDebugTarget->Init(mLaunchPath, mTargetPath, (addr_target)(intptr)mDebugEvent.u.CreateProcessInfo.lpBaseOfImage);
			addr_target gotImageBase = (addr_target)(intptr)mDebugEvent.u.CreateProcessInfo.lpBaseOfImage;
			if (launchBinary->mImageBase != gotImageBase)
			{
				BF_FATAL("Image base didn't match");
			}

			launchBinary->mImageBase = gotImageBase;
			launchBinary->mImageSize = (int)launchBinary->GetImageSize();
			launchBinary->mOrigImageData = new DbgModuleMemoryCache(launchBinary->mImageBase, launchBinary->mImageSize);

			if (launchBinary == mDebugTarget->mTargetBinary)
				mDebugTarget->SetupTargetBinary();

			if (mDebugEvent.u.CreateProcessInfo.hFile != NULL)
				CloseHandle(mDebugEvent.u.CreateProcessInfo.hFile);

			mDbgProcessHandle = mDebugEvent.u.CreateProcessInfo.hProcess;
			mDbgThreadHandle = mDebugEvent.u.CreateProcessInfo.hThread;

			mGotStartupEvent = true;
			mDebugManager->mOutMessages.push_back("modulesChanged");
		}
		break;
	case EXIT_PROCESS_DEBUG_EVENT:
		{
			BfLogDbg("EXIT_PROCESS_DEBUG_EVENT\n");

			DWORD exitCode = mDebugEvent.u.ExitProcess.dwExitCode;

			String exitMessage;
			switch (exitCode)
			{
			case STATUS_DLL_NOT_FOUND:
				exitMessage = "STATUS_DLL_NOT_FOUND";
				break;
			case STATUS_DLL_INIT_FAILED:
				exitMessage = "STATUS_DLL_INIT_FAILED";
				break;
			case STATUS_ENTRYPOINT_NOT_FOUND:
				exitMessage = "STATUS_ENTRYPOINT_NOT_FOUND";
				break;
			}

			String exitCodeStr;
			if ((exitCode >= 0x10000000) && (exitCode <= 0xF7000000))
				exitCodeStr = StrFormat("0x%X", exitCode);
			else
				exitCodeStr = StrFormat("%d", exitCode);

			if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ProcessExitMessages))
			{
				if (!exitMessage.IsEmpty())
					OutputMessage(StrFormat("Process terminated. ExitCode: %s (%s).\n", exitCodeStr.c_str(), exitMessage.c_str()));
				else
					OutputMessage(StrFormat("Process terminated. ExitCode: %s.\n", exitCodeStr.c_str()));
			}

			mRunState = RunState_Terminated;
			mDebugManager->mOutMessages.push_back("modulesChanged");
		}
		break;
	case LOAD_DLL_DEBUG_EVENT:
		{
			WCHAR moduleNameStr[MAX_PATH] = { 0 };
			GetFinalPathNameByHandleW(mDebugEvent.u.LoadDll.hFile, moduleNameStr, MAX_PATH, FILE_NAME_NORMALIZED);

			std::wstring wow64Dir;
			std::wstring systemDir;

			PWSTR wow64DirPtr = NULL;
			SHGetKnownFolderPath(FOLDERID_SystemX86, KF_FLAG_NO_ALIAS, NULL, &wow64DirPtr);
			if (wow64DirPtr != NULL)
			{
				wow64Dir = wow64DirPtr;
				CoTaskMemFree(wow64DirPtr);
			}

			PWSTR systemDirPtr = NULL;
			SHGetKnownFolderPath(FOLDERID_System, KF_FLAG_NO_ALIAS, NULL, &systemDirPtr);
			if (systemDirPtr != NULL)
			{
				systemDir = systemDirPtr;
				CoTaskMemFree(systemDirPtr);
			}

			if ((mDebugEvent.u.LoadDll.lpImageName != 0) && (mDebugEvent.u.LoadDll.fUnicode))
			{
				addr_target strAddr = ReadMemory<addr_target>((addr_target)(intptr)mDebugEvent.u.LoadDll.lpImageName);

				for (int i = 0; i < MAX_PATH - 1; i++)
				{
					WCHAR c = ReadMemory<WCHAR>(strAddr + i*2);
					moduleNameStr[i] = (WCHAR)c;
					if (c == 0)
						break;
				}
			}

			String origModuleName = UTF8Encode(moduleNameStr);
			String moduleName = origModuleName;

			String loadMsg;
			HANDLE altFileHandle = INVALID_HANDLE_VALUE;
			if (moduleName != origModuleName)
			{
				loadMsg = StrFormat("Loading DLL: %s(%s) @ %s", origModuleName.c_str(), moduleName.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.LoadDll.lpBaseOfDll, true).c_str());
				altFileHandle = ::CreateFileW(UTF8Decode(moduleName).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
			}
			else
			{
				loadMsg = StrFormat("Loading DLL: %s @ %s", moduleName.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.LoadDll.lpBaseOfDll, true).c_str());
			}

			BfLogDbg("LOAD_DLL_DEBUG_EVENT %s\n", moduleName.c_str());

			bool skipLoad = false;
#ifdef BF_DBG_32
			if (((uintptr)mDebugEvent.u.LoadDll.lpBaseOfDll & 0xFFFFFFFF00000000LL) != 0)
			{
				skipLoad = true;
				loadMsg += " - Skipped";
			}
#endif

			if (!skipLoad)
			{
				FileHandleStream stream;
				stream.mFileHandle = mDebugEvent.u.LoadDll.hFile;
				if (altFileHandle != INVALID_HANDLE_VALUE)
					stream.mFileHandle = altFileHandle;
				if (mDebugTarget->SetupDyn(moduleName, &stream, (intptr)mDebugEvent.u.LoadDll.lpBaseOfDll) == NULL)
					loadMsg += " - Failed to load";
				stream.mFileHandle = 0;
			}

			if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ModuleLoadMessages))
				OutputMessage(loadMsg + "\n");

			if (altFileHandle != INVALID_HANDLE_VALUE)
				::CloseHandle(altFileHandle);
			::CloseHandle(mDebugEvent.u.LoadDll.hFile);

			// Try to bind any breakpoints tied to this DLL
			RehupBreakpoints(true);
			mDebugManager->mOutMessages.push_back("modulesChanged");
		}
		break;
	case UNLOAD_DLL_DEBUG_EVENT:
		{
			bool needsBreakpointRehup = false;

			String name = "???";
			DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress((addr_target)(intptr)mDebugEvent.u.UnloadDll.lpBaseOfDll);
			if (dbgModule != NULL)
			{
				name = dbgModule->mFilePath;

				for (int i = 0; i < (int)mBreakpoints.size(); i++)
				{
					auto breakpoint = mBreakpoints[i];
					auto checkBreakpoint = breakpoint;
					bool hasAddr = false;
					while (checkBreakpoint != NULL)
					{
						if ((checkBreakpoint->mAddr >= dbgModule->mImageBase) && (checkBreakpoint->mAddr < dbgModule->mImageBase + dbgModule->mImageSize))
							hasAddr = true;
						checkBreakpoint = (WdBreakpoint*)checkBreakpoint->mLinkedSibling;
					}

					if (hasAddr)
					{
						DetachBreakpoint(breakpoint);
						needsBreakpointRehup = true;
					}
				}

				bool hadTarget = mDebugTarget->mTargetBinary != NULL;
				mDebugTarget->UnloadDyn(dbgModule->mImageBase);
				if (needsBreakpointRehup)
					RehupBreakpoints(true);

				if ((mDebugTarget->mTargetBinary == NULL) && (hadTarget))
				{
					mRunState = RunState_TargetUnloaded;
				}

				mPendingDebugInfoLoad.Remove(dbgModule);
				mPendingDebugInfoRequests.Remove(dbgModule);
				mDebugManager->mOutMessages.push_back("modulesChanged");
			}

			if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ModuleUnloadMessages) && !name.empty())
				OutputMessage(StrFormat("Unloading DLL: %s @ %0s\n", name.c_str(), EncodeDataPtr((addr_target)(intptr)mDebugEvent.u.UnloadDll.lpBaseOfDll, true).c_str()));

			BfLogDbg("UNLOAD_DLL_DEBUG_EVENT %s\n", name.c_str());
		}
		break;
	case OUTPUT_DEBUG_STRING_EVENT:
		{
			const int maxChars = 1024 * 1024;
			int len = BF_MIN(maxChars, (int)mDebugEvent.u.DebugString.nDebugStringLength); // 1MB max
			char* message = new char[len + 1];

			message[0] = 0;
			message[len] = 0;
			ReadMemory((addr_target)(intptr)mDebugEvent.u.DebugString.lpDebugStringData, len, message);

			if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId == mDebugEvalThreadInfo.mThreadId))
				mDebugManager->mOutMessages.push_back(String("dbgEvalMsg ") + message);
			else
				mDebugManager->mOutMessages.push_back(String("msg ") + message);

			BfLogDbg("OUTPUT_DEBUG_STRING_EVENT (BreakAddr:%@): %s\n", threadInfo->mIsAtBreakpointAddress, message);

			BF_ASSERT_REL(threadInfo->mIsAtBreakpointAddress == 0);

			delete [] message;
		}
		break;
	case CREATE_THREAD_DEBUG_EVENT:
		{
			WdThreadInfo* threadInfo = new WdThreadInfo();
			threadInfo->mProcessId = mDebugEvent.dwProcessId;
			threadInfo->mThreadId = mDebugEvent.dwThreadId;
			threadInfo->mHThread = mDebugEvent.u.CreateThread.hThread;
			threadInfo->mThreadLocalBase = mDebugEvent.u.CreateThread.lpThreadLocalBase;
			threadInfo->mStartAddress = (void*)mDebugEvent.u.CreateThread.lpStartAddress;

			BF_CONTEXT lcContext;
			lcContext.ContextFlags = BF_CONTEXT_CONTROL;
			BF_GetThreadContext(threadInfo->mHThread, &lcContext);
			threadInfo->mStartSP = BF_CONTEXT_SP(lcContext);

			mThreadMap[mDebugEvent.dwThreadId] = threadInfo;
			mDebuggerWaitingThread = threadInfo;
			mThreadList.push_back(mDebuggerWaitingThread);
			UpdateThreadDebugRegisters();
			if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadCreateMessages))
				OutputMessage(StrFormat("Creating thread %d\n", mDebugEvent.dwThreadId));
		}
		break;
	case EXIT_THREAD_DEBUG_EVENT:
		{
			if (!(gDebugManager->GetOutputFilterFlags() & BfOutputFilterFlags_ThreadExitMessages))
				OutputMessage(StrFormat("Exiting thread %d\n", mDebugEvent.dwThreadId));

			if (mSteppingThread == threadInfo)
			{
				// We were attempting stepping on this thread, but not anymore!
				ClearStep();
			}
			::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_CONTINUE);
			mIsDebuggerWaiting = false;
			mWantsDebugContinue = false;

			if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId == mDebugEvalThreadInfo.mThreadId))
			{
				// Thread terminated while evaluating! Is there a more graceful way of handling this?
				CleanupDebugEval(false);
				mRunState = RunState_Running;
			}

			mThreadList.Remove(mDebuggerWaitingThread);

			delete mDebuggerWaitingThread;
			mDebuggerWaitingThread = NULL;
			mThreadMap.Remove(mDebugEvent.dwThreadId);
			return true;
		}
		break;
	case RIP_EVENT:
		OutputMessage("RIP Event\n");
		break;
	case EXCEPTION_DEBUG_EVENT:
		{
			auto exceptionRecord = &mDebugEvent.u.Exception.ExceptionRecord;

			switch (exceptionRecord->ExceptionCode)
			{
			case STATUS_WX86_BREAKPOINT:
			case EXCEPTION_BREAKPOINT:
				{
					if (mRunState == RunState_Terminating)
					{
						BfLogDbg("Ignoring event because of RunState_Terminating\n");
						break;
					}

					mAtBreakThread = threadInfo;
					mActiveThread = mAtBreakThread;

					bool isHighAddr = false;
#ifdef BF_DBG_32
					if (((uintptr)exceptionRecord->ExceptionAddress & 0xFFFFFFFF00000000) != 0)
					{
						if (mActiveThread == mThreadList.front())
						{
							// Skip the initial Wow64 ntdll.dll!LdrpDoDebuggerBreak
							mRunState = RunState_Running;
							break;
						}
						isHighAddr = true;
					}
#endif

					addr_target pcAddress = (addr_target)(intptr)exceptionRecord->ExceptionAddress;
					if (isHighAddr)
						pcAddress = (addr_target)-1;
					//mStoppedAtAddress = pcAddress;

					bool isStepOut = false;

					if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto))
					{
						isStepOut = mStepBreakpointAddrs.Contains(pcAddress);
					}

					BF_CONTEXT lcContext;
					lcContext.ContextFlags = BF_CONTEXT_ALL;
					BF_GetThreadContext(threadInfo->mHThread, &lcContext);

					BfLogDbg("EXCEPTION_BREAKPOINT Thread:%d %p SP:%p\n", mActiveThread->mThreadId, pcAddress, BF_CONTEXT_SP(lcContext));

					uint8 origImageData = 0xCC;
					mDebugTarget->ReadOrigImageData(pcAddress, &origImageData, 1);
					bool wasDebugBreakpoint = origImageData != 0xCC;

					DbgSubprogram* dwSubprogram = NULL;
					DbgLineData* dwLineData = NULL;
					if (!isStepOut)
					{
						dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
						if (dwSubprogram == NULL)
							dwSubprogram = mDebugTarget->FindSubProgram(pcAddress, DbgOnDemandKind_LocalOnly);
					}

					bool isLineStart = (dwLineData != NULL) && (dwSubprogram->GetLineAddr(*dwLineData) == pcAddress);
					bool isNonDebuggerBreak = false;

					if (wasDebugBreakpoint)
					{
						// Go ahead and set EIP back one instruction
						BF_CONTEXT_IP(lcContext)--;
						BF_SetThreadContext(threadInfo->mHThread, &lcContext);

						if ((dwSubprogram != NULL) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid) &&
							(pcAddress == dwSubprogram->mBlock.mLowPC))
						{
							BfLogDbg("Hit HotReplaceKind_Invalid breakpoint\n");
							mRunState = RunState_Paused;
							mDebugManager->mOutMessages.push_back("error This lambda was replaced by a new version that has incompatible captures. A program restart is required.");
							PhysRemoveBreakpoint(pcAddress);
							break;
						}
					}
					else
					{
						// This was an actual "break" instruction
						BfLogDbg("Non-debugger break\n");
						isNonDebuggerBreak = true;

						auto prevState = mRunState;

						// Make it an "auto" stop, so for example when we have an assert/retry we won't stop inside assembly
						mRequestedStackFrameIdx = -2;

						mRunState = RunState_Paused;
						CheckNonDebuggerBreak();
						if (IsInRunState())
						{
							BF_ASSERT((prevState == RunState_Running) || (prevState == RunState_DebugEval));
							mRunState = prevState;
							break; // Continue as if nothing happened
						}

						if (prevState == RunState_DebugEval)
							mRequestedStackFrameIdx = -1; // Don't show a rolled back stack idx if a debug eval fails

						ClearStep();
					}

					if (threadInfo->mIsBreakRestorePaused)
					{
						// The thread is supposed to be paused, but the IP has been reset
						//  so just break here so we'll hit that breakpoint again once we're
						//  actually unpaused properly
						BfLogDbg("Ignoring EXCEPTION_BREAKPOINT\n", threadInfo->mThreadId);
						break;
					}

					if ((mRunState == RunState_DebugEval) || (mRunState == RunState_HotStep))
					{
						// If we hit a breakpoint while doing a debug eval, we just remove the breakpoint
						//  and expect to reinstate it during a rehup after the evaluation has completed
						WdBreakpoint* breakpoint = (WdBreakpoint*)FindBreakpointAt((uintptr_t) exceptionRecord->ExceptionAddress);
						if (breakpoint != NULL)
						{
							mNeedsRehupBreakpoints = true;
							RemoveBreakpoint(breakpoint->mLineData.GetAddress());
						}
						break;
					}

					bool isDeeper = false;

					int stepBreakAddrIdx = (int)mStepBreakpointAddrs.IndexOf(pcAddress);

					WdBreakpoint* breakpoint = NULL;
					bool ignoreBreakpoint = false;

					if ((mStepType != StepType_None) && (mSteppingThread == mAtBreakThread))
					{
						if (mStepType == StepType_ToTempBreakpoint)
						{
							RemoveTempBreakpoints();
							mRunState = RunState_Paused;
							break;
						}

						if (mContinueFromBreakpointFailed)
						{
							BfLogDbg("Continuing from ContinueFromBreakpointFailed\n");
							SetupStep(mStepType);
							mRunState = RunState_Running;
							break;
						}

						if (!isStepOut)
							breakpoint = (WdBreakpoint*)FindBreakpointAt(pcAddress);

						// Ignore breakpoint if it's on the line we're stepping off of
						if ((breakpoint != NULL) && (breakpoint->mAddr == mStepPC) &&
							(mStepSP == BF_CONTEXT_SP(lcContext)))
						{
							ignoreBreakpoint = true;
						}
						else if ((breakpoint != NULL) && (stepBreakAddrIdx == -1) && (!CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress)))
						{
							ignoreBreakpoint = true;
						}

						if ((stepBreakAddrIdx == -1) && (breakpoint == NULL) && (!isNonDebuggerBreak))
						{
							// If a breakpoint is removed in a prior thread
							BfLogDbg("Ignoring step break (old breakpoint)\n");

							if ((mSteppingThread == mAtBreakThread) && (mStepSwitchedThreads))
							{
								SetupStep(mStepType);
							}
							break;
						}

						if ((stepBreakAddrIdx != -1) && (breakpoint == NULL) && (mSteppingThread != mActiveThread))
						{
							BfLogDbg("Ignoring break (wrong thread)\n");
							ThreadRestorePause(mSteppingThread, mActiveThread);
							threadInfo->mIsAtBreakpointAddress = pcAddress;
							break;
						}

						isDeeper = mStepSP > BF_CONTEXT_SP(lcContext);
						if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto))
						{
							isDeeper = mStepSP >= BF_CONTEXT_SP(lcContext);
							BfLogDbg("StepOut Iteration SP:%p StartSP:%p IsDeeper:%d\n", BF_CONTEXT_SP(lcContext), mStepSP, isDeeper);
						}

						if (((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_ThenInto)) && (breakpoint == NULL) && (isDeeper))
						{
							// We're encountered recursion

							// Make sure we don't already have one of these stored
							BF_ASSERT(mStoredReturnValueAddr == 0);
							threadInfo->mIsAtBreakpointAddress = pcAddress;
							break; // Don't fall through, we don't want to set mIsAtBreakpointAddress
						}

						if (isStepOut)
						{
							threadInfo->mIsAtBreakpointAddress = pcAddress;

							if (mStepType == StepType_StepOut_ThenInto)
							{
								dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
								if ((dwLineData != NULL) && (pcAddress == dwSubprogram->GetLineAddr(*dwLineData)))
								{
									// Our step out from a filtered function put us at the start of a new line. Stop here
									// <do nothing>
								}
								else
								{
									// .. otherwise keep going until we get to the start of a new line
									SetupStep(StepType_StepInto);
									mRunState = RunState_Running;
									break;
								}
							}

							if (!mStepInAssembly)
							{
								// Keep stepping out until we find a frame that we have source for
								DbgSubprogram* dwSubprogram = NULL;
								DbgLineData* dwLineData = FindLineDataAtAddress(BF_CONTEXT_IP(lcContext), &dwSubprogram);
								if (dwLineData == NULL)
								{
									SetupStep(StepType_StepOut);
									break;
								}

								if ((dwLineData->mColumn == -1) && (!dwSubprogram->HasValidLines()))
								{
									// This is a method we don't actually want to be in, it has no valid lines!
									SetupStep(StepType_StepOut);
									break;
								}

								if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
								{
									// We've stepped out, but right into the start of an inlined method, so step out of this inlined method now...
									SetupStep(StepType_StepOut);
									break;
								}
							}

							ClearStep();
							mRunState = RunState_Paused;
							threadInfo->mStoppedAtAddress = pcAddress;

							break;
						}

						mRunState = RunState_Paused;
						if (breakpoint != NULL)
						{
							// While stepping we hit a legit breakpoint
							threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;

							// Ignore breakpoint on return statement if we're return-stepping
							mRunState = RunState_Breakpoint;
						}

						if ((mStepType == StepType_StepInto) && (dwSubprogram != NULL))
						{
							// Don't filter out the current subprogram (would break cases where we explicitly stepped into or hit breakpoint in a filtered subprogram)
							bool isInStartSubprogram = (mStepStartPC >= dwSubprogram->mBlock.mLowPC) && (mStepStartPC < dwSubprogram->mBlock.mHighPC);
							if ((!isInStartSubprogram) && (IsStepFiltered(dwSubprogram, dwLineData)))
							{
								BfLogDbg("Hit step filter\n");
								mRunState = RunState_Running;
								SetupStep(StepType_StepOut_ThenInto);
								break;
							}
						}

						if ((mStepType == StepType_StepOver) && (stepBreakAddrIdx == 0) && (mStepBreakpointAddrs[0] != 0) && (mStepBreakpointAddrs.size() > 1))
						{
							// Break was on the 'call' instruction, not the instruction after it -- means recursion
							BfLogDbg("StepOver detected recursing\n");
							mStepIsRecursing = true;
							if (mTempBreakpoint.Remove(mStepBreakpointAddrs[0]))
							{
								RemoveBreakpoint(mStepBreakpointAddrs[0]);
							}
							mStepBreakpointAddrs[0] = 0;
							mRunState = RunState_Running;
							break;
						}

						if ((mStepType == StepType_StepOver) && (stepBreakAddrIdx > 0) && (mStepBreakpointAddrs[0] != 0) && (isDeeper))
						{
							// This is the first time we've hit the target breakpoint.
							if (HasSteppedIntoCall())
							{
								mStepIsRecursing = true;
								RemoveBreakpoint(mStepBreakpointAddrs[0]);
								mStepBreakpointAddrs[0] = 0;
								//mStepBreakpointAddrs.erase(mStepBreakpointAddrs.begin());
							}
						}

						if ((mStepType == StepType_StepOver) && (mStepIsRecursing) && (stepBreakAddrIdx != -1) && (isDeeper))
						{
							// Decrement so the equality test on "step out" marks us as not being deeper when we
							//  hit the expected SP
							BfLogDbg("Converting StepOver to StepOut\n");
							mStepSP--;
							mStepType = StepType_StepOut_ThenInto;
							//SetupStep(StepType_StepOut);
							mRunState = RunState_Running;
							break;
						}

						if ((mStepType == StepType_StepOver) && (!ignoreBreakpoint) && (breakpoint == NULL) && (!mStepInAssembly))
						{
							// Test for stepping over inline method
							DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);

							// mTempBreakpoints will have 2 entries if we are on a 'call' line.  If we have an inlined call immediately following a call, then we
							//  assume we're hitting a return break
							/*if ((dwSubprogram != NULL) && (dwSubprogram->mInlineParent != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (mTempBreakpoint.size() < 2))
							{
								BfLogDbg("Attempting StepOver of inlined method\n");
								SetupStep(StepType_StepOut);
								mRunState = RunState_Running;
								break;
							} */

							//TODO: The previous logic with the "(mTempBreakpoint.size() < 2)" was causing Try!(Method()); stepovers to enter into Try!.  What did we mean by
							//  "assume we're hitting a return break"?
							if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
							{
								RemoveTempBreakpoints();
								BfLogDbg("Attempting StepOver of inlined method\n");
								SetupStep(StepType_StepOut);
								mRunState = RunState_Running;
								break;
							}
						}

						if (mStepType == StepType_StepOut_Inline)
						{
							if (mOrigStepType == StepType_StepOver)
							{
								// For the step over, if we are still inside the source line after an inline then step over again...
								DbgSubprogram* origSubprogram = NULL;
								auto origLineData = FindLineDataAtAddress(mStepStartPC, &origSubprogram);
								DbgSubprogram* curSubprogram = NULL;
								auto curLineData = FindLineDataAtAddress(pcAddress, &curSubprogram);
								if ((origLineData != NULL) &&
									((origLineData == curLineData) ||
									 ((origSubprogram == curSubprogram) && (origLineData->mLine == curLineData->mLine))))
								{
									mRunState = RunState_Running;
									SetupStep(StepType_StepOver);
									break;
								}
							}

							ClearStep();
							break;
						}

						if ((mStepType != StepType_None) && (ignoreBreakpoint) && (!mStepInAssembly) && (stepBreakAddrIdx == -1))
						{
							// Ignore breakpoint by just continuing...
							mRunState = RunState_Running;
							break;
						}

						RemoveTempBreakpoints();

						if ((mStepType != StepType_None) && (!mStepInAssembly)  && (!isLineStart) && (stepBreakAddrIdx != -1))
						{
							SetupStep(mStepType);
							mRunState = RunState_Running;
						}
						else
						{
							//if (mStepType != StepType_Return)
							if (stepBreakAddrIdx != -1)
							{
								// Even if we've detected we're at a breakpoint, we mark ourselves as just stepping if we also
								//  have a step breakpoint here
								StepLineTryPause(pcAddress, true);
							}

							if (mRunState == RunState_Paused)
								ClearStep();
						}

						if (ignoreBreakpoint)
						{
							SetupStep(mStepType);
							mRunState = RunState_Running;
						}

						if ((mRunState == RunState_Paused) && (breakpoint != NULL))
						{
							// Just do the 'check' here so we can do the logging/condition stuff
							CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress);
						}
					}
					else
					{
						breakpoint = (WdBreakpoint*)FindBreakpointAt((uintptr_t)exceptionRecord->ExceptionAddress);
						if ((breakpoint != NULL) && (!CheckConditionalBreakpoint(breakpoint, dwSubprogram, pcAddress)))
						{
							ClearCallStack();
							BfLogDbg("Skipping conditional breakpoint. Setting mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
							threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;
							mRunState = RunState_Running;
							break;
						}
						if (breakpoint != NULL)
						{
							BfLogDbg("Breakpoint hit. mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
							threadInfo->mIsAtBreakpointAddress = breakpoint->mAddr;
							mRunState = RunState_Breakpoint;
						}
						else if ((stepBreakAddrIdx != -1) || (isNonDebuggerBreak))
						{
							if (mRunState != RunState_DebugEval)
							{
								// Was in mStepBreakpointAddrs list
								if ((isNonDebuggerBreak) || (mStepType == StepType_None) || (mSteppingThread == mAtBreakThread))
								{
									BfLogDbg("Hit mStepBreakpointAddrs breakpoint\n");
									mRunState = RunState_Paused;
								}
								else
								{
									BfLogDbg("Ignored mStepBreakpointAddrs breakpoint (wrong thread)\n");
									mRunState = RunState_Running;
								}
							}
						}
						else
						{
							BfLogDbg("Ignoring break (old or ignored breakpoint)\n");
							mRunState = RunState_Running;
						}
					}

					if ((breakpoint != NULL) && (!ignoreBreakpoint))
					{
						mActiveBreakpoint = breakpoint;
						mBreakStackFrameIdx = -1;
					}

					if ((mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint))
						threadInfo->mStoppedAtAddress = pcAddress;
				}
				break;
			case STATUS_WX86_SINGLE_STEP:
			case EXCEPTION_SINGLE_STEP:
				{
					if (mRunState == RunState_Terminating)
					{
						BfLogDbg("Ignoring event because of RunState_Terminating\n");
						break;
					}

					if ((mStepSwitchedThreads) && (mActiveThread == mSteppingThread) && (mActiveThread->mIsAtBreakpointAddress != NULL))
					{
						ContinueFromBreakpoint();
						break;
					}

					if (mRunState == RunState_HotStep)
					{
						BF_ASSERT(mActiveThread == mDebuggerWaitingThread);
						mRunState = RunState_Paused;
						break;
					}

					mActiveThread = mDebuggerWaitingThread;

					BF_CONTEXT lcContext;
					lcContext.ContextFlags = BF_CONTEXT_ALL;
					BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
					addr_target pcAddress = BF_CONTEXT_IP(lcContext);

					bool wasUnfilteredStep = mStepType == StepType_StepInto_Unfiltered;
					if (mStepType == StepType_StepInto_UnfilteredSingle)
					{
						wasUnfilteredStep = true;
						mStepType = StepType_StepInto;
						mStepStartPC = pcAddress;
					}

					BfLogDbg("EXCEPTION_SINGLE_STEP Thread:%d PC:%p\n", mActiveThread->mThreadId, exceptionRecord->ExceptionAddress);

					if (lcContext.Dr6 & 0x0F) // Memory breakpoint hit
					{
						WdBreakpoint* foundBreakpoint = NULL;
						for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
						{
							if ((lcContext.Dr6 & ((intptr_target)1 << memoryWatchSlot)) != 0)
							{
								foundBreakpoint = mMemoryBreakpoints[memoryWatchSlot].mBreakpoint;
								break;
							}
						}

						BF_ASSERT(foundBreakpoint != NULL);

						DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
						if (CheckConditionalBreakpoint(foundBreakpoint, subprogram, pcAddress))
						{
							if (foundBreakpoint != NULL)
							{
								mDebugManager->mOutMessages.push_back(StrFormat("memoryBreak %s", EncodeDataPtr(foundBreakpoint->mMemoryBreakpointInfo->mMemoryAddress, false).c_str()));
								mRunState = RunState_Paused;
							}

							mActiveBreakpoint = foundBreakpoint;
							mBreakStackFrameIdx = -1;
							RemoveTempBreakpoints();
							BfLogDbg("Memory breakpoint hit: %p\n", foundBreakpoint);
						}
						else
							ClearCallStack();
						break;
					}

					if ((mRunState == RunState_DebugEval) && (mDebugEvalThreadInfo.mThreadId == mDebuggerWaitingThread->mThreadId))
					{
						if ((addr_target)(intptr)exceptionRecord->ExceptionAddress == mDebugEvalSetRegisters.GetPC())
						{
							// This indicates we are returning from kernel mode and our registers are clobbered
							SetRegisters(&mDebugEvalSetRegisters);
						}
						break;
					}

					bool hadBreakpointContinue = true;
					if (threadInfo->mBreakpointAddressContinuing != 0)
					{
						bool wantsBreakpoint = WantsBreakpointAt(threadInfo->mBreakpointAddressContinuing);
						BfLogDbg("Continuing breakpoint at %p WantsReset:%d\n", threadInfo->mBreakpointAddressContinuing, wantsBreakpoint);

						if (wantsBreakpoint)
						{
							PhysSetBreakpoint(threadInfo->mBreakpointAddressContinuing);
						}
						threadInfo->mBreakpointAddressContinuing = NULL;
						hadBreakpointContinue = true;

						ThreadRestoreUnpause();
					}

					if ((mSteppingThread != NULL) && (mSteppingThread != mActiveThread))
					{
						// This SINGLE_STEP happened in the wrong thread - we need the stepping thread to do the stepping!
						//  Try again.
						mActiveThread = mSteppingThread;
						SingleStepX86();
						break;
					}

					bool isDeeper = mStepSP > BF_CONTEXT_SP(lcContext);
					if ((mStepSwitchedThreads) && (mStepType == StepType_StepOver) && (isDeeper))
					{
						if (HasSteppedIntoCall())
						{
							// Since we switched threads, we needed to do a hardware step which has placed us inside a
							//  call, so we need to step out of that now...
							SetupStep(StepType_StepOut_NoFrame);
							break;
						}
					}

					// If we don't have a mStepBreakpointAddrs set, that means we're stepping through individual instructions --
					//  so process the new location here
					if (((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered) || (mStepType == StepType_StepOver)) && (mStepBreakpointAddrs.size() == 0))
					{
						DbgSubprogram* dwSubprogram = NULL;
						DbgLineData* dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);

						if ((dwSubprogram != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced))
						{
							BfLogDbg("Stepping through hot thunk\n");
							mRunState = RunState_Running;
							SingleStepX86();
							break;
						}

						if ((mStepType == StepType_StepOver) && (!mStepInAssembly))
						{
							if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC))
							{
								BfLogDbg("Attempting StepOver of inlined method - SingleStep\n");
								SetupStep(StepType_StepOut);
								mRunState = RunState_Running;
								break;
							}
						}

						// Column of -1 means "Illegal", keep stepping!
						if ((mStepInAssembly) ||
							((dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dwLineData->mColumn >= 0) &&
								((dwSubprogram->GetLineAddr(*dwLineData) == pcAddress) || (mStepStopOnNextInstruction))))
						{
							// Hit a line while stepping, we're done!
							mRunState = RunState_Paused;
							StepLineTryPause(pcAddress, false);
							if (mRunState == RunState_Paused)
							{
								if ((mStepType == StepType_StepInto) && (!wasUnfilteredStep) && (!mStepInAssembly) && (dwSubprogram != NULL))
								{
									// Don't filter out the current subprogram (would break cases where we explicitly stepped into or hit breakpoint in a filtered subprogram)
									bool isInStartSubprogram = (mStepStartPC >= dwSubprogram->mBlock.mLowPC) && (mStepStartPC < dwSubprogram->mBlock.mHighPC);
									if ((!isInStartSubprogram) && (IsStepFiltered(dwSubprogram, dwLineData)))
									{
										BfLogDbg("Hit step filter (2)\n");
										mRunState = RunState_Running;
										SetupStep(StepType_StepOut_ThenInto);
										break;
									}
								}

								ClearStep();
								mCurNoInfoStepTries = 0; // Reset
							}
							else
								SetupStep(mStepType);
						}
						else if (dwSubprogram != NULL)
						{
							if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && ((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered)))
							{
								SingleStepX86();
							}
							else
							{
								// Inside a line's instruction, keep going
								SetupStep(mStepType);
								mCurNoInfoStepTries = 0; // Reset
							}
						}
						else if (mStepType == StepType_StepInto_Unfiltered)
						{
							CPUInst inst;
							if (mDebugTarget->DecodeInstruction(pcAddress, &inst))
							{
								if (inst.IsBranch())
								{
									auto target = inst.GetTarget();
									if (target != 0)
									{
										DbgSubprogram* destSubprogram = mDebugTarget->FindSubProgram(target);
										if ((destSubprogram != NULL) && (target == destSubprogram->mBlock.mLowPC))
										{
											// We're jumping to an actual subprogram, so continue stepping here
											mStepType = StepType_StepInto_UnfilteredSingle;
											SingleStepX86();
											break;
										}
									}
								}
							}

							// We requested to step into this method so stop here even if we don't have source
							mRunState = RunState_Paused;
						}
						else
						{
							// No debug info!
							bool doStepOut = false;
							if (mCurNoInfoStepTries < 16)
							{
								mCurNoInfoStepTries++;
								BfLogDbg("NoInfoStepTries: %d\n", mCurNoInfoStepTries);
								if (!SetupStep(mStepType))
									doStepOut = true;
							}
							else
								doStepOut = true;
							if (doStepOut)
							{
								// Step out of current call.
								mStepSP = 0;
								SetupStep(StepType_StepOut_NoFrame);
								// Aggressive stepout - don't monitor BP
								mStepSP = 0;
							}
						}
					}
					else if (!hadBreakpointContinue)
					{
						BF_DBG_FATAL("EXCEPTION_SINGLE_STEP bad debugger state");
					}

					if (mRunState == RunState_Paused)
						threadInfo->mStoppedAtAddress = pcAddress;
				}
				break;
			default:
				{
					bool isSystemException =
						(exceptionRecord->ExceptionCode >= STATUS_ACCESS_VIOLATION) &&
						(exceptionRecord->ExceptionCode <= STATUS_ASSERTION_FAILURE);

					bool isFirstChance = mDebugEvent.u.Exception.dwFirstChance != 0;
					bool handled = false;

					//TODO: Use a user-defined filter here to determine whether to stop or continue
					if (isFirstChance)
					{
						if (exceptionRecord->ExceptionCode == 0x406D1388) // Visual C
						{
							if ((int32)exceptionRecord->ExceptionInformation[0] == 0x1000)
							{
								struct THREADNAME_INFO
								{
									DWORD dwType; // Must be 0x1000.
									LPCSTR szName; // Pointer to name (in user addr space).
									DWORD dwThreadID; // Thread ID (-1=caller thread).
									DWORD dwFlags; // Reserved for future use, must be zero.
								};

								THREADNAME_INFO* threadNameInfo = (THREADNAME_INFO*)exceptionRecord->ExceptionInformation;

								DwFormatInfo formatInfo;
								formatInfo.mRawString = true;
								String nameStr = ReadString(DbgType_SChar, (intptr)threadNameInfo->szName, false, 1024, formatInfo, false);

								WdThreadInfo* namingThreadInfo = threadInfo;
								if (threadNameInfo->dwThreadID != (DWORD)-1)
								{
									namingThreadInfo = NULL;
									mThreadMap.TryGetValue(threadNameInfo->dwThreadID, &namingThreadInfo);
								}

								if (namingThreadInfo != NULL)
								{
									namingThreadInfo->mName = nameStr;
									FilterThreadName(namingThreadInfo->mName);
								}
							}
							else if (((int32)exceptionRecord->ExceptionInformation[0] == 0x1001) && ((int32)exceptionRecord->ExceptionInformation[1] == 0x1002))
							{
								struct FailMessage
								{
									addr_target mPtr0; // Unknown
									addr_target mPtr1; // 0
									addr_target mPtr2; // 0
									addr_target mPtr3; // Unknown
									addr_target mErrorStr;
								};

								FailMessage failMessage = ReadMemory<FailMessage>(exceptionRecord->ExceptionInformation[2]);
								DwFormatInfo formatInfo;
								String failStr = ReadString(DbgType_SChar16, failMessage.mErrorStr, false, 8192, formatInfo, false);

								mDebugManager->mOutMessages.push_back(StrFormat("error Run-Time Check Failure %d - %s", exceptionRecord->ExceptionInformation[6], failStr.c_str()));
								mRunState = RunState_Paused;
								mRequestedStackFrameIdx = -2; // -2 = "auto"
								handled = true;
							}
						}

						if (!handled)
						{
							if (mRunState != RunState_DebugEval)
								OutputMessage(StrFormat("Skipping first chance exception %08X at address %@ in thread %d\n", exceptionRecord->ExceptionCode, exceptionRecord->ExceptionAddress, threadInfo->mThreadId));
							::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_EXCEPTION_NOT_HANDLED);
							mIsDebuggerWaiting = false;
						}
					}
					else
					{
						BfLogDbg("EXCEPTION in thread %d at %p\n", threadInfo->mThreadId, exceptionRecord->ExceptionAddress);
						OutputDebugStrF("EXCEPTION\n");
						mActiveThread = threadInfo;
						memcpy(&mCurException, exceptionRecord, sizeof(EXCEPTION_RECORD));

						if (mRunState == RunState_DebugEval)
						{
							if ((intptr)mCurException.ExceptionAddress == 42)
							{
								BfLogDbg("RunState_DebugEval_Done\n");
								OutputDebugStrF(" RunState_DebugEval_Done\n");
							}
							else
							{
								BfLogDbg("Exception at 0x%@ in thread %d, exception code 0x%08X",
									mCurException.ExceptionAddress, mActiveThread->mThreadId, mCurException.ExceptionCode);
								mDebugPendingExpr->mException = StrFormat("Exception at 0x%@ in thread %d, exception code 0x%08X",
									mCurException.ExceptionAddress, mActiveThread->mThreadId, mCurException.ExceptionCode);
							}
							mRunState = RunState_DebugEval_Done;

							mExplicitStopThread = mActiveThread;
							mRequestedStackFrameIdx = mDebugPendingExpr->mCallStackIdx;
						}
						else
						{
							mRunState = RunState_Exception;
						}
					}
				}
				break;
			}
		}
		break;
	}

	if ((mDebugEvalThreadInfo.mThreadId != 0) && (mRunState != RunState_DebugEval) && (mRunState != RunState_DebugEval_Done))
	{
		CleanupDebugEval();
	}

	// Stepping done?
	if (mStepType == StepType_None)
	{
		mLastValidStepIntoPC = 0;
	}

	BF_ASSERT(mDebuggerWaitingThread != NULL);
	return true;
}

void WinDebugger::Update()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

// 	if (mRunState == RunState_DebugEval)
// 		ContinueDebugEvent();
	if (mRunState == RunState_DebugEval_Done)
	{
		if (mDebugPendingExpr != NULL)
		{
			mDebugPendingExpr->mIdleTicks++;
			if (mDebugPendingExpr->mIdleTicks >= 2)
			{
				BfLogDbg("Finishing pending expr in thread %d\n", mDebugEvalThreadInfo.mThreadId);

				mRunState = RunState_Paused;
				CleanupDebugEval();
			}
		}
	}
	else if (mDebugPendingExpr != NULL)
	{
		mDebugPendingExpr->mIdleTicks = 0;
	}
}

void WinDebugger::ContinueDebugEvent()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BF_ASSERT(mRunState != RunState_DebugEval_Done);

	if (!mWantsDebugContinue)
		return;

	if (!TryRunContinue())
		return;

// 	if ((mRunState == RunState_DebugEval) && (mDebuggerWaitingThread->mThreadId != mDebugEvalThreadInfo.mThreadId))
// 	{
// 		// Don't process the 'mIsAtBreakpointAddress' stuff
// 		mWantsDebugContinue = false;
// 		mContinueEvent.Set();
// 		return;
// 	}

	if ((mDebuggerWaitingThread->mIsAtBreakpointAddress == 0) && (mDebuggerWaitingThread->mStoppedAtAddress != 0))
	{
		auto breakpoint = FindBreakpointAt(mDebuggerWaitingThread->mStoppedAtAddress);
		if (breakpoint != NULL)
		{
			mDebuggerWaitingThread->mIsAtBreakpointAddress = mDebuggerWaitingThread->mStoppedAtAddress;
		}
	}

	if (mDebuggerWaitingThread->mIsAtBreakpointAddress != 0)
	{
		if (!ContinueFromBreakpoint())
		{
			BfLogDbg("ContinueFromBreakpoint failed\n");

			ClearCallStack();
			mDebuggerWaitingThread->mStoppedAtAddress = 0;
			mDebuggerWaitingThread->mIsAtBreakpointAddress = 0;
			mWantsDebugContinue = false;
			mContinueFromBreakpointFailed = true;
			mContinueEvent.Set();
			return;
		}
	}

	if ((mRunState == RunState_Breakpoint) || (mRunState == RunState_Paused) || (mRunState == RunState_TargetUnloaded))
	{
		ClearCallStack();
		mRunState = RunState_Running;
	}

	mDebuggerWaitingThread->mStoppedAtAddress = 0;
	mWantsDebugContinue = false;
	BF_ASSERT_REL(mDebuggerWaitingThread->mIsAtBreakpointAddress == 0);
	mContinueEvent.Set();
}

static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
{
	int wantProcessId = lParam;

	HWND owner = GetWindow(hwnd, GW_OWNER);
	if (!IsWindowVisible(hwnd))
		return TRUE;

	DWORD processId = 0;
	DWORD threadId = GetWindowThreadProcessId(hwnd, &processId);

	if (processId != wantProcessId)
		return TRUE;
	
	while (true)
	{
		HWND parentHWnd = GetParent(hwnd);
		if (parentHWnd != NULL)
		{
			hwnd = parentHWnd;
			continue;
		}
		SetForegroundWindow(hwnd);
		break;
	}
	
	return TRUE;
}

void WinDebugger::ForegroundTarget(int altProcessId)
{
	int wantProcessId = altProcessId;
	if (wantProcessId == 0)
		wantProcessId = ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId;

	HWND hwnd = ::GetForegroundWindow();
	if (hwnd != INVALID_HANDLE_VALUE)
	{
		DWORD processId = 0;
		GetWindowThreadProcessId(hwnd, &processId);
		if (processId == ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
			return; // Already good
	}
	
	EnumWindows(WdEnumWindowsProc, wantProcessId);
}

static int gFindLineDataAt = 0;

DbgLineData* WinDebugger::FindLineDataAtAddress(addr_target address, DbgSubprogram** outSubProgram, DbgSrcFile** outSrcFile, int* outLineIdx, DbgOnDemandKind onDemandKind)
{
	gFindLineDataAt++;

	BP_ZONE("WinDebugger::FindLineDataAtAddress");

	auto dwSubprogram = mDebugTarget->FindSubProgram((addr_target)address, onDemandKind);
	if (dwSubprogram == NULL)
		return NULL;

	FixupLineDataForSubprogram(dwSubprogram);
	auto lineData = dwSubprogram->FindClosestLine(address, outSubProgram, outSrcFile, outLineIdx);

	return lineData;
}

DbgLineData* WinDebugger::FindLineDataInSubprogram(addr_target address, DbgSubprogram* dwSubprogram)
{
	auto dwCompileUnit = dwSubprogram->mCompileUnit;

	FixupLineDataForSubprogram(dwSubprogram);
	auto lineData = dwSubprogram->FindClosestLine(address);
	return lineData;
}

bool WinDebugger::IsStepFiltered(DbgSubprogram* dbgSubprogram, DbgLineData* dbgLineData)
{
	if (mIsStepIntoSpecific)
		return false;

	if (dbgSubprogram->mStepFilterVersion != mDebugManager->mStepFilterVersion)
	{
		String filterName;
		CreateFilterName(filterName, dbgSubprogram);
		dbgSubprogram->PopulateSubprogram();

		bool doDefault = false;

		StepFilter* stepFilterPtr;
		if (mDebugManager->mStepFilters.TryGetValue(filterName, &stepFilterPtr))
		{
			switch (stepFilterPtr->mFilterKind)
			{
			case BfStepFilterKind_Default:
				doDefault = true;
				break;
			case BfStepFilterKind_Filtered:
				dbgSubprogram->mIsStepFiltered = true;
				break;
			case BfStepFilterKind_NotFiltered:
				dbgSubprogram->mIsStepFiltered = false;
				break;
			}
		}
		else
		{
			doDefault = true;
		}

		if (doDefault)
		{
			dbgSubprogram->mIsStepFiltered = dbgSubprogram->mIsStepFilteredDefault;
		}

		dbgSubprogram->mStepFilterVersion = mDebugManager->mStepFilterVersion;
	}

	if (!dbgSubprogram->mIsStepFiltered)
	{
		if (dbgLineData != NULL)
		{
			auto dbgSrcFile = dbgSubprogram->GetLineSrcFile(*dbgLineData);
			if (dbgSrcFile->mStepFilterVersion != mDebugManager->mStepFilterVersion)
			{
				dbgSrcFile->mFileExistKind = dbgSubprogram->mCompileUnit->mDbgModule->CheckSourceFileExist(dbgSrcFile->GetLocalPath());
				dbgSrcFile->mStepFilterVersion = mDebugManager->mStepFilterVersion;
			}

			switch (dbgSrcFile->mFileExistKind)
			{
			case DbgFileExistKind_NotFound:
				return true;
			case DbgFileExistKind_HasOldSourceCommand:
				if (mDebugManager->mStepOverExternalFiles)
					return true;
			}
		}
	}

	return dbgSubprogram->mIsStepFiltered;
}

void WinDebugger::RemoveTempBreakpoints()
{
	BfLogDbg("RemoveTempBreakpoints\n");

	for (auto address : mTempBreakpoint)
	{
		RemoveBreakpoint(address);
// 		if (FindBreakpointAt(address) == NULL)
// 		{
// 			RemoveBreakpoint(address);
// 		}
// 		else
// 		{
// 			BfLogDbg("Ignoring remove on temp breakpoint %p\n", address);
// 		}
	}
	mTempBreakpoint.Clear();
	mStepBreakpointAddrs.Clear();
}

void WinDebugger::RehupBreakpoints(bool doFlush)
{
	BfLogDbg("RehupBreakpoints\n");

	// First pass- detach breakpoints that need to be rebound
	for (int i = 0; i < (int)mBreakpoints.size(); i++)
	{
		auto breakpoint = mBreakpoints[i];
		while (breakpoint != NULL)
		{
			if (((breakpoint->mSrcFile != NULL) && (breakpoint->mSrcFile->mDeferredRefs.size() > 0)) ||
				(!breakpoint->mSymbolName.IsEmpty()))
			{
				// This breakpoint was already bound, but we loaded a debug module that also had this file so rebind it
				DetachBreakpoint(breakpoint);
			}

			breakpoint = (WdBreakpoint*)breakpoint->mLinkedSibling;
		}
	}

	// Second pass- actually set breakpoints
	for (int i = 0; i < (int)mBreakpoints.size(); i++)
	{
		auto breakpoint = mBreakpoints[i];
		while (breakpoint != NULL)
		{
			CheckBreakpoint(breakpoint);
 			if (breakpoint->mAddr != 0)
 				SetBreakpoint(breakpoint->mAddr, true);
			breakpoint = (WdBreakpoint*)breakpoint->mLinkedSibling;
		}
	}

	mNeedsRehupBreakpoints = false;
}

bool WinDebugger::WantsBreakpointAt(addr_target address)
{
	if (mTempBreakpoint.Contains(address))
		return true;
	for (auto breakpoint : mBreakpoints)
	{
		WdBreakpoint* checkBreakpoint = breakpoint;
		while (checkBreakpoint != NULL)
		{
			if (address == checkBreakpoint->mAddr)
				return true;
			checkBreakpoint = (WdBreakpoint*)checkBreakpoint->mLinkedSibling;
		}
	}
	return false;
}

void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint, DbgSrcFile* srcFile, int lineNum, int hotIdx)
{
	BP_ZONE("WinDebugger::CheckBreakpoint:atLoc");

	if (hotIdx == -1)
	{
		BF_ASSERT(wdBreakpoint->mPendingHotBindIdx == -1);
	}
	WdBreakpoint* headBreakpoint = wdBreakpoint;
	headBreakpoint->mPendingHotBindIdx = -1;

	bool foundInSequence = false;
	DbgSubprogram* lastFoundSubprogram = NULL;

	int highestHotIdx = -1;
	bool foundLine = false;

	int bestLineNum = -1;
	int bestLineOffset = 0x7FFFFFFF;

	auto _CheckLineInfo = [&](DbgSubprogram* dbgSubprogram, DbgLineInfo* dbgLineInfo)
	{
		// Scan first so we can determine if we want to do fix up line data or not.
		bool hasNear = false;
		int maxLineDist = 6;

		for (int lineIdx = 0; lineIdx < dbgLineInfo->mLines.mSize; lineIdx++)
		{
			auto lineData = &dbgLineInfo->mLines[lineIdx];
			auto& ctx = dbgLineInfo->mContexts[lineData->mCtxIdx];
			if (ctx.mSrcFile != srcFile)
				continue;

			int lineOffset = lineData->mLine - lineNum;
			if ((lineOffset >= 0) && (lineOffset <= maxLineDist))
				hasNear = true;
		}

		if (!hasNear)
			return;

		FixupLineDataForSubprogram(dbgSubprogram);

		for (int lineIdx = 0; lineIdx < dbgLineInfo->mLines.mSize; lineIdx++)
		{
			//TODO: Do fixup lineData... ?
			auto lineData = &dbgLineInfo->mLines[lineIdx];
			auto& ctx = dbgLineInfo->mContexts[lineData->mCtxIdx];
			if (ctx.mSrcFile != srcFile)
				continue;

// 			if (ctx.mInlinee != NULL)
// 			{
// 				if (lineIdx + 1 < dbgLineInfo->mLines.mSize)
// 				{
// 					auto nextLineData = &dbgLineInfo->mLines[lineIdx + 1];
// 					if (nextLineData->mRelAddress == lineData->mRelAddress)
// 					{
// 						// Use the later entry (same logic from DisassembleAt)
// 						continue;
// 					}
// 				}
// 			}

			if ((lineData->mColumn == -1) && (wdBreakpoint->mInstrOffset == -1))
				continue;

			int lineOffset = lineData->mLine - lineNum;

			if (lineOffset == 0)
			{
				foundLine = true;

				auto address = dbgSubprogram->GetLineAddr(*lineData);
				auto subProgram = mDebugTarget->FindSubProgram(address);
				if (subProgram->mNeedLineDataFixup)
					FixupLineDataForSubprogram(subProgram);

				if (subProgram != NULL)
					highestHotIdx = BF_MAX(highestHotIdx, subProgram->mCompileUnit->mDbgModule->mHotIdx);

				if ((foundInSequence) && (subProgram != lastFoundSubprogram))
					foundInSequence = false;

				if ((subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address < subProgram->mBlock.mLowPC + sizeof(HotJumpOp)))
				{
					// If this breakpoint ends up on the hot jmp instruction
					continue;
				}

				if (!foundInSequence)
				{
					lastFoundSubprogram = subProgram;

					if ((subProgram != NULL) && (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address == subProgram->mBlock.mLowPC))
					{
						// This instruction is actually the hot jump, we don't need a breakpoint here
						foundInSequence = true;
						continue;
					}

					if (wdBreakpoint->mSrcFile != NULL)
					{
						wdBreakpoint = new WdBreakpoint();
						// Insert at head
						wdBreakpoint->mLinkedSibling = headBreakpoint->mLinkedSibling;
						headBreakpoint->mLinkedSibling = wdBreakpoint;
						wdBreakpoint->mRequestedLineNum = headBreakpoint->mRequestedLineNum;
						wdBreakpoint->mLineNum = headBreakpoint->mLineNum;
						wdBreakpoint->mColumn = headBreakpoint->mColumn;
						wdBreakpoint->mInstrOffset = headBreakpoint->mInstrOffset;
						wdBreakpoint->mIsLinkedSibling = true;
						wdBreakpoint->mHead = headBreakpoint;
					}

					if (wdBreakpoint->mInstrOffset > 0)
					{
						for (int instIdx = 0; instIdx < wdBreakpoint->mInstrOffset; instIdx++)
						{
							CPUInst inst;
							if (!mDebugTarget->DecodeInstruction(address, &inst))
								break;
							address += inst.mSize;
						}
					}

					wdBreakpoint->mSrcFile = ctx.mSrcFile;
					wdBreakpoint->mLineData = DbgLineDataEx(lineData, subProgram);
					wdBreakpoint->mBreakpointType = BreakpointType_User;
					wdBreakpoint->mAddr = address;

					if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mStoppedAtAddress == address))
					{
						BfLogDbg("CheckBreakpoint setting mIsAtBreakpointAddress = %p\n", address);
						mDebuggerWaitingThread->mIsAtBreakpointAddress = address;
					}

					BfLogDbg("Breakpoint %p found at %s in %s\n", wdBreakpoint, subProgram->mName, GetFileName(subProgram->mCompileUnit->mDbgModule->mFilePath).c_str());

					mBreakpointAddrMap.ForceAdd(address, wdBreakpoint);
					SetBreakpoint(address);

					foundInSequence = true;
				}
			}
			else
			{
				//TODO: We didn't have this here, but if we don't have this then there are some cases where the method-closing brace generates code in
				// multiple places so we need to ensure this will break on them all
				foundInSequence = false;
			}

			if ((lineOffset >= 0) && (lineOffset <= maxLineDist) && (lineOffset <= bestLineOffset))
			{
				if (lineOffset < bestLineOffset)
				{
					bestLineNum = lineData->mLine;
					bestLineOffset = lineOffset;
				}
			}
		}
	};

	for (int pass = 0; pass < 2; pass++)
	{
		if (lineNum == -1)
			break;

		bestLineNum = -1;
		bestLineOffset = 0x7FFFFFFF;

		if (hotIdx >= 0)
		{
			if (hotIdx >= srcFile->mHotReplacedDbgLineInfo.size())
				return;

			auto hotReplacedLineInfo = srcFile->mHotReplacedDbgLineInfo[hotIdx];
			for (auto& hotReplacedEntry : hotReplacedLineInfo->mEntries)
			{
				_CheckLineInfo(hotReplacedEntry.mSubprogram, hotReplacedEntry.mLineInfo);
			}
		}
		else
		{
			for (auto subprogram : srcFile->mLineDataRefs)
				_CheckLineInfo(subprogram, subprogram->mLineInfo);
		}

		if (foundLine)
			break;

		// Don't allow the breakpoint to be inexactly bound -- only match on pass 0
		if (hotIdx != -1)
			break;

		if (bestLineNum == -1)
			break;

		lineNum = bestLineNum;
		wdBreakpoint->mLineNum = bestLineNum;
	}

	int highestCheckHotIdx = highestHotIdx - 1;
	if (hotIdx != -1)
		highestCheckHotIdx = hotIdx - 1;

	for (int hotFileIdx = highestCheckHotIdx; hotFileIdx >= 0; hotFileIdx--)
	{
		auto& hotReplacedDbgLineData = wdBreakpoint->mSrcFile->mHotReplacedDbgLineInfo;
		// Only try to bind to an old hot version if we haven't unloaded the hot module
		if ((hotFileIdx < (int)hotReplacedDbgLineData.size()) && (hotReplacedDbgLineData[hotFileIdx]->mEntries.size() > 0))
		{
			headBreakpoint->mPendingHotBindIdx = hotFileIdx;
			break;
		}
	}
}

void WinDebugger::HotBindBreakpoint(Breakpoint* breakpoint, int lineNum, int hotIdx)
{
	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
	CheckBreakpoint(wdBreakpoint, wdBreakpoint->mSrcFile, lineNum, hotIdx);
}

void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint)
{
	if (!mGotStartupEvent)
		return;

	if (wdBreakpoint->mThreadId == 0) // Not bound to threadId yet...
	{
		return;
	}

	if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
	{
		if (wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap != 0)
			return;

		if (mFreeMemoryBreakIndices.size() == 0)
			return;

		if ((IsInRunState()) || (mActiveThread == NULL))
			return;

		int wantBytes[4];
		int wantBindCount = 0;
		int bytesLeft = wdBreakpoint->mMemoryBreakpointInfo->mByteCount;
		addr_target curAddr = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress;
		while (bytesLeft > 0)
		{
			if (wantBindCount >= mFreeMemoryBreakIndices.size())
				return;

			int curByteCount = 1;
#ifdef BF_DBG_64
			if ((bytesLeft >= 8) && ((curAddr & 7) == 0))
				curByteCount = 8;
			else
#endif
			if ((bytesLeft >= 4) && ((curAddr & 3) == 0))
				curByteCount = 4;
			else if ((bytesLeft >= 2) && ((curAddr & 1) == 0))
				curByteCount = 2;

			wantBytes[wantBindCount++] = curByteCount;
			bytesLeft -= curByteCount;
			curAddr += curByteCount;
		}

		addr_target curOfs = 0;
		for (int i = 0; i < wantBindCount; i++)
		{
			int memoryBreakIdx = mFreeMemoryBreakIndices.back();
			mFreeMemoryBreakIndices.pop_back();

			mMemoryBreakpoints[memoryBreakIdx].mBreakpoint = wdBreakpoint;
			mMemoryBreakpoints[memoryBreakIdx].mAddress = wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress + curOfs;
			mMemoryBreakpoints[memoryBreakIdx].mByteCount = wantBytes[i];
			mMemoryBreakpoints[memoryBreakIdx].mOfs = curOfs;
			curOfs += wantBytes[i];
			mMemoryBreakpointVersion++;

			wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap |= 1<<memoryBreakIdx;
		}

		UpdateThreadDebugRegisters();
	}

	if (wdBreakpoint->mAddr != 0)
		return;

	if (!wdBreakpoint->mSymbolName.IsEmpty())
	{
		auto headBreakpoint = wdBreakpoint->GetHeadBreakpoint();

		String symbolName = wdBreakpoint->mSymbolName;
		bool onlyBindFirst = false;
		if (symbolName.StartsWith("-"))
		{
			symbolName.Remove(0);
			onlyBindFirst = true;
		}

		for (auto dbgModule : mDebugTarget->mDbgModules)
		{
			dbgModule->ParseSymbolData();

			addr_target targetAddr = -1;
			auto entry = dbgModule->mSymbolNameMap.Find(symbolName.c_str());
			if (entry != NULL)
			{
				DbgSymbol* dwSymbol = entry->mValue;
				targetAddr = dwSymbol->mAddress;
			}

			if (targetAddr == -1)
			{
				if (symbolName == ".")
				{
					targetAddr = mDebugTarget->mLaunchBinary->mImageBase + mDebugTarget->mLaunchBinary->mEntryPoint;
					onlyBindFirst = true;
				}
			}

			if (targetAddr != -1)
			{
				if (wdBreakpoint->mAddr == 0)
				{
					wdBreakpoint->mAddr = targetAddr;
					wdBreakpoint->mBreakpointType = BreakpointType_User;
					mBreakpointAddrMap.ForceAdd(wdBreakpoint->mAddr, wdBreakpoint);
					SetBreakpoint(wdBreakpoint->mAddr);
				}
				else
				{
					wdBreakpoint = new WdBreakpoint();
					// Insert at head
					wdBreakpoint->mLinkedSibling = headBreakpoint->mLinkedSibling;
					headBreakpoint->mLinkedSibling = wdBreakpoint;
					wdBreakpoint->mSymbolName = headBreakpoint->mSymbolName;
					wdBreakpoint->mIsLinkedSibling = true;
					wdBreakpoint->mHead = headBreakpoint;
				}

				if (onlyBindFirst)
					break;
			}
		}
		return;
	}

	BP_ZONE("WinDebugger::CheckBreakpoint");

	// Rehup if we load a DLL that also uses this file we bound to (thus the mDeferredRefs check)
	if (wdBreakpoint->mSrcFile == NULL)
	{
		DbgSrcFile* srcFile = mDebugTarget->GetSrcFile(wdBreakpoint->mFilePath);
		if (srcFile == NULL)
			return;

		for (auto& deferredSrcFileRef : srcFile->mDeferredRefs)
		{
			deferredSrcFileRef.mDbgModule->ParseCompileUnit(deferredSrcFileRef.mCompileUnitId);
		}
		srcFile->mDeferredRefs.Clear();

		CheckBreakpoint(wdBreakpoint, srcFile, wdBreakpoint->mRequestedLineNum, -1);
	}
}

bool WinDebugger::IsMemoryBreakpointSizeValid(addr_target addr, int size)
{
	int wantBindCount = 0;
	int bytesLeft = size;
	addr_target curAddr = addr;

	for (int i = 0; i < 4; i++)
	{
		int curByteCount = 1;
#ifdef BF_DBG_64
		if ((bytesLeft >= 8) && ((curAddr & 7) == 0))
			curByteCount = 8;
		else
#endif
		if ((bytesLeft >= 4) && ((curAddr & 3) == 0))
			curByteCount = 4;
		else if ((bytesLeft >= 2) && ((curAddr & 1) == 0))
			curByteCount = 2;
		bytesLeft -= curByteCount;
		curAddr += curByteCount;

		if (bytesLeft == 0)
			return true;
	}

	return false;
}

bool WinDebugger::HasMemoryBreakpoint(addr_target addr, int size)
{
	for (int i = 0; i < 4; i++)
	{
		if ((mMemoryBreakpoints[i].mAddress == addr) &&
			(mMemoryBreakpoints[i].mOfs == 0) &&
			(mMemoryBreakpoints[i].mBreakpoint->mMemoryBreakpointInfo->mByteCount == size))
			return true;
	}

	return false;
}

Breakpoint* WinDebugger::CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("CreateBreakpoint %s %d %d\n", fileName.c_str(), lineNum, wantColumn);

	WdBreakpoint* wdBreakpoint = new WdBreakpoint();
	wdBreakpoint->mFilePath = FixPathAndCase(fileName);
	wdBreakpoint->mRequestedLineNum = lineNum;
	wdBreakpoint->mLineNum = lineNum;
	wdBreakpoint->mColumn = wantColumn;
	wdBreakpoint->mInstrOffset = instrOffset;
	mBreakpoints.push_back(wdBreakpoint);

	BfLogDbg("CreateBreakpoint Created %p\n", wdBreakpoint);

	return wdBreakpoint;
}

void WinDebugger::CheckBreakpoint(Breakpoint* checkBreakpoint)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	CheckBreakpoint((WdBreakpoint*)checkBreakpoint);
}

Breakpoint* WinDebugger::CreateMemoryBreakpoint(intptr addr, int byteCount)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("CreateMemoryBreakpoint %p %d\n", addr, byteCount);

	WdBreakpoint* wdBreakpoint = new WdBreakpoint();

	WdMemoryBreakpointInfo* memoryBreakInfo = new WdMemoryBreakpointInfo();
	memoryBreakInfo->mMemoryAddress = addr;
	memoryBreakInfo->mByteCount = byteCount;
	wdBreakpoint->mMemoryBreakpointInfo = memoryBreakInfo;
	mBreakpoints.push_back(wdBreakpoint);
	CheckBreakpoint(wdBreakpoint);
	return wdBreakpoint;
}

Breakpoint* WinDebugger::CreateSymbolBreakpoint(const StringImpl& symbolName)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("CreateSymbolBreakpoint %s\n", symbolName.c_str());

	WdBreakpoint* wdBreakpoint = new WdBreakpoint();
	wdBreakpoint->mSymbolName = symbolName;
	mBreakpoints.push_back(wdBreakpoint);
	CheckBreakpoint(wdBreakpoint);
	return wdBreakpoint;
}

Breakpoint* WinDebugger::CreateAddressBreakpoint(intptr inAddress)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("CreateAddressBreakpoint %p\n", inAddress);

	addr_target address = (addr_target)inAddress;
	WdBreakpoint* wdBreakpoint = new WdBreakpoint();
	wdBreakpoint->mAddr = address;
	mBreakpointAddrMap.ForceAdd(wdBreakpoint->mAddr, wdBreakpoint);
	SetBreakpoint(address);

	if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mStoppedAtAddress == address))
	{
		BfLogDbg("CreateAddressBreakpoint setting mIsAtBreakpointAddress = %p\n", address);
		mDebuggerWaitingThread->mIsAtBreakpointAddress = address;
	}

	mBreakpoints.push_back(wdBreakpoint);
	return wdBreakpoint;
}

void WinDebugger::DeleteBreakpoint(Breakpoint* breakpoint)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;

	while (wdBreakpoint != NULL)
	{
		BfLogDbg("WinDebugger::DeleteBreakpoint %p Count:%d\n", wdBreakpoint, mBreakpoints.size());

		if (wdBreakpoint == mActiveBreakpoint)
			mActiveBreakpoint = NULL;

		if (wdBreakpoint->mCondition != NULL)
		{
			if (!wdBreakpoint->mIsLinkedSibling)
				delete wdBreakpoint->mCondition;
		}

		if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
		{
			for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
			{
				if (mMemoryBreakpoints[memoryWatchSlot].mBreakpoint == wdBreakpoint)
				{
					mFreeMemoryBreakIndices.push_back(memoryWatchSlot);
					mMemoryBreakpoints[memoryWatchSlot] = WdMemoryBreakpointBind();
					mMemoryBreakpointVersion++;
					UpdateThreadDebugRegisters();
				}
			}

			wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap = 0;
		}

		if (wdBreakpoint->mAddr != 0)
		{
			mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);
			RemoveBreakpoint(wdBreakpoint->mAddr);

			for (auto thread : mThreadList)
			{
				if (thread->mIsAtBreakpointAddress == wdBreakpoint->mAddr)
					thread->mIsAtBreakpointAddress = NULL;
				if (thread->mBreakpointAddressContinuing == wdBreakpoint->mAddr)
					thread->mBreakpointAddressContinuing = NULL;
			}
		}

		if (!wdBreakpoint->mIsLinkedSibling)
		{
			mBreakpoints.Remove(wdBreakpoint);
		}

		auto nextBreakpoint = (WdBreakpoint*)wdBreakpoint->mLinkedSibling;
		delete wdBreakpoint;

		wdBreakpoint = nextBreakpoint;
	}
}

void WinDebugger::DetachBreakpoint(Breakpoint* breakpoint)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("WinDebugger::DetachBreakpoint %p\n", breakpoint);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;

	if (wdBreakpoint->mAddr != 0)
	{
		mBreakpointAddrMap.Remove(wdBreakpoint->mAddr, wdBreakpoint);
		RemoveBreakpoint(wdBreakpoint->mAddr);
		if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mIsAtBreakpointAddress == wdBreakpoint->mAddr))
			mDebuggerWaitingThread->mIsAtBreakpointAddress = NULL;
		if ((mDebuggerWaitingThread != NULL) && (mDebuggerWaitingThread->mBreakpointAddressContinuing == wdBreakpoint->mAddr))
			mDebuggerWaitingThread->mBreakpointAddressContinuing = NULL;
		wdBreakpoint->mLineData = DbgLineDataEx();
		wdBreakpoint->mAddr = 0;
	}

	if (wdBreakpoint->mCondition != NULL)
	{
		delete wdBreakpoint->mCondition->mDbgEvaluationContext;
		wdBreakpoint->mCondition->mDbgEvaluationContext = NULL;
	}

	if (wdBreakpoint->mMemoryBreakpointInfo != NULL)
	{
		for (int memoryWatchSlot = 0; memoryWatchSlot < 4; memoryWatchSlot++)
		{
			if (mMemoryBreakpoints[memoryWatchSlot].mBreakpoint == wdBreakpoint)
			{
				mFreeMemoryBreakIndices.push_back(memoryWatchSlot);
				mMemoryBreakpoints[memoryWatchSlot] = WdMemoryBreakpointBind();
				mMemoryBreakpointVersion++;
				UpdateThreadDebugRegisters();
			}
		}

		wdBreakpoint->mMemoryBreakpointInfo->mMemoryWatchSlotBitmap = 0;
	}

	if (wdBreakpoint->mLinkedSibling != NULL)
	{
		DeleteBreakpoint(wdBreakpoint->mLinkedSibling);
		wdBreakpoint->mLinkedSibling = NULL;
	}

	wdBreakpoint->mSrcFile = NULL;
	wdBreakpoint->mPendingHotBindIdx = -1;
}

void WinDebugger::MoveBreakpoint(Breakpoint* breakpoint, int lineNum, int wantColumn, bool rebindNow)
{
	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;

	AutoCrit autoCrit(mDebugManager->mCritSect);

	DetachBreakpoint(wdBreakpoint);

	//TODO: This doesn't actually rebind correctly while the app is running
	if ((lineNum != -1) && (wantColumn != -1))
	{
		wdBreakpoint->mRequestedLineNum = lineNum;
		wdBreakpoint->mLineNum = lineNum;
		wdBreakpoint->mColumn = wantColumn;
	}
	if (rebindNow)
		CheckBreakpoint(wdBreakpoint);
}

void WinDebugger::MoveMemoryBreakpoint(Breakpoint* breakpoint, intptr addr, int byteCount)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
	DetachBreakpoint(wdBreakpoint);
	wdBreakpoint->mMemoryBreakpointInfo->mMemoryAddress = addr;
	wdBreakpoint->mMemoryBreakpointInfo->mByteCount = byteCount;
	CheckBreakpoint(wdBreakpoint);
}

void WinDebugger::DisableBreakpoint(Breakpoint* breakpoint)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
	DetachBreakpoint(wdBreakpoint);

	delete wdBreakpoint->mMemoryBreakpointInfo;
	wdBreakpoint->mMemoryBreakpointInfo = NULL;
}

void WinDebugger::SetBreakpointCondition(Breakpoint* breakpoint, const StringImpl& conditionExpr)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
	BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);

	if (conditionExpr.empty())
	{
		delete wdBreakpoint->mCondition;
		WdBreakpoint* curBreakpoint = wdBreakpoint;
		wdBreakpoint->mCondition = NULL;
	}
	else
	{
		delete wdBreakpoint->mCondition;
		auto condition = new WdBreakpointCondition();
		condition->mExpr = conditionExpr;
		wdBreakpoint->mCondition = condition;
	}
}

void WinDebugger::SetBreakpointLogging(Breakpoint* breakpoint, const StringImpl& logging, bool breakAfterLogging)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	WdBreakpoint* wdBreakpoint = (WdBreakpoint*)breakpoint;
	BF_ASSERT(!wdBreakpoint->mIsLinkedSibling);
	wdBreakpoint->mLogging = logging;
	wdBreakpoint->mBreakAfterLogging = breakAfterLogging;
}

bool WinDebugger::CheckConditionalBreakpoint(WdBreakpoint* breakpoint, DbgSubprogram* dbgSubprogram, addr_target pcAddress)
{
	// What was this assertion for?
	//BF_ASSERT(mCallStack.size() == 0);

	auto headBreakpoint = breakpoint->GetHeadBreakpoint();

	if (headBreakpoint->mThreadId != -1)
	{
		if ((mActiveThread != NULL) && (mActiveThread->mThreadId != headBreakpoint->mThreadId))
			return false;
	}

	auto _SplitExpr = [&](const StringImpl& expr, StringImpl& outExpr, StringImpl& outSubject)
	{
		int crPos = expr.IndexOf('\n');
		if (crPos != -1)
		{
			outExpr += expr.Substring(0, crPos);
			outSubject += expr.Substring(crPos + 1);
		}
		else
		{
			outExpr += expr;
		}
	};

	if (headBreakpoint->mCondition != NULL)
	{
		ClearCallStack();

		auto conditional = headBreakpoint->mCondition;
		if (conditional->mDbgEvaluationContext == NULL)
		{
			CPURegisters registers;
			PopulateRegisters(&registers);
			auto pcAddress = registers.GetPC();
			DbgSubprogram* subprogram = mDebugTarget->FindSubProgram(pcAddress);
			if (subprogram == NULL)
			{
				return false;
			}

			StringT<256> expr;
			StringT<256> subjectExpr;
			if (breakpoint->mMemoryBreakpointInfo != NULL)
			{
				subjectExpr += "*";
			}

			_SplitExpr(conditional->mExpr, expr, subjectExpr);
			DbgLanguage language = DbgLanguage_Unknown;
			if (expr.StartsWith("@Beef:"))
			{
				expr.Remove(0, 6);
				language = DbgLanguage_Beef;
			}
			else if (expr.StartsWith("@C:"))
			{
				expr.Remove(0, 3);
				language = DbgLanguage_C;
			}

			conditional->mDbgEvaluationContext = new DbgEvaluationContext(this, subprogram->mCompileUnit->mDbgModule, expr);
			if (language != DbgLanguage_Unknown)
				conditional->mDbgEvaluationContext->mDbgExprEvaluator->mLanguage = language;
			conditional->mDbgEvaluationContext->mDbgExprEvaluator->mSubjectExpr = subjectExpr;
			conditional->mDbgEvaluationContext->mDbgExprEvaluator->mDbgCompileUnit = subprogram->mCompileUnit;
			conditional->mDbgEvaluationContext->mDbgExprEvaluator->mCallStackIdx = 0;
			conditional->mDbgEvaluationContext->mDbgExprEvaluator->mExpressionFlags = (DwEvalExpressionFlags)(DwEvalExpressionFlag_AllowSideEffects);
		}

		WdStackFrame* wdStackFrame = new WdStackFrame();
		PopulateRegisters(&wdStackFrame->mRegisters);
		mCallStack.Add(wdStackFrame);
		DbgTypedValue result = conditional->mDbgEvaluationContext->EvaluateInContext(DbgTypedValue());
		ClearCallStack();

		if ((result.mType != NULL) && (result.mType->mTypeCode == DbgType_Bitfield))
			result.mType = result.mType->mTypeParam;

		if (conditional->mDbgEvaluationContext->mPassInstance->HasFailed())
		{
			String errorStr = "FAILED";
			for (auto error : conditional->mDbgEvaluationContext->mPassInstance->mErrors)
			{
				if (!error->mIsWarning)
					errorStr = error->mError;
			}
			String condError = StrFormat("error Conditional breakpoint expression '%s' failed: %s", conditional->mExpr.c_str(), errorStr.c_str());
			mDebugManager->mOutMessages.push_back(condError);
			return true;
		}
		else if (conditional->mDbgEvaluationContext->mDbgExprEvaluator->mBlockedSideEffects)
		{
			mDebugManager->mOutMessages.push_back(StrFormat("error Conditional breakpoint expression '%s' contained function calls, which is not allowed", conditional->mExpr.c_str()));
			return true;
		}
		else if ((!result) || (!result.mType->IsBoolean()))
		{
			mDebugManager->mOutMessages.push_back(StrFormat("error Conditional breakpoint expression '%s' must result in a boolean value", conditional->mExpr.c_str()));
			return true;
		}
		else if (!result.mBool)
			return false;
	}

	headBreakpoint->mHitCount++;
	switch (headBreakpoint->mHitCountBreakKind)
	{
	case DbgHitCountBreakKind_Equals:
		if (headBreakpoint->mHitCount != headBreakpoint->mTargetHitCount)
			return false;
		break;
	case DbgHitCountBreakKind_GreaterEquals:
		if (headBreakpoint->mHitCount < headBreakpoint->mTargetHitCount)
			return false;
		break;
	case DbgHitCountBreakKind_Multiple:
		if ((headBreakpoint->mHitCount % headBreakpoint->mTargetHitCount) != 0)
			return false;
		break;
	}

	mActiveBreakpoint = breakpoint;
	mBreakStackFrameIdx = -1;
	if (!headBreakpoint->mLogging.IsEmpty())
	{
		ClearCallStack();

		DwFormatInfo formatInfo;
		formatInfo.mCallStackIdx = 0;

		DbgCompileUnit* dbgCompileUnit = NULL;
		if (dbgSubprogram == NULL)
			dbgSubprogram = mDebugTarget->FindSubProgram(pcAddress);
		if (dbgSubprogram != NULL)
		{
			dbgCompileUnit = dbgSubprogram->mCompileUnit;
			formatInfo.mLanguage = dbgSubprogram->GetLanguage();
		}

		auto prevRunState = mRunState;
		mRunState = RunState_Paused; // We need to be paused to avoid certain errors in the eval
		String displayString;

		String expr;
		_SplitExpr(headBreakpoint->mLogging, expr, formatInfo.mSubjectExpr);

		if (expr.StartsWith("@Script:"))
		{
			displayString = "script ";
			displayString += expr.Substring(8);
		}
		else
		{
			if (expr.StartsWith("@Beef:"))
			{
				expr.Remove(0, 6);
				formatInfo.mLanguage = DbgLanguage_Beef;
			}
			else if (expr.StartsWith("@C:"))
			{
				expr.Remove(0, 3);
				formatInfo.mLanguage = DbgLanguage_C;
			}

			ProcessEvalString(dbgCompileUnit, DbgTypedValue(), expr, displayString, formatInfo, NULL, false);
			mRunState = prevRunState;

			displayString.Insert(0, "log ");
			displayString.Append("\n");
		}

		mDebugManager->mOutMessages.push_back(displayString);

		if (!headBreakpoint->mBreakAfterLogging)
			return false;
	}

	return true;
}

void WinDebugger::CleanupDebugEval(bool restoreRegisters)
{
	BfLogDbg("CleanupDebugEval ThreadId=%d\n", mDebugEvalThreadInfo.mThreadId);

	WdThreadInfo* evalThreadInfo = NULL;
	if (mThreadMap.TryGetValue(mDebugEvalThreadInfo.mThreadId, &evalThreadInfo))
	{
		if ((restoreRegisters) && (!mDbgBreak))
		{
			SetAndRestoreValue<WdThreadInfo*> activeThread(mActiveThread, evalThreadInfo);
			RestoreAllRegisters();
// 			if (mRunState == RunState_Running_ToTempBreakpoint)
// 				mRunState = RunState_Paused;
		}

		evalThreadInfo->mStartSP = mDebugEvalThreadInfo.mStartSP;
		evalThreadInfo->mStoppedAtAddress = mDebugEvalThreadInfo.mStoppedAtAddress;
		evalThreadInfo->mIsAtBreakpointAddress = mDebugEvalThreadInfo.mIsAtBreakpointAddress;
		evalThreadInfo->mBreakpointAddressContinuing = mDebugEvalThreadInfo.mBreakpointAddressContinuing;
	}

	delete mDebugPendingExpr;
	mDebugPendingExpr = NULL;
	mDebugEvalThreadInfo = WdThreadInfo();

	OutputRawMessage("rehupLoc");
}

bool WinDebugger::FixCallStackIdx(int& callStackIdx)
{
	callStackIdx = BF_MAX(callStackIdx, 0);
	if (mCallStack.IsEmpty())
		UpdateCallStack();

	int stackSize = (int)mCallStack.size();
	while (callStackIdx >= mCallStack.size())
	{
		UpdateCallStack();
		if (stackSize == (int)mCallStack.size())
			break; // Didn't change
		stackSize = (int)mCallStack.size();
	}

	if (callStackIdx >= stackSize)
	{
		callStackIdx = 0;
		return false;
	}
	return true;
}

bool WinDebugger::HasLineInfoAt(addr_target address)
{
	BP_ZONE("WinDebugger::HasLineInfoAt");

	DbgSubprogram* dbgSubprogram = NULL;
	auto dwLineData = FindLineDataAtAddress(address, &dbgSubprogram);
	return (dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dbgSubprogram->GetLineAddr(*dwLineData) == address);
}

void WinDebugger::StepLineTryPause(addr_target address, bool requireExactMatch)
{
	if (mStepInAssembly)
		return;

	if (mStepLineData.mLineData != NULL)
	{
		DbgSubprogram* dbgSubprogram = NULL;
		DbgSrcFile* dbgSrcFile = NULL;
		auto dwLineData = FindLineDataAtAddress(address, &dbgSubprogram, &dbgSrcFile);
		if ((dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && ((!requireExactMatch) || (dbgSubprogram->GetLineAddr(*dwLineData) == address)))
		{
			// "Invalid" line
			if (dwLineData->mColumn == -1)
			{
				SetupStep(mStepType);
				mRunState = RunState_Running;
				return;
			}

			// If we're on the same line but a different column or a <= address then keep it keep looking
			if ((dbgSrcFile == mStepLineData.GetSrcFile()) &&
				((!requireExactMatch) || (dwLineData != mStepLineData.mLineData) || (address <= mStepStartPC)) &&
				(dwLineData->mLine == mStepLineData.mLineData->mLine))
			{
				SetupStep(mStepType);
				mRunState = RunState_Running;
				return;
			}
		}
	}

	mRunState = RunState_Paused;
}

void WinDebugger::BreakAll()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	::DebugBreakProcess(mProcessInfo.hProcess);
}

void WinDebugger::StepInto(bool inAssembly)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!TryRunContinue())
		return;

	BfLogDbg("StepInto\n");

	mCurNoInfoStepTries = 0; // Reset
	mStepInAssembly = inAssembly;

	SetupStep(StepType_StepInto);
	ContinueDebugEvent();
}

void WinDebugger::StepIntoSpecific(intptr inAddr)
{
	addr_target addr = (addr_target)inAddr;

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!TryRunContinue())
		return;

	BfLogDbg("StepIntoSpecific %p\n", addr);

	mCurNoInfoStepTries = 0; // Reset
	mStepInAssembly = false;

	SetupStep(StepType_StepInto);
	mIsStepIntoSpecific = true;
	mStepType = StepType_StepInto_Unfiltered;
	if (mStepStartPC != addr)
	{
		RemoveTempBreakpoints();
		SetTempBreakpoint(addr);
		mStepBreakpointAddrs.push_back(addr);
	}

	ContinueDebugEvent();
}

void WinDebugger::PushValue(CPURegisters* registers, int64 val)
{
	addr_target* regSP = registers->GetSPRegisterRef();
	*regSP -= sizeof(addr_target);
	WriteMemory<addr_target>(*regSP, (addr_target)val);
}

void WinDebugger::PushValue(CPURegisters* registers, const DbgTypedValue& typedValue)
{
	addr_target* regSP = registers->GetSPRegisterRef();

	int byteCount = typedValue.mType->GetByteCount();
	if ((byteCount == 8) || (sizeof(addr_target) == 8))
	{
		*regSP -= sizeof(int64);

		addr_target val = typedValue.mInt64;
		if (typedValue.mType->IsCompositeType())
			val = typedValue.mSrcAddress;
		WriteMemory<int64>(*regSP, val);
	}
	else
	{
		*regSP -= sizeof(int32);
		addr_target val = typedValue.mInt32;
		if (typedValue.mType->IsCompositeType())
			val = typedValue.mSrcAddress;
		WriteMemory<int32>(*regSP, val);
	}
}

void WinDebugger::SetThisRegister(CPURegisters* registers, addr_target val)
{
#if BF_DBG_32
	registers->mIntRegs.ecx = val;
#else
	registers->mIntRegs.rcx = val;
#endif
}

void WinDebugger::AddParamValue(int paramIdx, bool hadThis, CPURegisters* registers, const DbgTypedValue& typedValue)
{
#if BF_DBG_32
	PushValue(registers, typedValue);
#else
	int regIdx = paramIdx + (hadThis ? 1 : 0);
	if (typedValue.mType->IsFloat())
	{
		PushValue(registers, typedValue);
		if (regIdx < 4)
		{
			if (typedValue.mType->mTypeCode == DbgType_Single)
			{
				registers->mXmmRegsArray[regIdx].f[0] = typedValue.mSingle;
			}
			else
			{
				registers->mXmmDRegsArray[regIdx].d[0] = typedValue.mDouble;
			}
		}
	}
	else
	{
		PushValue(registers, typedValue);
		if (regIdx < 4)
		{
			int64 val;
			if (typedValue.mType->IsCompositeType())
            	val = typedValue.mSrcAddress;
			else
				val = typedValue.mPtr;
			if (regIdx == 0)
				registers->mIntRegs.rcx = val;
			else if (regIdx == 1)
				registers->mIntRegs.rdx = val;
			else if (regIdx == 2)
				registers->mIntRegs.r8 = val;
			else if (regIdx == 3)
				registers->mIntRegs.r9 = val;
		}
	}
#endif
}

bool WinDebugger::CheckNeedsSRetArgument(DbgType* retType)
{
	if (!retType->IsCompositeType())
		return false;

	if (retType->GetByteCount() == 0)
		return false;

	//TODO: Change when we change the calling convention
	if (retType->GetLanguage() == DbgLanguage_Beef)
		return true;

	int retSize = retType->GetByteCount();
	//TODO: Check for 'POD' type?
	if ((retSize == 1) || (retSize == 2) || (retSize == 4) || (retSize == sizeof(addr_target)))
		return false;
	return true;
}

DbgTypedValue WinDebugger::ReadReturnValue(CPURegisters* registers, DbgType* type)
{
	DbgTypedValue retValue;

	if (type->IsFloat())
	{
		retValue.mType = type;
#if BF_DBG_32
		retValue.mDouble = ConvertFloat80ToDouble(registers->mFpMmRegsArray[0].fp.fp80);
		if (type->mSize == 4)
			retValue.mSingle = (float)retValue.mDouble;
#else
		if (retValue.mType->mTypeCode == DbgType_Single)
			retValue.mSingle = registers->mXmmRegsArray[0].f[0];
		else
			retValue.mDouble = registers->mXmmDRegsArray[0].d[0];
#endif
	}
	else if (type->IsCompositeType())
	{
		retValue.mType = type;
		if (CheckNeedsSRetArgument(type))
		{
#ifdef BF_DBG_32
			retValue.mSrcAddress = mSavedContext.Esp - BF_ALIGN(type->GetByteCount(), 16);
#else
			retValue.mSrcAddress = mSavedContext.Rsp - BF_ALIGN(type->GetByteCount(), 16);
#endif
		}
		else
		{
#ifdef BF_DBG_32
			retValue.mInt32 = mSavedContext.Eax;
#else
			retValue.mInt64 = mSavedContext.Rax;
#endif
		}
	}
	else
	{
#ifdef BF_DBG_32
		retValue.mType = type;
		retValue.mInt32 = registers->mIntRegs.eax;
		if (type->mSize == 8)
			(&retValue.mInt32)[1] = registers->mIntRegs.edx;
#else
		retValue.mType = type;
		retValue.mInt64 = registers->mIntRegs.rax;
#endif
		return retValue;
	}

	return retValue;
}

bool WinDebugger::SetRegisters(CPURegisters* registers)
{
	BF_CONTEXT lcContext;
	lcContext.ContextFlags = BF_CONTEXT_CONTROL | BF_CONTEXT_INTEGER | BF_CONTEXT_FLOATING_POINT | BF_CONTEXT_EXTENDED_REGISTERS | BF_CONTEXT_SEGMENTS;
	lcContext.ContextFlags |= BF_CONTEXT_EXCEPTION_REQUEST;

	BF_GetThreadContext(mActiveThread->mHThread, &lcContext);

#ifdef BF_DBG_32
	lcContext.Eax = registers->mIntRegs.eax;
	lcContext.Ecx = registers->mIntRegs.ecx;
	lcContext.Edx = registers->mIntRegs.edx;
	lcContext.Ebx = registers->mIntRegs.ebx;
	lcContext.Esp = registers->mIntRegs.esp;
	lcContext.Ebp = registers->mIntRegs.ebp;
	lcContext.Esi = registers->mIntRegs.esi;
	lcContext.Edi = registers->mIntRegs.edi;
	lcContext.Eip = registers->mIntRegs.eip;
	lcContext.EFlags = registers->mIntRegs.efl;

	BF_ASSERT(sizeof(lcContext.FloatSave.RegisterArea) == sizeof(registers->mFpMmRegsArray));
	memcpy(lcContext.FloatSave.RegisterArea, registers->mFpMmRegsArray, sizeof(lcContext.FloatSave.RegisterArea));

	BF_ASSERT(sizeof(registers->mXmmRegsArray) == 32*sizeof(float));
	memcpy(&lcContext.ExtendedRegisters[160], registers->mXmmRegsArray, sizeof(registers->mXmmRegsArray));
#else
	lcContext.Rax = registers->mIntRegs.rax;
	lcContext.Rcx = registers->mIntRegs.rcx;
	lcContext.Rdx = registers->mIntRegs.rdx;
	lcContext.Rbx = registers->mIntRegs.rbx;
	lcContext.Rsp = registers->mIntRegs.rsp;
	lcContext.Rbp = registers->mIntRegs.rbp;
	lcContext.Rsi = registers->mIntRegs.rsi;
	lcContext.Rdi = registers->mIntRegs.rdi;
	lcContext.Rip = registers->mIntRegs.rip;
	lcContext.EFlags = (DWORD)registers->mIntRegs.efl;

	lcContext.R8 = registers->mIntRegs.r8;
	lcContext.R9 = registers->mIntRegs.r9;
	lcContext.R10 = registers->mIntRegs.r10;
	lcContext.R11 = registers->mIntRegs.r11;
	lcContext.R12 = registers->mIntRegs.r12;
	lcContext.R13 = registers->mIntRegs.r13;
	lcContext.R14 = registers->mIntRegs.r14;
	lcContext.R15 = registers->mIntRegs.r15;

	for (int i = 0; i < 8; i++)
	{
		memcpy(&lcContext.FltSave.FloatRegisters[i], &registers->mFpMmRegsArray[i], 10);
	}

	BF_ASSERT(sizeof(registers->mXmmRegsArray) == 64 * sizeof(float));
	memcpy(BF_CONTEXT_XMMDATA(lcContext), registers->mXmmRegsArray, sizeof(registers->mXmmRegsArray));
#endif

	//lcContext.ContextFlags |= BF_CONTEXT_EXCEPTION_REQUEST;

	BF_SetThreadContext(mActiveThread->mHThread, &lcContext);

	return (lcContext.ContextFlags & (BF_CONTEXT_EXCEPTION_ACTIVE | BF_CONTEXT_SERVICE_ACTIVE)) == 0;
}

void WinDebugger::SaveAllRegisters()
{
	BfLogDbg("SaveAllRegisters setting mSavedAtBreakpointAddress = %p\n", mActiveThread->mIsAtBreakpointAddress);
	mSavedAtBreakpointAddress = mActiveThread->mIsAtBreakpointAddress;
	mSavedBreakpointAddressContinuing = mActiveThread->mBreakpointAddressContinuing;
	mSavedContext.ContextFlags = BF_CONTEXT_ALL;
	BF_GetThreadContext(mActiveThread->mHThread, &mSavedContext);
}

void WinDebugger::RestoreAllRegisters()
{
	BfLogDbg("RestoreAllRegisters setting mIsAtBreakpointAddress = %p\n", mSavedAtBreakpointAddress);
	mActiveThread->mIsAtBreakpointAddress = mSavedAtBreakpointAddress;
	mActiveThread->mBreakpointAddressContinuing = mSavedBreakpointAddressContinuing;
	BF_SetThreadContext(mActiveThread->mHThread, &mSavedContext);

#ifdef BF_DBG_32
	//TODO: Find the test that this was required for...
// 	if (mActiveThread->mIsAtBreakpointAddress == mSavedContext.Eip)
// 	{
// 		if (mRunState == RunState_Running_ToTempBreakpoint)
// 			mRunState = RunState_Paused;
// 	}
// 	else
// 	{
// 		SetTempBreakpoint(mSavedContext.Eip);
// 		mRunState = RunState_Running_ToTempBreakpoint;
// 		mStepType = StepType_ToTempBreakpoint;
// 		mSteppingThread = mActiveThread;
// 	}
#endif
}

void WinDebugger::OutputMessage(const StringImpl& msg)
{
	if (this == NULL)
		return;
	AutoCrit autoCrit(mDebugManager->mCritSect);
	mDebugManager->mOutMessages.push_back("msg " + msg);
}

void WinDebugger::OutputRawMessage(const StringImpl& msg)
{
	if (this == NULL)
		return;
	AutoCrit autoCrit(mDebugManager->mCritSect);
	mDebugManager->mOutMessages.push_back(msg);
}

void WinDebugger::SetRunState(RunState runState)
{
	mRunState = runState;
}

bool WinDebugger::TryRunContinue()
{
	if (mRunState == RunState_Exception)
	{
		mIsContinuingFromException = true;
		mRunState = RunState_Paused;
	}

	if (((mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint)) && (mNeedsRehupBreakpoints))
		RehupBreakpoints(true);

	return true;
}

void WinDebugger::ClearStep()
{
	BfLogDbg("ClearStep\n");

	RemoveTempBreakpoints();
	mOrigStepType = StepType_None;
	mStepType = StepType_None;
	mStepStartPC = 0;
	mStepSP = 0;
	mStepPC = 0;
	mIsStepIntoSpecific = false;
	mStepIsRecursing = false;
	mStepStopOnNextInstruction = false;
	mStepLineData = DbgLineDataEx();
}

bool WinDebugger::SetupStep(StepType stepType)
{
	BP_ZONE("SetupStep");

	RemoveTempBreakpoints();
	if (mNeedsRehupBreakpoints)
		RehupBreakpoints(true);

	if (mOrigStepType == StepType_None)
		mOrigStepType = stepType;
	mStepType = stepType;
	mSteppingThread = mActiveThread;
	mStepSwitchedThreads = false;
	mContinueFromBreakpointFailed = false;

	CPURegisters registers;
	PopulateRegisters(&registers);
	addr_target pcAddress = registers.GetPC();

	if (mStepLineData.IsNull())
	{
		DbgSubprogram* dbgSubprogram = NULL;
		auto dbgLineData = FindLineDataAtAddress(pcAddress, &dbgSubprogram);
		mStepLineData = DbgLineDataEx(dbgLineData, dbgSubprogram);
		mStepStartPC = registers.GetPC();
	}

	bool isDeeper = mStepSP > registers.GetSP();

	BfLogDbg("SetupStep %d PC:%p SP:%p StepStartSP:%p Thread:%d\n", stepType, (addr_target)registers.GetPC(), (addr_target)registers.GetSP(), (addr_target)mStepSP, mSteppingThread->mThreadId);

	mStepSP = registers.GetSP();
	mStepPC = registers.GetPC();

	if ((mStepType == StepType_StepOut) || (mStepType == StepType_StepOut_NoFrame) || (mStepType == StepType_StepOut_ThenInto))
	{
		if (mStepType != StepType_StepOut_NoFrame)
		{
			// Test for stepping out of an inline method
			DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);
			if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL))
			{
				DbgSubprogram* topSubprogram = dwSubprogram->GetRootInlineParent();

				if ((mOrigStepType == StepType_StepInto) || (mOrigStepType == StepType_StepInto_Unfiltered))
				{
					mStepType = mOrigStepType;
				}
				else
				{
					mStepType = StepType_StepOut_Inline;
					// Set up pcAddress to detect recursion
					//TODO: We can't set a physical breakpoint here because we will immediately hit it when attempting to step over an inlined method.
					//  An inlined method can't recurse anyway, but store the pcAddress in mTempBreakpoints because we still check that for recursion
					// SetTempBreakpoint(pcAddress);
					//mTempBreakpoint.push_back(pcAddress);
					mStepBreakpointAddrs.push_back(pcAddress);
				}

				addr_target endAddress = dwSubprogram->mBlock.mHighPC;
				if (dwSubprogram->mHasLineAddrGaps)
				{
					// Keep bumping out the address as long as we can find lines that contain the nextPC
					addr_target nextAddr = pcAddress;
					for (auto& lineInfo : topSubprogram->mLineInfo->mLines)
					{
						auto lineAddr = topSubprogram->GetLineAddr(lineInfo);
						if ((nextAddr >= lineAddr) && (nextAddr < lineAddr + lineInfo.mContribSize))
						{
							auto ctx = topSubprogram->mLineInfo->mContexts[lineInfo.mCtxIdx];
							if (ctx.mInlinee == dwSubprogram)
							{
								nextAddr = lineAddr + lineInfo.mContribSize;
							}
						}
					}
					if (nextAddr != pcAddress)
						endAddress = nextAddr;
				}

				BfLogDbg("Stepping out of inlined method, end address: %p\n", endAddress);
				SetTempBreakpoint(endAddress);
				mStepBreakpointAddrs.push_back(endAddress);

				addr_target decodeAddress = dwSubprogram->mBlock.mLowPC;
				while (decodeAddress < endAddress)
				{
					CPUInst inst;
					if (!mDebugTarget->DecodeInstruction(decodeAddress, &inst))
						break;

					addr_target targetAddress = inst.GetTarget();
					// We need to find a targetAddress
					if ((targetAddress != 0) &&
						!((targetAddress >= dwSubprogram->mBlock.mLowPC) && (targetAddress < dwSubprogram->mBlock.mHighPC)) &&
						((targetAddress >= topSubprogram->mBlock.mLowPC) && (targetAddress < topSubprogram->mBlock.mHighPC)))
					{
						BfLogDbg("Stepping out of inlined method, branch address: %p\n", targetAddress);
						SetTempBreakpoint(targetAddress);
						mStepBreakpointAddrs.push_back(targetAddress);
					}

					decodeAddress += inst.GetLength();
				}

				return true;
			}
		}

		if ((mStepType != StepType_StepOut_NoFrame) && (RollBackStackFrame(&registers, true)))
		{
			bool isStackAdjust = false;
			DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress);
			if (dwSubprogram != NULL)
			{
				if ((strcmp(dwSubprogram->mName, "_chkstk") == 0) ||
					(strcmp(dwSubprogram->mName, "__chkstk") == 0) ||
					(strcmp(dwSubprogram->mName, "_alloca_probe") == 0))
					isStackAdjust = true;
			}

			pcAddress = registers.GetPC();
			if (isStackAdjust)
			{
				// We set it to zero so we never detect an "isDeeper" condition which would skip over the return-location breakpoint
				mStepSP = 0;
			}
			else
			{
				addr_target oldAddress = pcAddress;

				CPUInst inst;
				while (true)
				{
					if (mStepInAssembly)
						break;
					if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
						break;
					if ((inst.IsBranch()) || (inst.IsCall()) || (inst.IsReturn()))
						break;
#ifdef BF_DBG_32
					if (!inst.StackAdjust(mStepSP))
						break;
#endif
					DbgSubprogram* checkSubprogram = NULL;
					auto checkLineData = FindLineDataAtAddress(pcAddress, &checkSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
					if (checkLineData == NULL)
						break;
					if (checkSubprogram->GetLineAddr(*checkLineData) == pcAddress)
						break;
					pcAddress += inst.GetLength();
				}

				if (pcAddress != oldAddress)
				{
					BfLogDbg("Adjusting stepout address from %p to %p\n", oldAddress, pcAddress);
				}
			}

			BfLogDbg("SetupStep Stepout SetTempBreakpoint %p\n", pcAddress);
			SetTempBreakpoint(pcAddress);
			mStepBreakpointAddrs.push_back(pcAddress);
			if (mStepType != StepType_StepOut_ThenInto)
				mStepType = StepType_StepOut;
		}
		else
		{
			// Try to handle the case where we just entered this call so the return address is the first entry on the stack
			addr_target* regSP = registers.GetSPRegisterRef();
			pcAddress = ReadMemory<addr_target>(*regSP);
			*regSP += sizeof(addr_target);
			if (mDebugTarget->FindSubProgram(pcAddress) != NULL)
			{
				BfLogDbg("SetupStep Stepout SetTempBreakpoint (2) %p\n", pcAddress);

				SetTempBreakpoint(pcAddress);
				mStepBreakpointAddrs.push_back(pcAddress);
				if (mOrigStepType == StepType_StepInto)
					mStepType = StepType_StepInto;
				else
					mStepType = StepType_StepOver;
				return true;
			}
			else
			{
				// Just do stepovers until we eventually step out
				//BF_DBG_FATAL("StepOut Failed");
				BfLogDbg("StepOut Failed\n");

				if (mLastValidStepIntoPC != 0)
				{
					BfLogDbg("Using mLastValidStepIntoPC: %p\n", mLastValidStepIntoPC);
					if (mOrigStepType == StepType_StepInto)
						mStepType = StepType_StepInto;
					else
						mStepType = StepType_StepOver;
					SetTempBreakpoint(mLastValidStepIntoPC);
					mStepBreakpointAddrs.push_back(0);
					mStepBreakpointAddrs.push_back(mLastValidStepIntoPC);
					mLastValidStepIntoPC = 0;
					return true;
				}
				else
				{
					BfLogDbg("Stopping");
					mStepType = StepType_None;
					mRunState = RunState_Paused;
					return true;
				}
			}
		}
	}

	if ((mStepType != StepType_StepOut) && (mStepType != StepType_StepOut_ThenInto))
	{
		if (mDebuggerWaitingThread != mSteppingThread)
		{
			// We've switched threads, so there's a possible race condition:
			//  This new thread may already have an EXCEPTION_BREAKPOINT queued up so the PC is actually
			//  located one byte past the BREAK instruction, which is one byte into whatever instruction
			//  was previously there.  We can't insert normal BREAK instructions because we don't know
			//  if the current PC is actually at an instruction start, so we do a single step with a
			//  slower stack call check to see if we need to step out after a "step over"
			BfLogDbg("Step - switched threads mIsAtBreakpointAddress:%p\n", mSteppingThread->mIsAtBreakpointAddress);
			mStepSwitchedThreads = true;
			SingleStepX86();
			return true;
		}

		bool breakOnNext = false;

		int instIdx = 0;
		for (instIdx = 0; true; instIdx++)
		{
			bool isAtLine = false;
			DbgSubprogram* dwSubprogram = NULL;

			auto dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
			isAtLine = (instIdx > 0) && (dwLineData != NULL) && (dwLineData->IsStackFrameSetup()) && (dwSubprogram->GetLineAddr(*dwLineData) == pcAddress);

			// "Never step into" line
			if ((dwLineData != NULL) && (dwLineData->mColumn == -2) && (stepType == StepType_StepInto))
				stepType = StepType_StepOver;

			CPUInst inst;
			if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
			{
				BfLogDbg("Decode failed, set up SingleStepX86 %p\n", pcAddress);
				SingleStepX86();
				mStepStopOnNextInstruction = true;
				return true;
			}

			if (instIdx > 256)
			{
				BfLogDbg("Too many SetupStep iterations");
				breakOnNext = true;
			}

			if ((inst.IsReturn()) && (instIdx == 0) && (!mStepInAssembly))
			{
				// Do actual STEP OUT so we set up proper "stepping over unimportant post-return instructions"
				if (stepType == StepType_StepInto)
					return SetupStep(StepType_StepOut_ThenInto);
				else
					return SetupStep(StepType_StepOut);
			}

			if ((breakOnNext) || (mStepInAssembly) || (isAtLine) || (inst.IsBranch()) || (inst.IsCall()) || (inst.IsReturn()))
			{
				if (((instIdx == 0) || (mStepInAssembly)) && (!breakOnNext))
				{
					if ((stepType == StepType_StepOver) && (inst.IsCall()))
					{
						// Continue - sets a breakpoint on the call line to detect recursion.
						//  The next loop through will set a breakpoint on the line after the return
						BfLogDbg("StepHadCall\n");
						breakOnNext = true;

						BfLogDbg("StepHadCall setting mIsAtBreakpointAddress = %p\n", pcAddress);
						mSteppingThread->mIsAtBreakpointAddress = pcAddress;

						SetTempBreakpoint(pcAddress);
						mStepBreakpointAddrs.push_back(pcAddress);
					}
					else
					{
						if (inst.IsCall())
						{
							if ((mLastValidStepIntoPC == 0) || (dwSubprogram != NULL))
								mLastValidStepIntoPC = pcAddress + inst.mSize;
						}

						if ((dwLineData != NULL) && (inst.IsBranch()))
						{
							addr_target targetAddr = inst.GetTarget();
							if (targetAddr < dwSubprogram->GetLineAddr(*dwLineData))
							{
								// Jumping backwards, stop at next instruction
								mStepStopOnNextInstruction = true;
							}
						}

						bool isPrefixOnly = false;
						if ((mStepInAssembly) && (stepType == StepType_StepOver) && (inst.IsRep(isPrefixOnly)))
						{
							if (isPrefixOnly)
							{
								CPUInst nextInst;
								if (mDebugTarget->DecodeInstruction(pcAddress + inst.GetLength(), &nextInst))
								{
									if (nextInst.IsBranch())
									{
										// repne jmp - this appears in __chkstk (for example)
										// We don't have a good way to "step over" this one, so just do a single step
									}
									else
									{
										// Step over the rep + target instruction
										auto doneAddr = pcAddress + inst.GetLength() + nextInst.GetLength();
										BfLogDbg("SetupStep SetTempBreakpoint %p\n", doneAddr);
										SetTempBreakpoint(doneAddr);
										mStepBreakpointAddrs.push_back(doneAddr);
										break;
									}
								}
							}
							else
							{
								// Step over the instruction
								auto doneAddr = pcAddress + inst.GetLength();
								BfLogDbg("SetupStep SetTempBreakpoint %p\n", doneAddr);
								SetTempBreakpoint(doneAddr);
								mStepBreakpointAddrs.push_back(doneAddr);
								break;
							}
						}

						// Just step a single instruction
						BfLogDbg("SetupStep SingleStepX86 %p\n", pcAddress);
						SingleStepX86();
						if (inst.IsReturn())
							mStepStopOnNextInstruction = true;
						break;
					}
				}
				else
				{
					// Move us to this instruction so we can hardware single-step into it
					BfLogDbg("SetupStep SetTempBreakpoint %p\n", pcAddress);
					SetTempBreakpoint(pcAddress);
					mStepBreakpointAddrs.push_back(pcAddress);
					break;
				}
			}

			// Not an interesting instruction - move to next
			pcAddress += inst.mSize;

			if ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL) && (pcAddress >= dwSubprogram->mBlock.mHighPC))
			{
				auto endAddress = dwSubprogram->mBlock.mHighPC;
				BfLogDbg("Stepping past end of inlined method, end address: %p\n", endAddress);

				mStepType = StepType_StepOut_Inline;
				SetTempBreakpoint(endAddress);
				mStepBreakpointAddrs.push_back(endAddress);
				return true;
			}
		}

		if (instIdx > 1)
			BfLogDbg("SetupStep instIdx: %d\n", instIdx);
	}

	return true;
}

void WinDebugger::CheckNonDebuggerBreak()
{
	enum MessageType
	{
		MessageType_None = 0,
		MessageType_Error = 1,
		MessageType_ProfilerCmd = 2
	};

	CPURegisters registers;
	PopulateRegisters(&registers);
	addr_target pcAddress = registers.GetPC();

	addr_target debugMessageDataAddr = (addr_target)-1;
	if (mDebugTarget->mTargetBinary != NULL)
	{
		mDebugTarget->mTargetBinary->ParseSymbolData();
		debugMessageDataAddr = mDebugTarget->FindSymbolAddr("gBfDebugMessageData");
	}
	if (debugMessageDataAddr != (addr_target)-1)
	{
		struct BfDebugMessageData
		{
			int mMessageType; // 0 = none, 1 = error
			int mStackWindbackCount;
			int mBufParamLen;
			addr_target mBufParam;
			addr_target mPCOverride;
		};
		BfDebugMessageData messageData = ReadMemory<BfDebugMessageData>(debugMessageDataAddr);
		WriteMemory<int>(debugMessageDataAddr, 0); // Zero out type so we won't trigger again
		if (messageData.mMessageType != 0)
		{
			llvm::SmallVector<char, 4096> strBuf;
			int strLen = messageData.mBufParamLen;
			strBuf.resize(strLen + 1);

			char* str = &strBuf[0];
			str[strLen] = 0;
			if (ReadMemory(messageData.mBufParam, strLen, str))
			{
				if (messageData.mMessageType == MessageType_Error)
				{
					mRequestedStackFrameIdx = messageData.mStackWindbackCount;
					if (messageData.mPCOverride != 0)
					{
						mShowPCOverride = messageData.mPCOverride;
						mRequestedStackFrameIdx = -2;
					}
					mDebugManager->mOutMessages.push_back(StrFormat("error %s", str));
				}
				else if (messageData.mMessageType == MessageType_ProfilerCmd)
				{
					// It's important to set this here, because we unlock the critSect during StopSampling and we can't have the
					//  IDE thinking that we're actually paused when it checks the mRunState
					mRunState = RunState_Running;

					char* cmd = strtok(str, "\t");
					if (strcmp(cmd, "StartSampling") == 0)
					{
						char* sessionIdStr = strtok(NULL, "\t");
						char* threadIdStr = strtok(NULL, "\t");
						char* sampleRateStr = strtok(NULL, "\t");
						char* descStr = strtok(NULL, "\t");

						if (threadIdStr != NULL)
						{
							int threadId = atoi(threadIdStr);
							int sampleRate = atoi(sampleRateStr);
							int sessionId = atoi(sessionIdStr);

							Profiler** profilerPtr;
							if (mPendingProfilerMap.TryAdd(sessionId, NULL, &profilerPtr))
							{
								DbgProfiler* profiler = new DbgProfiler(this);
								if (descStr != NULL)
									profiler->mDescription = descStr;
								if (sampleRate > 0)
									profiler->mSamplesPerSecond = sampleRate;
								profiler->Start();
								*profilerPtr = profiler;

								mDebugManager->mOutMessages.push_back("newProfiler");
								mNewProfilerList.push_back(profiler);
							}
						}
					}
					else if (strcmp(cmd, "StopSampling") == 0)
					{
						char* sessionIdStr = strtok(NULL, "\t");
						if (sessionIdStr != NULL)
						{
							int sessionId = atoi(sessionIdStr);

							Profiler* profiler;
							if (mPendingProfilerMap.Remove(sessionId, &profiler))
							{
								if (profiler->IsSampling())
								{
									// Need to unlock so we don't deadlock
									mDebugManager->mCritSect.Unlock();
									profiler->Stop();
									mDebugManager->mCritSect.Lock();
								}
							}
						}
					}
					else if (strcmp(cmd, "ClearSampling") == 0)
					{
						for (auto& kv : mPendingProfilerMap)
						{
							auto profiler = kv.mValue;
							profiler->Clear();
						}
					}
					else if (strcmp(cmd, "ClearOutput") == 0)
					{
						mDebugManager->mOutMessages.push_back("clearOutput");
					}
				}
				return;
			}
		}
	}

	intptr_target objAddr;
	auto dbgBreakKind = mDebugTarget->GetDbgBreakKind(pcAddress, &registers, &objAddr);
	if (dbgBreakKind == DbgBreakKind_ObjectAccess)
	{
		String errorStr = "error Attempted to access deleted object";
		String objectAddr = EncodeDataPtr((addr_target)objAddr, true);
		errorStr += StrFormat("\x1LEAK\t(System.Object)%s\n   (%s)%s\n", objectAddr.c_str(), "System.Object", objectAddr.c_str());
		mDebugManager->mOutMessages.push_back(errorStr);
		return;
	}
	else if (dbgBreakKind == DbgBreakKind_ArithmeticOverflow)
	{
		String errorStr = "error Arithmetic overflow detected";
		mDebugManager->mOutMessages.push_back(errorStr);
		return;
	}

	bool showMainThread = false;

	String symbol;
	addr_target offset;
	DbgModule* dbgModule;
	if (mDebugTarget->FindSymbolAt(pcAddress, &symbol, &offset, &dbgModule))
	{
		if ((symbol == "DbgBreakPoint") || (symbol == "RtlUserThreadStart") || (symbol == "RtlUserThreadStart@8"))
		{
			showMainThread = true;
		}
	}
#ifdef BF_DBG_32
	else if ((dbgModule != NULL) && (dbgModule->mDisplayName.Equals("kernel32.dll", StringImpl::CompareKind_OrdinalIgnoreCase)))
	{
		showMainThread = true;
	}
#endif

	if (showMainThread)
	{
		// This is a manual break, show the main thread
		mActiveThread = mThreadList.front();
		if (mDebugPendingExpr != NULL)
		{
			for (auto thread : mThreadList)
			{
				if (thread->mThreadId == mDebugEvalThreadInfo.mThreadId)
				{
					mActiveThread = thread;
					break;
				}
			}
		}
	}
}

bool WinDebugger::HasSteppedIntoCall()
{
	//  Some calls (like __chkstk) actually push results to the stack, so we need to check
	//  if we're REALLY deeper or not, by rolling back the callstack once

	CPURegisters registers;
	PopulateRegisters(&registers);
	if (RollBackStackFrame(&registers, true))
	{
		// If the previous frames SP is equal or deeper than our step start then we are indeed inside a call!
		if (mStepSP >= registers.GetSP())
			return true;
	}
	return false;
}

void WinDebugger::StepOver(bool inAssembly)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("StepOver\n");

	if (!TryRunContinue())
		return;

	mCurNoInfoStepTries = 0; // Reset
	mStepInAssembly = inAssembly;

	SetupStep(StepType_StepOver);
	ContinueDebugEvent();
}

void WinDebugger::StepOut(bool inAssembly)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("StepOut\n");

	if (!TryRunContinue())
		return;

	mCurNoInfoStepTries = 0; // Reset
	mStepInAssembly = inAssembly;

	SetupStep(StepType_StepOut);

	ContinueDebugEvent();
}

void WinDebugger::SetNextStatement(bool inAssembly, const StringImpl& fileName, int64 lineNumOrAsmAddr, int wantColumn)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	DbgSubprogram* subProgram = NULL;
	if (!inAssembly)
	{
		if (mCallStack.size() == 0)
			UpdateCallStack();
		if (mCallStack.size() > 0)
		{
			UpdateCallStackMethod(0);
			subProgram = mCallStack[0]->mSubProgram;
		}
		if (subProgram == NULL)
			return;
	}

	DbgSubprogram* rootInlineParent = NULL;
	if (subProgram != NULL)
		rootInlineParent = subProgram->GetRootInlineParent();

	String result;
	if (mDebugTarget == NULL)
		return;

	DbgSrcFile* srcFile = NULL;
	if (!fileName.IsEmpty())
	{
		srcFile = mDebugTarget->GetSrcFile(fileName);
		if (srcFile == NULL)
			return;
	}

	addr_target pcAddress = 0;
	if (inAssembly)
	{
		pcAddress = lineNumOrAsmAddr;
	}
	else
	{
		int lineNum = (int)lineNumOrAsmAddr;

		addr_target bestAddr[2] = { 0, 0 };
		int checkLineNum[2] = { lineNum - 1, lineNum };

		auto _CheckLineInfo = [&](DbgSubprogram* dbgSubprogram, DbgLineInfo* dbgLineInfo)
		{
			for (int iPass = 0; iPass < 2; ++iPass)
			{
				int bestLineOffset = 0x7FFFFFFF;

				for (auto& lineData : dbgLineInfo->mLines)
				{
					auto addr = dbgSubprogram->GetLineAddr(lineData);
					if ((addr < subProgram->mBlock.mLowPC) || (addr >= subProgram->mBlock.mHighPC))
						continue;

					int lineOffset = lineData.mLine - checkLineNum[iPass];

					if ((lineOffset >= 0) && (lineOffset <= 6) && (lineOffset <= bestLineOffset))
					{
						if (lineOffset < bestLineOffset)
						{
							bestLineOffset = lineOffset;
							bestAddr[iPass] = addr;
						}
					}
				}
			}
		};

		for (int checkHotIdx = -1; checkHotIdx < (int)srcFile->mHotReplacedDbgLineInfo.size(); checkHotIdx++)
		{
			if (checkHotIdx >= 0)
			{
				auto hotReplacedLineInfo = srcFile->mHotReplacedDbgLineInfo[checkHotIdx];
				for (auto& hotReplacedEntry : hotReplacedLineInfo->mEntries)
				{
					_CheckLineInfo(hotReplacedEntry.mSubprogram, hotReplacedEntry.mLineInfo);
				}
			}
			else
			{
				for (auto subprogram : srcFile->mLineDataRefs)
					_CheckLineInfo(subprogram, subprogram->mLineInfo);
			}

			if (bestAddr[1] != 0)
				break;
		}

		if (bestAddr[1] != 0)
		{
			const int kMaxAddrDist = 64; // within reasonable range
			if ((bestAddr[0] != 0) && (bestAddr[1] - bestAddr[0] <= kMaxAddrDist))
			{
				addr_target addrStart = bestAddr[0];
				addr_target addrEnd = bestAddr[1];
				addr_target addr = addrStart;

				BF_ASSERT(addrEnd - addr <= kMaxAddrDist);

				addr_target lastOp = 0;
				while (addr < addrEnd)
				{
					CPUInst inst;
					if (!mDebugTarget->DecodeInstruction(addr, &inst))
						break;
					lastOp = addr;
					addr += inst.GetLength();
				}
			}

			pcAddress = (uint64)bestAddr[1];
		}
	}

	if (pcAddress)
	{
		BF_ASSERT(mActiveThread->mBreakpointAddressContinuing == 0);
		mActiveThread->mIsAtBreakpointAddress = 0;
		mActiveThread->mStoppedAtAddress = pcAddress;

		if (mCallStack.size() == 0)
			UpdateCallStack();

		CPURegisters* regs = &mCallStack.front()->mRegisters;
		*regs->GetPCRegisterRef() = pcAddress;

		SetRegisters(regs);

		WdBreakpoint* breakpoint = (WdBreakpoint*)FindBreakpointAt(pcAddress);
		if (breakpoint != NULL)
		{
			BfLogDbg("SetNextStatement setting mIsAtBreakpointAddress = %p\n", breakpoint->mAddr);
			mActiveThread->mIsAtBreakpointAddress = breakpoint->mAddr;
		}
	}
}

bool WinDebugger::PopulateRegisters(CPURegisters* registers, BF_CONTEXT& lcContext)
{
#ifdef BF_DBG_32
	registers->mIntRegs.eax = lcContext.Eax;
	registers->mIntRegs.ecx = lcContext.Ecx;
	registers->mIntRegs.edx = lcContext.Edx;
	registers->mIntRegs.ebx = lcContext.Ebx;
	registers->mIntRegs.esp = lcContext.Esp;
	registers->mIntRegs.ebp = lcContext.Ebp;
	registers->mIntRegs.esi = lcContext.Esi;
	registers->mIntRegs.edi = lcContext.Edi;
	registers->mIntRegs.eip = lcContext.Eip;
	registers->mIntRegs.efl = lcContext.EFlags;

	BF_ASSERT(sizeof(lcContext.FloatSave.RegisterArea) == sizeof(registers->mFpMmRegsArray));
	memcpy(registers->mFpMmRegsArray, lcContext.FloatSave.RegisterArea, sizeof(lcContext.FloatSave.RegisterArea));

	BF_ASSERT(sizeof(registers->mXmmRegsArray) == 32 * sizeof(float));
	memcpy(registers->mXmmRegsArray, &lcContext.ExtendedRegisters[160], sizeof(registers->mXmmRegsArray));
#else
	registers->mIntRegs.rax = lcContext.Rax;
	registers->mIntRegs.rcx = lcContext.Rcx;
	registers->mIntRegs.rdx = lcContext.Rdx;
	registers->mIntRegs.rbx = lcContext.Rbx;
	registers->mIntRegs.rsp = lcContext.Rsp;
	registers->mIntRegs.rbp = lcContext.Rbp;
	registers->mIntRegs.rsi = lcContext.Rsi;
	registers->mIntRegs.rdi = lcContext.Rdi;
	registers->mIntRegs.rip = lcContext.Rip;
	registers->mIntRegs.efl = lcContext.EFlags;

	registers->mIntRegs.r8 = lcContext.R8;
	registers->mIntRegs.r9 = lcContext.R9;
	registers->mIntRegs.r10 = lcContext.R10;
	registers->mIntRegs.r11 = lcContext.R11;
	registers->mIntRegs.r12 = lcContext.R12;
	registers->mIntRegs.r13 = lcContext.R13;
	registers->mIntRegs.r14 = lcContext.R14;
	registers->mIntRegs.r15 = lcContext.R15;

	registers->mIntRegs.gs = lcContext.SegGs;

	for (int i = 0; i < 8; i++)
	{
		memcpy(&registers->mFpMmRegsArray[i], &lcContext.FltSave.FloatRegisters[i], 10);
	}

	BF_ASSERT(sizeof(registers->mXmmRegsArray) == 64 * sizeof(float));
	memcpy(registers->mXmmRegsArray, BF_CONTEXT_XMMDATA(lcContext), sizeof(registers->mXmmRegsArray));
#endif

	return (lcContext.ContextFlags & (BF_CONTEXT_EXCEPTION_ACTIVE | BF_CONTEXT_SERVICE_ACTIVE)) == 0;
}

bool WinDebugger::PopulateRegisters(CPURegisters* registers)
{
	/*static bool sCheckedProcessorFeatures = false;
	static bool sMmxAvailable = false;
	static bool sXmmAvailable = false;
	if (!sCheckedProcessorFeatures)
	{
		//CDH we don't do anything with these yet since we grab BF_CONTEXT_ALL anyway, but could be useful
		sMmxAvailable = ::IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE) != 0;
		sXmmAvailable = ::IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0;
		sCheckedProcessorFeatures = true;
	}*/

	BF_ASSERT(registers != nullptr);

	BF_CONTEXT lcContext;
	lcContext.ContextFlags = BF_CONTEXT_ALL | BF_CONTEXT_EXCEPTION_REQUEST;
	BF_GetThreadContext(mActiveThread->mHThread, &lcContext);

	return PopulateRegisters(registers, lcContext);
}

bool WinDebugger::RollBackStackFrame(CPURegisters* registers, bool isStackStart)
{
	BF_ASSERT(registers != nullptr);

	return mDebugTarget->RollBackStackFrame(registers, NULL, isStackStart);
}

bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, addr_target newTarget, int newTargetSize)
{
	BfLogDbg("SetHotJump %s %p->%p\n", oldSubprogram->mName, oldSubprogram->mBlock.mLowPC, newTarget);

	//AutoCrit autoCrit(mDebugManager->mCritSect);
	BF_ASSERT(mDebugManager->mCritSect.mLockCount == 1);

	addr_target jmpInstStart = oldSubprogram->mBlock.mLowPC;
	addr_target jmpInstEnd = jmpInstStart + sizeof(HotJumpOp);

	if (jmpInstEnd > oldSubprogram->mBlock.mHighPC)
	{
		if ((oldSubprogram->mBlock.mHighPC - oldSubprogram->mBlock.mLowPC == 1) &&
			(newTargetSize == 1))
			return true; // Special case for just stub 'ret' methods
		String err = StrFormat("Failed to hot replace method, method '%s' too small to insert hot thunk", oldSubprogram->ToString().c_str());
		Fail(err);
		return false;
	}

	if (oldSubprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
	{
		for (int hotThreadIdx = 0; hotThreadIdx < (int)mHotThreadStates.size(); hotThreadIdx++)
		{
			auto& hotThreadState = mHotThreadStates[hotThreadIdx];
			WdThreadInfo* threadInfo = NULL;
			if (!mThreadMap.TryGetValue((uint32)hotThreadState.mThreadId, &threadInfo))
				continue;

			int tryStart = GetTickCount();

			while ((hotThreadState.mRegisters.GetPC() >= jmpInstStart) && (hotThreadState.mRegisters.GetPC() < jmpInstEnd))
			{
				if (GetTickCount() - tryStart >= 8000)
				{
					Fail("Failed to hot replace method, can't move past prelude");
					return false;
				}

				BfLogDbg("SetHotJump skipping through %p\n", hotThreadState.mRegisters.GetPC());

				bool removedBreakpoint = false;

				mActiveThread = threadInfo;
				if ((mActiveThread->mStoppedAtAddress >= jmpInstStart) && (mActiveThread->mStoppedAtAddress < jmpInstEnd))
				{
					for (addr_target addr = jmpInstStart; addr < jmpInstEnd; addr++)
					{
						if (mPhysBreakpointAddrMap.ContainsKey(addr))
						{
							removedBreakpoint = true;
							RemoveBreakpoint(addr);
						}
					}
				}

				RunState oldRunState = mRunState;
				mRunState = RunState_HotStep;

				if (mWantsDebugContinue)
				{
					mWantsDebugContinue = false;
					BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
					mContinueEvent.Set();
				}

				BF_CONTEXT lcContext;
				lcContext.ContextFlags = BF_CONTEXT_ALL;
				BF_GetThreadContext(mActiveThread->mHThread, &lcContext);
				lcContext.EFlags |= 0x100; // Set trap flag, which raises "single-step" exception
				BF_SetThreadContext(mActiveThread->mHThread, &lcContext);

				::ResumeThread(mActiveThread->mHThread);
				BfLogDbg("ResumeThread %d\n", mActiveThread->mThreadId);

				while (mRunState != RunState_Terminated)
				{
					mDebugManager->mCritSect.Unlock();
					Sleep(0);
					mDebugManager->mCritSect.Lock();
					if (IsPaused())
						break;
					if (mWantsDebugContinue)
					{
						BF_GetThreadContext(mActiveThread->mHThread, &lcContext);

						mWantsDebugContinue = false;
						BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
						mContinueEvent.Set();
					}
				}

				BF_GetThreadContext(mActiveThread->mHThread, &lcContext);

				::SuspendThread(mActiveThread->mHThread);
				BfLogDbg("SuspendThread %d\n", mActiveThread->mThreadId);
				mRunState = oldRunState;

				if ((mRunState != RunState_Terminated) && (mRunState != RunState_Terminating))
				{
					if (!IsPaused())
					{
						BF_ASSERT(mWantsDebugContinue);
						mWantsDebugContinue = false;
						BF_ASSERT_REL(mActiveThread->mIsAtBreakpointAddress == 0);
						mContinueEvent.Set();
					}
				}

				PopulateRegisters(&hotThreadState.mRegisters);
			}
		}
	}

	HotJumpOp jumpOp;
	jumpOp.mOpCode = 0xE9;
	jumpOp.mRelTarget = newTarget - oldSubprogram->mBlock.mLowPC - sizeof(HotJumpOp);
	WriteMemory(oldSubprogram->mBlock.mLowPC, jumpOp);
	::FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)oldSubprogram->mBlock.mLowPC, sizeof(HotJumpOp));
	return true;
}

DbgSubprogram* WinDebugger::TryFollowHotJump(DbgSubprogram* subprogram, addr_target addr)
{
	if (subprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
		return subprogram;

	if (addr != subprogram->mBlock.mLowPC)
		return subprogram;

	auto dbgModule = subprogram->mCompileUnit->mDbgModule;

	HotJumpOp jumpOp = ReadMemory<HotJumpOp>(addr);
	if (jumpOp.mOpCode != 0xE9)
		return subprogram;
	addr_target jumpAddr = addr + jumpOp.mRelTarget + sizeof(HotJumpOp);

	auto jumpSubprogram = mDebugTarget->FindSubProgram(jumpAddr);
	if (jumpSubprogram == NULL)
		return subprogram;

	return jumpSubprogram;
}

bool WinDebugger::ShouldShowStaticMember(DbgType* dbgType, DbgVariable* member)
{
	// If locationData is non-null, that means it was added in addition to the static declaration in the CV type info,
	//  so only add the names from the type definition
	auto flavor = dbgType->mCompileUnit->mDbgModule->mDbgFlavor;
	return ((((dbgType->IsNamespace()) || (flavor != DbgFlavor_MS)) && ((member->mLocationData != NULL) || member->mIsConst)) ||
		((flavor == DbgFlavor_MS) && (member->mLocationData == NULL)));
}

String WinDebugger::GetMemberList(DbgType* dbgType, const StringImpl& expr, bool isPtr, bool isStatic, bool forceCast, bool isSplat, bool isReadOnly)
{
	auto dbgModule = dbgType->GetDbgModule();
	dbgType->PopulateType();
	auto language = dbgType->GetLanguage();

	if (!isStatic)
	{
		String retVal;
		bool needsNewline = false;
		bool isBfObject = false;

		if (dbgType->IsBfObjectPtr())
		{
			isBfObject = true;
			dbgType = dbgType->mTypeParam;
		}

		int baseIdx = 0;
		for (auto baseTypeEntry : dbgType->mBaseTypes)
		{
			auto baseType = baseTypeEntry->mBaseType;
			if ((baseType->mSize > 0) || (baseType->mTypeCode != DbgType_Struct) || (strcmp(baseType->mTypeName, "ValueType") != 0))
			{
				String baseTypeStr = baseType->ToStringRaw(language);
				if (baseIdx > 0)
					retVal += "\n";
				if (isSplat)
					retVal += "[base]\t((" + baseTypeStr + ")" + expr + "), nv";
				else if (dbgType->WantsRefThis())
					retVal += "[base]\t((" + baseTypeStr + ")this), nd, na, nv, this=" + expr;
				else
					retVal += "[base]\t((" + baseTypeStr + "*)this), nd, na, nv, this=" + expr;
				if (isReadOnly)
					retVal += ", ne";
			}

			needsNewline = true;

			baseIdx++;
		}

		String thisExpr = expr;
		String castString;
		if (dbgType->IsBfObject())
		{
			auto ptrType = dbgType->GetDbgModule()->GetPointerType(dbgType);
			castString = ptrType->ToStringRaw(language);
		}
		else
			castString = dbgType->ToStringRaw(language);

		bool hadStatics = false;
		for (auto member : dbgType->mMemberList)
		{
			if (member->mMemberOffset < 0)
				continue;

			if (member->mIsStatic)
			{
				if (ShouldShowStaticMember(dbgType, member))
					hadStatics = true;
			}
			else
			{
				bool ignoreMember = false;
				if (member->mName != NULL)
				{
					if ((member->mName[0] == '?') ||
						(member->mName[0] == '$') ||
						(strncmp(member->mName, "_vptr$", 6) == 0))
						ignoreMember = true;
				}

				if (!ignoreMember)
				{
					if (needsNewline)
						retVal += "\n";

					if (member->mName == NULL)
					{
						retVal += GetMemberList(member->mType, expr, isPtr, isStatic, forceCast, isSplat, isReadOnly);
					}
					else
					{
						retVal += String(member->mName);
						if (isSplat)
						{
							retVal += "\t(" + thisExpr + ")." + String(member->mName);
							// We don't want to rely on this being enforced here.  For one, ref types shouldn't get ", ne" added,
							//  and this doesn't solve the issue of attempting to assign via the Immediate window
							/*if (isReadOnly)
								retVal += ", ne";*/
						}
						else
						{
							if (forceCast)
								retVal += "\t((" + castString + ")this)." + String(member->mName);
							else if ((member->mName[0] >= '0') && (member->mName[0] <= '9')) // Numbered tuple member?
								retVal += "\tthis." + String(member->mName);
							else
								retVal += "\t" + String(member->mName);

							retVal += ", this=" + thisExpr;
// 							if (isReadOnly)
// 								retVal += ", ne";
						}
					}
					needsNewline = true;
				}
			}
		}
		if (hadStatics)
		{
			if (needsNewline)
				retVal += "\n";
			retVal += "Static values\t" + castString;
		}
		return retVal;
	}
	else
	{
		if (dbgType->IsBfObjectPtr())
			dbgType = dbgType->mTypeParam;

		String retVal;
		String memberPrefix = expr;

		bool needsNewline = false;
		bool hadStatics = false;
		for (auto member : dbgType->mMemberList)
		{
			if (member->mIsStatic)
			{
				if (ShouldShowStaticMember(dbgType, member))
				{
					if (needsNewline)
						retVal += "\n";
					retVal += String(member->mName) + "\t" + memberPrefix + "." + String(member->mName);
					needsNewline = true;
				}
			}
		}
		return retVal;
	}
	return "";
}

bool WinDebugger::ParseFormatInfo(DbgModule* dbgModule, const StringImpl& formatInfoStr, DwFormatInfo* formatInfo, BfPassInstance* bfPassInstance, int* assignExprOffset, String* assignExprString, String* errorString, DbgTypedValue contextTypedValue)
{
	String formatFlags = formatInfoStr;
	if (assignExprOffset != NULL)
		*assignExprOffset = -1;

	while (formatFlags.length() > 0)
	{
		formatFlags = Trim(formatFlags);
		if (formatFlags.IsEmpty())
			break;
		if (formatFlags[0] != ',')
		{
			return false;
		}
		else
		{
			int nextComma = formatFlags.IndexOf(',', 1);
			int quotePos = formatFlags.IndexOf('"', 1);
			if ((quotePos != -1) && (quotePos < nextComma))
			{
				int nextQuotePos = formatFlags.IndexOf('"', quotePos + 1);
				if (nextQuotePos != -1)
					nextComma = formatFlags.IndexOf(',', nextQuotePos + 1);
			}
			if (nextComma == -1)
				nextComma = formatFlags.length();

			String formatCmd = formatFlags.Substring(1, nextComma - 1);
			formatCmd = Trim(formatCmd);
			bool hadError = false;

			if (strncmp(formatCmd.c_str(), "this=", 5) == 0)
			{
				formatCmd = formatFlags.Substring(1);
				formatCmd = Trim(formatCmd);
				String thisExpr = formatCmd.Substring(5);
				if (thisExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, thisExpr, formatInfo);
				formatInfo->mExplicitThis = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				formatFlags = thisExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else if (strncmp(formatCmd.c_str(), "count=", 6) == 0)
			{
				formatCmd = formatFlags.Substring(1);
				formatCmd = Trim(formatCmd);
				String countExpr = formatCmd.Substring(6);
				if (countExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
				DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
				if ((countValue) && (countValue.mType->IsInteger()))
					formatInfo->mOverrideCount = (intptr)countValue.GetInt64();
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else if (strncmp(formatCmd.c_str(), "maxcount=", 9) == 0)
			{
				formatCmd = formatFlags.Substring(1);
				formatCmd = Trim(formatCmd);
				String countExpr = formatCmd.Substring(9);
				if (countExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
				DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
				if ((countValue) && (countValue.mType->IsInteger()))
					formatInfo->mMaxCount = (intptr)countValue.GetInt64();
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else if (strncmp(formatCmd.c_str(), "arraysize=", 10) == 0)
			{
				formatCmd = formatFlags.Substring(1);
				formatCmd = Trim(formatCmd);
				String countExpr = formatCmd.Substring(10);
				if (countExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
				DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
				if ((countValue) && (countValue.mType->IsInteger()))
					formatInfo->mArrayLength = (intptr)countValue.GetInt64();
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else if (strncmp(formatCmd.c_str(), "assign=", 7) == 0)
			{
				formatCmd = formatFlags.Substring(1);
				formatCmd = Trim(formatCmd);
				String assignExpr = formatCmd.Substring(7);
				if (assignExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, assignExpr, formatInfo);
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				if (assignExprOffset != NULL)
				{
					//TODO: Keep track of the offset directly, this is a hack
					*assignExprOffset = (int)formatInfoStr.IndexOf("assign=") + 7;
				}
				if (assignExprString != NULL)
					*assignExprString = dbgEvaluationContext.mExprNode->ToString();
				formatFlags = assignExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else if (strncmp(formatCmd.c_str(), "refid=", 6) == 0)
			{
				formatInfo->mReferenceId = formatCmd.Substring(6);
				if ((formatInfo->mReferenceId.mLength >= 2) && (formatInfo->mReferenceId[0] == '\"'))
					formatInfo->mReferenceId = formatInfo->mReferenceId.Substring(1, formatInfo->mReferenceId.length() - 2);
			}
			else if (strncmp(formatCmd.c_str(), "action=", 7) == 0)
			{
				formatInfo->mAction = formatCmd.Substring(7);
				if ((formatInfo->mAction.mLength >= 2) && (formatInfo->mAction[0] == '\"'))
					formatInfo->mAction = formatInfo->mReferenceId.Substring(1, formatInfo->mReferenceId.length() - 2);
			}
			else if (strncmp(formatCmd.c_str(), "_=", 2) == 0)
			{
				formatInfo->mSubjectExpr = formatCmd.Substring(2);
				if ((formatInfo->mSubjectExpr.mLength >= 2) && (formatInfo->mSubjectExpr[0] == '\"'))
					formatInfo->mSubjectExpr = formatInfo->mSubjectExpr.Substring(1, formatInfo->mSubjectExpr.length() - 2);
			}
			else if (strncmp(formatCmd.c_str(), "expectedType=", 13) == 0)
			{
				formatInfo->mExpectedType = formatCmd.Substring(13);
				if ((formatInfo->mExpectedType.mLength >= 2) && (formatInfo->mExpectedType[0] == '\"'))
					formatInfo->mExpectedType = formatInfo->mExpectedType.Substring(1, formatInfo->mExpectedType.length() - 2);
			}
			else if (strncmp(formatCmd.c_str(), "namespaceSearch=", 16) == 0)
			{
				formatInfo->mNamespaceSearch = formatCmd.Substring(16);
				if ((formatInfo->mNamespaceSearch.mLength >= 2) && (formatInfo->mNamespaceSearch[0] == '\"'))
					formatInfo->mNamespaceSearch = formatInfo->mNamespaceSearch.Substring(1, formatInfo->mNamespaceSearch.length() - 2);
			}
			else if (formatCmd == "d")
			{
				formatInfo->mDisplayType = DwDisplayType_Decimal;
			}
			else if (formatCmd == "x")
			{
				formatInfo->mDisplayType = DwDisplayType_HexLower;
			}
			else if (formatCmd == "X")
			{
				formatInfo->mDisplayType = DwDisplayType_HexUpper;
			}
			else if (formatCmd == "s")
			{
				formatInfo->mHidePointers = true;
				formatInfo->mDisplayType = DwDisplayType_Ascii;
			}
			else if (formatCmd == "s8")
			{
				formatInfo->mHidePointers = true;
				formatInfo->mDisplayType = DwDisplayType_Utf8;
			}
			else if (formatCmd == "s16")
			{
				formatInfo->mHidePointers = true;
				formatInfo->mDisplayType = DwDisplayType_Utf16;
			}
			else if (formatCmd == "s32")
			{
				formatInfo->mHidePointers = true;
				formatInfo->mDisplayType = DwDisplayType_Utf32;
			}
			else if (formatCmd == "nd")
			{
				formatInfo->mIgnoreDerivedClassInfo = true;
			}
			else if (formatCmd == "na")
			{
				formatInfo->mHidePointers = true;
			}
			else if (formatCmd == "nm")
			{
				formatInfo->mNoMembers = true;
			}
			else if (formatCmd == "ne")
			{
				formatInfo->mNoEdit = true;
			}
			else if (formatCmd == "nv")
			{
				formatInfo->mNoVisualizers = true;
			}
			else if (formatCmd == "rawStr")
			{
				formatInfo->mRawString = true;
			}
			else if (((!formatCmd.IsEmpty()) && ((formatCmd[0] >= '0') && (formatCmd[0] <= '9'))) ||
					 (formatCmd.StartsWith("(")))
			{
				String countExpr = formatCmd;
				if (countExpr.empty())
					break;
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, countExpr, formatInfo);
				DbgTypedValue countValue = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
				if ((countValue) && (countValue.mType->IsInteger()))
					formatInfo->mArrayLength = (intptr)countValue.GetInt64();
				if (dbgEvaluationContext.HadError())
				{
					if (errorString != NULL)
						*errorString = dbgEvaluationContext.GetErrorStr();
					return false;
				}
				formatFlags = countExpr.Substring(dbgEvaluationContext.mExprNode->GetSrcEnd());
				continue;
			}
			else
				hadError = true;

			if (hadError)
			{
				if (errorString != NULL)
					*errorString = "Invalid format flags";
				return false;
			}

			formatFlags = formatFlags.Substring(nextComma);
		}
	}
	return true;
}

String WinDebugger::MaybeQuoteFormatInfoParam(const StringImpl& str)
{
	bool needsQuote = false;
	for (int i = 0; i < (int)str.length(); i++)
	{
		char c = str[i];
		if (c == ',')
			needsQuote = true;
	}
	if (!needsQuote)
		return str;

	String qStr = "\"";
	qStr += str;
	qStr += "\"";
	return qStr;
}

DbgTypedValue WinDebugger::EvaluateInContext(DbgCompileUnit* dbgCompileUnit, const DbgTypedValue& contextTypedValue, const StringImpl& subExpr, DwFormatInfo* formatInfo, String* outReferenceId, String* outErrors)
{
	DbgEvaluationContext dbgEvaluationContext(this, dbgCompileUnit->mDbgModule, subExpr, formatInfo, contextTypedValue);
	if (dbgEvaluationContext.mDbgExprEvaluator == NULL)
		return DbgTypedValue();
	dbgEvaluationContext.mDbgExprEvaluator->mDbgCompileUnit = dbgCompileUnit;
	if (formatInfo != NULL)
	{
		dbgEvaluationContext.mDbgExprEvaluator->mLanguage = formatInfo->mLanguage;
		dbgEvaluationContext.mDbgExprEvaluator->mSubjectExpr = formatInfo->mSubjectExpr;
	}
	dbgEvaluationContext.mDbgExprEvaluator->mReferenceId = outReferenceId;
	auto result = dbgEvaluationContext.EvaluateInContext(contextTypedValue);
	if ((formatInfo != NULL) && (dbgEvaluationContext.mDbgExprEvaluator->mCountResultOverride != -1))
		formatInfo->mOverrideCount = dbgEvaluationContext.mDbgExprEvaluator->mCountResultOverride;
	if (dbgEvaluationContext.mPassInstance->HasFailed())
	{
		if (outErrors != NULL)
		{
			int errIdx = 0;
			for (auto err : dbgEvaluationContext.mPassInstance->mErrors)
			{
				if (errIdx > 0)
					(*outErrors) += "\n";
				(*outErrors) += err->mError;
				errIdx++;
			}
		}
		return DbgTypedValue();
	}
	return result;
}

void WinDebugger::DbgVisFailed(DebugVisualizerEntry* debugVis, const StringImpl& evalString, const StringImpl& errors)
{
	bool onlyMemError = errors.StartsWith("Failed to read") && !errors.Contains('\n');
	if ((!debugVis->mShowedError) && (!onlyMemError))
	{
		debugVis->mShowedError = true;
		String errStr = StrFormat("DbgVis '%s' failed while evaluating condition '%s'\n", debugVis->mName.c_str(), evalString.c_str());
		String spacedErrors = errors;
		spacedErrors.Insert(0, " ");
		spacedErrors.Replace("\n", "\n ");
		errStr += spacedErrors;
		OutputMessage(errStr);
	}
}

bool WinDebugger::EvalCondition(DebugVisualizerEntry* debugVis, DbgCompileUnit* dbgCompileUnit, DbgTypedValue typedVal, DwFormatInfo& formatInfo, const StringImpl& condition, const Array<String>& dbgVisWildcardCaptures, String& errorStr)
{
	DwFormatInfo displayStrFormatInfo = formatInfo;
	displayStrFormatInfo.mHidePointers = false;
	displayStrFormatInfo.mRawString = false;

	String errors;
	const String conditionStr = mDebugManager->mDebugVisualizers->DoStringReplace(condition, dbgVisWildcardCaptures);
	DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, typedVal, conditionStr, &displayStrFormatInfo, NULL, &errors);
	if ((!evalResult) || (!evalResult.mType->IsBoolean()))
	{
		if (formatInfo.mRawString)
			return false;

		errorStr += "<DbgVis Failed>";
		DbgVisFailed(debugVis, conditionStr, errors);
		return false;
	}

	return evalResult.mBool;
}

String WinDebugger::GetArrayItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgType* valueType, DbgTypedValue& curNode, int& count, String* outContinuationData)
{
	DbgEvaluationContext conditionEvaluationContext(this, dbgCompileUnit, debugVis->mCondition);

	String addrs;

	bool checkLeft = true;

	int usedCount = 0;
	while (usedCount < count)
	{
		DbgTypedValue condVal = conditionEvaluationContext.EvaluateInContext(curNode);
		if (!condVal)
			break;
		if (condVal.mBool)
		{
			auto val = curNode;
			if (valueType == NULL)
			{
				String typeAddr = val.mType->ToStringRaw();
				// RPad
				typeAddr.Append(' ', sizeof(addr_target) * 2 - typeAddr.length());
				addrs += typeAddr;
			}

			String addr = EncodeDataPtr(val.mPtr, false);
			addrs += addr;
			usedCount++;
		}

		curNode.mPtr += curNode.mType->mTypeParam->GetStride();
	}
	count = usedCount;

	if (outContinuationData != NULL)
	{
		*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(valueType, false) +
			EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
	}

	return addrs;
}

String WinDebugger::GetLinkedListItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, addr_target endNodePtr, DbgType* valueType, DbgTypedValue& curNode, int& count, String* outContinuationData)
{
	DbgEvaluationContext nextEvaluationContext(this, dbgCompileUnit, debugVis->mNextPointer);
	DbgEvaluationContext valueEvaluationContext(this, dbgCompileUnit, debugVis->mValuePointer);

	String addrs;

	bool checkLeft = true;

	int mapIdx;
	for (mapIdx = 0; mapIdx < count; mapIdx++)
	{
		if (curNode.mPtr == endNodePtr)
			break;
		DbgTypedValue val = valueEvaluationContext.EvaluateInContext(curNode);
		if (!val)
			break;
		if (val.mPtr == 0)
			break;

		if (valueType == NULL)
		{
			String typeAddr = val.mType->ToStringRaw();
			// RPad
			typeAddr.Append(' ', sizeof(addr_target)*2 - typeAddr.length());
			addrs += typeAddr;
		}

		String addr = EncodeDataPtr(val.mPtr, false);
		addrs += addr;

		curNode = nextEvaluationContext.EvaluateInContext(curNode);
	}
	count = mapIdx;

	if (outContinuationData != NULL)
	{
		*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(endNodePtr, false) + EncodeDataPtr(valueType, false) +
			EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
	}

	return addrs;
}

String WinDebugger::GetDictionaryItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgTypedValue dictValue, int bucketIdx, int nodeIdx, int& count, String* outContinuationData)
{
	//DbgEvaluationContext bucketsEvaluationContext(this, dbgModule, debugVis->mBuckets);
	DbgEvaluationContext nextEvaluationContext(this, dbgCompileUnit->mDbgModule, debugVis->mNextPointer);

	DbgTypedValue bucketsPtr = EvaluateInContext(dbgCompileUnit, dictValue, debugVis->mBuckets);
	DbgTypedValue entriesPtr = EvaluateInContext(dbgCompileUnit, dictValue, debugVis->mEntries);
	if ((!bucketsPtr) || (!entriesPtr))
	{
		count = -1;
		return "";
	}
	int entrySize = entriesPtr.mType->mTypeParam->GetStride();
	int bucketIdxSize = bucketsPtr.mType->mTypeParam->GetStride();

	String addrs;

	bool checkLeft = true;

	int encodeCount = 0;
	while (encodeCount < count)
	{
		if (nodeIdx != -1)
		{
			DbgTypedValue entryValue;
			entryValue.mSrcAddress = entriesPtr.mPtr + (nodeIdx * entrySize);
			entryValue.mType = entriesPtr.mType->mTypeParam;

			addrs += EncodeDataPtr(entryValue.mSrcAddress, false);

			DbgTypedValue nextValue = nextEvaluationContext.EvaluateInContext(entryValue);
			if ((!nextValue) || (!nextValue.mType->IsInteger()))
			{
				break;
			}

			nodeIdx = (int)nextValue.GetInt64();
			encodeCount++;
		}
		else
		{
			if (bucketIdxSize == 4)
				nodeIdx = ReadMemory<int>(bucketsPtr.mPtr + bucketIdx * sizeof(int32));
			else
				nodeIdx = (int)ReadMemory<int64>(bucketsPtr.mPtr + bucketIdx * sizeof(int64));
			bucketIdx++;
		}
	}

	count = encodeCount;
	//count = mapIdx;

	if (outContinuationData != NULL)
	{
		*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(dictValue.mType, false) + EncodeDataPtr(dictValue.mSrcAddress, false) +
			EncodeDataPtr((addr_target)bucketIdx, false) + EncodeDataPtr((addr_target)nodeIdx, false);
	}

	return addrs;
}

String WinDebugger::GetTreeItems(DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, Array<addr_target>& parentList, DbgType*& valueType, DbgTypedValue& curNode, int count, String* outContinuationData)
{
	DbgEvaluationContext leftEvaluationContext(this, dbgCompileUnit, debugVis->mLeftPointer);
	DbgEvaluationContext rightEvaluationContext(this, dbgCompileUnit, debugVis->mRightPointer);
	DbgEvaluationContext valueEvaluationContext(this, dbgCompileUnit, debugVis->mValuePointer);

	DbgEvaluationContext conditionEvaluationContext(this, dbgCompileUnit, debugVis->mCondition);

	String addrs;

	bool checkLeft = true;

	if ((curNode.mPtr & 2) != 0) // Flag from continuation
	{
		checkLeft = false;
		curNode.mPtr &= (addr_target)~2;
	}

	HashSet<intptr> seenAddrs;

	for (int mapIdx = 0; mapIdx < count; mapIdx++)
	{
		DbgTypedValue readNode;
		while (true)
		{
			bool checkNode = (curNode.mPtr & 1) == 0;

			readNode = curNode;
			readNode.mPtr &= (addr_target)~1;

			if (checkLeft)
			{
				DbgTypedValue leftValue = leftEvaluationContext.EvaluateInContext(readNode);
				bool isEmpty = leftValue.mPtr == NULL;
				if ((leftValue) && (conditionEvaluationContext.HasExpression()))
				{
					auto condValue = conditionEvaluationContext.EvaluateInContext(leftValue);
					if (condValue)
						isEmpty = !condValue.mBool;
				}
				if (isEmpty)
				{
					checkLeft = false;
					break; // Handle node
				}

				parentList.push_back(curNode.mPtr);
				curNode = leftValue;
			}
			else if (checkNode)
			{
				break; // Handle node
			}
			else
			{
				DbgTypedValue rightValue = rightEvaluationContext.EvaluateInContext(readNode);
				bool isEmpty = rightValue.mPtr == NULL;
				if ((rightValue) && (conditionEvaluationContext.HasExpression()))
				{
					auto condValue = conditionEvaluationContext.EvaluateInContext(rightValue);
					if (condValue)
						isEmpty = !condValue.mBool;
				}
				if (!isEmpty)
				{
					curNode = rightValue;
					checkLeft = true;
				}
				else
				{
					if (parentList.size() == 0)
					{
						// Failed
						break;
					}

					curNode.mPtr = parentList.back();
					parentList.pop_back();
					continue; // Don't check against seenAddrs
				}
			}

			if (!seenAddrs.Add(curNode.mPtr))
			{
				// Failed!
				return "";
			}
		}

		DbgTypedValue val = valueEvaluationContext.EvaluateInContext(readNode);
		if (valueType == NULL)
			valueType = val.mType;

		String addr = EncodeDataPtr(val.mPtr, false);
		addrs += addr;

		curNode.mPtr |= 1; // Node handled
	}

	if (!checkLeft)
		curNode.mPtr |= 2;

	if (outContinuationData != NULL)
	{
		*outContinuationData += EncodeDataPtr(debugVis, false) + EncodeDataPtr(valueType, false) +  EncodeDataPtr(curNode.mType, false) + EncodeDataPtr(curNode.mPtr, false);
		for (auto parent : parentList)
			*outContinuationData += EncodeDataPtr(parent, false);
	}

	return addrs;
}

String WinDebugger::GetCollectionContinuation(const StringImpl& continuationData, int callStackIdx, int count)
{
	DbgCompileUnit* dbgCompileUnit = GetCallStackCompileUnit(callStackIdx);;

	if (!IsPaused())
		return "";

	const char* dataPtr = continuationData.c_str();
	DebugVisualizerEntry* debugVis = (DebugVisualizerEntry*)DecodeLocalDataPtr(dataPtr);

	if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_TreeItems)
	{
		DbgType* valueType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		DbgTypedValue curNode;
		curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		curNode.mPtr = DecodeTargetDataPtr(dataPtr);

		Array<addr_target> parentList;
		String newContinuationData;
		while (*dataPtr != 0)
			parentList.push_back(DecodeTargetDataPtr(dataPtr));

		String retVal = GetTreeItems(dbgCompileUnit, debugVis, parentList, valueType, curNode, count, &newContinuationData);
		retVal += "\n" + newContinuationData;
		return retVal;
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_LinkedList)
	{
		addr_target endNodePtr = DecodeTargetDataPtr(dataPtr);
		DbgType* valueType = (DbgType*) DecodeLocalDataPtr(dataPtr);
		DbgTypedValue curNode;
		curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		curNode.mPtr = DecodeTargetDataPtr(dataPtr);

		String newContinuationData;

		if (count < 0)
			count = 3;

		String retVal = GetLinkedListItems(dbgCompileUnit, debugVis, endNodePtr, valueType, curNode, count, &newContinuationData);
		retVal += "\n" + newContinuationData;
		return retVal;
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array)
	{
		DbgType* valueType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		DbgTypedValue curNode;
		curNode.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		curNode.mPtr = DecodeTargetDataPtr(dataPtr);

		String newContinuationData;

		if (count < 0)
			count = 3;

		String retVal = GetArrayItems(dbgCompileUnit, debugVis, valueType, curNode, count, &newContinuationData);
		retVal += "\n" + newContinuationData;
		return retVal;
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Dictionary)
	{
		DbgTypedValue dictValue;
		dictValue.mType = (DbgType*)DecodeLocalDataPtr(dataPtr);
		dictValue.mSrcAddress = DecodeTargetDataPtr(dataPtr);

		int bucketIdx = (int)DecodeTargetDataPtr(dataPtr);
		int nodeIdx = (int)DecodeTargetDataPtr(dataPtr);

		String newContinuationData;
		String retVal = GetDictionaryItems(dbgCompileUnit, debugVis, dictValue, bucketIdx, nodeIdx, count, &newContinuationData);
		retVal += "\n" + newContinuationData;
		return retVal;
	}

	return "";
}

template <typename T>
static String IntTypeToString(T val, const StringImpl& name, DwDisplayInfo* displayInfo, DwFormatInfo& formatInfo)
{
	auto intDisplayType = displayInfo->mIntDisplayType;
	if (formatInfo.mDisplayType == DwDisplayType_Decimal)
		intDisplayType = DwIntDisplayType_Decimal;
	else if (formatInfo.mDisplayType == DwDisplayType_HexUpper)
		intDisplayType = DwIntDisplayType_HexadecimalUpper;
	else if (formatInfo.mDisplayType == DwDisplayType_HexLower)
		intDisplayType = DwIntDisplayType_HexadecimalLower;

	if (intDisplayType == DwIntDisplayType_Binary)
	{
		String binary;
		for (int i = 0; i < sizeof(T) * 8; i++)
		{
			if ((i != 0) && (i % 4 == 0))
				binary = "'" + binary;

			if ((i != 0) && (i % 16 == 0))
				binary = "'" + binary;

			binary = ((val & ((T)1 << i)) ? "1" : "0") + binary;
		}
		return StrFormat("0b'%s\n%s", binary.c_str(), name.c_str());
	}

	if (intDisplayType == DwIntDisplayType_Octal)
	{
		String format;
		if (sizeof(T) == 8)
		{
			format = StrFormat("0o%%lo\n%s", name.c_str());
		}
		else
			format = StrFormat("0o%%0%do\n%s", sizeof(val) * 2, name.c_str());
		return StrFormat(format.c_str(), (std::make_unsigned<T>::type)(val));
	}

	if (intDisplayType == DwIntDisplayType_HexadecimalUpper)
	{
		String format;
		if (sizeof(T) == 8)
		{
			format = StrFormat("0x%%l@\n%s", name.c_str());
		}
		else
			format = StrFormat("0x%%0%dX\n%s", sizeof(val) * 2, name.c_str());
		return StrFormat(format.c_str(), (std::make_unsigned<T>::type)(val));
	}

	if (intDisplayType == DwIntDisplayType_HexadecimalLower)
	{
		String format;
		if (sizeof(T) == 8)
		{
			format = StrFormat("0x%%l@\n%s", name.c_str());
		}
		else
			format = StrFormat("0x%%0%dx\n%s", sizeof(val) * 2, name.c_str());
		return ToLower(StrFormat(format.c_str(), (std::make_unsigned<T>::type)(val)));
	}

	if (std::is_unsigned<T>::value)
	{
		if (sizeof(T) == 8)
		{
			if (val > 0x7FFFFFFFF)
				return StrFormat("%llu\n%s\n:editVal\t%lluUL", val, name.c_str(), val);
			else
				return StrFormat("%llu\n%s", val, name.c_str());
		}
		else
			return StrFormat("%u\n%s", val, name.c_str());
	}
	else
	{
		if (sizeof(T) == 8)
		{
			if ((val > 0x7FFFFFFFF) || (val < -0x80000000LL))
				return StrFormat("%lld\n%s\n:editVal\t%lldL", val, name.c_str(), val);
			else
				return StrFormat("%lld\n%s", val, name.c_str(), val);
		}
		else
			return StrFormat("%d\n%s", val, name.c_str());
	}
}

DwDisplayInfo* WinDebugger::GetDisplayInfo(const StringImpl& referenceId)
{
	DwDisplayInfo* displayInfo = &mDebugManager->mDefaultDisplayInfo;
	if (!referenceId.empty())
	{
		if (!mDebugManager->mDisplayInfos.TryGetValue(referenceId, &displayInfo))
		{
			int dollarIdx = referenceId.LastIndexOf('$');
			if ((dollarIdx > 0) && (referenceId[dollarIdx - 1] == ']'))
			{
				// Try getting series displayinfo
				mDebugManager->mDisplayInfos.TryGetValueWith(StringView(referenceId, 0, dollarIdx), &displayInfo);
			}
		}
	}
	return displayInfo;
}

static String WrapWithModifiers(const StringImpl& origName, DbgType* dbgType, DbgLanguage language)
{
	if (language == DbgLanguage_Unknown)
		language = dbgType->GetLanguage();

	String name = origName;
	while (true)
	{
		if (dbgType->mTypeCode == DbgType_Const)
		{
			if (language == DbgLanguage_Beef)
				name = "readonly " + name;
			else
				name = "const " + name;
			dbgType = dbgType->mTypeParam;
		}
		else if (dbgType->mTypeCode == DbgType_Volatile)
		{
			name = "volatile " + name;
			dbgType = dbgType->mTypeParam;
		}
		else if (dbgType->mTypeCode == DbgType_TypeDef)
		{
			dbgType = dbgType->mTypeParam;
		}
		else if (dbgType->mTypeCode == DbgType_Ref)
		{
			if (language == DbgLanguage_Beef)
				name = "ref " + name;
			else
				name = name + "&";
			dbgType = dbgType->mTypeParam;
		}
		else if (dbgType->mTypeCode == DbgType_Bitfield)
		{
			return dbgType->ToString(language);
		}
		else
			return name;
	}
}

DebugVisualizerEntry* WinDebugger::FindVisualizerForType(DbgType* dbgType, Array<String>* wildcardCaptures)
{
	auto entry = mDebugManager->mDebugVisualizers->FindEntryForType(dbgType->ToString(DbgLanguage_Unknown, true), dbgType->mCompileUnit->mDbgModule->mDbgFlavor, wildcardCaptures);

	if (entry == NULL)
	{
		dbgType = dbgType->GetPrimaryType();
		dbgType->PopulateType();
		for (auto baseTypeEntry : dbgType->mBaseTypes)
		{
			entry = FindVisualizerForType(baseTypeEntry->mBaseType, wildcardCaptures);
			if (entry != NULL)
				break;
		}
	}

	return entry;
}

#define GET_FROM(ptr, T) *((T*)(ptr += sizeof(T)) - 1)

String WinDebugger::ReadString(DbgTypeCode charType, intptr addr, bool isLocalAddr, intptr maxLength, DwFormatInfo& formatInfo, bool wantStringView)
{
	int origMaxLength = maxLength;
	if (addr == 0)
		return "";

	BP_ZONE("WinDebugger::ReadString");

	String retVal = "\"";
	bool wasTerminated = false;
	String valString;
	intptr maxShowSize = 255;

	if (maxLength == -1)
		maxLength = formatInfo.mOverrideCount;
	else if (formatInfo.mOverrideCount != -1)
		maxLength = BF_MIN(formatInfo.mOverrideCount, maxLength);
	if (formatInfo.mMaxCount != -1)
		maxLength = BF_MIN(formatInfo.mMaxCount, maxLength);

	if (maxLength == -1)
		maxLength = 8 * 1024 * 1024; // Is 8MB crazy?
	if ((!formatInfo.mRawString) && (!wantStringView))
		maxLength = BF_MIN(maxLength, maxShowSize);

	if (wantStringView)
	{
		// Limit the original string view to 1MB, reevaluate on "More"
		maxLength = BF_MIN(maxLength, 1024 * 1024);
	}

	//EnableMemCache();
	bool readFailed = false;
	intptr strPtr = addr;

	int charLen = 1;
	if ((charType == DbgType_SChar16) || (charType == DbgType_UChar16))
		charLen = 2;
	else if ((charType == DbgType_SChar32) || (charType == DbgType_UChar32))
		charLen = 4;

	bool isUTF8 = formatInfo.mDisplayType == DwDisplayType_Utf8;

	int readSize = BF_MIN(1024, maxLength * charLen);
	uint8 buf[1024];
	uint8* bufPtr = NULL;
	uint8* bufEnd = NULL;
	bool hasHighAscii = false;

	int i;
	for (i = 0; i < maxLength; i++)
	{
		if (bufPtr >= bufEnd)
		{
			while (true)
			{
				if (readSize < charLen)
				{
					readFailed = true;
					break;
				}

				if (ReadMemory(strPtr, readSize, buf, isLocalAddr))
					break;

				readSize /= 2;
			}
			if (readFailed)
				break;

			bufPtr = buf;
			bufEnd = buf + readSize;
		}

		switch (charLen)
		{
		case 1:
			{
				char c = GET_FROM(bufPtr, char);
				if ((c != 0) || (formatInfo.mOverrideCount != -1))
				{
					if ((uint8)c >= 0x80)
						hasHighAscii = true;
					valString.Append(c);
				}
				else
					wasTerminated = true;
			}
			break;
		case 2:
			{
				uint16 c16 = GET_FROM(bufPtr, uint16);
				if ((c16 != 0) || (formatInfo.mOverrideCount != -1))
				{
					char str[8];
					u8_toutf8(str, 8, c16);
					valString += str;
				}
				else
					wasTerminated = true;
			}
			break;
		case 4:
			{
				uint32 c32 = GET_FROM(bufPtr, uint32);
				if ((c32 != 0) || (formatInfo.mOverrideCount != -1))
				{
					char str[8];
					u8_toutf8(str, 8, c32);
					valString += str;
				}
				else
					wasTerminated = true;
			}
			break;
		}

		if ((wasTerminated) && (formatInfo.mOverrideCount != -1))
		{
			valString += '\x00';
			wasTerminated = false;
		}

		if ((wasTerminated) || (readFailed))
		{
			break;
		}
		strPtr += charLen;
	}
	//DisableMemCache();

	if (formatInfo.mOverrideCount != -1)
	{
		if (i == formatInfo.mOverrideCount)
			wasTerminated = true;
	}

	if (strPtr == addr + origMaxLength)
		wasTerminated = true;

	if (valString.length() == formatInfo.mOverrideCount)
		wasTerminated = true;

// 	if (formatInfo.mDisplayType == DwDisplayType_Ascii)
// 	{
// 		// Our encoding for retVal is already assumed to be UTF8, so the special case here actually Ascii
// 		valString = UTF8Encode(ToWString(valString));
// 	}

	if ((formatInfo.mRawString) || (wantStringView))
	{
		if ((formatInfo.mDisplayType == DwDisplayType_Utf8) || (!hasHighAscii))
			return valString;

		String utf8Str;
		for (int i = 0; i < (int)valString.length(); i++)
		{
			char c = valString[i];
			if ((uint8)c >= 0x80)
			{
				utf8Str += (char)(0xC0 | (((uint8)c & 0xFF) >> 6));
				utf8Str += (char)(0x80 | ((uint8)c & 0x3F));
			}
			else
				utf8Str += c;
		}
		return utf8Str;
	}

	if ((readFailed) && (valString.IsEmpty()))
		return "< Failed to read string >";

	retVal += SlashString(valString, true, true, formatInfo.mLanguage == DbgLanguage_Beef);

	// We could go over 'maxShowSize' if we have a lot of slashed chars. An uninitialized string can be filled with '\xcc' chars
	if ((!formatInfo.mRawString) && (!wantStringView) && ((int)retVal.length() > maxShowSize))
	{
		retVal = retVal.Substring(0, maxShowSize);
		wasTerminated = false;
	}

	if (wasTerminated)
		retVal += "\"";
	else
		retVal += "...";

	return retVal;
}

bool WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength)
{
	bool success = true;

	for (int i = 0; i < (int)evalStr.length(); i++)
	{
		char c = evalStr[i];
		char nextC = 0;
		if (i < (int)evalStr.length() - 1)
			nextC = evalStr[i + 1];
		if ((c == '{') && (nextC != '{'))
		{
			// Evaluate

			int endIdx = i;
			for (; endIdx < (int)evalStr.length(); endIdx++)
			{
				//TODO: Do better parsing - this paren could be inside a string, for example
				if (evalStr[endIdx] == '}')
					break;
			}

			DwFormatInfo displayStrFormatInfo = formatInfo;
			displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + (int)displayString.length();
			displayStrFormatInfo.mHidePointers = false;

			if ((limitLength) && (displayStrFormatInfo.mTotalSummaryLength > 255))
			{
				displayString += "...";
			}
			else
			{
				String evalString = evalStr.Substring(i + 1, endIdx - i - 1);
				String errors;
				DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, useTypedValue, evalString, &displayStrFormatInfo, NULL, &errors);
				if (evalResult)
				{
					if (displayStrFormatInfo.mNoEdit)
						formatInfo.mNoEdit = true;

					String result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);

					if ((formatInfo.mRawString) && (limitLength))
					{
						displayString = result;
						return success;
					}

					if (displayStrFormatInfo.mRawString)
					{
						displayString += result;
					}
					else
					{
						int crPos = result.IndexOf('\n');
						if (crPos != -1)
							displayString += result.Substring(0, crPos);
						else
							displayString += result;
					}
				}
				else if (debugVis != NULL)
				{
					success = false;
					displayString += "<DbgVis Failed>";
					DbgVisFailed(debugVis, evalString, errors);
				}
				else
				{
					success = false;
					displayString += "<Eval Failed>";
				}
			}

			i = endIdx;
			continue;
		}
		else if ((c == '{') && (nextC == '{'))
		{
			// Skip next paren
			i++;
		}
		else if ((c == '}') && (nextC == '}'))
		{
			// Skip next paren
			i++;
		}

		displayString += c;
	}

	return success;
}

static bool IsNormalChar(uint32 c)
{
	return (c < 0x80);
}

String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, const StringImpl& expr, DwFormatInfo& formatInfo, DbgExprEvaluator* optEvaluator, bool fullPrecision)
{
	BP_ZONE("WinDebugger::DbgTypedValueToString");

	DbgTypedValue typedValue = origTypedValue;
	auto dbgCompileUnit = typedValue.mType->mCompileUnit;
	auto dbgModule = typedValue.mType->GetDbgModule();
	auto language = origTypedValue.mType->GetLanguage();
	if (language == DbgLanguage_Unknown)
		language = formatInfo.mLanguage;
	formatInfo.mLanguage = language;
	bool isBeef = language == DbgLanguage_Beef;

	char str[32];
	bool readFailed = false;
	bool isCompositeType = false;
	bool isSizedArray = false;
	bool isEnum = false;
	int64 enumVal = 0;
	String result;
	String stringViewData;

	DwDisplayInfo* displayInfo = GetDisplayInfo(formatInfo.mReferenceId);
	bool wantStringView = (displayInfo->mFormatStr == "str") && (formatInfo.mAllowStringView);

	DbgType* origValueType = typedValue.mType;
	bool origHadRef = false;
	DbgType* dwValueType = typedValue.mType->RemoveModifiers(&origHadRef);

	if (dwValueType == NULL)
		dwValueType = dbgModule->GetPrimitiveType(DbgType_Void, language);
	else
		dwValueType = dwValueType->GetPrimaryType();

	if (dwValueType->mTypeCode == DbgType_TypeDef)
	{
		DbgTypedValue realTypedVal = typedValue;
		realTypedVal.mType = dwValueType->mTypeParam;
		return DbgTypedValueToString(realTypedVal, expr, formatInfo, optEvaluator);
	}

	if (formatInfo.mRawString)
	{
		if ((dwValueType->mTypeCode != DbgType_Struct) && (dwValueType->mTypeCode != DbgType_Class) && (dwValueType->mTypeCode != DbgType_Ptr) && (dwValueType->mTypeCode != DbgType_SizedArray))
			return "";
	}

	auto _ShowArraySummary = [&](String& retVal, addr_target ptrVal, int64 arraySize, DbgType* innerType)
	{
		String displayString;
		displayString += "{";
		for (int idx = 0; idx < arraySize; idx++)
		{
			if (formatInfo.mTotalSummaryLength + retVal.length() + displayString.length() > 255)
			{
				displayString += "...";
				break;
			}

			if ((idx != 0) && (!displayString.EndsWith('{')))
				displayString += ", ";

			DwFormatInfo displayStrFormatInfo = formatInfo;
			displayStrFormatInfo.mExpandItemDepth = 1;
			displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + retVal.length() + displayString.length();
			displayStrFormatInfo.mHidePointers = false;
			displayStrFormatInfo.mArrayLength = -1;

			// Why did we have this "na" on here? It made "void*[3]" type things show up as "{,,}"
			//String evalStr = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + StrFormat(")[%d], na", idx);
			String evalStr = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + StrFormat(")[%lld]", idx);
			DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, typedValue, evalStr, &displayStrFormatInfo);
			String result;
			if (evalResult)
			{
				result = DbgTypedValueToString(evalResult, evalStr, displayStrFormatInfo, NULL);
				int crPos = result.IndexOf('\n');
				if (crPos != -1)
					result.RemoveToEnd(crPos);
			}
			else
				result = "???";

			displayString += result;
		}
		displayString += "}";
		retVal += displayString;
	};

	if (formatInfo.mArrayLength != -1)
	{
		if (formatInfo.mRawString)
			return "";

		if (dwValueType->IsPointer())
		{
			String retVal;
			addr_target ptrVal = (addr_target)typedValue.mPtr;
			if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
			{
				retVal = EncodeDataPtr(ptrVal, true) + " ";
				retVal += dwValueType->mTypeParam->ToString(language);
				retVal += StrFormat("[%lld] ", (int64)formatInfo.mArrayLength);
			}

			_ShowArraySummary(retVal, ptrVal, formatInfo.mArrayLength, dwValueType->mTypeParam);

			String idxStr = "[{0}]";

			DbgType* innerType = dwValueType->mTypeParam;

			retVal += "\n" + dwValueType->ToString(language);

			String evalStr = "*((" + typedValue.mType->ToStringRaw(language) + ")" + EncodeDataPtr(ptrVal, true) + " + {0})";

			retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, (int)BF_MAX(formatInfo.mArrayLength, 0), 10000) +
				"\t" + idxStr + "\t" + evalStr;
			return retVal;
		}
		else
		{
			DwFormatInfo newFormatInfo = formatInfo;
			newFormatInfo.mArrayLength = -1;

			String retVal = DbgTypedValueToString(typedValue, expr, newFormatInfo, optEvaluator);

			int crPos = (int)retVal.IndexOf('\n');
			if (crPos != -1)
				retVal = "!Array length flag not valid with this type" + retVal.Substring(crPos);
			return retVal;
		}
	}

	switch (dwValueType->mTypeCode)
	{
	case DbgType_Void:
		return "\nvoid";
	case DbgType_Bool:
		{
			if (typedValue.mUInt8 == 0)
				return "false\n" + WrapWithModifiers("bool", origValueType, language);
			else if (typedValue.mUInt8 == 1)
				return "true\n" + WrapWithModifiers("bool", origValueType, language);
			else
				return StrFormat("true (%d)\n", typedValue.mUInt8) + WrapWithModifiers("bool", origValueType, language);
		}
		break;
	case DbgType_UChar:
		if (language != DbgLanguage_Beef)
			return IntTypeToString<uint8>(typedValue.mUInt8, WrapWithModifiers("uint8_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_SChar:
		{
			if (typedValue.mInt8 != 0)
			{
				char str[2] = {(char)typedValue.mInt8};
				result = SlashString(str, formatInfo.mDisplayType == DwDisplayType_Utf8, true);

				if (!IsNormalChar(typedValue.mUInt8))
					result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt8);
				else
					result = StrFormat("'%s'\n", result.c_str());
			}
			else
				result = "'\\0'\n";
			return result + WrapWithModifiers("char", origValueType, language);
		}
		break;
	case DbgType_UChar16:
		if (language != DbgLanguage_Beef)
			return IntTypeToString<int16>(typedValue.mUInt8, WrapWithModifiers("uint16_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_SChar16:
		{
			if (typedValue.mInt16 != 0)
			{
				u8_toutf8(str, 8, typedValue.mUInt32);
				result = SlashString(str, true, true);
				if (!IsNormalChar(typedValue.mUInt32))
					result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt16);
				else
					result = StrFormat("'%s'\n", result.c_str());
			}
			else
				result = "'\\0'\n";
			return result + WrapWithModifiers(isBeef ? "char16" : "int16_t", origValueType, language);
		}
		break;
	case DbgType_UChar32:
	case DbgType_SChar32:
		{
			if (typedValue.mInt32 != 0)
			{
				u8_toutf8(str, 8, typedValue.mUInt32);
				result = SlashString(str, true, true);
				if (!IsNormalChar(typedValue.mUInt32))
					result = StrFormat("'%s' (0x%02X)\n", result.c_str(), typedValue.mUInt32);
				else
					result = StrFormat("'%s'\n", result.c_str());
			}
			else
				result = "'\\0'\n";
			return result + WrapWithModifiers(isBeef ? "char32" : "int32_t", origValueType, language);
		}
		break;
	case DbgType_i8:
		return IntTypeToString<int8>(typedValue.mInt8, WrapWithModifiers(isBeef ? "int8" : "int8_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_u8:
		return IntTypeToString<uint8>(typedValue.mUInt8, WrapWithModifiers(isBeef ? "uint8" : "uint8_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_i16:
		return IntTypeToString<int16>(typedValue.mInt16, WrapWithModifiers(isBeef ? "int16" : "int16_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_u16:
		return IntTypeToString<uint16>(typedValue.mUInt16, WrapWithModifiers(isBeef ? "uint16" : "uint16_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_i32:
		return IntTypeToString<int32>(typedValue.mInt32, WrapWithModifiers(isBeef ? "int32" : "int32_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_u32:
		return IntTypeToString<uint32>(typedValue.mUInt32, WrapWithModifiers(isBeef ? "uint32" : "uint32_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_i64:
		return IntTypeToString<int64>(typedValue.mInt64, WrapWithModifiers(isBeef ? "int64" : "int64_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_u64:
		return IntTypeToString<uint64>(typedValue.mUInt64, WrapWithModifiers(isBeef ? "uint64" : "uint64_t", origValueType, language), displayInfo, formatInfo);
	case DbgType_RegGroup:
		{
			if ((typedValue.mRegNum >= CPUReg_M128_XMMREG_FIRST) && (typedValue.mRegNum <= CPUReg_M128_XMMREG_LAST))
			{
				int callStackIdx = formatInfo.mCallStackIdx;
				FixCallStackIdx(callStackIdx);

				UpdateRegisterUsage(callStackIdx);
				WdStackFrame* wdStackFrame = mCallStack[callStackIdx];
				RegForm regForm = RegForm_Unknown;
				if (typedValue.mRegNum < (int)wdStackFrame->mRegForms.size())
					regForm = wdStackFrame->mRegForms[typedValue.mRegNum];

				int xmmMajor = typedValue.mRegNum - CPUReg_M128_XMMREG_FIRST;
				String headerStr;

				String xmmType = "__m128";

				int xmmCount = 4;
				if ((regForm == RegForm_Double) || (regForm == RegForm_Double2) ||
					(regForm == RegForm_Long) || (regForm == RegForm_Long2) ||
					(regForm == RegForm_ULong) || (regForm == RegForm_ULong2))
					xmmCount = 2;
				//TODO: add byte, short, int, etc...

				if (optEvaluator)
				{
					DwMmDisplayType mmDwMmDisplayType = displayInfo->mMmDisplayType;
					if (mmDwMmDisplayType == DwMmDisplayType_Default)
					{
						if ((regForm == RegForm_Double) || (regForm == RegForm_Double2))
							mmDwMmDisplayType = DwMmDisplayType_Double;
						else if (regForm == RegForm_Int4)
							mmDwMmDisplayType = DwMmDisplayType_Int32;
					}

					if (mmDwMmDisplayType == DwMmDisplayType_Double)
					{
						xmmType = "__m128d";
						xmmCount = 2;
						double xmmRegVals[2];
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_Double);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mDouble;
						}
						headerStr = StrFormat("(%f, %f)", xmmRegVals[0], xmmRegVals[1]);
					}
					else if (mmDwMmDisplayType == DwMmDisplayType_UInt8)
					{
						int xmmRegVals[4];
						xmmCount = 16;
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < BF_ARRAY_COUNT(xmmRegVals); ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mInt32;
						}
						headerStr = StrFormat("(%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)",
							xmmRegVals[0] & 0xFF, (xmmRegVals[0] >> 8) & 0xFF, (xmmRegVals[0] >> 16) & 0xFF, (xmmRegVals[0] >> 24) & 0xFF,
							xmmRegVals[1] & 0xFF, (xmmRegVals[1] >> 8) & 0xFF, (xmmRegVals[1] >> 16) & 0xFF, (xmmRegVals[1] >> 24) & 0xFF,
							xmmRegVals[2] & 0xFF, (xmmRegVals[2] >> 8) & 0xFF, (xmmRegVals[2] >> 16) & 0xFF, (xmmRegVals[2] >> 24) & 0xFF,
							xmmRegVals[3] & 0xFF, (xmmRegVals[3] >> 8) & 0xFF, (xmmRegVals[3] >> 16) & 0xFF, (xmmRegVals[3] >> 24) & 0xFF);
					}
					else if (mmDwMmDisplayType == DwMmDisplayType_Int16)
					{
						int xmmRegVals[4];
						xmmCount = 8;
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < BF_ARRAY_COUNT(xmmRegVals); ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mInt32;
						}
						headerStr = StrFormat("(%d, %d, %d, %d, %d, %d, %d, %d)",
							xmmRegVals[0] & 0xFFFF, (xmmRegVals[0] >> 16) & 0xFFFF,
							xmmRegVals[1] & 0xFFFF, (xmmRegVals[1] >> 16) & 0xFFFF,
							xmmRegVals[2] & 0xFFFF, (xmmRegVals[2] >> 16) & 0xFFFF,
							xmmRegVals[3] & 0xFFFF, (xmmRegVals[3] >> 16) & 0xFFFF);
					}
					else if (mmDwMmDisplayType == DwMmDisplayType_Int32)
					{
						int xmmRegVals[4];
						xmmCount = 4;
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i32);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mInt32;
						}
						headerStr = StrFormat("(%d, %d, %d, %d)", xmmRegVals[0], xmmRegVals[1], xmmRegVals[2], xmmRegVals[3]);
					}
					else if (mmDwMmDisplayType == DwMmDisplayType_Int64)
					{
						int64 xmmRegVals[2];
						xmmCount = 2;
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_i64);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mInt64;
						}
						headerStr = StrFormat("(%lld, %lld)", xmmRegVals[0], xmmRegVals[1]);
					}
					else // float
					{
						float xmmRegVals[4];
						xmmCount = 4;
						CPURegisters* regs = optEvaluator->GetRegisters();
						for (int xmmMinor = 0; xmmMinor < xmmCount; ++xmmMinor)
						{
							DbgTypedValue xmmReg = GetRegister(StrFormat("xmm%d_%d", xmmMajor, xmmMinor), language, regs, &wdStackFrame->mRegForms);
							BF_ASSERT(xmmReg.mType->mTypeCode == DbgType_Single);
							BF_ASSERT(xmmReg.mRegNum == CPUReg_XMMREG_FIRST + (xmmMajor * 4) + xmmMinor);
							xmmRegVals[xmmMinor] = xmmReg.mSingle;
						}
						headerStr = StrFormat("(%f, %f, %f, %f)", xmmRegVals[0], xmmRegVals[1], xmmRegVals[2], xmmRegVals[3]);
					}
				}
				else
				{
					headerStr = StrFormat("XMM%d", xmmMajor);
				}

				result = headerStr + "\n" + xmmType;
				for (int i = 0; i < xmmCount; i++)
				{
					if (xmmCount == 16)
						result += WrapWithModifiers(StrFormat("\n[%d]\t(uint8)($xmm%d_%d >> %d)", i, xmmMajor, i / 4, (i % 4)*8), origValueType, language);
					else if (xmmCount == 8)
						result += WrapWithModifiers(StrFormat("\n[%d]\t(int16)($xmm%d_%d >> %d)", i, xmmMajor, i / 2, (i % 2)*8), origValueType, language);
					else
						result += WrapWithModifiers(StrFormat("\n[%d]\t$xmm%d_%d", i, xmmMajor, i), origValueType, language);
				}

				return result;
			}
			else
			{
				switch (typedValue.mRegNum)
				{
				case CPUReg_CAT_ALLREGS:
				{
					return "ALLREGS\n__allregs\niregs\t$iregs\nflags\t$flags\nfpregs\t$fpregs\nmmregs\t$mmregs\nxmmregs\t$xmmregs";
				}
				break;
				case CPUReg_CAT_IREGS:
				{
#ifdef BF_DBG_32
					String headerStr;
					if (optEvaluator)
					{
						CPURegisters* regs = optEvaluator->GetRegisters();
						headerStr = StrFormat("(eax=0x%08x, ebx=0x%08x, ecx=0x%08x, edx=0x%08x, esi=0x%08x, edi=0x%08x, esp=0x%08x, ebp=0x%08x, eip=0x%08x, efl=0x%08x)",
							(uint32)regs->mIntRegs.eax, (uint32)regs->mIntRegs.ebx, (uint32)regs->mIntRegs.ecx, (uint32)regs->mIntRegs.edx,
							(uint32)regs->mIntRegs.esi, (uint32)regs->mIntRegs.edi, (uint32)regs->mIntRegs.esp, (uint32)regs->mIntRegs.ebp,
							(uint32)regs->mIntRegs.eip, (uint32)regs->mIntRegs.efl);
					}
					else
					{
						headerStr = "IREGS";
					}

					return StrFormat("%s\n__iregs\neax\t$eax\nebx\t$ebx\necx\t$ecx\nedx\t$edx\nesi\t$esi\nedi\t$edi\nesp\t$esp\nebp\t$ebp\neip\t$eip", headerStr.c_str());
#else
					String headerStr;
					if (optEvaluator)
					{
						CPURegisters* regs = optEvaluator->GetRegisters();
						headerStr = StrFormat("(rax=0x%@, rbx=0x%@, rcx=0x%@, rdx=0x%@, rsi=0x%@, rdi=0x%@, rsp=0x%@, rbp=0x%@, eip=0x%@, r8=0x%@, r9=0x%@, r10=0x%@, r11=0x%@, r12=0x%@, r13=0x%@, r14=0x%@, r15=0x%@, efl=0x%08x)",
							(uint64)regs->mIntRegs.rax, (uint64)regs->mIntRegs.rbx, (uint64)regs->mIntRegs.rcx, (uint64)regs->mIntRegs.rdx,
							(uint64)regs->mIntRegs.rsi, (uint64)regs->mIntRegs.rdi, (uint64)regs->mIntRegs.rsp, (uint64)regs->mIntRegs.rbp,
							(uint64)regs->mIntRegs.rip,
							(uint64)regs->mIntRegs.r8, (uint64)regs->mIntRegs.r9, (uint64)regs->mIntRegs.r10, (uint64)regs->mIntRegs.r11,
							(uint64)regs->mIntRegs.r12, (uint64)regs->mIntRegs.r13, (uint64)regs->mIntRegs.r14, (uint64)regs->mIntRegs.r15,
							(uint32)regs->mIntRegs.efl);
					}
					else
					{
						headerStr = "IREGS";
					}

					return StrFormat("%s\n__iregs\neax\t$eax\nebx\t$ebx\necx\t$ecx\nedx\t$edx\nesi\t$esi\nedi\t$edi\nesp\t$esp\nebp\t$ebp\neip\t$eip\nr8\t$r8\nr9\t$r9\nr10\t$r10\nr11\t$r11\nr12\t$r12\nr13\t$r13\nr14\t$r14\nr15\t$r15", headerStr.c_str());
#endif
				}
				break;
				case CPUReg_CAT_FPREGS:
				{
					String headerStr;
					if (optEvaluator)
					{
						CPURegisters* regs = optEvaluator->GetRegisters();
						headerStr = "(";
						for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
						{
							if (i)
								headerStr += ", ";
							double val = ConvertFloat80ToDouble(regs->mFpMmRegsArray[i].fp.fp80);
							headerStr += StrFormat("%f", val);
						}
						headerStr += ")";
					}
					else
					{
						headerStr = "FPREGS";
					}

					result = StrFormat("%s\n__fpregs", headerStr.c_str());
					for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
						result += StrFormat("\n[%d]\t$st%d", i, i);

					return result;
				}
				break;
				case CPUReg_CAT_MMREGS:
				{
					String headerStr;
					if (optEvaluator)
					{
						CPURegisters* regs = optEvaluator->GetRegisters();
						headerStr = "(";
						for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
						{
							if (i)
								headerStr += ", ";
							uint64 val = regs->mFpMmRegsArray[i].mm;
							headerStr += StrFormat("0x%016llx", val);
						}
						headerStr += ")";
					}
					else
					{
						headerStr = "MMREGS";
					}

					result = StrFormat("%s\n__mmregs", headerStr.c_str());
					for (int i = 0; i < CPURegisters::kNumFpMmRegs; ++i)
						result += StrFormat("\n[%d]\t$mm%d", i, i);

					return result;
				}
				break;
				case CPUReg_CAT_XMMREGS:
				{
					String headerStr = StrFormat("XMMREGS[%d]", CPURegisters::kNumXmmRegs); // these are too big to put a useful header for the entire category

					result = StrFormat("%s\n__xmmregs", headerStr.c_str());
					for (int i = 0; i < CPURegisters::kNumXmmRegs; ++i)
						result += StrFormat("\n[%d]\t$xmm%d", i, i);

					return result;
				}
				break;
				case CPUReg_CAT_FLAGS:
				{
					String headerStr;
					if (optEvaluator)
					{
						CPURegisters* regs = optEvaluator->GetRegisters();
#ifdef BF_DBG_32
#define FLAGVAR(abbr, name) int flag##abbr = ((regs->mIntRegs.efl & ((uint64)1 << CPURegisters::GetFlagBitForRegister(X86Reg_FLAG_##abbr##_##name))) != 0) ? 1 : 0
						FLAGVAR(CF, CARRY);
						FLAGVAR(PF, PARITY);
						FLAGVAR(AF, ADJUST);
						FLAGVAR(ZF, ZERO);
						FLAGVAR(SF, SIGN);
						FLAGVAR(IF, INTERRUPT);
						FLAGVAR(DF, DIRECTION);
						FLAGVAR(OF, OVERFLOW);
#undef FLAGVAR
#else
#define FLAGVAR(abbr, name) int flag##abbr = ((regs->mIntRegs.efl & ((uint64)1 << CPURegisters::GetFlagBitForRegister(X64Reg_FLAG_##abbr##_##name))) != 0) ? 1 : 0
						FLAGVAR(CF, CARRY);
						FLAGVAR(PF, PARITY);
						FLAGVAR(AF, ADJUST);
						FLAGVAR(ZF, ZERO);
						FLAGVAR(SF, SIGN);
						FLAGVAR(IF, INTERRUPT);
						FLAGVAR(DF, DIRECTION);
						FLAGVAR(OF, OVERFLOW);
#undef FLAGVAR
#endif
						headerStr = StrFormat("(CF=%d, PF=%d, AF=%d, ZF=%d, SF=%d, IF=%d, DF=%d, OF=%d)",
							flagCF, flagPF, flagAF, flagZF, flagSF, flagIF, flagDF, flagOF);
					}
					else
					{
						headerStr = "FLAGS";
					}

					return StrFormat("%s\n__flags\nCarry (CF)\t$flagcf\nParity (PF)\t$flagpf\nAdjust (AF)\t$flagaf\nZero (ZF)\t$flagzf\nSign (SF)\t$flagsf\nInterrupt (IF)\t$flagif\nDirection (DF)\t$flagdf\nOverflow (OF)\t$flagof",
						headerStr.c_str());
				}
				break;
				default:
					BF_ASSERT(false && "unknown category register");
					return "UNKNOWNCATEGORY\n__unknown\n";
				}
			}
		}
		break;
	case DbgType_Single:
		{
			DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
			if (floatDisplayType == DwFloatDisplayType_Default)
				floatDisplayType = DwFloatDisplayType_Minimal;
			if (floatDisplayType == DwFloatDisplayType_Minimal)
				ExactMinimalFloatToStr(typedValue.mSingle, str);
			else if (floatDisplayType == DwFloatDisplayType_Full)
				sprintf(str, "%1.9g", typedValue.mSingle);
			else if (floatDisplayType == DwFloatDisplayType_HexUpper)
				sprintf(str, "0x%04X", typedValue.mUInt32);
			else //if (floatDisplayType == DwFloatDisplayType_HexLower)
				sprintf(str, "0x%04x", typedValue.mUInt32);
			return StrFormat("%s\n%s", str, WrapWithModifiers("float", origValueType, language).c_str());
		}
	case DbgType_Double:
		{
			DwFloatDisplayType floatDisplayType = displayInfo->mFloatDisplayType;
			if (floatDisplayType == DwFloatDisplayType_Default)
				floatDisplayType = DwFloatDisplayType_Minimal;
			if (floatDisplayType == DwFloatDisplayType_Minimal)
				ExactMinimalDoubleToStr(typedValue.mDouble, str);
			else if (floatDisplayType == DwFloatDisplayType_Full)
				sprintf(str, "%1.17g", typedValue.mDouble);
			else if (floatDisplayType == DwFloatDisplayType_HexUpper)
				sprintf(str, "0x%08llX", typedValue.mUInt64);
			else //if (floatDisplayType == DwFloatDisplayType_HexLower)
				sprintf(str, "0x%08llx", typedValue.mUInt64);
			return StrFormat("%s\n%s", str, WrapWithModifiers("double", origValueType, language).c_str());
		}
	case DbgType_Subroutine:
		if (typedValue.mCharPtr != NULL)
			return StrFormat("%s\nfunc", typedValue.mCharPtr);
		else
			return "\nfunc";
	case DbgType_RawText:
		return StrFormat("%s\nrawtext", typedValue.mCharPtr);
	case DbgType_Ptr:
	{
		addr_target ptrVal = (addr_target)typedValue.mPtr;
		String retVal;
		DbgType* innerType = dwValueType->mTypeParam;
		if (innerType == NULL)
			return EncodeDataPtr(ptrVal, true) + "\nvoid*";

		bool isChar = false;

		DbgType* unmodInnerType = innerType->RemoveModifiers();
		if (unmodInnerType != NULL)
		{
			if (language == DbgLanguage_Beef)
			{
				if ((unmodInnerType->mTypeCode == DbgType_UChar) ||
					(unmodInnerType->mTypeCode == DbgType_UChar16) ||
					(unmodInnerType->mTypeCode == DbgType_UChar32))
					isChar = true;
			}
			else
			{
				if ((unmodInnerType->mTypeCode == DbgType_SChar) ||
					(unmodInnerType->mTypeCode == DbgType_SChar16) ||
					(unmodInnerType->mTypeCode == DbgType_SChar32))
					isChar = true;
			}
		}

		if ((isChar) && (formatInfo.mArrayLength == -1))
		{
			if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
				retVal = EncodeDataPtr(ptrVal, true);

			int strLen = formatInfo.mOverrideCount;
			if (typedValue.mIsLiteral)
			{
				if (strLen == -1)
					strLen = 0x7FFFFFFF;
				if (typedValue.mDataLen > 0)
					strLen = BF_MIN(strLen, typedValue.mDataLen);
				else
					strLen = BF_MIN(strLen, strlen(typedValue.mCharPtr));
			}

			SetAndRestoreValue<intptr> prevOverrideLen(formatInfo.mOverrideCount, strLen);
			String strResult = ReadString(unmodInnerType->mTypeCode, typedValue.mLocalIntPtr, typedValue.mIsLiteral, strLen, formatInfo, wantStringView);
			if (formatInfo.mRawString)
				return strResult;
			if (!strResult.IsEmpty())
			{
				if (!retVal.IsEmpty())
					retVal += " ";
				if (!wantStringView)
					retVal += strResult;
			}
			retVal += "\n" + origValueType->ToString(language);
			retVal += "\n:stringView";
			if (wantStringView)
			{
				retVal += "\t";
				retVal += SlashString(strResult, false, false, true);
			}
			return retVal;
		}
		else if ((unmodInnerType != NULL) &&
			((unmodInnerType->mTypeCode == DbgType_Class) || (unmodInnerType->mTypeCode == DbgType_Struct) || (unmodInnerType->mTypeCode == DbgType_Union)))
		{
			isCompositeType = true;
		}
		else if ((unmodInnerType != NULL) && (unmodInnerType->mTypeCode == DbgType_SizedArray))
		{
			isSizedArray = true;
		}
		else if (unmodInnerType->mTypeCode == DbgType_Subroutine)
		{
			if (formatInfo.mRawString)
				return "";
			addr_target funcPtr = (addr_target)typedValue.mPtr;
			String retVal;
			if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
				retVal = EncodeDataPtr(funcPtr, true);

			String symbolName;
			addr_target offset;
			DbgModule* dwarf;
			static String demangledName;
			auto subProgram = mDebugTarget->FindSubProgram(funcPtr);
			if (subProgram != NULL)
			{
				demangledName = subProgram->ToString();
			}
			else if (mDebugTarget->FindSymbolAt(funcPtr, &symbolName, &offset, &dwarf))
			{
				demangledName = BfDemangler::Demangle(symbolName, language);

				if (offset != 0)
					demangledName += StrFormat("+%d", offset);
			}
			else
			{
				auto dbgModule = mDebugTarget->FindDbgModuleForAddress(funcPtr);
				if (dbgModule != NULL)
					demangledName += dbgModule->GetLinkedModule()->mDisplayName + "!";
				demangledName += StrFormat("0x%@", funcPtr);
			}

			retVal += " {";
			retVal += demangledName;
			retVal += "}";
			retVal += "\n" + origValueType->ToString(language);

			return retVal;
		}
		else if (unmodInnerType->mTypeCode == DbgType_Void)
		{
			if (formatInfo.mRawString)
				return "";
			addr_target ptr = (addr_target)typedValue.mPtr;
			String symbolName;
			addr_target offset;
			DbgModule* dwarf;
			String demangledName;

			retVal += demangledName = StrFormat("0x%@", ptr);

			if (mDebugTarget->FindSymbolAt(ptr, &symbolName, &offset, &dwarf))
			{
				if (offset == 0)
				{
					retVal += " {";
					retVal += BfDemangler::Demangle(symbolName, language);
					retVal += "}";
				}
			}

			retVal += "\n" + origValueType->ToString(language);

			return retVal;
		}
		else
		{
			if (formatInfo.mRawString)
				return "";
			addr_target ptrVal = (addr_target)typedValue.mPtr;
			String retVal;
			if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
				retVal = EncodeDataPtr(ptrVal, true);

			if (ptrVal != 0)
			{
				DbgExprEvaluator dbgExprEvaluator(this, dbgModule, NULL, -1, -1);
				DbgTypedValue innerTypedVal = dbgExprEvaluator.ReadTypedValue(NULL, innerType, typedValue.mPtr, DbgAddrType_Target);
				if (innerTypedVal)
				{
					DwFormatInfo defaultFormatInfo;
					defaultFormatInfo.mLanguage = formatInfo.mLanguage;
					defaultFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + 2; // Take into accout the necessary {}'s
					defaultFormatInfo.mExpandItemDepth++;
					String innerStr = DbgTypedValueToString(innerTypedVal, "", defaultFormatInfo, &dbgExprEvaluator);
					int crIdx = innerStr.IndexOf('\n');
					if (crIdx != -1)
					{
						String innerDataStr = innerStr.Substring(0, crIdx);
						if (!innerDataStr.empty())
						{
							if (!retVal.empty())
								retVal += " ";
							retVal += "{" + innerDataStr + "}";
						}
					}
					else
					{
						retVal += "{ ??? }";
					}
				}
			}

			retVal += "\n" + origValueType->ToString(language);
			innerType->PopulateType();

			if ((ptrVal != 0) &&
				((!innerType->mMemberList.IsEmpty()) || (innerType->mSize > 0) || (innerType->mTypeParam != NULL)))
			{
				String ptrDataStr = StrFormat("(%s)", dwValueType->ToStringRaw(language).c_str()) + EncodeDataPtr(typedValue.mPtr, true);
				retVal += "\n*\t";
				// Why did we have this?  It messed up a pointer to sized array
				/*if (language == DbgLanguage_Beef)
					retVal += "this";
				else*/
					retVal += "*this";

				if (!formatInfo.mReferenceId.empty())
					retVal += ", refid=" + MaybeQuoteFormatInfoParam(formatInfo.mReferenceId);

				retVal += ", this=" + ptrDataStr;
			}
			return retVal;
		}

		break;
	}
	case DbgType_Union:
	case DbgType_Class:
	case DbgType_Struct:
		isCompositeType = true;
		break;
	case DbgType_Enum:
		enumVal = typedValue.GetInt64();
		isEnum = true;
		break;
	case DbgType_SizedArray:
		{
			isSizedArray = true;
		}
		break;
	default:
		break;
	}

	if (isSizedArray)
	{
		String retVal;
		addr_target ptrVal = 0;

		DbgType* arrayType = dwValueType;
		DbgType* innerType = dwValueType->mTypeParam;
		if (dwValueType->mTypeCode == DbgType_SizedArray)
		{
			ptrVal = (addr_target)typedValue.mSrcAddress;
		}
		else
		{
			BF_ASSERT(dwValueType->mTypeCode == DbgType_Ptr);
			arrayType = innerType;
			innerType = arrayType->mTypeParam;
			ptrVal = typedValue.mPtr;

			if ((!typedValue.mIsLiteral) && (!formatInfo.mHidePointers))
				retVal = EncodeDataPtr(ptrVal, true) + " ";
		}
		if (ptrVal == 0)
			ptrVal = typedValue.mPtr;

		intptr arraySize = 0;
		intptr innerSize = innerType->GetStride();
		if (innerSize > 0)
			arraySize = arrayType->GetStride() / innerSize;
		else
		{
			// Failure!
		}

		String idxStr = "[{0}]";

		if (innerType->IsChar(language))
		{
			String strVal = ReadString(innerType->mTypeCode, typedValue.mSrcAddress, false, arraySize, formatInfo, false);
			if (formatInfo.mRawString)
				return strVal;
			retVal += strVal;
		}
		else
		{
			if (formatInfo.mRawString)
				return "";

			_ShowArraySummary(retVal, ptrVal, arraySize, innerType);
		}

		retVal += "\n" + origValueType->ToString(language);

		String referenceId = dwValueType->ToString(language);
		String evalStr;

		// Why did we have the "na"? Do we not want to show addresses for all members?
		evalStr = "((" + innerType->ToStringRaw(language) + "*)" + EncodeDataPtr(ptrVal, true) + ")[{0}], refid=" + MaybeQuoteFormatInfoParam(referenceId + ".[]");
		if (typedValue.mIsReadOnly)
			evalStr += ", ne";
		retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, (int)BF_MAX(arraySize, 0), 10000) +
			"\t" + idxStr + "\t" + evalStr;
		return retVal;
	}

	dwValueType->PopulateType();

	if (isEnum)
	{
		String retVal;
		int64 bitsLeft = enumVal;
		int valueCount = 0;

		String editVal;

		dwValueType = dwValueType->GetPrimaryType();

		dwValueType->PopulateType();

		while ((bitsLeft != 0) || (valueCount == 0))
		{
			DbgVariable* bestMatch = NULL;

			for (auto member : dwValueType->mMemberList)
			{
				if (member->mConstValue == bitsLeft)
				{
					bestMatch = member;
					break;
				}
			}

			if (bestMatch == NULL)
			{
				for (auto member : dwValueType->mMemberList)
				{
					if ((member->mConstValue != 0) &&
						((member->mConstValue & bitsLeft) == member->mConstValue))
					{
						bestMatch = member;
						break;
					}
				}
			}

			if (bestMatch == NULL)
				break;

			if (valueCount > 0)
			{
				retVal += " | ";
				if (language == DbgLanguage_C)
					editVal += " | ";
			}

			if (language == DbgLanguage_Beef)
				retVal += ".";
			retVal += bestMatch->mName;

			if (language == DbgLanguage_C)
			{
				if (dwValueType->mParent != NULL)
				{
					editVal += dwValueType->mParent->ToString(language);
					editVal += "::";
				}
				editVal += bestMatch->mName;
			}

			valueCount++;
			bitsLeft &= ~bestMatch->mConstValue;
		}

		if ((valueCount == 0) || (bitsLeft != 0))
		{
			if (valueCount > 0)
				retVal += " | ";
			retVal += StrFormat("%lld", bitsLeft);

			if (language == DbgLanguage_C)
			{
				if (valueCount > 0)
					editVal += " | ";
				editVal += StrFormat("%lld", bitsLeft);
			}
		}

		retVal += "\n" + origValueType->ToString();
		if (language == DbgLanguage_C)
		{
			retVal += "\n:editVal\t";
			retVal += editVal;
		}
		retVal += "\n:canEdit";

		return retVal;
	}
	else if (isCompositeType)
	{
		addr_target ptrVal;
		if (dwValueType->IsPointer())
			ptrVal = (addr_target)typedValue.mPtr;
		else
			ptrVal = (addr_target)typedValue.mSrcAddress;
		String retVal;
		if ((!typedValue.mIsLiteral) && (dwValueType->IsPointer()) &&
			((!formatInfo.mHidePointers) || (ptrVal == 0)))
			retVal = EncodeDataPtr(ptrVal, true);

		DbgType* innerType = dwValueType;
		bool wasPtr = false;
		if (innerType->mTypeCode == DbgType_Ptr)
		{
			wasPtr = true;
			innerType = dwValueType->mTypeParam;
			innerType = innerType->RemoveModifiers();
		}

		innerType = innerType->GetPrimaryType();
		addr_target dataPtr = wasPtr ? typedValue.mPtr : typedValue.mSrcAddress;
		DbgType* actualType = NULL;
		bool useActualRawType = false;

		bool isBfObject = innerType->IsBfObject();
		bool hasCPPVTable = false;
		if (!isBfObject)
			hasCPPVTable = innerType->HasCPPVTable();

		int bfObjectFlags = 0;
		addr_target classVDataPtr = 0;
		bool isAppendBfObject = false;
		bool isStackBfObject = false;
		bool isDeletedBfObject = false;
		bool isCompositeWithoutAddress = false;

		if (innerType->IsBfPayloadEnum())
		{
			if (formatInfo.mRawString)
				return "";

			auto tagMember = innerType->mMemberList.mTail;
			int tagIdx = 0;

			if (dataPtr == -1)
			{
				DbgEvaluationContext dbgEvaluationContext(this, dbgModule, "(int)" + expr, &formatInfo);
				auto dscValue = dbgEvaluationContext.EvaluateInContext(DbgTypedValue());
				tagIdx = dscValue.mInt32;
			}
			else if (!ReadMemory((intptr)ptrVal + tagMember->mMemberOffset, tagMember->mType->mSize, (void*)&tagIdx))
			{
				return StrFormat("!Failed to read from 0x%@", ptrVal);
			}

			char findStr[16];
			findStr[0] = '_';
			itoa(tagIdx, findStr + 1, 10);
			int len = strlen(findStr);
			findStr[len] = '_';
			len++;

			if (!retVal.empty())
				retVal += " ";

			int startIdx = 0;
			for (auto member : innerType->mMemberList)
			{
				if (strncmp(member->mName, findStr, len) == 0)
				{
					retVal += ".";
					retVal += member->mName + len;

					String tupleExpr;

					DbgTypedValue tupleVal;
					if (dataPtr == -1)
					{
						tupleVal.mSrcAddress = -1;
						tupleVal.mType = member->mType;
						//tupleExpr = "$" + expr + "$u";
						tupleVal.mVariable = typedValue.mVariable;
						tupleExpr = "(" + member->mType->ToStringRaw() + ")(" + expr + ")";
					}
					else
					{
						tupleVal.mType = member->mType;
						tupleVal.mSrcAddress = ptrVal;
					}

					DwFormatInfo displayStrFormatInfo = formatInfo;
					displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + (int)retVal.length();
					displayStrFormatInfo.mExpandItemDepth++;
					displayStrFormatInfo.mHidePointers = false;
					retVal += DbgTypedValueToString(tupleVal, tupleExpr, displayStrFormatInfo, NULL);
					int idx = (int)retVal.IndexOf('\n');
					if (idx != -1)
					{
						if ((idx > 2) && (strncmp(retVal.c_str() + idx - 2, "()", 2) == 0))
						{
							// Take off a terminating "()" on the value, if there is one
							retVal.Remove(idx - 2, 2);
						}

						String typeName = innerType->ToString(DbgLanguage_Unknown, true);
						typeName += " ";
						retVal.Insert(idx + 1, typeName);
					}

					return retVal;
				}
			}
		}

		if (isBfObject)
		{
			classVDataPtr = ReadMemory<addr_target>(ptrVal);
			mDebugTarget->GetCompilerSettings();
			if (mDebugTarget->mBfObjectHasFlags)
			{
				bfObjectFlags = ((int)classVDataPtr) & 0xFF;

				if ((bfObjectFlags & BfObjectFlag_Deleted) != 0)
					isDeletedBfObject = true;
				if ((bfObjectFlags & BfObjectFlag_AppendAlloc) != 0)
					isAppendBfObject = true;
				if ((bfObjectFlags & (BfObjectFlag_StackAlloc | BfObjectFlag_Allocated)) == BfObjectFlag_StackAlloc)
					isStackBfObject = true;

				classVDataPtr &= ~0xFF;
			}
		}

		if (!formatInfo.mIgnoreDerivedClassInfo)
		{
			if (isBfObject)
			{
				dbgModule->ParseSymbolData();

				String symbolName;
				addr_target symOffset;
				if ((mDebugTarget->FindSymbolAt(classVDataPtr, &symbolName, &symOffset)) && (symOffset < 0x100))
				{
					String mangledClassName;

					const char* symEnd = "sBfClassVData";
					int symEndLen = strlen(symEnd);
					if (((int)symbolName.length() > symEndLen) && (strstr(symbolName.c_str(), symEnd) != NULL))
						mangledClassName = symbolName;

					// If we have flags then we may be pointing past the _typeData, actually.  We could fix this by masking out
					//  the flags area, but we need to be sure we are running a build that supports flags
					symEnd = "sBfTypeData";
					symEndLen = strlen(symEnd);
					if (((int) symbolName.length() > symEndLen) && (strstr(symbolName.c_str(), symEnd) != NULL))
						mangledClassName = symbolName;

					if (mangledClassName.length() > 0)
					{
						String className = BfDemangler::Demangle(mangledClassName, innerType->GetLanguage(), BfDemangler::Flag_RawDemangle);

						for (int i = 0; i < className.length() - 3; i++)
						{
							if ((className[i] == 'b') &&
								(className[i + 1] == 'f') &&
								(className[i + 2] == '.'))
							{
								bool matches;
								if (i == 0)
									matches = true;
								else
								{
									char prevC = className[i - 1];
									if ((prevC == ' ') ||
										(prevC == ',') ||
										(prevC == '<'))
									{
										matches = true;
									}
								}
								if (matches)
									className.Remove(i, 3);
							}
						}

						int lastDot = (int)className.LastIndexOf('.');
						if (lastDot > 0)
							className = className.Substring(0, lastDot);

						const char* arrPrefix = "System.Array1<";
						if (strncmp(className.c_str(), arrPrefix, strlen(arrPrefix)) == 0)
						{
							className = className.Substring(strlen(arrPrefix), className.length() - strlen(arrPrefix) - 1);
							className += "[]";
						}

						auto typeEntry = dbgModule->GetLinkedModule()->mTypeMap.Find(className.c_str(), DbgLanguage_BeefUnfixed);
						if (typeEntry != NULL)
						{
							actualType = typeEntry->mValue;
							if (!actualType->IsBfObject())
							{
								if (actualType->mTypeCode == DbgType_Ptr)
								{
									actualType = actualType->mTypeParam;
								}
							}
						}
					}
				}
			}
			else if (hasCPPVTable)
			{
				dbgModule->ParseSymbolData();

				addr_target classVDataPtr = ReadMemory<addr_target>(ptrVal);
				String symbolName;

				addr_target offset = 0;
				if (mDebugTarget->FindSymbolAt(classVDataPtr, &symbolName, &offset, NULL))
				{
					// On GNU, vtable indices can "go negative" for things like RTTI and virtual inheritance, so
					//  we can't rely on an exact vtable address lookup
					if (offset < 0x200)
					{
						DbgLanguage lang = innerType->GetLanguage();
						const char* symStart = (innerType->mCompileUnit->mDbgModule->mDbgFlavor == DbgFlavor_GNU) ? "_ZTV" : "??_7";
						if (strncmp(symbolName.c_str(), symStart, strlen(symStart)) == 0)
						{
							//String mangledClassName = symbolName.Substring(1);
							String className = BfDemangler::Demangle(symbolName, lang);
							int vtableNameIdx = (int)className.IndexOf("::`vftable'");
							if (vtableNameIdx != -1)
								className = className.Substring(0, vtableNameIdx);

							auto typeEntry = dbgModule->mTypeMap.Find(className.c_str(), DbgLanguage_C);
							if (typeEntry != NULL)
							{
								actualType = typeEntry->mValue;

								if ((int)className.IndexOf('<') != -1)
									useActualRawType = true;

								int thisOffset = 0;
								if (!DbgExprEvaluator::TypeIsSubTypeOf(actualType, innerType, &thisOffset))
								{
									// This catches virtual inheritance cases where we can't downcast
									actualType = NULL;
								}
							}
						}
					}
				}
			}
		}

		DbgType* displayType = origValueType;

		String displayString;
		bool wantsCustomExpandedItems = false;
		DebugVisualizerEntry* debugVis = NULL;
		Array<String> dbgVisWildcardCaptures;
		DbgType* dwUseType = (actualType != NULL) ? actualType : innerType;
		//auto ptrDataType = dwValueType;
		//TODO: Changed this from the above to account for COFF types where 'this' is always a fwd reference, does this cause any issues?
		auto ptrDataType = innerType;
		String ptrDataStr;
		if (/*(!innerType->IsBfObject()) &&*/ (!ptrDataType->IsPointer()))
		{
			if ((dataPtr != 0) || (ptrDataType->GetByteCount() > sizeof(addr_target)))
			{
				bool wantsRefThis = ptrDataType->WantsRefThis();
				ptrDataType = ptrDataType->GetDbgModule()->GetPointerType(ptrDataType);
				if (wantsRefThis)
					ptrDataStr += "*";
			}
			else
			{
				// Data is inline - must be int-sized or less
				isCompositeWithoutAddress = true;
				dataPtr = typedValue.mPtr;
			}
		}
		String ptrDataTypeStr = ptrDataType->ToStringRaw();
		ptrDataStr += StrFormat("(%s)", ptrDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true);

		DbgType* dwUsePtrType = dwUseType;
		String ptrUseDataStr;
		if (!dwUsePtrType->IsPointer())
		{
			bool wantsRefThis = dwUsePtrType->WantsRefThis();
			dwUsePtrType = dwUsePtrType->GetDbgModule()->GetPointerType(dwUsePtrType);
			if (wantsRefThis)
				ptrUseDataStr += "*";
		}
		String ptrUseDataTypeStr = dwUsePtrType->ToStringRaw();
		ptrUseDataStr += StrFormat("(%s)", ptrUseDataTypeStr.c_str()) + EncodeDataPtr(dataPtr, true);

		if ((origTypedValue.mSrcAddress == -1) && (origTypedValue.mVariable != NULL))
		{
			ptrDataStr = origTypedValue.mVariable->mName;
			if (!origTypedValue.mType->RemoveModifiers()->Equals(origTypedValue.mVariable->mType->RemoveModifiers()))
			{
				//ptrDataStr = StrFormat("(%s)%s", origTypedValue.mType->ToString().c_str(), origTypedValue.mVariable->mName);
				ptrDataStr = expr;
			}
			ptrUseDataStr = ptrDataStr;
		}

		bool isNull = wasPtr && (dataPtr == 0);
		bool isBadSrc = !wasPtr && (dataPtr == 0) && (!dwValueType->IsValuelessType());

		if ((ptrVal == 0) && (dwValueType->IsTypedPrimitive()))
		{
			DbgTypedValue rawVal;
			rawVal.mInt64 = origTypedValue.mInt64;
			rawVal.mType = dwValueType->GetRootBaseType();

			ptrDataStr = "(" + dwUseType->ToStringRaw() + ")";
			ptrDataStr += DbgTypedValueToString(rawVal, expr, formatInfo, optEvaluator, fullPrecision);

			int editValIdx = ptrDataStr.IndexOf(":editVal");
			if (editValIdx != -1)
				ptrDataStr.Remove(0, editValIdx + 9);
			int crPos = (int)ptrDataStr.IndexOf('\n');
			if (crPos != -1)
				ptrDataStr.RemoveToEnd(crPos);
			ptrUseDataStr = ptrDataStr;

			if ((origTypedValue.mRegNum != -1) && (!expr.IsEmpty()) && (!formatInfo.mExplicitThis))
			{
				// There's no address, use direct local identifier
				ptrDataStr = expr;
				ptrUseDataStr = expr;
			}

			// This keeps 'function' types from showing null as "<null parent>"
			isBadSrc = false;
		}
		else if ((ptrVal == 0) && (dwValueType->IsCompositeType()))
		{
		}

		DbgTypedValue useTypedValue = typedValue;
		if ((origHadRef) || ((typedValue.mType->HasPointer()) && (!dwUseType->HasPointer())))
		{
			useTypedValue.mSrcAddress = useTypedValue.mPtr;
			useTypedValue.mPtr = 0;

			if (dwUseType->IsTypedPrimitive())
			{
				int byteCount = dwUseType->GetByteCount();
				if (byteCount <= sizeof(intptr))
				{
					ReadMemory(useTypedValue.mSrcAddress, byteCount, &useTypedValue.mPtr);
				}
			}
		}
		useTypedValue.mType = dwUseType;
		if ((!formatInfo.mNoVisualizers) && (!isNull) && (!isBadSrc))
		{
			if (language == DbgLanguage_Beef)
				dwUseType->FixName();
			debugVis = FindVisualizerForType(dwUseType, &dbgVisWildcardCaptures);
		}

		bool hadCustomDisplayString = false;
		if (debugVis != NULL)
		{
			auto& displayStringList = (formatInfo.mRawString || wantStringView) ? debugVis->mStringViews : debugVis->mDisplayStrings;

			for (auto displayEntry : displayStringList)
			{
				if (!displayEntry->mCondition.empty())
				{
					if (!EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString))
						continue;
				}

				hadCustomDisplayString = true;
				String displayStr = mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures);
				if (displayString.length() > 0)
					displayString += " ";

				if (wantStringView)
				{
					DwFormatInfo strFormatInfo = formatInfo;
					strFormatInfo.mRawString = true;
					ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, stringViewData, strFormatInfo, debugVis, true);
				}
				else
					ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, displayString, formatInfo, debugVis, true);
				if (formatInfo.mRawString)
					return displayString;

				break;
			}

			if ((!debugVis->mExpandItems.empty()) || (debugVis->mCollectionType != DebugVisualizerEntry::CollectionType_None))
			{
				wantsCustomExpandedItems = true;
			}
		}

		if (formatInfo.mRawString)
			return "";

		bool isTuple = (dwUseType->mName != NULL) && (dwUseType->mName[0] == '(') && (language == DbgLanguage_Beef);

		if (isBadSrc)
		{
			displayString += "<null parent>";
		}
		else if ((!isNull) && (!formatInfo.mNoVisualizers) && (!hadCustomDisplayString))
		{
			// Create our own custom display

			String firstRet;
			String bigRet = isTuple ? "(" : "{ ";

			int memberIdx = 0;
			DbgType* summaryType = dwUseType;
			bool summaryDone = false;
			bool truncatedMemberList = false;

			DbgTypedValue summaryTypedValue = useTypedValue;
			String summaryDataStr = ptrDataStr;
			String splatStr;
			if (dataPtr == -1)
				splatStr = expr;

			while (summaryType != NULL)
			{
				summaryType->PopulateType();

				if ((summaryType->IsTypedPrimitive()) &&
					((summaryType->mBaseTypes.IsEmpty()) || (!summaryType->mBaseTypes.front()->mBaseType->IsTypedPrimitive())))
				{
					if (formatInfo.mTotalSummaryLength + (int)displayString.length() > 255)
					{
						truncatedMemberList = true;
						summaryDone = true;
						bigRet += "...";
					}
					else
					{
						DwFormatInfo displayStrFormatInfo = formatInfo;
						displayStrFormatInfo.mExpandItemDepth = 1;
						displayStrFormatInfo.mTotalSummaryLength += (int)displayString.length();
						displayStrFormatInfo.mHidePointers = false;

						DbgType* primType = summaryType->mTypeParam;
						String result;

						if (primType->IsInteger())
							formatInfo.mTypeKindFlags = (DbgTypeKindFlags)(formatInfo.mTypeKindFlags | DbgTypeKindFlag_Int);

						if ((dataPtr != 0) && (dataPtr != -1))
						{
							String evalString = "(" + primType->ToString() +  ")" + ptrDataStr;
							DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, origTypedValue, evalString, &displayStrFormatInfo);
							if (evalResult)
								result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
						}
						else
						{
							DbgTypedValue evalResult = origTypedValue;
							evalResult.mType = primType;
							String evalString = "(" + primType->ToString() + ")" + expr;
							result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
						}

						if (formatInfo.mRawString)
							return result;

						int crPos = result.IndexOf('\n');
						if (crPos != -1)
							result.RemoveToEnd(crPos);

						if (memberIdx == 0)
							firstRet = result;

						bigRet += result;
						memberIdx++;
					}
				}

				for (auto member : summaryType->mMemberList)
				{
					if (!member->mIsStatic)
					{
						if (formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length() > 255)
						{
							truncatedMemberList = true;
							summaryDone = true;
							bigRet += "...";
							break;
						}

						if (member->mName != NULL)
						{
							if (member->mName[0] == '$')
								continue;

							if (!isdigit(*member->mName))
							{
								if (memberIdx != 0)
									bigRet += isTuple ? ", " : " ";

								if ((!isTuple) || (member->mName[0] != '_'))
								{
									bigRet += String(member->mName);
									bigRet += isTuple ? ":" : "=";
								}
							}
							else
							{
								if (memberIdx != 0)
									bigRet += ", ";
							}

							DwFormatInfo displayStrFormatInfo = formatInfo;
							displayStrFormatInfo.mExpandItemDepth = 1;
							displayStrFormatInfo.mHidePointers = false;
							displayStrFormatInfo.mTotalSummaryLength = formatInfo.mTotalSummaryLength + retVal.length() + bigRet.length();

							String evalString;
							if (dataPtr != -1)
							{
								if ((member->mName[0] >= '0') && (member->mName[0] <= '9'))
									evalString += "this.";
								evalString += String(member->mName); // +", this=" + summaryDataStr;
							}
							else
							{
								evalString = "(";
								evalString += splatStr;
								evalString += ").";
								evalString += member->mName;
							}
							String referenceId;
							String result;
							if (!member->mType->IsValuelessType())
							{
								DbgTypedValue evalResult = EvaluateInContext(dbgCompileUnit, summaryTypedValue, evalString, &displayStrFormatInfo, &referenceId);
								if (evalResult)
								{
									displayStrFormatInfo.mReferenceId = referenceId;
									result = DbgTypedValueToString(evalResult, evalString, displayStrFormatInfo, NULL);
									int crPos = result.IndexOf('\n');
									if (crPos != -1)
										result.RemoveToEnd(crPos);
								}
								else
									result = "???";
							}

							if (member->mType->IsInteger())
								formatInfo.mTypeKindFlags = (DbgTypeKindFlags)(formatInfo.mTypeKindFlags | DbgTypeKindFlag_Int);

							if (formatInfo.mRawString)
								return result;

							if (memberIdx == 0)
								firstRet = result;

							bigRet += result;
							//formatInfo.mEmbeddedDisplayCount = displayStrFormatInfo.mEmbeddedDisplayCount;
							memberIdx++;
						}
						else
						{
							//TODO: Handle C++ unions?
						}
					}
				}

				if (truncatedMemberList)
					break;

				// Find first base class with members
				DbgType* nextSummaryType = NULL;
				for (auto checkBase : summaryType->mBaseTypes)
				{
					auto checkBaseType = checkBase->mBaseType;
					checkBaseType = checkBaseType->GetPrimaryType();
					checkBaseType->PopulateType();
					if ((checkBaseType->GetByteCount() > 0) || (checkBaseType->IsPrimitiveType()))
					{
						if (!splatStr.empty())
						{
							splatStr = "(" + checkBaseType->ToString() + ")" + splatStr;
						}
						else
						{
							summaryTypedValue.mType = checkBaseType;
						}
						nextSummaryType = checkBaseType;
						break;
					}
				}
				summaryType = nextSummaryType;

				if (summaryType == NULL)
					break;

				// Don't add the Object members
				if ((summaryType->GetBaseType() == NULL) && (summaryType->IsBfObject()))
					break;

				// If we don't have many members then find a base class with some members to show
				if ((memberIdx != 0) && (displayString.length() >= 255))
				{
					truncatedMemberList = true;
					bigRet += "...";
					break;
				}
			}

			bigRet += isTuple ? ")" : " }";

			if (displayString.length() > 0)
				displayString += " ";
			if ((memberIdx == 1) && (!truncatedMemberList) && (firstRet.IndexOf('{') == -1) && (!isTuple))
				displayString += "{ " + firstRet + " }";
			else
				displayString += bigRet;
		}

		DbgType* memberListType = actualType;
		bool memberListForceCast = false;
		if (actualType != NULL)
		{
			String valTypeName = displayType->ToString();
			String actualTypeName = actualType->ToString(DbgLanguage_Unknown, true);
			String actualUseTypeName = actualTypeName;

			if ((int)actualTypeName.IndexOf('^') != -1)
				useActualRawType = true;

			if (useActualRawType)
				actualUseTypeName = actualType->ToStringRaw();

			if (displayString.empty())
			{
				// Nothing to display
			}
			else
			{
				if (!retVal.empty())
					retVal += " ";
				retVal += displayString;
			}

			retVal += "\n" + valTypeName;

			if ((innerType->IsBaseBfObject()) || (innerType->IsInterface()))
			{
				if (actualType != innerType)
				{
					retVal += " {" + actualTypeName + "}";
					memberListForceCast = true;
				}
			}
			else
			{
				if (actualType != innerType)
				{
					retVal += " {" + actualTypeName + "}";
					retVal += "\n";
					if (!wantsCustomExpandedItems)
					{
						retVal += "[" + actualTypeName + "]\t((" + actualUseTypeName;
						if (!actualType->IsBfObject())
							retVal += "*";
						retVal += ")this), nd, na, nv, this=" + ptrDataStr;
						memberListType = innerType;
					}
				}
			}
		}
		else
		{
			if ((formatInfo.mHidePointers) && (formatInfo.mIgnoreDerivedClassInfo))
			{
				displayType = innerType;
				if (displayString.empty())
					retVal += displayType->ToString(DbgLanguage_Unknown, true);
			}

			if (!displayString.empty())
			{
				if (!retVal.empty())
					retVal += " ";
				retVal += displayString;
			}
			else
			{
				if (formatInfo.mRawString)
					return "";
			}

			retVal += "\n" + displayType->ToString(DbgLanguage_Unknown, true);
			memberListType = innerType;
		}

		if ((isBfObject) && (mDebugTarget->mBfObjectHasFlags) && (!formatInfo.mNoVisualizers) && (!formatInfo.mRawString))
		{
			int stackTraceLen = 1;
			addr_target stackTraceAddr = ptrVal + sizeof(addr_target);
			if ((bfObjectFlags & BfObjectFlag_AllocInfo) != 0)
			{
				addr_target objectSize = ReadMemory<addr_target>(ptrVal + sizeof(addr_target));
				addr_target largeAllocInfo = ReadMemory<addr_target>(ptrVal + objectSize);
				stackTraceLen = (largeAllocInfo >> 8) & 0xFFFF;
				stackTraceAddr = ptrVal + objectSize + sizeof(addr_target);
			}
			else if ((bfObjectFlags & BfObjectFlag_AllocInfo_Short) != 0)
			{
				addr_target dbgAllocInfo = ReadMemory<addr_target>(ptrVal + sizeof(addr_target));
				stackTraceLen = dbgAllocInfo & 0xFF;
				stackTraceAddr = ptrVal + (dbgAllocInfo >> 16);
			}

			retVal += StrFormat("\n[AllocStackTrace]\t(System.CallStackList)%s, count=%d, na", EncodeDataPtr(stackTraceAddr, true).c_str(), stackTraceLen);
		}

		retVal += StrFormat("\n:language\t%d", language);

		if (formatInfo.mNoMembers)
		{
			//
		}
		else if (wantsCustomExpandedItems)
		{
			HandleCustomExpandedItems(retVal, dbgCompileUnit, debugVis, dwUseType, dwValueType, ptrUseDataStr, ptrDataStr, useTypedValue, dbgVisWildcardCaptures, formatInfo);
		}
		else if ((!isNull) && (!isBadSrc))
		{
			if (dataPtr == -1)
			{
				//String splatName = ((origTypedValue.mSrcAddress == -1) && (origTypedValue.mVariable != NULL)) ? origTypedValue.mVariable->mName : expr;
				String splatName = expr;
				retVal += "\n" + GetMemberList(memberListType, splatName, wasPtr, false, false, true, origTypedValue.mIsReadOnly);
			}
			else
			{
                retVal += "\n" + GetMemberList(memberListType, ptrDataStr, wasPtr, false, memberListForceCast, isCompositeWithoutAddress, origTypedValue.mIsReadOnly);
			}
		}

		if (formatInfo.mExpandItemDepth > 0)
			return retVal;
		if (isAppendBfObject)
			retVal += "\n:appendAlloc";
		if (isStackBfObject)
			retVal += "\n:stack";
		if (isDeletedBfObject)
			retVal += "\n:deleted";

		if (!formatInfo.mAction.IsEmpty())
		{
			retVal += "\n:action\t";
			retVal += formatInfo.mAction;
		}
		else if ((debugVis != NULL) && (!debugVis->mAction.empty()))
		{
			String rawActionStr = mDebugManager->mDebugVisualizers->DoStringReplace(debugVis->mAction, dbgVisWildcardCaptures);
			String actionStr;
			ProcessEvalString(dbgCompileUnit, useTypedValue, rawActionStr, actionStr, formatInfo, debugVis, true);
			retVal += "\n:action\t" + actionStr;
		}

		if ((!typedValue.mIsLiteral) && (dwValueType->IsPointer()))
		{
			retVal += "\n:editVal\t" + EncodeDataPtr(ptrVal, true);
		}

		if (((debugVis != NULL) && (!debugVis->mStringViews.IsEmpty())) || (wantStringView))
			retVal += "\n:stringView";

		if (wantStringView)
		{
			retVal += "\t";
			retVal += SlashString(stringViewData, false, false, true);
		}

		return retVal;
	}

	return "Unknown Type\n" + origValueType->ToString();
}

void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgCompileUnit, DebugVisualizerEntry* debugVis, DbgType* dwUseType, DbgType* dwValueType, String& ptrUseDataStr, String& ptrDataStr, DbgTypedValue useTypedValue, Array<String>& dbgVisWildcardCaptures, DwFormatInfo& formatInfo)
{
	auto debugVisualizers = mDebugManager->mDebugVisualizers;
	auto dbgModule = dbgCompileUnit->mDbgModule;

	if (formatInfo.mExpandItemDepth > 10) // Avoid crashing on circular ExpandItems
		return;

	auto language = formatInfo.mLanguage;

	bool isReadOnly = false;
	if (useTypedValue.mIsReadOnly)
		isReadOnly = true;

	for (auto entry : debugVis->mExpandItems)
	{
		if (!entry->mCondition.empty())
		{
			String error;
			if (!EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, entry->mCondition, dbgVisWildcardCaptures, error))
			{
				if (!error.empty())
					retVal += "\n" + entry->mName + "\t@!<DbgVis Failed>@!";
				continue;
			}
		}
		String replacedStr = debugVisualizers->DoStringReplace(entry->mValue, dbgVisWildcardCaptures);
		retVal += "\n" + entry->mName + "\t" + replacedStr + ", this=(" + ptrUseDataStr + ")";
	}

	String referenceId = dwUseType->ToString();

	if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_ExpandedItem)
	{
		DbgTypedValue itemValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
		if (itemValue)
		{
			DwFormatInfo itemFormatInfo = formatInfo;
			itemFormatInfo.mExpandItemDepth++;
			String itemRetVal = DbgTypedValueToString(itemValue, "", itemFormatInfo, NULL);

			int crIdx = (int)itemRetVal.IndexOf('\n');
			if (crIdx != -1)
			{
				crIdx = (int)itemRetVal.IndexOf('\n', crIdx + 1);
				if (crIdx != -1)
					retVal += itemRetVal.Substring(crIdx);
			}
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Array)
	{
		DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
		Array<int> lowerDimSizes;
		for (auto lowerDim : debugVis->mLowerDimSizes)
		{
			DbgTypedValue lowerDimValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(lowerDim, dbgVisWildcardCaptures), &formatInfo);
			int dimSize = 0;
			if ((lowerDimValue) && (lowerDimValue.mType->IsInteger()))
				dimSize = (int)lowerDimValue.GetInt64();
			dimSize = BF_MAX(dimSize, 1);
			lowerDimSizes.push_back(dimSize);
		}

		if ((sizeValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
		{
			if (!debugVis->mCondition.IsEmpty())
			{
				int size = (int)sizeValue.GetInt64();
				DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);

				DbgTypedValue curNode = headPointer;
				Array<addr_target> parentList;
				String continuationData;

				int totalSize = 2;
				auto valueType = headPointer.mType;
				String addrs = GetArrayItems(dbgCompileUnit, debugVis, valueType, headPointer, totalSize, &continuationData);
				String firstAddr;
				String secondAddr;
				bool hasSecondAddr = valueType == NULL;
				if (addrs.length() > 0)
				{
					const char* addrsPtr = addrs.c_str();
					firstAddr = addrs.Substring(0, sizeof(addr_target) * 2);
					if (hasSecondAddr)
						secondAddr = addrs.Substring(sizeof(addr_target) * 2, sizeof(addr_target) * 2);
				}

				String evalStr;
				if (valueType != NULL)
				{
					evalStr = "(" + valueType->ToStringRaw();
					if (!valueType->IsPointer())
						evalStr += "*";
					evalStr += ")0x{1}";
				}
				else
				{
					evalStr += "({1})0x{2}";
				}
				if (!debugVis->mShowElementAddrs)
					evalStr.Insert(0, "*");

				if (addrs.length() > 0)
				{
					evalStr += ", refid=\"" + referenceId + ".[]\"";
					if (isReadOnly)
						evalStr += ", ne";
					retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, BF_MAX(size, 0), 10000) +
						"\t[{0}]\t" + evalStr + "\t" + firstAddr;

					if (hasSecondAddr)
						retVal += "\t" + secondAddr;

					if (size != 0)
					{
						retVal += "\n:addrs\t" + addrs;
						if (valueType == NULL)
							retVal += "\n:addrsEntrySize\t2";
						if (continuationData.length() > 0)
							retVal += "\n:continuation\t" + continuationData;
					}
				}
			}
			else if (lowerDimSizes.size() == 1)
			{
				int dimSize1 = lowerDimSizes[0];

				String evalStr = "(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
					StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
				evalStr += ", refid=\"" + referenceId + ".[]\"";
				if (isReadOnly)
					evalStr += ", ne";
				retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1, 50000) +
					"\t[{0}]\t" + evalStr;
			}
			else if (lowerDimSizes.size() == 2)
			{
				int dimSize1 = lowerDimSizes[0];
				int dimSize2 = lowerDimSizes[1];

				DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
				if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
				{
					String evalStr = StrFormat("((%s[%d]*)", headPointer.mType->mTypeParam->ToStringRaw(language).c_str(), dimSize2) + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
						StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
					evalStr += ", refid=\"" + referenceId + ".[]\"";
					if (isReadOnly)
						evalStr += ", ne";
					retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1 / dimSize2, 50000) +
						"\t[{0}]\t" + evalStr;
				}
			}
			else if (lowerDimSizes.size() == 3)
			{
				int dimSize1 = lowerDimSizes[0];
				int dimSize2 = lowerDimSizes[1];
				int dimSize3 = lowerDimSizes[2];

				DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures), &formatInfo);
				if ((headPointer.mType != NULL) && (headPointer.mType->IsPointer()))
				{
					String evalStr = StrFormat("((%s[%d][%d]*)", headPointer.mType->mTypeParam->ToStringRaw(language).c_str(), dimSize2, dimSize3) + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) +
						StrFormat(" + {0} * %d), arraysize=%d, na, this=", dimSize1, dimSize1) + ptrUseDataStr;
					evalStr += ", refid=\"" + referenceId + ".[]\"";
					if (isReadOnly)
						evalStr += ", ne";
					retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1 / dimSize2 / dimSize3, 50000) +
						"\t[{0}]\t" + evalStr;
				}
			}
			else
			{
				String evalStr = "*(" + debugVisualizers->DoStringReplace(debugVis->mValuePointer, dbgVisWildcardCaptures) + " + {0}), this=" + ptrUseDataStr;
				evalStr += ", refid=\"" + referenceId + ".[]${0}\"";
				if (isReadOnly)
					evalStr += ", ne";
				retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64(), 50000) +
					"\t[{0}]\t" + evalStr;
			}
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_IndexItems)
	{
		DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);

		if ((sizeValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
		{
			String evalStr = debugVis->mValuePointer + ", this=" + ptrUseDataStr;
			evalStr.Replace("$i", "{0}");

			evalStr += ", refid=\"" + referenceId + ".[]\"";
			if (isReadOnly)
				evalStr += ", ne";
			retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64(), 50000) +
				"\t[{0}]\t" + evalStr;
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_LinkedList)
	{
		DbgType* valueType = NULL;
		if (!debugVis->mValueType.empty())
		{
			valueType = dbgModule->FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures), dwValueType);
			if (valueType != NULL)
				valueType = valueType->ResolveTypeDef();
		}

		DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);
		if (headPointer)
		{
			DbgTypedValue endPointer;
			if (!debugVis->mEndPointer.empty())
				endPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mEndPointer, dbgVisWildcardCaptures), &formatInfo);
			DbgTypedValue nextPointer = EvaluateInContext(dbgCompileUnit, headPointer, debugVisualizers->DoStringReplace(debugVis->mNextPointer, dbgVisWildcardCaptures), &formatInfo);

			int size = -1;
			if (!debugVis->mSize.empty())
			{
				auto sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
				if (sizeValue)
					size = (int)sizeValue.GetInt64();
			}

			DbgTypedValue curNode = headPointer;
			Array<addr_target> parentList;
			String continuationData;

			int totalSize = 2;
			String addrs = GetLinkedListItems(dbgCompileUnit, debugVis, endPointer.mPtr, valueType, curNode, totalSize, &continuationData);
			String firstAddr;
			String secondAddr;
			bool hasSecondAddr = valueType == NULL;
			if (addrs.length() > 0)
			{
				const char* addrsPtr = addrs.c_str();
				firstAddr = addrs.Substring(0, sizeof(addr_target)*2);
				if (hasSecondAddr)
					secondAddr = addrs.Substring(sizeof(addr_target)*2, sizeof(addr_target)*2);
			}

			String evalStr;
			if (valueType != NULL)
			{
				evalStr = "(" + valueType->ToStringRaw();
				if (!valueType->IsPointer())
					evalStr += "*";
				evalStr += ")0x{1}";
			}
			else
			{
				evalStr += "({1})0x{2}";
			}
			if (!debugVis->mShowElementAddrs)
				evalStr.Insert(0, "*");

			if (addrs.length() > 0)
			{
				evalStr += ", refid=\"" + referenceId + ".[]\"";
				if (isReadOnly)
					evalStr += ", ne";
				retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
					"\t[{0}]\t" + evalStr + "\t" + firstAddr;

				if (hasSecondAddr)
					retVal += "\t" + secondAddr;

				if (size != 0)
				{
					retVal += "\n:addrs\t" + addrs;
					if (valueType == NULL)
						retVal += "\n:addrsEntrySize\t2";
					if (continuationData.length() > 0)
						retVal += "\n:continuation\t" + continuationData;
				}
			}
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_TreeItems)
	{
		DbgType* valueType = NULL;
		if (!debugVis->mValueType.empty())
		{
			valueType = dbgModule->FindType(debugVisualizers->DoStringReplace(debugVis->mValueType, dbgVisWildcardCaptures), dwValueType);
			if (valueType != NULL)
				valueType = valueType->ResolveTypeDef();
		}

		DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
		DbgTypedValue headPointer = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mHeadPointer, dbgVisWildcardCaptures), &formatInfo);

		if (sizeValue)
			sizeValue.mType = sizeValue.mType->RemoveModifiers();
		if ((sizeValue) && (headPointer) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
		{
			DbgTypedValue curNode = headPointer;
			Array<addr_target> parentList;
			String continuationData;

			int getItemCount = (int)BF_MIN(sizeValue.GetInt64(), 32LL);

			String addrs = GetTreeItems(dbgCompileUnit, debugVis, parentList, valueType, curNode, getItemCount, &continuationData);
			addr_target firstAddr = 0;
			addr_target secondAddr = 0;
			bool hasSecondAddr = valueType == NULL;
			if (addrs.length() > 0)
			{
				const char* addrsPtr = addrs.c_str();
				firstAddr = DecodeTargetDataPtr(addrsPtr);
				if (hasSecondAddr)
					secondAddr = DecodeTargetDataPtr(addrsPtr);
			}

			String evalStr;
			if (valueType != NULL)
			{
				evalStr = "*(" + valueType->ToStringRaw();
				if (!valueType->IsPointer())
					evalStr += "*";
				evalStr += ")0x{1}";
			}
			else
			{
				evalStr += "*(_T_{1}*)0x{2}";
			}

			int size = (int)sizeValue.GetInt64();
			if (addrs.length() == 0)
			{
				evalStr = ""; // Failed
			}

			evalStr += ", refid=\"" + referenceId + ".[]\"";
			if (isReadOnly)
				evalStr += ", ne";
			retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
				"\t[{0}]\t" + evalStr + "\t" + EncodeDataPtr(firstAddr, false);

			if (hasSecondAddr)
				retVal += "\t" + EncodeDataPtr(secondAddr, false);

			if (addrs.length() > 0)
			{
				retVal += "\n:addrs\t" + addrs;
				if (continuationData.length() > 0)
					retVal += "\n:continuation\t" + continuationData;
			}
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_Dictionary)
	{
		DbgTypedValue sizeValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mSize, dbgVisWildcardCaptures), &formatInfo);
		DbgTypedValue entriesPtrValue = EvaluateInContext(dbgCompileUnit, useTypedValue, debugVisualizers->DoStringReplace(debugVis->mEntries, dbgVisWildcardCaptures), &formatInfo);

		if (sizeValue)
			sizeValue.mType = sizeValue.mType->RemoveModifiers();
		if ((sizeValue) && (entriesPtrValue) && (sizeValue.mType->IsInteger()) && (sizeValue.GetInt64() > 0))
		{
			String continuationData;

			DbgType* valueType = entriesPtrValue.mType;

			int getItemCount = (int)std::min(sizeValue.GetInt64(), 2LL);

			DbgType* useTypedValType = useTypedValue.mType;
			addr_target useTypedValPtr = useTypedValue.mPtr;
			addr_target useTypedValAddr = useTypedValue.mSrcAddress;

			String addrs = GetDictionaryItems(dbgCompileUnit, debugVis, useTypedValue, 0, -1, getItemCount, &continuationData);
			addr_target firstAddr = 0;
			if (addrs.length() > 0)
			{
				const char* addrsPtr = addrs.c_str();
				firstAddr = DecodeTargetDataPtr(addrsPtr);
			}

			String evalStr = "((" + valueType->ToStringRaw() + ")0x{1}), na";

			evalStr += ", refid=\"" + referenceId + ".[]\"";
			if (isReadOnly)
				evalStr += ", ne";
			retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64(), 10000) +
				"\t[{0}]\t" + evalStr + "\t" + EncodeDataPtr(firstAddr, false);

			if (addrs.length() > 0)
			{
				retVal += "\n:addrs\t" + addrs;
				if (continuationData.length() > 0)
					retVal += "\n:continuation\t" + continuationData;
			}
		}
	}
	else if (debugVis->mCollectionType == DebugVisualizerEntry::CollectionType_CallStackList)
	{
		int size = 0;

		String addrs;
		String firstVal;
		auto ptr = useTypedValue.mPtr;

		for (int i = 0; i < formatInfo.mOverrideCount; i++)
		{
			auto funcAddr = ReadMemory<addr_target>(ptr + i * sizeof(addr_target));
			auto srcFuncAddr = funcAddr;

			addrs += EncodeDataPtr(funcAddr - 1, false);
			if (i == 0)
				firstVal = addrs;
			addrs += EncodeDataPtr((addr_target)0, false);
			size++;

			int inlineIdx = 0;

			auto subProgram = mDebugTarget->FindSubProgram(funcAddr - 1, DbgOnDemandKind_LocalOnly);
			while (subProgram != NULL)
			{
				if (subProgram->mInlineeInfo == NULL)
					break;

				auto prevFuncAddr = subProgram->mBlock.mLowPC;

				subProgram = subProgram->mInlineeInfo->mInlineParent;
				addrs += EncodeDataPtr(subProgram->mBlock.mLowPC + 1, false);
				addrs += EncodeDataPtr(prevFuncAddr, false);
				size++;

				inlineIdx++;
			}
		}

		String evalStr = "(System.CallStackAddr)0x{1}";
		evalStr += ", refid=\"" + referenceId + ".[]\"";
		evalStr += ", ne";

		retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, size, 10000) +
			"\t[{0}]\t" + evalStr + ", action=ShowCodeAddr {1} {2}\t" + firstVal + "\t" + EncodeDataPtr((addr_target)0, false);

		retVal += "\n:addrs\t" + addrs;
		retVal += "\n:addrsEntrySize\t2";
		return;
	}

	if (formatInfo.mExpandItemDepth == 0)
	{
		//retVal += "\n[Raw View]\tthis, this=" + ptrDataStr + ", nv";
		retVal += "\n[Raw View]\t" + ptrDataStr + ", nv";
	}
}

bool WinDebugger::IsPaused()
{
	return (mRunState == RunState_Paused) || (mRunState == RunState_Breakpoint) || (mRunState == RunState_Exception) || (mRunState == RunState_DebugEval_Done);
}

DbgTypedValue WinDebugger::GetRegister(const StringImpl& regName, DbgLanguage language, CPURegisters* registers, Array<RegForm>* regForms)
{
	int regNum = -1;

	String lwrRegName(regName);
	_strlwr((char*)lwrRegName.c_str());

	// int regs

#ifdef BF_DBG_32
	DbgTypeCode regType = DbgType_i32;
	if (lwrRegName == "eax")
		regNum = X86Reg_EAX;
	else if (lwrRegName == "ecx")
		regNum = X86Reg_ECX;
	else if (lwrRegName == "edx")
		regNum = X86Reg_EDX;
	else if (lwrRegName == "ebx")
		regNum = X86Reg_EBX;
	else if (lwrRegName == "esp")
		regNum = X86Reg_ESP;
	else if (lwrRegName == "ebp")
		regNum = X86Reg_EBP;
	else if (lwrRegName == "esi")
		regNum = X86Reg_ESI;
	else if (lwrRegName == "edi")
		regNum = X86Reg_EDI;
	else if (lwrRegName == "eip")
		regNum = X86Reg_EIP;
	else if (lwrRegName == "efl")
		regNum = X86Reg_EFL;
#else
	DbgTypeCode regType = DbgType_i64;

	if (lwrRegName == "rax")
		regNum = X64Reg_RAX;
	else if (lwrRegName == "rcx")
		regNum = X64Reg_RCX;
	else if (lwrRegName == "rdx")
		regNum = X64Reg_RDX;
	else if (lwrRegName == "rbx")
		regNum = X64Reg_RBX;
	else if (lwrRegName == "rsp")
		regNum = X64Reg_RSP;
	else if (lwrRegName == "rbp")
		regNum = X64Reg_RBP;
	else if (lwrRegName == "rsi")
		regNum = X64Reg_RSI;
	else if (lwrRegName == "rdi")
		regNum = X64Reg_RDI;
	else if (lwrRegName == "rip")
		regNum = X64Reg_RIP;
	else if (lwrRegName == "r8")
		regNum = X64Reg_R8;
	else if (lwrRegName == "r9")
		regNum = X64Reg_R9;
	else if (lwrRegName == "r10")
		regNum = X64Reg_R10;
	else if (lwrRegName == "r11")
		regNum = X64Reg_R11;
	else if (lwrRegName == "r12")
		regNum = X64Reg_R12;
	else if (lwrRegName == "r13")
		regNum = X64Reg_R13;
	else if (lwrRegName == "r14")
		regNum = X64Reg_R14;
	else if (lwrRegName == "r15")
		regNum = X64Reg_R15;
	else
	{
		regType = DbgType_i32;
		if (lwrRegName == "eax")
			regNum = X64Reg_RAX;
		else if (lwrRegName == "ecx")
			regNum = X64Reg_RCX;
		else if (lwrRegName == "edx")
			regNum = X64Reg_RDX;
		else if (lwrRegName == "ebx")
			regNum = X64Reg_RBX;
		else if (lwrRegName == "efl")
			regNum = X64Reg_EFL;
		else if (lwrRegName == "esi")
			regNum = X64Reg_RSI;
		else if (lwrRegName == "edi")
			regNum = X64Reg_RDI;
		else if (lwrRegName == "r8d")
			regNum = X64Reg_R8;
		else if (lwrRegName == "r9d")
			regNum = X64Reg_R9;
		else if (lwrRegName == "r10d")
			regNum = X64Reg_R10;
		else if (lwrRegName == "r11d")
			regNum = X64Reg_R11;
		else if (lwrRegName == "r12d")
			regNum = X64Reg_R12;
		else if (lwrRegName == "r13d")
			regNum = X64Reg_R13;
		else if (lwrRegName == "r14d")
			regNum = X64Reg_R14;
		else if (lwrRegName == "r15d")
			regNum = X64Reg_R15;
		else
		{
			regType = DbgType_i16;
			if (lwrRegName == "ax")
				regNum = X64Reg_RAX;
			else if (lwrRegName == "cx")
				regNum = X64Reg_RCX;
			else if (lwrRegName == "dx")
				regNum = X64Reg_RDX;
			else if (lwrRegName == "bx")
				regNum = X64Reg_RBX;
			else if (lwrRegName == "si")
				regNum = X64Reg_RSI;
			else if (lwrRegName == "di")
				regNum = X64Reg_RDI;
			else if (lwrRegName == "r8w")
				regNum = X64Reg_R8;
			else if (lwrRegName == "r9w")
				regNum = X64Reg_R9;
			else if (lwrRegName == "r10w")
				regNum = X64Reg_R10;
			else if (lwrRegName == "r11w")
				regNum = X64Reg_R11;
			else if (lwrRegName == "r12w")
				regNum = X64Reg_R12;
			else if (lwrRegName == "r13w")
				regNum = X64Reg_R13;
			else if (lwrRegName == "r14w")
				regNum = X64Reg_R14;
			else if (lwrRegName == "r15w")
				regNum = X64Reg_R15;
			else
			{
				regType = DbgType_i8;
				if (lwrRegName == "al")
					regNum = X64Reg_RAX;
				else if (lwrRegName == "cl")
					regNum = X64Reg_RCX;
				else if (lwrRegName == "dl")
					regNum = X64Reg_RDX;
				else if (lwrRegName == "bl")
					regNum = X64Reg_RBX;
				else if (lwrRegName == "sil")
					regNum = X64Reg_RSI;
				else if (lwrRegName == "dil")
					regNum = X64Reg_RDI;
				else if (lwrRegName == "r8b")
					regNum = X64Reg_R8;
				else if (lwrRegName == "r9b")
					regNum = X64Reg_R9;
				else if (lwrRegName == "r10b")
					regNum = X64Reg_R10;
				else if (lwrRegName == "r11b")
					regNum = X64Reg_R11;
				else if (lwrRegName == "r12b")
					regNum = X64Reg_R12;
				else if (lwrRegName == "r13b")
					regNum = X64Reg_R13;
				else if (lwrRegName == "r14b")
					regNum = X64Reg_R14;
				else if (lwrRegName == "r15b")
					regNum = X64Reg_R15;
			}
		}
	}
#endif

	auto dbgModule = mEmptyDebugTarget->GetMainDbgModule();

	if (regNum != -1)
	{
		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(regType, language);
		typedVal.mInt64 = registers->mIntRegsArray[regNum];
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	// st regs

	if ((lwrRegName.length() == 3) && (lwrRegName[0] == 's') && (lwrRegName[1] == 't') && (lwrRegName[2] >= '0') && (lwrRegName[2] <= '7'))
	{
		regNum = CPUReg_FPSTREG_FIRST + (lwrRegName[2] - '0');
	}
	if (regNum != -1)
	{
		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Double, language);
		typedVal.mDouble = ConvertFloat80ToDouble(registers->mFpMmRegsArray[regNum - CPUReg_FPSTREG_FIRST].fp.fp80);
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	// mm regs

	if ((lwrRegName.length() == 3) && (lwrRegName[0] == 'm') && (lwrRegName[1] == 'm') && (lwrRegName[2] >= '0') && (lwrRegName[2] <= '7'))
	{
		regNum = CPUReg_MMREG_FIRST + (lwrRegName[2] - '0');
	}
	if (regNum != -1)
	{
		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i64, language);
		typedVal.mInt64 = registers->mFpMmRegsArray[regNum - CPUReg_MMREG_FIRST].mm;
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	// xmm regs

#ifdef BF_DBG_32
	if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '7') &&
		(lwrRegName[4] == '_') && (lwrRegName[5] >= '0') && (lwrRegName[5] <= '3'))
	{
		regNum = CPUReg_XMMREG_FIRST + ((lwrRegName[3] - '0') * 4) + (lwrRegName[5] - '0');
	}
#else
	if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '9') &&
		(lwrRegName[4] == '_') && (lwrRegName[5] >= '0') && (lwrRegName[5] <= '3'))
	{
		regNum = CPUReg_XMMREG_FIRST + ((lwrRegName[3] - '0') * 4) + (lwrRegName[5] - '0');
	}
	if ((lwrRegName.length() == 7) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] == '1') && (lwrRegName[4] >= '0') && (lwrRegName[4] <= '9') &&
		(lwrRegName[5] == '_') && (lwrRegName[6] >= '0') && (lwrRegName[6] <= '3'))
	{
		regNum = CPUReg_XMMREG_FIRST + ((10 + (lwrRegName[4] - '0')) * 4) + (lwrRegName[6] - '0');
	}
#endif

	if (regNum != -1)
	{
		int xmmMajor = (regNum - CPUReg_XMMREG_FIRST) >> 2;
		int xmmMinor = (regNum - CPUReg_XMMREG_FIRST) & 3;

		DwMmDisplayType mmDisplayType = GetDisplayInfo(StrFormat("$XMM%d", xmmMajor))->mMmDisplayType;

		RegForm regForm = RegForm_Unknown;
		if (regForms != NULL)
		{
			int regFormIdx = CPUReg_M128_XMMREG_FIRST + xmmMajor;
			if (regFormIdx < (int)regForms->size())
				regForm = (*regForms)[regFormIdx];
		}
		if (mmDisplayType == DwMmDisplayType_Default)
		{
			if ((regForm == RegForm_Double) || (regForm == RegForm_Double2))
				mmDisplayType = DwMmDisplayType_Double;
			else if (regForm == RegForm_Int4)
				mmDisplayType = DwMmDisplayType_Int32;
		}

		//TODO: Add int types
		if (mmDisplayType == DwMmDisplayType_Double)
		{
			DbgTypedValue typedVal;
			typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Double, language);
			typedVal.mDouble = registers->mXmmDRegsArray[xmmMajor].d[xmmMinor];
			typedVal.mRegNum = regNum;
			return typedVal;
		}
		else if ((mmDisplayType == DwMmDisplayType_UInt8) || (mmDisplayType == DwMmDisplayType_Int16) || (mmDisplayType == DwMmDisplayType_Int32))
		{
			DbgTypedValue typedVal;
			typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i32, language);
			typedVal.mInt32 = registers->mXmmI32RegsARray[xmmMajor].i[xmmMinor];
			typedVal.mRegNum = regNum;
			return typedVal;
		}
		else if (mmDisplayType == DwMmDisplayType_Int64)
		{
			DbgTypedValue typedVal;
			typedVal.mType = dbgModule->GetPrimitiveType(DbgType_i64, language);
			typedVal.mInt64 = registers->mXmmI64RegsARray[xmmMajor].i[xmmMinor];
			typedVal.mRegNum = regNum;
			return typedVal;
		}

		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Single, language);
		typedVal.mSingle = registers->mXmmRegsArray[xmmMajor].f[xmmMinor];
		typedVal.mRegNum = regNum;
		return typedVal;
	}
#ifdef BF_DBG_32
	if ((lwrRegName.length() == 4) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '7'))
	{
		regNum = CPUReg_M128_XMMREG_FIRST + (lwrRegName[3] - '0');
	}
#else
	if ((lwrRegName.length() == 4) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] >= '0') && (lwrRegName[3] <= '9'))
	{
		regNum = CPUReg_M128_XMMREG_FIRST + (lwrRegName[3] - '0');
	}
	if ((lwrRegName.length() == 5) && (lwrRegName[0] == 'x') && (lwrRegName[1] == 'm') && (lwrRegName[2] == 'm') && (lwrRegName[3] == '1') && (lwrRegName[4] >= '0') && (lwrRegName[4] <= '5'))
	{
		regNum = CPUReg_M128_XMMREG_FIRST + 10 + (lwrRegName[4] - '0');
	}
#endif
	if (regNum != -1)
	{
		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_RegGroup, language);
		typedVal.mSingle = 0.0f; // ignored at a higher level (but if it's used as an rvalue in the meantime, it'll resolve to zero)
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	// flags

	if ((lwrRegName.length() == 6) && (lwrRegName[0] == 'f') && (lwrRegName[1] == 'l') && (lwrRegName[2] == 'a') && (lwrRegName[3] == 'g') && (lwrRegName[5] == 'f'))
	{
		switch(lwrRegName[4])
		{
		case 'c': regNum = CPUReg_FLAG_CF_CARRY; break;
		case 'p': regNum = CPUReg_FLAG_PF_PARITY; break;
		case 'a': regNum = CPUReg_FLAG_AF_ADJUST; break;
		case 'z': regNum = CPUReg_FLAG_ZF_ZERO; break;
		case 's': regNum = CPUReg_FLAG_SF_SIGN; break;
		case 'i': regNum = CPUReg_FLAG_IF_INTERRUPT; break;
		case 'd': regNum = CPUReg_FLAG_DF_DIRECTION; break;
		case 'o': regNum = CPUReg_FLAG_OF_OVERFLOW; break;
		default: break;
		}
	}
	if (regNum != -1)
	{
		int flagBit = CPURegisters::GetFlagBitForRegister(regNum);
		BF_ASSERT(flagBit >= 0);

		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_Bool, language);
		typedVal.mBool = (registers->mIntRegs.efl & ((uint64)1 << flagBit)) != 0;
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	// categories

	if (lwrRegName == "allregs")
		regNum = CPUReg_CAT_ALLREGS;
	else if (lwrRegName == "iregs")
		regNum = CPUReg_CAT_IREGS;
	else if (lwrRegName == "fpregs")
		regNum = CPUReg_CAT_FPREGS;
	else if (lwrRegName == "mmregs")
		regNum = CPUReg_CAT_MMREGS;
	else if (lwrRegName == "xmmregs")
		regNum = CPUReg_CAT_XMMREGS;
	else if (lwrRegName == "flags")
		regNum = CPUReg_CAT_FLAGS;
	if (regNum != -1)
	{
		DbgTypedValue typedVal;
		typedVal.mType = dbgModule->GetPrimitiveType(DbgType_RegGroup, language);
		typedVal.mSingle = 0.0f; // ignored at a higher level (but if it's used as an rvalue in the meantime, it'll resolve to zero)
		typedVal.mRegNum = regNum;
		return typedVal;
	}

	return DbgTypedValue();
}

DbgModule* WinDebugger::GetCallStackDbgModule(int callStackIdx)
{
	if ((mRunState == RunState_NotStarted) || (!IsPaused()))
		return mEmptyDebugTarget->GetMainDbgModule();
	if (callStackIdx == -1)
		return mDebugTarget->GetMainDbgModule();
	FixCallStackIdx(callStackIdx);
	if (callStackIdx >= mCallStack.size())
		return mDebugTarget->GetMainDbgModule();
	UpdateCallStackMethod(callStackIdx);
	auto subProgram = mCallStack[callStackIdx]->mSubProgram;
	if (subProgram != NULL)
		return subProgram->mCompileUnit->mDbgModule;

	auto dbgModule = mDebugTarget->FindDbgModuleForAddress(mCallStack[callStackIdx]->mRegisters.GetPC());
	if (dbgModule != NULL)
		return dbgModule;
	return mDebugTarget->GetMainDbgModule();
}

DbgSubprogram* WinDebugger::GetCallStackSubprogram(int callStackIdx)
{
	if ((IsInRunState()) || (mRunState == RunState_NotStarted) || (callStackIdx == -1))
		return NULL;
	if (callStackIdx >= (int)mCallStack.size())
		UpdateCallStack();
	if (mCallStack.IsEmpty())
		return NULL;
	if (callStackIdx >= (int)mCallStack.size())
		callStackIdx = 0;
	UpdateCallStackMethod(callStackIdx);
	auto subProgram = mCallStack[callStackIdx]->mSubProgram;
	return subProgram;
}

DbgCompileUnit* WinDebugger::GetCallStackCompileUnit(int callStackIdx)
{
	if ((IsInRunState()) || (mRunState == RunState_NotStarted) || (callStackIdx == -1))
		return NULL;
	if (callStackIdx >= (int)mCallStack.size())
		UpdateCallStack();
	if (mCallStack.IsEmpty())
		return NULL;
	if (callStackIdx >= (int)mCallStack.size())
		callStackIdx = 0;
	UpdateCallStackMethod(callStackIdx);
	auto subProgram = mCallStack[callStackIdx]->mSubProgram;
	if (subProgram == NULL)
		return NULL;
	return subProgram->mCompileUnit;
}

String WinDebugger::EvaluateContinue(DbgPendingExpr* pendingExpr, BfPassInstance& bfPassInstance)
{
	DbgModule* dbgModule = NULL;
	DbgCompileUnit* dbgCompileUnit = NULL;

	if (pendingExpr->mThreadId == -1)
	{
		if ((pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef) && (mDebugTarget != NULL) && (mDebugTarget->mTargetBinary != NULL))
			dbgModule = mDebugTarget->mTargetBinary;
		else
			dbgModule = mEmptyDebugTarget->GetMainDbgModule();
	}
	else
	{
		dbgModule = GetCallStackDbgModule(pendingExpr->mCallStackIdx);
		if ((dbgModule != NULL) &&(!dbgModule->mDebugTarget->mIsEmpty))
			dbgCompileUnit = GetCallStackCompileUnit(pendingExpr->mCallStackIdx);
	}

	if (dbgModule == NULL)
		dbgModule = mEmptyDebugTarget->GetMainDbgModule();

	if (!pendingExpr->mException.empty())
	{
		RestoreAllRegisters();
		return "!" + pendingExpr->mException;
	}

	DwAutoComplete autoComplete;

	if (bfPassInstance.HasFailed())
	{
		// Don't allow pending calls if we've already failed in the calling Evaluate()
		pendingExpr->mExpressionFlags = (DwEvalExpressionFlags)(pendingExpr->mExpressionFlags & ~DwEvalExpressionFlag_AllowCalls);
	}

	DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, pendingExpr->mCallStackIdx, pendingExpr->mCursorPos);
	if (!pendingExpr->mFormatInfo.mStackSearchStr.IsEmpty())
	{
		dbgExprEvaluator.mStackSearch = new DbgStackSearch();
		dbgExprEvaluator.mStackSearch->mSearchStr = pendingExpr->mFormatInfo.mStackSearchStr;
	}
	dbgExprEvaluator.mLanguage = pendingExpr->mFormatInfo.mLanguage;
	dbgExprEvaluator.mReferenceId = &pendingExpr->mReferenceId;
	dbgExprEvaluator.mExpressionFlags = pendingExpr->mExpressionFlags;
	dbgExprEvaluator.mExplicitThis = pendingExpr->mFormatInfo.mExplicitThis;
	dbgExprEvaluator.mSubjectExpr = pendingExpr->mFormatInfo.mSubjectExpr;
	dbgExprEvaluator.mNamespaceSearchStr = pendingExpr->mFormatInfo.mNamespaceSearch;
	dbgExprEvaluator.mExpectingTypeName = pendingExpr->mFormatInfo.mExpectedType;
	dbgExprEvaluator.mCallResults = &pendingExpr->mCallResults;
	if ((pendingExpr->mExpressionFlags & DwEvalExpressionFlag_ValidateOnly) != 0)
	{
		 dbgExprEvaluator.mValidateOnly = true;
	}

	if (pendingExpr->mCursorPos != -1)
	{
		dbgExprEvaluator.mAutoComplete = &autoComplete;
	}
	dbgExprEvaluator.mDbgCompileUnit = dbgCompileUnit;

	DbgTypedValue exprResult;
	if (pendingExpr->mExplitType != NULL)
	{
		exprResult.mHasNoValue = true;
		exprResult.mType = pendingExpr->mExplitType;
	}
	else if (pendingExpr->mExprNode != NULL)
	{
		exprResult = dbgExprEvaluator.Resolve(pendingExpr->mExprNode);
	}

	if (dbgExprEvaluator.mCreatedPendingCall)
	{
		BF_ASSERT(mRunState == RunState_DebugEval);
		//ContinueDebugEvent();
		return "!pending";
	}

	if (dbgExprEvaluator.mCountResultOverride != -1)
		pendingExpr->mFormatInfo.mOverrideCount = dbgExprEvaluator.mCountResultOverride;

	String val;
	if (bfPassInstance.HasFailed())
	{
		BfLogDbgExpr("Evaluate Failed: %s\n", bfPassInstance.mErrors[0]->mError.c_str());
		val = StrFormat("!%d\t%d\t%s", bfPassInstance.mErrors[0]->GetSrcStart(), bfPassInstance.mErrors[0]->GetSrcLength(), bfPassInstance.mErrors[0]->mError.c_str());
	}
	else if (dbgExprEvaluator.mBlockedSideEffects)
	{
		BfLogDbgExpr("Evaluate blocked side effects\n");
		val = "!sideeffects";
	}
	else if (!exprResult)
	{
		if (exprResult.mType != NULL)
		{
			BfLogDbgExpr("Evaluate success\n");

			String typeName = exprResult.mType->ToString();
			DbgType* rawType = exprResult.mType;
			if (rawType->IsBfObjectPtr())
				rawType = rawType->mTypeParam;
			String typeNameRaw = rawType->ToStringRaw();
			val = typeName + "\n" + typeName;
			val += "\n" + GetMemberList(exprResult.mType, typeNameRaw, false, true, false, false, exprResult.mIsReadOnly);
			if (exprResult.mType->mTypeCode == DbgType_Namespace)
			{
				val += "\n:type\tnamespace";
			}
			else
			{
				auto type = exprResult.mType;
				if (type->IsPointer())
					type = type->mTypeParam;
				if (type->IsBfObject())
					val += "\n:type\tclass";
				else
					val += "\n:type\tvaluetype";
			}

			if (!pendingExpr->mReferenceId.empty())
				val += "\n:referenceId\t" + pendingExpr->mReferenceId;
		}
		else
			val = "!";
	}
	else if ((pendingExpr->mExpressionFlags & (DwEvalExpressionFlag_MemoryAddress)) != 0)
	{
		DbgType* resultType = exprResult.mType->RemoveModifiers();
		if ((resultType->IsInteger()) || (resultType->IsPointerOrRef()))
		{
			val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", 0);
		}
		else
		{
			if (exprResult.mSrcAddress != 0)
				val = StrFormat("!Type '%s' is invalid. A pointer or address value is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
			else
				val = StrFormat("!Type '%s' is invalid. A pointer or address value is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
		}
	}
	else if ((pendingExpr->mExpressionFlags & (DwEvalExpressionFlag_MemoryWatch)) != 0)
	{
		DbgType* resultType = exprResult.mType->RemoveModifiers();

		bool isMemoryWatch = (pendingExpr->mExpressionFlags & DwEvalExpressionFlag_MemoryWatch) != 0;

		if (!resultType->IsPointerOrRef())
		{
			if (exprResult.mSrcAddress != 0)
				val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
			else
				val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
		}
		else
		{
			auto innerType = resultType->mTypeParam;
			int byteCount = innerType->GetByteCount();

			if (pendingExpr->mFormatInfo.mArrayLength != -1)
				byteCount *= pendingExpr->mFormatInfo.mArrayLength;

			if (byteCount == 0)
			{
				val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected, try casting to a non-void pointer type.", exprResult.mType->ToString().c_str());
			}
	#ifdef BF_DBG_32
			else if ((isMemoryWatch) && (!IsMemoryBreakpointSizeValid(exprResult.mPtr, byteCount)))
			{
				if (innerType->mSize > 16)
					val = StrFormat("!Element size is %d bytes. A maximum of 16 bytes can be watched. Try casting to an appropriately-sized pointer or watching an individual member.", byteCount);
				else if (!IsMemoryBreakpointSizeValid(0, byteCount))
					val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch. Try casting to an appropriately-sized pointer.", byteCount);
				else
					val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch at non-aligned address %@. Try casting to an appropriately-sized pointer.", byteCount, exprResult.mPtr);
			}
	#else
			else if ((isMemoryWatch) && (!IsMemoryBreakpointSizeValid(exprResult.mPtr, byteCount)))
			{
				if (innerType->mSize > 32)
					val = StrFormat("!Element size is %d bytes. A maximum of 32 bytes can be watched. Try casting to an appropriately-sized pointer or watching an individual member.", byteCount);
				else if (!IsMemoryBreakpointSizeValid(0, byteCount))
					val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch. Try casting to an appropriately-sized pointer.", byteCount);
				else
					val = StrFormat("!Element size is %d bytes, which is not a supported size for a memory watch at non-aligned address %@. Try casting to an appropriately-sized pointer.", byteCount, exprResult.mPtr);
			}
	#endif
			else
			{
				auto language = dbgExprEvaluator.GetLanguage();
				val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", byteCount) + "\n" + StrFormat("%d\t", language) + innerType->ToStringRaw(language);
			}
		}
	}
	else
	{
		if (pendingExpr->mFormatInfo.mNoEdit)
			exprResult.mIsReadOnly = true;
		if (!pendingExpr->mReferenceId.empty())
			pendingExpr->mFormatInfo.mReferenceId = pendingExpr->mReferenceId;
		val = DbgTypedValueToString(exprResult, pendingExpr->mExprNode->ToString(), pendingExpr->mFormatInfo, &dbgExprEvaluator, (pendingExpr->mExpressionFlags & DwEvalExpressionFlag_FullPrecision) != 0);

		if ((!val.empty()) && (val[0] == '!'))
			return val;

		if (pendingExpr->mFormatInfo.mRawString)
			return val;

		if (exprResult.mIsLiteral)
			val += "\n:literal";

		if (bfPassInstance.HasMessages())
		{
			for (auto error : bfPassInstance.mErrors)
			{
				if (error->mIsWarning)
				{
					val += "\n:warn\t";
					val += error->mError;
				}
			}
		}

		if (!pendingExpr->mFormatInfo.mReferenceId.empty())
			val += "\n:referenceId\t" + pendingExpr->mFormatInfo.mReferenceId;

		auto breakAddress = exprResult.mSrcAddress;
		int breakSize = exprResult.mType->GetByteCount();
		if (exprResult.mType->IsRef())
			breakSize = exprResult.mType->mTypeParam->GetByteCount();
		if ((breakAddress != 0) && (HasMemoryBreakpoint(breakAddress, breakSize)))
			val += StrFormat("\n:break\t%@", breakAddress);

		auto checkType = exprResult.mType->RemoveModifiers();
		if (checkType->IsBfObjectPtr())
			val += "\n:type\tobject";
		else if ((checkType->IsPointer()) || (checkType->mTypeCode == DbgType_Subroutine))
			val += "\n:type\tpointer";
		else if (checkType->IsInteger())
			val += "\n:type\tint";
		else if (checkType->IsFloat())
			val += "\n:type\tfloat";
		else if ((exprResult.mRegNum >= X64Reg_M128_XMM0) && (exprResult.mRegNum <= X64Reg_M128_XMM15))
			val += "\n:type\tmm128";
		else
			val += "\n:type\tvaluetype";

		if ((pendingExpr->mFormatInfo.mTypeKindFlags & DbgTypeKindFlag_Int) != 0)
			val += "\n:type\tint";

		if (dbgExprEvaluator.mHadSideEffects)
			val += "\n:sideeffects";
		if ((dbgExprEvaluator.mStackSearch != NULL) && (dbgExprEvaluator.mStackSearch->mStartingStackIdx != dbgExprEvaluator.mCallStackIdx))
			val += StrFormat("\n:stackIdx\t%d", dbgExprEvaluator.mCallStackIdx);

		auto underlyingType = exprResult.mType->RemoveModifiers();
		bool canEdit = true;
		if (pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef)
		{
			if (exprResult.mType->IsConst())
				canEdit = false;
		}
		if (pendingExpr->mFormatInfo.mNoEdit)
			canEdit = false;
		if (exprResult.mIsReadOnly)
			canEdit = false;

		const char* langStr = (pendingExpr->mFormatInfo.mLanguage == DbgLanguage_Beef) ? "@Beef:" : "@C:";

		if (exprResult.mSrcAddress != 0)
		{
			val += StrFormat("\n:addrValueExpr\t%s(%s*)", langStr, exprResult.mType->ToString(pendingExpr->mFormatInfo.mLanguage).c_str());
			val += EncodeDataPtr(exprResult.mSrcAddress, true);
		}

		if (exprResult.mType->IsPointerOrRef())
		{
			auto underlyingType = exprResult.mType->mTypeParam;
			if (underlyingType != NULL)
			{
				val += StrFormat("\n:pointeeExpr\t%s(%s%s)", langStr, underlyingType->ToString(pendingExpr->mFormatInfo.mLanguage).c_str(),
					underlyingType->IsBfObject() ? "" : "*");
				val += EncodeDataPtr(exprResult.mPtr, true);
			}

			val += "\n:pointer\t" + EncodeDataPtr(exprResult.mPtr, true);
		}

		if (val[0] == '!')
		{
			// Already has an error embedded, can't edit
		}
		else if ((exprResult.mSrcAddress != 0) && (underlyingType->mTypeCode >= DbgType_i8) && (underlyingType->mTypeCode <= DbgType_Ptr) &&
            (underlyingType->mTypeCode != DbgType_Class) && (underlyingType->mTypeCode != DbgType_Struct))
		{
			if (canEdit)
				val += "\n:canEdit";
			if (exprResult.mType->mTypeCode == DbgType_Ptr)
			{
				val += "\n:editVal\t" + EncodeDataPtr(exprResult.mPtr, true);
			}
		}
		else if ((underlyingType->IsStruct()) && (exprResult.mSrcAddress != 0) && (underlyingType->IsTypedPrimitive()))
		{
			auto primType = underlyingType->GetRootBaseType();
			DbgTypedValue primVal = dbgExprEvaluator.ReadTypedValue(NULL, primType, exprResult.mSrcAddress, DbgAddrType_Target);

			String primResult = DbgTypedValueToString(primVal, "", pendingExpr->mFormatInfo, NULL);
			int crPos = (int)primResult.IndexOf('\n');
			if (crPos != -1)
				primResult.RemoveToEnd(crPos);

			if (canEdit)
				val += "\n:canEdit";
			val += "\n:editVal\t" + primResult;
		}
		else if (exprResult.mRegNum >= 0)
		{
			bool isPseudoReg = ( ((exprResult.mRegNum >= X86Reg_M128_XMMREG_FIRST) && (exprResult.mRegNum <= X86Reg_M128_XMMREG_LAST))
				|| ((exprResult.mRegNum >= X86Reg_CAT_FIRST) && (exprResult.mRegNum <= X86Reg_CAT_LAST)) );
			if (!isPseudoReg)
			{
				if (canEdit)
					val += "\n:canEdit";
				if (exprResult.mType->mTypeCode == DbgType_Ptr)
				{
					val += "\n:editVal\t" + EncodeDataPtr(exprResult.mPtr, true);
				}
			}
		}
	}

	if (pendingExpr->mFormatInfo.mRawString)
		return "";

	if (val[0] != '!')
	{
		if (pendingExpr->mUsedSpecifiedLock)
			val += "\n:usedLock";

		if (pendingExpr->mStackIdxOverride != -1)
			val += StrFormat("\n:stackIdx\t%d", pendingExpr->mStackIdxOverride);
	}

	if (pendingExpr->mCursorPos != -1)
		val += GetAutocompleteOutput(autoComplete);

	return val;
}

String WinDebugger::EvaluateContinue()
{
	BP_ZONE("WinDebugger::EvaluateContinue");

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (mDebugPendingExpr == NULL)
		return "!Evaluation canceled";

	if (!IsPaused())
		return "!Not paused";

	if (mRunState == RunState_DebugEval_Done)
		mRunState = RunState_Paused;

	BfPassInstance bfPassInstance(mBfSystem);
	String result = EvaluateContinue(mDebugPendingExpr, bfPassInstance);
	if (result != "!pending")
	{
		BfLogDbg("EvaluateContinue finishing pending expr in thread %d\n", mDebugEvalThreadInfo.mThreadId);
		CleanupDebugEval();
	}

	return result;
}

void WinDebugger::EvaluateContinueKeep()
{
	if (mDebugPendingExpr != NULL)
		mDebugPendingExpr->mIdleTicks = 0;
}

static void PdbTestFile(WinDebugger* debugger, const StringImpl& path)
{
	if (!path.EndsWith(".PDB", StringImpl::CompareKind_OrdinalIgnoreCase))
		return;

	OutputDebugStrF("Testing %s\n", path.c_str());
	COFF coffFile(debugger->mDebugTarget);
	uint8 wantGuid[16] = { 0 };
	if (!coffFile.TryLoadPDB(path, wantGuid, -1))
		return;
	if (!coffFile.mIs64Bit)
		return;
	coffFile.ParseTypeData();
	coffFile.ParseSymbolData();
	coffFile.ParseGlobalsData();

	for (int i = 0; i < coffFile.mTypes.mSize; i++)
		coffFile.mTypes[i]->PopulateType();

	for (int i = 0; i < coffFile.mCvModuleInfo.mSize; i++)
		coffFile.ParseCompileUnit(i);
}

static void PdbTest(WinDebugger* debugger, const StringImpl& path)
{
	for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Files))
	{
		String filePath = fileEntry.GetFilePath();

		PdbTestFile(debugger, filePath);
	}

	for (auto& fileEntry : FileEnumerator(path, FileEnumerator::Flags_Directories))
	{
		String childPath = fileEntry.GetFilePath();
		String dirName;
		dirName = GetFileName(childPath);

		PdbTest(debugger, childPath);
	}
}

String WinDebugger::Evaluate(const StringImpl& expr, DwFormatInfo formatInfo, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
{
	BP_ZONE_F("WinDebugger::Evaluate %s", BP_DYN_STR(expr.c_str()));

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((expressionFlags & DwEvalExpressionFlag_Symbol) != 0)
	{
		DwAutoComplete autoComplete;

		String retVal;

		retVal += GetAutocompleteOutput(autoComplete);
		return retVal;
	}

	UpdateCallStackMethod(callStackIdx);

	BfLogDbgExpr("Evaluate %s in thread %d\n", expr.c_str(), (mActiveThread != NULL) ? mActiveThread->mThreadId : 0);

	if (language != -1)
		formatInfo.mLanguage = (DbgLanguage)language;

	auto activeThread = mActiveThread;
	if ((!IsPaused()) && (mRunState != RunState_NotStarted) && (mRunState != RunState_DebugEval))
	{
		activeThread = NULL;
		callStackIdx = -1;
	}

	if (mDebugPendingExpr != NULL)
	{
		// We already have a pending call
		expressionFlags = (DwEvalExpressionFlags)(expressionFlags & ~DwEvalExpressionFlag_AllowCalls);
	}

	if ((expressionFlags & DwEvalExpressionFlag_RawStr) != 0)
	{
		formatInfo.mRawString = true;
	}

	if ((expressionFlags & DwEvalExpressionFlag_AllowStringView) != 0)
	{
		formatInfo.mAllowStringView = true;
	}

	auto terminatedExpr = expr + ";";

	auto prevActiveThread = mActiveThread;
	bool restoreActiveThread = false;
	defer(
		{
			if (restoreActiveThread)
				SetActiveThread(prevActiveThread->mThreadId);
		});

	bool usedSpecifiedLock = false;
	int stackIdxOverride = -1;

	if (terminatedExpr.StartsWith('{'))
	{
		String locString;
		int closeIdx = terminatedExpr.IndexOf('}');
		if (closeIdx != -1)
			locString = terminatedExpr.Substring(1, closeIdx - 1);

		for (int i = 0; i <= closeIdx; i++)
			terminatedExpr[i] = ' ';

		locString.Trim();
		if (locString.StartsWith("Thread:", StringImpl::CompareKind_OrdinalIgnoreCase))
		{
			bool foundLockMatch = true;

			locString.Remove(0, 7);
			char* endPtr = NULL;
			int64 threadId = (int64)strtoll(locString.c_str(), &endPtr, 10);

			if (endPtr != NULL)
			{
				locString.Remove(0, endPtr - locString.c_str());
				locString.Trim();

				if (locString.StartsWith("SP:", StringImpl::CompareKind_OrdinalIgnoreCase))
				{
					locString.Remove(0, 3);
					char* endPtr = NULL;
					uint64 sp = (uint64)strtoll(locString.c_str(), &endPtr, 16);

					if (endPtr != NULL)
					{
						locString.Remove(0, endPtr - locString.c_str());
						locString.Trim();

						if (locString.StartsWith("Func:", StringImpl::CompareKind_OrdinalIgnoreCase))
						{
							locString.Remove(0, 5);
							char* endPtr = NULL;
							int64 funcAddr = (int64)strtoll(locString.c_str(), &endPtr, 16);

							if (endPtr != NULL)
							{
								// Actually do it

								if ((mActiveThread != NULL) && (mActiveThread->mThreadId != threadId))
									restoreActiveThread = true;
								if ((mActiveThread == NULL) || (mActiveThread->mThreadId != threadId))
									SetActiveThread(threadId);
								if ((mActiveThread != NULL) && (mActiveThread->mThreadId == threadId))
								{
									int foundStackIdx = -1;

									int checkStackIdx = 0;
									while (true)
									{
										if (checkStackIdx >= mCallStack.mSize)
											UpdateCallStack();
										if (checkStackIdx >= mCallStack.mSize)
											break;

										auto stackFrame = mCallStack[checkStackIdx];
										if (stackFrame->mRegisters.GetSP() == sp)
										{
											foundStackIdx = checkStackIdx;
											break;
										}

										if (stackFrame->mRegisters.GetSP() > sp)
										{
											foundStackIdx = checkStackIdx - 1;
											break;
										}

										checkStackIdx++;
									}

									if (foundStackIdx != -1)
									{
										UpdateCallStackMethod(foundStackIdx);
										auto stackFrame = mCallStack[foundStackIdx];

										if ((stackFrame->mSubProgram != NULL) && ((int64)stackFrame->mSubProgram->mBlock.mLowPC == funcAddr))
										{
											if ((callStackIdx != foundStackIdx) || (mActiveThread != prevActiveThread))
												usedSpecifiedLock = true;
											callStackIdx = foundStackIdx;
											foundLockMatch = true;
										}
									}
								}
							}
						}
					}
				}
			}

			if (!foundLockMatch)
				return "!Locked stack frame not found";

			bool doClear = false;
			for (int i = closeIdx; i < terminatedExpr.mLength; i++)
			{
				char c = terminatedExpr[i];
				if (doClear)
				{
					terminatedExpr[i] = ' ';
					if (c == '}')
						break;
				}
				else
				{
					if (c == '{')
					{
						int endIdx = terminatedExpr.IndexOf('}');
						if (endIdx == -1)
							break;
						terminatedExpr[i] = ' ';
						doClear = true;
					}
					else if (!::isspace((uint8)c))
						break;
				}
			}
		}
		else if (!locString.IsEmpty())
		{
			const char* checkPtr = locString.c_str();
			if ((*checkPtr == '^') || (*checkPtr == '@'))
				checkPtr++;

			char* endPtr = NULL;
			int useCallStackIdx = strtol(checkPtr, &endPtr, 10);
			if (endPtr == locString.c_str() + locString.length())
			{
				if (locString[0] == '@')
					callStackIdx = useCallStackIdx;
				else
					callStackIdx += useCallStackIdx;
				stackIdxOverride = callStackIdx;
			}
			else
			{
				formatInfo.mStackSearchStr = locString;
			}
		}
	}

	auto dbgModule = GetCallStackDbgModule(callStackIdx);
	auto dbgSubprogram = GetCallStackSubprogram(callStackIdx);
	DbgCompileUnit* dbgCompileUnit = NULL;
	if (dbgSubprogram != NULL)
		dbgCompileUnit = dbgSubprogram->mCompileUnit;

	if ((expr.length() > 0) && (expr[0] == '!'))
	{
		if (expr.StartsWith("!step "))
		{
			expressionFlags = (DwEvalExpressionFlags)(expressionFlags | DwEvalExpressionFlag_StepIntoCalls);
			for (int i = 0; i < 5; i++)
				terminatedExpr[i] = ' ';
		}
		else
		{
			String cmd = expr;
			int commaPos = (int)cmd.IndexOf(',');
			if (commaPos != -1)
				cmd.RemoveToEnd(commaPos);

			if (cmd == "!info")
			{
				OutputMessage(StrFormat("Module: %s\n", dbgModule->mDisplayName.c_str()));
				if (dbgSubprogram == NULL)
				{
					//
				}
				else if (dbgSubprogram->mLinkName != NULL)
				{
					OutputMessage(StrFormat("Link Name: %s\n", dbgSubprogram->mLinkName));
				}
				else
				{
					String outSymbol;
					if (mDebugTarget->FindSymbolAt(dbgSubprogram->mBlock.mLowPC, &outSymbol))
					{
						OutputMessage(StrFormat("Link Name: %s\n", outSymbol.c_str()));
					}
				}
				return "";
			}
			else if (cmd == "!dbg")
			{
				mDbgBreak = true;
				return "";
			}
			else if (cmd == "!pdbtest")
			{
				PdbTest(this, "c:\\");
			}
			else if (cmd.StartsWith("!pdbtest "))
				PdbTestFile(this, cmd.Substring(9));
		}
	}

	bool valIsAddr = false;

	BfParser* parser = new BfParser(mBfSystem);
	parser->mCompatMode = true;

	BfPassInstance bfPassInstance(mBfSystem);

	if ((terminatedExpr.length() > 2) && (terminatedExpr[0] == '@'))
	{
		if (terminatedExpr[1] == '!') // Return string as error
		{
			int errorEnd = (int)terminatedExpr.IndexOf("@!", 2);
			if (errorEnd != -1)
				return terminatedExpr.Substring(1, errorEnd - 1);
			else
				return terminatedExpr.Substring(1);
		}
		else if (terminatedExpr[1] == '>') // Return string as text
		{
			int errorEnd = (int)terminatedExpr.IndexOf("@>", 2);
			if (errorEnd != -1)
				return terminatedExpr.Substring(2, errorEnd - 1);
			else
				return terminatedExpr.Substring(2);
		}
		else // Look for "@:" or "@Beef:" style
		{
			int colonIdx = terminatedExpr.IndexOf(':');
			if (colonIdx > 0)
			{
				bool isValid = true;
				DbgLanguage language = DbgLanguage_Unknown;
				String lang = terminatedExpr.Substring(1, colonIdx - 1);
				lang = ToUpper(lang);
				if ((lang == "") || (lang == "BEEF"))
				{
					language = DbgLanguage_Beef;
				}
				else if (lang == "C")
				{
					language = DbgLanguage_C;
				}
				if (language != DbgLanguage_Unknown)
				{
					for (int i = 0; i < colonIdx + 1; i++)
						terminatedExpr[i] = ' ';

					DbgLanguage curLanguage = DbgLanguage_Unknown;
					if (dbgSubprogram != NULL)
						curLanguage = dbgSubprogram->GetLanguage();
					if (language != curLanguage)
					{
						dbgModule = mDebugTarget->mTargetBinary;
						dbgSubprogram = NULL;
						formatInfo.mLanguage = language;
						callStackIdx = -1;
					}
				}
			}
		}
	}

	parser->SetSource(terminatedExpr.c_str(), terminatedExpr.length());
	parser->Parse(&bfPassInstance);

	BfReducer bfReducer;
	bfReducer.mAlloc = parser->mAlloc;
	bfReducer.mSystem = mBfSystem;
	bfReducer.mPassInstance = &bfPassInstance;
	bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser->mRootNode);
	bfReducer.mVisitorPos.MoveNext();
	bfReducer.mCompatMode = parser->mCompatMode;
	bfReducer.mSource = parser;
	auto exprNode = bfReducer.CreateExpression(parser->mRootNode->mChildArr.GetAs<BfAstNode*>(0));
	parser->Close();

	formatInfo.mCallStackIdx = callStackIdx;
	if ((formatInfo.mLanguage == DbgLanguage_Unknown) && (dbgSubprogram != NULL))
		formatInfo.mLanguage = dbgSubprogram->GetLanguage();

	DbgPendingExpr* pendingExpr = new DbgPendingExpr();
	if (activeThread != NULL)
		pendingExpr->mThreadId = activeThread->mThreadId;
	pendingExpr->mParser = parser;
	pendingExpr->mCallStackIdx = callStackIdx;
	pendingExpr->mCursorPos = cursorPos;
	pendingExpr->mExpressionFlags = expressionFlags;
	pendingExpr->mExprNode = exprNode;

	DbgType* explicitType = NULL;
	String formatFlags;
	String assignExpr;
	int assignExprOffset = -1;

	if ((exprNode != NULL) && (exprNode->GetSrcEnd() < (int)expr.length()))
	{
		int formatOffset = exprNode->GetSrcEnd();
		while (formatOffset < (int)expr.length())
		{
			char c = expr[formatOffset];
			if (c == ' ')
				formatOffset++;
			else
				break;
		}

 		formatFlags = Trim(expr.Substring(formatOffset));
		bool isComplexType = false;
		for (char c : formatFlags)
			if (c == '>')
				isComplexType = true;
		if (isComplexType)
		{
			explicitType = dbgModule->FindType(expr);
		}

		if ((explicitType == NULL) && (formatFlags.length() > 0))
		{
			String errorString = "Invalid expression";
			if (!ParseFormatInfo(dbgModule, formatFlags, &formatInfo, &bfPassInstance, &assignExprOffset, &assignExpr, &errorString))
			{
				if (formatInfo.mRawString)
					return "";
				bfPassInstance.FailAt(errorString, parser->mSourceData, exprNode->GetSrcEnd(), (int) expr.length() - exprNode->GetSrcEnd());
				formatFlags = "";
			}
			if (assignExprOffset != -1)
				assignExprOffset += formatOffset;
		}
	}

	if (assignExpr.length() > 0)
	{
		String newEvalStr = exprNode->ToString() + " = ";
		int errorOffset = (int)newEvalStr.length();
		newEvalStr += assignExpr;
		String result = Evaluate(newEvalStr, formatInfo, callStackIdx, cursorPos, language, expressionFlags);
		if (result[0] == '!')
		{
			int tabPos = (int)result.IndexOf('\t');
			if (tabPos > 0)
			{
				int errorStart = atoi(result.Substring(1, tabPos - 1).c_str());
				if (errorStart >= errorOffset)
				{
					result = StrFormat("!%d", errorStart - errorOffset + assignExprOffset) + result.Substring(tabPos);
				}
			}
		}
		return result;
	}

	pendingExpr->mUsedSpecifiedLock = usedSpecifiedLock;
	pendingExpr->mStackIdxOverride = stackIdxOverride;
	pendingExpr->mExplitType = explicitType;
	pendingExpr->mFormatInfo = formatInfo;
	String result = EvaluateContinue(pendingExpr, bfPassInstance);
	if (result == "!pending")
	{
		BF_ASSERT(mDebugPendingExpr == NULL);
		if (mDebugPendingExpr != NULL)
		{
			return "!retry"; // We already have a pending
		}
		mDebugPendingExpr = pendingExpr;
		mDebugEvalThreadInfo = *mActiveThread;
		mActiveThread->mIsAtBreakpointAddress = 0;
		mActiveThread->mStoppedAtAddress = 0;
		mActiveThread->mBreakpointAddressContinuing = 0;
	}
	else
		delete pendingExpr;

	return result;
}

String WinDebugger::Evaluate(const StringImpl& expr, int callStackIdx, int cursorPos, int language, DwEvalExpressionFlags expressionFlags)
{
	DwFormatInfo formatInfo;
	return Evaluate(expr, formatInfo, callStackIdx, cursorPos, language, expressionFlags);
}

static void ConvertDoubleToFloat80(double d, byte fp80[10])
{
	uint64 di = *reinterpret_cast<uint64*>(&d);

	uint64 m = di & (((uint64)1 << 52) - 1);
	uint64 e = (di >> 52) & 0x7ff;

	memset(fp80, 0, 10);

	// sign bit is directly transferred
	if (di & ((uint64)1 << 63))
		fp80[9] |= 0x80;

	if (!e && !m)
		return; // zero

	fp80[7] |= 0x80; // leading integer bit in mantissa (always 1 in normalized numbers)

	if (e == 0x7ff)
	{
		fp80[9] |= 0x7f;
		fp80[8] = 0xff;

		if (m == 0)
			return; // inf

		fp80[7] |= 0x3f; // any nonzero value will be a NaN (SNaN or QNaN)
		if (m & ((uint64)1 << 51))
			fp80[7] |= 0x40; // QNaN

		return;
	}

	int useExponent = (int)e - 1023;

	if (!e)
	{
		// denormal; can renormalize though since fp80 supports lower exponents
		BF_ASSERT(m != 0); // we should have trapped zero above
		while (!(m & ((uint64)1 << 51)))
		{
			m <<= 1;
			--useExponent;
		}
		// finally we have our leading 1 bit; strip that off and we have a normalized number again
		m <<= 1;
		--useExponent;

		m &= (((uint64)1 << 52) - 1);
	}

	useExponent += 16383;
	BF_ASSERT((useExponent > 0) && (useExponent < 0x7fff));
	*reinterpret_cast<uint16*>(&fp80[8]) |= (uint16)useExponent;
	*reinterpret_cast<uint64*>(&fp80[0]) |= (m << 11);
}

bool WinDebugger::AssignToReg(int callStackIdx, DbgTypedValue regVal, DbgTypedValue value, String& outError)
{
	BF_ASSERT(regVal.mRegNum >= 0);

	if (mCallStack.size() == 0)
	{
		outError = "No call stack";
		return false;
	}

	if (callStackIdx >= (int)mCallStack.size())
	{
		outError = "Invalid call stack index";
		return false;
	}

	auto registers = &mCallStack[callStackIdx]->mRegisters;
	void* regPtr = NULL;

#ifdef BF_DBG_32
	if ((regVal.mRegNum >= X86Reg_INTREG_FIRST) && (regVal.mRegNum <= X86Reg_INTREG_LAST))
	{
		BF_ASSERT(regVal.mType->mSize == sizeof(int32));
		registers->mIntRegsArray[regVal.mRegNum - X86Reg_INTREG_FIRST] = (uint64)value.mUInt32; // don't sign-extend
	}
	else if ((regVal.mRegNum >= X86Reg_FPSTREG_FIRST) && (regVal.mRegNum <= X86Reg_FPSTREG_LAST))
	{
		BF_ASSERT(regVal.mType->mSize == sizeof(float) || regVal.mType->mSize == sizeof(double));
		CPURegisters::FpMmReg* reg = &registers->mFpMmRegsArray[regVal.mRegNum - X86Reg_FPSTREG_FIRST];
		double d;
		if (regVal.mType->mSize == sizeof(float))
			d = (double)value.mSingle;
		else
			d = value.mDouble;
		ConvertDoubleToFloat80(d, reg->fp.fp80);
	}
	else if ((regVal.mRegNum >= X86Reg_MMREG_FIRST) && (regVal.mRegNum <= X86Reg_MMREG_LAST))
	{
		BF_ASSERT(regVal.mType->mSize == sizeof(int32) || regVal.mType->mSize == sizeof(int64));
		CPURegisters::FpMmReg* reg = &registers->mFpMmRegsArray[regVal.mRegNum - X86Reg_MMREG_FIRST];
		if (regVal.mType->mSize == sizeof(int32))
			reg->mm = (uint64)value.mUInt32; // don't sign-extend
		else if (regVal.mType->mSize == sizeof(int64))
			reg->mm = value.mInt64;
		// whenever we use the low 64 bits of the reg as mm, the upper 16 bits of the 80-bit float must be set to all-1s to indicate NaN
		reg->fp.fp80[8] = reg->fp.fp80[9] = 0xFF;
	}
	else if ((regVal.mRegNum >= X86Reg_XMMREG_FIRST) && (regVal.mRegNum <= X86Reg_XMMREG_LAST))
	{
		int xmmMajor = (regVal.mRegNum - X86Reg_XMMREG_FIRST) >> 2;
		int xmmMinor = (regVal.mRegNum - X86Reg_XMMREG_FIRST) & 3;

		registers->mXmmRegsArray[xmmMajor].f[xmmMinor] = value.mSingle;
	}
	else if ((regVal.mRegNum >= X86Reg_M128_XMMREG_FIRST) && (regVal.mRegNum <= X86Reg_M128_XMMREG_LAST))
	{
		outError = "Cannot write directly to 128-bit XMM register, please use inner float components";
		return false;
	}
	else if ((regVal.mRegNum >= X86Reg_FLAG_FIRST) && (regVal.mRegNum <= X86Reg_FLAG_LAST))
	{
		int flagBit = CPURegisters::GetFlagBitForRegister(regVal.mRegNum);
		if (flagBit >= 0)
		{
			if (value.mBool)
				registers->mIntRegs.efl |= ((uint64)1 << flagBit);
			else
				registers->mIntRegs.efl &= ~((uint64)1 << flagBit);
		}
		else
		{
			outError = "Unrecognized flag";
			return false;
		}
	}
	else if ((regVal.mRegNum >= X86Reg_CAT_FIRST) && (regVal.mRegNum <= X86Reg_CAT_LAST))
	{
		outError = "Cannot write directly to register categories, please use inner float components";
		return false;
	}
#else

	if ((regVal.mRegNum >= X64Reg_INTREG_FIRST) && (regVal.mRegNum <= X64Reg_INTREG_LAST))
	{
		//BF_ASSERT(regVal.mType->mSize == sizeof(addr_target));
		registers->mIntRegsArray[regVal.mRegNum - X64Reg_INTREG_FIRST] = value.GetInt64(); // don't sign-extend
		regPtr = &registers->mIntRegsArray[regVal.mRegNum - X64Reg_INTREG_FIRST];
	}
	else if ((regVal.mRegNum >= X64Reg_FPSTREG_FIRST) && (regVal.mRegNum <= X64Reg_FPSTREG_LAST))
	{
		BF_ASSERT(regVal.mType->mSize == sizeof(float) || regVal.mType->mSize == sizeof(double));
		CPURegisters::FpMmReg* reg = &registers->mFpMmRegsArray[regVal.mRegNum - X64Reg_FPSTREG_FIRST];
		double d;
		if (regVal.mType->mSize == sizeof(float))
			d = (double)value.mSingle;
		else
			d = value.mDouble;
		ConvertDoubleToFloat80(d, reg->fp.fp80);
		regPtr = reg;
	}
	else if ((regVal.mRegNum >= X64Reg_MMREG_FIRST) && (regVal.mRegNum <= X64Reg_MMREG_LAST))
	{
		BF_ASSERT(regVal.mType->mSize == sizeof(int32) || regVal.mType->mSize == sizeof(int64));
		CPURegisters::FpMmReg* reg = &registers->mFpMmRegsArray[regVal.mRegNum - X64Reg_MMREG_FIRST];
		if (regVal.mType->mSize == sizeof(int32))
			reg->mm = (uint64)value.mUInt32; // don't sign-extend
		else if (regVal.mType->mSize == sizeof(int64))
			reg->mm = value.mInt64;
		// whenever we use the low 64 bits of the reg as mm, the upper 16 bits of the 80-bit float must be set to all-1s to indicate NaN
		reg->fp.fp80[8] = reg->fp.fp80[9] = 0xFF;
		regPtr = reg;
	}
	else if ((regVal.mRegNum >= X64Reg_XMMREG_FIRST) && (regVal.mRegNum <= X64Reg_XMMREG_LAST))
	{
		int xmmMajor = (regVal.mRegNum - X64Reg_XMMREG_FIRST) >> 2;
		int xmmMinor = (regVal.mRegNum - X64Reg_XMMREG_FIRST) & 3;

		if (value.mType->GetByteCount() == 4)
			registers->mXmmRegsArray[xmmMajor].f[xmmMinor] = value.mSingle;
		else if (value.mType->GetByteCount() == 8)
			registers->mXmmDRegsArray[xmmMajor].d[xmmMinor] = value.mDouble;
		else
			BF_FATAL("Invalid XMM set value type");
		regPtr = &registers->mXmmRegsArray[xmmMajor];
	}
	else if ((regVal.mRegNum >= X64Reg_M128_XMMREG_FIRST) && (regVal.mRegNum <= X64Reg_M128_XMMREG_LAST))
	{
		outError = "Cannot write directly to 128-bit XMM register, please use inner float components";
		return false;
	}
	else if ((regVal.mRegNum >= X64Reg_FLAG_FIRST) && (regVal.mRegNum <= X64Reg_FLAG_LAST))
	{
		int flagBit = CPURegisters::GetFlagBitForRegister(regVal.mRegNum);
		if (flagBit >= 0)
		{
			if (value.mBool)
				registers->mIntRegs.efl |= ((uint64)1 << flagBit);
			else
				registers->mIntRegs.efl &= ~((uint64)1 << flagBit);
			regPtr = &registers->mIntRegs.efl;
		}
		else
		{
			outError = "Unrecognized flag";
			return false;
		}
	}
	else if ((regVal.mRegNum >= X64Reg_CAT_FIRST) && (regVal.mRegNum <= X64Reg_CAT_LAST))
	{
		outError = "Cannot write directly to register categories, please use inner float components";
		return false;
	}
	else
		BF_FATAL("Not implemented");
#endif

	if (callStackIdx == 0)
	{
		SetRegisters(&mCallStack[0]->mRegisters);
		return true;
	}
	else
	{
		bool wasSaved = false;
		for (int calleeStackIdx = callStackIdx - 1; calleeStackIdx >= 0; calleeStackIdx--)
		{
			auto calleeRegisters = &mCallStack[calleeStackIdx]->mRegisters;
			if (!mDebugTarget->PropogateRegisterUpCallStack(registers, calleeRegisters, regPtr, wasSaved))
			{
				outError = "Failed to set register";
				return false;
			}
			if (wasSaved)
				return true;
		}

		// This register wasn't saved, so commit it to the callstack top
		return AssignToReg(0, regVal, value, outError);
	}
}

String WinDebugger::GetAutocompleteOutput(DwAutoComplete& autoComplete)
{
	String val = "\n:autocomplete\n";

	if (autoComplete.mInsertStartIdx != -1)
	{
		val += StrFormat("insertRange\t%d %d\n", autoComplete.mInsertStartIdx, autoComplete.mInsertEndIdx);
	}

	Array<AutoCompleteEntry*> entries;

	for (auto& entry : autoComplete.mEntriesSet)
	{
		entries.Add(&entry);
	}
	std::sort(entries.begin(), entries.end(), [](AutoCompleteEntry* lhs, AutoCompleteEntry* rhs)
	{
		return stricmp(lhs->mDisplay, rhs->mDisplay) < 0;
	});

	for (auto entry : entries)
	{
		val += String(entry->mEntryType);
		val += "\t";
		val += String(entry->mDisplay);
		val += "\n";
	}

	/*if (autoComplete.mEntries.size() != 0)
	{
		for (auto& entry : autoComplete.mEntries)
		{
			val += String(entry.mEntryType) + "\t" + String(entry.mDisplay) + "\n";
		}
	}*/
	return val;
}

String WinDebugger::EvaluateToAddress(const StringImpl& expr, int callStackIdx, int cursorPos)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (IsInRunState())
		return "!Target not paused";

	auto dbgModule = GetCallStackDbgModule(callStackIdx);
	auto dbgCompileUnit = GetCallStackCompileUnit(callStackIdx);

	BfParser parser(mBfSystem);
	parser.mCompatMode = true;

	BfPassInstance bfPassInstance(mBfSystem);
	auto terminatedExpr = expr + ";";
	parser.SetSource(terminatedExpr.c_str(), terminatedExpr.length());
	parser.Parse(&bfPassInstance);

	BfReducer bfReducer;
	bfReducer.mAlloc = parser.mAlloc;
	bfReducer.mSystem = mBfSystem;
	bfReducer.mPassInstance = &bfPassInstance;
	bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser.mRootNode);
	bfReducer.mVisitorPos.MoveNext();
	bfReducer.mSource = &parser;
	auto exprNode = bfReducer.CreateExpression(parser.mRootNode->GetFirst());
	parser.Close();

	DwAutoComplete autoComplete;
	DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, callStackIdx, cursorPos);
	if (cursorPos != -1)
		dbgExprEvaluator.mAutoComplete = &autoComplete;
	dbgExprEvaluator.mDbgCompileUnit = dbgCompileUnit;

	DwFormatInfo formatInfo;
	formatInfo.mCallStackIdx = callStackIdx;

	DbgTypedValue exprResult;
	if (exprNode != NULL)
		exprResult = dbgExprEvaluator.Resolve(exprNode);

	DbgType* resultType = exprResult.mType->RemoveModifiers();

	String val;
	if (bfPassInstance.HasFailed())
	{
		val = StrFormat("!%d\t%d\t%s", bfPassInstance.mErrors[0]->mSrcStart, bfPassInstance.mErrors[0]->GetSrcLength(), bfPassInstance.mErrors[0]->mError.c_str());
	}
	else if (exprResult.mType == NULL)
	{
		val = "!Invalid expression";
	}
	else if (!resultType->IsPointerOrRef())
	{
		if (exprResult.mSrcAddress != 0)
			val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. Try using the '&' address-of operator.", exprResult.mType->ToString().c_str());
		else
			val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected. An explicit cast may be required.", exprResult.mType->ToString().c_str());
	}
	else
	{
		auto innerType = resultType->mTypeParam;
		int byteCount = innerType->GetByteCount();
		if (byteCount == 0)
		{
			val = StrFormat("!Type '%s' is invalid. A sized pointer type is expected, try casting to a non-void pointer type.", exprResult.mType->ToString().c_str());
		}
#ifdef BF_DBG_32
		else if ((byteCount != 1) && (byteCount != 2) && (byteCount != 4))
		{
			val = StrFormat("!Element size is %d bytes. Only 1, 2, or 4 byte elements can be tracked. Try casting to an appropriately-sized pointer.", innerType->mSize);
		}
#else
		else if ((byteCount != 1) && (byteCount != 2) && (byteCount != 4) && (byteCount != 8))
		{
			val = StrFormat("!Element size is %d bytes. Only 1, 2, 4, or 8 byte elements can be tracked. Try casting to an appropriately-sized pointer.", innerType->mSize);
		}
#endif
		else
		{
			val = EncodeDataPtr(exprResult.mPtr, false) + "\n" + StrFormat("%d", byteCount);
		}
	}

	if (cursorPos != -1)
		val += GetAutocompleteOutput(autoComplete);

	return val;
}

// This is currently only used for autocomplete during conditional breakpoint expression entry.
//  If we want to use it for more than that then remove DwEvalExpressionFlags_ValidateOnly
String WinDebugger::EvaluateAtAddress(const StringImpl& expr, intptr atAddr, int cursorPos)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (IsInRunState())
		return "!Target not paused";

	if (!IsPaused())
		return "!Target not running";

	WdStackFrame stackFrame;
	memset(&stackFrame.mRegisters, 0, sizeof(stackFrame.mRegisters));
	stackFrame.mHasGottenSubProgram = true;
	*stackFrame.mRegisters.GetPCRegisterRef() = (intptr_target)atAddr;
	stackFrame.mSubProgram = mDebugTarget->FindSubProgram((addr_target)atAddr);
	if (stackFrame.mSubProgram == NULL)
		return "!Invalid address";
	mCallStack.push_back(&stackFrame);
	int callStackIdx = (int)mCallStack.size() - 1;
	String val = Evaluate(expr, callStackIdx, cursorPos, -1, DwEvalExpressionFlag_ValidateOnly);
	mCallStack.pop_back();

	return val;
}

String WinDebugger::GetAutoExpressions(int callStackIdx, uint64 memoryRangeStart, uint64 memoryRangeLen)
{
	BP_ZONE("WinDebugger::GetAutoExpressions");

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (IsInRunState())
		return "!Not paused";

	if (!IsPaused())
		return "!Not running";

	if (!FixCallStackIdx(callStackIdx))
		return "";

	CPUStackFrame* stackFrame = (callStackIdx >= 0) ? mCallStack[callStackIdx] : mCallStack.front();

	String result;

	DbgAutoValueMapType dwarfAutos;
	mDebugTarget->GetAutoValueNames(dwarfAutos, stackFrame, memoryRangeStart, memoryRangeLen);

	for (auto const &a : dwarfAutos)
	{
		std::pair<uint64, uint64> varRange = a.mValue;
		if (varRange.first != 0)
			result += StrFormat("&%s\t%llu\t%llu\n", a.mKey.c_str(), varRange.second, varRange.first);
		else
			result += StrFormat("?%s\t%llu\n", a.mKey.c_str(), varRange.second);
	}

#ifdef BF_DBG_64
	// add int regs
	const char* regStrs[] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "rip", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", 0 };
#else
	// add int regs
	const char* regStrs[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "eip", 0 };
#endif
	for (const char** p = regStrs; *p; ++p)
		result += StrFormat("$%s\t%d\n", *p, sizeof(addr_target));

	if (callStackIdx < (int)mCallStack.size() - 2)
	{
		WdStackFrame* prevStackFrame = mCallStack[callStackIdx + 1];
		// Inlined methods have no stack frame
		int stackSize = prevStackFrame->mRegisters.GetSP() - stackFrame->mRegisters.GetSP();
		result += StrFormat("&$StackFrame\t%llu\t%llu\n", stackSize, stackFrame->mRegisters.GetSP());
	}

	return result;
}

String WinDebugger::GetAutoLocals(int stackFrameIdx, bool showRegs)
{
	BP_ZONE("WinDebugger::GetAutoExpressions");

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (IsInRunState())
		return "";

	if (!IsPaused())
		return "";

	if (mCallStack.size() == 0)
		UpdateCallStack();
	String result;

	Array<String> localList;

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	UpdateCallStackMethod(actualStackFrameIdx);

	if (actualStackFrameIdx >= mCallStack.size())
		return "";
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
	if (dwSubprogram == NULL)
		return "";

	auto langage = dwSubprogram->GetLanguage();

	DbgLineData* dwLineData = FindLineDataInSubprogram(wdStackFrame->GetSourcePC(), dwSubprogram);
	if (dwLineData == NULL)
		return "";

	dwSubprogram->PopulateSubprogram();
	mDebugTarget->GetAutoLocalsInBlock(localList, dwSubprogram, &dwSubprogram->mBlock, wdStackFrame, dwLineData);

	String lastLocal;
	for (auto local : localList)
	{
		if (langage == DbgLanguage_C)
		{
			if ((local == "this") && (strncmp(dwSubprogram->mName, "<lambda_", 8) == 0))
			{
				// Use explicit "$this" so we can see the actual capture
				result += "$this\n";
				continue;
			}
		}

		bool wasAlias = false;
		for (int i = 0; i < (int)local.length() - 1; i++)
		{
			if ((local[i] == '$') && (local[i + 1] == 'a'))
			{
				// Alias
				wasAlias = true;
				String localName = local.Substring(0, i) + "\n";
				if (localName != lastLocal)
				{
					result += localName;
					lastLocal = localName;
				}
				break;
			}
		}
		if (!wasAlias)
			result += local + "\n";
	}

	if (showRegs)
	{
		result += "$FLAGS\n";

		UpdateRegisterUsage(stackFrameIdx);
		for (int regIdx = 0; regIdx < (int)wdStackFrame->mRegForms.size(); regIdx++)
		{
			if (wdStackFrame->mRegForms[regIdx] != RegForm_Invalid)
				result += "$" + String(CPURegisters::GetRegisterName(regIdx)) + "\n";
		}
	}

	return result;
}

String WinDebugger::CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx)
{
	DbgCompileUnit* compileUnit = GetCallStackCompileUnit(callStackIdx);
	DbgModule* dbgModule = GetCallStackDbgModule(callStackIdx);
	if (dbgModule == NULL)
		return "!failed";

	DbgLanguage language = DbgLanguage_Unknown;
	if (compileUnit != NULL)
		language = compileUnit->mLanguage;

	auto terminatedParentExpr = parentExpr + ";";

	String parentPrefix;

	int colonIdx = terminatedParentExpr.IndexOf(':');
	if (colonIdx > 0)
	{
		bool isValid = true;
		String lang = terminatedParentExpr.Substring(1, colonIdx - 1);
		lang = ToUpper(lang);
		if ((lang == "") || (lang == "BEEF"))
		{
			language = DbgLanguage_Beef;
		}
		else if (lang == "C")
		{
			language = DbgLanguage_C;
		}
		if (language != DbgLanguage_Unknown)
		{
			parentPrefix += terminatedParentExpr.Substring(0, colonIdx + 1);
			terminatedParentExpr.Remove(0, colonIdx + 1);
		}
	}

	if (terminatedParentExpr.StartsWith('{'))
	{
		int prefixEnd = terminatedParentExpr.IndexOf('}');
		parentPrefix += terminatedParentExpr.Substring(0, prefixEnd + 1);
		terminatedParentExpr.Remove(0, prefixEnd + 1);
	}

	BfPassInstance bfPassInstance(mBfSystem);

	BfParser parser(mBfSystem);
	parser.mCompatMode = language != DbgLanguage_Beef;
	auto terminatedExpr = expr + ";";
	parser.SetSource(terminatedExpr.c_str(), terminatedExpr.length());
	parser.Parse(&bfPassInstance);

	BfParser parentParser(mBfSystem);
	parentParser.mCompatMode = language != DbgLanguage_Beef;
	parentParser.SetSource(terminatedParentExpr.c_str(), terminatedParentExpr.length());
	parentParser.Parse(&bfPassInstance);

	BfReducer bfReducer;
	bfReducer.mCompatMode = true;
	bfReducer.mAlloc = parser.mAlloc;
	bfReducer.mSystem = mBfSystem;
	bfReducer.mPassInstance = &bfPassInstance;
	bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parser.mRootNode);
	bfReducer.mVisitorPos.MoveNext();
	bfReducer.mSource = &parser;
	auto exprNode = bfReducer.CreateExpression(parser.mRootNode->GetFirst());
	bfReducer.mAlloc = parentParser.mAlloc;
	bfReducer.mVisitorPos = BfReducer::BfVisitorPos(parentParser.mRootNode);
	bfReducer.mVisitorPos.MoveNext();
	auto parentExprNode = bfReducer.CreateExpression(parentParser.mRootNode->GetFirst());
	parser.Close();

	if ((exprNode == NULL) || (parentExprNode == NULL))
		return "!failed";

	DbgExprEvaluator dbgExprEvaluator(this, dbgModule, &bfPassInstance, callStackIdx, -1);

	DwFormatInfo formatInfo;
	formatInfo.mCallStackIdx = callStackIdx;
	formatInfo.mLanguage = language;

	String formatFlags;
	String assignExpr;
	if ((exprNode != NULL) && (exprNode->GetSrcEnd() < (int) expr.length()))
	{
		formatFlags = Trim(expr.Substring(exprNode->GetSrcEnd()));
		if (formatFlags.length() > 0)
		{
			String errorString = "Invalid expression";
			if (!ParseFormatInfo(dbgModule, formatFlags, &formatInfo, &bfPassInstance, NULL, &assignExpr, &errorString))
			{
				bfPassInstance.FailAt(errorString, parser.mSourceData, exprNode->GetSrcEnd(), (int) expr.length() - exprNode->GetSrcEnd());
				formatFlags = "";
			}
		}
	}
	dbgExprEvaluator.mExplicitThis = formatInfo.mExplicitThis;
	dbgExprEvaluator.mExplicitThisExpr = parentExprNode;

	DbgTypedValue exprResult = dbgExprEvaluator.Resolve(exprNode);
	BfAstNode* headNode = dbgExprEvaluator.FinalizeExplicitThisReferences(exprNode);

	BfPrinter printer(parser.mRootNode, NULL, NULL);
	printer.mIgnoreTrivia = true;
	printer.mReformatting = true;
	printer.VisitChild(headNode);
	String result;
	result += parentPrefix;
	result += printer.mOutString;
	if (formatInfo.mNoVisualizers)
		result += ", nv";
	if (formatInfo.mNoMembers)
		result += ", nm";
	if (formatInfo.mNoEdit)
		result += ", ne";
	if (formatInfo.mIgnoreDerivedClassInfo)
		result += ", nd";
	if (formatInfo.mDisplayType == DwDisplayType_Ascii)
		result += ", s";
	if (formatInfo.mDisplayType == DwDisplayType_Utf8)
		result += ", s8";
	if (formatInfo.mDisplayType == DwDisplayType_Utf16)
		result += ", s16";
	if (formatInfo.mDisplayType == DwDisplayType_Utf32)
		result += ", s32";
	return result;
}

String WinDebugger::GetProcessInfo()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((mActiveThread == NULL) && (!mIsRunning))
		return "";

	SYSTEM_INFO sysinfo = { 0 };
	GetSystemInfo(&sysinfo);

	FILETIME creationTime = { 0 };
	FILETIME exitTime = { 0 };
	FILETIME kernelTime = { 0 };
	FILETIME userTime = { 0 };
	::GetProcessTimes(mProcessInfo.hProcess, &creationTime, &exitTime, &kernelTime, &userTime);

	String retStr;
	PROCESS_MEMORY_COUNTERS memInfo = { 0 };
	::GetProcessMemoryInfo(mProcessInfo.hProcess, &memInfo, sizeof(PROCESS_MEMORY_COUNTERS));

	FILETIME currentTime = { 0 };
	::GetSystemTimeAsFileTime(&currentTime);

	retStr += StrFormat("VirtualMemory\t%lld\n", memInfo.PagefileUsage);
	retStr += StrFormat("WorkingMemory\t%lld\n", memInfo.WorkingSetSize);
	retStr += StrFormat("RunningTime\t%lld\n", *(int64*)&currentTime - *(int64*)&creationTime);
	retStr += StrFormat("KernelTime\t%lld\n", *(int64*)&kernelTime / sysinfo.dwNumberOfProcessors);
	retStr += StrFormat("UserTime\t%lld\n", *(int64*)&userTime / sysinfo.dwNumberOfProcessors);

	return retStr;
}

int WinDebugger::GetProcessId()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	if (!mThreadList.IsEmpty())
		return mThreadList[0]->mProcessId;
	return mDbgProcessId;
}

String WinDebugger::GetThreadInfo()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	String retStr;
	if ((mActiveThread == NULL) && (!mIsRunning))
	{
		retStr = "";
	}
	else
	{
		if (mActiveThread != NULL)
			retStr = StrFormat("%d", mActiveThread->mThreadId);

		for (auto threadInfo : mThreadList)
		{
			SetAndRestoreValue<WdThreadInfo*> prevThread(mActiveThread, threadInfo);

			retStr += "\n";

			for (int pass = 0; pass < 2; pass++)
			{
				CPURegisters registers;
				PopulateRegisters(&registers);

				String locString = EncodeDataPtr((addr_target)registers.GetPC(), true);

				TryGetThreadName(threadInfo);

				bool hadThreadName = true;
				String threadName = threadInfo->mName;
				if (threadName.IsEmpty())
				{
					hadThreadName = false;
					if (threadInfo->mThreadId == mProcessInfo.dwThreadId)
						threadName = "Main Thread";
					else
						threadName = "Worker Thread";
				}

				bool isInvalid = false;
				addr_target appendAddr = 0;

				for (int stackIdx = 0; true; stackIdx++)
				{
					auto subProgram = mDebugTarget->FindSubProgram(registers.GetPC(), DbgOnDemandKind_LocalOnly);
					if (subProgram != NULL)
					{
						if (subProgram->mLineInfo != NULL)
						{
							DbgModule* module = subProgram->mCompileUnit->mDbgModule;
							DbgModule* linkedModule = module->GetLinkedModule();
							if (linkedModule->mDisplayName.length() > 0)
							{
								locString = linkedModule->mDisplayName + "!" + subProgram->ToString();
								if (!hadThreadName)
									threadName = module->mDisplayName + " thread";
							}
							else
							{
								locString = subProgram->ToString();
							}
							appendAddr = 0;
							break;
						}
					}

					DbgModule* module = mDebugTarget->FindDbgModuleForAddress(registers.GetPC());
					if (module == NULL)
					{
						isInvalid = true;
						break;
					}

					DbgModule* linkedModule = module->GetLinkedModule();
					appendAddr = (addr_target)registers.GetPC();
					locString = linkedModule->mDisplayName + "!" + EncodeDataPtr((addr_target)registers.GetPC(), true);
					if (!hadThreadName)
						threadName = linkedModule->mDisplayName + " thread";

					if ((mActiveThread == mExplicitStopThread) && (mActiveBreakpoint != NULL))
					{
						if ((subProgram == NULL) ||
							(mActiveBreakpoint->mAddr < subProgram->mBlock.mLowPC) ||
							(mActiveBreakpoint->mAddr >= subProgram->mBlock.mHighPC))
							break;
					}

					if (pass == 1) // Just take the first item
						break;

					if (stackIdx == 128)
						break; // Too many!

					addr_target returnAddr;
					if (!mDebugTarget->RollBackStackFrame(&registers, &returnAddr, true))
					{
						isInvalid = true;
						break;
					}
				}

				if ((isInvalid) && (pass == 0))
					continue;

				if (appendAddr != 0)
				{
					String symbolName;
					addr_target offset;
					DbgModule* dwarf;
					if (mDebugTarget->FindSymbolAt(appendAddr, &symbolName, &offset, &dwarf))
					{
						DbgModule* linkedModule = dwarf->GetLinkedModule();
						String demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Unknown);
						if (!linkedModule->mDisplayName.empty())
						{
							demangledName = linkedModule->mDisplayName + "!" + demangledName;
						}
						locString = demangledName + StrFormat("+0x%X", offset);
					}
				}

				retStr += StrFormat("%d\t%s\t%s", threadInfo->mThreadId, threadName.c_str(), locString.c_str());

				String attrs;
				if (threadInfo->mFrozen)
				{
					attrs += "Fr";
				}
				if (!attrs.IsEmpty())
				{
					retStr += "\t";
					retStr += attrs;
				}

				break;
			}
		}
	}
	return retStr;
}

void WinDebugger::SetActiveThread(int threadId)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((mActiveThread != NULL) && (mActiveThread->mThreadId == threadId))
		return;

	auto prevThread = mActiveThread;

	if (mThreadMap.TryGetValue(threadId, &mActiveThread))
	{
		BfLogDbg("SetActiveThread %d\n", threadId);

		if (prevThread != NULL)
		{
			Array<WdStackFrame*>* prevFrameArray = NULL;
			mSavedCallStacks.TryAdd(prevThread, NULL, &prevFrameArray);
			for (auto frameInfo : *prevFrameArray)
				delete frameInfo;
			*prevFrameArray = mCallStack;
			mCallStack.Clear();
		}

		DoClearCallStack(false);

		Array<WdStackFrame*>* newFrameArray = NULL;
		if (mSavedCallStacks.TryGetValue(mActiveThread, &newFrameArray))
		{
			mCallStack = *newFrameArray;
			newFrameArray->Clear();
		}
	}
	else
	{
		BfLogDbg("SetActiveThread %d FAILED\n", threadId);
	}
}

int WinDebugger::GetActiveThread()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (mActiveThread == NULL)
		return -1;
	return mActiveThread->mThreadId;
}

void WinDebugger::FreezeThread(int threadId)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BF_ASSERT(!IsInRunState());
	auto thread = mThreadMap[threadId];
	if (!thread->mFrozen)
	{
		thread->mFrozen = true;
		::SuspendThread(thread->mHThread);
		BfLogDbg("SuspendThread %d from FreezeThread\n", thread->mThreadId);
	}
}

void WinDebugger::ThawThread(int threadId)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BF_ASSERT(!IsInRunState());
	auto thread = mThreadMap[threadId];
	if (thread->mFrozen)
	{
		thread->mFrozen = false;
		::ResumeThread(thread->mHThread);
		BfLogDbg("ResumeThread %d from ThawThread\n", thread->mThreadId);
	}
}

bool WinDebugger::IsActiveThreadWaiting()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	return mActiveThread == mDebuggerWaitingThread;
}

void WinDebugger::DoClearCallStack(bool clearSavedStacks)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	BfLogDbg("ClearCallstack\n");
	BF_ASSERT(mRunState != RunState_DebugEval);

	for (auto wdStackFrame : mCallStack)
		delete wdStackFrame;

	if (clearSavedStacks)
	{
		for (auto& kv : mSavedCallStacks)
		{
			for (auto wdStackFrame : kv.mValue)
				delete wdStackFrame;
		}
		mSavedCallStacks.Clear();
	}

	mCallStack.Clear();
	mIsPartialCallStack = true;
}

void WinDebugger::ClearCallStack()
{
	DoClearCallStack(true);
}

void WinDebugger::UpdateCallStack(bool slowEarlyOut)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!mIsPartialCallStack)
		return;

	if (mActiveThread == NULL)
		return;

	if (IsInRunState())
		return;

	uint32 tickStart = BFTickCount();

	CPURegisters registers;

	if (mCallStack.size() > 0)
	{
		WdStackFrame* wdStackFrame = mCallStack.back();
		if (wdStackFrame->mIsEnd)
		{
			return;
		}
		memcpy(&registers, &wdStackFrame->mRegisters, sizeof(registers));
		bool regsRolledBack = RollBackStackFrame(&registers, mCallStack.size() == 1);
		// If we can't roll them back then mIsEnd should have been set for the previous frame
		BF_ASSERT(regsRolledBack);
	}
	else
	{
		BF_ASSERT(mIsPartialCallStack);
		mCallStack.Reserve(1024);
		PopulateRegisters(&registers);
		BfLogDbg("UpdateCallStack starting. Thread=%d PC=0x%p\n", mActiveThread->mThreadId, registers.GetPC());
	}

	bool isPartial = false;

	// Incrementally fill callstack structure to avoid stepping slowdown during deep nesting
	for (int fillIdx = 0; fillIdx < (slowEarlyOut ? 10000 : 100000); fillIdx++)
	{
		WdStackFrame* wdStackFrame = new WdStackFrame();
		memcpy(&wdStackFrame->mRegisters, &registers, sizeof(registers));
		wdStackFrame->mIsStart = mCallStack.size() == 0;
		wdStackFrame->mIsEnd = false;

		bool rollbackSuccess = false;
		for (int tryCount = 0; tryCount < 16; tryCount++)
		{
			if (!RollBackStackFrame(&registers, wdStackFrame->mIsStart))
			{
				break;
			}
			if (registers.GetPC() > 0xFFFF)
			{
				rollbackSuccess = true;
				break;
			}

			if (mCallStack.size() > 0)
				break; // Only retry for the first frame
		}

		if (!rollbackSuccess)
			wdStackFrame->mIsEnd = true;

		if (registers.GetSP() <= wdStackFrame->mRegisters.GetSP())
		{
			// SP went the wrong direction, stop rolling back
			wdStackFrame->mIsEnd = true;
		}

		mCallStack.push_back(wdStackFrame);

		if (IsMiniDumpDebugger())
		{
			// Make sure to queue up any debug stuff we need
			UpdateCallStackMethod((int)mCallStack.size() - 1);
		}

		if (wdStackFrame->mIsEnd)
			break;

		// Time-limit callstack generation.  Most useful for debug mode.
		if ((slowEarlyOut) && ((fillIdx % 100) == 0))
		{
			uint32 tickEnd = BFTickCount();
			if (tickEnd - tickStart >= 10)
			{
				isPartial = true;
				break;
			}
		}
	}

	if (!isPartial)
		mIsPartialCallStack = false;
}

int WinDebugger::GetCallStackCount()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	return (int)mCallStack.size();
}

int WinDebugger::GetRequestedStackFrameIdx()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((mActiveThread == mExplicitStopThread) && (mRequestedStackFrameIdx >= -1))
	{
		if (mActiveBreakpoint != NULL)
			mRequestedStackFrameIdx = GetBreakStackFrameIdx();
		if (mRequestedStackFrameIdx == -1)
			mRequestedStackFrameIdx = 0;
		return mRequestedStackFrameIdx;
	}

	int newCallStackIdx = 0;
	while (true)
	{
		if (newCallStackIdx >= (int)mCallStack.size() - 1)
			UpdateCallStack();
		if (newCallStackIdx >= (int)mCallStack.size() - 1)
			break;

		intptr addr;
		String file;
		int hotIdx;
		int defLineStart;
		int defLineEnd;
		int line;
		int column;
		int language;
		int stackSize;
		int8 flags;
		GetStackFrameInfo(newCallStackIdx, &addr, &file, &hotIdx, &defLineStart, &defLineEnd, &line, &column, &language, &stackSize, &flags);
		if (!file.empty())
			return newCallStackIdx;
		newCallStackIdx++;
	}

	return 0;
}

int WinDebugger::GetBreakStackFrameIdx()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if ((mActiveBreakpoint == NULL) || (mRunState != RunState_Breakpoint))
		return -1;
	if ((mBreakStackFrameIdx != -1) || (mActiveThread != mExplicitStopThread))
		return mBreakStackFrameIdx;

	mBreakStackFrameIdx = 0;
	BF_ASSERT(mActiveBreakpoint != NULL);
	if (mCallStack.IsEmpty())
		UpdateCallStack();
	if (!mCallStack.IsEmpty())
	{
		UpdateCallStackMethod(0);

		for (int stackIdx = 0; stackIdx < (int)mCallStack.size(); stackIdx++)
		{
			auto callStackEntry = mCallStack[stackIdx];
			if (callStackEntry->mSubProgram == NULL)
				break;
			if ((mActiveBreakpoint->mAddr < callStackEntry->mSubProgram->mBlock.mLowPC) ||
				(mActiveBreakpoint->mAddr >= callStackEntry->mSubProgram->mBlock.mHighPC))
				break;

			DbgSubprogram* specificSubprogram = callStackEntry->mSubProgram;
			auto dwLineData = callStackEntry->mSubProgram->FindClosestLine(mActiveBreakpoint->mAddr, &specificSubprogram);
			if (dwLineData == NULL)
				break;

			if (mActiveBreakpoint->mLineData == dwLineData)
			{
				mBreakStackFrameIdx = stackIdx;
				break;
			}
		}
	}
	return mBreakStackFrameIdx;
}

static const char* SafeString(const char* str)
{
	if (str == NULL)
		return "???";
	return str;
}

void WinDebugger::UpdateRegisterUsage(int stackFrameIdx)
{
	WdStackFrame* wdStackFrame = mCallStack[stackFrameIdx];
	if (wdStackFrame->mRegForms.size() != 0)
		return;

	auto dwSubprogram = wdStackFrame->mSubProgram;
	if (dwSubprogram == NULL)
		return;

	addr_target addr = dwSubprogram->mBlock.mLowPC;

	const uint8* baseOp = nullptr;
	while (addr < dwSubprogram->mBlock.mHighPC)
	{
		CPUInst inst;
		if (!mDebugTarget->DecodeInstruction(addr, &inst))
			break;

		bool overrideForm = inst.mAddress <= (addr_target)wdStackFrame->mRegisters.GetPC();
		inst.MarkRegsUsed(wdStackFrame->mRegForms, overrideForm);

		addr += inst.GetLength();
	}
}

// It's safe to pass an invalid idx in here
void WinDebugger::UpdateCallStackMethod(int stackFrameIdx)
{
	if (mCallStack.empty())
		return;

	int startIdx = std::min(stackFrameIdx, (int)mCallStack.size() - 1);
	while (startIdx >= 0)
	{
		WdStackFrame* wdStackFrame = mCallStack[startIdx];
		if (wdStackFrame->mHasGottenSubProgram)
			break;
		startIdx--;
	}
	startIdx++;

	for (int checkFrameIdx = startIdx; checkFrameIdx <= stackFrameIdx; checkFrameIdx++)
	{
		//BF_ASSERT(checkFrameIdx < mCallStack.size());
		if (checkFrameIdx >= mCallStack.size())
			break;

		WdStackFrame* wdStackFrame = mCallStack[checkFrameIdx];

		wdStackFrame->mHasGottenSubProgram = true;
		addr_target pcAddress = (addr_target)wdStackFrame->GetSourcePC();
		DbgSubprogram* dwSubprogram = mDebugTarget->FindSubProgram(pcAddress, DbgOnDemandKind_LocalOnly);
		wdStackFrame->mHasGottenSubProgram = true;
		wdStackFrame->mSubProgram = dwSubprogram;

		if ((dwSubprogram == NULL) && (IsMiniDumpDebugger()))
		{
			// FindSymbolAt will queue up debug info if necessary...
			String symbolName;
			addr_target offset;
			DbgModule* dbgModule;
			mDebugTarget->FindSymbolAt(pcAddress, &symbolName, &offset, &dbgModule);
		}

		auto prevStackFrame = wdStackFrame;

		// Insert inlines
		int insertIdx = checkFrameIdx + 1;
		while ((dwSubprogram != NULL) && (dwSubprogram->mInlineeInfo != NULL))
		{
			WdStackFrame* inlineStackFrame = new WdStackFrame();
			*inlineStackFrame = *wdStackFrame;
			inlineStackFrame->mInInlineMethod = true;
			wdStackFrame->mInInlineCall = true;
			inlineStackFrame->mSubProgram = dwSubprogram->mInlineeInfo->mInlineParent;
			mCallStack.Insert(insertIdx, inlineStackFrame);
			dwSubprogram = dwSubprogram->mInlineeInfo->mInlineParent;
			insertIdx++;
			checkFrameIdx++;

			prevStackFrame = inlineStackFrame;
		}
	}
}

void WinDebugger::GetCodeAddrInfo(intptr addr, intptr inlineCallAddr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd, int* outLine, int* outColumn)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	DbgSubprogram* subProgram = NULL;
	DbgLineData* callingLineData = FindLineDataAtAddress((addr_target)addr, &subProgram);

	if (inlineCallAddr != 0)
	{
		auto inlinedSubProgram = mDebugTarget->FindSubProgram(inlineCallAddr);
		if (inlinedSubProgram != 0)
		{
			FixupLineDataForSubprogram(inlinedSubProgram->mInlineeInfo->mRootInliner);
			DbgSubprogram* parentSubprogram = inlinedSubProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
			auto foundLine = parentSubprogram->FindClosestLine(inlinedSubProgram->mBlock.mLowPC, &parentSubprogram);
			if (foundLine != NULL)
			{
				auto srcFile = parentSubprogram->GetLineSrcFile(*foundLine);
				*outFile = srcFile->GetLocalPath();
				*outLine = foundLine->mLine;
			}

			*outHotIdx = inlinedSubProgram->mCompileUnit->mDbgModule->mHotIdx;
			*outColumn = -1;

			DbgSubprogram* callingSubProgram = NULL;
			DbgLineData* callingLineData = FindLineDataAtAddress(inlinedSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
			if ((callingLineData != NULL) && (callingSubProgram == subProgram))
			{
				auto callingSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
				auto srcFile = callingSrcFile;
				*outFile = srcFile->GetLocalPath();

				if (*outLine == callingLineData->mLine)
					*outColumn = callingLineData->mColumn;
			}
			return;
		}
	}

	if (subProgram != NULL)
	{
		if ((subProgram->mInlineeInfo != NULL) && ((addr_target)addr >= subProgram->mBlock.mHighPC))
			callingLineData = &subProgram->mInlineeInfo->mLastLineData;

		*outHotIdx = subProgram->mCompileUnit->mDbgModule->mHotIdx;
		*outFile = subProgram->GetLineSrcFile(*callingLineData)->GetLocalPath();
		*outLine = callingLineData->mLine;
		*outColumn = callingLineData->mColumn;

		FixupLineDataForSubprogram(subProgram);
		DbgLineData* dwStartLineData = NULL;
		DbgLineData* dwEndLineData = NULL;
		if (subProgram->mLineInfo != NULL)
		{
			if (subProgram->mLineInfo->mLines.size() > 0)
			{
				dwStartLineData = &subProgram->mLineInfo->mLines[0];
				dwEndLineData = &subProgram->mLineInfo->mLines.back();
			}
		}
		else
		{
			if (subProgram->mInlineeInfo != NULL)
			{
				dwStartLineData = &subProgram->mInlineeInfo->mFirstLineData;
				dwEndLineData = &subProgram->mInlineeInfo->mLastLineData;
			}
		}

		if (dwEndLineData != NULL)
		{
			*outDefLineStart = dwStartLineData->mLine;
			*outDefLineEnd = dwEndLineData->mLine;
		}
	}
}

void WinDebugger::GetStackAllocInfo(intptr addr, int* outThreadId, int* outStackIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	*outThreadId = 0;
	if (outStackIdx != NULL)
		*outStackIdx = -1;

	if (!IsPaused())
		return;

	for (auto thread : mThreadList)
	{
		NT_TIB64 tib = { 0 };
		if (!ReadMemory((intptr)thread->mThreadLocalBase, sizeof(tib), &tib))
			continue;

		MEMORY_BASIC_INFORMATION stackInfo = { 0 };
		if (VirtualQueryEx(mProcessInfo.hProcess, (void*)(tib.StackBase - 1), &stackInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
			continue;

		if ((addr >= (intptr)stackInfo.AllocationBase) && (addr < (intptr)tib.StackBase))
		{
			*outThreadId = thread->mThreadId;

			if (outStackIdx == NULL)
				return;

			if (mActiveThread == thread)
			{
				UpdateCallStack(false);
				for (int callStackIdx = 0; callStackIdx < (int)mCallStack.size(); callStackIdx++)
				{
					UpdateCallStackMethod(callStackIdx);

					auto stackFrame = mCallStack[callStackIdx];
					if (addr >= (intptr)stackFrame->mRegisters.GetSP())
					{
						*outStackIdx = callStackIdx;
					}
				}
			}
			return;
		}
	}
}

String WinDebugger::GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* outFile, int* outHotIdx, int* outDefLineStart, int* outDefLineEnd,
	int* outLine, int* outColumn, int* outLanguage, int* outStackSize, int8* outFlags)
{
	enum FrameFlags
	{
		FrameFlags_Optimized = 1,
		FrameFlags_HasPendingDebugInfo = 2,
		FrameFlags_CanGetOldSource = 4,
		FrameFlags_WasHotReplaced = 8,
		FrameFlags_HadError = 0x10
	};

	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (mCallStack.size() == 0)
		UpdateCallStack();

	*addr = 0;
	*outFile = "";
	*outHotIdx = 0;
	*outDefLineStart = -1;
	*outDefLineEnd = -1;
	*outLine = -1;
	*outColumn = 0;
	*outLanguage = 0;
	*outStackSize = 0;
	*outFlags = 0;

	UpdateCallStackMethod(stackFrameIdx);

	if (stackFrameIdx >= mCallStack.size())
	{
		return "";
	}

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);

	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();
	if (stackFrameIdx == -1)
		pcAddress = mShowPCOverride;

	*addr = pcAddress;

	if (actualStackFrameIdx < (int)mCallStack.size() - 2)
	{
		WdStackFrame* prevStackFrame = mCallStack[actualStackFrameIdx + 1];
		// Inlined methods have no stack frame
		*outStackSize = prevStackFrame->mRegisters.GetSP() - wdStackFrame->mRegisters.GetSP();
	}

	const auto& _CheckHashSrcFile = [&](String& outStr, DbgModule* dbgModule, DbgSrcFile* srcFile)
	{
		if (srcFile->mHashKind != DbgHashKind_None)
		{
			outStr += "#";
			srcFile->GetHash(outStr);
		}
	};

	auto _SetFlags = [&](DbgSubprogram* dwSubprogram)
	{
		DbgModule* dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
		if (dwSubprogram->mIsOptimized)
			*outFlags |= FrameFlags_Optimized;
		if (dbgModule->HasPendingDebugInfo())
			*outFlags |= FrameFlags_HasPendingDebugInfo;
		if (dbgModule->CanGetOldSource())
			*outFlags |= FrameFlags_CanGetOldSource;
		if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
			*outFlags |= FrameFlags_WasHotReplaced;
	};

	auto _FixFilePath = [&](DbgModule* dbgModule)
	{
		if (outFile == NULL)
			return;

		if (outFile->StartsWith("$Emit"))
		{
			int dollarPos = outFile->IndexOf('$', 1);
			if (dollarPos == -1)
				return;

			outFile->Insert(dollarPos, StrFormat("%d", dbgModule->mId));
		}
	};

	if (wdStackFrame->mInInlineMethod)
	{
		WdStackFrame* nextStackFrame = mCallStack[actualStackFrameIdx - 1];
		auto subProgram = nextStackFrame->mSubProgram;

		_SetFlags(subProgram);

		FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);
		DbgSubprogram* parentSubprogram = subProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
		auto foundLine = parentSubprogram->FindClosestLine(subProgram->mBlock.mLowPC, &parentSubprogram);
		if (foundLine != NULL)
		{
			auto srcFile = parentSubprogram->GetLineSrcFile(*foundLine);
			*outFile = srcFile->GetLocalPath();
			_CheckHashSrcFile(*outFile, subProgram->mCompileUnit->mDbgModule, srcFile);
			*outLine = foundLine->mLine;
		}

		*outLanguage = subProgram->GetLanguage();
		*outHotIdx = subProgram->mCompileUnit->mDbgModule->mHotIdx;
		*outColumn = -1;

		DbgSubprogram* callingSubProgram = NULL;
		DbgLineData* callingLineData = FindLineDataAtAddress(nextStackFrame->mSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
		if ((callingLineData != NULL) && (callingSubProgram == wdStackFrame->mSubProgram))
		{
			auto callingSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
			*outLanguage = callingSubProgram->mCompileUnit->mLanguage;
			auto srcFile = callingSrcFile;
			*outFile = srcFile->GetLocalPath();

			_CheckHashSrcFile(*outFile, subProgram->mCompileUnit->mDbgModule, srcFile);
			if (*outLine == callingLineData->mLine)
				*outColumn = callingLineData->mColumn;
		}

		String name = wdStackFrame->mSubProgram->ToString();
		DbgModule* dbgModule = wdStackFrame->mSubProgram->mCompileUnit->mDbgModule;
		DbgModule* linkedModule = dbgModule->GetLinkedModule();
		if (!linkedModule->mDisplayName.empty())
			name = linkedModule->mDisplayName + "!" + name;
		_FixFilePath(dbgModule);
		return name;
	}

	DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
	if (dwSubprogram != NULL)
	{
		String demangledName;
		if ((dwSubprogram->mName != NULL) && (strncmp(dwSubprogram->mName, ":Sep@", 5) == 0))
		{
			char* p;
			auto addr = strtoll(dwSubprogram->mName + 5, &p, 16);
			if (addr != 0)
			{
				auto parentSubprogram = mDebugTarget->FindSubProgram(addr);
				if (parentSubprogram != NULL)
					demangledName = parentSubprogram->ToString();
			}
		}
		if (demangledName.IsEmpty())
		{
			dwSubprogram->ToString(demangledName, true);
		}

		DbgSrcFile* dwSrcFile = NULL;
		DbgLineData* dwLineData = NULL;

		FixupLineDataForSubprogram(dwSubprogram);
		addr_target findAddress = wdStackFrame->GetSourcePC();
		DbgSubprogram* specificSubprogram = dwSubprogram;
		dwLineData = dwSubprogram->FindClosestLine(findAddress, &specificSubprogram);

		if ((dwLineData == NULL) && (dwSubprogram->mInlineeInfo != NULL) && (findAddress >= dwSubprogram->mBlock.mHighPC))
			dwLineData = &dwSubprogram->mInlineeInfo->mLastLineData;

		if (dwLineData != NULL)
			dwSrcFile = dwSubprogram->GetLineSrcFile(*dwLineData);

		DbgLineData* dwStartLineData = NULL;
		DbgLineData* dwEndLineData = NULL;
 		if (dwSubprogram->mLineInfo != NULL)
 		{
			if (dwSubprogram->mLineInfo->mLines.size() > 0)
			{
				dwStartLineData = &dwSubprogram->mLineInfo->mLines[0];
				dwEndLineData = &dwSubprogram->mLineInfo->mLines.back();
			}
 		}
		else
		{
			if (dwSubprogram->mInlineeInfo != NULL)
			{
				dwStartLineData = &dwSubprogram->mInlineeInfo->mFirstLineData;
				dwEndLineData = &dwSubprogram->mInlineeInfo->mLastLineData;
			}
		}

		DbgModule* dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
		DbgModule* linkedModule = dbgModule->GetLinkedModule();
		if (!linkedModule->mDisplayName.empty())
			demangledName = linkedModule->mDisplayName + "!" + demangledName;

		if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
			demangledName = "#" + demangledName;

		_SetFlags(dwSubprogram);

		if ((dwLineData != NULL) && (dwSrcFile != NULL))
		{
			*outFile = dwSrcFile->GetLocalPath();
			_CheckHashSrcFile(*outFile, dbgModule, dwSrcFile);

			*outHotIdx = dbgModule->mHotIdx;
			*outLine = dwLineData->mLine;
			*outColumn = dwLineData->mColumn;
			*outLanguage = (int)dwSubprogram->mCompileUnit->mLanguage;

			if (dwEndLineData != NULL)
			{
				*outDefLineStart = dwStartLineData->mLine;
				*outDefLineEnd = dwEndLineData->mLine;
			}

			_FixFilePath(dbgModule);
			return demangledName;
		}
		else
		{
			_FixFilePath(dbgModule);
			return demangledName + StrFormat("+0x%X", pcAddress - dwSubprogram->mBlock.mLowPC);
		}
	}
	else
	{
		String symbolName;
		addr_target offset;
		DbgModule* dbgModule = NULL;
		if (mDebugTarget->FindSymbolAt(pcAddress, &symbolName, &offset, &dbgModule))
		{
			if (dbgModule->HasPendingDebugInfo())
			{
				*outFlags |= FrameFlags_HasPendingDebugInfo;

				if (mPendingDebugInfoLoad.ContainsKey(dbgModule))
				{
					String outName = EncodeDataPtr(pcAddress, true);
					if ((dbgModule != NULL) && (!dbgModule->mDisplayName.empty()))
						outName = dbgModule->mDisplayName + "!<Loading...>" + outName;
					return outName;
				}
			}

			DbgModule* linkedModule = dbgModule->GetLinkedModule();
			String demangledName = BfDemangler::Demangle(symbolName, DbgLanguage_Unknown);
			if (!linkedModule->mDisplayName.empty())
				demangledName = linkedModule->mDisplayName + "!" + demangledName;
			_FixFilePath(dbgModule);
			return demangledName + StrFormat("+0x%X", offset);
		}
	}

	DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress(pcAddress);
	DbgModule* linkedModule = NULL;
	if (dbgModule != NULL)
	{
		linkedModule = dbgModule->GetLinkedModule();
		if (dbgModule->HasPendingDebugInfo())
			*outFlags |= FrameFlags_HasPendingDebugInfo;
	}
	String outName = EncodeDataPtr(pcAddress, true);
	if ((linkedModule != NULL) && (!linkedModule->mDisplayName.empty()))
		outName = linkedModule->mDisplayName + "!" + outName;
	_FixFilePath(dbgModule);
	return outName;
}

String WinDebugger::GetStackFrameId(int stackFrameIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!FixCallStackIdx(stackFrameIdx))
		return "";

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	intptr addr = 0;
	if (wdStackFrame->mSubProgram != NULL)
		addr = wdStackFrame->mSubProgram->mBlock.mLowPC;
	else
		addr = wdStackFrame->mRegisters.GetPC();

	String str = StrFormat("Thread:%d SP:%llX Func:%llX", mActiveThread->mThreadId, wdStackFrame->mRegisters.GetSP(), addr);
	return str;
}

String WinDebugger::Callstack_GetStackFrameOldFileInfo(int stackFrameIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!FixCallStackIdx(stackFrameIdx))
		return "";

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	DbgModule* dbgModule = NULL;
	DbgSrcFile* dbgSrcFile = NULL;

	if (wdStackFrame->mInInlineMethod)
	{
		WdStackFrame* nextStackFrame = mCallStack[actualStackFrameIdx - 1];
		auto subProgram = nextStackFrame->mSubProgram;
		dbgModule = subProgram->mCompileUnit->mDbgModule;

		FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);
		DbgSubprogram* parentSubprogram = subProgram->mInlineeInfo->mInlineParent; // Require it be in the inline parent
		auto foundLine = parentSubprogram->FindClosestLine(subProgram->mBlock.mLowPC, &parentSubprogram);
		if (foundLine != NULL)
			dbgSrcFile = parentSubprogram->GetLineSrcFile(*foundLine);

		DbgSubprogram* callingSubProgram = NULL;
		DbgLineData* callingLineData = FindLineDataAtAddress(nextStackFrame->mSubProgram->mBlock.mLowPC - 1, &callingSubProgram);
		if ((callingLineData != NULL) && (callingSubProgram == wdStackFrame->mSubProgram))
			dbgSrcFile = callingSubProgram->GetLineSrcFile(*callingLineData);
	}
	else
	{
		DbgSubprogram* dwSubprogram = wdStackFrame->mSubProgram;
		if (dwSubprogram != NULL)
		{
			FixupLineDataForSubprogram(dwSubprogram);
			addr_target findAddress = wdStackFrame->GetSourcePC();

			DbgSubprogram* dbgSubprogram = NULL;
			DbgLineData* dwLineData = dwSubprogram->FindClosestLine(findAddress, &dbgSubprogram, &dbgSrcFile);
			dbgModule = dwSubprogram->mCompileUnit->mDbgModule;
		}
	}

	if (dbgSrcFile != NULL)
	{
		// Note: we must use mFilePath here, make sure we don't use GetLocalPath()
		return dbgModule->GetOldSourceCommand(dbgSrcFile->mFilePath);
	}
	return "";
}

int WinDebugger::GetJmpState(int stackFrameIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!FixCallStackIdx(stackFrameIdx))
		return -1;

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();

	CPUInst inst;
	if (!mDebugTarget->DecodeInstruction(pcAddress, &inst))
		return -1;

	return inst.GetJmpState(wdStackFrame->mRegisters.mIntRegs.efl);
}

intptr WinDebugger::GetStackFrameCalleeAddr(int stackFrameIdx)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!FixCallStackIdx(stackFrameIdx))
		return -1;

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	addr_target pcAddress = (addr_target)wdStackFrame->mRegisters.GetPC();
	if (stackFrameIdx == -1)
		pcAddress = mShowPCOverride;

	if (wdStackFrame->mInInlineMethod)
	{
		WdStackFrame* inlineStackFrame = mCallStack[actualStackFrameIdx - 1];
		return inlineStackFrame->mSubProgram->mBlock.mLowPC - 1;
	}

	return pcAddress - 1;
}

String WinDebugger::GetStackMethodOwner(int stackFrameIdx, int& language)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	if (!FixCallStackIdx(stackFrameIdx))
		return "";

	int actualStackFrameIdx = BF_MAX(0, stackFrameIdx);
	if (actualStackFrameIdx >= (int)mCallStack.size())
		actualStackFrameIdx = 0;
	UpdateCallStackMethod(actualStackFrameIdx);
	WdStackFrame* wdStackFrame = mCallStack[actualStackFrameIdx];

	if (wdStackFrame->mSubProgram == NULL)
		return "";
	auto parentType = wdStackFrame->mSubProgram->GetParent();
	if (parentType == NULL)
		return "";
	parentType = parentType->GetPrimaryType();
	language = (int)parentType->GetLanguage();
	return parentType->ToString();
}

String WinDebugger::FindCodeAddresses(const StringImpl& fileName, int line, int column, bool allowAutoResolve)
{
	String result;
	if (mDebugTarget == NULL)
		return "";

	DbgSrcFile* srcFile = mDebugTarget->GetSrcFile(fileName);
	if (srcFile == NULL)
		return result;

	bool foundInSequence = false;
	WdBreakpoint* prevBreakpoint = NULL;

	int bestLineOffset = 0x7FFFFFFF;

	for (auto dbgSubprogram : srcFile->mLineDataRefs)
	{
		for (auto& lineData : dbgSubprogram->mLineInfo->mLines)
		{
			auto lineSrcFile = dbgSubprogram->GetLineSrcFile(lineData);
			if (lineSrcFile != srcFile)
				continue;
			int lineOffset = lineData.mLine - line;

			if ((lineOffset >= 0) && (lineOffset <= 12) && (lineOffset <= bestLineOffset))
			{
				if (lineOffset < bestLineOffset)
				{
					bestLineOffset = lineOffset;
					result = "";
				}

				if (!foundInSequence)
				{
					auto addr = dbgSubprogram->GetLineAddr(lineData);
					result += EncodeDataPtr(addr, false) + "\t" + dbgSubprogram->ToString() + "\n";
				}
			}

			// New sequence?
			if (!lineData.IsStackFrameSetup())
				foundInSequence = false;
		}
	}

	return result;
}

String WinDebugger::GetAddressSourceLocation(intptr address)
{
	DbgSubprogram* subProgram = NULL;
	DbgLineData* lineData = FindLineDataAtAddress(address, &subProgram);
	if (lineData != NULL)
		return StrFormat("%s:%d:%d", subProgram->GetLineSrcFile(*lineData)->GetLocalPath().c_str(), lineData->mLine + 1, lineData->mColumn + 1);

	String outSymbol;
	addr_target offset = 0;
	DbgModule* dbgModule;
	if (mDebugTarget->FindSymbolAt(address, &outSymbol, &offset, &dbgModule))
	{
		if (offset < 0x10000)
		{
			outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
			if (offset > 0)
				outSymbol += StrFormat("+%x", offset);
			return outSymbol;
		}
	}

	return StrFormat("0x%@", address);
}

String WinDebugger::GetAddressSymbolName(intptr address, bool demangle)
{
	auto subProgram = mDebugTarget->FindSubProgram(address);
	if (subProgram != NULL)
		return subProgram->ToString();

	String outSymbol;
	addr_target offset = 0;
	DbgModule* dbgModule;
	if (mDebugTarget->FindSymbolAt(address, &outSymbol, &offset, &dbgModule))
	{
		if (offset < 0x10000)
		{
			if (demangle)
				outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
			if (offset > 0)
				outSymbol += StrFormat("+%x", offset);
			return outSymbol;
		}
	}

	return StrFormat("0x%@", address);
}

String WinDebugger::DisassembleAtRaw(intptr inAddress)
{
	addr_target address = (addr_target)inAddress;

	const int addrBorder = 1024;

	for (int offset = 0; offset < 8; offset++)
	{
		String result;
		bool addOffset = true;
		bool hadAddr = false;

		DbgModule* dbgModule = mDebugTarget->FindDbgModuleForAddress(address);
		DbgModuleMemoryCache* memCache = NULL;
		defer
		(
			if (dbgModule == NULL)
				delete memCache;
		);

		if ((dbgModule != NULL) && (dbgModule->mOrigImageData == NULL))
			dbgModule = NULL;

		result += "R\n"; // Raw

		addr_target addrStart = address;
		if (dbgModule != NULL)
		{
			dbgModule->ParseSymbolData();
			memCache = dbgModule->mOrigImageData;
			addrStart = BF_MAX((addr_target)dbgModule->mImageBase, address - addrBorder - offset);
		}
		else
		{
			memCache = new DbgModuleMemoryCache(addrStart & (4096 - 1), 4096 * 2);
		}

		if (memCache->mAddr == 0)
			return "";

		//addr_target imageBase = dbgModule->mImageBase;
		//int imageSize = dbgModule->mImageSize;

		addr_target dataAddr = addrStart;
		addr_target addrEnd = addrStart + addrBorder * 2 + 16;

		while (dataAddr < addrEnd)
		{
			if (dataAddr == address)
				hadAddr = true;
			if (dataAddr > address)
			{
				if (!hadAddr)
				{
					if (offset == 7)
					{
						dataAddr = address;
					}
					break;
				}
			}

			String outSymbol;
			addr_target symOffset = 0;
			DbgModule* symDWARF;

			if (mDebugTarget->FindSymbolAt(dataAddr, &outSymbol, &symOffset, &symDWARF))
			{
				if (symOffset == 0)
				{
					outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);
					if ((symDWARF != NULL) && (!symDWARF->mDisplayName.empty()))
						outSymbol = symDWARF->GetLinkedModule()->mDisplayName + "!" + outSymbol;
					result += "T " + outSymbol + ":\n";
				}
			}

			CPUInst inst;
			if (!mCPU->Decode(dataAddr, memCache, &inst))
			{
				if ((offset == 7) && (!hadAddr))
				{
					uint8 instData[1];
					memCache->Read(dataAddr, instData, 1);
					int instLen = 1;

#ifdef BF_DBG_32
					result += StrFormat("D %08X:        ", dataAddr);
#else
					result += StrFormat("D %@:        ", dataAddr);
#endif
					for (int i = 0; i < instLen; i++)
						result += StrFormat("%02X ", instData[i]);
					for (int i = instLen; i < 8; i++)
						result += "   ";
					result += "\n";
					dataAddr++;

					continue;
				}
				break;
			}

			int instLen = inst.GetLength();

#ifdef BF_DBG_32
			result += StrFormat("D %08X:        ", dataAddr);
#else
			result += StrFormat("D %@:        ", dataAddr);
#endif

			uint8 instData[32];
			int showInstLen = BF_MIN(32, instLen);
			memCache->Read(dataAddr, instData, showInstLen);

			for (int i = 0; i < showInstLen; i++)
				result += StrFormat("%02X ", instData[i]);
			for (int i = instLen; i < 8; i++)
				result += "   ";

			result += mCPU->InstructionToString(&inst, dataAddr);

			if ((inst.IsCall()) || (inst.IsBranch()))
			{
				addr_target targetAddr = inst.GetTarget();
				if (targetAddr != 0)
				{
					if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &symOffset))
					{
						if (symOffset < 0x10000)
						{
							outSymbol = BfDemangler::Demangle(outSymbol, DbgLanguage_Unknown);

							result += " ; " + outSymbol;
							if (symOffset > 0)
								result += StrFormat("+%x", symOffset);
							//result += ">";
						}
					}
				}
			}

			result += "\n";
			dataAddr += instLen;
		}

		if (!hadAddr)
			continue;

		return result;
	}

	return "";
}

String WinDebugger::DisassembleAt(intptr inAddress)
{
	BP_ZONE("WinDebugger::DisassembleAt");

	AutoCrit autoCrit(mDebugManager->mCritSect);

	addr_target address = (addr_target)inAddress;

	if (mDebugTarget == NULL)
		return "";

	String result;
	auto dwSubProgram = mDebugTarget->FindSubProgram(address);
	if (dwSubProgram == NULL)
		return DisassembleAtRaw(address);
	dwSubProgram = dwSubProgram->GetRootInlineParent();
	DbgModule* dwarf = dwSubProgram->mCompileUnit->mDbgModule;

	int frameBaseRegister = mDebugTarget->GetFrameBaseRegister(dwSubProgram);

	addr_target addrStart = dwSubProgram->mBlock.mLowPC;
	addr_target addrEnd = dwSubProgram->mBlock.mHighPC;

	auto dwCompileUnit = dwSubProgram->mCompileUnit;
	{
		FixupLineData(dwCompileUnit);
	}

	DbgSrcFile* dwSrcFile = NULL;

	FixupLineDataForSubprogram(dwSubProgram);

	DbgLineData* dwLineData = NULL;
	if (dwSubProgram->mLineInfo != NULL)
    	dwLineData = &dwSubProgram->mLineInfo->mLines[0];
	int nextLineDataIdx = 1;

	if (dwSubProgram->mIsOptimized)
		result += "O\n";

	DbgSrcFile* srcFile = NULL;
	int firstLine = 0;
	int curLine = 0;
	if (dwLineData != NULL)
	{
		srcFile = dwSubProgram->GetLineSrcFile(*dwLineData);
		result += "S " + srcFile->GetLocalPath() + "\n";
		if (srcFile->mHashKind != DbgHashKind_None)
		{
			result += "H ";
			srcFile->GetHash(result);
			result += "\n";
		}

		curLine = BF_MAX(0, dwLineData->mLine - 5);
		//for (; curLine <= dwLineData->mLine; curLine++)
		result += StrFormat("L %d %d\n", curLine, dwLineData->mLine - curLine + 1);
		curLine = dwLineData->mLine + 1;
		firstLine = dwLineData->mLine;
	}

	Array<DbgSubprogram*> inlineStack;

	Array<DbgBlock*> blockList;
	blockList.push_back(&dwSubProgram->mBlock);

	addr_target dataAddr = addrStart;
	int decodeFailureCount = 0;

	auto& _PopInlineStack = [&]()
	{
		int depth = inlineStack.size();
		auto curStackEntry = inlineStack.back();
		if (depth > 1)
			result += StrFormat("T <<<%d Inline End ", depth);
		else
			result += "T <<< Inline End ";
		result += curStackEntry->ToString();
		result += "\n";
		inlineStack.pop_back();
	};

	std::function<void(DbgSubprogram* subprogram, int depth)> _UpdateInlineStackHelper = [&](DbgSubprogram* subprogram, int depth)
	{
		int stackIdx = depth - 1;
		if (stackIdx < inlineStack.size())
		{
			auto curStackEntry = inlineStack[stackIdx];
			if (curStackEntry != subprogram)
				_PopInlineStack();
		}

		if (depth > 1)
		{
			_UpdateInlineStackHelper(subprogram->mInlineeInfo->mInlineParent, depth - 1);
		}

		if (stackIdx >= inlineStack.size())
		{
			if (depth > 1)
				result += StrFormat("T >>>%d Inline ", depth);
			else
				result += "T >>> Inline ";
			result += subprogram->ToString();
			result += "\n";
			inlineStack.push_back(subprogram);
		}
	};

	auto _UpdateInlineStack = [&](DbgSubprogram* subprogram)
	{
		if (subprogram == NULL)
		{
			while (!inlineStack.IsEmpty())
				_PopInlineStack();
			return;
		}

		int inlineDepth = subprogram->GetInlineDepth();
		while (inlineDepth < inlineStack.size())
			_PopInlineStack();
		if (inlineDepth > 0)
			_UpdateInlineStackHelper(subprogram, inlineDepth);
	};

	while (dataAddr < addrEnd)
	{
		// Pop off old scopes
		while (blockList.size() > 0)
		{
			auto lastBlock = blockList.back();
			if (dataAddr < lastBlock->mHighPC)
				break;
			blockList.pop_back();
		}

		// Check entry into new child scopes
		auto lastBlock = blockList.back();
		for (auto checkBlock : lastBlock->mSubBlocks)
		{
			if ((dataAddr >= checkBlock->mLowPC) && (dataAddr < checkBlock->mHighPC))
			{
				blockList.push_back(checkBlock);
				break;
			}
		}

		bool allowSourceJump = false;

		if ((dwLineData != NULL) && (dwLineData->mContribSize != 0) && (dataAddr >= dwSubProgram->GetLineAddr(*dwLineData) + dwLineData->mContribSize))
		{
			DbgSubprogram* inlinedSubprogram = NULL;
			auto inlinedLine = dwSubProgram->FindClosestLine(dataAddr, &inlinedSubprogram);

			_UpdateInlineStack(dwSubProgram);
		}

		// Update line data
		while ((dwLineData != NULL) && (dwSubProgram->GetLineAddr(*dwLineData) <= dataAddr))
		{
			_UpdateInlineStack(dwSubProgram->GetLineInlinee(*dwLineData));

			const int lineLimit = 5; // 15

			if (allowSourceJump)
				curLine = dwLineData->mLine;

			auto lineSrcFile = dwSubProgram->GetLineSrcFile(*dwLineData);

			if (lineSrcFile != srcFile)
			{
				srcFile = lineSrcFile;
				result += "S ";
				result += srcFile->GetLocalPath();
				result += "\n";
				// Just show the one line from the new file
				curLine = dwLineData->mLine;
			}

			if (dwLineData->mLine < curLine - 1)
			{
				// Jumping backwards - possibly into inlined method, or possibly in current method.
				//  Show previous 6 lines, for context
				curLine = BF_MAX(0, dwLineData->mLine - lineLimit);
			}

			if ((curLine <= firstLine) && (dwLineData->mLine >= firstLine))
			{
				// Jumping from inlined method (declared above) back into main method
				curLine = dwLineData->mLine;
			}

			if (curLine < dwLineData->mLine - lineLimit)
			{
				// Don't show huge span of source - only show the last 6 lines at maximum
				curLine = dwLineData->mLine - lineLimit;
			}

			//for ( ; curLine <= dwLineData->mLine; curLine++)
			result += StrFormat("L %d %d\n", curLine, dwLineData->mLine - curLine + 1);
			curLine = dwLineData->mLine + 1;

			DbgLineData* nextLineData = NULL;
			while (nextLineDataIdx < dwSubProgram->mLineInfo->mLines.mSize)
			{
				nextLineData = &dwSubProgram->mLineInfo->mLines[nextLineDataIdx];

				//TODO:
				/*{
					result += StrFormat("T LineIdx: %d (%@ to %@)", nextLineDataIdx, dwSubProgram->GetLineAddr(*nextLineData), dwSubProgram->GetLineAddr(*nextLineData) + nextLineData->mContribSize);
					auto inlinee = dwSubProgram->GetLineInlinee(*nextLineData);
					if (inlinee != NULL)
					{
						result += StrFormat(" Inlinee: %s Depth: %d", inlinee->mName, inlinee->GetInlineDepth());
					}
					result += "\n";
				}*/

				auto nextLineAddr = dwSubProgram->GetLineAddr(*nextLineData);
				if (nextLineAddr > dataAddr)
				{
					if (nextLineDataIdx + 1 < dwSubProgram->mLineInfo->mLines.mSize)
					{
						auto peekLineData = &dwSubProgram->mLineInfo->mLines[nextLineDataIdx + 1];
						if (peekLineData->mRelAddress == nextLineData->mRelAddress)
						{
							// Use the later entry
							++nextLineDataIdx;
							continue;
						}
					}

					break;
				}
				// If we go back to an older entry beacuse of a gap then we need to catch back up...
				++nextLineDataIdx;
				nextLineData = NULL; // Keep searching...
			}

			dwLineData = nextLineData;
			nextLineDataIdx++;
		}

		// Have we gone off the end of the inline function?
		//  We may not have an explicit non-inlined line data at the transition point...
		while (!inlineStack.IsEmpty())
		{
			auto subProgram = inlineStack.back();
			if (dataAddr < subProgram->mBlock.mHighPC)
				break;
			_PopInlineStack();
		}

		bool hadDecodeFailure = false;
		CPUInst inst;
		if (!mCPU->Decode(dataAddr, dwarf->mOrigImageData, &inst))
			hadDecodeFailure = true;

		if ((decodeFailureCount == 8) || ((decodeFailureCount > 0) && (!hadDecodeFailure)))
		{
			for (int i = decodeFailureCount; i < 4 + sizeof(addr_target); i++)
				result += "   ";
			result += " ???\n";
			decodeFailureCount = 0;
		}

		if (decodeFailureCount == 0)
		{
#ifdef BF_DBG_32
			result += StrFormat("D %08X:  ", dataAddr);
#else
			result += StrFormat("D %@:  ", dataAddr);
#endif
		}
		if (hadDecodeFailure)
		{
			uint8 byte = 0;
			dwarf->mOrigImageData->Read(dataAddr, &byte, 1);
			result += StrFormat("%02X ", byte);
			dataAddr++;
			decodeFailureCount++;
			continue;
		}

		int instLen = inst.GetLength();
		uint8 instData[32];
		int showInstLen = BF_MIN(32, instLen);
		dwarf->mOrigImageData->Read(dataAddr, instData, showInstLen);

		for (int i = 0; i < showInstLen; i++)
			result += StrFormat("%02X ", instData[i]);

		for (int i = instLen; i < 4 + sizeof(addr_target); i++)
			result += "   ";

		result += " ";

		result += mCPU->InstructionToString(&inst, dataAddr);

		int reg;
		int offset;
		if (inst.GetIndexRegisterAndOffset(&reg, &offset))
		{
			for (int blockIdx = (int)blockList.size() - 1; blockIdx >= 0; blockIdx--)
			{
				auto dwBlock = blockList[blockIdx];
				for (auto variable : dwBlock->mVariables)
				{
					int varRegister;
					int varOffset;
					if (mDebugTarget->GetVariableIndexRegisterAndOffset(variable, &varRegister, &varOffset))
					{
						if (varRegister == -1)
							varRegister = frameBaseRegister;
						if ((reg == varRegister) && (offset == varOffset))
						{
							result += " ; ";
							result += variable->mName;
							break;
						}
					}
				}
			}
		}
		else if ((inst.IsCall()) || (inst.IsBranch()) || (inst.IsLoadAddress()))
		{
			addr_target targetAddr = inst.GetTarget();
			if (targetAddr != 0)
			{
				if ((targetAddr >= addrStart) && (targetAddr < addrEnd))
				{
					result += StrFormat("\nJ %s", EncodeDataPtr(targetAddr, false).c_str());
				}
				else
				{
					String outSymbol;
					addr_target offset = 0;
					if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &offset))
					{
						if (offset < 0x10000)
						{
							outSymbol = BfDemangler::Demangle(outSymbol, dwSubProgram->GetLanguage());

							result += " ; " + outSymbol;
							if (offset > 0)
								result += StrFormat("+%x", offset);
						}
					}
				}
			}
		}

		result += "\n";
		dataAddr += instLen;
	}

	// Why did we want to "show lines at end"??
	// Show lines at end
	/*if (curLine > 0)
	{
		for (int i = 0; i < 6; i++, curLine++)
			result += StrFormat("L %d\n", curLine);
	}*/
	return result;
}

String WinDebugger::FindLineCallAddresses(intptr inAddress)
{
	String callAddresses;

	addr_target address = (addr_target)inAddress;

	DbgSubprogram* dwSubprogram = NULL;

	DbgLineData* startLineData = FindLineDataAtAddress(address, &dwSubprogram, NULL);

	if (dwSubprogram == NULL)
		return "";

	CPURegisters registers;
	PopulateRegisters(&registers);

	auto inlinerSubprogram = dwSubprogram->GetRootInlineParent();
	FixupLineDataForSubprogram(inlinerSubprogram);

	if (inlinerSubprogram->mLineInfo->mLines.mSize == 0)
		return "";
	auto lineData = &inlinerSubprogram->mLineInfo->mLines[0];

	addr_target addr = dwSubprogram->mBlock.mLowPC;
	addr_target endAddr = dwSubprogram->mBlock.mHighPC;

	DbgSubprogram* checkSubprogram = dwSubprogram;
	DbgLineData* checkLineData = lineData;
	addr_target checkLineAddr = 0;

	int lineIdx = 0;
	while (checkLineData != NULL)
	{
		//auto nextLineData = dwSubprogram->mCompileUnit->mLineDataMap.GetNext(checkLineData);
		++lineIdx;

		DbgLineData* nextLineData = NULL;
		addr_target nextLineAddr;
		if (lineIdx < inlinerSubprogram->mLineInfo->mLines.size())
		{
			nextLineData = &inlinerSubprogram->mLineInfo->mLines[lineIdx];
			nextLineAddr = dwSubprogram->GetLineAddr(*nextLineData);
		}
		else
			nextLineAddr = inlinerSubprogram->mBlock.mHighPC;

		// This stuff doesn't make sense...
		DbgSubprogram* nextSubProgram;
		if (nextLineData != NULL)
		{
			if (nextLineAddr > dwSubprogram->mBlock.mHighPC)
				break;

			endAddr = nextLineAddr;
			nextSubProgram = mDebugTarget->FindSubProgram(endAddr);
			if (nextSubProgram != NULL)
			{
				auto dbgModule = nextSubProgram->mCompileUnit->mDbgModule;
				dbgModule->ParseSymbolData();
			}
		}
		else
		{
			nextSubProgram = dwSubprogram;
			endAddr = dwSubprogram->mBlock.mHighPC;
		}

		auto _HandleSection = [&]()
		{
			while (addr < endAddr)
			{
				CPUInst inst;
				if (!mDebugTarget->DecodeInstruction(addr, &inst))
					break;

				*registers.GetPCRegisterRef() = addr;
				if (inst.IsCall())
				{
					bool addSymbol = true;

					if (addr < (addr_target)inAddress)
						callAddresses += "-";
					callAddresses += EncodeDataPtr(addr, false);

					addr_target targetAddr = inst.GetTarget(this, &registers);
					if (targetAddr != 0)
					{
						String outSymbol;
						auto subprogram = mDebugTarget->FindSubProgram(targetAddr);
						if (subprogram != NULL)
						{
							CreateFilterName(outSymbol, subprogram);
							addSymbol = true;
						}
						else
						{
							addr_target offset = 0;
							String fullSymbolName;
							if (mDebugTarget->FindSymbolAt(targetAddr, &outSymbol, &offset))
							{
								if (offset < 0x200)
								{
									//outSymbol = BfDemangler::Demangle(outSymbol, dwSubprogram->GetLanguage());
									if (outSymbol == "___chkstk_ms")
										addSymbol = false;
									else
									{
										String demangledName = BfDemangler::Demangle(outSymbol, DbgLanguage_C);
										outSymbol.clear();
										CreateFilterName(outSymbol, demangledName.c_str(), DbgLanguage_C);
									}
								}
								else
									outSymbol.clear();
							}
						}

						if (addSymbol)
						{
							if (outSymbol.empty())
								callAddresses += "\tFunc@" + EncodeDataPtr(targetAddr, false);
							else
								callAddresses += "\t" + outSymbol;

							String attrs;
							bool isFiltered = false;
							if (subprogram != NULL)
							{
								subprogram->PopulateSubprogram();
								isFiltered = subprogram->mIsStepFilteredDefault;
								if (isFiltered)
									attrs += "d"; // 'd' for default filtered
							}

							StepFilter* stepFilterPtr = NULL;
							if (mDebugManager->mStepFilters.TryGetValue(outSymbol, &stepFilterPtr))
								isFiltered = stepFilterPtr->IsFiltered(isFiltered);

							if (isFiltered)
								attrs += "f";  // 'f' for filter

							if (!attrs.IsEmpty())
								callAddresses += "\t" + attrs;
						}
					}

					if (addSymbol)
						callAddresses += "\n";
				}

				inst.PartialSimulate(this, &registers);
				addr += inst.GetLength();
			}
		};

		// For inlining - only add calls that are found either directly in our main block (not an inlined block)
		//  But add inlined methods when their parent is our current block
		if ((checkSubprogram == dwSubprogram) && (checkLineData->mLine == startLineData->mLine))
		{
			_HandleSection();
		}
		else if ((checkSubprogram->mInlineeInfo != NULL) && (checkSubprogram->mInlineeInfo->mInlineParent == dwSubprogram))
		{
			if (checkLineAddr == checkSubprogram->mBlock.mLowPC)
			{
				addr_target inlineStartAddr = checkSubprogram->mBlock.mLowPC;

				// Find the calling line
				DbgSubprogram* callingSubprogram = dwSubprogram;
				auto checkLineData = dwSubprogram->FindClosestLine(inlineStartAddr, &callingSubprogram);
				if ((checkLineData != NULL) && (checkLineData->mCtxIdx == startLineData->mCtxIdx) && (checkLineData->mLine == startLineData->mLine))
				{
					if (inlineStartAddr <= (addr_target)inAddress)
						callAddresses += "-";
					callAddresses += EncodeDataPtr(inlineStartAddr, false);
					String outSymbol;
					CreateFilterName(outSymbol, checkSubprogram);
					callAddresses += "\t" + outSymbol;

					bool isFiltered = dwSubprogram->mIsStepFilteredDefault;
					StepFilter* stepFilterPtr;
					if (mDebugManager->mStepFilters.TryGetValue(outSymbol, &stepFilterPtr))
						isFiltered = stepFilterPtr->IsFiltered(isFiltered);

					if (isFiltered)
						callAddresses += "\tf"; // 'f' for filter

					callAddresses += "\n";
				}

// 				if (checkSubprogram->mBlock.mHighPC < endAddr)
// 				{
// 					addr = checkSubprogram->mBlock.mHighPC;
// 					_HandleSection();
// 				}
			}

			// If we have unattributed data after the end of an inlined method, add that
			if ((endAddr > checkSubprogram->mBlock.mHighPC) && (nextSubProgram == dwSubprogram))
			{
				addr = checkSubprogram->mBlock.mHighPC;
				_HandleSection();
			}
		}

		checkLineData = nextLineData;
		checkSubprogram = nextSubProgram;
		checkLineAddr = nextLineAddr;
		addr = endAddr;
	}

	return callAddresses;
}

String WinDebugger::GetCurrentException()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	String result = StrFormat("%s\n%08X",
		EncodeDataPtr((addr_target)(intptr)mCurException.ExceptionAddress, true).c_str(),
		mCurException.ExceptionCode);

	String exStr;
	switch (mCurException.ExceptionCode)
	{
	case EXCEPTION_ACCESS_VIOLATION:
	{
		String accessType;
		if (mCurException.ExceptionInformation[0] == 0)
			accessType = "reading from";
		else if (mCurException.ExceptionInformation[0] == 8)
			accessType = "executing";
		else
			accessType = "writing to";
		exStr = StrFormat("EXCEPTION_ACCESS_VIOLATION %s %s", accessType.c_str(), EncodeDataPtr((addr_target)mCurException.ExceptionInformation[1], true).c_str());
	}
	break;
	case EXCEPTION_DATATYPE_MISALIGNMENT:
		exStr = "EXCEPTION_DATATYPE_MISALIGNMENT";
	case EXCEPTION_SINGLE_STEP:
		exStr = "EXCEPTION_SINGLE_STEP";
		break;
	case EXCEPTION_BREAKPOINT:
		exStr = "EXCEPTION_BREAKPOINT";
		break;
	case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
		exStr = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
		break;
	case EXCEPTION_FLT_DENORMAL_OPERAND:
		exStr = "EXCEPTION_FLT_DENORMAL_OPERAND";
		break;
	case EXCEPTION_FLT_DIVIDE_BY_ZERO:
		exStr = "EXCEPTION_FLT_DIVIDE_BY_ZERO";
		break;
	case EXCEPTION_FLT_INEXACT_RESULT:
		exStr = "EXCEPTION_FLT_INEXACT_RESULT";
		break;
	case EXCEPTION_FLT_INVALID_OPERATION:
		exStr = "EXCEPTION_FLT_INVALID_OPERATIO";
		break;
	case EXCEPTION_FLT_OVERFLOW:
		exStr = "EXCEPTION_FLT_OVERFLOW";
		break;
	case EXCEPTION_FLT_STACK_CHECK:
		exStr = "EXCEPTION_FLT_STACK_CHECK";
		break;
	case EXCEPTION_FLT_UNDERFLOW:
		exStr = "EXCEPTION_FLT_UNDERFLOW";
		break;
	case EXCEPTION_INT_DIVIDE_BY_ZERO:
		exStr = "EXCEPTION_INT_DIVIDE_BY_ZERO";
		break;
	case EXCEPTION_INT_OVERFLOW:
		exStr = "EXCEPTION_INT_OVERFLOW";
		break;
	case EXCEPTION_PRIV_INSTRUCTION:
		exStr = "EXCEPTION_PRIV_INSTRUCTION";
		break;
	case EXCEPTION_IN_PAGE_ERROR:
		exStr = "EXCEPTION_IN_PAGE_ERROR";
		break;
	case EXCEPTION_ILLEGAL_INSTRUCTION:
		exStr = "EXCEPTION_ILLEGAL_INSTRUCTION";
		break;
	case EXCEPTION_NONCONTINUABLE_EXCEPTION:
		exStr = "EXCEPTION_NONCONTINUABLE_EXCEPTION";
		break;
	case EXCEPTION_STACK_OVERFLOW:
		exStr = "EXCEPTION_STACK_OVERFLOW";
		break;
	case EXCEPTION_INVALID_DISPOSITION:
		exStr = "EXCEPTION_INVALID_DISPOSITION";
		break;
	case EXCEPTION_GUARD_PAGE:
		exStr = "EXCEPTION_GUARD_PAGE";
		break;
	case EXCEPTION_INVALID_HANDLE:
		exStr = "EXCEPTION_INVALID_HANDLE";
		break;
	case CONTROL_C_EXIT:
		exStr = "CONTROL_C_EXIT";
		break;
	default:
		exStr += StrFormat("EXCEPTION %08X", mCurException.ExceptionCode);
	}
	if (mActiveThread != NULL)
		exStr += StrFormat(" in thread %d", mActiveThread->mThreadId);

	if (!exStr.empty())
		result += "\n" + exStr;

	// After we retrieve the exception then we can go back to just being normal 'paused'
	//  This allows us to evaluate stuff, Set Next Statement, etc.
	mRunState = RunState_Paused;

	return result.c_str();
}

void WinDebugger::SetAliasPath(const StringImpl& origPath, const StringImpl& localPath)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	String fixedOrigPath = FixPathAndCase(origPath);
	String fixedLocalPath = FixPathAndCase(localPath);

	auto origFile = mDebugTarget->AddSrcFile(origPath);
	origFile->mLocalPath = FixPath(localPath);

	mDebugTarget->mLocalToOrigSrcMap[fixedLocalPath] = fixedOrigPath;
	// We invalidate the step filters, because previously-failing 'CheckSourceFileExist' checks may now succeed
	mDebugManager->mStepFilterVersion++;
}

String WinDebugger::GetModulesInfo()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	String str;

	for (auto module : mDebugTarget->mDbgModules)
	{
		COFF* coff = (COFF*)module;
		if (module->mHotIdx > 0)
			continue;

		str += module->mDisplayName;
		str += "\t";
		if (module->mLoadState == DbgModuleLoadState_Loaded)
		{
			str += module->mFilePath;
		}
		else if (module->mLoadState == DbgModuleLoadState_NotLoaded)
		{
			str += module->mFilePath;
			str += " (Loading...)";
		}
		else if (module->mLoadState == DbgModuleLoadState_Failed)
		{
			str += "!";
			str += module->mFilePath;
		}

		if (module->mMappedImageFile != NULL)
		{
			str += " (";
			str += module->mMappedImageFile->mFileName;
			str += ")";
		}

		str += "\t";
		str += coff->mPDBPath;
		str += "\t";
		str += module->mVersion;
		str += StrFormat("\t%@-%@\t%dk\t", module->mImageBase, module->mImageBase + module->mImageSize, module->mImageSize / 1024);

		time_t timestamp = coff->mTimeStamp;
		if (timestamp == 0)
			timestamp = GetFileTimeWrite(coff->mFilePath);
		if (timestamp != 0)
		{
			char timeString[256];
			auto time_info = localtime(&timestamp);
			strftime(timeString, sizeof(timeString), "%D %T", time_info);
			str += timeString;
		}
		str += "\n";
	}

	return str;
}

String WinDebugger::GetModuleInfo(const StringImpl& modulePath)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	String result;

	for (auto dbgModule : mDebugTarget->mDbgModules)
	{
		if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
		{
			dbgModule->ParseGlobalsData();
			dbgModule->PopulateStaticVariableMap();

			auto coff = (COFF*)dbgModule;
			coff->ParseCompileUnits();

			int fileSize = 0;
			//
			{
				FileStream fs;
				fs.Open(coff->mFilePath, "rb");
				fileSize = fs.GetSize();
			}

			result += StrFormat("Path: %s FileSize:%0.2fk MemoryImage:%0.2fk\n", coff->mFilePath.c_str(), fileSize / 1024.0f, coff->mImageSize / 1024.0f);

			result += "Sections:\n";
			for (auto& section : coff->mSections)
			{
				result += StrFormat("\t%s\t%0.2fk\n", section.mName.c_str(), (section.mAddrLength) / 1024.0f);
			}
			result += "\n";

			result += "Compile Units:\n";
			for (auto compileUnit : dbgModule->mCompileUnits)
			{
				coff->MapCompileUnitMethods(compileUnit);
				result += StrFormat("\t%s PCRange:%0.2fk\n", compileUnit->mName.c_str(), (compileUnit->mHighPC - compileUnit->mLowPC) / 1024.0f);
			}
			result += "\n";

			Array<CvModuleInfo*> moduleInfos;
			for (auto moduleInfo : coff->mCvModuleInfo)
			{
				if (moduleInfo->mSectionContrib.mSize > 0)
					moduleInfos.Add(moduleInfo);
			}
			moduleInfos.Sort([](CvModuleInfo* lhs, CvModuleInfo* rhs)
				{
					return lhs->mSectionContrib.mSize > rhs->mSectionContrib.mSize;
				});

			int totalContrib = 0;
			result += "CV Module Info:\n";
			for (auto moduleInfo : moduleInfos)
			{
				auto section = coff->mSections[moduleInfo->mSectionContrib.mSection - 1];

				result += StrFormat("\t%s\t%s\t%0.2fk\t%@-%@\n", moduleInfo->mModuleName, section.mName.c_str(), (moduleInfo->mSectionContrib.mSize) / 1024.0f,
					coff->GetSectionAddr(moduleInfo->mSectionContrib.mSection, moduleInfo->mSectionContrib.mOffset),
					coff->GetSectionAddr(moduleInfo->mSectionContrib.mSection, moduleInfo->mSectionContrib.mOffset + moduleInfo->mSectionContrib.mSize));
				totalContrib += moduleInfo->mSectionContrib.mSize;
			}
			result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
			result += "\n";

			addr_target minAddr = 0;
			Array<DbgCompileUnitContrib*> contribs;
			for (auto itr = mDebugTarget->mContribMap.begin(); itr != mDebugTarget->mContribMap.end(); ++itr)
			{
				auto contrib = *itr;
				if (contrib->mDbgModule != coff)
					continue;

				if (contrib->mAddress < minAddr)
					continue;

				minAddr = contrib->mAddress + contrib->mLength;

				auto section = &coff->mSectionHeaders[contrib->mSection - 1];
				if (section->mSizeOfRawData <= 0)
					continue;

				contribs.Add(contrib);
			}
			contribs.Sort([](DbgCompileUnitContrib* lhs, DbgCompileUnitContrib* rhs)
				{
					return lhs->mLength > rhs->mLength;
				});

			totalContrib = 0;
			result += "Contribs:\n";
			for (auto contrib : contribs)
			{
				auto cvModule = coff->mCvModuleInfo[contrib->mCompileUnitId];
				auto section = &coff->mSectionHeaders[contrib->mSection - 1];
				result += StrFormat("\t%s\t%s\t%0.2fk\t%@\n", cvModule->mModuleName, section->mName, (contrib->mLength)/1024.0f, contrib->mAddress);
				totalContrib += contrib->mLength;
			}
			result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
			result += "\n";

			struct SymbolEntry
			{
				const char* mName;
				addr_target mAddress;
				int mSize;
			};
			Array<SymbolEntry> symbolEntries;

			for (auto symbol : mDebugTarget->mSymbolMap)
			{
				if (symbol->mDbgModule != coff)
					continue;

				if (!symbolEntries.IsEmpty())
				{
					auto lastSymbol = &symbolEntries.back();
					if (lastSymbol->mSize == 0)
						lastSymbol->mSize = symbol->mAddress - lastSymbol->mAddress;
				}

				SymbolEntry symbolEntry;
				symbolEntry.mName = symbol->mName;
				symbolEntry.mAddress = symbol->mAddress;
				symbolEntry.mSize = 0;
				symbolEntries.Add(symbolEntry);
			}
			if (!symbolEntries.IsEmpty())
			{
				auto lastSymbol = &symbolEntries.back();
				for (auto contrib : contribs)
				{
					if ((lastSymbol->mAddress >= contrib->mAddress) && (lastSymbol->mAddress < contrib->mAddress + contrib->mLength))
					{
						lastSymbol->mSize = (contrib->mAddress + contrib->mLength) - lastSymbol->mAddress;
						break;
					}
				}
			}
			symbolEntries.Sort([](const SymbolEntry& lhs, const SymbolEntry& rhs)
				{
					return lhs.mSize > rhs.mSize;
				});

			totalContrib = 0;
			result += "Symbols:\n";
			for (auto symbolEntry : symbolEntries)
			{
				result += StrFormat("\t%s\t%0.2fk\t%@\n", symbolEntry.mName, (symbolEntry.mSize) / 1024.0f, symbolEntry.mAddress);
				totalContrib += symbolEntry.mSize;
			}
			result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
			result += "\n";

			//////////////////////////////////////////////////////////////////////////

			totalContrib = 0;
			result += "Static Variables:\n";
			for (auto& variable : coff->mStaticVariables)
			{
				result += StrFormat("\t%s\t%0.2fk\n", variable->mName, (variable->mType->GetByteCount()) / 1024.0f);
				totalContrib += variable->mType->GetByteCount();
			}
			result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
			result += "\n";

			totalContrib = 0;
			result += "Methods:\n";
			Array<DbgSubprogram*> methods;
			for (int typeIdx = 0; typeIdx < coff->mTypes.mSize; typeIdx++)
			{
				auto type = coff->mTypes[typeIdx];
				type->PopulateType();
				for (auto method : type->mMethodList)
					methods.Add(method);
			}
			for (auto compileUnit : dbgModule->mCompileUnits)
			{
				for (auto method : compileUnit->mOrphanMethods)
					methods.Add(method);
			}
			methods.Sort([](DbgSubprogram* lhs, DbgSubprogram* rhs)
				{
					return lhs->GetByteCount() > rhs->GetByteCount();
				});
			for (auto method : methods)
			{
				int methodSize = method->GetByteCount();
				if (methodSize <= 0)
					continue;
				auto name = method->ToString();
				result += StrFormat("\t%s\t%0.2fk\n", name.c_str(), methodSize / 1024.0f);
				totalContrib += methodSize;
			}

			result += StrFormat("\tTOTAL: %0.2fk\n", (totalContrib) / 1024.0f);
			result += "\n";
		}
	}

	return result;
}

void WinDebugger::CancelSymSrv()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);
	if (mActiveSymSrvRequest != NULL)
		mActiveSymSrvRequest->Cancel();
}

bool WinDebugger::HasPendingDebugLoads()
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	return (!mPendingImageLoad.IsEmpty()) || (!mPendingDebugInfoLoad.IsEmpty());
}

int WinDebugger::LoadImageForModule(const StringImpl &modulePath, const StringImpl& imagePath)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	for (auto dbgModule : mDebugTarget->mDbgModules)
	{
		if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
		{
			auto coff = (COFF*)dbgModule;

			if (!coff->LoadModuleImage(imagePath))
			{
				mDebugManager->mOutMessages.push_back("error Failed to load image " + imagePath);
			}
			ModuleChanged(dbgModule);

			return 0;
		}
	}

	return 0;
}

int WinDebugger::LoadDebugInfoForModule(DbgModule* dbgModule)
{
	if (!dbgModule->HasPendingDebugInfo())
		return 0;

	if (dbgModule->RequestDebugInfo())
	{
		ClearCallStack(); // Make this re-resolve with debug info
		return 1;
	}

	DbgPendingDebugInfoLoad* dbgPendingDebugInfoLoad = NULL;
	if (mPendingDebugInfoLoad.TryAdd(dbgModule, NULL, &dbgPendingDebugInfoLoad))
	{
		dbgPendingDebugInfoLoad->mModule = dbgModule;
		dbgPendingDebugInfoLoad->mAllowRemote = true;
		return 2;
	}
	dbgPendingDebugInfoLoad->mAllowRemote = true;

	return 0;
}

int WinDebugger::LoadDebugInfoForModule(const StringImpl& moduleName)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	for (auto dbgModule : mDebugTarget->mDbgModules)
	{
		String checkModuleName = GetFileName(dbgModule->mFilePath);
		if (moduleName.Equals(checkModuleName, StringImpl::CompareKind_OrdinalIgnoreCase))
		{
			return LoadDebugInfoForModule(dbgModule);
		}
	}

	return 0;
}

int WinDebugger::LoadDebugInfoForModule(const StringImpl& modulePath, const StringImpl& debugFileName)
{
	AutoCrit autoCrit(mDebugManager->mCritSect);

	for (auto dbgModule : mDebugTarget->mDbgModules)
	{
		if (modulePath.Equals(dbgModule->mFilePath, StringImpl::CompareKind_OrdinalIgnoreCase))
		{
			auto coff = (COFF*)dbgModule;

			String err;
			if (!coff->mPDBLoaded)
			{
				dbgModule->mFailMsgPtr = &err;
				if (coff->TryLoadPDB(debugFileName, coff->mWantPDBGuid, coff->mWantAge))
				{
					ModuleChanged(dbgModule);
				}
				dbgModule->mFailMsgPtr = NULL;
			}
			else
			{
				err = StrFormat("Module '%s' already has debug information loaded", GetFileName(modulePath).c_str());
			}

			if (!err.IsEmpty())
			{
				mDebugManager->mOutMessages.push_back("error " + err);
			}

			return 0;
		}
	}

	return 0;
}

void WinDebugger::FixupLineData(DbgCompileUnit* compileUnit)
{
	if (!compileUnit || !compileUnit->mNeedsLineDataFixup)
		return;
	compileUnit->mNeedsLineDataFixup = false;
}

static int CompareLineData(const void* lineDataP1, const void* lineDataP2)
{
	int cmpResult = (int)(((DbgLineData*)lineDataP1)->mRelAddress - ((DbgLineData*)lineDataP2)->mRelAddress);
	if (cmpResult != 0)
		return cmpResult;

	// A larger contrib size means it's the 'outer' inlinee
	cmpResult = -(((DbgLineData*)lineDataP1)->mContribSize - ((DbgLineData*)lineDataP2)->mContribSize);
	if (cmpResult != 0)
		return cmpResult;

	return -(((DbgLineData*)lineDataP1)->mCtxIdx - ((DbgLineData*)lineDataP2)->mCtxIdx);
}

void WinDebugger::FixupLineDataForSubprogram(DbgSubprogram* subProgram)
{
	if ((subProgram == NULL) || (!subProgram->mNeedLineDataFixup))
		return;

	BP_ZONE("FixupLineDataForSubprogram");

	subProgram->mNeedLineDataFixup = false;
	if (subProgram->mInlineeInfo != NULL)
		FixupLineDataForSubprogram(subProgram->mInlineeInfo->mRootInliner);

	if ((subProgram->mLineInfo == NULL) || (subProgram->mLineInfo->mLines.mSize == 0))
		return;

	//TODO: I think this was covering up a bug in DWARF line encoding? Figure this out
// 	if (subProgram->mLineInfo->mLines.mSize >= 2)
// 	{
// 		DbgLineData* line0 = &subProgram->mLineInfo->mLines[0];
// 		DbgLineData* line1 = &subProgram->mLineInfo->mLines[1];
//
//
// 		if ((line0->mRelAddress == line1->mRelAddress) && (!line0->IsStackFrameSetup()) && (line1->IsStackFrameSetup()))
// 		{
// 			CPUInst inst;
// 			if (mCPU->Decode(line0->mAddress, subProgram->mCompileUnit->mDbgModule->mOrigImageData, &inst))
// 				line1->mAddress += inst.GetLength();
// 		}
// 	}

	qsort(subProgram->mLineInfo->mLines.mVals, subProgram->mLineInfo->mLines.mSize, sizeof(DbgLineData), CompareLineData);

	// If we have multiple lines with the same line/column/context, merge them
	if (!subProgram->mLineInfo->mLines.IsEmpty())
	{
		auto prevLine = &subProgram->mLineInfo->mLines[0];
		for (int i = 1; i < subProgram->mLineInfo->mLines.mSize; i++)
		{
			auto nextLine = &subProgram->mLineInfo->mLines[i];

			if ((nextLine->mLine == prevLine->mLine) && (nextLine->mColumn == prevLine->mColumn) && (nextLine->mCtxIdx == prevLine->mCtxIdx) &&
				(nextLine->mRelAddress == prevLine->mRelAddress + prevLine->mContribSize))
			{
				prevLine->mContribSize += nextLine->mContribSize;
				// This messed up inline cases because mContribSize actually INCLUDES inlined lines so it caused the address to skip too far
				//nextLine->mRelAddress += nextLine->mContribSize;
				//nextLine->mContribSize = 0;
			}
			else
			{
				prevLine = nextLine;
			}
		}
	}
}

void WinDebugger::ReserveHotTargetMemory(int size)
{
	HotTargetMemory hotTargetMemory;
	hotTargetMemory.mOffset = 0;
	hotTargetMemory.mSize = 0;
	hotTargetMemory.mPtr = NULL;

	if (size > 0)
	{
		// In 64-bit mode we have a reserved region on program load that we commit here because the offsets
		//  must be within 32-bits of the original EXE image, but in 32-bit mode we don't reserve anything
		//  until here
#ifdef BF_DBG_32
	//hotTargetMemory.mSize = std::max(1024 * 1024, size);
		BF_ASSERT((size & (mPageSize - 1)) == 0);
		hotTargetMemory.mSize = size;
		hotTargetMemory.mPtr = (addr_target)(intptr)VirtualAllocEx(mProcessInfo.hProcess, NULL, hotTargetMemory.mSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
		mDebugTarget->mHotHeap->AddTrackedRegion(hotTargetMemory.mPtr, hotTargetMemory.mSize);
#else
		hotTargetMemory.mSize = size;
		hotTargetMemory.mPtr = mDebugTarget->mHotHeap->Alloc(size);
		BF_ASSERT(hotTargetMemory.mPtr != 0);
		auto ptr = ::VirtualAllocEx(mProcessInfo.hProcess, (void*)(intptr)hotTargetMemory.mPtr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
		BF_ASSERT(ptr == (void*)(intptr)hotTargetMemory.mPtr);
#endif
	}

	BfLogDbg("ReserveHotTargetMemory %p %d\n", hotTargetMemory.mPtr, hotTargetMemory.mSize);
	int err = GetLastError();
	mHotTargetMemory.push_back(hotTargetMemory);
}

addr_target WinDebugger::AllocHotTargetMemory(int size, bool canExecute, bool canWrite, int* outAllocSize)
{
	int prot = PAGE_READWRITE;
	if (canExecute && canWrite)
		prot = PAGE_EXECUTE_READWRITE;
	else if (canExecute)
		prot = PAGE_EXECUTE_READ;

	size = (size + (mPageSize - 1)) & ~(mPageSize - 1);
	*outAllocSize = size;

	HotTargetMemory* hotTargetMemory = NULL;
	bool foundHotTargetMemory = false;
	for (int i = mHotTargetMemory.mSize - 1; i >= BF_MAX(mHotTargetMemory.mSize - 32, 0); i--)
	{
		hotTargetMemory = &mHotTargetMemory[i];
		if (hotTargetMemory->mPtr == 0)
		{
			Fail("Failed to allocate memory for hot loading");
			return 0;
		}

		if (hotTargetMemory->GetSizeLeft() >= size)
		{
			foundHotTargetMemory = true;
			break;
		}
	}

	if (!foundHotTargetMemory)
	{
		ReserveHotTargetMemory(size);
		foundHotTargetMemory = true;
		hotTargetMemory = &mHotTargetMemory.back();
	}

	BF_ASSERT(hotTargetMemory->mOffset + size <= hotTargetMemory->mSize);
	addr_target result = hotTargetMemory->mPtr + hotTargetMemory->mOffset;

	::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)result, size, prot, NULL);

	BfLogDbg("AllocHotTargetMemory: %p %d %d %d\n", result, size, canExecute, canWrite);

	hotTargetMemory->mOffset += size;
	return result;
}

void WinDebugger::ReleaseHotTargetMemory(addr_target addr, int size)
{
#ifdef BF_DBG_32
	::VirtualFreeEx(mProcessInfo.hProcess, (void*)(intptr)addr, 0, MEM_RELEASE);
#else
	mDebugTarget->mHotHeap->Release(addr, size);
	::VirtualFreeEx(mProcessInfo.hProcess, (void*)(intptr)addr, size, MEM_DECOMMIT);
#endif
}

void WinDebugger::CleanupHotHeap()
{
	mDebugTarget->mLastHotHeapCleanIdx = mDebugTarget->mHotHeap->mBlockAllocIdx;

	// Our criteria for determining whether a hot loaded file is still being used:
	//  1) If we are currently executing a method from that object file.
	//  2) If the symbol map has a symbol with that address.
	//  3) If the static variable map contains a reference - including a conservative scan of the data
	//        This handles vdata references
	// This is a conservative check which won't purge hot reloads that contain deleted
	//  methods (for example), but it will purge hot reloads where all the changed
	//  data has been overwritten.
	// For delegate bindings, the original module declaring the bind creates a "preserve"
	//  global such as "bf_hs_preserve@_ZN5TestO4TestEv", whose preserved symbol ensures it
	//  doesn't get unloaded.  The current version of that method resides in "_ZN5TestO4TestEv",
	//  ensuring that the method pointed to by the global variable is valid
	mDebugTarget->mHotHeap->ClearReferencedFlags();

	addr_target lowAddr = mDebugTarget->mHotHeap->mHotAreaStart;
	addr_target highAddr = lowAddr + mDebugTarget->mHotHeap->mHotAreaSize;

	// Do conservative scan through all thread stacks.  Stack traces aren't 100% reliable, so we
	//  need to do a full conservative scan of any addresses stored in the stack
	//  to ensure we don't miss any return addresses
	for (int threadIdx = 0; threadIdx < (int)mThreadList.size(); threadIdx++)
	{
		WdThreadInfo* threadInfo = mThreadList[threadIdx];

		BF_CONTEXT lcContext;
		lcContext.ContextFlags = BF_CONTEXT_CONTROL;
		BF_GetThreadContext(threadInfo->mHThread, &lcContext);

		addr_target checkStackAddr = BF_CONTEXT_SP(lcContext);
		checkStackAddr &= ~(sizeof(addr_target) - 1);

		// Conservative check on registers
		for (int regNum = 0; regNum < sizeof(BF_CONTEXT)/sizeof(addr_target); regNum++)
		{
			addr_target checkAddr = ((addr_target*)&lcContext)[regNum];
			if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
				mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
		}

		// Conservative check on all stack data
		while (checkStackAddr < threadInfo->mStartSP)
		{
			addr_target checkAddrArr[1024];
			int numAddrsChecking = BF_MIN(1024, (int)((threadInfo->mStartSP - checkStackAddr) / sizeof(addr_target)));
			ReadMemory(checkStackAddr, numAddrsChecking * sizeof(addr_target), checkAddrArr);
			checkStackAddr += numAddrsChecking * sizeof(addr_target);

			for (int addrIdx = 0; addrIdx < numAddrsChecking; addrIdx++)
			{
				addr_target checkAddr = checkAddrArr[addrIdx];
				if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
					mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
			}
		}
	}

	auto mainModule = mDebugTarget->mTargetBinary;
	for (auto entry : mainModule->mSymbolNameMap)
	{
		auto dwSymbol = entry->mValue;
		addr_target checkAddr = dwSymbol->mAddress;
		if ((checkAddr >= lowAddr) && (checkAddr < highAddr))
			mDebugTarget->mHotHeap->MarkBlockReferenced(checkAddr);
	}

	mDebugTarget->CleanupHotHeap();

	BfLogDbg("Hot load memory used: %dk\n", (int)mDebugTarget->mHotHeap->GetUsedSize() / 1024);
}

int WinDebugger::EnableWriting(intptr address, int size)
{
	DWORD oldProt;
	bool success = ::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)address, size, PAGE_READWRITE, &oldProt);
	if (!success)
	{
		int err = GetLastError();
	}
	return (int)oldProt;
}

int WinDebugger::SetProtection(intptr address, int size, int prot)
{
	DWORD oldProt;
	::VirtualProtectEx(mProcessInfo.hProcess, (void*)(intptr)address, size, prot, &oldProt);
	return (int)oldProt;
}

void WinDebugger::EnableMemCache()
{
	mMemCacheAddr = 1;
}

void WinDebugger::DisableMemCache()
{
	mMemCacheAddr = 0;
}

bool WinDebugger::ReadMemory(intptr address, uint64 length, void* dest, bool local)
{
	if (local)
	{
		__try
		{
			memcpy(dest, (void*)address, length);
			return true;
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			return false;
		}
	}

	if (mMemCacheAddr != 0)
	{
		addr_target targetAddr = (addr_target)address;
		if ((targetAddr >= mMemCacheAddr) && (targetAddr + length <= mMemCacheAddr + WD_MEMCACHE_SIZE) && (mMemCacheAddr > 1))
		{
			memcpy(dest, mMemCacheData + (targetAddr - mMemCacheAddr), length);
			return true;
		}

		// We need a new block
		SIZE_T dwReadBytes;
		if (::ReadProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, mMemCacheData, (SIZE_T)WD_MEMCACHE_SIZE, &dwReadBytes) != 0)
		{
			mMemCacheAddr = targetAddr;
			memcpy(dest, mMemCacheData, length);
			return true;
		}

		// Failed, turn off caching
		mMemCacheAddr = 0;
	}

	SIZE_T dwReadBytes;
	if (::ReadProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, dest, (SIZE_T)length, &dwReadBytes) != 0)
		return true;
	int lastErr = ::GetLastError();
	memset(dest, 0, length);
	return false;
}

bool WinDebugger::WriteMemory(intptr address, void* src, uint64 length)
{
	SIZE_T dwBytesWritten = 0;
	int result = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, src, (SIZE_T)length, &dwBytesWritten);
	return result != 0;
}

addr_target WinDebugger::GetTLSOffset(int tlsIndex)
{
	typedef LONG NTSTATUS;
	typedef DWORD KPRIORITY;
	typedef WORD UWORD;

	enum THREADINFOCLASS
	{
		ThreadBasicInformation,
	};

	struct CLIENT_ID
	{
		HANDLE UniqueProcess;
		HANDLE UniqueThread;
	};

	struct
	{
		NTSTATUS mExitStatus;
		void* mTebBaseAddress;
		CLIENT_ID mClientId;
		KAFFINITY mAffinityMask;
		KPRIORITY mPriority;
		KPRIORITY mBasePriority;
	} threadInfo = { 0 };

	ULONG len = 0;
	bool loadedManually = false;
	static HMODULE module = NULL;
	static NTSTATUS(__stdcall *NtQueryInformationThread)(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);

	if (module == NULL)
	{
		module = GetModuleHandleA("ntdll.dll");
		NtQueryInformationThread = reinterpret_cast<decltype(NtQueryInformationThread)>(GetProcAddress(module, "NtQueryInformationThread"));
	}

	if (NtQueryInformationThread == NULL)
		return 0;
	NTSTATUS status = NtQueryInformationThread(mActiveThread->mHThread, (THREADINFOCLASS)0, &threadInfo, sizeof(threadInfo), nullptr);
	if (status < 0)
		return 0;

#ifdef BF_DBG_32
	addr_target tibAddr = ReadMemory<addr_target>((intptr)threadInfo.mTebBaseAddress + 0x0);
	addr_target tlsTable = ReadMemory<addr_target>((intptr)tibAddr + 0x2C);
#else
	addr_target tlsTable = ReadMemory<addr_target>((intptr)threadInfo.mTebBaseAddress + 0x58);
#endif
	return ReadMemory<addr_target>(tlsTable + tlsIndex * sizeof(addr_target));
}

bool WinDebugger::WriteInstructions(intptr address, void* src, uint64 length)
{
	SIZE_T dwBytesWritten = 0;
	bool result = ::WriteProcessMemory(mProcessInfo.hProcess, (void*)(intptr)address, src, (SIZE_T)length, &dwBytesWritten) != 0;
	result |= ::FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)address, (SIZE_T)length) != 0;
	BF_ASSERT(result);
	BfLogDbg("WriteInstructions: %p %d\n", address, length);
	return result;
}

DbgMemoryFlags WinDebugger::GetMemoryFlags(intptr address)
{
	MEMORY_BASIC_INFORMATION memBasicInfo;
	if (::VirtualQueryEx(mProcessInfo.hProcess, (void*)address, &memBasicInfo, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
	{
		//BfLogDbg("VirtualQueryEx failed with %d\n", GetLastError());
		return DbgMemoryFlags_None;
	}

	DbgMemoryFlags flags = DbgMemoryFlags_None;
	if (memBasicInfo.AllocationProtect & PAGE_READWRITE)
	{
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
	}
	if (memBasicInfo.AllocationProtect & PAGE_READONLY)
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
	if (memBasicInfo.AllocationProtect & PAGE_WRITECOPY)
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
	if (memBasicInfo.AllocationProtect & PAGE_EXECUTE)
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
	if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_READ)
	{
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
	}
	if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_READWRITE)
	{
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Read);
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
	}
	if (memBasicInfo.AllocationProtect & PAGE_EXECUTE_WRITECOPY)
	{
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Execute);
		flags = (DbgMemoryFlags)(flags | DbgMemoryFlags_Write);
	}
	return flags;
}

#ifdef BF_DBG_32
Debugger* Beefy::CreateDebugger32(DebugManager* debugManager, DbgMiniDump* miniDump)
#else
Debugger* Beefy::CreateDebugger64(DebugManager* debugManager, DbgMiniDump* miniDump)
#endif
{
	if (miniDump != NULL)
	{
		auto debugger = new MiniDumpDebugger(debugManager, miniDump);

		return debugger;
	}
	return new WinDebugger(debugManager);
}

#ifdef BF_DBG_32

void WdAllocTest()
{
	Array<BeefyDbg32::WdStackFrame*> stackFrameList;

	for (int i = 0; true; i++)
	{
		WdStackFrame* stackFrame = new WdStackFrame();
		stackFrameList.push_back(stackFrame);
	}
}
#endif

#endif //!defined BF32 || !defined BF_DBG_64