Skip to content

Commit

Permalink
Merge pull request #23 from AnimatedSwine37/file-size-bug
Browse files Browse the repository at this point in the history
Fix original file being read when it is bigger than the emulated version
  • Loading branch information
Sewer56 authored Sep 20, 2024
2 parents 97be84b + c3c2ce0 commit 43adc6e
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Emulator/PAK.Stream.Emulator/ModConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"ModId": "reloaded.universal.fileemulationframework.pak",
"ModName": "PAK Emulator for File Emulation Framework",
"ModAuthor": "ltsophia & friends",
"ModVersion": "1.1.0",
"ModVersion": "1.1.1",
"ModDescription": "Simulates Atlus PAK files. Allows mods to add or replace existing files using files on disk.",
"ModDll": "PAK.Stream.Emulator.dll",
"ModIcon": "Preview.png",
Expand Down
53 changes: 23 additions & 30 deletions Emulator/PAK.Stream.Emulator/Pak/PakBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,8 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep
pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length)));
if (length > overwrittenFile.Length)
{
var overstream = new MemoryStream();
byte[] buffer = new byte[length - overwrittenFile.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
overstream.Write(buffer, 0, buffer.Length);
var overstream = new MemoryStream(buffer);
pairs.Add(new(overstream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry + overwrittenFile.Length, buffer.Length)));
}
}
Expand Down Expand Up @@ -179,10 +174,6 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep
{
entryStream.Seek((int)entryStream.Length, SeekOrigin.Begin);
byte[] buffer = new byte[length - (int)entryStream.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
entryStream.Write(buffer, 0, buffer.Length);
}
pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length)));
Expand Down Expand Up @@ -212,6 +203,12 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep
// Merge the slices and add.
foreach (var merged in FileSliceStreamExtensions.MergeStreams(mergeAbleStreams))
pairs.Add(merged);

// Add an extra empty entry header (the game might try to read it)
byte[] dummyBuffer = new byte[sizeof(V1FileEntry)];
MemoryStream dummyHeader = new MemoryStream(dummyBuffer);
pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V1FileEntry))));

// Return MultiStream
return new MultiStream(pairs, logger);
}
Expand Down Expand Up @@ -294,13 +291,8 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep
pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length)));
if (length > overwrittenFile.Length)
{
var overstream = new MemoryStream();
byte[] buffer = new byte[length - overwrittenFile.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
overstream.Write(buffer, 0, buffer.Length);
var overstream = new MemoryStream(buffer);
pairs.Add(new(overstream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry + overwrittenFile.Length, buffer.Length)));
}
}
Expand Down Expand Up @@ -334,10 +326,6 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep
{
entryStream.Seek((int)entryStream.Length, SeekOrigin.Begin);
byte[] buffer = new byte[length - (int)entryStream.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
entryStream.Write(buffer, 0, buffer.Length);
}
pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length)));
Expand Down Expand Up @@ -368,6 +356,13 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep
// Merge the slices and add.
foreach (var merged in FileSliceStreamExtensions.MergeStreams(mergeAbleStreams))
pairs.Add(merged);

// Add an extra empty entry header (the game might try to read it)
byte[] dummyBuffer = new byte[sizeof(V2FileEntry)];
MemoryStream dummyHeader = new MemoryStream(dummyBuffer);
dummyHeader.Write(dummyBuffer, 0, dummyBuffer.Length);
pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V2FileEntry))));

// Return MultiStream
return new MultiStream(pairs, logger);
}
Expand Down Expand Up @@ -450,13 +445,8 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep
pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length)));
if (length > overwrittenFile.Length)
{
var overstream = new MemoryStream();
byte[] buffer = new byte[length - overwrittenFile.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
overstream.Write(buffer, 0, buffer.Length);
var overstream = new MemoryStream(buffer);
pairs.Add(new(overstream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry + overwrittenFile.Length, buffer.Length)));
}
}
Expand Down Expand Up @@ -489,10 +479,6 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep
{
entryStream.Seek((int)entryStream.Length, SeekOrigin.Begin);
byte[] buffer = new byte[length - (int)entryStream.Length];
for (int f = 0; i < buffer.Length; i++)
{
buffer[f] = 0x00;
}
entryStream.Write(buffer, 0, buffer.Length);
}
pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length)));
Expand Down Expand Up @@ -520,6 +506,13 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep
// Merge the slices and add.
foreach (var merged in FileSliceStreamExtensions.MergeStreams(mergeAbleStreams))
pairs.Add(merged);

// Add an extra empty entry header (the game might try to read it)
byte[] dummyBuffer = new byte[sizeof(V3FileEntry)];
MemoryStream dummyHeader = new MemoryStream(dummyBuffer);
dummyHeader.Write(dummyBuffer, 0, dummyBuffer.Length);
pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V3FileEntry))));

// Return MultiStream
return new MultiStream(pairs, logger);
}
Expand Down
2 changes: 1 addition & 1 deletion FileEmulationFramework.Interfaces/IEmulatedFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public interface IEmulatedFile
/// <param name="offset">Offset of the data to be received.</param>
/// <param name="info">Additional information about the current file being processed.</param>
/// <param name="numReadBytes">Number of bytes read by the function call.</param>
/// <returns>True if the operation succeeded, else false. Return true if at least 1 byte was read.</returns>
/// <returns>True if at least one byte was read, false otherwise (this indicates the EOF has been reached).</returns>
public unsafe bool ReadData(IntPtr handle, byte* buffer, uint length, long offset, IFileInformation info, out int numReadBytes);

