diff --git a/.gitmodules b/.gitmodules index c2813ab..7c32b53 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,5 +3,4 @@ url = https://github.com/Heroes-Hacking-Central/Heroes.SDK.git [submodule "Submodules/Atlus-Script-Tools"] path = Submodules/Atlus-Script-Tools - url = https://github.com/AnimatedSwine37/Atlus-Script-Tools.git - branch = bf-emulator + url = https://github.com/tge-was-taken/Atlus-Script-Tools.git diff --git a/Emulator/BF.File.Emulator/Bf/BfBuilder.cs b/Emulator/BF.File.Emulator/Bf/BfBuilder.cs index c169cbc..c12ba16 100644 --- a/Emulator/BF.File.Emulator/Bf/BfBuilder.cs +++ b/Emulator/BF.File.Emulator/Bf/BfBuilder.cs @@ -105,10 +105,38 @@ public void AddEnumFile(string filePath) } } + /// + /// Tries to get all files that the base flow imports + /// + /// The format of the flowscript + /// The library to use for the flowscript + /// The encoding of the flowscript + /// An array of absolute paths to all files that the base flows import, + /// both directly and transitively. This includes the base files. + /// + public bool TryGetImports(FlowFormatVersion flowFormat, Library library, Encoding encoding, + out string[] foundImports) + { + // Use compiler arg overrides (if they're there) + if (_library != null) library = _library; + if (_encoding != null) encoding = _encoding; + if (_flowFormat != null) flowFormat = (FlowFormatVersion)_flowFormat; + + var compiler = new FlowScriptCompiler(flowFormat); + compiler.Library = OverrideLibraries(library); + compiler.Encoding = encoding; + + var imports = new List(); + imports.AddRange(_flowFiles); + imports.AddRange(_msgFiles); + + return compiler.TryGetImports(imports, out foundImports); + } + /// /// Builds a BF file. /// - public unsafe Stream? Build(IntPtr originalHandle, string originalPath, FlowFormatVersion flowFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBf = false) + public EmulatedBf? Build(IntPtr originalHandle, string originalPath, FlowFormatVersion flowFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBf = false) { _log?.Info("[BfEmulator] Building BF File | {0}", originalPath); @@ -135,21 +163,43 @@ public void AddEnumFile(string filePath) imports.AddRange(_flowFiles.GetRange(1, _flowFiles.Count - 1)); imports.AddRange(_msgFiles); - if (!compiler.TryCompileWithImports(bfStream, imports, baseFlow, out FlowScript flowScript)) + try { - _log?.Error("[BfEmulator] Failed to compile BF File | {0}", originalPath); - return null; - } + if (!compiler.TryCompileWithImports(bfStream, imports, baseFlow, out FlowScript flowScript, + out var sources)) + { + _log?.Error("[BfEmulator] Failed to compile BF File | {0}", originalPath); + return null; + } - // Return the compiled bf - var bfBinary = flowScript.ToBinary(); - var stream = StreamUtils.CreateMemoryStream(bfBinary.Header.FileSize); - bfBinary.ToStream(stream, true); - stream.Position = 0; + // Return the compiled bf + var bfBinary = flowScript.ToBinary(); + var stream = StreamUtils.CreateMemoryStream(bfBinary.Header.FileSize); + bfBinary.ToStream(stream, true); + stream.Position = 0; - return stream; + DateTime lastWrite = sources.Where(x => x != null).Select(Fiel.GetLastWriteTimeUtc).Max(); + return new EmulatedBf(stream, sources, lastWrite); + } + catch (Exception exception) + { + var flows = string.Join(", ", _flowFiles.Concat(_msgFiles)); + _log.Error( + "[BF Builder] Failed to compile bf {0} with source files: {1}. This may be due to your mods not being translated. Error: {2}", + originalPath, flows, exception.Message); + if (exception.StackTrace != null) + _log.Error(exception.StackTrace); + return null; + } } + /// + /// Applies library file overrides to the base library. + /// This adds aliases to functions with different names, replaces functions with different + /// return values or parameters, and adds any completely new functions. + /// + /// The base library to override + /// A deep copy of the base library with overrides applied private Library OverrideLibraries(Library library) { if (_libraryEnums.Count == 0 && _libraryFuncs.Count == 0) return library; @@ -176,7 +226,15 @@ private Library OverrideLibraries(Library library) if (module != -1 && index != -1) { - library.FlowScriptModules[module].Functions[index] = func; + var existingFunc = library.FlowScriptModules[module].Functions[index]; + if (FlowFunctionsSame(existingFunc, func)) + { + existingFunc.Aliases.Add(func.Name); + } + else + { + library.FlowScriptModules[module].Functions[index] = func; + } } else { @@ -188,4 +246,17 @@ private Library OverrideLibraries(Library library) library.FlowScriptModules[0].Enums.AddRange(_libraryEnums); return library; } -} + + /// + /// Checks if two flowscript functions are effectively the same. + /// They are the same if the return type and parameter types are the same. + /// + /// The first function to compare + /// The other function to compare + /// Truee if the two functions are effectively the same, false otherwise + private bool FlowFunctionsSame(FlowScriptModuleFunction func1, FlowScriptModuleFunction func2) + { + return func1.ReturnType == func2.ReturnType && func1.Parameters.Select(param => param.Type) + .SequenceEqual(func2.Parameters.Select(param => param.Type)); + } +} \ No newline at end of file diff --git a/Emulator/BF.File.Emulator/Bf/BfBuilderFactory.cs b/Emulator/BF.File.Emulator/Bf/BfBuilderFactory.cs index cc6faa1..46a621d 100644 --- a/Emulator/BF.File.Emulator/Bf/BfBuilderFactory.cs +++ b/Emulator/BF.File.Emulator/Bf/BfBuilderFactory.cs @@ -148,7 +148,7 @@ private void OverrideCompilerArgs(string file) _flowFormat = GetFlowScriptFormatVersion(outFormat); _library = LibraryLookup.GetLibrary(args.Library); - _encoding = AtlusEncoding.GetByName(args.Encoding); + _encoding = AtlusEncoding.Create(args.Encoding); _log.Info($"[BfBuilderFactory] Changed script compiler args to OutFormat: {args.OutFormat}, Library: {args.Library}, Encoding: {args.Encoding}"); } diff --git a/Emulator/BF.File.Emulator/Bf/EmulatedBf.cs b/Emulator/BF.File.Emulator/Bf/EmulatedBf.cs new file mode 100644 index 0000000..17935c3 --- /dev/null +++ b/Emulator/BF.File.Emulator/Bf/EmulatedBf.cs @@ -0,0 +1,31 @@ +namespace BF.File.Emulator.Bf; + +/// +/// Contains information about a BF file that has been emulated +/// +public class EmulatedBf +{ + /// + /// A list of full paths to all source files used to compile this bf. + /// This includes all msg, bf, and flow files that are imported excluding the base bf. + /// + public List Sources { get; } + + /// + /// The stream for the emulated file + /// + public Stream Stream { get; } + + /// + /// The last write time of the file + /// This is set as the maximum last write time of all sources when the file was first emulated + /// + public DateTime LastWriteTime { get; } + + public EmulatedBf(Stream stream, List sources, DateTime lastWriteTime) + { + Sources = sources; + Stream = stream; + LastWriteTime = lastWriteTime; + } +} \ No newline at end of file diff --git a/Emulator/BF.File.Emulator/BfEmulator.cs b/Emulator/BF.File.Emulator/BfEmulator.cs index 46f3d15..63ed7c6 100644 --- a/Emulator/BF.File.Emulator/BfEmulator.cs +++ b/Emulator/BF.File.Emulator/BfEmulator.cs @@ -25,7 +25,7 @@ public class BfEmulator : IEmulator // Note: Handle->Stream exists because hashing IntPtr is easier; thus can resolve reads faster. private readonly BfBuilderFactory _builderFactory; - private readonly ConcurrentDictionary _pathToStream = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _pathToEmulated = new(StringComparer.OrdinalIgnoreCase); private Logger _log; private FlowFormatVersion _flowFormat; @@ -46,17 +46,17 @@ public BfEmulator(Logger log, bool dumpFiles, Game game) case Game.P3P: _flowFormat = FlowFormatVersion.Version1; _library = LibraryLookup.GetLibrary("P3P"); - _encoding = AtlusEncoding.GetByName("P4"); + _encoding = AtlusEncoding.Create("P3P_EFIGS"); break; case Game.P4G: _flowFormat = FlowFormatVersion.Version1; _library = LibraryLookup.GetLibrary("P4G"); - _encoding = AtlusEncoding.GetByName("P4"); + _encoding = AtlusEncoding.Create("P4G_EFIGS"); break; case Game.P5R: _flowFormat = FlowFormatVersion.Version3BigEndian; _library = LibraryLookup.GetLibrary("P5R"); - _encoding = AtlusEncoding.GetByName("P5R"); + _encoding = AtlusEncoding.Create("P5R_EFIGS"); break; } } @@ -65,13 +65,13 @@ public bool TryCreateFile(IntPtr handle, string filepath, string route, out IEmu { // Check if we already made a custom BF for this file. emulated = null!; - if (_pathToStream.TryGetValue(filepath, out var stream)) + if (_pathToEmulated.TryGetValue(filepath, out var emulatedBf)) { // Avoid recursion into same file. - if (stream == null) + if (emulatedBf == null) return false; - emulated = new EmulatedFile(stream); + emulated = new EmulatedFile(emulatedBf.Stream, emulatedBf.LastWriteTime); return true; } @@ -109,15 +109,16 @@ public bool TryCreateEmulatedFile(IntPtr handle, string srcDataPath, string outp return false; // Make the BF file. - _pathToStream[outputPath] = null; // Avoid recursion into same file. + _pathToEmulated[outputPath] = null; // Avoid recursion into same file. - stream = builder!.Build(handle, srcDataPath, _flowFormat, _library, _encoding, _listener, isEmpty); - if (stream == null) + var emulatedBf = builder!.Build(handle, srcDataPath, _flowFormat, _library, _encoding, _listener, isEmpty); + if (emulatedBf == null) return false; - _pathToStream.TryAdd(outputPath, stream); - emulated = new EmulatedFile(stream); - _log.Info("[BfEmulator] Created Emulated file with Path {0}", outputPath); + stream = emulatedBf.Stream; + _pathToEmulated.TryAdd(outputPath, emulatedBf); + emulated = new EmulatedFile(stream, emulatedBf.LastWriteTime); + _log.Info("[BfEmulator] Created Emulated file with Path {0} and Last Write {1}", outputPath, emulatedBf.LastWriteTime); if (DumpFiles) DumpFile(route, stream); @@ -143,13 +144,13 @@ public void OnModLoading(string modFolder) /// Full path to the file. public void UnregisterFile(string bfPath) { - _pathToStream!.Remove(bfPath, out var stream); - stream?.Dispose(); + _pathToEmulated!.Remove(bfPath, out var emulated); + emulated?.Stream.Dispose(); } - public void RegisterFile(string destinationPath, Stream stream) + public void RegisterFile(string destinationPath, Stream stream, DateTime lastWriteTime) { - _pathToStream.TryAdd(destinationPath, stream); + _pathToEmulated.TryAdd(destinationPath, new EmulatedBf(stream, new List(), lastWriteTime)); } private void DumpFile(string route, Stream stream) @@ -162,11 +163,25 @@ private void DumpFile(string route, Stream stream) _log.Info($"[BfEmulator] Written To {dumpPath}"); } + internal bool TryGetImports(string route, out string[] imports) + { + if (!_builderFactory.TryCreateFromPath(route, out var builder)) + { + imports = Array.Empty(); + return false; + } + + return builder!.TryGetImports(_flowFormat, _library, _encoding, out imports); + } + internal List GetInput() => _builderFactory.RouteFileTuples; internal void AddFromFolders(string dir) => _builderFactory.AddFromFolders(dir); internal void AddFile(string file, string route) => _builderFactory.AddFile(Path.GetFileName(file), file, Path.GetDirectoryName(file)!, route); + + internal void SetEncoding(string encoding) => _encoding = AtlusEncoding.Create(encoding); + } public class AtlusLogListener : LogListener diff --git a/Emulator/BF.File.Emulator/BfEmulatorApi.cs b/Emulator/BF.File.Emulator/BfEmulatorApi.cs index 95993b7..59cd4e2 100644 --- a/Emulator/BF.File.Emulator/BfEmulatorApi.cs +++ b/Emulator/BF.File.Emulator/BfEmulatorApi.cs @@ -1,4 +1,5 @@ -using BF.File.Emulator.Interfaces; +using System.Diagnostics; +using BF.File.Emulator.Interfaces; using BF.File.Emulator.Interfaces.Structures.IO; using BF.File.Emulator.Utilities; using FileEmulationFramework.Interfaces; @@ -7,6 +8,9 @@ using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; +// Aliasing for readability, since our assembly name has priority over 'File' +using Fiel = System.IO.File; + namespace BF.File.Emulator; public class BfEmulatorApi : IBfEmulator @@ -57,8 +61,9 @@ public void RegisterBf(string sourcePath, string destinationPath) Native.SetFilePointerEx(handle, 0, IntPtr.Zero, 0); var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read); - var emulated = new EmulatedFile(fileStream); - _bfEmulator.RegisterFile(destinationPath, fileStream); + var lastWrite = Fiel.GetLastWriteTimeUtc(sourcePath); + var emulated = new EmulatedFile(fileStream, lastWrite); + _bfEmulator.RegisterFile(destinationPath, fileStream, lastWrite); _framework.RegisterVirtualFile(destinationPath, emulated, false); _logger.Info("[BfEmulatorApi] Registered bf {0} at {1}", sourcePath, destinationPath); @@ -97,4 +102,14 @@ public void AddDirectory(string dir) _bfEmulator.AddFromFolders(dir); } + public void SetEncoding(string encoding) + { + _logger.Info("Setting encoding to {0}", encoding); + _bfEmulator.SetEncoding(encoding); + } + + public bool TryGetImports(string route, out string[] imports) + { + return _bfEmulator.TryGetImports(route, out imports); + } } diff --git a/Emulator/BMD.File.Emulator/Bmd/BmdBuilder.cs b/Emulator/BMD.File.Emulator/Bmd/BmdBuilder.cs index ef4c204..8ea9719 100644 --- a/Emulator/BMD.File.Emulator/Bmd/BmdBuilder.cs +++ b/Emulator/BMD.File.Emulator/Bmd/BmdBuilder.cs @@ -1,13 +1,10 @@ -using FileEmulationFramework.Lib.IO; -using FileEmulationFramework.Lib.Utilities; -using FileEmulationFramework.Lib.Memory; +using FileEmulationFramework.Lib.Utilities; using AtlusScriptLibrary.MessageScriptLanguage.Compiler; using MessageFormatVersion = AtlusScriptLibrary.MessageScriptLanguage.FormatVersion; using AtlusScriptLibrary.Common.Libraries; using System.Text; using Microsoft.Win32.SafeHandles; using AtlusScriptLibrary.MessageScriptLanguage; -using System.Text.Json; using BMD.File.Emulator.Utilities; // Aliasing for readability, since our assembly name has priority over 'File' @@ -48,7 +45,7 @@ public void AddMsgFile(string filePath) /// /// Builds a BMD file. /// - public unsafe Stream? Build(IntPtr originalHandle, string originalPath, MessageFormatVersion msgFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBmd = false) + public EmulatedBmd? Build(IntPtr originalHandle, string originalPath, MessageFormatVersion msgFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBmd = false) { _log?.Info("[BmdEmulator] Building BMD File | {0}", originalPath); @@ -67,18 +64,30 @@ public void AddMsgFile(string filePath) if (!noBaseBmd) bmdStream = new FileStream(new SafeFileHandle(originalHandle, false), FileAccess.Read); - if (!compiler.TryCompileWithImports(bmdStream, _msgFiles, out MessageScript messageScript)) + try { - _log?.Error("[BmdEmulator] Failed to compile BMD File | {0}", originalPath); - return null; - } + if (!compiler.TryCompileWithImports(bmdStream, _msgFiles, out MessageScript messageScript)) + { + _log?.Error("[BmdEmulator] Failed to compile BMD File | {0}", originalPath); + return null; + } - // Return the compiled bmd - var bmdBinary = messageScript.ToBinary(); - var stream = StreamUtils.CreateMemoryStream(bmdBinary.Header.FileSize); - bmdBinary.ToStream(stream, true); - stream.Position = 0; + // Return the compiled bmd + var bmdBinary = messageScript.ToBinary(); + var stream = StreamUtils.CreateMemoryStream(bmdBinary.FileSize); + bmdBinary.ToStream(stream, true); + stream.Position = 0; - return stream; + DateTime lastWrite = _msgFiles.Select(Fiel.GetLastWriteTimeUtc).Max(); + return new EmulatedBmd(stream, _msgFiles, lastWrite); + } + catch (Exception exception) + { + var msgs = string.Join(", ", _msgFiles); + _log.Error( + "[BMD Builder] Failed to compile bf {0} with msgs {1}. This may be due to your mods not being translated. Error: {2}", + originalPath, msgs, exception.Message); + return null; + } } -} +} \ No newline at end of file diff --git a/Emulator/BMD.File.Emulator/Bmd/BmdBuilderFactory.cs b/Emulator/BMD.File.Emulator/Bmd/BmdBuilderFactory.cs index 6ed7a4a..3f81b4e 100644 --- a/Emulator/BMD.File.Emulator/Bmd/BmdBuilderFactory.cs +++ b/Emulator/BMD.File.Emulator/Bmd/BmdBuilderFactory.cs @@ -122,7 +122,7 @@ private void OverrideCompilerArgs(string file) _messageFormat = GetMessageScriptFormatVersion(outFormat); _library = LibraryLookup.GetLibrary(args.Library); - _encoding = AtlusEncoding.GetByName(args.Encoding); + _encoding = AtlusEncoding.Create(args.Encoding); _log.Info($"[BmdBuilderFactory] Changed script compiler args to OutFormat: {args.OutFormat}, Library: {args.Library}, Encoding: {args.Encoding}"); } diff --git a/Emulator/BMD.File.Emulator/Bmd/EmulatedBmd.cs b/Emulator/BMD.File.Emulator/Bmd/EmulatedBmd.cs new file mode 100644 index 0000000..80abcb8 --- /dev/null +++ b/Emulator/BMD.File.Emulator/Bmd/EmulatedBmd.cs @@ -0,0 +1,31 @@ +namespace BMD.File.Emulator.Bmd; + +/// +/// Contains information about a BMD file that has been emulated +/// +public class EmulatedBmd +{ + /// + /// A list of full paths to all source files used to compile this bmd. + /// This includes all msg files but excludes the base bmd. + /// + public List Sources { get; } + + /// + /// The stream for the emulated file + /// + public Stream Stream { get; } + + /// + /// The last write time of the file + /// This is set as the maximum last write time of all sources when the file was first emulated + /// + public DateTime LastWriteTime { get; } + + public EmulatedBmd(Stream stream, List sources, DateTime lastWriteTime) + { + Sources = sources; + Stream = stream; + LastWriteTime = lastWriteTime; + } +} \ No newline at end of file diff --git a/Emulator/BMD.File.Emulator/BmdEmulator.cs b/Emulator/BMD.File.Emulator/BmdEmulator.cs index a199459..b3bd782 100644 --- a/Emulator/BMD.File.Emulator/BmdEmulator.cs +++ b/Emulator/BMD.File.Emulator/BmdEmulator.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.Text; using BMD.File.Emulator.Bmd; -using FileEmulationFramework.Lib.Memory; using BMD.File.Emulator.Utilities; using MessageFormatVersion = AtlusScriptLibrary.MessageScriptLanguage.FormatVersion; using static BMD.File.Emulator.Mod; @@ -11,7 +10,6 @@ using AtlusScriptLibrary.Common.Text.Encodings; using AtlusScriptLibrary.Common.Logging; using Logger = FileEmulationFramework.Lib.Utilities.Logger; -using System.Diagnostics; namespace BMD.File.Emulator; @@ -27,7 +25,7 @@ public class BmdEmulator : IEmulator // Note: Handle->Stream exists because hashing IntPtr is easier; thus can resolve reads faster. private readonly BmdBuilderFactory _builderFactory; - private readonly ConcurrentDictionary _pathToStream = new(StringComparer.OrdinalIgnoreCase); + private readonly ConcurrentDictionary _pathToEmulated = new(StringComparer.OrdinalIgnoreCase); private Logger _log; private MessageFormatVersion _messageFormat; @@ -47,17 +45,17 @@ public BmdEmulator(Logger log, bool dumpFiles, Game game) case Game.P3P: _messageFormat = MessageFormatVersion.Version1; _library = LibraryLookup.GetLibrary("P3P"); - _encoding = AtlusEncoding.GetByName("P4"); + _encoding = AtlusEncoding.Create("P3P_EFIGS"); break; case Game.P4G: _messageFormat = MessageFormatVersion.Version1; _library = LibraryLookup.GetLibrary("P4G"); - _encoding = AtlusEncoding.GetByName("P4"); + _encoding = AtlusEncoding.Create("P4G_EFIGS"); break; case Game.P5R: _messageFormat = MessageFormatVersion.Version1BigEndian; _library = LibraryLookup.GetLibrary("P5R"); - _encoding = AtlusEncoding.GetByName("P5R"); + _encoding = AtlusEncoding.Create("P5R_EFIGS"); break; } } @@ -66,13 +64,13 @@ public bool TryCreateFile(IntPtr handle, string filepath, string route, out IEmu { // Check if we already made a custom BMD for this file. emulated = null!; - if (_pathToStream.TryGetValue(filepath, out var stream)) + if (_pathToEmulated.TryGetValue(filepath, out var emulatedBmd)) { // Avoid recursion into same file. - if (stream == null) + if (emulatedBmd == null) return false; - emulated = new EmulatedFile(stream); + emulated = new EmulatedFile(emulatedBmd.Stream, emulatedBmd.LastWriteTime); return true; } @@ -110,15 +108,16 @@ public bool TryCreateEmulatedFile(IntPtr handle, string srcDataPath, string outp return false; // Make the BMD file. - _pathToStream[outputPath] = null; // Avoid recursion into same file. + _pathToEmulated[outputPath] = null; // Avoid recursion into same file. - stream = builder!.Build(handle, srcDataPath, _messageFormat, _library, _encoding, _listener, isEmpty); - if (stream == null) + var emulatedBmd = builder!.Build(handle, srcDataPath, _messageFormat, _library, _encoding, _listener, isEmpty); + if (emulatedBmd == null) return false; - _pathToStream.TryAdd(outputPath, stream); - emulated = new EmulatedFile(stream); - _log.Info("[BmdEmulator] Created Emulated file with Path {0}", outputPath); + stream = emulatedBmd.Stream; + _pathToEmulated.TryAdd(outputPath, emulatedBmd); + emulated = new EmulatedFile(stream, emulatedBmd.LastWriteTime); + _log.Info("[BmdEmulator] Created Emulated file with Path {0} and Last Write {1}", outputPath, emulatedBmd.LastWriteTime); if (DumpFiles) DumpFile(route, stream); @@ -144,13 +143,13 @@ public void OnModLoading(string modFolder) /// Full path to the file. public void UnregisterFile(string bmdPath) { - _pathToStream!.Remove(bmdPath, out var stream); - stream?.Dispose(); + _pathToEmulated!.Remove(bmdPath, out var emulated); + emulated?.Stream.Dispose(); } - public void RegisterFile(string destinationPath, Stream stream) + public void RegisterFile(string destinationPath, Stream stream, DateTime lastWriteTime) { - _pathToStream.TryAdd(destinationPath, stream); + _pathToEmulated.TryAdd(destinationPath, new EmulatedBmd(stream, new List(), lastWriteTime)); } private void DumpFile(string route, Stream stream) @@ -168,6 +167,8 @@ private void DumpFile(string route, Stream stream) internal void AddFromFolders(string dir) => _builderFactory.AddFromFolders(dir); internal void AddFile(string file, string route) => _builderFactory.AddFile(Path.GetFileName(file), file, Path.GetDirectoryName(file)!, route); + + public void SetEncoding(string encoding) => _encoding = AtlusEncoding.Create(encoding); } public class AtlusLogListener : LogListener diff --git a/Emulator/BMD.File.Emulator/BmdEmulatorApi.cs b/Emulator/BMD.File.Emulator/BmdEmulatorApi.cs index 69bc204..536ea06 100644 --- a/Emulator/BMD.File.Emulator/BmdEmulatorApi.cs +++ b/Emulator/BMD.File.Emulator/BmdEmulatorApi.cs @@ -1,14 +1,14 @@ using BMD.File.Emulator.Interfaces; using BMD.File.Emulator.Interfaces.Structures.IO; -using BMD.File.Emulator.Utilities; using FileEmulationFramework.Interfaces; using FileEmulationFramework.Interfaces.Reference; -using FileEmulationFramework.Lib.Memory; using FileEmulationFramework.Lib.Utilities; using Microsoft.Win32.SafeHandles; -using System.Diagnostics; using System.Runtime.InteropServices; +// Aliasing for readability, since our assembly name has priority over 'File' +using Fiel = System.IO.File; + namespace BMD.File.Emulator; public class BmdEmulatorApi : IBmdEmulator @@ -59,9 +59,10 @@ public void RegisterBmd(string sourcePath, string destinationPath) Native.SetFilePointerEx(handle, 0, IntPtr.Zero, 0); + var lastWrite = Fiel.GetLastWriteTimeUtc(sourcePath); var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read); - var emulated = new EmulatedFile(fileStream); - _bmdEmulator.RegisterFile(destinationPath, fileStream); + var emulated = new EmulatedFile(fileStream, lastWrite); + _bmdEmulator.RegisterFile(destinationPath, fileStream, lastWrite); _framework.RegisterVirtualFile(destinationPath, emulated, false); _logger.Info("[BmdEmulatorApi] Registered bmd {0} at {1}", sourcePath, destinationPath); @@ -99,4 +100,10 @@ public void AddDirectory(string dir) { _bmdEmulator.AddFromFolders(dir); } + + public void SetEncoding(string encoding) + { + _logger.Info("Setting encoding to {0}", encoding); + _bmdEmulator.SetEncoding(encoding); + } } diff --git a/Emulator/Interfaces/BF.File.Emulator.Interfaces/IBfEmulator.cs b/Emulator/Interfaces/BF.File.Emulator.Interfaces/IBfEmulator.cs index 6b372de..af7ee2d 100644 --- a/Emulator/Interfaces/BF.File.Emulator.Interfaces/IBfEmulator.cs +++ b/Emulator/Interfaces/BF.File.Emulator.Interfaces/IBfEmulator.cs @@ -42,4 +42,18 @@ public interface IBfEmulator /// /// The directory to add the files from public void AddDirectory(string dir); + + /// + /// Sets the encoding that bf emulator will use + /// + /// The name of the encoding to use + public void SetEncoding(string encoding); + + /// + /// Gets a list of files that a flow file imports + /// + /// The route to the file to check + /// A list of full paths to all files that are imported (directly or transitively) by the flow file + /// True if the imports could be determined, false otherwise + public bool TryGetImports(string route, out string[] imports); } diff --git a/Emulator/Interfaces/BMD.File.Emulator.Interfaces/IBmdEmulator.cs b/Emulator/Interfaces/BMD.File.Emulator.Interfaces/IBmdEmulator.cs index eaff03c..2f56f66 100644 --- a/Emulator/Interfaces/BMD.File.Emulator.Interfaces/IBmdEmulator.cs +++ b/Emulator/Interfaces/BMD.File.Emulator.Interfaces/IBmdEmulator.cs @@ -42,4 +42,10 @@ public interface IBmdEmulator /// /// The directory to add the files from public void AddDirectory(string dir); + + /// + /// Sets the encoding that bf emulator will use + /// + /// The name of the encoding to use + public void SetEncoding(string encoding); } diff --git a/Emulator/PAK.Stream.Emulator/Pak/PakBuilder.cs b/Emulator/PAK.Stream.Emulator/Pak/PakBuilder.cs index b3fd939..50b95ec 100644 --- a/Emulator/PAK.Stream.Emulator/Pak/PakBuilder.cs +++ b/Emulator/PAK.Stream.Emulator/Pak/PakBuilder.cs @@ -5,9 +5,7 @@ using FileEmulationFramework.Lib.IO.Struct; using FileEmulationFramework.Lib.Utilities; using Microsoft.Win32.SafeHandles; -using Reloaded.Memory; using Reloaded.Memory.Extensions; -using Reloaded.Memory.Streams; using Reloaded.Memory.Utilities; // Aliasing for readability, since our assembly name has priority over 'stream' @@ -19,6 +17,12 @@ namespace PAK.Stream.Emulator.Pak; public class PakBuilder { private readonly Dictionary _customFiles = new Dictionary(); + private readonly Logger? _log; + + public PakBuilder(Logger? log = null) + { + _log = log; + } /// /// Adds a file to the Virtual PAK builder. @@ -29,7 +33,15 @@ public void AddOrReplaceFile(string filePath, string pakName) { var nameIndex = filePath.IndexOf(pakName.Replace('\\', '/'), StringComparison.Ordinal); var file = filePath.Substring(nameIndex + pakName.Length + 1).Replace('\\', '/'); - _customFiles[file] = new(filePath); + var fileSlice = new FileSlice(filePath); + if (fileSlice.Length == 0) + { + _log?.Error("[Pak Builder] File {0} is empty, not adding it to pak {1}", filePath, pakName); + } + else + { + _customFiles[file] = fileSlice; + } } /// @@ -40,16 +52,24 @@ public void AddOrReplaceFile(string filePath, string pakName) /// Path to where the file will be in the pak public void AddOrReplaceFileWithPath(string filePath, string inPakPath) { - _customFiles[inPakPath.Replace('\\', '/')] = new(filePath); + var fileSlice = new FileSlice(filePath); + if (fileSlice.Length == 0) + { + _log?.Error("[Pak Builder] File {0} is empty, not adding it into pak at {1}", filePath, inPakPath); + } + else + { + _customFiles[inPakPath.Replace('\\', '/')] = fileSlice; + } } /// /// Builds an PAK file. /// - public unsafe MultiStream Build(IntPtr handle, string filepath, Logger? logger = null, string folder = "", long baseOffset = 0) + public unsafe MultiStream Build(IntPtr handle, string filepath, string folder = "", long baseOffset = 0) { - logger?.Info($"[{nameof(PakBuilder)}] Building PAK File | {{0}}", filepath); + _log?.Info($"[{nameof(PakBuilder)}] Building PAK File | {{0}}", filepath); // Get original file's entries. IEntry[] entries = GetEntriesFromFile(handle, baseOffset, out var format); @@ -57,19 +77,19 @@ public unsafe MultiStream Build(IntPtr handle, string filepath, Logger? logger = switch (format) { case FormatVersion.Version1: - return BuildV1(entries, handle, filepath, logger, folder, baseOffset); + return BuildV1(entries, handle, filepath, folder, baseOffset); case FormatVersion.Version2: case FormatVersion.Version2BE: - return BuildV2(entries, handle, filepath, format == FormatVersion.Version2BE, logger, folder, baseOffset); + return BuildV2(entries, handle, filepath, format == FormatVersion.Version2BE, folder, baseOffset); case FormatVersion.Version3: case FormatVersion.Version3BE: - return BuildV3(entries, handle, filepath, format == FormatVersion.Version3BE, logger, folder, baseOffset); + return BuildV3(entries, handle, filepath, format == FormatVersion.Version3BE, folder, baseOffset); default: return null!; // Will nver even get here, an error is thrown in GetEntriesFromFile for invalid formats } } - private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filepath, Logger? logger = null, string folder = "", long baseOffset = 0) + private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filepath, string folder = "", long baseOffset = 0) { int sizeofentry = sizeof(V1FileEntry); @@ -122,7 +142,7 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep int length = 0; if (intFiles.TryGetValue(x, out var overwrittenFile)) { - logger?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); + _log?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); // For custom files, add to pairs directly. length = overwrittenFile.Length; @@ -137,7 +157,7 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep length = (int)Align(length, 64); - pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); + pairs.Add(new(new FileSliceStreamW32(overwrittenFile, _log), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); if (length > overwrittenFile.Length) { byte[] buffer = new byte[length - overwrittenFile.Length]; @@ -149,14 +169,14 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep { length = (int)Align(entries[x].Length, 64); var entryContents = new FileSlice(baseOffset + fileOffset + sizeofentry, length, filepath); - Strim entryStream = new FileSliceStreamW32(entryContents, logger); + Strim entryStream = new FileSliceStreamW32(entryContents, _log); var container = entries[x].FileName.Trim().Replace("../", ""); if (innerPaksToEdit.Contains(container) && DetectVersion(entryStream) != FormatVersion.Unknown) { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry - 4, filepath); - Strim headerStream = new FileSliceStreamW32(entryHeader, logger); + Strim headerStream = new FileSliceStreamW32(entryHeader, _log); - entryStream = this.Build(handle, filepath, logger, container, baseOffset + fileOffset + sizeofentry); + entryStream = this.Build(handle, filepath, container, baseOffset + fileOffset + sizeofentry); length = (int)entryStream.Length; var headerStream2 = new MemoryStream(4); @@ -181,7 +201,7 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep else { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry, filepath); - Strim headerStream = new FileSliceStreamW32(entryHeader, logger); + Strim headerStream = new FileSliceStreamW32(entryHeader, _log); pairs.Add(new(headerStream, OffsetRange.FromStartAndLength(currentOffset, sizeofentry))); pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length))); @@ -210,10 +230,10 @@ private unsafe MultiStream BuildV1(IEntry[] entries, IntPtr handle, string filep pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V1FileEntry)))); // Return MultiStream - return new MultiStream(pairs, logger); + return new MultiStream(pairs, _log); } - private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filepath, bool bigEndian, Logger? logger = null, string folder = "", long baseOffset = 0) + private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filepath, bool bigEndian, string folder = "", long baseOffset = 0) { int sizeofentry = sizeof(V2FileEntry); @@ -275,7 +295,7 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep int length = 0; if (intFiles.TryGetValue(x, out var overwrittenFile)) { - logger?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); + _log?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); // For custom files, add to pairs directly. length = overwrittenFile.Length; @@ -288,7 +308,7 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep entrystream.Write(writelength); pairs.Add(new(entrystream, OffsetRange.FromStartAndLength(currentOffset, sizeof(V2FileEntry)))); - pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); + pairs.Add(new(new FileSliceStreamW32(overwrittenFile, _log), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); if (length > overwrittenFile.Length) { byte[] buffer = new byte[length - overwrittenFile.Length]; @@ -301,14 +321,14 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep length = entries[x].Length; length = bigEndian ? Endian.Reverse(length) : length; var entryContents = new FileSlice(baseOffset + fileOffset + sizeofentry, length, filepath); - Strim entryStream = new FileSliceStreamW32(entryContents, logger); + Strim entryStream = new FileSliceStreamW32(entryContents, _log); var container = entries[x].FileName.Trim().Replace("../", ""); if (innerPaksToEdit.Contains(container) && DetectVersion(entryStream) != FormatVersion.Unknown) { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry - 4, filepath); - headerStream = new FileSliceStreamW32(entryHeader, logger); + headerStream = new FileSliceStreamW32(entryHeader, _log); - entryStream = this.Build(handle, filepath, logger, container, baseOffset + fileOffset + sizeofentry); + entryStream = this.Build(handle, filepath, container, baseOffset + fileOffset + sizeofentry); length = (int)entryStream.Length; var headerStream2 = new MemoryStream(4); @@ -333,14 +353,11 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep else { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry, filepath); - headerStream = new FileSliceStreamW32(entryHeader, logger); + headerStream = new FileSliceStreamW32(entryHeader, _log); pairs.Add(new(headerStream, OffsetRange.FromStartAndLength(currentOffset, sizeofentry))); pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length))); - } - - } else { @@ -364,10 +381,10 @@ private unsafe MultiStream BuildV2(IEntry[] entries, IntPtr handle, string filep pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V2FileEntry)))); // Return MultiStream - return new MultiStream(pairs, logger); + return new MultiStream(pairs, _log); } - private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filepath, bool bigEndian, Logger? logger = null, string folder = "", long baseOffset = 0) + private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filepath, bool bigEndian, string folder = "", long baseOffset = 0) { int sizeofentry = sizeof(V3FileEntry); @@ -429,7 +446,7 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep int length = 0; if (intFiles.TryGetValue(x, out var overwrittenFile)) { - logger?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); + _log?.Info($"{nameof(PakBuilder)} | Injecting {{0}}, in slot {{1}}", overwrittenFile.FilePath, x); // For custom files, add to pairs directly. length = overwrittenFile.Length; @@ -442,7 +459,7 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep entrystream.Write(writelength); pairs.Add(new(entrystream, OffsetRange.FromStartAndLength(currentOffset, sizeof(V3FileEntry)))); - pairs.Add(new(new FileSliceStreamW32(overwrittenFile, logger), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); + pairs.Add(new(new FileSliceStreamW32(overwrittenFile, _log), OffsetRange.FromStartAndLength(currentOffset + sizeofentry, overwrittenFile.Length))); if (length > overwrittenFile.Length) { byte[] buffer = new byte[length - overwrittenFile.Length]; @@ -455,14 +472,14 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep length = entries[x].Length; length = bigEndian ? Endian.Reverse(length) : length; var entryContents = new FileSlice(baseOffset + fileOffset + sizeofentry, length, filepath); - Strim entryStream = new FileSliceStreamW32(entryContents, logger); + Strim entryStream = new FileSliceStreamW32(entryContents, _log); var container = entries[x].FileName.Trim().Replace("../", ""); if (innerPaksToEdit.Contains(container) && DetectVersion(entryStream) != FormatVersion.Unknown) { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry - 4, filepath); - headerStream = new FileSliceStreamW32(entryHeader, logger); + headerStream = new FileSliceStreamW32(entryHeader, _log); - entryStream = this.Build(handle, filepath, logger, container, baseOffset + fileOffset + sizeofentry); + entryStream = this.Build(handle, filepath, container, baseOffset + fileOffset + sizeofentry); length = (int)entryStream.Length; var headerStream2 = new MemoryStream(4); @@ -486,7 +503,7 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep else { var entryHeader = new FileSlice(baseOffset + fileOffset, sizeofentry, filepath); - headerStream = new FileSliceStreamW32(entryHeader, logger); + headerStream = new FileSliceStreamW32(entryHeader, _log); pairs.Add(new(headerStream, OffsetRange.FromStartAndLength(currentOffset, sizeofentry))); pairs.Add(new(entryStream, OffsetRange.FromStartAndLength(currentOffset + sizeofentry, length))); @@ -514,7 +531,7 @@ private unsafe MultiStream BuildV3(IEntry[] entries, IntPtr handle, string filep pairs.Add(new(dummyHeader, OffsetRange.FromStartAndLength(currentOffset, sizeof(V3FileEntry)))); // Return MultiStream - return new MultiStream(pairs, logger); + return new MultiStream(pairs, _log); } private static bool IsValidFormatVersion1(Strim stream) diff --git a/Emulator/PAK.Stream.Emulator/Pak/PakBuilderFactory.cs b/Emulator/PAK.Stream.Emulator/Pak/PakBuilderFactory.cs index 74674a5..6c26d07 100644 --- a/Emulator/PAK.Stream.Emulator/Pak/PakBuilderFactory.cs +++ b/Emulator/PAK.Stream.Emulator/Pak/PakBuilderFactory.cs @@ -63,7 +63,7 @@ public bool TryCreateFromPath(string path, out PakBuilder? builder) continue; // Make builder if not made. - builder ??= new PakBuilder(); + builder ??= new PakBuilder(_log); // Add files to builder. var dir = group.Files.Directory.FullPath; @@ -77,7 +77,7 @@ public bool TryCreateFromPath(string path, out PakBuilder? builder) continue; // Make builder if not made. - builder ??= new PakBuilder(); + builder ??= new PakBuilder(_log); builder.AddOrReplaceFileWithPath(group.FilePath, group.VirtualPath); diff --git a/Emulator/PAK.Stream.Emulator/PakEmulator.cs b/Emulator/PAK.Stream.Emulator/PakEmulator.cs index 2772ccb..2d6fe00 100644 --- a/Emulator/PAK.Stream.Emulator/PakEmulator.cs +++ b/Emulator/PAK.Stream.Emulator/PakEmulator.cs @@ -81,7 +81,7 @@ public bool TryCreateEmulatedFile(IntPtr handle, string srcDataPath, string outp // Make the PAK file. _pathToStream[outputPath] = null; // Avoid recursion into same file. - stream = builder!.Build(handle, srcDataPath, _log); + stream = builder!.Build(handle, srcDataPath); _pathToStream.TryAdd(outputPath, stream); emulated = new EmulatedFile(stream); diff --git a/FileEmulationFramework.Interfaces/IEmulatedFile.cs b/FileEmulationFramework.Interfaces/IEmulatedFile.cs index f86b665..358bd75 100644 --- a/FileEmulationFramework.Interfaces/IEmulatedFile.cs +++ b/FileEmulationFramework.Interfaces/IEmulatedFile.cs @@ -26,9 +26,18 @@ public interface IEmulatedFile public unsafe bool ReadData(IntPtr handle, byte* buffer, uint length, long offset, IFileInformation info, out int numReadBytes); /// - /// Called when a handle to the given file is closed.. + /// Called when a handle to the given file is closed. /// /// The handle of the file. /// Additional information about the current file being processed. public void CloseHandle(IntPtr handle, IFileInformation info); + + /// + /// Tries to get the last write time of a file with a given handle. + /// + /// The handle of the file. + /// Additional information about the current file being processed. + /// The time the emulated file was last written to. + /// True if the last write to was determined, false if the operation was not supported. + public bool TryGetLastWriteTime(IntPtr handle, IFileInformation info, out DateTime? lastWriteTime); } \ No newline at end of file diff --git a/FileEmulationFramework.Interfaces/Reference/EmulatedFile.cs b/FileEmulationFramework.Interfaces/Reference/EmulatedFile.cs index a45a03c..e413640 100644 --- a/FileEmulationFramework.Interfaces/Reference/EmulatedFile.cs +++ b/FileEmulationFramework.Interfaces/Reference/EmulatedFile.cs @@ -10,12 +10,31 @@ public class EmulatedFile : IEmulatedFile where TStream : Stream /// public TStream BaseStream { get; set; } + /// + /// The last time the file was written to. + /// If null, the last write time of the original file will be reported. + /// + public DateTime? LastWrite { get; set; } + /// /// Creates an emulated file from a stream. + /// This file will report the last write time of the original. /// /// Stream from which this emulated file is based from. public EmulatedFile(TStream baseStream) => BaseStream = baseStream; + + /// + /// Creates an emulated file from a stream with a last write time. + /// + /// Stream from which this emulated file is based from. + /// The last write time that the emulated file will report. + public EmulatedFile(TStream baseStream, DateTime lastWrite) + { + BaseStream = baseStream; + LastWrite = lastWrite; + } + /// public long GetFileSize(IntPtr handle, IFileInformation info) => BaseStream.Length; @@ -30,4 +49,11 @@ public unsafe bool ReadData(IntPtr handle, byte* buffer, uint length, long offse /// public void CloseHandle(IntPtr handle, IFileInformation info) { } + + /// + public bool TryGetLastWriteTime(IntPtr handle, IFileInformation info, out DateTime? lastWriteTime) + { + lastWriteTime = LastWrite; + return lastWriteTime != null; + } } \ No newline at end of file diff --git a/FileEmulationFramework.Lib/Utilities/Native.cs b/FileEmulationFramework.Lib/Utilities/Native.cs index 1ead5ea..1ac9a94 100644 --- a/FileEmulationFramework.Lib/Utilities/Native.cs +++ b/FileEmulationFramework.Lib/Utilities/Native.cs @@ -453,6 +453,18 @@ public DateTime ToDateTime() long fileTime = (long)((high << 32) + low); return DateTime.FromFileTimeUtc(fileTime); } + + /// + /// Creates a LARGE_INTEGER from a long + /// + /// The long value to create a LARGE_INTEGER from + public LARGE_INTEGER(long value) + { + // Setting QuadPart sets all three, just adding the = 0s to make the compiler happy + LowPart = 0; + HighPart = 0; + QuadPart = value; + } } /// The type of access to a file mapping object, which determines the page protection of the pages. diff --git a/FileEmulationFramework.Tests/Emulators/BF/BfEmulatorTests.cs b/FileEmulationFramework.Tests/Emulators/BF/BfEmulatorTests.cs index b751e79..4a8e292 100644 --- a/FileEmulationFramework.Tests/Emulators/BF/BfEmulatorTests.cs +++ b/FileEmulationFramework.Tests/Emulators/BF/BfEmulatorTests.cs @@ -1,14 +1,10 @@ using AtlusScriptLibrary.Common.Libraries; using AtlusScriptLibrary.Common.Text.Encodings; using BF.File.Emulator.Bf; -using FileEmulationFramework.Lib.Memory; using FileEmulationFramework.Lib.Utilities; using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Threading.Tasks; using Xunit; using FlowFormatVersion = AtlusScriptLibrary.FlowScriptLanguage.FormatVersion; @@ -27,16 +23,16 @@ public void SingleFlow() { var flowFormat = FlowFormatVersion.Version1; var library = LibraryLookup.GetLibrary("P4G"); - var encoding = AtlusEncoding.GetByName("P4"); + var encoding = AtlusEncoding.Create("P4G_EFIGS"); var builder = new BfBuilder(); builder.AddFlowFile(Assets.SimpleFlow); var handle = Native.CreateFileW(Assets.BaseBf, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero); - var stream = builder.Build(handle, Assets.BaseBf, flowFormat, library, encoding); + var emulatedBf = builder.Build(handle, Assets.BaseBf, flowFormat, library, encoding); // Write to file for checking. using var fileStream = new FileStream("field.bf", FileMode.Create); - stream.CopyTo(fileStream); + emulatedBf!.Stream.CopyTo(fileStream); fileStream.Close(); // Parse file and check. @@ -49,17 +45,17 @@ public void MultipleFlows() { var flowFormat = FlowFormatVersion.Version1; var library = LibraryLookup.GetLibrary("P4G"); - var encoding = AtlusEncoding.GetByName("P4"); + var encoding = AtlusEncoding.Create("P4G_EFIGS"); var builder = new BfBuilder(); builder.AddFlowFile(Assets.SimpleFlow); builder.AddFlowFile(Assets.ComplexFlow); var handle = Native.CreateFileW(Assets.BaseBf, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero); - var stream = builder.Build(handle, Assets.BaseBf, flowFormat, library, encoding); + var emulatedBf = builder.Build(handle, Assets.BaseBf, flowFormat, library, encoding); // Write to file for checking. using var fileStream = new FileStream("field.bf", FileMode.Create); - stream.CopyTo(fileStream); + emulatedBf!.Stream.CopyTo(fileStream); fileStream.Close(); // Parse file and check. diff --git a/FileEmulationFramework.Tests/Emulators/BMD/BmdEmulatorTests.cs b/FileEmulationFramework.Tests/Emulators/BMD/BmdEmulatorTests.cs index 6204a60..152004d 100644 --- a/FileEmulationFramework.Tests/Emulators/BMD/BmdEmulatorTests.cs +++ b/FileEmulationFramework.Tests/Emulators/BMD/BmdEmulatorTests.cs @@ -23,16 +23,16 @@ public void SingleMsg() { var msgFormat = MessageFormatVersion.Version1BigEndian; var library = LibraryLookup.GetLibrary("P5R"); - var encoding = AtlusEncoding.GetByName("P5R"); + var encoding = AtlusEncoding.Persona5RoyalEFIGS; var builder = new BmdBuilder(); builder.AddMsgFile(Assets.FirstMsg); var handle = Native.CreateFileW(Assets.BaseBmd, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero); - var stream = builder.Build(handle, Assets.BaseBmd, msgFormat, library, encoding); + var emulatedBmd = builder.Build(handle, Assets.BaseBmd, msgFormat, library, encoding); // Write to file for checking. using var fileStream = new FileStream("e722_103.bmd", FileMode.Create); - stream.CopyTo(fileStream); + emulatedBmd!.Stream.CopyTo(fileStream); fileStream.Close(); // Parse file and check. @@ -48,17 +48,17 @@ public void MultipleMsgs() { var msgFormat = MessageFormatVersion.Version1BigEndian; var library = LibraryLookup.GetLibrary("P5R"); - var encoding = AtlusEncoding.GetByName("P5R"); + var encoding = AtlusEncoding.Persona5RoyalEFIGS; var builder = new BmdBuilder(); builder.AddMsgFile(Assets.FirstMsg); builder.AddMsgFile(Assets.SecondMsg); var handle = Native.CreateFileW(Assets.BaseBmd, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero); - var stream = builder.Build(handle, Assets.BaseBmd, msgFormat, library, encoding); + var emulatedBmd = builder.Build(handle, Assets.BaseBmd, msgFormat, library, encoding); // Write to file for checking. using var fileStream = new FileStream("e722_103.bmd", FileMode.Create); - stream.CopyTo(fileStream); + emulatedBmd!.Stream.CopyTo(fileStream); fileStream.Close(); // Parse file and check. diff --git a/FileEmulationFramework.sln b/FileEmulationFramework.sln index 0971363..b855053 100644 --- a/FileEmulationFramework.sln +++ b/FileEmulationFramework.sln @@ -37,8 +37,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PAK.Stream.Emulator.Interfa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BF.File.Emulator", "Emulator\BF.File.Emulator\BF.File.Emulator.csproj", "{4132B2C3-4380-4193-A1E5-17395311D784}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AtlusScriptLibrary", "Submodules\Atlus-Script-Tools\Source\AtlusScriptLibrary\AtlusScriptLibrary.csproj", "{62EE486F-AE33-4DBB-AC4C-409A22CB04AD}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BF.File.Emulator.Interfaces", "Emulator\Interfaces\BF.File.Emulator.Interfaces\BF.File.Emulator.Interfaces.csproj", "{EE178CAD-7CD0-41EB-96FA-B1DCC4DA4E73}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BMD.File.Emulator", "Emulator\BMD.File.Emulator\BMD.File.Emulator.csproj", "{E053BEE9-B765-462E-A80D-B25949348835}" @@ -52,6 +50,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SPD.File.Emulator", "Emulat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SPD.File.Emulator.Interfaces", "Emulator\Interfaces\SPD.File.Emulator.Interfaces\SPD.File.Emulator.Interfaces.csproj", "{549B1691-ED3F-4AEF-8F60-B6410A361C3A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtlusScriptLibrary", "Submodules\Atlus-Script-Tools\Source\AtlusScriptLibrary\AtlusScriptLibrary.csproj", "{BBBCC290-346C-4AAC-AE76-CAB05243A10C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -81,12 +81,15 @@ Global {3365837F-814B-4478-AA6F-E932986E4652}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3365837F-814B-4478-AA6F-E932986E4652}.Release|Any CPU.ActiveCfg = Release|Any CPU {3365837F-814B-4478-AA6F-E932986E4652}.Release|Any CPU.Build.0 = Release|Any CPU + {3365837F-814B-4478-AA6F-E932986E4652}.Debug|Any CPU.Build.0 = Debug|Any CPU {3A0C238F-28E4-49BC-95FA-2F3A538A3EED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3A0C238F-28E4-49BC-95FA-2F3A538A3EED}.Release|Any CPU.ActiveCfg = Release|Any CPU {3A0C238F-28E4-49BC-95FA-2F3A538A3EED}.Release|Any CPU.Build.0 = Release|Any CPU + {3A0C238F-28E4-49BC-95FA-2F3A538A3EED}.Debug|Any CPU.Build.0 = Debug|Any CPU {F0CB5B64-CC0C-4511-A454-EC6515C9217A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F0CB5B64-CC0C-4511-A454-EC6515C9217A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0CB5B64-CC0C-4511-A454-EC6515C9217A}.Release|Any CPU.Build.0 = Release|Any CPU + {F0CB5B64-CC0C-4511-A454-EC6515C9217A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D8656F8-3C54-469B-909C-8C9200D6C200}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D8656F8-3C54-469B-909C-8C9200D6C200}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D8656F8-3C54-469B-909C-8C9200D6C200}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -98,6 +101,7 @@ Global {DE067AF0-EA87-4A2C-9C04-25438BA23C29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE067AF0-EA87-4A2C-9C04-25438BA23C29}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE067AF0-EA87-4A2C-9C04-25438BA23C29}.Release|Any CPU.Build.0 = Release|Any CPU + {DE067AF0-EA87-4A2C-9C04-25438BA23C29}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAE1FB74-7A16-47A2-8FBC-78A167C4DD9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CAE1FB74-7A16-47A2-8FBC-78A167C4DD9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {CAE1FB74-7A16-47A2-8FBC-78A167C4DD9C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -110,10 +114,6 @@ Global {4132B2C3-4380-4193-A1E5-17395311D784}.Debug|Any CPU.Build.0 = Debug|Any CPU {4132B2C3-4380-4193-A1E5-17395311D784}.Release|Any CPU.ActiveCfg = Release|Any CPU {4132B2C3-4380-4193-A1E5-17395311D784}.Release|Any CPU.Build.0 = Release|Any CPU - {62EE486F-AE33-4DBB-AC4C-409A22CB04AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62EE486F-AE33-4DBB-AC4C-409A22CB04AD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62EE486F-AE33-4DBB-AC4C-409A22CB04AD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62EE486F-AE33-4DBB-AC4C-409A22CB04AD}.Release|Any CPU.Build.0 = Release|Any CPU {EE178CAD-7CD0-41EB-96FA-B1DCC4DA4E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE178CAD-7CD0-41EB-96FA-B1DCC4DA4E73}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE178CAD-7CD0-41EB-96FA-B1DCC4DA4E73}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -134,6 +134,10 @@ Global {549B1691-ED3F-4AEF-8F60-B6410A361C3A}.Debug|Any CPU.Build.0 = Debug|Any CPU {549B1691-ED3F-4AEF-8F60-B6410A361C3A}.Release|Any CPU.ActiveCfg = Release|Any CPU {549B1691-ED3F-4AEF-8F60-B6410A361C3A}.Release|Any CPU.Build.0 = Release|Any CPU + {BBBCC290-346C-4AAC-AE76-CAB05243A10C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBBCC290-346C-4AAC-AE76-CAB05243A10C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBBCC290-346C-4AAC-AE76-CAB05243A10C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBBCC290-346C-4AAC-AE76-CAB05243A10C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -150,12 +154,12 @@ Global {CAE1FB74-7A16-47A2-8FBC-78A167C4DD9C} = {E530561A-D74E-48FD-B16B-8ECA7EE35ED6} {17A7D1A1-DCEB-4C1B-A606-452AA5D968C9} = {604407C2-9BB1-4923-A744-DF5E1551A412} {4132B2C3-4380-4193-A1E5-17395311D784} = {E530561A-D74E-48FD-B16B-8ECA7EE35ED6} - {62EE486F-AE33-4DBB-AC4C-409A22CB04AD} = {732CB582-B44E-4EF6-9FDC-B1126309A9E0} {EE178CAD-7CD0-41EB-96FA-B1DCC4DA4E73} = {604407C2-9BB1-4923-A744-DF5E1551A412} {E053BEE9-B765-462E-A80D-B25949348835} = {E530561A-D74E-48FD-B16B-8ECA7EE35ED6} {C527D538-F3A4-4FC4-A641-B7359691815F} = {604407C2-9BB1-4923-A744-DF5E1551A412} {8B9B13C0-DA50-4E0F-9454-6E95BE9BC9E2} = {E530561A-D74E-48FD-B16B-8ECA7EE35ED6} {549B1691-ED3F-4AEF-8F60-B6410A361C3A} = {604407C2-9BB1-4923-A744-DF5E1551A412} + {BBBCC290-346C-4AAC-AE76-CAB05243A10C} = {732CB582-B44E-4EF6-9FDC-B1126309A9E0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {33D4FC90-A513-4AB4-BF0A-704F0E193DCC} diff --git a/FileEmulationFramework/FileAccessServer.cs b/FileEmulationFramework/FileAccessServer.cs index d8c559e..9297053 100644 --- a/FileEmulationFramework/FileAccessServer.cs +++ b/FileEmulationFramework/FileAccessServer.cs @@ -13,6 +13,7 @@ using Native = FileEmulationFramework.Lib.Utilities.Native; using System.Collections.Concurrent; using Reloaded.Memory.Utilities; +using Microsoft.Win32.SafeHandles; // ReSharper disable RedundantArgumentDefaultValue @@ -31,7 +32,7 @@ public static unsafe class FileAccessServer private static List Emulators { get; } = new(); private static readonly ConcurrentDictionary HandleToInfoMap = new(); - private static readonly ConcurrentDictionary PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary PathToVirtualFileMap = new(StringComparer.OrdinalIgnoreCase); private static readonly ConcurrentDictionary PathToHandleMap = new(StringComparer.OrdinalIgnoreCase); private static readonly object ThreadLock = new(); @@ -39,7 +40,8 @@ public static unsafe class FileAccessServer private static IHook _readFileHook = null!; private static IHook _setFilePointerHook = null!; private static IHook _getFileSizeHook = null!; - private static IHook _getFileAttributesHook = null!; + private static IHook _getFileFullAttributesHook = null!; + private static IHook _getFileAttributesHook = null!; private static IAsmHook _closeHandleHook = null!; private static NtQueryInformationFileFn _ntQueryInformationFile; @@ -60,7 +62,8 @@ public static void Init(Logger logger, NativeFunctions functions, IReloadedHooks _setFilePointerHook = functions.SetFilePointer.Hook(typeof(FileAccessServer), nameof(SetInformationFileHook)).Activate(); _getFileSizeHook = functions.GetFileSize.Hook(typeof(FileAccessServer), nameof(QueryInformationFileImpl)).Activate(); _ntQueryInformationFile = _getFileSizeHook.OriginalFunction; - _getFileAttributesHook = functions.NtQueryFullAttributes.Hook(typeof(FileAccessServer), nameof(QueryAttributesFileImpl)).Activate(); + _getFileFullAttributesHook = functions.NtQueryFullAttributes.Hook(typeof(FileAccessServer), nameof(QueryFullAttributesFileImpl)).Activate(); + _getFileAttributesHook = functions.NtQueryAttributes.Hook(typeof(FileAccessServer), nameof(QueryAttributesFileImpl)).Activate(); // We need to cook some assembly for NtClose, because Native->Managed // transition can invoke thread setup code which will call CloseHandle again @@ -106,27 +109,80 @@ private static void DequeueHandles() private static int 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)) + if (!HandleToInfoMap.TryGetValue(hfile, out var info)) return result; + + if (fileInformationClass == FileInformationClass.FileStandardInformation) + { + var information = (FILE_STANDARD_INFORMATION*)fileInformation; + var oldSize = information->EndOfFile; + var newSize = info.File.GetFileSize(hfile, info); + if (newSize != -1) + information->EndOfFile = newSize; - var information = (FILE_STANDARD_INFORMATION*)fileInformation; - var oldSize = information->EndOfFile; - var newSize = info.File.GetFileSize(hfile, info); - if (newSize != -1) - information->EndOfFile = newSize; + _logger.Info("[FileAccessServer] File Size Override | Old: {0}, New: {1} | {2}", oldSize, newSize, info.FilePath); + } + else if (fileInformationClass == FileInformationClass.FileBasicInformation) + { + var information = (FILE_BASIC_INFORMATION*)fileInformation; + if (info.File.TryGetLastWriteTime(hfile, info, out var newWriteTime)) + { + var oldWriteTime = information->LastWriteTime.ToDateTime(); + information->LastWriteTime = new LARGE_INTEGER(newWriteTime!.Value.ToFileTimeUtc()); + _logger.Info("[FileAccessServer] File Last Write Override | Old: {0}, New: {1} | {2}", oldWriteTime, + newWriteTime, info.FilePath); + } + } - _logger.Info("[FileAccessServer] File Size Override | Old: {0}, New: {1} | {2}", oldSize, newSize, info.FilePath); return result; } [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] - private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_NETWORK_OPEN_INFORMATION* information) + private static int QueryFullAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_NETWORK_OPEN_INFORMATION* information) { - var result = _getFileAttributesHook.OriginalFunction.Value.Invoke(attributes, information); + var result = _getFileFullAttributesHook.OriginalFunction.Value.Invoke(attributes, information); + + // We don't support directories currently + if ((information->FileAttributes & (uint)FileAttributes.Directory) != 0) + return result; + var path = Strings.TrimWindowsPrefixes(attributes->ObjectName); + SafeFileHandle? safeHandle = null; + if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info)) - return result; + { + // We haven't tried to create an emulated file for this yet, try it + if (!PathToVirtualFileMap.TryGetValue(path, out info)) + { + // Prevent recursion + PathToVirtualFileMap[path] = null; + + // There is a virtual file but no handle exists for it, we need to make one + try + { + safeHandle = File.OpenHandle(path); + } + catch (Exception e) + { + // If we couldn't make the handle this probably isn't a file we can emulate + return result; + } + + hfile = safeHandle.DangerousGetHandle(); + + // We tried to make one but failed, this file isn't emulated + if (!HandleToInfoMap.TryGetValue(hfile, out info)) + { + safeHandle.Close(); + return result; + } + } + + // We've tried to create an emulated file for this before but failed, this file isn't emulated + if (info == null) + return result; + } var oldSize = information->EndOfFile; var newSize = info.File.GetFileSize(hfile, info); @@ -135,6 +191,38 @@ private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_N _logger.Info("[FileAccessServer] File Size Override | Old: {0}, New: {1} | {2}", oldSize, newSize, path); + if (info.File.TryGetLastWriteTime(hfile, info, out var newWriteTime)) + { + var oldWriteTime = information->LastWriteTime.ToDateTime(); + information->LastWriteTime = new LARGE_INTEGER(newWriteTime!.Value.ToFileTimeUtc()); + _logger.Info("[FileAccessServer] File Last Write Override | Old: {0}, New: {1} | {2}", oldWriteTime, + newWriteTime, path); + } + + // Clean up if we needed to make a new handle + if (safeHandle != null) + safeHandle.Close(); + + return result; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int QueryAttributesFileImpl(OBJECT_ATTRIBUTES* attributes, FILE_BASIC_INFORMATION* information) + { + var result = _getFileAttributesHook.OriginalFunction.Value.Invoke(attributes, information); + var path = Strings.TrimWindowsPrefixes(attributes->ObjectName); + + if (!PathToHandleMap.TryGetValue(path, out var hfile) || !HandleToInfoMap.TryGetValue(hfile, out var info)) + return result; + + if (info.File.TryGetLastWriteTime(hfile, info, out var newWriteTime)) + { + var oldWriteTime = information->LastWriteTime.ToDateTime(); + information->LastWriteTime = new LARGE_INTEGER(newWriteTime!.Value.ToFileTimeUtc()); + _logger.Info("[FileAccessServer] File Last Write Override | Old: {0}, New: {1} | {2}", oldWriteTime, + newWriteTime, path); + } + return result; } @@ -235,7 +323,7 @@ private static int NtCreateFileImpl(IntPtr* handle, FileAccess access, OBJECT_AT _logger.Debug("[FileAccessServer] Accessing: {0}, {1}, Route: {2}", hndl, newFilePath, _currentRoute.FullPath); // Try Accept New File (virtual override) - if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo)) + if (PathToVirtualFileMap.TryGetValue(newFilePath, out var fileInfo) && fileInfo != null) { // Reuse of emulated file (backed by stream) is safe because file access is single threaded. lock (ThreadLock) diff --git a/FileEmulationFramework/Utilities/Native.cs b/FileEmulationFramework/Utilities/Native.cs index d660835..aa2166e 100644 --- a/FileEmulationFramework/Utilities/Native.cs +++ b/FileEmulationFramework/Utilities/Native.cs @@ -86,6 +86,17 @@ public FuncPtr< > Value; } + [Reloaded.Hooks.Definitions.X64.Function(Reloaded.Hooks.Definitions.X64.CallingConventions.Microsoft)] + [Reloaded.Hooks.Definitions.X86.Function(Reloaded.Hooks.Definitions.X86.CallingConventions.Stdcall)] + public struct NtQueryAttributesFileFn + { + public FuncPtr< + Ptr, // attributes + Ptr, // information + int // status + > Value; + } + [Reloaded.Hooks.Definitions.X64.Function(Reloaded.Hooks.Definitions.X64.CallingConventions.Microsoft)] [Reloaded.Hooks.Definitions.X86.Function(Reloaded.Hooks.Definitions.X86.CallingConventions.Stdcall)] public struct NtQueryFullAttributesFileFn @@ -118,6 +129,17 @@ public struct FILE_STANDARD_INFORMATION public bool Directory; } + [StructLayout(LayoutKind.Sequential)] + public struct FILE_BASIC_INFORMATION + { + public LARGE_INTEGER CreationTime; + public LARGE_INTEGER LastAccessTime; + public LARGE_INTEGER LastWriteTime; + public LARGE_INTEGER ChangeTime; + public ulong FileAttributes; + } + + [StructLayout(LayoutKind.Sequential)] public struct FILE_NETWORK_OPEN_INFORMATION { diff --git a/FileEmulationFramework/Utilities/NativeFunctions.cs b/FileEmulationFramework/Utilities/NativeFunctions.cs index c2db931..24f1f52 100644 --- a/FileEmulationFramework/Utilities/NativeFunctions.cs +++ b/FileEmulationFramework/Utilities/NativeFunctions.cs @@ -16,15 +16,17 @@ public struct NativeFunctions public IFunction SetFilePointer; public IFunction GetFileSize; public IFunction NtQueryFullAttributes; + public IFunction NtQueryAttributes; public IFunction CloseHandle; - public NativeFunctions(IntPtr ntCreateFile, IntPtr ntReadFile, IntPtr ntSetInformationFile, IntPtr ntQueryInformationFile, IntPtr ntQueryFullAttributesFile, IntPtr closeHandle, IReloadedHooks hooks) + public NativeFunctions(IntPtr ntCreateFile, IntPtr ntReadFile, IntPtr ntSetInformationFile, IntPtr ntQueryInformationFile, IntPtr ntQueryFullAttributesFile, IntPtr ntQueryAttributesFile, IntPtr closeHandle, IReloadedHooks hooks) { NtCreateFile = hooks.CreateFunction(ntCreateFile); NtReadFile = hooks.CreateFunction(ntReadFile); SetFilePointer = hooks.CreateFunction(ntSetInformationFile); GetFileSize = hooks.CreateFunction(ntQueryInformationFile); NtQueryFullAttributes = hooks.CreateFunction(ntQueryFullAttributesFile); + NtQueryAttributes = hooks.CreateFunction(ntQueryAttributesFile); CloseHandle = hooks.CreateFunction(closeHandle); } @@ -41,12 +43,13 @@ public static NativeFunctions GetInstance(IReloadedHooks hooks) var ntReadFilePointer = NativeFn.GetProcAddress(ntdllHandle, "NtReadFile"); var setFilePointer = NativeFn.GetProcAddress(ntdllHandle, "NtSetInformationFile"); var getFileSize = NativeFn.GetProcAddress(ntdllHandle, "NtQueryInformationFile"); - var ntQueryAttributesPointer = NativeFn.GetProcAddress(ntdllHandle, "NtQueryFullAttributesFile"); + var ntQueryFullAttributesPointer = NativeFn.GetProcAddress(ntdllHandle, "NtQueryFullAttributesFile"); + var ntQueryAttributesPointer = NativeFn.GetProcAddress(ntdllHandle, "NtQueryAttributesFile"); var k32Handle = NativeFn.LoadLibrary("kernel32"); var closeHandle = NativeFn.GetProcAddress(k32Handle, "CloseHandle"); - _instance = new(ntCreateFilePointer, ntReadFilePointer, setFilePointer, getFileSize, ntQueryAttributesPointer, closeHandle, hooks); + _instance = new(ntCreateFilePointer, ntReadFilePointer, setFilePointer, getFileSize, ntQueryFullAttributesPointer, ntQueryAttributesPointer, closeHandle, hooks); _instanceMade = true; return _instance; diff --git a/Submodules/Atlus-Script-Tools b/Submodules/Atlus-Script-Tools index 59a6b9b..fce608b 160000 --- a/Submodules/Atlus-Script-Tools +++ b/Submodules/Atlus-Script-Tools @@ -1 +1 @@ -Subproject commit 59a6b9b54846b95675241a81ec3d6517243d3309 +Subproject commit fce608b6238f645b4f7bf6c7a5d70c0305d2499b diff --git a/global.json b/global.json index 17d3d8d..d4e0b89 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,7 @@ { - "sdk": { - "version": "7.0.100", - "rollForward": "latestFeature" - } - } \ No newline at end of file + "sdk": { + "version": "8.0.0", + "rollForward": "latestFeature", + "allowPrerelease": true + } +} \ No newline at end of file