/*********************************************************************************/
/*                                                                               */
/*  XBOX XBE Launch Code                                                         */
/*                                                                               */
/*  File:   launch.cpp                                                           */
/*                                                                               */
/*  Author: bkenwright@xbdev.net                                                 */
/*  Date:   27-02-04                                                             */
/*  URL:    www.xbdev.net                                                        */
/*                                                                               */
/*********************************************************************************/
/*                                                                               */
/*  Platform: XBOX - OpenXDK and XDK                                             */
/*                                                                               */
/*********************************************************************************/
/*                                                                               */
/*  Details:                                                                     */
/*  Well I put together a set of functions so we could launch xbox executables   */
/*  from our code, its been designed so it uses the kernel functions..which are  */
/*  are from the bios.  So the code doesn't really depend on any librarys,       */
/*  which means that our code will work on the openxdk and xdk.                  */
/*                                                                               */
/*  The few external library's I used are for string manipulation..which are     */
/*  in stdio.h and string.h....                                                  */
/*                                                                               */
/*  Not noticed any serious bugs yet...and I've skips a lot of error checking    */
/*  in the code to keep it simple, if you find any bugs or improvements I'm glad */
/*  for any feedback.                                                            */
/*                                                                               */
/*********************************************************************************/
/*                                                                               */
/*                                                                               */
/*  Examples of how to use in your code:                                         */
/*                                                                               */
/*  xRunXbe ( "Harddisk0\\Partition2", "",            "default.xbe"); // msdash  */
/*                                                                               */
/*	xRunXbe ( "Harddisk0\\Partition2", "\\apps\\dvd2","dvd.xbe");     // dvd app */
/*                                                                               */
/*	xRunXbe ( "Harddisk0\\Partition2", "",            "xboxdash.xbe");// evox    */
/*                                                                               */
/*********************************************************************************/


#include <string.h>       // memset(..), strcpy(..), strcat(..), strlen(..)
#include <stdio.h>        // sprintf(..)



/*********************************************************************************/
/*                                                                               */
/*  Here are the external declarations....Now I'm sorry about the naming of the  */
/*  variables...hehe....got lazy while debugging.                                */
/*  But the linker is only concerned with how many bytes are being passed and    */
/*  the name of the function, that is why it works.                              */
/*  Might be better to rename them to a more details naming later on so they     */
/*  make more sense.                                                             */
/*  The real juicy info, is the LaunchDataPage variable in the kernel memory     */
/*  area - which is where we set what is going to boot after our warm boot.      */
/*                                                                               */
/*********************************************************************************/

extern "C" unsigned long * LaunchDataPage;

extern "C" unsigned long __stdcall HalReturnToFirmware(unsigned int a);
extern "C" unsigned long __stdcall MmAllocateContiguousMemory(unsigned int a);
extern "C" unsigned long __stdcall MmPersistContiguousMemory( unsigned int a, 
															  unsigned int b, 
															  unsigned int c);



struct stUNICODE_STRING 
{
  unsigned short Length;
  unsigned short MaximumLength;
  wchar_t *  Buffer;
};// End stUNICODE_STRING struct



extern "C" unsigned long __stdcall IoDeleteSymbolicLink( stUNICODE_STRING * SymbolicLinkName);
extern "C" unsigned long __stdcall IoCreateSymbolicLink( stUNICODE_STRING * SymbolicLinkName, 
														 stUNICODE_STRING * DeviceName);



struct stDataLaunch
{
	unsigned int  dwLaunchDataType;
	char *        TitleID;
	char          szLaunchPath[207];
	unsigned int  dwFlags;
	char          pad[0x1eb];
	char          LaunchData[0xBFF];
};// End stDataLaunch struct





