diff --git a/src/common/Settings.hpp b/src/common/Settings.hpp index 013652272..000ff615d 100644 --- a/src/common/Settings.hpp +++ b/src/common/Settings.hpp @@ -100,7 +100,7 @@ public: unsigned int FlagsLLE; DebugMode KrnlDebugMode; char szKrnlDebug[MAX_PATH] = ""; - char szStorageLocation[MAX_PATH] = ""; + char szStorageLocation[xbox::max_path] = ""; unsigned int LoggedModules[NUM_INTEGERS_LOG]; int LogLevel = 1; bool bUseLoaderExec; diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index 1589c2741..923d5f9c7 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -30,6 +30,7 @@ #include "Mutex.h" #include "common\IPCHybrid.hpp" #include "common\input\Button.h" +#include "common/xbox_types.h" #include "CxbxVersion.h" #include "core/common/imgui/settings.h" #include @@ -245,8 +246,8 @@ class EmuShared : public Mutex // ****************************************************************** // * File storage location // ****************************************************************** - void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, MAX_PATH); Unlock(); } - void SetStorageLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, MAX_PATH); Unlock(); } + void GetStorageLocation(char *path) { Lock(); strncpy(path, m_core.szStorageLocation, xbox::max_path); Unlock(); } + void SetStorageLocation(const char *path) { Lock(); strncpy(m_core.szStorageLocation, path, xbox::max_path); Unlock(); } // ****************************************************************** // * ClipCursor flag Accessors @@ -301,6 +302,22 @@ class EmuShared : public Mutex Unlock(); } + // ****************************************************************** + // * TitleMountPath Accessor + // ****************************************************************** + void GetTitleMountPath(char *value) + { + Lock(); + std::strncpy(value, m_TitleMountPath, sizeof(m_TitleMountPath)); + Unlock(); + } + void SetTitleMountPath(const char* value) + { + Lock(); + std::strncpy(m_TitleMountPath, value, sizeof(m_TitleMountPath) - 1); + Unlock(); + } + // ****************************************************************** // * Reset specific variables to default for kernel mode. // ****************************************************************** @@ -351,6 +368,7 @@ class EmuShared : public Mutex int m_DeviceType[4]; char m_DeviceControlNames[4][HIGHEST_NUM_BUTTONS][HOST_BUTTON_NAME_LENGTH]; char m_DeviceName[4][50]; + char m_TitleMountPath[xbox::max_path]; // Settings class in memory should not be tampered by third-party. // Third-party program should only be allow to edit settings.ini file. diff --git a/src/common/xbox_types.h b/src/common/xbox_types.h index 971fa9a9d..8eac42c5f 100644 --- a/src/common/xbox_types.h +++ b/src/common/xbox_types.h @@ -133,4 +133,9 @@ namespace xbox // ****************************************************************** static_assert(CHAR_BIT == 8); static_assert(sizeof(char16_t) == 2); + + // ****************************************************************** + // Defines + // ****************************************************************** + constexpr uint_xt max_path{ 260 }; // Xbox file path max limitation } diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 13b8af253..ecdd4e56d 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -958,7 +958,7 @@ xbox::dword_xt WINAPI xbox::EMUPATCH(XLaunchNewImageA) if (lpTitlePath == xbox::zeroptr) { // If no path is specified, then the xbe is rebooting to dashboard - char szDashboardPath[MAX_PATH] = { 0 }; + char szDashboardPath[xbox::max_path] = { 0 }; XboxDevice* rootDevice = CxbxDeviceByDevicePath(DeviceHarddisk0Partition2); if (rootDevice != nullptr) sprintf(szDashboardPath, "%s\\xboxdash.xbe", rootDevice->HostDevicePath.c_str()); diff --git a/src/core/kernel/exports/EmuKrnlHal.cpp b/src/core/kernel/exports/EmuKrnlHal.cpp index d719bdfa5..0606b8971 100644 --- a/src/core/kernel/exports/EmuKrnlHal.cpp +++ b/src/core/kernel/exports/EmuKrnlHal.cpp @@ -28,7 +28,6 @@ #define LOG_PREFIX CXBXR_MODULE::HAL - #include // For HalReadSMCTrayState, etc. #include // For PathRemoveFileSpec() #include "Logging.h" // For LOG_FUNC() @@ -540,7 +539,7 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi std::string TitlePath = xbox::LaunchDataPage->Header.szLaunchPath; - char szWorkingDirectoy[MAX_PATH]; + char szWorkingDirectoy[xbox::max_path]; // If the title path starts with a semicolon, remove it if (TitlePath.length() > 0 && TitlePath[0] == ';') { @@ -552,30 +551,7 @@ XBSYSAPI EXPORTNUM(49) xbox::void_xt DECLSPEC_NORETURN NTAPI xbox::HalReturnToFi TitlePath = DeviceHarddisk0Partition2 + "\\xboxdash.xbe"; } - std::string XbePath = TitlePath; - // Convert Xbox XBE Path to Windows Path - { - HANDLE rootDirectoryHandle = nullptr; - std::wstring wXbePath; - // We pretend to come from NtCreateFile to force symbolic link resolution - CxbxConvertFilePath(TitlePath, wXbePath, &rootDirectoryHandle, "NtCreateFile"); - - // Convert Wide String as returned by above to a string, for XbePath - XbePath = utf16_to_ascii(wXbePath.c_str()); - - // If the rootDirectoryHandle is not null, we have a relative path - // We need to prepend the path of the root directory to get a full DOS path - if (rootDirectoryHandle != nullptr) { - char directoryPathBuffer[MAX_PATH]; - GetFinalPathNameByHandle(rootDirectoryHandle, directoryPathBuffer, MAX_PATH, VOLUME_NAME_DOS); - XbePath = directoryPathBuffer + std::string("\\") + XbePath; - - // Trim \\?\ from the output string, as we want the raw DOS path, not NT path - // We can do this always because GetFinalPathNameByHandle ALWAYS returns this format - // Without exception - XbePath.erase(0, 4); - } - } + std::string& XbePath = CxbxConvertXboxToHostPath(TitlePath); // Determine Working Directory { diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 72fcd012e..ecb1aa747 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -90,7 +90,7 @@ static std::vector g_hThreads; char szFilePath_CxbxReloaded_Exe[MAX_PATH] = { 0 }; char szFolder_CxbxReloadedData[MAX_PATH] = { 0 }; char szFilePath_EEPROM_bin[MAX_PATH] = { 0 }; -char szFilePath_Xbe[MAX_PATH*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is MAX_PATH*2 = 520 +char szFilePath_Xbe[xbox::max_path*2] = { 0 }; // NOTE: LAUNCH_DATA_HEADER's szLaunchPath is xbox::max_path*2 = 520 std::string CxbxBasePath; HANDLE CxbxBasePathHandle; @@ -1005,7 +1005,7 @@ void CxbxKrnlEmulate(unsigned int reserved_systems, blocks_reserved_t blocks_res } // Once clean up process is done, proceed set to global variable string. - strncpy(szFilePath_Xbe, xbePath.c_str(), MAX_PATH - 1); + strncpy(szFilePath_Xbe, xbePath.c_str(), xbox::max_path - 1); std::replace(xbePath.begin(), xbePath.end(), ';', '/'); // Load Xbe (this one will reside above WinMain's virtual_memory_placeholder) CxbxKrnl_Xbe = new Xbe(xbePath.c_str(), false); // TODO : Instead of using the Xbe class, port Dxbx _ReadXbeBlock() @@ -1231,6 +1231,24 @@ void LoadXboxKeys(std::string path) EmuLog(LOG_LEVEL::WARNING, "Failed to load Keys.bin. Cxbx-Reloaded will be unable to read Save Data from a real Xbox"); } +//TODO: Possible move CxbxResolveHostToFullPath inline function someplace else if become useful elsewhere. +// Let filesystem library clean it up for us, including resolve host's symbolic link path. +// Since internal kernel do translate to full path than preserved host symoblic link path. +static inline void CxbxResolveHostToFullPath(std::filesystem::path& file_path, std::string_view finish_error_sentence) { + std::error_code error; + std::filesystem::path sanityPath = std::filesystem::canonical(file_path, error); + if (error.value() != 0) { + CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "Could not resolve to %s: %s", finish_error_sentence.data(), file_path.string().c_str()); + } + file_path = sanityPath; +} +// TODO: Eventually, we should remove this function to start using std::filesystem::path method for all host paths. +static inline void CxbxResolveHostToFullPath(std::string& file_path, std::string_view finish_error_sentence) { + std::filesystem::path sanityPath(file_path); + CxbxResolveHostToFullPath(sanityPath, finish_error_sentence); + file_path = sanityPath.string(); +} + __declspec(noreturn) void CxbxKrnlInit ( void *pTLSData, @@ -1341,34 +1359,67 @@ __declspec(noreturn) void CxbxKrnlInit #endif // Initialize devices : - char szBuffer[sizeof(szFilePath_Xbe)]; - g_EmuShared->GetStorageLocation(szBuffer); + { + char szBuffer[sizeof(szFilePath_Xbe)]; + g_EmuShared->GetStorageLocation(szBuffer); - CxbxBasePath = std::string(szBuffer) + "\\EmuDisk\\"; + CxbxBasePath = std::string(szBuffer) + "\\EmuDisk"; + CxbxResolveHostToFullPath(CxbxBasePath, "Cxbx-Reloaded's EmuDisk directory"); + // Since canonical always remove the extra slash, we need to manually add it back. + // TODO: Once CxbxBasePath is filesystem::path, replace CxbxBasePath's + operators to / for include path separator internally. + CxbxBasePath = std::filesystem::path(CxbxBasePath).append("").string(); + } - // Determine XBE Path - strncpy(szBuffer, szFilePath_Xbe, sizeof(szBuffer)-1); - szBuffer[sizeof(szBuffer) - 1] = '\0'; // Safely null terminate at the end. + // Determine xbe path + std::filesystem::path xbePath; + { + std::string szBuffer(szFilePath_Xbe); + std::replace(szBuffer.begin(), szBuffer.end(), ';', '/'); + xbePath = szBuffer; + } + CxbxResolveHostToFullPath(xbePath, "xbe's file"); - std::string xbePath(szBuffer); - std::replace(xbePath.begin(), xbePath.end(), ';', '/'); - std::string xbeDirectory(szBuffer); - size_t lastFind = xbeDirectory.find(';'); + // Determine location for where possible auto mount D letter if ";" delimiter exist. + // Also used to store in EmuShared's title mount path permanent storage on first emulation launch. + // Unless it's launch within Cxbx-Reloaded's EmuDisk directly, then we don't store anything in title mount path storage. + std::string relative_path(szFilePath_Xbe); + size_t lastFind = relative_path.find(';'); // First find if there is a semicolon when dashboard or title disc (such as demo disc) has it. // Then we must obey the current directory it asked for. if (lastFind != std::string::npos) { - if (xbeDirectory.find(';', lastFind + 1) != std::string::npos) { + if (relative_path.find(';', lastFind + 1) != std::string::npos) { CxbxKrnlCleanupEx(LOG_PREFIX_INIT, "Cannot contain multiple of ; symbol."); } - xbeDirectory = xbeDirectory.substr(0, lastFind); + relative_path = relative_path.substr(0, lastFind); } else { - xbeDirectory = xbeDirectory.substr(0, xbeDirectory.find_last_of("\\/")); + relative_path = relative_path.substr(0, relative_path.find_last_of("\\/")); } + CxbxResolveHostToFullPath(relative_path, "xbe's directory"); + CxbxBasePathHandle = CreateFile(CxbxBasePath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - memset(szBuffer, 0, sizeof(szBuffer)); - // Games may assume they are running from CdRom : - CxbxDefaultXbeDriveIndex = CxbxRegisterDeviceHostPath(DeviceCdrom0, xbeDirectory); + // Titles may assume they are running from CdRom0/Mbfs : + std::string_view titleDevice = g_bIsChihiro ? DriveMbfs : DeviceCdrom0; + int CxbxTitleDeviceDriveIndex = -1; + bool isEmuDisk = _strnicmp(relative_path.c_str(), CxbxBasePath.c_str(), CxbxBasePath.size() - 1) == 0; + if (BootFlags == BOOT_NONE) { + // Remember our first initialize mount path for CdRom0/Mbfs. + if (!isEmuDisk) { + g_EmuShared->SetTitleMountPath(relative_path.c_str()); + CxbxTitleDeviceDriveIndex = CxbxRegisterDeviceHostPath(titleDevice, relative_path); + } + else { + g_EmuShared->SetTitleMountPath(""); + } + } + else { + char szBuffer[sizeof(szFilePath_Xbe)]; + g_EmuShared->GetTitleMountPath(szBuffer); + if (szBuffer[0] != '\0') { + CxbxTitleDeviceDriveIndex = CxbxRegisterDeviceHostPath(titleDevice, szBuffer); + } + } + // Partition 0 contains configuration data, and is accessed as a native file, instead as a folder : CxbxRegisterDeviceHostPath(DeviceHarddisk0Partition0, CxbxBasePath + "Partition0", /*IsFile=*/true); // The first two partitions are for Data and Shell files, respectively : @@ -1384,33 +1435,69 @@ __declspec(noreturn) void CxbxKrnlInit // Create default symbolic links : EmuLogInit(LOG_LEVEL::DEBUG, "Creating default symbolic links."); { - // TODO: DriveD should always point to the Xbe Path + // TODO: DriveD should auto mount based on the launchdata page's ; delimiter in the xbe path. // This is the only symbolic link the Xbox Kernel sets, the rest are set by the application, usually via XAPI. // If the Xbe is located outside of the emulated HDD, mounting it as DeviceCdrom0 is correct // If the Xbe is located inside the emulated HDD, the full path should be used, eg: "\\Harddisk0\\partition2\\xboxdash.xbe" - CxbxCreateSymbolicLink(DriveD, DeviceCdrom0); - // Arrange that the Xbe path can reside outside the partitions, and put it to g_hCurDir : - EmuNtSymbolicLinkObject* xbePathSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(CxbxDefaultXbeDriveLetter); - g_hCurDir = xbePathSymbolicLinkObject->RootDirectoryHandle; +#ifdef CXBX_KERNEL_REWORK_ENABLED + if (lastFind != std::string::npos) { +#else + // HACK: It is a hack to override XDK's default mount to CdRom0 which may not exist when launch to dashboard directly. + // Otherwise, titles may launch to dashboard, more specifically xbox live title, and back. + if (CxbxTitleDeviceDriveIndex == -1 || lastFind != std::string::npos) { +#endif + CxbxCreateSymbolicLink(DriveD, relative_path); + // Arrange that the Xbe path can reside outside the partitions, and put it to g_hCurDir : + EmuNtSymbolicLinkObject* xbePathSymbolicLinkObject = FindNtSymbolicLinkObjectByDriveLetter(CxbxAutoMountDriveLetter); + g_hCurDir = xbePathSymbolicLinkObject->RootDirectoryHandle; + } } // Determine Xbox path to XBE and place it in XeImageFileName { - std::string fileName(xbePath); - // Strip out the path, leaving only the XBE file name - // NOTE: we assume that the XBE is always on the root of the D: drive - // This is a safe assumption as the Xbox kernel ALWAYS mounts D: as the Xbe Path - if (fileName.rfind('\\') != std::string::npos) - fileName = fileName.substr(fileName.rfind('\\') + 1); + std::string fileName; + if (xbox::LaunchDataPage == xbox::zeroptr) { + // First launch and possible launch to dashboard + if (isEmuDisk) { + XboxDevice* xbeLoc = CxbxDeviceByHostPath(xbePath.string()); + fileName = xbeLoc->XboxDevicePath; + } + // Otherwise it might be from titleDevice source. + else { + fileName = titleDevice; + } - if (xbox::XeImageFileName.Buffer != NULL) - free(xbox::XeImageFileName.Buffer); + // Strip out the path, leaving only the XBE file name to append. + if (xbePath.has_filename()) { + fileName += "\\" + xbePath.filename().string(); + } + } + else { + // One way to say launch to dashboard. We already load the xbe and check it's xbe type. + if (xbox::LaunchDataPage->Header.szLaunchPath[0] == '\0') { + fileName = DeviceHarddisk0Partition2; + + // Strip out the path, leaving only the XBE file name to append. + if (xbePath.has_filename()) { + fileName += "\\" + xbePath.filename().string(); + } + } + // Otherwise, preserve the launch path and replace delimiter. + else { + fileName = xbox::LaunchDataPage->Header.szLaunchPath; + std::replace(fileName.begin(), fileName.end(), ';', '\\'); + } + } + + if (xbox::XeImageFileName.Buffer != xbox::zeroptr) { + xbox::ExFreePool(xbox::XeImageFileName.Buffer); + } // Assign the running Xbe path, so it can be accessed via the kernel thunk 'XeImageFileName' : - xbox::XeImageFileName.MaximumLength = MAX_PATH; - xbox::XeImageFileName.Buffer = (PCHAR)xbox::ExAllocatePool(MAX_PATH); - sprintf(xbox::XeImageFileName.Buffer, "%c:\\%s", CxbxDefaultXbeDriveLetter, fileName.c_str()); - xbox::XeImageFileName.Length = (USHORT)strlen(xbox::XeImageFileName.Buffer); + xbox::XeImageFileName.Length = static_cast(fileName.size()); + xbox::XeImageFileName.MaximumLength = xbox::XeImageFileName.Length + 1; + xbox::XeImageFileName.Buffer = (PCHAR)xbox::ExAllocatePoolWithTag(xbox::XeImageFileName.MaximumLength, 'nFeX'); + strncpy_s(xbox::XeImageFileName.Buffer, xbox::XeImageFileName.MaximumLength,fileName.c_str(), fileName.size()); EmuLogInit(LOG_LEVEL::INFO, "XeImageFileName = %s", xbox::XeImageFileName.Buffer); } diff --git a/src/core/kernel/init/CxbxKrnl.h b/src/core/kernel/init/CxbxKrnl.h index f4df488e6..d2f14a74e 100644 --- a/src/core/kernel/init/CxbxKrnl.h +++ b/src/core/kernel/init/CxbxKrnl.h @@ -223,7 +223,7 @@ extern std::string CxbxKrnl_DebugFileName; extern char szFilePath_CxbxReloaded_Exe[MAX_PATH]; extern char szFolder_CxbxReloadedData[MAX_PATH]; extern char szFilePath_EEPROM_bin[MAX_PATH]; -extern char szFilePath_Xbe[MAX_PATH*2]; +extern char szFilePath_Xbe[xbox::max_path*2]; #ifdef __cplusplus } diff --git a/src/core/kernel/support/EmuFile.cpp b/src/core/kernel/support/EmuFile.cpp index 71e501ea3..84f297f94 100644 --- a/src/core/kernel/support/EmuFile.cpp +++ b/src/core/kernel/support/EmuFile.cpp @@ -39,6 +39,7 @@ #pragma warning(default:4005) #include "core\kernel\init\CxbxKrnl.h" #include "Logging.h" +#include "common/util/strConverter.hpp" // utf16_to_ascii #include @@ -273,9 +274,7 @@ const std::string DeviceHarddisk0Partition17 = DeviceHarddisk0PartitionPrefix + const std::string DeviceHarddisk0Partition18 = DeviceHarddisk0PartitionPrefix + "18"; const std::string DeviceHarddisk0Partition19 = DeviceHarddisk0PartitionPrefix + "19"; const std::string DeviceHarddisk0Partition20 = DeviceHarddisk0PartitionPrefix + "20"; // 20 = Largest possible partition number -const char CxbxDefaultXbeDriveLetter = 'D'; -int CxbxDefaultXbeDriveIndex = -1; EmuNtSymbolicLinkObject* NtSymbolicLinkObjects['Z' - 'A' + 1]; std::vector Devices; @@ -446,6 +445,14 @@ NTSTATUS CxbxConvertFilePath( if (RelativePath.compare(0, 7, "serial:") == 0) return STATUS_UNRECOGNIZED_VOLUME; + // TODO: CDROM0: need access to raw file handle which doesn't exist in file system. + // Similar concept with serial: and perhaps mediaboards. + // Raw handle access to the CDROM0: + /*if (RelativePath.compare(0, 7, "CDROM0:") == 0) { + // we should have a return and likely forward to special handler function, including serial: above. + return ?; + }*/ + // The path seems to be a device path, look it up : NtSymbolicLinkObject = FindNtSymbolicLinkObjectByDevice(RelativePath); // Fixup RelativePath path here @@ -593,16 +600,61 @@ int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath) return -1; } -XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath) +int CxbxDeviceIndexByHostPath(const char * HostDevicePath) { - int DeviceIndex = CxbxDeviceIndexByDevicePath(XboxDevicePath.c_str()); + for (size_t i = 0; i < Devices.size(); i++) + if (_strnicmp(HostDevicePath, Devices[i].HostDevicePath.c_str(), Devices[i].HostDevicePath.length()) == 0) + return(i); + + return -1; +} + +XboxDevice *CxbxDeviceByDevicePath(const std::string_view XboxDevicePath) +{ + int DeviceIndex = CxbxDeviceIndexByDevicePath(XboxDevicePath.data()); if (DeviceIndex >= 0) return &Devices[DeviceIndex]; return nullptr; } -int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevicePath, bool IsFile) +XboxDevice *CxbxDeviceByHostPath(const std::string_view HostDevicePath) +{ + int DeviceIndex = CxbxDeviceIndexByHostPath(HostDevicePath.data()); + if (DeviceIndex >= 0) + return &Devices[DeviceIndex]; + + return nullptr; +} + +// Convert Xbox XBE Path to Host Path +std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath) +{ + HANDLE rootDirectoryHandle = nullptr; + std::wstring wXbePath; + // We pretend to come from NtCreateFile to force symbolic link resolution + CxbxConvertFilePath(XboxDevicePath.data(), wXbePath, &rootDirectoryHandle, "NtCreateFile"); + + // Convert Wide String as returned by above to a string, for XbePath + std::string XbePath = utf16_to_ascii(wXbePath.c_str()); + + // If the rootDirectoryHandle is not null, we have a relative path + // We need to prepend the path of the root directory to get a full DOS path + if (rootDirectoryHandle != nullptr) { + char directoryPathBuffer[MAX_PATH]; + GetFinalPathNameByHandle(rootDirectoryHandle, directoryPathBuffer, MAX_PATH, VOLUME_NAME_DOS); + XbePath = directoryPathBuffer + std::string("\\") + XbePath; + + // Trim \\?\ from the output string, as we want the raw DOS path, not NT path + // We can do this always because GetFinalPathNameByHandle ALWAYS returns this format + // Without exception + XbePath.erase(0, 4); + } + + return XbePath; +} + +int CxbxRegisterDeviceHostPath(const std::string_view XboxDevicePath, std::string HostDevicePath, bool IsFile) { XboxDevice newDevice; newDevice.XboxDevicePath = XboxDevicePath; @@ -611,7 +663,7 @@ int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevic bool succeeded{ false }; // All HDD partitions have a .bin file to allow direct file io on the partition info - if (_strnicmp(XboxDevicePath.c_str(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { + if (_strnicmp(XboxDevicePath.data(), DeviceHarddisk0PartitionPrefix.c_str(), DeviceHarddisk0PartitionPrefix.length()) == 0) { std::string partitionHeaderPath = HostDevicePath + ".bin"; if (!std::filesystem::exists(partitionHeaderPath)) { CxbxCreatePartitionHeaderFile(partitionHeaderPath, XboxDevicePath == DeviceHarddisk0Partition0); @@ -622,7 +674,8 @@ int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevic // If this path is not a raw file partition, create the directory for it if (!IsFile) { - succeeded = std::filesystem::exists(HostDevicePath) || std::filesystem::create_directory(HostDevicePath); + std::error_code error; // We do not want filesystem to throw an exception on directory creation. Instead, listen for return value to fail. + succeeded = std::filesystem::exists(HostDevicePath) || std::filesystem::create_directory(HostDevicePath, error); } if (succeeded) { @@ -636,14 +689,15 @@ int CxbxRegisterDeviceHostPath(std::string XboxDevicePath, std::string HostDevic } -NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath) +xbox::ntstatus_xt CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath) { - NTSTATUS result = 0; + xbox::ntstatus_xt result = 0; EmuNtSymbolicLinkObject* SymbolicLinkObject = FindNtSymbolicLinkObjectByName(SymbolicLinkName); - if (SymbolicLinkObject != NULL) - // In that case, close it (will also delete if reference count drops to zero) - SymbolicLinkObject->NtClose(); + // If symbolic link exist, return object name collsion. Do NOT delete existing symlink object! + if (SymbolicLinkObject != NULL) { + return xbox::status_object_name_collision; + } // Now (re)create a symbolic link object, and initialize it with the new definition : SymbolicLinkObject = new EmuNtSymbolicLinkObject(); @@ -660,7 +714,7 @@ NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::strin { NTSTATUS result = STATUS_OBJECT_NAME_INVALID; int i = 0; - int DeviceIndex = 0; + int DeviceIndex = -1; // If aFullPath is an empty string, set it to the CD-ROM drive // This should work for all titles, as CD-ROM is mapped to the current working directory @@ -688,29 +742,34 @@ NTSTATUS EmuNtSymbolicLinkObject::Init(std::string aSymbolicLinkName, std::strin // Make a distinction between Xbox paths (starting with '\Device'...) and host paths : IsHostBasedPath = _strnicmp(aFullPath.c_str(), DevicePrefix.c_str(), DevicePrefix.length()) != 0; - if (IsHostBasedPath) - DeviceIndex = CxbxDefaultXbeDriveIndex; - else + if (IsHostBasedPath) { + DeviceIndex = CxbxDeviceIndexByHostPath(aFullPath.c_str()); + } + else { DeviceIndex = CxbxDeviceIndexByDevicePath(aFullPath.c_str()); + } if (DeviceIndex >= 0) { result = xbox::status_success; SymbolicLinkName = aSymbolicLinkName; - if (IsHostBasedPath) - { - XboxSymbolicLinkPath = ""; + if (IsHostBasedPath) { + // Handle the case where a sub folder of the partition is mounted (instead of it's root) : + std::string ExtraPath = aFullPath.substr(Devices[DeviceIndex].HostDevicePath.length(), std::string::npos); + + XboxSymbolicLinkPath = Devices[DeviceIndex].XboxDevicePath + ExtraPath; HostSymbolicLinkPath = aFullPath; + } - else - { + else { XboxSymbolicLinkPath = aFullPath; HostSymbolicLinkPath = Devices[DeviceIndex].HostDevicePath; // Handle the case where a sub folder of the partition is mounted (instead of it's root) : std::string ExtraPath = aFullPath.substr(Devices[DeviceIndex].XboxDevicePath.length(), std::string::npos); - if (!ExtraPath.empty()) + if (!ExtraPath.empty()) { HostSymbolicLinkPath = HostSymbolicLinkPath + ExtraPath; + } } RootDirectoryHandle = CreateFile(HostSymbolicLinkPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); diff --git a/src/core/kernel/support/EmuFile.h b/src/core/kernel/support/EmuFile.h index c82b0f6b8..c1249ac91 100644 --- a/src/core/kernel/support/EmuFile.h +++ b/src/core/kernel/support/EmuFile.h @@ -91,8 +91,7 @@ extern const std::string DeviceHarddisk0Partition17; extern const std::string DeviceHarddisk0Partition18; extern const std::string DeviceHarddisk0Partition19; extern const std::string DeviceHarddisk0Partition20; -extern const char CxbxDefaultXbeDriveLetter; -extern int CxbxDefaultXbeDriveIndex; +constexpr char CxbxAutoMountDriveLetter = 'D'; extern std::string CxbxBasePath; extern HANDLE CxbxBasePathHandle; @@ -219,9 +218,11 @@ struct XboxDevice { CHAR* NtStatusToString(IN NTSTATUS Status); -int CxbxRegisterDeviceHostPath(std::string XboxFullPath, std::string HostDevicePath, bool IsFile = false); +int CxbxRegisterDeviceHostPath(std::string_view XboxFullPath, std::string HostDevicePath, bool IsFile = false); int CxbxDeviceIndexByDevicePath(const char *XboxDevicePath); -XboxDevice *CxbxDeviceByDevicePath(const std::string XboxDevicePath); +XboxDevice *CxbxDeviceByDevicePath(const std::string_view XboxDevicePath); +XboxDevice* CxbxDeviceByHostPath(const std::string_view HostPath); +std::string CxbxConvertXboxToHostPath(const std::string_view XboxDevicePath); char SymbolicLinkToDriveLetter(std::string aSymbolicLinkName); EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByDriveLetter(const char DriveLetter); @@ -231,7 +232,7 @@ EmuNtSymbolicLinkObject* FindNtSymbolicLinkObjectByRootHandle(HANDLE Handle); void CleanupSymbolicLinks(); HANDLE CxbxGetDeviceNativeRootHandle(std::string XboxFullPath); -NTSTATUS CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath); +xbox::ntstatus_xt CxbxCreateSymbolicLink(std::string SymbolicLinkName, std::string FullPath); std::wstring string_to_wstring(std::string const & src); std::wstring PUNICODE_STRING_to_wstring(NtDll::PUNICODE_STRING const & src);