/*
 *
 * dmserv.c
 *
 * The debug monitor server
 *
 */

#include "dmp.h"
#include "xboxp.h"
#include "xconfig.h"
#include "av.h"
#include "xlaunch.h"
#undef DeleteFile
#include "d3d8.h"

#define FTP_NOT_LOGGED_IN 0
#define FTP_LOGGED_IN 1

static XNADDR sAddress;
void ShowIPAddress(ULONG);
DWORD DedicatedServThread(LPVOID lpv);
unsigned short portcounter = 1024;

RTL_CRITICAL_SECTION csEch;
RTL_CRITICAL_SECTION csAccept;
CST rgcst[MAX_CONNECTIONS];
const char rgchHex[] = "0123456789ABCDEF";
char rgchDbgName[256];
ULONG ulIpAddr;
BOOL g_fServShutdown;
BOOL g_fDebugging;
BOOL g_fLockLevel;
BOOL g_fDebugSecureMode;// = TRUE;
ULARGE_INTEGER g_luBoxId;
KEVENT kevtServ;
PETHREAD pthrServ;
extern CHH rgchh[];
extern int cchh;
int portcommand_socket;


/*
WCHAR * ConvertString( char *text )
{
	static WCHAR temp[16*1024];

	if( ! text )
		return 0;

	mbstowcs( temp, text, 16*1024 );

	return temp;
}

PVOID malloc2( ULONG size )
{
	return DmAllocatePoolWithTag( size, 'hcmd' );
}

void free2( void *p )
{
	DmFreePool( p );
}

int strnicmp2( char *t1, char *t2, int size )
{
	int t;
	for( t=0; t<size; ++t )
	{
		if( toupper(t1[t]) != toupper(t2[t]) )
			return 1;
	}
	return 0;
}
/**/



int BindUDP( unsigned short port )
{
	struct sockaddr_in addr;
	char value;
	int tUDPsocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	value = 1;
	setsockopt( tUDPsocket, SOL_SOCKET, SO_BROADCAST, &value, value );
	memset(&(addr),0,sizeof(addr));
	addr.sin_family = AF_INET; 
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);
	bind(tUDPsocket,(struct sockaddr *)(&addr),sizeof(addr));

	return tUDPsocket;
}
/**************************************** FTP ************************************/
/*
char pwd[256] = "/";
int client=0;
int local_socket=0;



int FTP_send( int socket, char *command )
{
	char work[256];

	if( socket )
		local_socket = socket;

	sprintf(work,"[0x0%8X] = \"%s\"",local_socket, command);
	while( strpbrk( work,"\r\n") )
		strpbrk( work,"\r\n")[0] = '.';
	strcat( work,"\r");

	return send(local_socket, command, strlen(command),0 );
}

int FTP_CheckForCommand( char *command, char *buffer )
{
	if( !strnicmp2(command,buffer,min(strlen(command),strlen(buffer))))
		return 1;
	return 0;
}

char *FTP_Parameter( char *command )
{
	if( strpbrk( command, "\r\n" ) )
		strpbrk( command, "\r\n" )[0] = 0;

	if( strpbrk(command," " ) )
		return strpbrk(command," " )+1;

	return 0;
}

int FTP_Login( char *command )
{
	static char username[64];
	char *pointer;

	if( FTP_CheckForCommand( "USER", command ))
	{
		pointer = FTP_Parameter(command);
		strcpy(username, pointer);
		FTP_send(0,"331 Password required\r\n");
		return FTP_NOT_LOGGED_IN;
	}
	if( FTP_CheckForCommand( "PASS", command ))
	{
		pointer = FTP_Parameter(command);
		strcpy(username, pointer);
		if( !strnicmp2(username, "xbox",4 ) )
		{
			FTP_send( 0,"550 Illegal login.\r\n");
			return FTP_NOT_LOGGED_IN;
		}
		if( strnicmp2(FTP_Parameter(command), "x3",2 ) )
		{
			FTP_send( 0,"550 Illegal login.\r\n");
			return FTP_NOT_LOGGED_IN;
		}

//------------------- init ftp values
		strcpy(pwd,"/");
		FTP_send(0,"230 User");
		FTP_send(0,username);
		FTP_send(0,"logged in.\r\n");

		return FTP_LOGGED_IN;
	}
	return FTP_NOT_LOGGED_IN;
}

#define FTP_PORT_PASV 2
#define FTP_PORT_NORMAL 4

int PORT_Type = 0;

int FTP_ConnectToServer( int p1,int p2,int p3,int p4,unsigned short port )
{
	char v=1;
	struct sockaddr_in s_in;
	int temp_socket;

    memset(&s_in,0,sizeof(s_in));   
//  if you forget to empty this variable, you're on your own.  

    s_in.sin_family=AF_INET;
    s_in.sin_addr.S_un.S_addr = (p4<<24) + (p3<<16) + (p2<<8) + p1;
    s_in.sin_port=htons(port);
// this is now configured to crusaders's ftp-server
// (ok, I know it doesn't really exist yet) at port 21.

    temp_socket=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);

//  AF_INET and SOCK_STREAM is the typical tcp/ip-configuration.
//  We concentrate on this.

    if(temp_socket==SOCKET_ERROR)
    {
		return 0;
    }

    if(connect(temp_socket,(const struct sockaddr*)&s_in,sizeof(s_in))==SOCKET_ERROR)
		return 0;
 	return temp_socket;
}

void FTP_StartPort( char *command )
{
	char *param = FTP_Parameter( command );
	int ip1,ip2,ip3,ip4,p1,p2;

	sscanf(param,"%u,%u,%u,%u,%u,%u",&ip1,&ip2,&ip3,&ip4,&p1,&p2);
	portcommand_socket = FTP_ConnectToServer(ip1,ip2,ip3,ip4,(p1*256) + p2);
	if( portcommand_socket )
	{
		FTP_send(0,"200 PORT command successful.\r\n");
		PORT_Type = FTP_PORT_NORMAL;
	}
	else
	{
		FTP_send(0,"550 PORT command FAILED.\r\n");
		PORT_Type = 0;
	}
}

int FTP_StartPassive( int feedback )
{	
	struct sockaddr_in port_s_in;
	char work[256];
	int t;

	memset(&port_s_in,0,sizeof(port_s_in));   

	port_s_in.sin_family=AF_INET;
	port_s_in.sin_addr.s_addr = INADDR_ANY;
	port_s_in.sin_port=htons( portcounter );

    portcommand_socket=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
	if(portcommand_socket==SOCKET_ERROR)
	{
		return 0;
	}

//	 bind(portcommand_socket,(const sockaddr *)&port_s_in,sizeof(struct sockaddr_in));
	if(0 == bind(portcommand_socket, (struct sockaddr *)&port_s_in, sizeof port_s_in))
	{
		return 0;
	}  
	while( listen(portcommand_socket,5)==SOCKET_ERROR );

	if( feedback )
	{
		sprintf( work, "227 Entering passive mode (%u,%u,%u,%u,%u,%u)\r\n", sAddress.ina.S_un.S_un_b.s_b1, 
																			sAddress.ina.S_un.S_un_b.s_b2, 
																			sAddress.ina.S_un.S_un_b.s_b3, 
																			sAddress.ina.S_un.S_un_b.s_b4, 
																			portcounter / 256, 
																			portcounter%256);
		FTP_send(0,work);
	}
	PORT_Type = FTP_PORT_PASV;
	portcounter++;
	return portcommand_socket;
}

char *FTP_Path( char *path )
{
	static char work[256];
//	strcpy(work,pwd);

	if( path )
		strcpy( work, path );
	else
		strcpy( work, pwd );
		
	memset( work,0,256);
	if( strlen(path) > 1 )
		strncpy(work,&path[1],1 );		
	else
		return "";

	strcat(work,":");
	strcat(work,path+2);

	while( strpbrk(work,"/") )
		strpbrk(work,"/")[0] = '\\';
	return work;
}
char month[12][4]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};


void FTP_SendDirInfo(void)
{
	char work[256];
	HANDLE h;
	WIN32_FIND_DATA data;

	if( PORT_Type == FTP_PORT_PASV )
	{
		struct sockaddr port_s_in;
		int size = sizeof(port_s_in);

		int temp = accept(portcommand_socket, &port_s_in, &size );
		closesocket( portcommand_socket );
		portcommand_socket = temp;
	}
	FTP_send(0,"150 Opening ASCII mode data connection for file list\r\n");
	if( strlen( pwd ) > 1 )
	{

		strcpy(work,FTP_Path(0));
		strcat(work,"*");

		memset( &data,0,sizeof(WIN32_FIND_DATA));
		h = FindFirstFile( work, &data );
		while( h != INVALID_HANDLE_VALUE )
		{
			SYSTEMTIME SystemTime;

			FileTimeToSystemTime( &data.ftCreationTime, &SystemTime );
			if( data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY )
			{
				sprintf(work, "drw-rw-rw-   1 user     group           0 %s %2d %02d:%02d %s\r\n", month[SystemTime.wMonth-1],SystemTime.wDay,SystemTime.wHour,SystemTime.wMinute,data.cFileName);
				FTP_send( portcommand_socket, work );
			}
			else
			{
				sprintf(work, "-rw-rw-rw-   1 user     group %11d %s %2d %02d:%02d %s\r\n", data.nFileSizeLow, month[SystemTime.wMonth-1],SystemTime.wDay,SystemTime.wHour,SystemTime.wMinute,data.cFileName);
				FTP_send( portcommand_socket, work );
			}
			if( !FindNextFile( h, &data ) )
			{
				FindClose( h );
				h = INVALID_HANDLE_VALUE;
			}
		}
	}
	else
	{
		FTP_send( portcommand_socket, "drw-rw-rw-   1 user     group           0 Jan 01 00:00 C\r\n" );
		FTP_send( portcommand_socket, "drw-rw-rw-   1 user     group           0 Jan 01 00:00 D\r\n" );
		FTP_send( portcommand_socket, "drw-rw-rw-   1 user     group           0 Jan 01 00:00 E\r\n" );
		FTP_send( portcommand_socket, "drw-rw-rw-   1 user     group           0 Jan 01 00:00 F\r\n" );
		FTP_send( portcommand_socket, "drw-rw-rw-   1 user     group           0 Jan 01 00:00 G\r\n" );
	}
	shutdown(portcommand_socket, SD_SEND );
	closesocket( portcommand_socket );
	FTP_send( client, "226 Transfer complete.\r\n");
}



void FTP_Store( char *command )
{
	char work[256];
	char *pointer;
	char *parameter ;
	char *buffer;
	int size;
	int size2;
	HANDLE fp;
	DWORD wrotebytes;

	if( PORT_Type == FTP_PORT_PASV )
	{
		struct sockaddr port_s_in;
		int size = sizeof(port_s_in);

		int temp = accept(portcommand_socket, &port_s_in, &size );
		closesocket( portcommand_socket );
		portcommand_socket = temp;
	}
	FTP_send(0,"150 Opening ASCII mode data connection.\r\n");
	parameter = FTP_Parameter( command );

	if( parameter[0] == '/' )
		strcpy(work,FTP_Path(parameter));
	else
	{
		pointer = FTP_Parameter(command);
		strcpy(work,FTP_Path(0));
		strcat(work,pointer);
	}

	fp = CreateFile( work, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0 );  

	buffer = malloc2(2*1024*1024);
	size=0;
	size2=0;
	wrotebytes=0;

	while( (size = recv( portcommand_socket, buffer+size2, 60*1024, 0 )))
	{
		size2+=size;
		if( size2 > (1*1024*1024))
		{
			WriteFile( fp, buffer,1*1024*1024, &wrotebytes,0 );	
			size2 -= 1024*1024;
			memcpy( buffer, buffer + (1024*1024), size2 );
		}
	}
	size2+=size;
	WriteFile( fp, buffer,size2, &wrotebytes,0 );	
	CloseHandle( fp );
	free2( buffer );

	shutdown(portcommand_socket, SD_SEND );
	closesocket( portcommand_socket );
	FTP_send( client, "226 Transfer complete.\r\n");
}


void FTP_SendFile( char *command )
{
	char work[256];
	char *pointer;
	char *parameter;
	HANDLE fp ;
	char *buffer;
	int size;
	int size2;
	DWORD wrotebytes;
	int bufferpointer;

	if( PORT_Type == FTP_PORT_PASV )
	{
		struct sockaddr port_s_in;
		int size = sizeof(port_s_in);

		int temp = accept(portcommand_socket, &port_s_in, &size );
		closesocket( portcommand_socket );
		portcommand_socket = temp;
	}
	FTP_send(0,"150 Opening ASCII mode data connection.\r\n");
	parameter = FTP_Parameter( command );

	if( parameter[0] == '/' )
		strcpy(work,FTP_Path(parameter));
	else
	{
		pointer = FTP_Parameter(command);
		strcpy(work,FTP_Path(0));
		strcat(work,pointer);
	}
	fp = CreateFile( work, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0 );


	buffer = malloc2(2*1024*1024);
	size=0;
	size2=0;
	wrotebytes=1;
	bufferpointer=0;

	while( wrotebytes )
	{
		ReadFile( fp, buffer,1024*1024 , &wrotebytes,0 );
		size2 += wrotebytes;
		size = wrotebytes;
		bufferpointer=0;

		while( size )
		{
			DWORD s = send( portcommand_socket, buffer+bufferpointer, min(size,60*1024), 0 );
			if( s < 1 )
			{
				CloseHandle( fp );
				free2( buffer);
				shutdown(portcommand_socket, SD_SEND );
				closesocket( portcommand_socket );
				FTP_send( client, "500 Transfer messed up.\r\n");
				size2 =0;
				return;
			}
			size -= s;
			bufferpointer += s;
		}
	}
	CloseHandle( fp );
	free2( buffer);
	shutdown(portcommand_socket, SD_SEND );
	closesocket( portcommand_socket );
	FTP_send( client, "226 Transfer complete.\r\n");
}

void FTP_Mainloop( char *command )
{
	char work[256]; 
	char *ptr;

	if(FTP_CheckForCommand(command,"SYST"))
	{
		FTP_send(0,"215 UNIX emulation by Avalaunch 0.47.5\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"TYPE"))
	{
		FTP_send(0,"200 Type set to A.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"CWD"))
	{
		if( FTP_Parameter(command)[0] == '/' )
		{
			strcpy( pwd, FTP_Parameter(command) );
			FTP_send(0,"250 CWD command successful.\r\n");
			return;
		}
		strcat( pwd, FTP_Parameter(command) );
		strcat( pwd, "/" );
		FTP_send(0,"250 CWD command successful.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"DELE"))
	{
		if( FTP_Parameter(command)[0] == '/' )
		{
			strcpy( work, FTP_Path( FTP_Parameter(command) ) );
//			if(DeleteFile( ConvertString(work) ))
  //              FTP_send(0,"250 File Deleted.\r\n");
	//		else
                FTP_send(0,"550 File Deletion error.\r\n");
			return;
		}
		
		strcpy(work,pwd);
		strcat( work, FTP_Parameter(command) );
        FTP_send(0,"550 File Deletion error.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"RMD"))
	{
		if( FTP_Parameter(command)[0] == '/' )
		{
			strcpy( work, FTP_Path( FTP_Parameter(command) ) );
			if(RemoveDirectory( work ))
                FTP_send(0,"250 Dir deleted.\r\n");
			else
                FTP_send(0,"550 dir creation error.\r\n");
			return;
		}		
		strcpy(work,pwd);
		strcat( work, FTP_Parameter(command) );
		if(RemoveDirectory( FTP_Path(work) ))
            FTP_send(0,"250 Dir deleted.\r\n");
		else
            FTP_send(0,"550 dir creation error.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"MKD"))
	{
		if( FTP_Parameter(command)[0] == '/' )
		{
			strcpy( work, FTP_Path( FTP_Parameter(command) ) );
			if(CreateDirectory( work,0 ))
                FTP_send(0,"250 Dir made.\r\n");
			else
                FTP_send(0,"550 dir creation error.\r\n");
			return;
		}
		
		strcpy(work,pwd);
		strcat( work, FTP_Parameter(command) );
		if(CreateDirectory( FTP_Path(work),0 ))
            FTP_send(0,"250 Dir made.\r\n");
		else
            FTP_send(0,"550 dir creation error.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"NOOP"))
	{
		FTP_send(0,"250 Sure.\r\n");
		return;
	}
	if(FTP_CheckForCommand(command,"CDUP"))
	{
		if( strlen(pwd) > 1 )
		{
			pwd[ strlen( pwd ) -1 ] = 0;

			ptr = strpbrk( pwd,"/" );

			while( strpbrk( ptr+1,"/" ) )
				ptr = strpbrk( ptr+1,"/" );	
			
			if( ptr	)
				ptr[1] = 0;
			FTP_send(0,"250 CWD command successful.\r\n");
			return;
		}
		else
		{
			FTP_send(0,"550 At root already\r\n");
			return;
		}
	}
	if(FTP_CheckForCommand(command,"PWD"))
	{
		sprintf(work,"257 \"%s\"\r\n",pwd);
		FTP_send(0,work);
		return;
	}
	if(FTP_CheckForCommand(command,"PASV"))
	{
		FTP_StartPassive(0);
		return;
	}
	if(FTP_CheckForCommand(command,"PORT"))
	{
		FTP_StartPort(command);
		return;
	}
	if(FTP_CheckForCommand(command,"LIST"))
	{
		FTP_SendDirInfo();
		return;
	}
	if(FTP_CheckForCommand(command,"RETR"))
	{
		FTP_SendFile( command );
		return;
	}
	if(FTP_CheckForCommand(command,"STOR"))
	{
		FTP_Store(command);
		return;
	}
	if(FTP_CheckForCommand(command,"SITE"))
	{
		FTP_send(0,"200 sure whatever\r\n");
		return;
	}
	FTP_send(0,"550 What was that?\r\n");
}

void LaunchFTP( int socket )
{
	struct sockaddr port_s_in;
	int life = sizeof( struct sockaddr_in );		
	
	char command[1024];
	int loggedinlevel = 0;

	client = accept( socket, &port_s_in, &life);
	FTP_send(client, "220 X3-FTP Server ready.\r\n");

	memset( command,0,1024);
	while( recv( client,command,1024,0) > 0 )
	{
		switch( loggedinlevel )
		{
		case FTP_NOT_LOGGED_IN :
			loggedinlevel = FTP_Login( command );
			break;
		case FTP_LOGGED_IN :
			FTP_Mainloop( command );
			break;
		}
		memset( command,0,1024);
	}
}


/**************************************** FTP ************************************/




int PingAllXboxes( void )
{
	static int			inited = 0;
	static int			udp_server_socket;
	static int			t1,t2,t3,t4;
	static struct sockaddr_in	server;
	static char			szWork[ 512 ];
	char				szTemp[ 512 ];
	static struct sockaddr_in	cliAddr;
	static int cliLen;
	static DWORD dwState;
	int life = 0;
	struct timeval timeout={0,0};
	fd_set readfds; 

	if( ( !inited ) )
	{
		dwState = XNetGetDebugXnAddr(&sAddress);
		XNetInAddrToString(sAddress.ina,szWork,64);

		// Get IP address
		//sscanf( szWork, "%d.%d.%d.%d", &t1, &t2, &t3, &t4 );
		strcpy( szWork,"192.168.1.102");

		cliLen = sizeof( cliAddr);

		// port 4905 is our avaping socket
		if( !inited )
		{
			udp_server_socket = BindUDP( 4905 );
		}

		inited = 1;
	}

	FD_ZERO(&readfds);
	FD_SET(udp_server_socket, &readfds);
	life = select( 0,&readfds, NULL, NULL, &timeout ); 

	if( life )
	{
		recvfrom(udp_server_socket, szWork, 512, 0,(struct sockaddr *) &cliAddr, &cliLen); 

		if( strcmp( szWork, "ping" ) == ( int )0 )
		{
			sprintf( szWork,
					 "%s;%s;%s;%d;%d\r\n\0",
					 "heyhey!",
					 "xbox",
					 "xbox",
					 21,
					 1);

			sendto(udp_server_socket,(char *)szWork,strlen(szWork)+1,0,(struct sockaddr *)(&cliAddr),sizeof(cliAddr));

			sprintf(szWork, "%d.%d.%d.%d",cliAddr.sin_addr.S_un.S_un_b.s_b1,cliAddr.sin_addr.S_un.S_un_b.s_b2,cliAddr.sin_addr.S_un.S_un_b.s_b3,cliAddr.sin_addr.S_un.S_un_b.s_b4 );

			// Check if we have this client in our list already, and if not respond with a ping
		}
	}

	return ( int )1;
}


INITIALIZED_OBJECT_STRING_RDATA(ostDash, "\\Device\\Harddisk0\\Partition1\\DASHBOARD.XBX");
CHAR XdkLauncherPathname[] = "\\Device\\Harddisk0\\Partition2\\xshell.xbe";

/* Extension stuff */
ECH *rgpech[MAX_ECH];

NTSTATUS
WINAPI
XWriteTitleInfoNoReboot(
    PCOSTR pszLaunchPath,
    PCOSTR pszDDrivePath,
    DWORD dwLaunchDataType,
    DWORD dwTitleId,
    PLAUNCH_DATA pLaunchData
    );

BOOL
WINAPI
XapiFormatFATVolume(
    POBJECT_STRING VolumePath
    );

void InitServ(void)
{
    InitializeCriticalSection(&csEch);
    InitializeCriticalSection(&csAccept);
    KeInitializeEvent(&kevtServ, NotificationEvent, FALSE);
}

void StopServ(void)
{
    int icst;
    PETHREAD pthr;

    /* Loop through the socket list and close each one down */
    EnterCriticalSection(&csAccept);
    g_fServShutdown = TRUE;
    for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
        if(rgcst[icst].s != INVALID_SOCKET) {
            /* If this socket isn't running on the current thread, it may be
             * inside of a select.  In that case, we take it off the thread,
             * stop the select, and give the thread a little bit of time to
             * exit the select */
            pthr = rgcst[icst].pthrDedicated;
            rgcst[icst].pthrDedicated = NULL;
            if(pthr != PsGetCurrentThread()) {
                KeAlertThread(&pthr->Tcb, KernelMode);
                Sleep(20);
            }
            shutdown(rgcst[icst].s, SD_SEND);
        }
    }
    LeaveCriticalSection(&csAccept);

    /* Wait for the sockets to close */
    Sleep(200);
}