// e.g. szDevice  = "\\Harddisk0\\Partition2"
//      szDir     = "apps\\dvds" or ""
//      szTitle   = "default.xbe"
//      pDashData = 0
void xLaunchXBE(char * szDevice, char * szDir, char * szTitle, void * pDashData )
{
	// Has LaunchDataPage already got allocated memory?
	if( *LaunchDataPage == 0 )
	{
		unsigned long mem = MmAllocateContiguousMemory(0x1000);
		*LaunchDataPage = mem;
	}

	MmPersistContiguousMemory( (unsigned int)*LaunchDataPage, 0x1000, 1);

	stDataLaunch * pMem  = (stDataLaunch*)*LaunchDataPage;

	memset((void*)pMem, 0, 0x1000 );

	pMem->dwLaunchDataType = 0xFFFFFFFF; // -1
	pMem->TitleID = 0;
	
	
	char* pStr = (char*)&(pMem->szLaunchPath[0]);

	strcpy( pStr,  "\\Device\\" );  // -> \\Device\\               <-
	strcat( pStr , szDevice );      // -> \\Harddisk0\\Partition2  <-
	strcat( pStr , szDir );         // -> \\apps\\dvds             <-
	strcat( pStr,  ";" );           // -> ;                        <-
	strcat( pStr,  szTitle );       // -> default.xbe              <-

	// Testing Code
	// strcpy( pStr , "\\Device\\Harddisk0\\Partition2" );
	// strcat( pStr,  ";" );
	// strcat( pStr,  "default.xbe" );


	HalReturnToFirmware(2);
}// End xLaunchXBE(..)





// Unmount the old D: drive
void xUnmountD()
{
	char szDestinationDrive[16];
	strcpy(szDestinationDrive,"\\??\\D:");

	stUNICODE_STRING LinkName = 
	{
		strlen(szDestinationDrive),
		strlen(szDestinationDrive) + 1,
		(wchar_t *)szDestinationDrive
	};

	IoDeleteSymbolicLink(&LinkName);
}// End xUnmountD()




// Mount the D: drive to a given device and directory
// szDevice = "Cdrom0" or "Harddisk0\Partition6"
// szDir = "" or "Game1"
void xMountD(char* szDevice, char* szDir)
{
	char szSourceDevice[256];
	char szDestinationDrive[16];

	strcpy(szDestinationDrive,"\\??\\D:");
	sprintf(szSourceDevice,"\\Device\\%s",szDevice);

	if (*szDir != 0x00 && *szDir != '\\') 
	{
		strcat(szSourceDevice, "\\");
		strcat(szSourceDevice, szDir);
	}

	stUNICODE_STRING LinkName = 
	{
		strlen(szDestinationDrive),
		strlen(szDestinationDrive) + 1,
		(wchar_t *)szDestinationDrive
	};
	
	stUNICODE_STRING DeviceName = 
	{
		strlen(szSourceDevice),
		strlen(szSourceDevice) + 1,
		(wchar_t *)szSourceDevice
	};

	IoCreateSymbolicLink(&LinkName, &DeviceName);
}// End xMountD(..)



// Forward declaration
void xLaunchXBE(char * szDevice, char * szDir, char * szTitlePath, void * pDashData );



// CdRom0
// Harddisk0\\Partition0
// Harddisk0\\Partition1   -> E
// Harddisk0\\Partition2   -> C


// szDevice = the device where the XBE is "Cdrom0"
// szDir = the subdirectory where your xbe is "\Games\Game1"
// szFile = the name of your XBE (without the path) - "Game1.xbe"
void xRunXbe (char* szDevice, char* szDir, char* szFile)
{
	xUnmountD();
	xMountD(szDevice, szDir);

	xLaunchXBE( szDevice, szDir, szFile, NULL );
}// End xRunXbe(..)


/*
  Examples

  	xRunXbe ( "Harddisk0\\Partition2", "",             "default.xbe");  // msdash

	xRunXbe ( "Harddisk0\\Partition2", "\\apps\\dvd2", "default.xbe");  // dvd2 app

	xRunXbe ( "Harddisk0\\Partition2", "",             "xboxdash.xbe"); // evox
*/



/*********************************************************************************/
/*                                                                               */
/*                          Program Entry Point                                  */
/*                                                                               */
/*********************************************************************************/
/*                                                                               */
/*  Few lines of code to test our code, launch different apps etc.               */
/*                                                                               */
/*********************************************************************************/

void main()
{

	//xRunXbe ( "Harddisk0\\Partition2", "",             "default.xbe");  // msdash
	//xRunXbe ( "Harddisk0\\Partition2", "\\apps\\dvd2", "default.xbe");  // dvd2 app
	//xRunXbe ( "Harddisk0\\Partition2",   "",           "xboxdash.xbe"); // evox

	//xRunXbe ( "Harddisk0\\Partition3", "\\DolphinClassic", 
	//                                  "default.xbe");                   // demo
	
	xRunXbe ( "Harddisk0\\Partition1", "\\games\\SnakeX\\executable", 
		                               "default.xbe");                    // wormsx


	while(true); // LockLoop - Loop to lock us here

}// end main()




