diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp index 59dc389a5..bf1198767 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.cpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.cpp @@ -514,9 +514,9 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSound_SetEffectData) } // ****************************************************************** -// * patch: IDirectSound_DownloadEffectsImage +// * patch: CDirectSound_DownloadEffectsImage // ****************************************************************** -xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSound_DownloadEffectsImage) +xbox::hresult_xt WINAPI xbox::EMUPATCH(CDirectSound_DownloadEffectsImage) ( LPDIRECTSOUND8 pThis, LPCVOID pvImageBuffer, @@ -526,17 +526,76 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(IDirectSound_DownloadEffectsImage) { DSoundMutexGuardLock; - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pThis) - LOG_FUNC_ARG(pvImageBuffer) - LOG_FUNC_ARG(dwImageSize) - LOG_FUNC_ARG(pImageLoc) - LOG_FUNC_ARG(ppImageDesc) - LOG_FUNC_END; + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pThis) + LOG_FUNC_ARG(pvImageBuffer) + LOG_FUNC_ARG(dwImageSize) + LOG_FUNC_ARG(pImageLoc) + LOG_FUNC_ARG(ppImageDesc) + LOG_FUNC_END; // This function is relative to DSP for Interactive 3-D Audio Level 2 (I3DL2) - LOG_NOT_SUPPORTED(); + LOG_INCOMPLETE(); + + if (ppImageDesc) { // If the guest code request a pointer to ImageDesc structure to be returned, then we should allocate a memory and create ImageDesc. + // Code block below is reversed from Otogi. + // ImageBuffer header starts from offset ox800 and ends in offset 0x817. actual DSP code segment starts from offset 0x818. + // DWORD at offset 0x804 and offset 0x80C should be the code segment sizes in dwords. + // ImageDesc is appended after code segments. There are additional two DWORDs data for each effect appended after ImageDesc which I have no idea what they are for. + + // Image buffer must be copied to internal and backed up 1st. the data might be referenced by the guest code later. + // The Code Segment Address and State address in DSEFFECTMAP aEffectMaps[1] must be rebased to the internal buffer. + + // NOTE: this buffer should also be freed in IDirectSound_Release when the ref counter drops to zero, but that function is currently not implemented. + static PBYTE ImageBufferBackup = zeroptr; //The image buffer must be backed up since some game might reference ths data. ex. PGR2. + if (ImageBufferBackup != zeroptr) { + ExFreePool(ImageBufferBackup); + } + + ImageBufferBackup = static_cast(ExAllocatePool(dwImageSize)); + if (ImageBufferBackup == zeroptr) { + return E_OUTOFMEMORY; + } + + std::memcpy(ImageBufferBackup, pvImageBuffer, dwImageSize); // Copy the ImageBuffer to internal backup Buffer. + + // from here, all process should base on internal image buffer. + dword_xt N1 = *(PDWORD)(ImageBufferBackup + 0x804); //Total code segment size in dwords + dword_xt N2 = *(PDWORD)(ImageBufferBackup + 0x80C); //Total state segment size in dwords + PBYTE pImageDesc = ImageBufferBackup + 0x818 + 4 * (N1 + N2); //calculate the starting address of ImageDesc in image buffer + DWORD EffectCount = ((LPDSEFFECTIMAGEDESC)pImageDesc)->dwEffectCount; //the first DWORD in ImageDesc is EffectCount. + dword_xt ImageDescSize = 8 + 32 * EffectCount; //The size of ImageDesc is two Dwords (8 bytes) + 8 DWORS (32 bytes) for each effects. + + // Process the DSEFFECTMAP in internal image buffer, rebase code segmemt address and state segment address of each effect. + PBYTE pEffectMaps = (pImageDesc + 8); //EffectMaps array start from here. + for (int effect_loop = 0; effect_loop < EffectCount; effect_loop++) { + PBYTE pCodeSeg = pEffectMaps + effect_loop * 32; + *(PDWORD)pCodeSeg += (DWORD)ImageBufferBackup; + + PBYTE pStateSeg = pEffectMaps + effect_loop * 32+8; + *(PDWORD)pStateSeg += (DWORD)ImageBufferBackup; + } + + // NOTE: this buffer should also be freed in IDirectSound_Release when the ref counter drops to zero, but that function is currently not implemented. + static PBYTE ImageDescBuffer = zeroptr; + if (ImageDescBuffer != zeroptr) { + ExFreePool(ImageDescBuffer); + } + + ImageDescBuffer = static_cast(ExAllocatePool(ImageDescSize)); + if (ImageDescBuffer == zeroptr) { + return E_OUTOFMEMORY; + } + + // NOTE: this is very wrong. The dsp image is encrypted, and thus simply copying the original encrypted image won't do any good. + *ppImageDesc = ImageDescBuffer; + std::memcpy(*ppImageDesc, pImageDesc, ImageDescSize); + + // with the code above, we could easily retrieve the address and size of ImageDesc within the image buffer. + // then we can allocate a new memory, copy the imageDesc from the image buffer to the newly allocated memory, + // then assign the newly allocated memory to the ppImageDesc. that's all. + } return S_OK; } diff --git a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp index e87167707..cefdaf186 100644 --- a/src/core/hle/DSOUND/DirectSound/DirectSound.hpp +++ b/src/core/hle/DSOUND/DirectSound/DirectSound.hpp @@ -127,6 +127,50 @@ struct SharedDSBuffer : DSBUFFER_S { } }; +typedef struct _DSEFFECTMAP { + LPVOID lpvCodeSegment; + DWORD dwCodeSize; + LPVOID lpvStateSegment; + DWORD dwStateSize; + LPVOID lpvYMemorySegment; + DWORD dwYMemorySize; + LPVOID lpvScratchSegment; + DWORD dwScratchSize; +} DSEFFECTMAP, *LPDSEFFECTMAP; +/*Members +lpvCodeSegment +Starting address of the DSP code segment for this effect. +dwCodeSize +Value that contains the code segment size, in DWORDs. +lpvStateSegment +Starting address of the effect state segment. +dwStateSize +Value that contains the size of the effect state segment, in DWORDs. +lpvYMemorySegment +Starting address of the DSP Y-memory segment. +dwYMemorySize +Value that contains the Y-memory segment size, in DWORDs. +lpvScratchSegment +Starting address of the scratch memory segment. +dwScratchSize +Value that contains the size of the scratch segment, in DWORDs. +*/ + +typedef struct _DSEFFECTIMAGEDESC { + DWORD dwEffectCount; + DWORD dwTotalScratchSize; + DSEFFECTMAP aEffectMaps[1]; +} DSEFFECTIMAGEDESC, *LPDSEFFECTIMAGEDESC; + +/*Members +dwEffectCount +Value that contains the number of effects in the image. +dwTotalScratchSize +Value that contains the total amount of space required by effects that use scratch space for delay lines. +aEffectMaps +Variable-length array that contains the effect descriptions. +*/ + //Custom flags (4 bytes support up to 31 shifts,starting from 0) #define DSE_FLAG_PCM (1 << 0) #define DSE_FLAG_XADPCM (1 << 1) @@ -414,9 +458,9 @@ xbox::hresult_xt WINAPI EMUPATCH(IDirectSound_SynchPlayback) ); // ****************************************************************** -// * patch: IDirectSound_DownloadEffectsImage +// * patch: CDirectSound_DownloadEffectsImage // ****************************************************************** -xbox::hresult_xt WINAPI EMUPATCH(IDirectSound_DownloadEffectsImage) +xbox::hresult_xt WINAPI EMUPATCH(CDirectSound_DownloadEffectsImage) ( LPDIRECTSOUND8 pThis, LPCVOID pvImageBuffer, diff --git a/src/core/hle/DSOUND/DirectSound/XFileMediaObject.cpp b/src/core/hle/DSOUND/DirectSound/XFileMediaObject.cpp index fb85b8a66..e4b0bc575 100644 --- a/src/core/hle/DSOUND/DirectSound/XFileMediaObject.cpp +++ b/src/core/hle/DSOUND/DirectSound/XFileMediaObject.cpp @@ -108,16 +108,156 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(XAudioDownloadEffectsImage) { DSoundMutexGuardLock; - LOG_FUNC_BEGIN - LOG_FUNC_ARG(pszImageName) - LOG_FUNC_ARG(pImageLoc) - LOG_FUNC_ARG(dwFlags) - LOG_FUNC_ARG(ppImageDesc) - LOG_FUNC_END; + LOG_FUNC_BEGIN + LOG_FUNC_ARG(pszImageName) + LOG_FUNC_ARG(pImageLoc) + LOG_FUNC_ARG(dwFlags) + LOG_FUNC_ARG(ppImageDesc) + LOG_FUNC_END; - LOG_NOT_SUPPORTED(); + LOG_INCOMPLETE(); - return S_OK; + xbox::hresult_xt result = S_OK; + if (ppImageDesc) { //only process image section/file which the guest code asks for ImageDesc. + + PBYTE pvImageBuffer; + dword_xt dwImageSize; + + if (dwFlags & 1) { // dwFlags == XAUDIO_DOWNLOADFX_XBESECTION, The DSP effects image is located in a section of the XBE. + /* + //future code for loading imgae from XBE section. these codes are reversd from PGR2. + + PXBEIMAGE_SECTION pImageSectionHandle=XGetSectionHandle(pszImageName); //get section handle by section name, not implemented yet. + // perhaps use pImageSectionHandle = CxbxKrnl_Xbe->FindSection(pszImageName); will be easier. + + if (XeLoadSection(pImageSectionHandle) > 0) { //load section handle and get the loaded address. + //note this sction must be freed after the internal image bacup and ImageDesc was created. + //EmuKnrlXe.cpp implements XeLoadSection(). could reference that code. + pvImageBuffer = pImageSectionHandle->VirtualAddress; + } + + dwImageSize=pImageSectionHandle->VirtualSize; //get section size by section handle. + + result = xbox::EMUPATCH(CDirectSound_DownloadEffectsImage)(pThis_tmp, pvImageBuffer,dwImageSize,pImageLoc,ppImageDesc); + + if(pImageSectionHandle<>0 && pImageSectionHandle!=-1) + XeUnloadSection(pImageSectionHandle); + + */ + + LOG_TEST_CASE("Loading dsp images from xbe sections is currently not yet supported"); + + result = S_OK;//this line should be removed once the section loading code was implemented. + } + else { // load from file + LPDIRECTSOUND8 pThis_tmp = zeroptr; + HANDLE hFile; + DWORD dwBytesRead; + + // using xbox::NtCreateFile() directly instead of Host CreateFile(); + OBJECT_ATTRIBUTES obj; + ANSI_STRING file_name; + IO_STATUS_BLOCK io_status_block; + RtlInitAnsiString(&file_name, pszImageName); + XB_InitializeObjectAttributes(&obj, &file_name, obj_case_insensitive, ObDosDevicesDirectory()); + ntstatus_xt NtStatusCreateFile; + //LARGE_INTEGER tmp_LargeInt; + //tmp_LargeInt.QuadPart= dwImageSize; + NtStatusCreateFile = NtCreateFile( + &hFile, + FILE_GENERIC_READ, // FILE_READ_DATA, GENERIC_READ, DesiredAccess, + &obj, + &io_status_block, + zeroptr, // AllocationSize OPTIONAL, must be none zero, no effect for read acceess. + FILE_ATTRIBUTE_NORMAL, // FileAttributes, + FILE_SHARE_READ, // ShareAccess, + FILE_OPEN, // CreateDisposition, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE); // CreateOptions; CreateFileA Convert dwCreationDisposition== 3 OPEN_EXISTING to CreateOptions = 1 FILE_DIRECTORY_FILE!!?? but with 1, this will fail. + + //process possible error with NtCreateFile() + if (NtStatusCreateFile < 0) { + //ULONG DOSERRORNtCreateFile=RtlNtStatusToDosError(NtStatusCreateFile); + EmuLog(LOG_LEVEL::WARNING, "%s: Image file NtCreateFile() error", __func__); + if (NtStatusCreateFile == status_object_name_collision) { + EmuLog(LOG_LEVEL::WARNING, "%s: Image file name collision", __func__); + } + else if (NtStatusCreateFile == status_file_is_a_directory) { + EmuLog(LOG_LEVEL::WARNING, "%s: Image file name is a directory or invalid", __func__); + } + hFile= INVALID_HANDLE_VALUE; + } + + if (hFile != INVALID_HANDLE_VALUE) { + FILE_STANDARD_INFORMATION FileStdInfo; + NTSTATUS NtStatusQueryInfoFile = NtQueryInformationFile( + hFile, + &io_status_block, + &FileStdInfo, // FileInformation + sizeof(FILE_STANDARD_INFORMATION), + FileStandardInformation); // FileInformationClass; Enumation of the file information class. + if (NtStatusQueryInfoFile >= 0) { + dwImageSize = FileStdInfo.EndOfFile.u.LowPart; + } + else { + EmuLog(LOG_LEVEL::WARNING, "%s: Image file NtQueryInformationFile() error.", __func__); + dwImageSize = 0; + } + + if (dwImageSize > 0) { //proceed the process only if the file size > 0 + pvImageBuffer = new BYTE[dwImageSize]; //allocate buffer to read in to image file. + + //use NtReadFile() to replace host CreatFile(); + ntstatus_xt NtStatusReadFile = NtReadFile( + hFile, + 0, // Event OPTIONAL + 0, // ApcRoutine OPTIONAL + 0, // ApcContext + &io_status_block, + pvImageBuffer, + dwImageSize, + zeroptr); // ByteOffset OPTIONAL + + DWORD dwBytesRead = 0; + if (NtStatusReadFile == status_pending) { + NtStatusReadFile = NtWaitForSingleObject(hFile, 0, 0); + if (NtStatusReadFile < 0){ //something wrong + EmuLog(LOG_LEVEL::WARNING, "%s: Image file NtReadFile error", __func__); + if (NtStatusReadFile != status_end_of_file) { + if ((NtStatusReadFile & 0xC0000000) == 0x80000000) { //Error happened during file reading + dwBytesRead = io_status_block.Information; + EmuLog(LOG_LEVEL::WARNING, "%s: NtReadFile read file end", __func__); + // ULONG DOSErrorNtReadFile = RtlNtStatusToDosError(NtStatusReadFile); this is supposed to be the error code of xbox::CreateFile() + } + } else { + dwBytesRead = 0; + } + } + NtStatusReadFile = io_status_block.Status; + } + if (NtStatusReadFile >= 0) { + dwBytesRead = io_status_block.Information; + } + + if (dwBytesRead == dwImageSize) { // only process the image if the whole image was read successfully. + result = xbox::EMUPATCH(CDirectSound_DownloadEffectsImage)(pThis_tmp, pvImageBuffer,dwImageSize,pImageLoc,ppImageDesc); + } + else { + EmuLog(LOG_LEVEL::WARNING, "%s: Image file NtReadFile read in lenth not enough", __func__); + } + + if (pvImageBuffer) { + delete[] pvImageBuffer; + } + + if (hFile != INVALID_HANDLE_VALUE) { + NtClose(hFile); + } + } + } + } + } + + return result; } // ****************************************************************** diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index e27f43424..7fa2bb3f3 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -312,7 +312,8 @@ std::map g_PatchTable = { PATCH_ENTRY("IDirectSound_CommitEffectData", xbox::EMUPATCH(IDirectSound_CommitEffectData), PATCH_HLE_DSOUND), PATCH_ENTRY("IDirectSound_CreateSoundBuffer", xbox::EMUPATCH(IDirectSound_CreateSoundBuffer), PATCH_HLE_DSOUND), PATCH_ENTRY("IDirectSound_CreateSoundStream", xbox::EMUPATCH(IDirectSound_CreateSoundStream), PATCH_HLE_DSOUND), - PATCH_ENTRY("IDirectSound_DownloadEffectsImage", xbox::EMUPATCH(IDirectSound_DownloadEffectsImage), PATCH_HLE_DSOUND), +// PATCH_ENTRY("IDirectSound_DownloadEffectsImage", xbox::EMUPATCH(IDirectSound_DownloadEffectsImage), PATCH_HLE_DSOUND), + PATCH_ENTRY("CDirectSound_DownloadEffectsImage", xbox::EMUPATCH(CDirectSound_DownloadEffectsImage), PATCH_HLE_DSOUND), PATCH_ENTRY("IDirectSound_EnableHeadphones", xbox::EMUPATCH(IDirectSound_EnableHeadphones), PATCH_HLE_DSOUND), PATCH_ENTRY("IDirectSound_GetCaps", xbox::EMUPATCH(IDirectSound_GetCaps), PATCH_HLE_DSOUND), PATCH_ENTRY("IDirectSound_GetEffectData", xbox::EMUPATCH(IDirectSound_GetEffectData), PATCH_HLE_DSOUND), diff --git a/src/core/kernel/common/ob.h b/src/core/kernel/common/ob.h index 5df648399..a63a1251e 100644 --- a/src/core/kernel/common/ob.h +++ b/src/core/kernel/common/ob.h @@ -35,6 +35,8 @@ typedef struct _OBJECT_HEADER_NAME_INFO { OBJECT_STRING Name; } OBJECT_HEADER_NAME_INFO, *POBJECT_HEADER_NAME_INFO; +inline constexpr dword_xt obj_case_insensitive = 0x40; + #define ObDosDevicesDirectory() ((HANDLE)-3) #define ObWin32NamedObjectsDirectory() ((HANDLE)-4) diff --git a/src/core/kernel/common/types.h b/src/core/kernel/common/types.h index 759483b1e..d579da3a5 100644 --- a/src/core/kernel/common/types.h +++ b/src/core/kernel/common/types.h @@ -92,6 +92,8 @@ inline constexpr dword_xt status_xbe_media_mismatch = 0xC0050002L; inline constexpr dword_xt status_object_name_invalid = 0xC0000033L; inline constexpr dword_xt status_object_name_not_found = 0xC0000034L; inline constexpr dword_xt status_object_name_collision = 0xC0000035L; +inline constexpr dword_xt status_file_is_a_directory = 0xC00000BAL; +inline constexpr dword_xt status_end_of_file = 0xC0000011L; inline constexpr dword_xt status_invalid_page_protection = 0xC0000045L; inline constexpr dword_xt status_conflicting_addresses = 0xC0000018L; inline constexpr dword_xt status_unable_to_free_vm = 0xC000001AL;