int SgnCompareRgch(const char *sz1, const char *sz2, int cch)
{
    while(cch-- && *sz1) {
        char ch1 = *sz1++;
        char ch2 = *sz2++;
        if(ch1 >= 'a' && ch2 <= 'z')
            ch1 -= 32;
        if(ch2 >= 'a' && ch2 <= 'z')
            ch2 -= 32;
        if(ch1 != ch2)
            return ch1 - ch2;
    }
    if(*sz1)
        return *sz1;
    return cch < 0 ? 0 : -*sz2;
}

BOOL FEqualRgch(const char *sz1, const char *sz2, int cch)
{
    return SgnCompareRgch(sz1, sz2, cch) == 0;
}

DWORD DwHexFromSz(LPCSTR sz, LPCSTR *szOut)
{
    DWORD dw = 0;

    for(;;) {
        if(*sz >= '0' && *sz <= '9')
            dw = dw * 16 + (*sz - '0');
        else if(*sz >= 'A' && *sz <= 'F')
            dw = dw * 16 + (*sz - 'A' + 10);
        else if(*sz >= 'a' && *sz <= 'f')
            dw = dw * 16 + (*sz - 'a' + 10);
        else
            break;
        ++sz;
    }
    if(szOut)
        *szOut = sz;
    return dw;
}

DWORD DwFromSz(LPCSTR sz, int *pcchUsed)
{
    DWORD dw = 0;
    LPCSTR szStart = sz;

    /* Process decimal, octal, or hex */
    if(*sz == '0') {
        ++sz;
        if(*sz == 'x')
            dw = DwHexFromSz(++sz, &sz);
        else
            while(*sz >= '0' && *sz <= '7')
                dw = dw * 8 + (*sz++ - '0');
    } else
        while(*sz >= '0' && *sz <= '9')
            dw = dw * 10 + (*sz++ - '0');
    if(pcchUsed)
        *pcchUsed = sz - szStart;
    return dw;
}

void CloseConn(int icst)
{
    KIRQL irql;

    ASSERT(rgcst[icst].s != INVALID_SOCKET);
    closesocket(rgcst[icst].s);

    /* The server thread will attempt to read data from this socket once
     * pechDedicated is cleared, and may attempt to reuse this
     * connection state as soon as the socket ID is set to INVALID.  We
     * need to do all of this as one atomic operation */
    irql = KeRaiseIrqlToDpcLevel();
    rgcst[icst].dwStatus = 0;
    rgcst[icst].pechDedicated = NULL;
    rgcst[icst].pthrDedicated = NULL;
    rgcst[icst].s = INVALID_SOCKET;
    KeLowerIrql(irql);
}

void InitDmcc(int icst)
{
    rgcst[icst].dmcc.BytesRemaining = -1;
    rgcst[icst].dmcc.Buffer = NULL;
    rgcst[icst].dmcc.CustomData = &rgcst[icst].ccs;
    rgcst[icst].dmcc.HandlingFunction = NULL;
    rgcst[icst].dwStatus &= ~(CONN_BINARY | CONN_BINSEND | CONN_MULTILINE);
}

BOOL PrintSockLine(SOCKET s, LPCSTR sz)
{
    char szT[1024];
    int ich;

    for(ich = 0; sz[ich] && ich < sizeof szT - 2; ++ich)
        szT[ich] = sz[ich];

    if(sz[ich]) {
        /* Didn't get the whole string, so we have to send two packets */
        if(send(s, sz, strlen(sz), 0) <= 0)
            return FALSE;
        if(send(s, "\015\012", 2, 0) <= 0)
            return FALSE;
    } else {
        szT[ich++] = '\015';
        szT[ich++] = '\012';
        if(send(s, szT, ich, 0) <= 0)
            return FALSE;
    }
    return TRUE;
}

BOOL ReadSockLine(SOCKET s, char *sz, int cch)
{
    int cb;
    int ich = 0;

    --cch; /* save room for \0 */
    while(ich < cch) {
        cb = recv(s, &sz[ich], 1, 0);
        if(cb <= 0) {
            if(ich == 0)
                return FALSE;
            sz[ich] = 0;
            return TRUE;
        }
        if(sz[ich] == '\012') {
            sz[ich] = 0;
            return TRUE;
        }
        if(sz[ich] != '\015')
            ++ich;
    }
    sz[ich] = 0;
    return TRUE;
}

__inline BOOL FIsSpace(char ch)
{
    return ch == ' ' || ch == '\015' || ch == 0;
}

const char *PchGetParam(LPCSTR szCmd, LPCSTR szKey, BOOL fNeedValue)
{
    const char *pchTok;
    int cchTok;
    BOOL fQuote = FALSE;

    /* Skip the command */
    for(pchTok = szCmd; !FIsSpace(*pchTok); ++pchTok);

    while(*pchTok) {
        /* Skip leading spaces */
        while(*pchTok && FIsSpace(*pchTok))
            ++pchTok;
        if(!*pchTok)
            return NULL;
        for(cchTok = 0; !FIsSpace(pchTok[cchTok]); ++cchTok) {
            if(pchTok[cchTok] == '=') {
                if(FEqualRgch(szKey, pchTok, cchTok))
                    return pchTok + cchTok + 1; /* Skip the '=' */
                break;
            }
        }
        /* If we didn't see the '=' we need to check anyway */
        if(!fNeedValue && pchTok[cchTok] != '=' && FEqualRgch(szKey, pchTok,
                cchTok))
            return pchTok + cchTok;
        /* No match, so we need to skip past the value */
        pchTok += cchTok;
        while(*pchTok && (!FIsSpace(*pchTok) || fQuote))
            if(*pchTok++ == '"')
                fQuote = !fQuote;
    }
    return NULL;
}

int CchOfWord(LPCSTR sz)
{
    int cch;
    for(cch = 0; !FIsSpace(sz[cch]); ++cch);
    return cch;
}

void GetParam(LPCSTR szLine, LPSTR szBuf, int cchBuf)
{
    int cch = 0;
    BOOL fQuote = FALSE;

    while(cch < cchBuf-1 && *szLine && (!FIsSpace(*szLine) || fQuote)) {
        if(*szLine == '"') {
            if(fQuote && szLine[1] == '"') {
                /* Double quote inside a string gets copied as a single
                 * quote */
                szBuf[cch++] = '"';
                szLine += 2;
            } else {
                fQuote = !fQuote;
                ++szLine;
            }
        } else
            szBuf[cch++] = *szLine++;
    }
    szBuf[cch] = 0;
}

BOOL FGetSzParam(LPCSTR szLine, LPCSTR szKey, LPSTR szBuf, int cchBuf)
{
    LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
    if(!pch)
        return FALSE;
    GetParam(pch, szBuf, cchBuf);
    return TRUE;
}

BOOL FGetDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw)
{
    int cch;
    char sz[32];
    LPCSTR pch = PchGetParam(szLine, szKey, TRUE);
    if(!pch)
        return FALSE;
    GetParam(pch, sz, sizeof sz);
    *pdw = DwFromSz(sz, &cch);
    return FIsSpace(sz[cch]);
}

BOOL FGetQwordParam(LPCSTR szLine, LPCSTR szKey, ULARGE_INTEGER *plu)
{
    int cch;
    char sz[32];
    LPCSTR pch;

    pch = PchGetParam(szLine, szKey, TRUE);
    if(!pch)
        return FALSE;
    GetParam(pch, sz, sizeof sz - 1);
    sz[sizeof sz - 1] = 0;

    /* Verify the 0q prefix */
    if(sz[0] != '0' || sz[1] != 'q')
        return FALSE;
    /* Make sure we have a bunch of hex characters */
    for(cch = 2; cch < sizeof sz && !FIsSpace(sz[cch]); ++cch) {
        if(!(sz[cch] >= '0' && sz[cch] <= '9' ||
                sz[cch] >= 'A' && sz[cch] <= 'F' ||
                sz[cch] >= 'a' && sz[cch] <= 'f'))
            return FALSE;
    }
    cch -= 2;
    if(cch <= 0)
        return FALSE;

    /* Move the text out to the end of the string and fill the preceding
     * characters with zeroes */
    memmove(&sz[sizeof sz - 1 - cch], &sz[2], cch);
    memset(sz, '0', sizeof sz - 1 - cch);

    /* Now parse out the two dwords */
    plu->LowPart = DwHexFromSz(&sz[sizeof sz - 9], NULL);
    sz[sizeof sz - 9] = 0;
    plu->HighPart = DwHexFromSz(&sz[sizeof sz - 17], NULL);
    return TRUE;
}

BOOL FGetNamedDwParam(LPCSTR szLine, LPCSTR szKey, DWORD *pdw, LPSTR szResp)
{
    if(!FGetDwParam(szLine, szKey, pdw)) {
        sprintf(szResp, "missing %s", szKey);
        return FALSE;
    }
    return TRUE;
}

ULONG UlAddrFromSz(LPCSTR sz)
{
    ULONG ul;
    int ib;
    int ich;

    for(ib = 0; ib < 4; ++ib) {
        BYTE b = 0;

        for(ich = 0; ich < 3; ++ich) {
            if(sz[ich] < '0' || sz[ich] > '9')
                break;
            b = 10 * b + (sz[ich] - '0');
        }
        if(ich == 0 || sz[ich] != (ib == 3 ? 0 : '.'))
            return 0;
        sz += ich + 1;
        ((BYTE *)&ul)[ib ^ 3] = b;
    }
    return ul;
}

LPCSTR SzStdResponse(HRESULT hr)
{
    LPCSTR pszResp;

    switch(hr) {
    case XBDM_NOSUCHFILE:
        pszResp = "file not found";
        break;
    case XBDM_NOMODULE:
        pszResp = "no such module";
        break;
    case XBDM_MEMUNMAPPED:
        pszResp = "memory not mapped";
        break;
    case XBDM_NOTHREAD:
        pszResp = "no such thread";
        break;
    case XBDM_INVALIDCMD:
        pszResp = "unknown command";
        break;
    case XBDM_NOTSTOPPED:
        pszResp = "not stopped";
        break;
    case XBDM_MUSTCOPY:
        pszResp = "file must be copied";
        break;
    case XBDM_ALREADYEXISTS:
        pszResp = "file already exists";
        break;
    case XBDM_DIRNOTEMPTY:
        pszResp = "directory not empty";
        break;
    case XBDM_BADFILENAME:
        pszResp = "filename is invalid";
        break;
    case XBDM_CANNOTCREATE:
        pszResp = "file cannot be created";
        break;
    case XBDM_DEVICEFULL:
        pszResp = "no room on device";
        break;
    case XBDM_MULTIRESPONSE:
        pszResp = "multiline response follows";
        break;
    case XBDM_BINRESPONSE:
        pszResp = "binary response follows";
        break;
    case XBDM_READYFORBIN:
        pszResp = "send binary data";
        break;
    case XBDM_CANNOTACCESS:
        pszResp = "access denied";
        break;
    case XBDM_NOTDEBUGGABLE:
        pszResp = "not debuggable";
        break;
    case XBDM_BADCOUNTTYPE:
        pszResp = "type invalid";
        break;
    case XBDM_COUNTUNAVAILABLE:
        pszResp = "data not available";
        break;
    case XBDM_NOTLOCKED:
        pszResp = "box is not locked";
        break;
    case XBDM_KEYXCHG:
        pszResp = "key exchange required";
        break;
    case XBDM_MUSTBEDEDICATED:
        pszResp = "dedicated connection required";
        break;
    case E_OUTOFMEMORY:
        pszResp = "out of memory";
        break;
    case E_UNEXPECTED:
        pszResp = "unexpected error";
        break;
    case E_INVALIDARG:
        pszResp = "bad parameter";
        break;
    case XBDM_NOERR:
        pszResp = "OK";
        break;
    default:
        pszResp = "";
        break;
    }
    return pszResp;
}

HRESULT HrFromStatus(NTSTATUS st, HRESULT hrDefault)
{
    switch(st) {
    case STATUS_DIRECTORY_NOT_EMPTY:
        return XBDM_DIRNOTEMPTY;
    case STATUS_OBJECT_NAME_COLLISION:
        return XBDM_ALREADYEXISTS;
    case STATUS_OBJECT_PATH_NOT_FOUND:
    case STATUS_OBJECT_NAME_NOT_FOUND:
        return XBDM_NOSUCHFILE;
    case STATUS_OBJECT_PATH_INVALID:
    case STATUS_OBJECT_NAME_INVALID:
        return XBDM_BADFILENAME;
    case STATUS_ACCESS_DENIED:
        return XBDM_CANNOTACCESS;
    case STATUS_DISK_FULL:
        return XBDM_DEVICEFULL;
    case STATUS_INSUFFICIENT_RESOURCES:
        return E_OUTOFMEMORY;
    case STATUS_INVALID_HANDLE:
        return E_INVALIDARG;
    }
    return hrDefault;
}

ULONG DmplOfConnection(PDM_CMDCONT pdmcc)
{
    if(!pdmcc)
        return -1;
    return rgcst[(PCST)pdmcc - rgcst].dmplCur;
}

BOOL FDedicateConnection(int icst, PECH pech)
{
    PETHREAD pthrExpect;
    HANDLE hthrNew;
    PVOID pfnCreateThread;

    pthrExpect = rgcst[icst].pthrDedicated;

    /* We should only be rededicating a connection running on the current
     * thread */
    if(PsGetCurrentThread() != pthrExpect) {
        DbgPrint("dm: rededicating a connection on the wrong thread!\n");
        return FALSE;
    }

    /* Create the new thread */
    if(pech) {
        pfnCreateThread = pech->pfnCreateThread;
        if(!pfnCreateThread)
            pfnCreateThread = CreateThread;
        hthrNew = (*(HANDLE (*)())pfnCreateThread)(NULL, 0,
            DedicatedServThread, (LPVOID)icst, CREATE_SUSPENDED, NULL);
        if(!hthrNew)
            return FALSE;
    } else
        pfnCreateThread = NULL;

    /* Send the success message */
    PrintSockLine(rgcst[icst].s, "205- connection dedicated");

    /* Mark the connection state for the new thread.  We mark the thread ID
     * as NULL; dedicated threads will fill it in when they start */
    rgcst[icst].pthrDedicated = NULL;
    rgcst[icst].pechDedicated = pech;

    /* Wake up the new thread */
    if(pfnCreateThread) {
        NtResumeThread(hthrNew, NULL);
        CloseHandle(hthrNew);
    } else {
        rgcst[icst].pthrDedicated = pthrServ;
        KeAlertThread(&pthrServ->Tcb, KernelMode);
    }

    return TRUE;
}

void SendHrSzResp(SOCKET s, HRESULT hr, LPCSTR szResp, LPSTR szBuf)
{
    /* Make sure we have an error code we like */
    if(((hr >> 16) & 0x7fff) != FACILITY_XBDM) {
        hr = SUCCEEDED(hr) ? XBDM_NOERR : XBDM_UNDEFINED;
        if(!szResp)
            szResp = SzStdResponse(E_UNEXPECTED);
    } else if((hr & 0xffff) > 0xff)
        hr = XBDM_UNDEFINED;

    if(FAILED(hr))
        szBuf[0] = '4';
    else
        szBuf[0] = '2';
    szBuf[1] = (char) ('0' + (hr & 0xffff) / 10);   // overflow?
    szBuf[2] = (char) ('0' + (hr & 0xffff) % 10);
    szBuf[3] = '-';
    szBuf[4] = ' ';
    if(szResp != szBuf) {
        if(szResp)
            strcpy(szBuf + 5, szResp);
        else
            szBuf[5] = 0;
    }
    PrintSockLine(s, szBuf);
}

HRESULT HrHandleBuiltinCommand(DWORD dmplCur, LPCSTR sz, int cchCmd,
    LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    int ichhMin, ichhMac, ichhMid;
    int sgn;
    BOOL fPermitted;

    ichhMin = 0;
    ichhMac = cchh;

    while(ichhMin < ichhMac) {
        ichhMid = (ichhMin + ichhMac) / 2;
        sgn = SgnCompareRgch(rgchh[ichhMid].szCommand, sz, cchCmd);
        if(sgn == 0)
            break;
        else if(sgn < 0)
            ichhMin = ichhMid + 1;
        else
            ichhMac = ichhMid;
    }

    if(ichhMin < ichhMac) {
        /* Check for appropriate permission */
        if(rgchh[ichhMid].dmplCombined)
            fPermitted = (dmplCur & rgchh[ichhMid].dmplCombined) ==
                rgchh[ichhMid].dmplCombined;
        else if(rgchh[ichhMid].dmplMask)
            fPermitted = dmplCur & rgchh[ichhMid].dmplMask;
        else
            fPermitted = TRUE;
        if(fPermitted) {
            /* Make sure we're running on the right thread */
            if(!pthrServ || PsGetCurrentThread() == pthrServ ||
                (rgchh[ichhMid].dwFlags & CHH_ANYTHREAD))
            {
                hr = (*rgchh[ichhMid].pfnHandler)(sz, szResp, cchResp, pdmcc);
            } else
                hr = XBDM_MUSTBEDEDICATED;
        } else
            hr = XBDM_CANNOTACCESS;
    } else
        hr = XBDM_INVALIDCMD;
    return hr;
}

void DoSendReceive(int icst, BOOL fSend)
{
    HRESULT hr;
    SOCKET s = rgcst[icst].s;
    char szBuf[256];
    char szResp[256];

    if(!rgcst[icst].dmcc.Buffer) {
        *szBuf = 0;
        rgcst[icst].dmcc.Buffer = szBuf;
        rgcst[icst].dmcc.BufferSize = sizeof szBuf;
    }
    szResp[0] = 0;
    if(!fSend) {
        int cb;
        /* We need to receive data.  First, we attempt to take it from our
         * command receive buffer */
        cb = rgcst[icst].dwStatus & CONN_RECEIVED;
        if(cb) {
            int ib;
            int ibT;

            ib = cb;
            if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
                ib = rgcst[icst].dmcc.BytesRemaining;
            if((DWORD)cb > rgcst[icst].dmcc.BufferSize)
                ib = rgcst[icst].dmcc.BufferSize;
            if(ib) {
                rgcst[icst].dmcc.DataSize = ib;
                memcpy(rgcst[icst].dmcc.Buffer, rgcst[icst].szBuf, ib);
                for(ibT = 0; ib < cb; )
                    rgcst[icst].szBuf[ibT++] = rgcst[icst].szBuf[ib++];
                rgcst[icst].dwStatus = (rgcst[icst].dwStatus &
                    ~CONN_RECEIVED) | ibT;
            } else
                rgcst[icst].dmcc.DataSize = 0;
        } else {
            /* Receive as much data as we can */
            cb = rgcst[icst].dmcc.BufferSize;
            if((DWORD)cb > rgcst[icst].dmcc.BytesRemaining)
                cb = rgcst[icst].dmcc.BytesRemaining;
            if(cb) {
                cb = recv(s, rgcst[icst].dmcc.Buffer, cb, 0);
                if(cb <= 0) {
                    /* Socket is gone, so clean up and flag an error */
                    cb  = 0;
                    CloseConn(icst);
                }
            }
            rgcst[icst].dmcc.DataSize = cb;
        }
    }
    hr = rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc, szResp,
        sizeof szResp);
    if(fSend) {
        if(SUCCEEDED(hr)) {
            if(rgcst[icst].dmcc.DataSize == -1) {
                /* We have a string to send, so send it */
                PrintSockLine(s, rgcst[icst].dmcc.Buffer);
            } else if(rgcst[icst].dmcc.DataSize) {
                /* We have data to send, so send it */
                int cbSend = rgcst[icst].dmcc.DataSize;
                while(cbSend) {
                    int cbSent = send(s, (PBYTE)rgcst[icst].dmcc.Buffer +
                        rgcst[icst].dmcc.DataSize - cbSend, cbSend, 0);
                    if(cbSent <= 0) {
                        /* A send error occurred, so close the connection and
                         * tell the handling function we can accept no more
                         * data */
                        rgcst[icst].dmcc.BytesRemaining = 0;
                        rgcst[icst].dmcc.HandlingFunction(&rgcst[icst].dmcc,
                            szResp, sizeof szResp);
                        CloseConn(icst);
                        break;
                    } else
                        cbSend -= cbSent;
                }
            }
        } else if(hr == XBDM_ENDOFLIST) {
            /* We're out of data, so send end-of-data if appropriate and go
             * back to text mode */
            if(rgcst[icst].dwStatus & CONN_MULTILINE)
                PrintSockLine(s, ".");
            InitDmcc(icst);
        } else {
            /* An unexpected error has occurred.  Forcibly terminate the
             * connection */
            CloseConn(icst);
        }
    } else if(rgcst[icst].dmcc.BytesRemaining == 0) {
        /* If we've reached the end of our data stream, we can give the
         * appropriate response */
        LPCSTR pszResp;

        /* We may have an extended response, in which case we need to
         * prepare for it */
        rgcst[icst].dwStatus &= ~CONN_BINARY;
        rgcst[icst].dmcc.BytesRemaining = -1;
        switch(hr) {
        case XBDM_MULTIRESPONSE:
            rgcst[icst].dwStatus |= CONN_MULTILINE;
            rgcst[icst].dmcc.DataSize = -1;
            break;
        case XBDM_BINRESPONSE:
            rgcst[icst].dwStatus |= CONN_BINSEND;
            break;
        default:
            InitDmcc(icst);
            break;
        }
        if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
            !rgcst[icst].dmcc.HandlingFunction)
        {
            /* We gotta fail this */
            hr = E_UNEXPECTED;
            rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
            szResp[0] = 0;
        }

        pszResp = szResp[0] ? szResp : SzStdResponse(hr);
        SendHrSzResp(s, hr, pszResp, szBuf);
    }

    if(rgcst[icst].dmcc.Buffer == szBuf)
        rgcst[icst].dmcc.Buffer = NULL;
}

PECH PechFindHandler(LPCSTR sz, int cch)
{
    int iech;

    /* Look up the command processor */
    for(iech = 0; iech < MAX_ECH; ++iech) {
        if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, cch))
            return rgpech[iech];
    }
    return NULL;
}

