diff --git a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h index 07616d4f4..9ee3acb04 100644 --- a/import/OpenXDK/include/xboxkrnl/xboxkrnl.h +++ b/import/OpenXDK/include/xboxkrnl/xboxkrnl.h @@ -1271,6 +1271,18 @@ typedef struct _LAUNCH_DATA_PAGE } LAUNCH_DATA_PAGE, *PLAUNCH_DATA_PAGE; +// ****************************************************************** +// * DASH_LAUNCH_DATA +// ****************************************************************** +typedef struct _DASH_LAUNCH_DATA +{ + DWORD dwReason; + DWORD dwContext; + DWORD dwParameter1; + DWORD dwParameter2; + BYTE Reserved[3072 - 16]; +} DASH_LAUNCH_DATA, *PDASH_LAUNCH_DATA; + // ****************************************************************** // * DISPATCHER_HEADER // ****************************************************************** diff --git a/resource/Cxbx.rc b/resource/Cxbx.rc index 2119e7eee..cc0b630ce 100644 Binary files a/resource/Cxbx.rc and b/resource/Cxbx.rc differ diff --git a/src/Common/Xbe.cpp b/src/Common/Xbe.cpp index 4b91fcaa5..904bcb849 100644 --- a/src/Common/Xbe.cpp +++ b/src/Common/Xbe.cpp @@ -33,15 +33,28 @@ // * All rights reserved // * // ****************************************************************** +#define _XBOXKRNL_DEFEXTRN_ + +// prevent name collisions +namespace xboxkrnl +{ + #include +}; + #include "Xbe.h" #include "CxbxUtil.h" // For RoundUp - +#include // filesystem related functions available on C++ 17 #include // For ctime +#include "devices\LED.h" // For LED::Sequence +#include "CxbxKrnl/CxbxKrnl.h" // For CxbxKrnlPrintUEM +#include "CxbxKrnl/EmuShared.h" // Include this to avoid including EmuXapi.h and EmuD3D8.h + +namespace fs = std::experimental::filesystem; + -#define PAGE_SIZE 0x1000 // construct via Xbe file -Xbe::Xbe(const char *x_szFilename) +Xbe::Xbe(const char *x_szFilename, bool bFromGUI) { char szBuffer[MAX_PATH]; @@ -54,8 +67,38 @@ Xbe::Xbe(const char *x_szFilename) // verify Xbe file was opened successfully if(XbeFile == 0) { - SetFatalError("Could not open Xbe file."); - return; + using namespace fs; // limit its scope inside here + + std::string XbeName = path(x_szFilename).filename().string(); // recover the xbe name + + // NOTE: the check for the existence of the child window is necessary because the user could have previously loaded the dashboard, + // removed/changed the path and attempt to load it again from the recent list, which will crash CxbxInitWindow below + // Note that GetHwnd(), CxbxKrnl_hEmuParent and HalReturnToFirmware are all not suitable here for various reasons + if (XbeName.compare(std::string("xboxdash.xbe")) == 0 && !bFromGUI) + { + // The dashboard could not be found on partition2. This is a fatal error on the Xbox so we display the UEM. The + // error code is different if we have a launch data page + + XTL::CxbxInitWindow(false); + + ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC; + + if (xboxkrnl::LaunchDataPage && xboxkrnl::LaunchDataPage->Header.dwLaunchDataType == LDT_FROM_DASHBOARD) + { + xboxkrnl::PDASH_LAUNCH_DATA pLaunchDashboard = (xboxkrnl::PDASH_LAUNCH_DATA)&(xboxkrnl::LaunchDataPage->LaunchData[0]); + FatalErrorCode += pLaunchDashboard->dwReason; + } + SetLEDSequence(0xE1); // green, red, red, red + CxbxKrnlPrintUEM(FatalErrorCode); // won't return + + // TODO: FATAL_ERROR_XBE_DASH_X2_PASS (requires DVD drive authentication emulation...) + } + else + { + // Report which xbe could not be found + SetFatalError(std::string("Could not open the Xbe file ") + XbeName); + return; + } } printf("OK\n"); diff --git a/src/Common/Xbe.h b/src/Common/Xbe.h index f6b0e6f27..088dc276f 100644 --- a/src/Common/Xbe.h +++ b/src/Common/Xbe.h @@ -51,7 +51,7 @@ class Xbe : public Error { public: // construct via Xbe file - Xbe(const char *x_szFilename); + Xbe(const char *x_szFilename, bool bFromGUI); // deconstructor ~Xbe(); diff --git a/src/Cxbx/ResCxbx.h b/src/Cxbx/ResCxbx.h index 70b20c4e2..d18c0d8f9 100644 --- a/src/Cxbx/ResCxbx.h +++ b/src/Cxbx/ResCxbx.h @@ -14,6 +14,7 @@ #define IDD_ABOUT 119 #define IDR_CONTRIBUTORS 121 #define IDR_COPYING 122 +#define IDS_UEM 123 #define IDC_SET_X 1000 #define IDC_SET_Y 1001 #define IDC_SET_A 1002 diff --git a/src/Cxbx/WndMain.cpp b/src/Cxbx/WndMain.cpp index 56a34bfa0..48b997715 100644 --- a/src/Cxbx/WndMain.cpp +++ b/src/Cxbx/WndMain.cpp @@ -1786,7 +1786,7 @@ void WndMain::OpenXbe(const char *x_filename) strcpy(m_XbeFilename, x_filename); - m_Xbe = new Xbe(m_XbeFilename); + m_Xbe = new Xbe(m_XbeFilename, true); if(m_Xbe->HasError()) { diff --git a/src/CxbxKrnl/CxbxKrnl.cpp b/src/CxbxKrnl/CxbxKrnl.cpp index 9f99cfb38..91aefb75a 100644 --- a/src/CxbxKrnl/CxbxKrnl.cpp +++ b/src/CxbxKrnl/CxbxKrnl.cpp @@ -571,6 +571,9 @@ void CxbxKrnlMain(int argc, char* argv[]) } } + // We must save this handle now to keep the child window working in the case we need to display the UEM + CxbxKrnl_hEmuParent = IsWindow(hWnd) ? hWnd : NULL; + g_CurrentProcessHandle = GetCurrentProcess(); // OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); // Write a header to the log @@ -600,6 +603,7 @@ void CxbxKrnlMain(int argc, char* argv[]) g_IsWine = true; } } + // Now we got the arguments, start by initializing the Xbox memory map : // PrepareXBoxMemoryMap() { @@ -670,7 +674,7 @@ void CxbxKrnlMain(int argc, char* argv[]) { // Load Xbe (this one will reside above WinMain's virtual_memory_placeholder) g_EmuShared->SetXbePath(xbePath.c_str()); - CxbxKrnl_Xbe = new Xbe(xbePath.c_str()); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock() + CxbxKrnl_Xbe = new Xbe(xbePath.c_str(), false); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock() if (CxbxKrnl_Xbe->HasFatalError()) { CxbxKrnlCleanup(CxbxKrnl_Xbe->GetError().c_str()); @@ -740,7 +744,6 @@ void CxbxKrnlMain(int argc, char* argv[]) EntryPoint ^= XOR_EP_KEY[g_XbeType]; // Launch XBE CxbxKrnlInit( - hWnd, XbeTlsData, XbeTls, CxbxKrnl_Xbe->m_LibraryVersion, @@ -790,7 +793,6 @@ void LoadXboxKeys(std::string path) __declspec(noreturn) void CxbxKrnlInit ( - HWND hwndParent, void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *pLibraryVersion, @@ -804,7 +806,6 @@ __declspec(noreturn) void CxbxKrnlInit CxbxKrnl_TLS = pTLS; CxbxKrnl_TLSData = pTLSData; CxbxKrnl_XbeHeader = pXbeHeader; - CxbxKrnl_hEmuParent = IsWindow(hwndParent) ? hwndParent : NULL; CxbxKrnl_DebugMode = DbgMode; CxbxKrnl_DebugFileName = (char*)szDebugFilename; @@ -837,7 +838,7 @@ __declspec(noreturn) void CxbxKrnlInit " pXBEHeaderSize : 0x%.08X\n" " Entry : 0x%.08X\n" ");\n", - GetCurrentThreadId(), hwndParent, pTLSData, pTLS, pLibraryVersion, DbgMode, szDebugFilename, pXbeHeader, dwXbeHeaderSize, Entry); + GetCurrentThreadId(), CxbxKrnl_hEmuParent, pTLSData, pTLS, pLibraryVersion, DbgMode, szDebugFilename, pXbeHeader, dwXbeHeaderSize, Entry); #else printf("[0x%X] INIT: Debug Trace Disabled.\n", GetCurrentThreadId()); #endif @@ -1008,7 +1009,7 @@ __declspec(noreturn) void CxbxKrnlInit // initialize grapchics DbgPrintf("INIT: Initializing render window.\n"); - XTL::CxbxInitWindow(pXbeHeader, dwXbeHeaderSize); + XTL::CxbxInitWindow(true); // Now process the boot flags to see if there are any special conditions to handle int BootFlags = 0; @@ -1296,6 +1297,11 @@ void CxbxKrnlPrintUEM(ULONG ErrorCode) xboxkrnl::XBOX_EEPROM Eeprom; ULONG ResultSize; + int BootFlags; + g_EmuShared->GetBootFlags(&BootFlags); + BootFlags &= ~BOOT_FATAL_ERROR; // clear the fatal error flag to avoid looping here endlessly + g_EmuShared->SetBootFlags(&BootFlags); + NTSTATUS status = xboxkrnl::ExQueryNonVolatileSetting(xboxkrnl::XC_MAX_ALL, &Type, &Eeprom, sizeof(Eeprom), &ResultSize); if (status == STATUS_SUCCESS) @@ -1329,11 +1335,6 @@ void CxbxKrnlPrintUEM(ULONG ErrorCode) g_CxbxFatalErrorCode = ErrorCode; g_CxbxPrintUEM = true; // print the UEM - int BootFlags; - g_EmuShared->GetBootFlags(&BootFlags); - BootFlags ^= BOOT_FATAL_ERROR; // clear the fatal error flag to avoid looping here endlessly - g_EmuShared->SetBootFlags(&BootFlags); - // Sleep forever to prevent continuing the initialization Sleep(INFINITE); } diff --git a/src/CxbxKrnl/CxbxKrnl.h b/src/CxbxKrnl/CxbxKrnl.h index ed34fd26a..26931420b 100644 --- a/src/CxbxKrnl/CxbxKrnl.h +++ b/src/CxbxKrnl/CxbxKrnl.h @@ -177,7 +177,7 @@ bool CxbxKrnlVerifyVersion(const char *szVersion); void CxbxKrnlMain(int argc, char* argv[]); /*! initialize emulation */ -__declspec(noreturn) void CxbxKrnlInit(HWND hwndParent, void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *LibraryVersion, DebugMode DbgMode, const char *szDebugFilename, Xbe::Header *XbeHeader, uint32 XbeHeaderSize, void (*Entry)()); +__declspec(noreturn) void CxbxKrnlInit(void *pTLSData, Xbe::TLS *pTLS, Xbe::LibraryVersion *LibraryVersion, DebugMode DbgMode, const char *szDebugFilename, Xbe::Header *XbeHeader, uint32 XbeHeaderSize, void (*Entry)()); /*! cleanup emulation */ __declspec(noreturn) void CxbxKrnlCleanup(const char *szErrorMessage, ...); diff --git a/src/CxbxKrnl/EmuD3D8.cpp b/src/CxbxKrnl/EmuD3D8.cpp index 7d832b6b1..918dd6cd8 100644 --- a/src/CxbxKrnl/EmuD3D8.cpp +++ b/src/CxbxKrnl/EmuD3D8.cpp @@ -57,6 +57,7 @@ namespace xboxkrnl #include "Logging.h" #include "EmuD3D8Logging.h" #include "HLEIntercept.h" // for bLLE_GPU +#include "Cxbx\\ResCxbx.h" #include #include @@ -97,8 +98,6 @@ static XTL::DDCAPS g_DriverCaps = { 0 }; static DWORD g_dwOverlayW = 640; // Cached Overlay Width static DWORD g_dwOverlayH = 480; // Cached Overlay Height static DWORD g_dwOverlayP = 640; // Cached Overlay Pitch -static Xbe::Header *g_XbeHeader = NULL; // XbeHeader -static uint32 g_XbeHeaderSize = 0; // XbeHeaderSize static HBRUSH g_hBgBrush = NULL; // Background Brush static volatile bool g_bRenderWindowActive = false; static XBVideo g_XBVideo; @@ -452,18 +451,15 @@ const char *D3DErrorString(HRESULT hResult) return buffer; } -VOID XTL::CxbxInitWindow(Xbe::Header *XbeHeader, uint32 XbeHeaderSize) +VOID XTL::CxbxInitWindow(bool bFullInit) { g_EmuShared->GetXBVideo(&g_XBVideo); if(g_XBVideo.GetFullscreen()) CxbxKrnl_hEmuParent = NULL; - // cache XbeHeader and size of XbeHeader - g_XbeHeader = XbeHeader; - g_XbeHeaderSize = XbeHeaderSize; - // create timing thread + if (bFullInit) { DWORD dwThreadId; @@ -499,7 +495,8 @@ VOID XTL::CxbxInitWindow(Xbe::Header *XbeHeader, uint32 XbeHeaderSize) // Ported from Dxbx : // If possible, assign this thread to another core than the one that runs Xbox1 code : - SetThreadAffinityMask(hRenderWindowThread, g_CPUOthers); + if (bFullInit) + SetThreadAffinityMask(hRenderWindowThread, g_CPUOthers); while(!g_bRenderWindowActive) SwitchToThread(); @@ -510,6 +507,70 @@ VOID XTL::CxbxInitWindow(Xbe::Header *XbeHeader, uint32 XbeHeaderSize) SetFocus(g_hEmuWindow); } +void DrawUEM(HWND hWnd) +{ + // Draw the universal error message (UEM) + // See http://xboxdevwiki.net/Fatal_Error + // Only call this from WM_PAINT message! + + PAINTSTRUCT ps; + + BeginPaint(hWnd, &ps); + + HDC hDC = GetDC(hWnd); + HDC hMemDC = CreateCompatibleDC(hDC); + HBITMAP hUEMBmp = CreateCompatibleBitmap(hDC, 640, 480); + HBITMAP hOriUEMBmp = (HBITMAP)SelectObject(hMemDC, hUEMBmp); + + + int nHeight = -MulDiv(8, GetDeviceCaps(hMemDC, LOGPIXELSY), 72); + + HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); + + HGDIOBJ tmpObj = SelectObject(hMemDC, hFont); + + SetBkColor(hMemDC, RGB(0, 0, 0)); + + SetTextColor(hMemDC, RGB(0, 204, 0)); + + wchar_t buff[500]; + LoadStringW(GetModuleHandle(NULL), IDS_UEM, buff, sizeof(buff) / sizeof(wchar_t)); + std::wstring wstr(buff); + + // Unfortunately, DrawTextW doesn't support vertical alignemnt, so we have to do the calculation + // ourselves. See here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/abd89aae-16a0-41c6-8db6-b119ea90b42a/win32-drawtext-how-center-in-vertical-with-new-lines-and-tabs?forum=vclanguage + + RECT rect = { 0, 0, 640, 480 }; + RECT textrect = { 0, 0, 640, 480 }; + DrawTextW(hMemDC, wstr.c_str(), wstr.length(), &textrect, DT_CALCRECT); + rect.top = (rect.bottom - textrect.bottom) / 2; + DrawTextW(hMemDC, wstr.c_str(), wstr.length(), &rect, DT_CENTER); + + + // Draw the Xbox error code + + SetTextColor(hMemDC, RGB(255, 255, 255)); + std::string err_str(std::to_string(g_CxbxFatalErrorCode)); + rect.left = 20; + DrawText(hMemDC, err_str.c_str(), err_str.length(), &rect, DT_LEFT); + + GetClientRect(hWnd, &rect); + SetStretchBltMode(hDC, COLORONCOLOR); + StretchBlt(hDC, rect.left, rect.top, rect.right, rect.bottom, hMemDC, 0, 0, 640, 480, SRCCOPY); + + SelectObject(hMemDC, hOriUEMBmp); + SelectObject(hDC, tmpObj); + + DeleteObject(hUEMBmp); + DeleteObject(hFont); + DeleteObject(hMemDC); + + if (hDC != NULL) + ReleaseDC(hWnd, hDC); + + EndPaint(hWnd, &ps); +} + inline DWORD GetXboxCommonResourceType(const XTL::X_D3DResource *pXboxResource) { // Don't pass in unassigned Xbox resources @@ -1369,75 +1430,7 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar { if (g_CxbxPrintUEM) { - // Draw the universal error message (UEM) - // See http://xboxdevwiki.net/Fatal_Error - - PAINTSTRUCT ps; - - BeginPaint(hWnd, &ps); - - HDC hDC = GetDC(hWnd); - HDC hMemDC = CreateCompatibleDC(hDC); - HBITMAP hUEMBmp = CreateCompatibleBitmap(hDC, 640, 480); - HBITMAP hOriUEMBmp = (HBITMAP)SelectObject(hMemDC, hUEMBmp); - - - int nHeight = -MulDiv(8, GetDeviceCaps(hMemDC, LOGPIXELSY), 72); - - HFONT hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_ROMAN, "Verdana"); - - HGDIOBJ tmpObj = SelectObject(hMemDC, hFont); - - SetBkColor(hMemDC, RGB(0, 0, 0)); - - SetTextColor(hMemDC, RGB(0, 204, 0)); - - std::wstring wstr( - L"Your Xbox requires service.\n\n\ -Please call Xbox Customer Support.\n\n\n\ -Ihre Xbox muss gewartet werden.\n\n\ -Bitte den Xbox-Kundendienst anrufen.\n\n\n\ -La consola Xbox requiere asistencia técnica.\n\n\ -Llame al servicio de soporte al cliente de la Xbox.\n\n\n\ -Xbox ha bisogno di manutenzione.\n\n\ -Chiamare l'Assistenza Clienti di Xbox.\n\n\n\ -Votre Xbox ne fonctionne pas correctement.\n\n\ -Veuillez contacter le Support à la clientèle Xbox.\n\n\n\ -不具合が生じました。お手数ですが、\n\n\ -Xboxカスタマー サポートにお問い合わせください。"); - - // Unfortunately, DrawTextW doesn't support vertical alignemnt, so we have to do the calculation - // ourselves. See here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/abd89aae-16a0-41c6-8db6-b119ea90b42a/win32-drawtext-how-center-in-vertical-with-new-lines-and-tabs?forum=vclanguage - - RECT rect = { 0, 0, 640, 480 }; - RECT textrect = { 0, 0, 640, 480 }; - DrawTextW(hMemDC, wstr.c_str(), wstr.length(), &textrect, DT_CALCRECT); - rect.top = (rect.bottom - textrect.bottom) / 2; - DrawTextW(hMemDC, wstr.c_str(), wstr.length(), &rect, DT_CENTER); - - - // Draw the Xbox error code - - SetTextColor(hMemDC, RGB(255, 255, 255)); - std::string err_str(std::to_string(g_CxbxFatalErrorCode)); - rect.left = 20; - DrawText(hMemDC, err_str.c_str(), err_str.length(), &rect, DT_LEFT); - - GetClientRect(hWnd, &rect); - SetStretchBltMode(hDC, COLORONCOLOR); - StretchBlt(hDC, rect.left, rect.top, rect.right, rect.bottom, hMemDC, 0, 0, 640, 480, SRCCOPY); - - SelectObject(hMemDC, hOriUEMBmp); - SelectObject(hDC, tmpObj); - - DeleteObject(hUEMBmp); - DeleteObject(hFont); - DeleteObject(hMemDC); - - if (hDC != NULL) - ReleaseDC(hWnd, hDC); - - EndPaint(hWnd, &ps); + DrawUEM(hWnd); } } break; diff --git a/src/CxbxKrnl/EmuD3D8.h b/src/CxbxKrnl/EmuD3D8.h index 68de8fcc2..596b6faba 100644 --- a/src/CxbxKrnl/EmuD3D8.h +++ b/src/CxbxKrnl/EmuD3D8.h @@ -43,7 +43,7 @@ #include // initialize render window -extern VOID CxbxInitWindow(Xbe::Header *XbeHeader, uint32 XbeHeaderSize); +extern VOID CxbxInitWindow(bool bFullInit); extern VOID CxbxSetPixelContainerHeader ( diff --git a/src/CxbxKrnl/EmuKrnlHal.cpp b/src/CxbxKrnl/EmuKrnlHal.cpp index e74a2ec57..d4593bed8 100644 --- a/src/CxbxKrnl/EmuKrnlHal.cpp +++ b/src/CxbxKrnl/EmuKrnlHal.cpp @@ -507,7 +507,11 @@ XBSYSAPI EXPORTNUM(49) xboxkrnl::VOID DECLSPEC_NORETURN NTAPI xboxkrnl::HalRetur g_EmuShared->GetXbePath(szWorkingDirectoy); snprintf(szArgsBuffer, 4096, "/load \"%s\" %u %d \"%s\"", szWorkingDirectoy, CxbxKrnl_hEmuParent, CxbxKrnl_DebugMode, CxbxKrnl_DebugFileName.c_str()); if ((int)ShellExecute(NULL, "open", szFilePath_CxbxReloaded_Exe, szArgsBuffer, szWorkingDirectoy, SW_SHOWDEFAULT) <= 32) + { + int BootFlags = 0; + g_EmuShared->SetBootFlags(&BootFlags); // clear all boot flags in the case of failure CxbxKrnlCleanup("Could not reboot"); + } break; }