/// <summary>
Expand Down
28 changes: 17 additions & 11 deletions FileEmulationFramework/FileAccessServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private static void DequeueHandles()
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
private static int QueryInformationFileImpl(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
private static NT_STATUS QueryInformationFileImpl(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
{
var result = _getFileSizeHook.OriginalFunction.Value.Invoke(hfile, ioStatusBlock, fileInformation, length, fileInformationClass);
if (fileInformationClass != FileInformationClass.FileStandardInformation || !HandleToInfoMap.TryGetValue(hfile, out var info))
Expand Down Expand Up @@ -139,12 +139,12 @@ private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_N
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
private static int SetInformationFileHook(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
private static NT_STATUS SetInformationFileHook(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
{
return SetInformationFileImpl(hfile, ioStatusBlock, fileInformation, length, fileInformationClass);
}

private static int SetInformationFileImpl(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
private static NT_STATUS SetInformationFileImpl(IntPtr hfile, IO_STATUS_BLOCK* ioStatusBlock, byte* fileInformation, uint length, FileInformationClass fileInformationClass)
{
if (fileInformationClass != FileInformationClass.FilePositionInformation || !HandleToInfoMap.TryGetValue(hfile, out var info))
return _setFilePointerHook.OriginalFunction.Value.Invoke(hfile, ioStatusBlock, fileInformation, length, fileInformationClass);
Expand All @@ -159,7 +159,7 @@ private static int SetInformationFileImpl(IntPtr hfile, IO_STATUS_BLOCK* ioStatu
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
private static int NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr* apcRoutine, IntPtr* apcContext,
private static NT_STATUS NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr* apcRoutine, IntPtr* apcContext,
IO_STATUS_BLOCK* ioStatus, byte* buffer, uint length, long* byteOffset, IntPtr key)
{
// Check if this is one of our files.
Expand All @@ -184,18 +184,24 @@ private static int NtReadFileImpl(IntPtr handle, IntPtr hEvent, IntPtr* apcRouti
requestedOffset += numReadBytes;
SetInformationFileImpl(handle, ioStatus, (byte*)&requestedOffset, sizeof(long), FileInformationClass.FilePositionInformation);

// Set number of read bytes.
ioStatus->Status = 0;
// Set status
ioStatus->Status = NT_STATUS.STATUS_SUCCESS;
ioStatus->Information = new(numReadBytes);
return 0;
return NT_STATUS.STATUS_SUCCESS;
}
else
{
_logger.Debug("[FileAccessServer] Likely EOF, Length: {0}, Offset: {1}", numReadBytes, requestedOffset);

// Set status (note that we're assuming that if File.ReadData fails then we're at the end of the file)
ioStatus->Status = NT_STATUS.STATUS_END_OF_FILE;
ioStatus->Information = new(numReadBytes);
return NT_STATUS.STATUS_END_OF_FILE;
}

return _readFileHook.OriginalFunction.Value.Invoke(handle, hEvent, apcRoutine, apcContext, ioStatus, buffer,
length, byteOffset, key);
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_ATTRIBUTES* objectAttributes, IO_STATUS_BLOCK* ioStatus, long* allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength)
private static NT_STATUS NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_ATTRIBUTES* objectAttributes, IO_STATUS_BLOCK* ioStatus, long* allocSize, uint fileAttributes, FileShare share, uint createDisposition, uint createOptions, IntPtr eaBuffer, uint eaLength)
{
lock (ThreadLock)
{
Expand Down
25 changes: 19 additions & 6 deletions FileEmulationFramework/Utilities/Native.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public struct CloseHandleFn
{
public FuncPtr<
IntPtr, // handle
int // status
NT_STATUS // status
> Value;
}

Expand All @@ -36,7 +36,7 @@ public FuncPtr<
uint, // createOptions
IntPtr, // eaBuffer
uint, // eaLength
int // status
NT_STATUS // status
> Value;
}

Expand All @@ -54,7 +54,7 @@ public FuncPtr<
uint, // length
Ptr<long>, // byteOffset
IntPtr, // key
int // status
NT_STATUS // status
> Value;
}

Expand All @@ -68,7 +68,7 @@ public FuncPtr<
Ptr<byte>, // fileInformation
uint, // length
FileInformationClass, // fileInformationClass
int // status
NT_STATUS // status
> Value;
}

Expand All @@ -82,7 +82,7 @@ public FuncPtr<
Ptr<byte>, // fileInformation
uint, // length
FileInformationClass, // fileInformationClass
int // status
NT_STATUS // status
> Value;
}

Expand All @@ -104,7 +104,7 @@ public FuncPtr<
[StructLayout(LayoutKind.Sequential)]
public struct IO_STATUS_BLOCK
{
public UInt32 Status;
public NT_STATUS Status;
public IntPtr Information;
}

Expand Down Expand Up @@ -197,4 +197,17 @@ public enum FileInformationClass
FileMaximumInformation,
#pragma warning restore CS1591
}

/// <summary>
/// An enumeration of Status values returned by functions.
/// There are a lot so only including ones that we actually need to use.
/// </summary>
public enum NT_STATUS : uint
{
#pragma warning disable CS1591
STATUS_SUCCESS = 0,
STATUS_END_OF_FILE = 0xC0000011,
#pragma warning restore CS1591
}

}

0 comments on commit 43adc6e

Please sign in to comment.