void HandleCommand(int icst, LPCSTR sz)
{
    int cchCmd = CchOfWord(sz);
    HRESULT hr = 0;
    int ich;
    char szResp[512];
    LPCSTR pszResp;
    PDM_CMDCONT pdmcc;
    DWORD dmplCur;
    PECH pech = NULL;

    /* Make sure we're prepared to call this thing */
    if(icst >= 0) {
        pdmcc = &rgcst[icst].dmcc;
        InitDmcc(icst);
        dmplCur = rgcst[icst].dmplCur;
    } else {
        pdmcc = NULL;
        dmplCur = -1;
    }
    szResp[5] = 0;
    /* If this command belongs to an external command processor, we send it
     * there */
    for(ich = 0; ich < cchCmd; ++ich) {
        if(sz[ich] == '!') {
            BOOL fMatchingConnection;

            /* Look up the command processor */
            pech = PechFindHandler(sz, ich);
            if(pech) {
                /* All external commands require control permission */
                if(dmplCur & DMPL_PRIV_CONTROL) {
                    /* Make sure this handler was appropriately dedicated, if
                     * necessary */
                    if(pech == rgcst[icst].pechDedicated)
                        fMatchingConnection = TRUE;
                    else if(!pech->pfnCreateThread && !rgcst[icst].pechDedicated)
                        fMatchingConnection = TRUE;
                    else
                        fMatchingConnection = FALSE;
                    if(fMatchingConnection) {
                        hr = pech->pfnHandler(sz, szResp + 5,
                            sizeof szResp - 5, pdmcc);
                        if(!hr)
                            hr = XBDM_NOERR;
                    } else
                        hr = XBDM_MUSTBEDEDICATED;
                } else
                    hr = XBDM_CANNOTACCESS;
            } else {
                /* No command handler, can't do anything */
                hr = XBDM_INVALIDCMD;
                *szResp = 0;
            }
            break;
        } else if(FIsSpace(sz[ich]))
            break;
    }

    if(!hr) {
        /* Nobody has handled it yet, so we use our own internal handler.
         * Internal commands must be run on the server thread to avoid
         * possible deadlock if xapi is stopped */
        hr = HrHandleBuiltinCommand(dmplCur, sz, cchCmd, szResp + 5,
            sizeof szResp - 5, pdmcc);
    }

    if(hr && icst >= 0) {
        /* We have some sort of response to process.  First convert to a known
         * error code */
        if(szResp[5])
            pszResp = szResp;
        else
            pszResp = SzStdResponse(hr);

        /* We may have to indicate a special mode */
        switch(hr) {
        case XBDM_MULTIRESPONSE:
            rgcst[icst].dwStatus |= CONN_MULTILINE;
            rgcst[icst].dmcc.DataSize = -1;
            break;
        case XBDM_BINRESPONSE:
            rgcst[icst].dwStatus |= CONN_BINSEND;
            break;
        case XBDM_READYFORBIN:
            rgcst[icst].dwStatus |= CONN_BINARY;
            break;
        }
        if(rgcst[icst].dwStatus & (CONN_MULTILINE | CONN_BINSEND | CONN_BINARY) &&
            !rgcst[icst].dmcc.HandlingFunction)
        {
            /* We gotta fail this */
            hr = E_UNEXPECTED;
            rgcst[icst].dwStatus &= ~(CONN_MULTILINE | CONN_BINSEND | CONN_BINARY);
        }

        if(hr == XBDM_DEDICATED) {
            /* We need to spin off a thread to handle this connection */
            if(FDedicateConnection(icst, pech))
                /* The new thread will send the dedicated message */
                hr = 0;
            else
                hr = E_FAIL;
        }
    }

    if(hr && icst >= 0) {
        /* Now send our response */
        SendHrSzResp(rgcst[icst].s, hr, pszResp, szResp);

        if(hr == XBDM_READYFORBIN && pdmcc->BytesRemaining == 0)
            /* Zero-length receive, so fake up the receive now to end it */
            DoSendReceive(icst, FALSE);
    }
}

void AnswerName(SOCKET s)
{
    struct {
        BYTE bRequest;
        BYTE cchName;
        char szName[256];
    } nm;
    struct sockaddr_in sin;
    int cbAddr = sizeof sin;
    int cbPkt;

    cbPkt = recvfrom(s, (PVOID)&nm, sizeof nm, 0, (struct sockaddr *)&sin,
        &cbAddr);
    switch(nm.bRequest) {
    case 1:
    case 3:
        /* Name request.  1 is to match the name, 3 is to match anything */
        if(nm.bRequest == 1) {
            nm.bRequest = 0;
            /* Need to ensure more than two bytes received before we can
             * look at nm.cchName */
            if(cbPkt < 2 || cbPkt != 2 + nm.cchName)
                /* Ill-formed packet, do nothing */
                break;
            nm.szName[nm.cchName] = 0;
            /* Is it us? */
            if(!nm.cchName || !FEqualRgch(nm.szName, rgchDbgName, nm.cchName)
                    || rgchDbgName[nm.cchName] != 0)
                /* Not for us */
                break;
        }
        /* Answer the request */
        nm.bRequest = 2;
        for(nm.cchName = 0; rgchDbgName[nm.cchName]; ++nm.cchName)
            nm.szName[nm.cchName] = rgchDbgName[nm.cchName];
        break;
    default:
        /* We won't answer a request we don't recognize */
        nm.bRequest = 0;
        break;
    }
    if(nm.bRequest)
        sendto(s, (PVOID)&nm, nm.cchName + 2, 0, (struct sockaddr *)&sin,
            cbAddr);
}

void HandleSocketRead(int icst)
{
    if(rgcst[icst].dwStatus & CONN_BINARY)
        DoSendReceive(icst, FALSE);
    else {
        /* Text data coming in; read it */
        int cbRcv;
        int cbBuf;
        char *pchBuf;
        char rgbBufT[64];
        int ich;

        if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
            /* Overflowed the buffer, ignore the data */
            pchBuf = rgbBufT;
            cbBuf = MAX_CONN_BUF - sizeof rgbBufT;
        } else {
            cbBuf = rgcst[icst].dwStatus & CONN_RECEIVED;
            pchBuf = &rgcst[icst].szBuf[cbBuf];
        }
        cbRcv = recv(rgcst[icst].s, pchBuf, MAX_CONN_BUF -
            cbBuf, 0);
        if(cbRcv <= 0) {
            /* Connection is bad, give up */
            CloseConn(icst);
            return;
        }
        if(!(rgcst[icst].dwStatus & CONN_OVERFLOW)) {
            rgcst[icst].dwStatus += cbRcv;
            if((rgcst[icst].dwStatus & CONN_RECEIVED) ==
                    MAX_CONN_BUF)
                rgcst[icst].dwStatus |= CONN_OVERFLOW;
        }
        if(pchBuf != rgcst[icst].szBuf) {
            /* We're receiving additional data, so we may need to look
             * back one for the CR */
            --pchBuf;
            ++cbRcv;
        }
        /* Let's see if we got our end-of-line, which must be of the
         * form CR-LF or CR-0 */
        for(ich = 0; ich < cbRcv; ++ich)
            if(pchBuf[ich] == '\015')
                break;
        if(ich < cbRcv - 1) {
            /* Got EOL, so let's do our requisite processing */
            if(rgcst[icst].dwStatus & CONN_OVERFLOW) {
                PrintSockLine(rgcst[icst].s, "406- line too long");
                rgcst[icst].dwStatus &= ~(CONN_OVERFLOW | CONN_RECEIVED);
            } else {
                pchBuf[ich++] = 0;
                HandleCommand(icst, rgcst[icst].szBuf);
                if(rgcst[icst].s != INVALID_SOCKET) {
                    int ichNew = 0;
                    while(++ich < cbRcv)
                        rgcst[icst].szBuf[ichNew++] = pchBuf[ich];
                    rgcst[icst].dwStatus &= ~CONN_RECEIVED;
                    rgcst[icst].dwStatus |= ichNew;
                }
            }
        }
    }
}

DWORD DedicatedServThread(LPVOID lpv)
{
    int icst = (int)lpv;

    DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
    rgcst[icst].pthrDedicated = PsGetCurrentThread();

    while(rgcst[icst].s != INVALID_SOCKET && rgcst[icst].pthrDedicated ==
        PsGetCurrentThread())
    {
        fd_set fds;
        BOOL fSend;

        FD_ZERO(&fds);
        FD_SET(rgcst[icst].s, &fds);
        fSend = rgcst[icst].dwStatus & (CONN_BINSEND | CONN_MULTILINE);

        while(select(0, fSend ? NULL : &fds, fSend ? &fds : NULL, NULL, 0) == 0);
        if(!FD_ISSET(rgcst[icst].s, &fds))
            /* How did this happen? */
            CloseConn(icst);
        else if(fSend)
            DoSendReceive(icst, TRUE);
        else
            HandleSocketRead(icst);
    }
    return 0;
}


DWORD ServerThread(LPVOID pv)
{
	struct timeval timeout={0,000001};
	SOCKET sockListen;
    SOCKET sockName;
    int icst;
    struct sockaddr_in sin;
    fd_set fds;
    fd_set fdsSend;
    NTSTATUS st;
    char sz[128];
    PULARGE_INTEGER plu;
	struct sockaddr_in port_s_in;
	int server_socket;
	fd_set readfds;
	int life = 0;

	memset(&port_s_in,0,sizeof(port_s_in));   

	port_s_in.sin_family=AF_INET;
	port_s_in.sin_addr.s_addr = INADDR_ANY;
	port_s_in.sin_port=htons( 21 );

    server_socket=socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
	if(server_socket==SOCKET_ERROR)
	{
		return 0;
	}

	if(0 == bind(server_socket, (struct sockaddr *)&port_s_in, sizeof port_s_in))
	{
		return 0;
	}  
	while( listen(server_socket,5)==SOCKET_ERROR );


    //Boost the base priority of this thread so that the dm is response, especially
    //important xbcp.
    KeSetBasePriorityThread(KeGetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

    DmGetCurrentDmtd()->DebugFlags |= DMFLAG_DEBUGTHREAD;
    pthrServ = PsGetCurrentThread();

    for(icst = 0; icst < MAX_CONNECTIONS; ++icst)
        rgcst[icst].s = INVALID_SOCKET;
    sockListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockListen == INVALID_SOCKET)
        return 0;
    st = TRUE;
    if(0 != setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, (PVOID)&st,
        sizeof st))
    {
        _asm int 3
        closesocket(sockListen);
        return 0;
    }
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(DEBUGGER_PORT);
    if(0 != bind(sockListen, (struct sockaddr *)&sin, sizeof sin)) {
        _asm int 3
        closesocket(sockListen);
        return 0;
    }
    if(0 != listen(sockListen, MAX_CONNECTIONS)) {
        _asm int 3
        closesocket(sockListen);
        return 0;
    }

    /* Set up our name answering socket */
    sockName = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if(sockName != INVALID_SOCKET) {
        BOOL fReuse = TRUE;
        if(0 != setsockopt(sockName, SOL_SOCKET, SO_REUSEADDR, (PVOID)&fReuse,
            sizeof fReuse))
        {
NameErr:
            closesocket(sockName);
            sockName = INVALID_SOCKET;
        } else {
            sin.sin_family = AF_INET;
            sin.sin_addr.s_addr = 0;
            sin.sin_port = htons(DEBUGGER_PORT);
            if(0 != bind(sockName, (struct sockaddr *)&sin, sizeof sin))
                goto NameErr;
        }
    }

    // DbgPrint("dm: listening\n");
    KeSetEvent(&kevtServ, EVENT_INCREMENT, FALSE);

    /* Now we wait for connections and answer what's there */
    for(;;) {
        do {
			FD_ZERO(&fds);
            FD_ZERO(&fdsSend);
            FD_SET(sockListen, &fds);
            if(sockName != INVALID_SOCKET)
                FD_SET(sockName, &fds);
            for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
                if(rgcst[icst].s != INVALID_SOCKET &&
                    rgcst[icst].pthrDedicated == PsGetCurrentThread())
                {
                    FD_SET(rgcst[icst].s, rgcst[icst].dwStatus &
                        (CONN_BINSEND | CONN_MULTILINE) ? &fdsSend : &fds);
                }
            }
			PingAllXboxes();
			
        } while(select(0, &fds, &fdsSend, NULL, &timeout) <= 0);
//		} while(select(0, &fds, &fdsSend, NULL, NULL) <= 0);

        /* Accept new connections */
        if(FD_ISSET(sockListen, &fds)) {
            SOCKET sockNew;
            int cb = sizeof sin;
            sockNew = accept(sockListen, (struct sockaddr *)&sin, &cb);
            if(sockNew == INVALID_SOCKET)
                continue;
            EnterCriticalSection(&csAccept);
            if(g_fServShutdown)
                icst = MAX_CONNECTIONS;
            else {
                for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
                    if(rgcst[icst].s == INVALID_SOCKET)
                        break;
                }
            }
            if(icst < MAX_CONNECTIONS) {
                rgcst[icst].s = sockNew;
                rgcst[icst].pthrDedicated = pthrServ;
            }
            LeaveCriticalSection(&csAccept);
            if(icst == MAX_CONNECTIONS) {
                PrintSockLine(sockNew, "401- max number of connections exceeded");
                fAllowKd = TRUE; // we're going to need to debug if this happens
                closesocket(sockNew);
            } else {
                if(g_fLockLevel) {
                    rgcst[icst].dmplCur = 0;
                    plu = &rgcst[icst].luConnectNonce;
                    KeQuerySystemTime((PLARGE_INTEGER)plu);
                    _asm {
                        mov ecx, plu
                        rdtsc
                        xor [ecx], eax
                        xor [ecx+4], edx
                    }
                    sprintf(sz, "201- boxid=0q%08x%08x nonce=0q%08x%08x",
                        g_luBoxId.HighPart, g_luBoxId.LowPart, plu->HighPart,
                        plu->LowPart);
                    PrintSockLine(sockNew, sz);
                } else {
                    rgcst[icst].dmplCur = -1;
                    PrintSockLine(sockNew, "201- connected");
                }
                rgcst[icst].dwStatus = 0;
                InitDmcc(icst);
            }
        } else if(FD_ISSET(sockName, &fds)) {
            /* Answer name requests */
            AnswerName(sockName);
        } else for(icst = 0; icst < MAX_CONNECTIONS; ++icst) {
            if(rgcst[icst].s == INVALID_SOCKET)
                continue;
            if(FD_ISSET(rgcst[icst].s, &fdsSend)) {
                DoSendReceive(icst, TRUE);
                continue;
            }
            if(FD_ISSET(rgcst[icst].s, &fds))
                HandleSocketRead(icst);
        }
    }
}

BOOL FStartDebugService(void)
{
    NTSTATUS st;
    HANDLE hthr;

    st = PsCreateSystemThread(&hthr, NULL, ServerThread, NULL, TRUE);
    return NT_SUCCESS(st);
}

HRESULT DmSetXboxName(LPCSTR szName)
{
    if(szName) {
        strncpy(rgchDbgName, szName, sizeof rgchDbgName);
        rgchDbgName[sizeof rgchDbgName - 1] = 0;
    } else
        rgchDbgName[0] = 0;
    WriteIniFile();
    return XBDM_NOERR;
}

HRESULT DmGetXboxName(LPSTR szName, LPDWORD pcch)
{
    DWORD ich;

    if (!szName || !pcch)
        return E_INVALIDARG;

    for(ich = 0; rgchDbgName[ich] && ich < *pcch - 1; ++ich)
        *szName++ = rgchDbgName[ich];
    *szName = 0;
    return rgchDbgName[ich] ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
}

HRESULT DmRegisterCommandProcessor(LPCSTR sz, PDM_CMDPROC pfn)
{
    return DmRegisterCommandProcessorEx(sz, pfn, NULL);
}

HRESULT DmRegisterCommandProcessorEx(LPCSTR sz, PDM_CMDPROC pfn,
    PVOID pfnCreateThread)
{
    int ich;
    int iech;
    ECH *pech;
    HRESULT hr;

    if (!sz)
        return E_INVALIDARG;

    RtlEnterCriticalSection(&csEch);
    if(!pfn) {
        /* We're going to unregister, so find the matching handler and pull
         * it */
        for(ich = 0; sz[ich]; ++ich);
        for(iech = 0; iech < MAX_ECH; ++iech) {
            if(rgpech[iech] && FEqualRgch(rgpech[iech]->szExtName, sz, ich)) {
                DmFreePool(rgpech[iech]);
                rgpech[iech] = NULL;
            }
        }
        hr = XBDM_NOERR;
    } else {
        /* Let's find somewhere to put it */
        for(iech = 0; iech < MAX_ECH; ++iech)
            if(!rgpech[iech])
                break;
        if(iech < MAX_ECH) {
            pech = DmAllocatePoolWithTag(sizeof *pech, 'hcmd');
        } else
            pech = NULL;
        if(pech) {
            for(ich = 0; sz[ich] && ich < sizeof pech->szExtName - 1; ++ich)
                pech->szExtName[ich] = sz[ich];
            if(sz[ich]) {
                DmFreePool(pech);
                hr = E_INVALIDARG;
            } else {
                pech->szExtName[ich] = 0;
                pech->pfnHandler = pfn;
                pech->pfnCreateThread = pfnCreateThread;
                rgpech[iech] = pech;
                hr = XBDM_NOERR;
            }
        } else
            hr = E_OUTOFMEMORY;
    }
    RtlLeaveCriticalSection(&csEch);
    return hr;
}

HRESULT HrDedicateConnection(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szHandler[256];
    int icst;
    PECH pech;

    if(!pdmcc)
        return E_INVALIDARG;

    if(PchGetParam(sz, "global", FALSE))
        pech = NULL;
    else if(!FGetSzParam(sz, "handler", szHandler, sizeof szHandler)) {
        strcpy(szResp, "missing handler");
        return E_FAIL;
    } else {
        pech = PechFindHandler(szHandler, strlen(szHandler));
        if(!pech)
            return XBDM_NOSUCHFILE;
    }

    icst = (PCST)pdmcc - rgcst;
    /* Rededicate the connection.  If this is successful, the success
     * message will be sent in the new thread */
    return FDedicateConnection(icst, pech) ? 0 : E_FAIL;
}

HRESULT DmThreadUserData(DWORD tid, PDWORD *ppdwRet)
{
    PETHREAD pthr;
    NTSTATUS st;
    HRESULT hr = XBDM_NOTHREAD;

    if (!ppdwRet)
        return E_INVALIDARG;

    if(tid == DM_CURRENT_THREAD) {
        pthr = PsGetCurrentThread();
        ObfReferenceObject(pthr);
        st = STATUS_SUCCESS;
    } else
        st = PsLookupThreadByThreadId((HANDLE)tid, &pthr);
    if(NT_SUCCESS(st)) {
        DMTD *pdmtd = DmGetCurrentDmtd();
        if(pdmtd) {
            *ppdwRet = &pdmtd->UserData;
            hr = XBDM_NOERR;
        }
        ObDereferenceObject(pthr);
    }
    return hr;
}

HRESULT HrEndConversation(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    int icst;

    if(!pdmcc)
        return E_INVALIDARG;
    icst = (PCST)pdmcc - rgcst;
    PrintSockLine(rgcst[icst].s, "200- bye");
    CloseConn(icst);
    return 0;
}

HRESULT HrSetDbgName(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szNewName[sizeof rgchDbgName];
    HRESULT hr;
    KIRQL irqlSav;

    if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
        /* We're querying for the current name */
        strcpy(szResp, rgchDbgName);
        hr = XBDM_NOERR;
    } else if(!FConnectionPermission(pdmcc, DMPL_PRIV_CONFIGURE))
        hr = XBDM_CANNOTACCESS;
    else {
        irqlSav = KeRaiseIrqlToDpcLevel();
        memcpy(rgchDbgName, szNewName, sizeof rgchDbgName);
        hr = XBDM_NOERR;
        KeLowerIrql(irqlSav);
        WriteIniFile();
    }
    return hr;
}

HRESULT HrToggleGPUCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    HRESULT hr;

    if(PchGetParam(sz, "enable", FALSE))
        hr = DmEnableGPUCounter(TRUE);
    else if(PchGetParam(sz, "disable", FALSE))
        hr = DmEnableGPUCounter(FALSE);
    return hr;
}

HRESULT HrToggleKDState(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    HRESULT hr = XBDM_NOERR;

    if(PchGetParam(sz, "enable", FALSE)) {
        fAllowKd = TRUE;
        strcpy(szResp, "kd enabled");
    } else if(PchGetParam(sz, "disable", FALSE)) {
        fAllowKd = FALSE;
        strcpy(szResp, "kd disabled");
    } else if(PchGetParam(sz, "except", FALSE)) {
        fAllowKd = TRUE;
        ExceptionsToKd();
    } else if(PchGetParam(sz, "exceptif", FALSE)) {
        /* Forward exceptions to KD only if KD is connected */
        if(*KdDebuggerEnabled) {
            fAllowKd = TRUE;
            ExceptionsToKd();
        }
    } else {
        strcpy(szResp, "improper command");
        hr = E_FAIL;
    }

    return hr;
}

HRESULT HrSetupNotifyAt(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    int icst;

    if(!pdmcc)
        return E_FAIL;
    icst = (PCST)pdmcc - rgcst;
    hr = FNotifyAtCmd(rgcst[icst].s, sz, FALSE) ? XBDM_NOERR :
        E_FAIL;
    /* Keep the ini file up-to-date */
    if(SUCCEEDED(hr))
        WriteIniFile();
    return hr;
}

HRESULT HrSetupNotify(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    int icst;
    HRESULT hr;

    if(!pdmcc)
        return E_FAIL;
    icst = (PCST)pdmcc - rgcst;

    if(FConvertToNotifySock(rgcst[icst].s)) {
        /* This socket is no longer our responsibility */
        rgcst[icst].s = INVALID_SOCKET;
        hr = 0;
    } else {
        strcpy(szResp, "notification limit exceeded");
        hr = E_FAIL;
    }
    return hr;
}

