public override unsafe Native.NtStatus NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr *apcRoutine, IntPtr *apcContext, ref Native.IO_STATUS_BLOCK ioStatus, byte *buffer, uint length, LARGE_INTEGER *byteOffset, IntPtr key) { var cpk = mCpkByHandle[handle]; var effOffset = Utils.ResolveReadFileOffset(cpk.FilePointer, byteOffset); Unsafe.CopyBlock(buffer, (byte *)cpk.Instance.Native.Ptr + effOffset, length); SetBytesRead(handle, (int)cpk.FilePointer, (int)length, ref ioStatus); return(NtStatus.Success); }
public override unsafe Native.NtStatus NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr *apcRoutine, IntPtr *apcContext, ref Native.IO_STATUS_BLOCK ioStatus, byte *buffer, uint length, LARGE_INTEGER *byteOffset, IntPtr key) { NtStatus result; var packHandle = mPacksByHandle[handle]; var pack = packHandle.Instance; var offset = packHandle.FilePointer; var reqOffset = (byteOffset != null || (byteOffset != null && byteOffset->HighPart == -1 && byteOffset->LowPart == FILE_USE_FILE_POINTER_POSITION)) ? byteOffset->QuadPart : -1; var effOffset = reqOffset == -1 ? offset : reqOffset; var dataOffset = pack.Native.Data - pack.Native.Ptr; if ((effOffset + length) <= dataOffset) { // Header read if (effOffset >= sizeof(DwPackHeader)) { // Ensure entry is redirected before entry is read // This improves startup times greatly, as otherwise thousands of redirections could potentially have to be done at // startup var entryIndex = ( int )((effOffset - sizeof(DwPackHeader)) / sizeof(DwPackEntry)); if (!pack.Entries[entryIndex].RedirectAttempted) { pack.Entries[entryIndex].Redirect(mModDb); } } Unsafe.CopyBlock(buffer, pack.Native.Ptr + effOffset, length); SetBytesRead(handle, ( int )offset, ( int )length, ref ioStatus); result = NtStatus.Success; } else if (effOffset >= dataOffset && effOffset < pack.VirtualFileSize) { result = ReadFile(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key, pack, offset, effOffset); } else { mLogger.Error($"{pack.FileName} Hnd: {handle} Unexpected read request!! Offset: {effOffset:X8} Length: {length:X8}"); result = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } if (result != NtStatus.Success) { mLogger.Error($"{pack.FileName} Hnd: {handle} NtReadFile failed with {result}!!!"); } return(result); }
public override unsafe NtStatus NtQueryInformationFileImpl(IntPtr hfile, out Native.IO_STATUS_BLOCK ioStatusBlock, void *fileInformation, uint length, Native.FileInformationClass fileInformationClass) { var result = mHooks.NtQueryInformationFileHook.OriginalFunction(hfile, out ioStatusBlock, fileInformation, length, fileInformationClass); if (!mPacksByHandle.TryGetValue(hfile, out var pack)) { return(result); } if (fileInformationClass == FileInformationClass.FileStandardInformation) { var info = (FILE_STANDARD_INFORMATION *)fileInformation; info->EndOfFile = uint.MaxValue; } else { mLogger.Debug($"NtQueryInformationFileImpl( IntPtr hfile = {hfile}, out Native.IO_STATUS_BLOCK ioStatusBlock, void* fileInformation, length = {length}, fileInformationClass = {fileInformationClass} )"); } return(result); }
public override Native.NtStatus NtCreateFileImpl(string newFilePath, out IntPtr handle, FileAccess access, ref Native.OBJECT_ATTRIBUTES objectAttributes, ref Native.IO_STATUS_BLOCK ioStatus, ref long allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength) { var result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); var fileName = Path.GetFileNameWithoutExtension(newFilePath); var ext = Path.GetExtension(newFilePath); var isWaveBank = ext.Equals(".xwb", StringComparison.OrdinalIgnoreCase); VirtualWaveBank waveBank = null; VirtualSoundBank soundBank = null; if (isWaveBank && !mWaveBankByName.TryGetValue(fileName, out waveBank)) { // Try get sound bank for cue names mSoundBankByName.TryGetValue(fileName, out soundBank); // Wave bank mWaveBankByName[fileName] = waveBank = new VirtualWaveBank(mLogger); // Load wave bank using (var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read, 1024 * 1024)) waveBank.LoadFromFile(newFilePath, fileStream); // Reopen file to reset it result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); ProcessWaveBankEntries(newFilePath, soundBank, waveBank); mLogger.Debug($"{newFilePath} registered"); } else if (!mSoundBankByName.TryGetValue(fileName, out soundBank)) { // Sound bank mSoundBankByName[fileName] = soundBank = new VirtualSoundBank(mLogger); // Load wave bank using (var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read, 1024 * 1024)) soundBank.LoadFromFile(newFilePath, fileStream); // Reopen file to reset it result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); // Find associated wave bank if (!mWaveBankByName.TryGetValue(soundBank.Native.WaveBankNames[0].Name, out waveBank)) { mLogger.Error($"{newFilePath} Can't find wavebank!"); } else { ProcessWaveBankEntries(newFilePath, soundBank, waveBank); } } if (isWaveBank) { mWaveBankByHandle.Add(handle, waveBank); mLogger.Debug($"{waveBank.FileName} Hnd {handle} registered"); } else { mSoundBankByHandle.Add(handle, soundBank); } return(result); }
public override unsafe Native.NtStatus NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr *apcRoutine, IntPtr *apcContext, ref Native.IO_STATUS_BLOCK ioStatus, byte *buffer, uint length, Native.LARGE_INTEGER *byteOffset, IntPtr key) { if (!mWaveBankByHandle.TryGetValue(handle, out var waveBank)) { return(mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key)); } var offset = waveBank.FilePointer; var reqOffset = (byteOffset != null || (byteOffset != null && byteOffset->HighPart == -1 && byteOffset->LowPart == FILE_USE_FILE_POINTER_POSITION)) ? byteOffset->QuadPart : -1; var effOffset = reqOffset == -1 ? offset : reqOffset; var result = NtStatus.Success; var waveDataOffset = waveBank.Native.Header->Segments[(int)WaveBankSegmentIndex.EntryWaveData].Offset; if ((effOffset + length) <= waveDataOffset) { // Header read Unsafe.CopyBlock(buffer, waveBank.Native.Ptr + effOffset, length); SetBytesRead(handle, (int)offset, (int)length, ref ioStatus); result = NtStatus.Success; } else if (effOffset >= waveDataOffset && effOffset < waveBank.VirtualFileSize) { if ((waveBank.Native.Data->Flags & WaveBankFlags.Compact) != 0) { // TODO: compact format result = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } else { ReadEntryWaveData(waveBank, effOffset, handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } } else { mLogger.Error($"{waveBank.FileName} Hnd: {handle} Unexpected read request!! Offset: {effOffset:X8} Length: {length:X8}"); result = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } if (result != NtStatus.Success) { mLogger.Error($"{waveBank.FileName} Hnd: {handle} NtReadFile failed with {result}!!!"); } return(result); }
private NtStatus ReadEntryWaveData(VirtualWaveBank waveBank, long absOffset, IntPtr handle, IntPtr hEvent, IntPtr *apcRoutine, IntPtr *apcContext, ref Native.IO_STATUS_BLOCK ioStatus, byte *buffer, uint length, Native.LARGE_INTEGER *byteOffset, IntPtr key) { var status = NtStatus.Success; var handled = false; var segBaseOffset = waveBank.Native.Header->Segments[(int)WaveBankSegmentIndex.EntryWaveData].Offset; var segOffset = absOffset - segBaseOffset; for (int i = 0; i < waveBank.Entries.Count; i++) { var entry = waveBank.Entries[i]; if (segOffset < entry.Native->PlayRegion.Offset || segOffset >= (entry.Native->PlayRegion.Offset + entry.Native->PlayRegion.Length)) { continue; } var fileDataOffset = segOffset - entry.Native->PlayRegion.Offset; var readEndOffset = fileDataOffset + length; var nextDataOffset = i < waveBank.Entries.Count - 1 ? waveBank.Entries[i + 1].Native->PlayRegion.Offset : (waveBank.VirtualFileSize - segBaseOffset); if (readEndOffset > nextDataOffset) { continue; } handled = true; if (!entry.IsRedirected) { // Trigger cache miss for (int j = 0; j < mCache.Length; j++) { var cacheEntry = mCache[j].Entry; if (mCache[j].Miss()) { mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {j} {cacheEntry.CueName} removed from cache"); } } mLogger.Info($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Data access Offset: 0x{absOffset:X8} Length: 0x{length:X8}"); status = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } else { mLogger.Info($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Data access Offset: 0x{absOffset:X8} Length: 0x{length:X8} redirected to {entry.FilePath}"); status = NtStatus.Success; if (fileDataOffset < 0) { mLogger.Error($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Offset is before start of data!!!"); continue; } else if (fileDataOffset > entry.FileSize) { mLogger.Error($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Offset is after end of data!!!"); //continue; } mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Reading 0x{length:X8} bytes from redirected file at offset 0x{fileDataOffset:X8}"); // Get cached file stream if the file was previously opened or open a new file Stream redirectedStream = null; for (int j = 0; j < mCache.Length; j++) { if (mCache[j].Entry == entry) { // Found entry in cache, increase score mCache[j].Hit(); mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} loaded from cache"); redirectedStream = mCache[j].Stream; break; } else { // Entry is not the one we're looking for, so we lower its score var cacheEntry = mCache[j].Entry; if (mCache[j].Miss()) { mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {j} {cacheEntry.CueName} removed from cache"); } } } if (redirectedStream == null) { // Wasn't found in cache mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} added to cache"); redirectedStream = entry.OpenRead(); for (int j = 0; j < mCache.Length; j++) { if (mCache[j].Entry == null) { mCache[j] = new CacheEntry() { Entry = entry, Score = 1, Stream = redirectedStream }; break;; } } } // Read from redirected file into the buffer try { redirectedStream.Seek(fileDataOffset, SeekOrigin.Begin); var readBytes = redirectedStream.Read(new Span <byte>(( void * )buffer, ( int )length)); SetBytesRead(handle, ( int )waveBank.FilePointer, ( int )length, ref ioStatus); if (readBytes != length) { mLogger.Error($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} File read length doesnt match requested read length!! Expected 0x{length:X8}, Actual 0x{readBytes:X8}"); } mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Wrote redirected file to buffer"); } catch (Exception e) { mLogger.Debug($"{waveBank.FileName} Hnd: {handle} Index: {i} {entry.CueName} Unhandled exception thrown during reading {entry.FileName}: {e}"); } } } if (!handled) { mLogger.Error($"{waveBank.FileName} Hnd: {handle} Unhandled file data read request!! Offset: 0x{absOffset:X8} Length: 0x{length:X8}"); status = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } return(status); }
public override Native.NtStatus NtCreateFileImpl(string filePath, out IntPtr handle, FileAccess access, ref Native.OBJECT_ATTRIBUTES objectAttributes, ref Native.IO_STATUS_BLOCK ioStatus, ref long allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength) { var result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); if (!mPacksByName.TryGetValue(filePath, out var pack)) { mPacksByName[filePath] = pack = new VirtualDwPack(mLogger, filePath); var pacIndex = int.Parse(pack.FileName.Substring(pack.FileName.Length - 5, 5)); var cpkName = pack.FileName.Substring(0, pack.FileName.Length - 5); var cpk = mLoadedCpks.Find(x => x.FileName.Contains(cpkName) && x.Entries.Any(y => y.PacIndex == pacIndex)); pack.Cpk = cpk; if (result != NtStatus.ObjectNameNotFound) { // Load file using (var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read, 1024 * 1024)) pack.LoadFromFile(filePath, fileStream); //pack.AddNewFiles( cpk ); // Reopen file to reset it result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); } else { pack.LoadFromCpk(pacIndex, cpk); handle = GenerateHandle(); ioStatus.Information = (IntPtr)1; ioStatus.Status = 0; result = NtStatus.Success; } mLogger.Debug($"Registered {filePath}"); // Entries are redirected as needed to improve startup performance } else if (result == NtStatus.ObjectNameNotFound) { // Find handle from name if (!mHandleByPack.TryGetValue(pack, out handle)) { handle = GenerateHandle(); } ioStatus.Information = (IntPtr)1; ioStatus.Status = 0; result = NtStatus.Success; } mPacksByHandle[handle] = new VirtualDwPackHandle() { Instance = pack }; mHandleByPack[pack] = handle; mLogger.Debug($"Hnd {handle} {filePath} handle registered"); return(result); }
private NtStatus ReadFile(IntPtr handle, IntPtr hEvent, IntPtr *apcRoutine, IntPtr *apcContext, ref Native.IO_STATUS_BLOCK ioStatus, byte *buffer, uint length, LARGE_INTEGER *byteOffset, IntPtr key, VirtualDwPack pack, long offset, long effOffset) { // File data read NtStatus result = NtStatus.Success; for (int i = 0; i < pack.Entries.Count; i++) { var entry = pack.Entries[i]; var dataOffset = (pack.Native.Data + entry.Native->DataOffset) - pack.Native.Ptr; if (effOffset < dataOffset || effOffset >= (dataOffset + entry.Native->CompressedSize)) { continue; } var fileDataOffset = effOffset - dataOffset; var readEndOffset = fileDataOffset + length; if (readEndOffset > entry.Native->CompressedSize) { continue; } // Make sure the file has been redirected // This is done as late as possible to improve startup times if (!entry.IsRedirected) { mLogger.Info($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Data access Offset: 0x{effOffset:X8} Length: 0x{length:X8}"); result = mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key); } else { mLogger.Info($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Data access Offset: 0x{effOffset:X8} Length: 0x{length:X8} redirected to {entry.RedirectedFilePath}"); result = NtStatus.Success; if (fileDataOffset < 0) { mLogger.Error($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Offset is before start of data!!!"); } else if (fileDataOffset > entry.RedirectedFileSize) { mLogger.Error($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Offset is after end of data!!!"); } mLogger.Debug($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Reading 0x{length:X8} bytes from redirected file at offset 0x{fileDataOffset:X8}"); // Get cached file stream if the file was previously opened or open a new file Stream redirectedStream; if (mCachedFile == entry) { redirectedStream = mCachedFileStream; } else { mCachedFileStream?.Close(); mCachedFile = entry; mCachedFileStream = redirectedStream = entry.OpenRead(); } // Read from redirected file into the buffer try { redirectedStream.Seek(fileDataOffset, SeekOrigin.Begin); var readBytes = redirectedStream.Read(new Span <byte>(( void * )buffer, ( int )length)); SetBytesRead(handle, ( int )offset, ( int )length, ref ioStatus); if (readBytes != length) { mLogger.Error($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) File read length doesnt match requested read length!! Expected 0x{length:X8}, Actual 0x{readBytes:X8}"); } mLogger.Debug($"{pack.FileName} Hnd: {handle} {entry.Native->Path} Idx: ({i}) Wrote redirected file to buffer"); } catch (Exception e) { mLogger.Debug($"{pack.FileName} Hnd: {handle} Idx: {i} {entry.Native->Path} Unhandled exception thrown during reading {entry.RedirectedFilePath}: {e}"); } } // Return early, we're done here return(result); } mLogger.Error($"{pack.FileName} Hnd: {handle} Unhandled file data read request!! Offset: 0x{effOffset:X8} Length: 0x{length:X8}"); return(mHooks.NtReadFileHook.OriginalFunction(handle, hEvent, apcRoutine, apcContext, ref ioStatus, buffer, length, byteOffset, key)); }
public override Native.NtStatus NtCreateFileImpl(string filePath, out IntPtr handle, FileAccess access, ref Native.OBJECT_ATTRIBUTES objectAttributes, ref Native.IO_STATUS_BLOCK ioStatus, ref long allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength) { var result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); if (!mCpkByName.TryGetValue(filePath, out var cpk)) { mCpkByName[filePath] = cpk = new VirtualCpk(mLogger); // Load file using (var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read, 1024 * 1024)) cpk.LoadFromFile(filePath, fileStream); // Reopen file to reset it result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); mLogger.Debug($"Registered {filePath}"); // Redirect entries to a non-existent pac that will be handled by the DwPack redirector cpk.Redirect(mModDb); } mCpkByHandle[handle] = new VirtualCpkHandle() { Instance = cpk }; mLogger.Debug($"Hnd {handle} {filePath} handle registered"); CpkLoaded?.Invoke(this, cpk); return(result); }
public override Native.NtStatus NtCreateFileImpl(string filePath, out IntPtr handle, FileAccess access, ref Native.OBJECT_ATTRIBUTES objectAttributes, ref Native.IO_STATUS_BLOCK ioStatus, ref long allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength) { var result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); if (!mPacksByName.TryGetValue(filePath, out var pack)) { mPacksByName[filePath] = pack = new VirtualDwPack(mLogger); // Load file using (var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read, 1024 * 1024)) pack.LoadFromFile(filePath, fileStream); // Reopen file to reset it result = mHooks.NtCreateFileHook.OriginalFunction(out handle, access, ref objectAttributes, ref ioStatus, ref allocSize, fileAttributes, share, createDisposition, createOptions, eaBuffer, eaLength); mLogger.Debug($"Registered {filePath}"); // Entries are redirected as needed to improve startup performance } mPacksByHandle[handle] = pack; mLogger.Debug($"Hnd {handle} {filePath} handle registered"); return(result); }