HRESULT HrDoStopon(BOOL fStop, LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD dw = 0;

    if(PchGetParam(sz, "all", FALSE)) {
        dw = -1;
        strcpy(szResp, fStop ? "stop on all" : "stop on none");
    } else {
        if(PchGetParam(sz, "fce", FALSE))
            dw |= DMSTOP_FCE;
        if(PchGetParam(sz, "debugstr", FALSE))
            dw |= DMSTOP_DEBUGSTR;
        if(PchGetParam(sz, "createthread", FALSE))
            dw |= DMSTOP_CREATETHREAD;
    }
    return DmStopOn(dw, fStop);
}

HRESULT HrStopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    return HrDoStopon(TRUE, sz, szResp, cchResp, pdmcc);
}

HRESULT HrNostopon(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    return HrDoStopon(FALSE, sz, szResp, cchResp, pdmcc);
}

HRESULT HrGo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    if(dwExecState == DMN_EXEC_START) {
        sprintf(szResp, "not stopped");
        return E_FAIL;
    }
    DmGo();
    return XBDM_NOERR;
}

HRESULT HrStop(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    if(dwExecState != DMN_EXEC_START) {
        sprintf(szResp, "already stopped");
        return E_FAIL;
    }
    DmStop();
    return XBDM_NOERR;
}

BOOL FTitleExists(LPCSTR szDir, LPCSTR szTitle, LPCSTR szCmdLine, BOOL fMapIt)
{
    HANDLE h;
    OBJECT_ATTRIBUTES oa;
    OBJECT_STRING objectName;
    IO_STATUS_BLOCK iosb;
    BOOL fRet;
    NTSTATUS st;
    OCHAR oszPath[MAX_OBJ_PATH+1];
    OCHAR *pochTitle;
    OCHAR *pochMax;
    int ich;

    if(!szDir || !szDir[0]) {
		/* No dir name, so we expect szTitle to have the complete path name
		 * to the title */
		if(!szTitle || !FFileNameToObName(szTitle, oszPath, sizeof oszPath /
			sizeof(OCHAR)))
		{
			return FALSE;
		}
		pochTitle = NULL;
	} else {
		if(!FFileNameToObName(szDir, oszPath, sizeof oszPath / sizeof(OCHAR)))
			return FALSE;
		pochTitle = oszPath;
	}
    if(!szTitle)
        szTitle = "default.xbe";

	if(pochTitle) {
		/* Construct a complete path for the title and remember where both
		 * parts go */
		pochMax = oszPath + sizeof oszPath / sizeof(OCHAR);
		while(*pochTitle)
			++pochTitle;
		/* If we're in a root dir, don't add the extra '\' */
		if(pochTitle > oszPath && pochTitle[-1] != '\\')
			*pochTitle++ = '\\';
		for(ich = 0; &pochTitle[ich] < pochMax && szTitle[ich]; ++ich)
			pochTitle[ich] = szTitle[ich];
		if(&pochTitle[ich] >= pochMax - 1)
			return FALSE;
		pochTitle[ich] = 0;
	}

    /* Verify the file exists */
    RtlInitObjectString(&objectName, oszPath);
    InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
        NULL);
    st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
    fRet = NT_SUCCESS(st);
    if(fRet) {
        NtClose(h);
        if(fMapIt) {
			if(pochTitle) {
				pochTitle[-1] = 0;
				pochMax = oszPath;
			} else {
				pochMax = NULL;
				pochTitle = oszPath;
			}
            /* We require that the command line passed in is the size of the
             * launch data, so we don't need to do any copying here */
            fRet = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, pochMax,
                szCmdLine ? LDT_FROM_DEBUGGER_CMDLINE : LDT_NONE, 0,
                (PLAUNCH_DATA) szCmdLine));
        }
    }
    return fRet;
}

HRESULT HrReceiveFileData(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    FILE_DISPOSITION_INFORMATION fdi;
    IO_STATUS_BLOCK iosb;

    if(pdmcc->DataSize) {
        if(pccs->rgbBuf)
            /* Digest the data in the buffer */
            pccs->ib += pdmcc->DataSize;
        else
            pccs->ib = DISK_BUF_SIZE;
        pdmcc->BytesRemaining -= pdmcc->DataSize;
    } else {
        /* If there's no data, then either we had a zero-length file, or
         * we've lost our socket.  In the latter case, we need to delete
         * the file */
        if(pdmcc->BytesRemaining) {
            pdmcc->BytesRemaining = 0;
            fdi.DeleteFile = TRUE;
            NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
                FileDispositionInformation);
        }
    }

    /* See if we're ready for a disk write */
    if(!pdmcc->BytesRemaining || pccs->ib == DISK_BUF_SIZE) {
        NTSTATUS st;

        if(pccs->hFile) {
            st = NtWriteFile(pccs->hFile, NULL, NULL, NULL, &iosb,
                pccs->rgbBuf, pccs->ib, NULL);
            /* Close the file if we're out of data */
            if(!NT_SUCCESS(st)) {
                /* If an error, try to delete the file and signal an error
                 * condition */
                fdi.DeleteFile = TRUE;
                NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
                    FileDispositionInformation);
                NtClose(pccs->hFile);
                pccs->hFile = NULL;
                pccs->fError = TRUE;
            } else if(!pdmcc->BytesRemaining) {
                NtClose(pccs->hFile);
                pccs->hFile = NULL;
            }
        }
        pccs->ib = 0;
        if(!pdmcc->BytesRemaining && pccs->rgbBuf)
            /* Clean up our buffer */
            DmFreePool(pccs->rgbBuf);
    }

    /* Make sure the buffer pointer is set up */
    if(pccs->rgbBuf) {
        pdmcc->Buffer = pccs->rgbBuf + pccs->ib;
        pdmcc->BufferSize = DISK_BUF_SIZE - pccs->ib;
    }

    return pccs->fError ? E_UNEXPECTED : XBDM_NOERR;
}

HRESULT HrReceiveFile(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    LARGE_INTEGER liSize;
    NTSTATUS st;
    char szName[MAX_OBJ_PATH+1];
    CCS *pccs;
    IO_STATUS_BLOCK iosb;
    FILE_END_OF_FILE_INFORMATION feof;
    FILE_DISPOSITION_INFORMATION fdi;
    HRESULT hr;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }
    if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
        return E_FAIL;

    pccs = (CCS *)pdmcc->CustomData;
    liSize.HighPart = 0;
    liSize.LowPart = pdmcc->BytesRemaining;

    st = FCreateFile(&pccs->hFile, GENERIC_WRITE | DELETE | SYNCHRONIZE,
        szName, &liSize, 0, 0, FILE_OVERWRITE_IF,
        FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        /* Set the size of the file now */
        feof.EndOfFile = liSize;
        st = NtSetInformationFile(pccs->hFile, &iosb, &feof, sizeof(feof),
            FileEndOfFileInformation);
        if(NT_SUCCESS(st)) {
            /* Make sure we'll have a buffer for received data */
            pccs->ib = 0;
            pccs->rgbBuf = DmAllocatePoolWithTag(DISK_BUF_SIZE, 'bdbd');
            /* Even if the alloc fails, we'll still have a small static buffer */
            pdmcc->Buffer = pccs->rgbBuf;
            pdmcc->BufferSize = DISK_BUF_SIZE;
            pccs->fError = FALSE;
            pdmcc->HandlingFunction = HrReceiveFileData;
            return XBDM_READYFORBIN;
        } else {
            /* Mark the file for deletion and close the file */
            fdi.DeleteFile = TRUE;
            NtSetInformationFile(pccs->hFile, &iosb, &fdi, sizeof(fdi),
                FileDispositionInformation);
            NtClose(pccs->hFile);
            pccs->hFile = NULL;
        }
    }
    return HrFromStatus(st, XBDM_CANNOTCREATE);
}

HRESULT HrSendFileData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    if(pdmcc->BytesRemaining == 0) {
        /* End of file */
        NtClose(pccs->hFile);
        if(pccs->rgbBuf)
            DmFreePool(pccs->rgbBuf);
        return XBDM_ENDOFLIST;
    }
    if(!pccs->fDidSize) {
        /* First we need to send the length */
        pdmcc->BytesRemaining = pccs->ib;
        memcpy(pdmcc->Buffer, &pccs->ib, sizeof(DWORD));
        pdmcc->DataSize = sizeof(DWORD);
        pccs->fDidSize = TRUE;
    } else {
        NTSTATUS st;
        IO_STATUS_BLOCK iosb;
        DWORD cb;

        cb = pdmcc->BufferSize;
        if(cb > pdmcc->BytesRemaining)
            cb = pdmcc->BytesRemaining;
        st = NtReadFile(pccs->h, NULL, NULL, NULL, &iosb, pdmcc->Buffer, cb,
            NULL);
        if(!NT_SUCCESS(st))
            // BUG BUG -- need to signal read failure
            RtlZeroMemory(pdmcc->Buffer, cb);
        pdmcc->BytesRemaining -= cb;
        pdmcc->DataSize = cb;
    }
    return XBDM_NOERR;
}

HRESULT HrSendFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    IO_STATUS_BLOCK iosb;
    FILE_NETWORK_OPEN_INFORMATION ni;
    char szName[MAX_OBJ_PATH+1];
    CCS *pccs;
    HRESULT hr;
    int icst;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    pccs = (CCS *)pdmcc->CustomData;
    st = FCreateFile(&pccs->hFile, GENERIC_READ | SYNCHRONIZE, szName,
                    NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
                    FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        st = NtQueryInformationFile(pccs->hFile, &iosb, &ni, sizeof ni,
            FileNetworkOpenInformation);
        if(NT_SUCCESS(st)) {
            if(ni.EndOfFile.HighPart) {
                /* File is too big to transfer */
                sprintf(szResp, "file is too big");
                hr = E_FAIL;
            } else {
                /* Get a buffer for sending if we can */
                pdmcc->BufferSize = 1024;
                pccs->rgbBuf = pdmcc->Buffer = DmAllocatePoolWithTag(
                    pdmcc->BufferSize, 'bdbd');
                pccs->ib = ni.EndOfFile.LowPart;
                pccs->fDidSize = FALSE;
                pdmcc->BytesRemaining = 1;
                pdmcc->HandlingFunction = HrSendFileData;
                return XBDM_BINRESPONSE;
            }
        } else
            hr = E_UNEXPECTED;
        NtClose(pccs->h);
    } else
        hr = HrFromStatus(st, XBDM_CANNOTACCESS);
    return hr;
}

void GetFileAttrSz(LPSTR sz, ULONG dwAttributes, PLARGE_INTEGER pliChange,
    PLARGE_INTEGER pliCreate, PLARGE_INTEGER pliSize)
{
    sprintf(sz, "sizehi=0x%x sizelo=0x%x createhi=0x%08x createlo=0x%08x "
        "changehi=0x%08x changelo=0x%08x%s%s%s", pliSize->HighPart,
        pliSize->LowPart, pliCreate->HighPart, pliCreate->LowPart,
        pliChange->HighPart, pliChange->LowPart,
        dwAttributes & FILE_ATTRIBUTE_DIRECTORY ? " directory" : "",
        dwAttributes & FILE_ATTRIBUTE_READONLY ? " readonly" : "",
        dwAttributes & FILE_ATTRIBUTE_HIDDEN ? " hidden" : "");
}

HRESULT HrReportFileAttributes(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    for(;;) {
        switch(pdmcc->BytesRemaining++) {
        case 1:
            GetFileAttrSz(pdmcc->Buffer, pccs->fba.FileAttributes,
                &pccs->fba.LastWriteTime, &pccs->fba.CreationTime,
                &pccs->fba.EndOfFile);
            return XBDM_NOERR;
        default:
            return XBDM_ENDOFLIST;
        }
    }
}

HRESULT HrGetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    OCHAR oszFilename[MAX_OBJ_PATH+1];
    NTSTATUS st;
    OBJECT_STRING objFilename;
    OBJECT_ATTRIBUTES oa;
    char szName[MAX_OBJ_PATH+1];
    CCS *pccs;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    /* Turn the filename into an object manager name*/
    if(!FFileNameToObName(szName, oszFilename, sizeof oszFilename /
            sizeof(OCHAR)))
        return XBDM_NOSUCHFILE;
    RtlInitObjectString(&objFilename, oszFilename);
    InitializeObjectAttributes(&oa, &objFilename, OBJ_CASE_INSENSITIVE, NULL,
        NULL);

    pccs = (CCS *)pdmcc->CustomData;
    pdmcc->HandlingFunction = HrReportFileAttributes;
    pdmcc->BytesRemaining = 1;
    st = NtQueryFullAttributesFile(&oa, &pccs->fba);
    return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
        XBDM_CANNOTACCESS);
}

HRESULT HrSetFileAttributes(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    HANDLE h;
    IO_STATUS_BLOCK iosb;
    char szName[MAX_OBJ_PATH+1];
    HRESULT hr;

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    st = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
        FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
        FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        FILE_NETWORK_OPEN_INFORMATION fna;

        st = NtQueryInformationFile(h, &iosb, &fna, sizeof fna,
            FileNetworkOpenInformation);
        if(NT_SUCCESS(st)) {
            FILE_BASIC_INFORMATION fba;
            DWORD dwAttrMask = 0;
            DWORD dwAttrNew = 0;
            DWORD dwT;

            RtlZeroMemory(&fba, sizeof fba);
            if(FGetDwParam(sz, "readonly", &dwT)) {
                dwAttrMask |= FILE_ATTRIBUTE_READONLY;
                if(dwT)
                    dwAttrNew |= FILE_ATTRIBUTE_READONLY;
            }
            if(FGetDwParam(sz, "hidden", &dwT)) {
                dwAttrMask |= FILE_ATTRIBUTE_HIDDEN;
                if(dwT)
                    dwAttrNew |= FILE_ATTRIBUTE_HIDDEN;
            }
            FGetDwParam(sz, "createhi", &fba.CreationTime.HighPart);
            FGetDwParam(sz, "createlo", &fba.CreationTime.LowPart);
            FGetDwParam(sz, "changehi", &fba.LastWriteTime.HighPart);
            FGetDwParam(sz, "changelo", &fba.LastWriteTime.LowPart);
            fba.FileAttributes = fna.FileAttributes ^ ((fna.FileAttributes ^
                dwAttrNew) & dwAttrMask);
            if(!fba.FileAttributes)
                fba.FileAttributes = FILE_ATTRIBUTE_NORMAL;
            st = NtSetInformationFile(h, &iosb, &fba, sizeof fba,
                FileBasicInformation);
            hr = NT_SUCCESS(st) ? XBDM_NOERR : HrFromStatus(st, E_FAIL);
        }
        NtClose(h);
    } else
        hr = HrFromStatus(st, XBDM_CANNOTACCESS);
    return hr;
}

HRESULT HrRenameFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    HANDLE h;
    IO_STATUS_BLOCK iosb;
    char  szName[MAX_OBJ_PATH+1];
    OCHAR oszNewName[MAX_OBJ_PATH+1];
    FILE_RENAME_INFORMATION fri;
    HRESULT hr;

    if(!FGetSzParam(sz, "newname", szName, sizeof szName)) {
        strcpy(szResp, "missing new name");
        return E_FAIL;
    }
    if(!FFileNameToObName(szName, oszNewName, MAX_OBJ_PATH+1)) {
        strcpy(szResp, "new name does not exist");
        return XBDM_NOSUCHFILE;
    }

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    /* Get a handle to the original file */
    st = FCreateFile(&h, GENERIC_WRITE | SYNCHRONIZE, szName, NULL, 0, 0,
                    FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        RtlZeroMemory(&fri, sizeof fri);
        RtlInitObjectString(&fri.FileName, oszNewName);
        st = NtSetInformationFile(h, &iosb, &fri, sizeof fri,
             FileRenameInformation);
        NtClose(h);
        switch(st) {
        case STATUS_NOT_SAME_DEVICE:
            hr = XBDM_MUSTCOPY;
            break;
        default:
            if(NT_SUCCESS(st))
                hr = XBDM_NOERR;
            else
                hr = HrFromStatus(st, XBDM_CANNOTCREATE);
            break;
        }
    } else
        hr = XBDM_CANNOTACCESS;

    return hr;
}

HRESULT HrDeleteFile(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    HANDLE h;
    IO_STATUS_BLOCK iosb;
    FILE_DISPOSITION_INFORMATION fdi;
    char szName[MAX_OBJ_PATH+1];
    HRESULT hr;
    BOOL fIsDirectory;
    char *pch;

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }
    fIsDirectory = PchGetParam(sz, "dir", FALSE) != NULL;

    /* We're going to prohibit the deletion of any drives */
    pch = szName;
    if(*pch == 'x' || *pch == 'X')
        ++pch;
    if(pch[1] == ':' && pch[2] == '\\' && pch[3] == 0)
        /* Looks like a drive name */
        return XBDM_CANNOTACCESS;

    st = FCreateFile(&h, DELETE | SYNCHRONIZE, szName, NULL, 0, 0,
        FILE_OPEN, fIsDirectory ?
        FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE :
        FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE);
    if(NT_SUCCESS(st)) {
        RtlZeroMemory(&fdi, sizeof fdi);
        fdi.DeleteFile = TRUE;
        st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
            FileDispositionInformation);
        if(NT_SUCCESS(st))
            hr = XBDM_NOERR;
        else
            hr = HrFromStatus(st, XBDM_CANNOTACCESS);
        NtClose(h);
    } else {
        switch(st) {
        case STATUS_NOT_A_DIRECTORY :
            sprintf(szResp, "not a directory");
            hr = XBDM_CANNOTACCESS;
            break;
        case STATUS_FILE_IS_A_DIRECTORY:
            sprintf(szResp, "is a directory");
            hr = XBDM_CANNOTACCESS;
            break;
        default:
            hr = HrFromStatus(st, XBDM_CANNOTACCESS);
            break;
        }
    }

    return hr;
}

HRESULT HrReportDirList(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    NTSTATUS st;
    IO_STATUS_BLOCK iosb;
    struct
    {
        FILE_DIRECTORY_INFORMATION fna;
        OCHAR ozName[MAX_OBJ_PATH+1];
    } fna;
    CCS *pccs = (CCS *)pdmcc->CustomData;
    HRESULT hr;
    LPSTR szResp;

    st = NtQueryDirectoryFile(pccs->h, NULL, NULL, NULL, &iosb,
        &fna.fna, sizeof fna, FileDirectoryInformation,
        NULL, (BOOLEAN)(pccs->i == 0));
    if(pdmcc->BytesRemaining && NT_SUCCESS(st))
    {
        ULONG ich;

        ++pccs->i;

        fna.fna.FileNameLength /= sizeof(OCHAR); // from bytes to cwch
        if(fna.fna.FileNameLength >= pdmcc->BufferSize)
            fna.fna.FileNameLength = pdmcc->BufferSize - 1;
        strcpy(pdmcc->Buffer, "name=\"");
        for(szResp = pdmcc->Buffer; *szResp; ++szResp);
        for(ich = 0; ich < fna.fna.FileNameLength; ++ich)
            szResp[ich] = (char)fna.fna.FileName[ich];
        szResp[ich++] = '"';
        szResp[ich++] = ' ';
        GetFileAttrSz(szResp + ich, fna.fna.FileAttributes,
            &fna.fna.LastWriteTime, &fna.fna.CreationTime, &fna.fna.EndOfFile);
        hr = XBDM_NOERR;
    } else {
        NtClose(pccs->h);
        DmFreePool(pdmcc->Buffer);
        hr = XBDM_ENDOFLIST;
    }
    return hr;
}

HRESULT HrGetDirList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    char szName[MAX_OBJ_PATH+1];
    CCS *pccs;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    pccs = (CCS *)pdmcc->CustomData;
    pccs->i = 0;
    pdmcc->BufferSize = 512;
    pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
    if(!pdmcc->Buffer)
        /* We can't fall back on the default 256-byte buffer in this case */
        return E_OUTOFMEMORY;
    pdmcc->HandlingFunction = HrReportDirList;
    pdmcc->DataSize = -1;

    st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
                FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);

    return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
        XBDM_CANNOTACCESS);
}

HRESULT HrMkdir(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    HANDLE h;
    char szName[MAX_OBJ_PATH+1];

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    st = FCreateFile(&h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName,
        NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE, FILE_DIRECTORY_FILE |
        FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        NtClose(h);
        return XBDM_NOERR;
    } else if(st == STATUS_OBJECT_NAME_COLLISION)
        return XBDM_ALREADYEXISTS;
    else
        return HrFromStatus(st, XBDM_CANNOTCREATE);
}

HRESULT HrFormatFAT(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    DWORD dwPartition;
    OCHAR szPartition[MAX_OBJ_PATH + 1];
    OBJECT_STRING ost;

    if(dwExecState != DMN_EXEC_PENDING) {
        strcpy(szResp, "not stopped");
        return E_FAIL;
    }
    if(!FGetNamedDwParam(sz, "partition", &dwPartition, szResp))
        return E_FAIL;
    if(dwPartition < 1 || dwPartition > 5)
        return XBDM_NOSUCHFILE;

    sprintf(szPartition, "\\Device\\Harddisk0\\Partition%d", dwPartition);
    RtlInitObjectString(&ost, szPartition);
    if(XapiFormatFATVolume(&ost))
        return XBDM_NOERR;

    strcpy(szResp, "unable to format");
    return XBDM_CANNOTACCESS;
}

HRESULT DmSetTitle(LPCSTR szDir, LPCSTR szTitle, LPCSTR pchCmdLine)
{
    if (!szTitle)
        return E_INVALIDARG;

    if(pchCmdLine) {
        KIRQL irql = KeRaiseIrqlToDpcLevel();
        if(pszCmdLine)
            DmFreePool(pszCmdLine);
        pszCmdLine = DmAllocatePool(sizeof(LD_FROM_DEBUGGER_CMDLINE));
        if(pszCmdLine)
            strcpy(pszCmdLine, pchCmdLine);
        KeLowerIrql(irql);
        if(!pszCmdLine)
            return E_OUTOFMEMORY;
    }

    if(!FTitleExists(szDir, szTitle, NULL, FALSE))
        return XBDM_CANNOTACCESS;
	if(szDir)
		strcpy(rgchTitleDir, szDir);
	else
		rgchTitleDir[0] = 0;
    strcpy(rgchTitleName, szTitle);
    return XBDM_NOERR;
}

HRESULT HrSetTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szNewDir[sizeof rgchTitleDir];
    char szNewName[sizeof rgchTitleName];
	char *pszDir;

    pszDir = FGetSzParam(sz, "dir", szNewDir, sizeof szNewDir) ?
		szNewDir : NULL;
    if(!FGetSzParam(sz, "name", szNewName, sizeof szNewName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }
    return DmSetTitle(pszDir, szNewName, PchGetParam(sz, "cmdline", TRUE));
}

HRESULT HrSetDefaultTitle(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szLaunchData[MAX_LAUNCH_PATH + 1];
    char szLaunchDir[MAX_PATH + 1];
    NTSTATUS st;
    HANDLE h;
    IO_STATUS_BLOCK iosb;
    HRESULT hr;
    char *pch;
    OBJECT_STRING ost;
    OBJECT_ATTRIBUTES oa;
    FILE_DISPOSITION_INFORMATION fdi;

    // Make sure dashboard.xbx file isn't read-only
    InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
    st = NtOpenFile(&h, FILE_WRITE_ATTRIBUTES|SYNCHRONIZE, &oa, &iosb, 0,
            FILE_SYNCHRONOUS_IO_NONALERT);

    if (NT_SUCCESS(st)) {
        FILE_BASIC_INFORMATION BasicInfo;
        RtlZeroMemory(&BasicInfo,sizeof(BasicInfo));
        BasicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
        NtSetInformationFile(h, &iosb, &BasicInfo, sizeof(BasicInfo), FileBasicInformation);
        NtClose(h);
    }

    if(PchGetParam(sz, "none", FALSE)) {
        // Set the default title to be the dashboard
        InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
        st = NtOpenFile(&h, DELETE | SYNCHRONIZE, &oa, &iosb, 0,
            FILE_SYNCHRONOUS_IO_NONALERT);
        if(NT_SUCCESS(st)) {
            RtlZeroMemory(&fdi, sizeof fdi);
            fdi.DeleteFile = TRUE;
            st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
                FileDispositionInformation);
            hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
            NtClose(h);
        } else
            hr = XBDM_NOERR;
        return hr;
    }

    RtlZeroMemory(szLaunchData, sizeof szLaunchData);

    if (PchGetParam(sz, "launcher", FALSE)) {
        // Set the default title to be the xdk launcher
        strcpy(szLaunchData, XdkLauncherPathname);
        pch = strrchr(szLaunchData, '\\');
        ASSERT(pch != NULL);
        pch += 1;
    } else {
        // Set the default title to be the specified XBE
        if(!FGetSzParam(sz, "dir", szLaunchDir, sizeof szLaunchDir - 1)) {
            strcpy(szResp, "missing dir");
            return E_FAIL;
        }
        if(!FFileNameToObName(szLaunchDir, szLaunchData, sizeof szLaunchData - 1))
            return XBDM_NOSUCHFILE;
        for(pch = szLaunchData; *pch; ++pch);
        if(pch[-1] != '\\')
            *pch++ = '\\';
        if(!FGetSzParam(sz, "name", pch, szLaunchData + (sizeof szLaunchData - 1) - pch))
        {
            strcpy(szResp, "missing name");
            return E_FAIL;
        }
    }

    /* Verify the file exists */
    RtlInitObjectString(&ost, szLaunchData);
    InitializeObjectAttributes(&oa, &ost, OBJ_CASE_INSENSITIVE, NULL, NULL);
    st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
    if(!NT_SUCCESS(st))
        return HrFromStatus(st, XBDM_CANNOTACCESS);
    NtClose(h);

    /* We've found the file, so now write out the name */
    pch[-1] = TITLE_PATH_DELIMITER;
    InitializeObjectAttributes(&oa, &ostDash, OBJ_CASE_INSENSITIVE, NULL, NULL);
    st = NtCreateFile(&h, GENERIC_WRITE | DELETE | SYNCHRONIZE,
        &oa, &iosb, NULL, 0, 0, FILE_OVERWRITE_IF,
        FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        st = NtWriteFile(h, NULL, NULL, NULL, &iosb, szLaunchData, sizeof
            szLaunchData - 1, NULL);
        if(!NT_SUCCESS(st)) {
            /* Can't write, so try to delete */
            hr = HrFromStatus(st, E_UNEXPECTED);
            RtlZeroMemory(&fdi, sizeof fdi);
            fdi.DeleteFile = TRUE;
            st = NtSetInformationFile(h, &iosb, &fdi, sizeof fdi,
                FileDispositionInformation);
            hr = NT_SUCCESS(st) ? XBDM_NOERR : E_UNEXPECTED;
        } else
            hr = XBDM_NOERR;
        NtClose(h);
    } else
        hr = E_FAIL;
    return hr;
}

HRESULT HrReportMemory(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    /* We print 32 bytes per line, which takes 64 characters + 2 newline chars
     * (66 chars) to constitute a line */
    DWORD cb;
    DWORD dwPageBase;
    BYTE b;
    DWORD ibLim = 32;
    DWORD ib;
    LPSTR sz = pdmcc->Buffer;
    BOOL fPageValid;

    if(!pdmcc->BytesRemaining) {
        /* No more data */
        if(pdmcc->Buffer && pdmcc->BufferSize == 1056) {
            DmFreePool(pdmcc->Buffer);
            pdmcc->Buffer = NULL;
        }
        return XBDM_ENDOFLIST;
    }

    cb = pdmcc->BufferSize / 66;
    if(cb > pdmcc->BytesRemaining)
        cb = pdmcc->BytesRemaining;
    dwPageBase = (DWORD)pccs->pb + 0x1000;
    pdmcc->BytesRemaining -= cb;

    while(cb) {
        ibLim = 32;

        if(cb < ibLim)
            ibLim = cb;
        for(ib = 0; ib < ibLim; ++ib, ++pccs->pb) {
            if(((DWORD)pccs->pb ^ dwPageBase) & 0xfffff000) {
                dwPageBase = (DWORD)pccs->pb & 0xfffff000;
                fPageValid = MmIsAddressValid(pccs->pb);
            }
            if(fPageValid)
                /* If this address has an associated breakpoint, then the
                 * memory we want isn't the int 3, but the breakpoint data */
                fPageValid = FGetMemory(pccs->pb, &b);
            if(fPageValid) {
                sz[2*ib] = rgchHex[(b >> 4) & 0xf];
                sz[2*ib+1] = rgchHex[b & 0xf];
            } else
                sz[2*ib] = sz[2*ib+1] = '?';
        }
        sz[2*ib] = '\015';
        sz[2*ib+1] = '\012';
        sz += 2*ib + 2;
        cb -= ibLim;
    }

    pdmcc->DataSize = sz - (LPSTR)pdmcc->Buffer;
    return XBDM_NOERR;
}

HRESULT HrGetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs;

    if(!pdmcc)
        return E_FAIL;
    pccs = (CCS *)pdmcc->CustomData;
    if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pccs->pb, szResp))
        return E_FAIL;
    if(!FGetNamedDwParam(sz, "length", &pdmcc->BytesRemaining, szResp))
        return E_FAIL;

    strcpy(szResp, "memory data follows");
    pdmcc->HandlingFunction = HrReportMemory;
    /* We'd like a ~1k buffer for sending data, but if we can't find the
     * memory, we'll just get the default buffer */
    pdmcc->BufferSize = 1056;
    pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'mdbX');
    return XBDM_MULTIRESPONSE;
}

HRESULT HrSetMemory(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    int i;
    char rgch[5];
    BYTE b;
    DWORD dwPageBase;
    BOOL fPageValid;
    int cb = 0;
    LPCSTR szData;
    BYTE *pb;

    if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pb, szResp))
        return E_FAIL;
    szData = PchGetParam(sz, "data", FALSE);
    dwPageBase = (DWORD)pb + 0x1000;

    rgch[0] = '0';
    rgch[1] = 'x';
    rgch[4] = 0;

    if(szData) {
        for(;;++cb, ++pb) {
            if(FIsSpace(*szData))
                break;
            for(i = 0; i < 2; ++i) {
                if(!(*szData >= '0' && *szData <= '9' ||
                        *szData >= 'a' && *szData <= 'f' ||
                        *szData >= 'A' && *szData <= 'F')) {
                    strcpy(szResp, "400- syntax error in data");
                    return E_FAIL;
                }
                rgch[2+i] = *szData++;
            }
            b = (BYTE)DwFromSz(rgch, NULL);
            if(((DWORD)pb ^ dwPageBase) & 0xfffff000) {
                dwPageBase = (DWORD)pb & 0xfffff000;
                fPageValid = MmIsAddressValid(pb);
            }
            if(fPageValid)
                /* If there's a breakpoint at this address, we need to set
                 * the breakpoint data, not change the int 3 */
                fPageValid = FSetMemory(pb, b);
            if(!fPageValid) {
                sprintf(szResp, "set %d bytes", cb);
                return XBDM_MEMUNMAPPED;
            }
        }
    }
    sprintf(szResp, "set %d bytes", cb);
    return XBDM_NOERR;
}

HRESULT HrReportThreads(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    int ich;

    if(!pdmcc->BytesRemaining || !pccs->i) {
        DmFreePool(pccs->h);
        return XBDM_ENDOFLIST;
    }

    /* Pack as many thread ids into the buffer as we can */
    for(ich = 0; (DWORD)ich < pdmcc->BufferSize - 10 && pccs->i; ) {
        sprintf((LPSTR)pdmcc->Buffer + ich, "%d\015\012",
            ((LPDWORD)pccs->h)[--pccs->i]);
        while(((LPSTR)pdmcc->Buffer)[ich])
            ++ich;
    }

    pdmcc->DataSize = ich;
    return XBDM_NOERR;
}

HRESULT HrListThreads(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    HRESULT hr;

    if(!pdmcc)
        return E_FAIL;
    /* Make a list that we think will be big enough */
    pccs = (CCS *)pdmcc->CustomData;
    pccs->h = DmAllocatePoolWithTag(256 * sizeof(DWORD), 'mdbX');
    if(!pccs->h)
        return E_OUTOFMEMORY;
    pccs->i = 256;
    hr = DmGetThreadList((LPDWORD)pccs->h, (LPDWORD)&pccs->i);
    if(FAILED(hr)) {
        if(hr == XBDM_BUFFER_TOO_SMALL)
            hr = E_UNEXPECTED;
        DmFreePool(pccs->h);
    } else {
        hr = XBDM_MULTIRESPONSE;
        strcpy(szResp, "thread list follows");
    }
    pdmcc->HandlingFunction = HrReportThreads;
    return hr;
}

HRESULT HrHaltThread(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    DWORD tid;

    if(!FGetDwParam(sz, "thread", &tid))
        tid = 0;
    return DmHaltThread(tid);
}

HRESULT HrContinueThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD tid;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    return DmContinueThread(tid, (BOOL)PchGetParam(sz, "exception", FALSE));
}

HRESULT HrSuspendThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD tid;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    return DmSuspendThread(tid);
}

HRESULT HrResumeThread(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD tid;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    return DmResumeThread(tid);
}

HRESULT HrFunctionCall(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD tid;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    return DmSetupFunctionCall(tid);
}

typedef struct _CREG {
    char *szName;
    DWORD dwOffset;
    DWORD cb;
} CREG;

#define CtxOffset(field) ((DWORD)(&((PCONTEXT)0)->field))
#define Creg(reg) { #reg, CtxOffset(reg), sizeof ((PCONTEXT)0)->reg }
#define FpCreg(reg) { #reg, CtxOffset(FloatSave.reg), \
    sizeof ((PCONTEXT)0)->FloatSave.reg }

static CREG rgcregCtrl[] = {
    Creg(Ebp), Creg(Esp), Creg(Eip), Creg(EFlags)
};

static CREG rgcregInt[] = {
    Creg(Eax), Creg(Ebx), Creg(Ecx), Creg(Edx), Creg(Edi), Creg(Esi)
};

static CREG rgcregFPCtrl[] = {
    FpCreg(Cr0NpxState)
};

typedef struct _RCC {
    CONTEXT cr;
    union {
        struct {
            int irgcreg;
            CREG *pcreg;
            int icreg;
            CREG *pcregNext;
        };
        struct {
            DWORD tid;
            BOOL fSend;
            int iSend;
            int ib;
        };
    };
} RCC;

CREG *PcregNext(RCC *prcc)
{
    while(!prcc->icreg) {
        /* Time to switch to the next register set */
        switch(prcc->irgcreg) {
        case 0:
            if((prcc->cr.ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL) {
                prcc->pcreg = rgcregCtrl;
                prcc->icreg = sizeof rgcregCtrl / sizeof(CREG);
            } else
                prcc->icreg = 0;
            ++prcc->irgcreg;
            break;
        case 1:
            if((prcc->cr.ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
                prcc->pcreg = rgcregInt;
                prcc->icreg = sizeof rgcregInt / sizeof(CREG);
            } else
                prcc->icreg = 0;
            ++prcc->irgcreg;
            break;
        case 2:
            if((prcc->cr.ContextFlags & CONTEXT_FLOATING_POINT) ==
                CONTEXT_FLOATING_POINT)
            {
                prcc->pcreg = rgcregFPCtrl;
                prcc->icreg = sizeof rgcregFPCtrl / sizeof(CREG);
            } else
                prcc->icreg = 0;
            ++prcc->irgcreg;
            break;
        default:
            /* Nothing left */
            return prcc->pcregNext = NULL;
        }
    }
    --prcc->icreg;
    return prcc->pcregNext = prcc->pcreg++;
}

#define SIZE_OF_EXT_REGISTERS 0x200

HRESULT HrDoExtContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    RCC *prcc = (RCC *)pdmcc->CustomData;
    HRESULT hr = XBDM_NOERR;
    BOOL fDone = FALSE;

    if(prcc->fSend) {
        switch(prcc->iSend++) {
        case 0:
            pdmcc->DataSize = sizeof(DWORD);
            memcpy(pdmcc->Buffer, &pdmcc->BytesRemaining, pdmcc->DataSize);
            break;
        case 1:
            pdmcc->Buffer = &prcc->cr.FloatSave;
            pdmcc->DataSize = pdmcc->BytesRemaining;
            break;
        default:
            pdmcc->BytesRemaining = 0;
            break;
        }
        if(!pdmcc->BytesRemaining) {
            fDone = TRUE;
            hr = XBDM_ENDOFLIST;
        }
    } else if(pdmcc->DataSize != 0) {
        DWORD cbCopy = SIZE_OF_EXT_REGISTERS - prcc->ib;
        if(cbCopy > pdmcc->DataSize)
            cbCopy = pdmcc->DataSize;
        if(cbCopy)
            memcpy((BYTE *)&prcc->cr.FloatSave + prcc->ib, pdmcc->Buffer,
                cbCopy);
        pdmcc->BytesRemaining -= pdmcc->DataSize;
        prcc->ib += cbCopy;
        if(!pdmcc->BytesRemaining) {
            fDone = TRUE;
            prcc->cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
            hr = DmSetThreadContext(prcc->tid, &prcc->cr);
        } else
            hr = XBDM_NOERR;
    } else
        fDone = TRUE;

    if(fDone)
        DmFreePool(prcc);
    return hr;
}

HRESULT HrGetExtContext(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    RCC *prcc;
    CONTEXT cr;
    HRESULT hr;
    DWORD tid;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    cr.ContextFlags = CONTEXT_EXTENDED_REGISTERS;
    hr = DmGetThreadContext(tid, &cr);
    if(SUCCEEDED(hr)) {
        if((cr.ContextFlags & CONTEXT_EXTENDED_REGISTERS) ==
            CONTEXT_EXTENDED_REGISTERS)
        {
            prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
            if(!prcc)
                return E_OUTOFMEMORY;
            memcpy(&prcc->cr, &cr, sizeof cr);
            prcc->iSend = 0;
            prcc->fSend = TRUE;
            pdmcc->CustomData = prcc;
            pdmcc->HandlingFunction = HrDoExtContext;
            ASSERT(SIZE_OF_EXT_REGISTERS <= sizeof prcc->cr.FloatSave);
            pdmcc->BytesRemaining = SIZE_OF_EXT_REGISTERS;
            hr = XBDM_BINRESPONSE;
        } else
            hr = XBDM_NOTSTOPPED;
    }
    return hr;
}

HRESULT HrReportContext(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    RCC *prcc = (RCC *)pdmcc->CustomData;
    DWORD ich = 0;
    DWORD ichLim = pdmcc->BufferSize - 22;
    CREG *pcreg;

    if(!pdmcc->BytesRemaining || !(pcreg = prcc->pcregNext)) {
        DmFreePool(prcc);
        return XBDM_ENDOFLIST;
    }

    /* Fill as much of the buffer as we can */
    while(pcreg && ich < ichLim) {
        if(pcreg->cb == 4)
            sprintf((char *)pdmcc->Buffer + ich, "%s=0x%08x\015\012",
                pcreg->szName, *(DWORD *)((BYTE *)&prcc->cr + pcreg->dwOffset));
        else
            sprintf((char *)pdmcc->Buffer + ich, "%s=0x%04x\015\012",
                pcreg->szName, *(USHORT *)((BYTE *)&prcc->cr + pcreg->dwOffset));
        while(((char *)pdmcc->Buffer)[ich])
            ++ich;
        pcreg = PcregNext(prcc);
    }

    pdmcc->DataSize = ich;
    return XBDM_NOERR;
}

HRESULT HrGetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    RCC *prcc;
    CONTEXT cr;
    HRESULT hr;
    DWORD tid;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    cr.ContextFlags = 0;
    if(PchGetParam(sz, "control", FALSE))
        cr.ContextFlags |= CONTEXT_CONTROL;
    if(PchGetParam(sz, "int", FALSE))
        cr.ContextFlags |= CONTEXT_INTEGER;
    if(PchGetParam(sz, "full", FALSE))
        cr.ContextFlags |= CONTEXT_FULL;
    if(PchGetParam(sz, "fp", FALSE))
        cr.ContextFlags |= CONTEXT_FLOATING_POINT;
    hr = DmGetThreadContext(tid, &cr);
    if(SUCCEEDED(hr)) {
        prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
        if(!prcc)
            return E_OUTOFMEMORY;
        memcpy(&prcc->cr, &cr, sizeof cr);
        prcc->icreg = 0;
        prcc->irgcreg = 0;
        PcregNext(prcc);
        pdmcc->CustomData = prcc;
        pdmcc->HandlingFunction = HrReportContext;
        hr = XBDM_MULTIRESPONSE;
    }
    return hr;
}

HRESULT HrSetContext(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CONTEXT cr;
    HRESULT hr;
    DWORD dw;
    DWORD tid;
    RCC *prcc;
    DWORD cbExtRegs;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;

    /* If we're going to set the extended context, make sure we'll be able to
     * do so */
    if(FGetDwParam(sz, "ext", &cbExtRegs)) {
        if(!pdmcc)
            return E_FAIL;
        pdmcc->BytesRemaining = cbExtRegs;
        prcc = DmAllocatePoolWithTag(sizeof *prcc, 'rcmd');
        if(!prcc)
            return E_OUTOFMEMORY;
    } else
        prcc = NULL;

    /* We need the current context and then we'll modify the appropriate
     * fields */
    cr.ContextFlags = CONTEXT_FULL | CONTEXT_FLOATING_POINT;
    hr = DmGetThreadContext(tid, &cr);
    if(SUCCEEDED(hr)) {
        int icreg;
        int irgcreg;
        CREG *pcreg;

        for(irgcreg = 0; irgcreg < 2; ++irgcreg) {
            switch(irgcreg) {
            case 0:
                pcreg = rgcregCtrl;
                icreg = sizeof rgcregCtrl / sizeof(CREG);
                break;
            case 1:
                pcreg = rgcregInt;
                icreg = sizeof rgcregInt / sizeof(CREG);
                break;
            case 2:
                pcreg = rgcregFPCtrl;
                icreg = sizeof rgcregFPCtrl / sizeof(CREG);
                break;
            }

            for(; icreg--; ++pcreg) {
                if(FGetDwParam(sz, pcreg->szName, &dw)) {
                    if(pcreg->cb == 4)
                        *(DWORD *)((BYTE *)&cr + pcreg->dwOffset) = dw;
                    else
                        *(USHORT *)((BYTE *)&cr + pcreg->dwOffset) = (USHORT)dw;
                }
            }
        }
        hr = DmSetThreadContext(tid, &cr);
    }
    /* Now see if we want to capture the extended context */
    if(prcc) {
        if(SUCCEEDED(hr)) {
            /* We're not going to be getting the cr0 state when the data
             * comes in, so we'll put it in now */
            prcc->cr.FloatSave.Cr0NpxState = cr.FloatSave.Cr0NpxState;
            pdmcc->CustomData = prcc;
            pdmcc->Buffer = NULL;
            prcc->fSend = FALSE;
            prcc->ib = 0;
            prcc->tid = tid;
            pdmcc->HandlingFunction = HrDoExtContext;
            hr = XBDM_READYFORBIN;
        } else
            DmFreePool(prcc);
    }

    return hr;
}

void FillSzFromWz(char *sz, int cch, WCHAR *wz, int cwch)
{
    if(cwch < 0) {
        while(*wz && cch > 1) {
            *sz++ = (char)*wz++;
            --cch;
        }
    } else {
        while(cwch && cch > 1) {
            *sz++ = (char)*wz++;
            --cch;
            --cwch;
        }
    }
    *sz = 0;
}

HRESULT HrReportCounters(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    HRESULT hr;
    DWORD cch;
    DM_COUNTINFO dmci;

    if(!pdmcc->BytesRemaining) {
        DmCloseCounters(pccs->pdmwc);
        return XBDM_ENDOFLIST;
    }

    cch = pdmcc->BufferSize;
    hr = DmWalkPerformanceCounters(&pccs->pdmwc, &dmci);
    if(SUCCEEDED(hr))
        sprintf(pdmcc->Buffer, "name=\"%s\" type=0x%08x", dmci.Name,
            dmci.Type);
    else {
        DmCloseCounters(pccs->pdmwc);
        pdmcc->BytesRemaining = 0;
    }
    return hr;
}

HRESULT HrListCounters(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    if(!pdmcc)
        return E_FAIL;
    pccs = (CCS *)pdmcc->CustomData;
    pccs->pdmwc = NULL;
    pdmcc->HandlingFunction = HrReportCounters;
    return XBDM_MULTIRESPONSE;
}

HRESULT HrReportCounterData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    switch(pdmcc->BytesRemaining++) {
    case 1:
        sprintf(pdmcc->Buffer, "type=0x%08x vallo=0x%08x "
            "valhi=0x%08x ratelo=0x%08x ratehi=0x%08x", pccs->dmcd.CountType,
            pccs->dmcd.CountValue.LowPart, pccs->dmcd.CountValue.HighPart,
            pccs->dmcd.RateValue.LowPart, pccs->dmcd.RateValue.HighPart);
        break;
    default:
        return XBDM_ENDOFLIST;
    }
    return XBDM_NOERR;
}

HRESULT HrQueryPerformanceCounter(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    HRESULT hr;
    char szName[256];
    HANDLE h;
    DWORD dwType;

    if(!pdmcc)
        return E_FAIL;

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    if(!FGetNamedDwParam(sz, "type", &dwType, szResp))
        return E_FAIL;

    hr = DmOpenPerformanceCounter(szName, &h);
    if(SUCCEEDED(hr)) {
        pccs = (CCS *)pdmcc->CustomData;
        hr = DmQueryPerformanceCounterHandle(h, dwType, &pccs->dmcd);
        NtClose(h);
    }
    if(SUCCEEDED(hr)) {
        pdmcc->HandlingFunction = HrReportCounterData;
        pdmcc->BytesRemaining = 1;
        hr = XBDM_MULTIRESPONSE;
        strcpy(szResp, "counter data follows");
    }
    return hr;
}

HRESULT HrReportModules(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    HRESULT hr;
    DMN_MODLOAD dmml;

    if(!pdmcc->BytesRemaining) {
        DmCloseLoadedModules(pccs->pdmwm);
        return XBDM_ENDOFLIST;
    }

    hr = DmWalkLoadedModules(&pccs->pdmwm, &dmml);
    if(SUCCEEDED(hr))
        GetModLoadSz(pdmcc->Buffer, &dmml);
    else {
        DmCloseLoadedModules(pccs->pdmwm);
        pdmcc->BytesRemaining = 0;
    }
    return hr;
}

HRESULT HrListModules(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    if(!pdmcc)
        return E_FAIL;
    pccs = (CCS *)pdmcc->CustomData;
    pccs->pdmwm = NULL;
    pdmcc->HandlingFunction = HrReportModules;
    return XBDM_MULTIRESPONSE;
}

HRESULT HrReportModuleSections(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    DMN_SECTIONLOAD dmsl;
    HRESULT hr;

    if(!pdmcc->BytesRemaining) {
        DmCloseModuleSections(pccs->pdmws);
        return XBDM_ENDOFLIST;
    }

    hr = DmWalkModuleSections(&pccs->pdmws, NULL, &dmsl);
    if(SUCCEEDED(hr))
        GetSectLoadSz(pdmcc->Buffer, &dmsl);
    else {
        DmCloseModuleSections(pccs->pdmws);
        pdmcc->BytesRemaining = 0;
    }
    return hr;
}

HRESULT HrListModuleSections(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    char szName[MAX_OBJ_PATH+1];
    HRESULT hr;
    DMN_SECTIONLOAD dmsl;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }
    pccs = (CCS *)pdmcc->CustomData;
    pccs->pdmws = NULL;
    hr = DmWalkModuleSections(&pccs->pdmws, szName, &dmsl);
    if(SUCCEEDED(hr) || hr == XBDM_ENDOFLIST) {
        pdmcc->HandlingFunction = HrReportModuleSections;
        RewindDmws(pccs->pdmws);
        hr = XBDM_MULTIRESPONSE;
    }
    return hr;
}

HRESULT HrDoBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    PVOID pvAddr;
    HRESULT hr;
    BOOL fClear;

#if 0
    if(PchGetParam(sz, "list", FALSE)) {
        DoListBreakpoints(isock);
        return;
    }
#endif
    if(PchGetParam(sz, "now", FALSE)) {
        Sleep(250);
        _asm int 3
        return XBDM_NOERR;
    }
    if(PchGetParam(sz, "start", FALSE)) {
        hr = DmSetInitialBreakpoint();
        if(FAILED(hr))
            strcpy(szResp, "execution not pending");
        return hr;
    }
    if(PchGetParam(sz, "clearall", FALSE)) {
        RemoveAllBreakpoints();
        return XBDM_NOERR;
    }
    fClear = PchGetParam(sz, "clear", FALSE) != 0;
    if(FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp)) {
        if(fClear)
            /* We're only removing this breakpoint */
            hr = DmRemoveBreakpoint(pvAddr);
        else
            hr = DmSetBreakpoint(pvAddr);
    } else {
        /* See if we're doing a hw breakpoint */
        DWORD dwType = DMBREAK_NONE;
        DWORD dwSize = 0;

        if(FGetDwParam(sz, "read", (DWORD *)&pvAddr))
            dwType = DMBREAK_READWRITE;
        else if(FGetDwParam(sz, "write", (DWORD *)&pvAddr))
            dwType = DMBREAK_WRITE;
        else if(FGetDwParam(sz, "execute", (DWORD *)&pvAddr))
            dwType = DMBREAK_EXECUTE;
        if(dwType == DMBREAK_NONE)
            /* Never saw a valid command */
            hr = E_FAIL;
        else if(fClear || FGetNamedDwParam(sz, "size", &dwSize, szResp)) {
            szResp[0] = 0;
            hr = DmSetDataBreakpoint(pvAddr, fClear ? DMBREAK_NONE : dwType,
                dwSize);
        } else
            hr = E_FAIL;
    }
    return hr;
}

HRESULT HrIsBreak(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    PVOID pvAddr;
    DWORD dwType;
    HRESULT hr;

    if(!FGetNamedDwParam(sz, "addr", (DWORD *)&pvAddr, szResp))
        return E_FAIL;

    hr = DmIsBreakpoint(pvAddr, &dwType);
    if(SUCCEEDED(hr))
        sprintf(szResp, "type=%d", dwType);
    return hr;
}

HRESULT HrSetConfig(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    ULONG ulIndex;
    DWORD dwValue;

    if(!FGetNamedDwParam(sz, "index", &ulIndex, szResp))
        return E_FAIL;

    if(!FGetNamedDwParam(sz, "value", &dwValue, szResp))
        return E_FAIL;

    if (!NT_SUCCESS(ExSaveNonVolatileSetting(ulIndex, REG_DWORD, &dwValue, sizeof(DWORD))))
    {
        strcpy(szResp, "setvalue failed");
        return E_FAIL;
    }

    return XBDM_NOERR;
}

#if 0
void DoListBreakpoints(int isock)
{
    SOCKET s = rgsockConnect[isock];
    BYTE *pb = (BYTE *)-1;
    char sz[64];

    PrintSockLine(s, "202- breakpoint list follows");
    while(FGetNextBreakpoint(&pb)) {
        sprintf(sz, "0x%08x", pb);
        PrintSockLine(s, sz);
    }
    PrintSockLine(s, ".");
}
#endif

HRESULT HrIsStopped(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    DM_THREADSTOP dmts;
    DWORD tid;

    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    hr = DmIsThreadStopped(tid, &dmts);
    if(SUCCEEDED(hr)) {
        switch(dmts.NotifiedReason) {
        case DM_DEBUGSTR:
            sprintf(szResp, "debugstr thread=%d stop",
                dmts.u.DebugStr.ThreadId);
            break;
        case DM_ASSERT:
            sprintf(szResp, "assert prompt thread=%d stop",
                dmts.u.DebugStr.ThreadId);
            break;
        default:
            if(!FGetNotifySz(dmts.NotifiedReason, (DWORD)&dmts.u, szResp))
                strcpy(szResp, "stopped");
            break;
        }
    }
    return hr;
}

HRESULT HrReportThreadInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    switch(pdmcc->BytesRemaining++) {
    case 1:
        sprintf(pdmcc->Buffer, "suspend=%d priority=%d tlsbase=0x%08x",
            pccs->dmti.SuspendCount, pccs->dmti.Priority, pccs->dmti.TlsBase);
        break;
    default:
        return XBDM_ENDOFLIST;
    }
    return XBDM_NOERR;
}

HRESULT HrThreadInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    DWORD tid;
    CCS *pccs;

    if(!pdmcc)
        return E_FAIL;
    if(!FGetNamedDwParam(sz, "thread", &tid, szResp))
        return E_FAIL;
    pccs = (CCS *)pdmcc->CustomData;
    hr = DmGetThreadInfo(tid, &pccs->dmti);
    if(SUCCEEDED(hr)) {
        pdmcc->HandlingFunction = HrReportThreadInfo;
        pdmcc->BytesRemaining = 1;
        hr = XBDM_MULTIRESPONSE;
        strcpy(szResp, "thread info follows");
    }
    return hr;
}

HRESULT HrReportXapiInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    switch(pdmcc->BytesRemaining++) {
    case 1:
        sprintf(pdmcc->Buffer, "lasterr=0x%08x", pccs->dmxd.LastErrorOffset);
        break;
    default:
        return XBDM_ENDOFLIST;
    }
    return XBDM_NOERR;
}

HRESULT HrXapiInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    DWORD tid;
    CCS *pccs;

    if(!pdmcc)
        return E_FAIL;
    pccs = (CCS *)pdmcc->CustomData;
    hr = DmGetXtlData(&pccs->dmxd);
    if(SUCCEEDED(hr)) {
        pdmcc->HandlingFunction = HrReportXapiInfo;
        pdmcc->BytesRemaining = 1;
        hr = XBDM_MULTIRESPONSE;
        strcpy(szResp, "xtl info follows");
    }
    return hr;
}

HRESULT HrDoLongName(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;
    char szModule[MAX_OBJ_PATH+1];

    if(!FGetSzParam(sz, "name", szModule, sizeof szModule)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    hr = DmGetModuleLongName(szModule, szResp, &cchResp);
    if(hr == E_FAIL)
        strcpy(szResp, "no long name available");
    return hr;
}

HRESULT HrReportXbeInfo(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    switch(pccs->iiXbe++) {
    case 0:
        sprintf(pdmcc->Buffer, "timestamp=0x%08x", pccs->pxbe->TimeStamp);
        break;
    case 1:
        sprintf(pdmcc->Buffer, "checksum=0x%08x", pccs->pxbe->CheckSum);
        break;
    case 2:
        /* We're a little sneaky with the name -- we know the buffer we have
         * for the name is big enough, so we slide it over, overwriting the
         * timestamp and checksum values, and insert the "name=" in front */
        memmove(&pccs->pxbe->LaunchPath[5], pccs->pxbe->LaunchPath,
            MAX_PATH + 1);
        memcpy(pccs->pxbe->LaunchPath, "name=", 5);
        pdmcc->Buffer = pccs->pxbe->LaunchPath;
        break;
    default:
        pdmcc->BytesRemaining = 0;
        break;
    }

    if(!pdmcc->BytesRemaining) {
        DmFreePool(pccs->pxbe);
        return XBDM_ENDOFLIST;
    }
    return XBDM_NOERR;
}

HRESULT HrXbeInfo(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    char szName[MAX_OBJ_PATH+1];
    char *pszName;
    CCS *pccs;
    HRESULT hr;

    if(!pdmcc)
        return E_FAIL;
    if(PchGetParam(sz, "running", FALSE))
        pszName = NULL;
    else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    } else
        pszName = szName;
    pccs = (CCS *)pdmcc->CustomData;
    pccs->pxbe = DmAllocatePoolWithTag(sizeof(DM_XBE), 'dmbX');
    if(!pccs->pxbe)
        return E_OUTOFMEMORY;
    hr = DmGetXbeInfo(pszName, pccs->pxbe);
    if(FAILED(hr))
        DmFreePool(pccs->pxbe);
    else {
        pdmcc->HandlingFunction = HrReportXbeInfo;
        pdmcc->BytesRemaining = 1;
        pccs->iiXbe = 0;
        hr = XBDM_MULTIRESPONSE;
    }
    return hr;
}

BOOL DmIsDebuggerPresent(void)
{
    return g_fDebugging;
}

HRESULT HrConnectDebugger(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    if(g_dmi.Flags & DMIFLAG_RUNSHELL)
        return XBDM_NOTDEBUGGABLE;
    if(PchGetParam(sz, "connect", FALSE)) {
        g_fDebugging = TRUE;
        fAllowKd = FALSE;
    } else if(PchGetParam(sz, "disconnect", FALSE))
        g_fDebugging = FALSE;
    else {
        sprintf(szResp, "no command");
        return E_FAIL;
    }
    return XBDM_NOERR;
}

HRESULT HrSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    LARGE_INTEGER li;

    KeQuerySystemTime(&li);
    if(li.HighPart > 0x20000000)
        return XBDM_CLOCKNOTSET;
    sprintf(szResp, "high=0x%x low=0x%x", li.HighPart, li.LowPart);
    return XBDM_NOERR;
}

HRESULT HrGetAltAddr(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    XNADDR xna;
    int err;

    err = XNetGetTitleXnAddr(&xna);
    if(err == XNET_GET_XNADDR_NONE || err == XNET_GET_XNADDR_PENDING)
        return E_FAIL;
    sprintf(szResp, "addr=0x%08x", ntohl(xna.ina.s_addr));
    return XBDM_NOERR;
}

HRESULT HrSetSystemTime(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    LARGE_INTEGER li;
    NTSTATUS st;
    DWORD tzflag;

    if(!FGetDwParam(sz, "clockhi", &li.HighPart) || !FGetDwParam(sz, "clocklo", &li.LowPart))
        return XBDM_CLOCKNOTSET;
    st = NtSetSystemTime(&li, NULL);

    // Set default timezone and language if necessary
    if (FGetDwParam(sz, "tz", &tzflag)) {
        XBOX_USER_SETTINGS userSettings;
        ULONG type, length;
        st = ExQueryNonVolatileSetting(
                XC_MAX_OS,
                &type,
                &userSettings,
                sizeof(userSettings),
                &length);

        if (NT_SUCCESS(st)) {
            BOOL isInvalid = FALSE;
            // Default to Pacific time zone if not set
            if (XBOX_INVALID_TIMEZONE_SETTING(userSettings)) {
                userSettings.TimeZoneBias = 480;
                strncpy(userSettings.TimeZoneStdName, "PST", XC_TZNAMELEN);
                strncpy(userSettings.TimeZoneDltName, "PDT", XC_TZNAMELEN);
                userSettings.TimeZoneStdBias = 0;
                userSettings.TimeZoneDltBias = -60;
                userSettings.TimeZoneStdDate.Month = 10;
                userSettings.TimeZoneStdDate.Day = 5;
                userSettings.TimeZoneStdDate.DayOfWeek = 0;
                userSettings.TimeZoneStdDate.Hour = 0;
                userSettings.TimeZoneDltDate.Month = 4;
                userSettings.TimeZoneDltDate.Day = 1;
                userSettings.TimeZoneDltDate.DayOfWeek = 0;
                userSettings.TimeZoneDltDate.Hour = 0;
                isInvalid = TRUE;
            }

            // Default language to English if not set
            if (userSettings.Language == XC_LANGUAGE_UNKNOWN) {
                userSettings.Language = XC_LANGUAGE_ENGLISH;
                isInvalid = TRUE;
            }

            if (isInvalid) {
                st = ExSaveNonVolatileSetting(
                        XC_MAX_OS,
                        REG_BINARY,
                        &userSettings,
                        sizeof(userSettings));
            }
        }
    }

    return NT_SUCCESS(st) ? XBDM_NOERR : XBDM_CLOCKNOTSET;
}

HRESULT HrReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    int icst;
    DWORD dw = 0;

    if(!pdmcc)
        return FALSE;
    icst = (PCST)pdmcc - rgcst;
    PrintSockLine(rgcst[icst].s, "200- OK");

    if(PchGetParam(sz, "stop", FALSE))
        dw |= DMBOOT_STOP;
    if(PchGetParam(sz, "wait", FALSE))
        dw |= DMBOOT_WAIT;
    if(PchGetParam(sz, "warm", FALSE))
        dw |= DMBOOT_WARM;
    if(PchGetParam(sz, "nodebug", FALSE))
        dw |= DMBOOT_NODEBUG;
    DmReboot(dw);
    return 0;
}

HRESULT HrMagicReboot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    int icst;
    HANDLE h;
    OBJECT_ATTRIBUTES oa;
    OBJECT_STRING objectName;
    IO_STATUS_BLOCK iosb;
    NTSTATUS st;
    OCHAR oszTitle[MAX_OBJ_PATH+1];
    OCHAR *pochTitle;
    OCHAR *poch;
    char szTitle[MAX_OBJ_PATH+1];
    DWORD dwFlags = DMBOOT_NODEBUG | DMBOOT_WARM;

    if(!FGetSzParam(sz, "title", szTitle, sizeof szTitle)) {
        strcpy(szResp, "missing title");
        return E_FAIL;
    }

    if(PchGetParam(sz, "debug", FALSE))
        dwFlags &= ~DMBOOT_NODEBUG;

    if(!FFileNameToObName(szTitle, oszTitle, sizeof oszTitle / sizeof(OCHAR)))
        return XBDM_NOSUCHFILE;

    for(poch = oszTitle; *poch; ++poch)
        if(*poch == '\\')
            pochTitle = poch;

    DmStop();
    /* Verify the file exists */
    RtlInitObjectString(&objectName, oszTitle);
    InitializeObjectAttributes(&oa, &objectName, OBJ_CASE_INSENSITIVE, NULL,
        NULL);
    st = NtOpenFile(&h, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_READ,
        FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
    if(NT_SUCCESS(st)) {
        NtClose(h);
        *pochTitle++ = 0;
        st = NT_SUCCESS(XWriteTitleInfoNoReboot(pochTitle, oszTitle, LDT_NONE,
            0, NULL));
        if(NT_SUCCESS(st)) {
            icst = (PCST)pdmcc - rgcst;
            PrintSockLine(rgcst[icst].s, "200- OK");
            DmReboot(dwFlags);
        }
    }
    return HrFromStatus(st, E_FAIL);
}

#include "xprofp.h"
#define XBOX_CAP_FILENAME "E:\\xboxcap.dat"

XProfpFileHeader* CAPGetFileHeader()
{
    XProfpFileHeader* fileheader = (XProfpFileHeader*)
        MmAllocateSystemMemory(XPROF_FILE_HEADER_SIZE, PAGE_READWRITE);

    if (fileheader) {
        PLIST_ENTRY list, head;
        ULONG count;

        memset(fileheader, 0, XPROF_FILE_HEADER_SIZE);
        fileheader->version = XPROF_FILE_VERSION;

        // save information about currently loaded modules
        head = g_dmi.LoadedModuleList;
        list = head->Flink;
        for (count=0; list != head; list=list->Flink) {
            PLDR_DATA_TABLE_ENTRY entry;
            WCHAR* wstr;
            CHAR* str;
            ULONG len;

            entry = CONTAINING_RECORD(list, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
            fileheader->modules[count].loadaddr = (ULONG) entry->DllBase;
            fileheader->modules[count].size = entry->SizeOfImage;

            len = entry->BaseDllName.Length / sizeof(WCHAR);
            if (len >= XPROF_MAX_MODULE_NAMELEN-1) {
                // The base DLL name is too long
                // Just ignore this module.
                continue;
            }

            // Truncate unicode name to ASCII
            wstr = entry->BaseDllName.Buffer;
            str = fileheader->modules[count].name;
            while (len--)
                *str++ = (CHAR) *wstr++;

            // Null-terminator
            *str = 0;
            count++;
        }

        fileheader->module_count = count;
    }

    return fileheader;
}

NTSTATUS CAPWriteFile(XProfpGlobals* xprofData)
{
    BYTE* bufstart = (BYTE*) xprofData->bufstart;
    BYTE* bufend = (BYTE*) xprofData->bufnext_stopped;
    HANDLE file = NULL;
    XProfpFileHeader* fileheader = NULL;
    ULONG count;
    NTSTATUS status;
    IO_STATUS_BLOCK iostatusBlock;
    FILE_END_OF_FILE_INFORMATION endOfFile;
    FILE_ALLOCATION_INFORMATION allocation;

    // Sanity check
    if (!bufstart || !bufend || bufend <= bufstart) {
        return STATUS_UNSUCCESSFUL;
    }

    // Create the file for writing
    //  we're doing sequential writes only and
    //  we don't need intermediate buffering

    count = bufend - bufstart;
    ASSERT(((ULONG_PTR) bufstart % PAGE_SIZE) == 0);

    status = FCreateFile(
                &file,
                GENERIC_WRITE | SYNCHRONIZE,
                XBOX_CAP_FILENAME,
                NULL,
                0,
                0,
                FILE_OVERWRITE_IF,
                FILE_SYNCHRONOUS_IO_NONALERT |
                    FILE_SEQUENTIAL_ONLY |
                    FILE_NO_INTERMEDIATE_BUFFERING);

    if (!NT_SUCCESS(status)) goto exit;

    // Generate the profile data file header
    fileheader = CAPGetFileHeader();
    if (!fileheader) {
        status = STATUS_NO_MEMORY;
        goto exit;
    }

    ASSERT(XPROF_FILE_HEADER_SIZE % PAGE_SIZE == 0);
    status = NtWriteFile(
                file,
                NULL,
                NULL,
                NULL,
                &iostatusBlock,
                fileheader,
                XPROF_FILE_HEADER_SIZE,
                NULL);

    if (!NT_SUCCESS(status)) goto exit;

    // Write the profile data out to disk
    //  size is rounded up to a multiple of 512
    status = NtWriteFile(
                file,
                NULL,
                NULL,
                NULL,
                &iostatusBlock,
                bufstart,
                (count + 511) & ~511,
                NULL);

    if (!NT_SUCCESS(status)) goto exit;

    // Set file size
    count += XPROF_FILE_HEADER_SIZE;
    endOfFile.EndOfFile.QuadPart = count;
    status = NtSetInformationFile(
                file,
                &iostatusBlock,
                &endOfFile,
                sizeof(endOfFile),
                FileEndOfFileInformation);

    if (!NT_SUCCESS(status)) goto exit;

    allocation.AllocationSize.QuadPart = count;
    status = NtSetInformationFile(
                file,
                &iostatusBlock,
                &allocation,
                sizeof(allocation),
                FileAllocationInformation);

exit:
    if (file) { NtClose(file); }
    if (fileheader) { MmFreeSystemMemory(fileheader, XPROF_FILE_HEADER_SIZE); }
    return status;
}

HRESULT HrCAPControl(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS status = STATUS_NOT_IMPLEMENTED;

    if (PchGetParam(sz, "start", FALSE)) {
        // Start profile data collection
        status = DmCapControl(XPROF_START, 0);
    } else {
        // Stop profile data collection
        status = DmCapControl(XPROF_STOP, 0);

        // Save the profile data to disk
        if (NT_SUCCESS(status)) {
            XProfpGlobals* xprofData = DmProfileData;
            LONG lock;

            // Prevent other threads from freeing the profile data buffer
            // while we're trying to write it to disk
            lock = InterlockedCompareExchange(&xprofData->lock, 1, 0);

            if (lock != 0) {
                status = STATUS_DEVICE_BUSY;
            } else {
                CAPWriteFile(xprofData);

                // Unlock the profile data buffer
                xprofData->lock = 0;
            }
        }
    }

    if (NT_SUCCESS(status)) return XBDM_NOERR;

    // Note: should we map error status to HRESULT?
    sprintf(szResp, "error status 0x%08x", status);
    return XBDM_UNDEFINED;
}

HRESULT HrIrtSweep(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS status = STATUS_NOT_IMPLEMENTED;
    HANDLE file;

    #ifdef _XBOX_ENABLE_PROFILING
    #define IRTSWEEP_FILENAME "E:\\irtsweep.dat"

    status = FCreateFile(
                &file,
                GENERIC_WRITE | SYNCHRONIZE,
                IRTSWEEP_FILENAME,
                NULL,
                0,
                0,
                FILE_OVERWRITE_IF,
                FILE_SYNCHRONOUS_IO_NONALERT |
                    FILE_SEQUENTIAL_ONLY |
                    FILE_NO_INTERMEDIATE_BUFFERING);

    if (NT_SUCCESS(status)) {
        status = IrtSweep(file);
        NtClose(file);
    }

    #endif

    if (NT_SUCCESS(status)) return XBDM_NOERR;
    sprintf(szResp, "error status 0x%08x", status);
    return XBDM_UNDEFINED;
}

HRESULT HrReceiveKernelImage(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    HRESULT hr = XBDM_NOERR;
    DWORD crc;
    CCS *pccs;

    pccs = pdmcc->CustomData;

    if (pdmcc->DataSize) {
        if ( pccs->KernelImage.BytesReceived + pdmcc->DataSize <= \
             pccs->KernelImage.ImageSize) {
            CopyMemory(pccs->KernelImage.ImageBuffer + pccs->KernelImage.BytesReceived, \
                pdmcc->Buffer, pdmcc->DataSize);
        }
        pdmcc->BytesRemaining -= pdmcc->DataSize;
        pccs->KernelImage.BytesReceived += pdmcc->DataSize;
    } else if (pdmcc->BytesRemaining) {
        DmFreePool(pccs->KernelImage.ImageBuffer);
        DmFreePool(pdmcc->Buffer);
        return E_UNEXPECTED;
    }

    if (pdmcc->BytesRemaining == 0) {
        crc = Crc32(0xFFFFFFFF, pccs->KernelImage.ImageBuffer, pccs->KernelImage.ImageSize);
        if (pccs->KernelImage.Crc != crc) {
            _snprintf(szResponse, cchResponse, "bad data detected in kernel image");
            hr = E_UNEXPECTED;
        } else {
            _snprintf(szResponse, cchResponse, "flashing...");
            Sleep(250);
            hr = FlashKernelImage(pccs->KernelImage.ImageBuffer,
                    pccs->KernelImage.ImageSize, szResponse, cchResponse,
                    pccs->KernelImage.IgnoreVersionChecking);
        }
        DmFreePool(pccs->KernelImage.ImageBuffer);
        DmFreePool(pdmcc->Buffer);
    }

    return hr;
}

HRESULT HrFlashKernelImage(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    SIZE_T ImageSize;
    BOOL IgnoreVersionChecking = FALSE;

    if (!pdmcc) {
        return E_FAIL;
    }

    pccs = (CCS *)pdmcc->CustomData;

    if (!FGetNamedDwParam(sz, "length", &ImageSize, szResp) || \
        !FGetNamedDwParam(sz, "crc", &pccs->KernelImage.Crc, szResp)) {
        _snprintf(szResp, cchResp, "Invalid parameter(s)");
        return E_FAIL;
    }

    FGetNamedDwParam(sz, "ignoreversionchecking", &IgnoreVersionChecking, szResp);
    pccs->KernelImage.IgnoreVersionChecking = IgnoreVersionChecking;

    pccs->KernelImage.ImageSize = ImageSize;
    pdmcc->BytesRemaining = ImageSize;

    if (ImageSize < 0x80000 || (ImageSize & (ImageSize - 1)) != 0) {
        _snprintf(szResp, cchResp, "Invalid kernel image size");
        return E_FAIL;
    }

    pccs->KernelImage.ImageBuffer = DmAllocatePoolWithTag(ImageSize, 'mdbX');

    if (!pccs->KernelImage.ImageBuffer) {
        _snprintf(szResp, cchResp, "Not enough memory");
        return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
    }

    pdmcc->Buffer = DmAllocatePoolWithTag(0x2000, 'mdbX');

    if (!pdmcc->Buffer) {
        DmFreePool(pccs->KernelImage.ImageBuffer);
        _snprintf(szResp, cchResp, "Not enough memory");
        return HrFromStatus(STATUS_NO_MEMORY, XBDM_UNDEFINED);
    }

    pdmcc->BufferSize = 0x2000;
    pdmcc->HandlingFunction = HrReceiveKernelImage;
    pccs->KernelImage.BytesReceived = 0;

    return XBDM_READYFORBIN;
}

typedef struct _DRIVENAME_CONVERSION_ENTRY
{
    PCOSTR  ObjectName;
    ULONG   ObjectNameLength; //Without a terminating NULL
    OCHAR       DriveName;
    BOOL    Visible;
} DRIVENAME_TABLE_ENTRY, *PDRIVENAME_TABLE_ENTRY;
extern DRIVENAME_TABLE_ENTRY DriveNameConversionTable[];

HRESULT HrGetDriveList(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    char szName[8];
    OBJECT_STRING objectName;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK iosb;
    NTSTATUS status;
    HANDLE h;
    DRIVENAME_TABLE_ENTRY *pdte;
    struct {
        OBJECT_DIRECTORY_INFORMATION odi;
        OCHAR sz[256];
    } odi;

    RtlInitObjectString(&objectName, "");
    InitializeObjectAttributes(&objectAttributes, &objectName,
        OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);

    status = NtOpenDirectoryObject(&h, &objectAttributes);
    if(NT_SUCCESS(status)) {
        int i = 0;

        // Query the directory object to get the list of actual drives
        for(;;) {
            status = NtQueryDirectoryObject(h, &odi, sizeof odi, (i == 0), &i,
                NULL);
            if (!NT_SUCCESS(status))
                break;

            /* Reject this drive if it's not one character plus a colon */
            if(odi.odi.Name.Length != 2 || odi.odi.Name.Buffer[1] != ':')
                continue;

            /* Compare each drive in the drivename conversion table to the
             * current enumerated drive.  If there is a match, then that drive
             * has been remapped - let the table handling code below deal with
             * it */
            for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
                BYTE bMask;
                if(pdte->DriveName < 'A' || (pdte->DriveName & 0xDF) > 'Z')
                    bMask = 0xFF;
                else
                    bMask = 0xDF;
                /* Compare case-insensitive */
                if (!((odi.odi.Name.Buffer[0] ^ pdte->DriveName) & bMask))
                {
                    /* Found a matching entry - ignore the entry; it'll get
                     * handled in the table handling code below */
                    break;
                }
            }

            if (!pdte->ObjectName)
            {
                HANDLE hT;
                /* The current enumerated drive does not exist in the table.
                 * We'll need to handle it here.  Is the drive mounted? */
                odi.odi.Name.Buffer[2] = '\\';
                odi.odi.Name.Length = 3;
                InitializeObjectAttributes(&objectAttributes, &odi.odi.Name,
                    OBJ_CASE_INSENSITIVE, ObDosDevicesDirectory(), NULL);
                status = NtOpenFile(&hT, SYNCHRONIZE,&objectAttributes, &iosb,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
                if (NT_SUCCESS(status))
                {
                    *szResp++ = odi.odi.Name.Buffer[0];
                    NtClose(hT);
                }
            }
        }
        NtClose(h);
    }

    // Enumerate through the conversion table
    szName[1] = ':';
    szName[2] = '\\';
    szName[3] = 0;
    for(pdte = DriveNameConversionTable; pdte->ObjectName; ++pdte) {
        if (pdte->Visible)
        {
            // Is the drive mounted?
            szName[0] = pdte->DriveName;
            status = FCreateFile(&h, SYNCHRONIZE, szName, NULL, 0,
                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
            if (NT_SUCCESS(status))
            {
                *szResp++ = pdte->DriveName;
                NtClose(h);
            }
        }
    }

    // Close off the 'list' string value
    *szResp = 0;

    // Mark that we've been through here once so that next time through we know we're done
    return XBDM_NOERR;
}

HRESULT HrReportDriveFreeSpace(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    FILE_FS_SIZE_INFORMATION NormalSizeInfo;
    CCS *pccs = (CCS *)pdmcc->CustomData;

    ULARGE_INTEGER FreeBytesAvailableToCaller;
    ULARGE_INTEGER TotalNumberOfBytes;
    ULARGE_INTEGER BytesPerAllocationUnit;

    if (pdmcc->BytesRemaining-- == 0)
    {
        // Already sent the data, so close the file, free up our buffer, and return "we're done!"
        NtClose(pccs->h);
        DmFreePool(pdmcc->Buffer);
        pdmcc->Buffer = NULL;
        return XBDM_ENDOFLIST;
    }

    // Determine the size parameters of the volume.
    Status = NtQueryVolumeInformationFile(
                pccs->h,
                &IoStatusBlock,
                &NormalSizeInfo,
                sizeof(NormalSizeInfo),
                FileFsSizeInformation
                );

    if ( !NT_SUCCESS(Status) ) {
        sprintf(pdmcc->Buffer, "unknown error (%d)", Status);
        return E_FAIL;
    }

    BytesPerAllocationUnit.QuadPart     = NormalSizeInfo.BytesPerSector   * NormalSizeInfo.SectorsPerAllocationUnit;
    FreeBytesAvailableToCaller.QuadPart = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.AvailableAllocationUnits.QuadPart;
    TotalNumberOfBytes.QuadPart         = BytesPerAllocationUnit.QuadPart * NormalSizeInfo.TotalAllocationUnits.QuadPart;

    // Add the results to the result string
    sprintf(pdmcc->Buffer, "freetocallerlo=0x%08x freetocallerhi=0x%08x totalbyteslo=0x%08x totalbyteshi=0x%08x totalfreebyteslo=0x%08x totalfreebyteshi=0x%08x",
            FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart,
            TotalNumberOfBytes.LowPart, TotalNumberOfBytes.HighPart,
            FreeBytesAvailableToCaller.LowPart, FreeBytesAvailableToCaller.HighPart);

    return XBDM_NOERR;
}

HRESULT HrGetDriveFreeSpace(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    NTSTATUS st;
    char szName[MAX_OBJ_PATH+1];
    CCS *pccs = (CCS *)pdmcc->CustomData;

    if(!pdmcc)
        return E_FAIL;

    if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    pdmcc->BufferSize = 512;
    pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
    pdmcc->HandlingFunction = HrReportDriveFreeSpace;
    pdmcc->BytesRemaining = 1;  // Internal counter
    st = FCreateFile(&pccs->h, FILE_LIST_DIRECTORY | SYNCHRONIZE, szName, NULL, 0,
                FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);

    return NT_SUCCESS(st) ? XBDM_MULTIRESPONSE : HrFromStatus(st,
        XBDM_CANNOTACCESS);
}

HRESULT HrSendFrameData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    // end of data?
    if (pdmcc->BytesRemaining <= 0) {
        DmFreePool(pdmcc->Buffer);
        pdmcc->Buffer = NULL;

        // We've finished sending the data, so start the threads going again
        DmGo();

        return XBDM_ENDOFLIST;
    }

    if(pccs->Screenshot.fFirstTime == FALSE) {
        /* First we need to send the various parameters (pitch, format, buffer size, etc) */
        sprintf(pdmcc->Buffer, "pitch=0x%08x width=0x%08x height=0x%08x format=0x%08x, framebuffersize=0x%08x",
                            pccs->Screenshot.Pitch, pccs->Screenshot.Width, pccs->Screenshot.Height, pccs->Screenshot.Format, pdmcc->BytesRemaining);
        pdmcc->DataSize = -1;
        pccs->Screenshot.fFirstTime = TRUE;
    } else {
        // if (buffersize%8k != 0) need to send less data
        DWORD cBytesToSend = min (pdmcc->BytesRemaining, pdmcc->BufferSize);
        // send 8k of data
        memcpy(pdmcc->Buffer, pccs->Screenshot.pbyFrameBuffer, cBytesToSend);
        pdmcc->DataSize = cBytesToSend;
        pdmcc->BytesRemaining -= cBytesToSend;

        // point at next 8k of data
        pccs->Screenshot.pbyFrameBuffer += cBytesToSend;
    }

    return XBDM_NOERR;
}

HRESULT HrScreenshot(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    D3DSurface d3dsurface;
    int width, height, pitch, bitdepth, format;
    DWORD start;

    if(!g_dmgd.Surface) {
        strcpy(szResp, "can't find screen buffer");
        return E_FAIL;
    }

    //pdmgd = DmGetCurrentValue((ULONG)((BYTE*)&dm.D3DDriverData - (BYTE*)&dm));
    d3dsurface = *(g_dmgd.Surface);

    // Stop all threads so that no more Presents are added to the queue.
    DmStop();

    // Busy-wait until all previously queues Presents are processed by the GPU
    start = NtGetTickCount();
    while( *(g_dmgd.FlipCounter) != *(g_dmgd.FrameCounter))
    {
        if ( (NtGetTickCount() - start) >= 2000 ) {
            DmGo();
            strcpy(szResp, "can't lock GPU");
            return E_FAIL;
        }
    }

    // Extract size information
    width = (d3dsurface.Size & D3DSIZE_WIDTH_MASK) + 1;
    height = ((d3dsurface.Size & D3DSIZE_HEIGHT_MASK) >> D3DSIZE_HEIGHT_SHIFT) + 1;
    pitch = (((d3dsurface.Size & D3DSIZE_PITCH_MASK) >> D3DSIZE_PITCH_SHIFT) + 1) * D3DTEXTURE_PITCH_ALIGNMENT;
    format = (d3dsurface.Format & D3DFORMAT_FORMAT_MASK) >> D3DFORMAT_FORMAT_SHIFT;

    switch(format) {
    case D3DFMT_LIN_A8R8G8B8:
        bitdepth = 4;
        break;

    case D3DFMT_LIN_X8R8G8B8:
        bitdepth = 4;
        break;

    case D3DFMT_LIN_R5G6B5:
        bitdepth = 2;
        break;

    case D3DFMT_LIN_X1R5G5B5:
        bitdepth = 2;
        break;

    default:
        DmGo();
        strcpy(szResp, "Unexpected framebuffer format");
        return E_FAIL;
    }

    pccs->Screenshot.pbyFrameBuffer = (BYTE *) (d3dsurface.Data | 0xF0000000);

    pccs->Screenshot.fFirstTime = FALSE;
    pccs->Screenshot.Width = width;
    pccs->Screenshot.Height = height;
    pccs->Screenshot.Pitch = pitch;
    pccs->Screenshot.Format = format;

    // Set up the transfer buffer
    pdmcc->BufferSize = 8192;
    pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize);
    pdmcc->HandlingFunction = HrSendFrameData;
    pdmcc->BytesRemaining = pitch * height;
    return XBDM_BINRESPONSE;
}

HRESULT HrSendPSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    // end of data?
    if (pdmcc->BytesRemaining <= 0)
    {
        pdmcc->Buffer = NULL;
        *(g_dmgd.pdwOpcode) = PSSNAP_DONE;      // signal D3D to release buffer
        return XBDM_ENDOFLIST;
    }

    pdmcc->DataSize = 32768;
    pdmcc->BytesRemaining = 0;

    return XBDM_NOERR;
}

HRESULT HrPSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    DWORD dwX, dwY, dwOpcode;
    struct
    {
        DWORD dwX;
        DWORD dwY;
        DWORD dwFlags;
        DWORD dwMarker;
    } args;
    DWORD dwStart;

    if(!FGetDwParam(sz, "x", &args.dwX))
    {
        strcpy(szResp, "Missing X parameter");
        return E_FAIL;
    }
    if(!FGetDwParam(sz, "y", &args.dwY))
    {
        strcpy(szResp, "Missing Y parameter");
        return E_FAIL;
    }

    if(!FGetDwParam(sz, "flags", &args.dwFlags))
        args.dwFlags = 0;

    if(!FGetDwParam(sz, "marker", &args.dwMarker))
        args.dwMarker = 0;

    if(*(g_dmgd.pdwOpcode))
    {
        sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
        return E_FAIL;
    }

    // build opcode for D3D pixel shader snapshot
    *(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
    *(g_dmgd.pdwOpcode) = PSSNAP_REQUEST;

    // wait for the opcode to change
    dwStart = NtGetTickCount();
    while(1)
    {
        dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
        if((dwOpcode == PSSNAP_ACK) ||
           (dwOpcode == PSSNAP_ERROR))
            break;

        Sleep(50);
        if ( (NtGetTickCount() - dwStart) >= 2000 ) {
            strcpy(szResp, "Opcode timeout:  Failed to receive acknowledge");
            *(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
            return E_FAIL;
        }
    }

    if(dwOpcode == PSSNAP_ERROR)
    {
        switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
        {
        case 1:
            sprintf(szResp, "The Application is using an unsupported framebuffer format.  Only A8R8G8B8 is supported at this time.");
            break;

        case 2:
            sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
            break;

        case 3:
            sprintf(szResp, "Debugger wasn't able to allocate memory.");
            break;

        default:
            sprintf(szResp, "D3D snapshot client returned error %d",  *(g_dmgd.pdwOpcode) & 0x00ffffff);
            break;
        }

        *(g_dmgd.pdwOpcode) = PSSNAP_IDLE;
        return E_FAIL;
    }

    // Set up the transfer buffer
    pdmcc->BufferSize = 32768;
    pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
    pdmcc->HandlingFunction = HrSendPSSnapshotData;
    pdmcc->BytesRemaining = 32768;

    return XBDM_BINRESPONSE;
}

HRESULT HrSendVSSnapshotData(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;

    // end of data?
    if (pdmcc->BytesRemaining <= 0)
    {
        pdmcc->Buffer = NULL;
        *(g_dmgd.pdwOpcode) = VSSNAP_DONE;      // signal D3D to release buffer
        return XBDM_ENDOFLIST;
    }

    pdmcc->DataSize = 32768;
    pdmcc->BytesRemaining = 0;

    return XBDM_NOERR;
}

HRESULT HrVSSnap(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    DWORD dwOpcode;
    DWORD dwStart;
    struct
    {
        DWORD dwFirst;
        DWORD dwLast;
        DWORD dwFlags;
        DWORD dwMarker;
    } args;

    if(*(g_dmgd.pdwOpcode))
    {
        sprintf(szResp, "D3D snapshot client in invalid state (%08x)", *(g_dmgd.pdwOpcode));
        return E_FAIL;
    }

    if(!FGetDwParam(sz, "first", &args.dwFirst))
    {
        strcpy(szResp, sz);
        return E_FAIL;
    }
    if(!FGetDwParam(sz, "last", &args.dwLast))
    {
        strcpy(szResp, "Missing Last parameter");
        return E_FAIL;
    }
    if(!FGetDwParam(sz, "flags", &args.dwFlags))
        args.dwFlags = 0;

    if(!FGetDwParam(sz, "marker", &args.dwMarker))
        args.dwMarker = 0;

    if(args.dwFlags & 0x08000000)
    {
        // xray sequence instead
        *(g_dmgd.pdwOpcode) = XRAY_BEGINREQ;
        *(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);

        // wait for the opcode to change
        dwStart = NtGetTickCount();
        while(1)
        {
            dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
            if((dwOpcode == XRAY_BEGINACK) ||
               (dwOpcode == XRAY_ERROR))
                break;

            Sleep(50);
            if ( (NtGetTickCount() - dwStart) >= 2000 ) {
                sprintf(szResp, "Opcode timeout:  Failed to receive acknowledge: %08x", dwOpcode);
                *(g_dmgd.pdwOpcode) = XRAY_IDLE;
                return E_FAIL;
            }
        }
        if(dwOpcode == XRAY_ERROR)
        {
            switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
            {
            case 2:
                sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
                break;

            default:
                sprintf(szResp, "D3D snapshot client returned error %d",  *(g_dmgd.pdwOpcode) & 0x00ffffff);
                break;
            }
            *(g_dmgd.pdwOpcode) = XRAY_IDLE;
            return E_FAIL;
        }
        *(g_dmgd.pdwOpcode) = XRAY_IDLE;
        return XBDM_NOERR;
    }
    else
    {
        if(args.dwFlags & 0x04000000)
        {
            // end xray sequence instead
            *(g_dmgd.pdwOpcode) = XRAY_ENDREQ;

            // wait for the opcode to change
            dwStart = NtGetTickCount();
            while(1)
            {
                dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
                if((dwOpcode == XRAY_ENDACK) ||
                   (dwOpcode == XRAY_ERROR))
                    break;

                Sleep(50);
                if ( (NtGetTickCount() - dwStart) >= 2000 ) {
                    strcpy(szResp, "Opcode timeout:  Failed to receive acknowledge");
                    *(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
                    return E_FAIL;
                }
            }

            if(dwOpcode == XRAY_ERROR)
            {
                switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
                {
                case 2:
                    sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
                    break;

                default:
                    sprintf(szResp, "D3D snapshot client returned error %d",  *(g_dmgd.pdwOpcode) & 0x00ffffff);
                    break;
                }
                *(g_dmgd.pdwOpcode) = XRAY_IDLE;
                return E_FAIL;
            }
            *(g_dmgd.pdwOpcode) = XRAY_IDLE;
            return XBDM_NOERR;
        }
    }

    // build opcode for D3D vertex shader snapshot
    dwOpcode = VSSNAP_REQUEST;

    *(g_dmgd.ppSnapshotBuffer) = (BYTE *)(&args);
    *(g_dmgd.pdwOpcode) = dwOpcode;

    // wait for the opcode to change
    dwStart = NtGetTickCount();
    while(1)
    {
        dwOpcode = *(g_dmgd.pdwOpcode) & 0xff000000;
        if((dwOpcode == VSSNAP_ACK) ||
           (dwOpcode == VSSNAP_ERROR))
            break;

        Sleep(50);
        if ( (NtGetTickCount() - dwStart) >= 2000 ) {
            strcpy(szResp, "Opcode timeout:  Failed to receive acknowledge");
            *(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
            return E_FAIL;
        }
    }

    if(dwOpcode == VSSNAP_ERROR)
    {
        switch(*(g_dmgd.pdwOpcode) & 0x00ffffff)
        {
        case 2:
            sprintf(szResp, "Debugger doesn't support an Xbox app using a Pure Device - Remove D3DCREATE_PUREDEVICE");
            break;

        case 3:
            sprintf(szResp, "Debugger wasn't able to allocate memory.");
            break;

        default:
            sprintf(szResp, "D3D snapshot client returned error %d",  *(g_dmgd.pdwOpcode) & 0x00ffffff);
            break;
        }
        *(g_dmgd.pdwOpcode) = VSSNAP_IDLE;
        return E_FAIL;
    }

    // Set up the transfer buffer
    pdmcc->BufferSize = 32768;
    pdmcc->Buffer = *(g_dmgd.ppSnapshotBuffer);
    pdmcc->HandlingFunction = HrSendVSSnapshotData;
    pdmcc->BytesRemaining = 32768;

    return XBDM_BINRESPONSE;
}

HRESULT HrSetLockMode(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    HRESULT hr;

    if(PchGetParam(sz, "unlock", FALSE)) {
        g_fLockLevel = FALSE;
        hr = XBDM_NOERR;
    } else if(FGetQwordParam(sz, "boxid", &g_luBoxId)) {
        g_fLockLevel = TRUE;
        hr = XBDM_NOERR;
    } else {
        /* Potential failure */
        hr = E_FAIL;
        strcpy(szResp, "missing parameters");
    }
    if(PchGetParam(sz, "encrypt", FALSE)) {
        if(g_fLockLevel) {
            g_fLockLevel = 2;
            hr = XBDM_NOERR;
        } else
            /* If unlock was specified, we'll return success as set above;
             * if not, we'll return failure.  In either case, hr is already
             * set correctly, so we'll just make sure the error message
             * is sufficient */
            strcpy(szResp, "box not locked; file encryption not enabled");
    }

    RemoveAllUsers();
    WriteIniFile();
    return hr;
}

HRESULT HrSetDebugSecureMode(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    int icst;

    if(!g_fDebugSecureMode)
        return XBDM_INVALIDCMD;
    if(!pdmcc)
        return E_FAIL;
    icst = (PCST)pdmcc - rgcst;
    rgcst[icst].dmplCur = -1;
    return XBDM_NOERR;
}

HRESULT HrGetBoxId(LPCSTR sz, LPSTR szResp, DWORD cchResp, PDM_CMDCONT pdmcc)
{
    if(!g_fLockLevel)
        return XBDM_NOTLOCKED;
    sprintf(szResp, "boxid=0q%08x%08x", g_luBoxId.HighPart, g_luBoxId.LowPart);
    return XBDM_NOERR;
}

HRESULT HrAuthenticateUser(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szName[256];
    ULARGE_INTEGER luResponse;
    int icst;
    BOOL fKeyXchg;
    LPCSTR pszName;

    if(!pdmcc)
        return E_FAIL;
    icst = (PCST)pdmcc - rgcst;
    if((rgcst[icst].dwStatus & CONN_DHKEY) && FGetQwordParam(sz, "passwd",
        &luResponse))
    {
        luResponse.LowPart ^= rgcst[icst].luDHKey.LowPart;
        luResponse.HighPart ^= rgcst[icst].luDHKey.HighPart;
        fKeyXchg = TRUE;
    } else if(!FGetQwordParam(sz, "resp", &luResponse)) {
noresp:
        strcpy(szResp, "missing response");
        return E_FAIL;
    } else
        fKeyXchg = FALSE;

    if(PchGetParam(sz, "admin", FALSE)) {
        /* Trying to authenticate using the admin passwd.  This requires a
         * valid response */
        if(fKeyXchg)
            goto noresp;
        pszName = NULL;
    } else if(!FGetSzParam(sz, "name", szName, sizeof szName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    } else
        pszName = szName;

    rgcst[icst].dmplCur = DmplAuthenticateUser(pszName,
        &rgcst[icst].luConnectNonce, &luResponse, &fKeyXchg);
    if(!rgcst[icst].dmplCur)
        return fKeyXchg ? XBDM_KEYXCHG : XBDM_CANNOTACCESS;
    return XBDM_NOERR;
}

HRESULT HrSetAdminPasswd(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    int icst;
    ULARGE_INTEGER luPasswd;
    ULARGE_INTEGER luT;
    PULARGE_INTEGER plu;

    if(PchGetParam(sz, "none", FALSE)) {
        g_fAdminPasswd = FALSE;
        WriteIniFile();
        return XBDM_NOERR;
    }

    /* We need to establish a key-exchange key.  But if we're running from the
     * ini file, we'll just use a key-exchange key of 0 to load in the
     * plaintext password */
    if(!pdmcc) {
        luT.QuadPart = 0;
        plu = &luT;
    } else {
        icst = (PCST)pdmcc - rgcst;
        if(!(rgcst[icst].dwStatus & CONN_DHKEY))
            return XBDM_KEYXCHG;
        plu = &rgcst[icst].luDHKey;
    }

    if(!FGetQwordParam(sz, "passwd", &luPasswd)) {
        strcpy(szResp, "missing passwd");
        return E_FAIL;
    }
    g_luAdminPasswd.LowPart = luPasswd.LowPart ^ plu->LowPart;
    g_luAdminPasswd.HighPart = luPasswd.HighPart ^ plu->HighPart;
    g_fAdminPasswd = TRUE;
    WriteIniFile();
    return XBDM_NOERR;
}

HRESULT HrGetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szUserName[128];
    DWORD dwPrivileges;
    HRESULT hr;
    int icst;

    dwPrivileges = DmplOfConnection(pdmcc);
    if(PchGetParam(sz, "me", FALSE)) {
        /* We're interested in the privileges for this connection */
        hr = pdmcc ? XBDM_NOERR : E_FAIL;
    } else if(!(dwPrivileges & DMPL_PRIV_MANAGE))
        hr = XBDM_CANNOTACCESS;
    else if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
        strcpy(szResp, "missing name");
        hr = E_FAIL;
    } else
        hr = DmGetUserAccess(szUserName, &dwPrivileges);

    if(SUCCEEDED(hr)) {
        if(dwPrivileges)
            FillAccessPrivSz(szResp-1, dwPrivileges);
        else
            strcpy(szResp, "none");
    }

    return hr;
}

HRESULT HrSetUserPriv(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    char szUserName[128];
    DWORD dwPrivileges;
    HRESULT hr;

    if(!FGetSzParam(sz, "name", szUserName, sizeof szUserName)) {
        strcpy(szResp, "missing name");
        return E_FAIL;
    }

    dwPrivileges = DmplFromSz(sz);
    return DmSetUserAccess(szUserName, dwPrivileges);
}

HRESULT HrReportUsers(PDM_CMDCONT pdmcc, LPSTR szResponse, DWORD cchResponse)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    HRESULT hr;
    DM_USER dusr;

    hr = DmWalkUserList(&pccs->pdmwu, &dusr);
    if(!pdmcc->BytesRemaining)
        hr = XBDM_ENDOFLIST;
    else if(SUCCEEDED(hr))
        FillUserInfoSz(pdmcc->Buffer, dusr.UserName, dusr.AccessPrivileges,
            NULL);
    else {
        DmCloseUserList(pccs->pdmwu);
        hr = XBDM_ENDOFLIST;
    }

    return hr;
}

HRESULT HrListUsers(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    CCS *pccs;
    if(!g_fLockLevel)
        return XBDM_NOTLOCKED;
    if(!pdmcc)
        return E_FAIL;

    pccs = (CCS *)pdmcc->CustomData;
    pdmcc->HandlingFunction = HrReportUsers;
    pccs->pdmwu = NULL;
    return XBDM_MULTIRESPONSE;
}

HRESULT HrPerformDHExchange(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    extern BYTE g_abOakleyGroup1Base[];
    extern BYTE g_abOakleyGroup1Mod[];
    BYTE rgbDHKey[96];
    BYTE rgbY[96];
    int icst;
    HRESULT hr;

    if(pdmcc->CustomData) {
        /* We're sending, so send all of the data */
        if(pdmcc->BytesRemaining) {
            pdmcc->DataSize = 96;
            pdmcc->BytesRemaining = 0;
            hr = XBDM_NOERR;
        } else {
            DmFreePool(pdmcc->Buffer);
            hr = XBDM_ENDOFLIST;
        }
    } else if(pdmcc->DataSize) {
        /* Grab the data that's here */
        memcpy((PUCHAR)pdmcc->Buffer + 2 * 96 - pdmcc->BytesRemaining,
            pdmcc->Buffer, pdmcc->DataSize);
        pdmcc->BytesRemaining -= pdmcc->DataSize;
        if(!pdmcc->BytesRemaining) {
            /* OK, now we've got g^x at buffer+96.  We need to compute y,
             * g^xy, and g^y.  First generate y */
            XNetRandom(rgbY, 96);
            /* Now compute g^xy */
            XcModExp((PULONG)rgbDHKey, (PULONG)((PUCHAR)pdmcc->Buffer + 96),
                (PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
                96 / 4);
            /* And prepare g^y to be sent */
            XcModExp((PULONG)pdmcc->Buffer, (PULONG)g_abOakleyGroup1Base,
                (PULONG)rgbY, (PULONG)g_abOakleyGroup1Mod,
                96 / 4);
            pdmcc->CustomData = pdmcc->Buffer;
            /* Now that we have g^xy, we can hash it and mark the key
             * exchange complete */
            icst = (PCST)pdmcc - rgcst;
            rgcst[icst].luDHKey.QuadPart = 0;
            XBCHashData(&rgcst[icst].luDHKey, rgbDHKey, sizeof rgbDHKey);
            rgcst[icst].dwStatus |= CONN_DHKEY;
            /* Now get ready to send g^y */
            hr = XBDM_BINRESPONSE;
        } else
            hr = XBDM_NOERR;
    } else {
        /* Connection died on receive */
        DmFreePool(pdmcc->Buffer);
        pdmcc->BytesRemaining = 0;
        hr = E_UNEXPECTED;
    }

    return hr;
}

HRESULT HrKeyExchange(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    if(!pdmcc)
        return E_FAIL;

    /* We need to allocate a buffer to build up the remote constant --
     * we can't let it trickle in via the standard buffer */
    pdmcc->BufferSize = 2 * 96;
    pdmcc->Buffer = DmAllocatePoolWithTag(pdmcc->BufferSize, 'HDmd');
    if(!pdmcc->Buffer)
        return E_OUTOFMEMORY;

    /* Set up to receive the remote constant */
    pdmcc->BytesRemaining = 96;
    pdmcc->HandlingFunction = HrPerformDHExchange;
    pdmcc->CustomData = NULL;
    return XBDM_READYFORBIN;
}

HRESULT HrReportMmGlobal(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    extern PMMGLOBALDATA MmGlobalData;
    PMMGLOBALDATA p = MmGlobalData;
    ULONG MmHighestPhysicalPage;
    PCI_SLOT_NUMBER PCISlotNumber;
    MM_STATISTICS mmstat;
    UCHAR MemoryTop;
    PMMPFN MmPfnDatabase;
    HRESULT hr;
    int n;

    switch (pdmcc->BytesRemaining++) {

    case 1:

        //
        // Access the host bridge's configuration space.
        //

        PCISlotNumber.u.AsULONG = 0;
        PCISlotNumber.u.bits.DeviceNumber = XPCICFG_HOSTBRIDGE_DEVICE_ID;
        PCISlotNumber.u.bits.FunctionNumber = XPCICFG_HOSTBRIDGE_FUNCTION_ID;
        HalReadPCISpace(0, PCISlotNumber.u.AsULONG, CR_CPU_MEMTOP_LIMIT,
            &MemoryTop, sizeof(UCHAR));

        MmHighestPhysicalPage = (((ULONG)MemoryTop + 1) * 4096) - 1;

        MmPfnDatabase = (PMMPFN)(MM_SYSTEM_PHYSICAL_MAP +
            (MiGetPdeAddress(MM_SYSTEM_PHYSICAL_MAP)->Hard.PageFrameNumber << PAGE_SHIFT));

        n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
                "MmHighestPhysicalPage=0x%x RetailPfnRegion=0x%x SystemPteRange=0x%x "
                "AvailablePages=0x%x AllocatedPagesByUsage=0x%x PfnDatabase=0x%x",
                MmHighestPhysicalPage, p->RetailPfnRegion, p->SystemPteRange,
                p->AvailablePages, p->AllocatedPagesByUsage, MmPfnDatabase);
        hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
        break;

    case 2:
        mmstat.Length = sizeof(mmstat);
        MmQueryStatistics(&mmstat);
        n = _snprintf(pdmcc->Buffer, pdmcc->BufferSize,
                "AddressSpaceLock=0x%x VadRoot=0x%x VadHint=0x%x VadFreeHint=0x%x "
                "MmNumberOfPhysicalPages=0x%x MmAvailablePages=0x%x",
                p->AddressSpaceLock, p->VadRoot, p->VadHint, p->VadFreeHint,
                mmstat.TotalPhysicalPages, mmstat.AvailablePages);

        hr = n < 0 ? XBDM_BUFFER_TOO_SMALL : XBDM_NOERR;
        break;

    default:
        hr = XBDM_ENDOFLIST;
    }

    return hr;
}

HRESULT HrGetMmGlobal(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    CCS *pccs = (CCS *)pdmcc->CustomData;
    pdmcc->HandlingFunction = HrReportMmGlobal;
    pdmcc->BytesRemaining = 1;
    return XBDM_MULTIRESPONSE;
}

HRESULT HrReturnBuffer(PDM_CMDCONT pdmcc, LPSTR szResponse,
    DWORD cchResponse)
{
    if(!pdmcc->BytesRemaining) {
        DmFreePool(pdmcc->Buffer);
        return XBDM_ENDOFLIST;
    }
    pdmcc->DataSize = pdmcc->BytesRemaining;
    pdmcc->BytesRemaining = 0;
    return XBDM_NOERR;
}

HRESULT HrGetDvdBlk(LPCSTR sz, LPSTR szResp, DWORD cchResp,
    PDM_CMDCONT pdmcc)
{
    DWORD dwBlk;
    CCS *pccs;
    HANDLE h;
    IO_STATUS_BLOCK iosb;
    OBJECT_ATTRIBUTES oa;
    OBJECT_STRING ost;
    NTSTATUS st;
    LARGE_INTEGER li;
    PDEVICE_OBJECT pdev;

    if(!pdmcc)
        return E_FAIL;

    if(!FGetNamedDwParam(sz, "block", &dwBlk, szResp))
        return E_FAIL;

    RtlInitObjectString(&ost, "\\Device\\Cdrom0");
    st = ObReferenceObjectByName(&ost, 0, IoDeviceObjectType, NULL, &pdev);
    if(!NT_SUCCESS(st))
        return E_FAIL;
    pdmcc->Buffer = DmAllocatePool(pdmcc->BufferSize = 2048);
    if(!pdmcc->Buffer) {
        ObDereferenceObject(pdev);
        return E_FAIL;
    }
    li.QuadPart = dwBlk;
    li.QuadPart *= 2048;
    st = IoSynchronousFsdRequest(IRP_MJ_READ, pdev, pdmcc->Buffer,
        2048, &li);
    ObDereferenceObject(pdev);
    if(!NT_SUCCESS(st)) {
        ExFreePool(pdmcc->Buffer);
        return E_FAIL;
    }
    pdmcc->HandlingFunction = HrReturnBuffer;
    pdmcc->BytesRemaining = 2048;
    return XBDM_BINRESPONSE;
}

/* This list must be sorted */
CHH rgchh[] = {
    { "adminpw", DMPL_PRIV_MANAGE, HrSetAdminPasswd },
    { "altaddr", DMPL_PRIV_CONTROL, HrGetAltAddr },
    { "authuser", 0, HrAuthenticateUser },
    { "boxid", 0, HrGetBoxId },
    { "break", DMPL_PRIV_CONTROL, HrDoBreak },
    { "bye", 0, HrEndConversation, 0, CHH_ANYTHREAD },
    { "capcontrol", DMPL_PRIV_CONTROL, HrCAPControl },
    { "continue", DMPL_PRIV_CONTROL, HrContinueThread },
    { "dbgname", 0, HrSetDbgName },
    { "debugger", DMPL_PRIV_CONTROL, HrConnectDebugger },
    { "debugmode", 0, HrSetDebugSecureMode },
    { "dedicate", 0, HrDedicateConnection, 0, CHH_ANYTHREAD },
    { "deftitle", DMPL_PRIV_CONTROL, HrSetDefaultTitle },
    { "delete", 0, HrDeleteFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
    { "dirlist", DMPL_PRIV_READ, HrGetDirList },
    { "drivefreespace", DMPL_PRIV_READ, HrGetDriveFreeSpace },
    { "drivelist", DMPL_PRIV_READ, HrGetDriveList },
    { "dvdblk", DMPL_PRIV_READ, HrGetDvdBlk },
    { "flash", DMPL_PRIV_CONFIGURE, HrFlashKernelImage },
    { "fmtfat", DMPL_PRIV_CONFIGURE, HrFormatFAT },
    { "funccall", DMPL_PRIV_CONTROL, HrFunctionCall },
    { "getcontext", DMPL_PRIV_CONTROL, HrGetContext },
    { "getextcontext", DMPL_PRIV_CONTROL, HrGetExtContext },
    { "getfile", DMPL_PRIV_READ, HrSendFile },
    { "getfileattributes", DMPL_PRIV_READ | DMPL_PRIV_WRITE, HrGetFileAttributes },
    { "getmem", DMPL_PRIV_CONTROL, HrGetMemory },
    { "getuserpriv", 0, HrGetUserPriv },
    { "go", DMPL_PRIV_CONTROL, HrGo },
    { "gpucount", DMPL_PRIV_CONTROL, HrToggleGPUCounters },
    { "halt", DMPL_PRIV_CONTROL, HrHaltThread },
    { "irtsweep", DMPL_PRIV_CONTROL, HrIrtSweep },
    { "isbreak", DMPL_PRIV_CONTROL, HrIsBreak },
    { "isstopped", DMPL_PRIV_CONTROL, HrIsStopped },
    { "kd", DMPL_PRIV_CONTROL, HrToggleKDState },
    { "keyxchg", 0, HrKeyExchange },
    { "lockmode", DMPL_PRIV_MANAGE, HrSetLockMode },
    { "magicboot", DMPL_PRIV_CONTROL, HrMagicReboot },
    { "mkdir", DMPL_PRIV_WRITE, HrMkdir },
    { "mmglobal", DMPL_PRIV_CONTROL, HrGetMmGlobal },
    { "modlong", DMPL_PRIV_CONTROL, HrDoLongName },
    { "modsections", DMPL_PRIV_CONTROL, HrListModuleSections },
    { "modules", DMPL_PRIV_CONTROL, HrListModules },
    { "nostopon", DMPL_PRIV_CONTROL, HrNostopon },
    { "notify", DMPL_PRIV_CONTROL, HrSetupNotify },
    { "notifyat", DMPL_PRIV_CONTROL, HrSetupNotifyAt },
    { "pclist", DMPL_PRIV_CONTROL, HrListCounters },
    { "pssnap", DMPL_PRIV_CONTROL, HrPSSnap },
    { "querypc", DMPL_PRIV_CONTROL, HrQueryPerformanceCounter },
    { "reboot", DMPL_PRIV_CONTROL, HrReboot },
    { "rename", 0, HrRenameFile, DMPL_PRIV_READ | DMPL_PRIV_WRITE },
    { "resume", DMPL_PRIV_CONTROL, HrResumeThread },
    { "screenshot", DMPL_PRIV_CONTROL, HrScreenshot },
    { "sendfile", DMPL_PRIV_WRITE, HrReceiveFile },
    { "setconfig", DMPL_PRIV_CONFIGURE, HrSetConfig },
    { "setcontext", DMPL_PRIV_CONTROL, HrSetContext },
    { "setfileattributes", DMPL_PRIV_WRITE, HrSetFileAttributes },
    { "setmem", DMPL_PRIV_CONTROL, HrSetMemory },
    { "setsystime", DMPL_PRIV_CONFIGURE, HrSetSystemTime },
    { "setuserpriv", DMPL_PRIV_MANAGE, HrSetUserPriv },
    { "stop", DMPL_PRIV_CONTROL, HrStop },
    { "stopon", DMPL_PRIV_CONTROL, HrStopon },
    { "suspend", DMPL_PRIV_CONTROL, HrSuspendThread },
    { "sysfileupd", DMPL_PRIV_CONTROL, HrUpdateSystemFile },
    { "systime", 0, HrSystemTime },
    { "threadinfo", DMPL_PRIV_CONTROL, HrThreadInfo },
    { "threads", DMPL_PRIV_CONTROL, HrListThreads },
    { "title", DMPL_PRIV_CONTROL, HrSetTitle },
    { "user", DMPL_PRIV_MANAGE, HrAddUserCommand },
    { "userlist", DMPL_PRIV_MANAGE, HrListUsers },
    { "vssnap", DMPL_PRIV_CONTROL, HrVSSnap },
    { "xbeinfo", DMPL_PRIV_CONTROL, HrXbeInfo },
    { "xtlinfo", DMPL_PRIV_CONTROL, HrXapiInfo },
};

int cchh = sizeof rgchh / sizeof rgchh[0];
