Skip to content

Commit b526438

Browse files
authored
Merge pull request #24 from AnimatedSwine37/localisation-framework-stuff
Script Tools Update, Support Emulating Last Write Time, Error Handling Improvements, Other Stuff
2 parents ed4e857 + 4286394 commit b526438

File tree

27 files changed

+540
-163
lines changed

27 files changed

+540
-163
lines changed

.gitmodules

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@
33
url = https://github.com/Heroes-Hacking-Central/Heroes.SDK.git
44
[submodule "Submodules/Atlus-Script-Tools"]
55
path = Submodules/Atlus-Script-Tools
6-
url = https://github.com/AnimatedSwine37/Atlus-Script-Tools.git
7-
branch = bf-emulator
6+
url = https://github.com/tge-was-taken/Atlus-Script-Tools.git

Emulator/BF.File.Emulator/Bf/BfBuilder.cs

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,38 @@ public void AddEnumFile(string filePath)
105105
}
106106
}
107107

108+
/// <summary>
109+
/// Tries to get all files that the base flow imports
110+
/// </summary>
111+
/// <param name="flowFormat">The format of the flowscript</param>
112+
/// <param name="library">The library to use for the flowscript</param>
113+
/// <param name="encoding">The encoding of the flowscript</param>
114+
/// <param name="foundImports">An array of absolute paths to all files that the base flows import,
115+
/// both directly and transitively. This includes the base files.</param>
116+
/// <returns></returns>
117+
public bool TryGetImports(FlowFormatVersion flowFormat, Library library, Encoding encoding,
118+
out string[] foundImports)
119+
{
120+
// Use compiler arg overrides (if they're there)
121+
if (_library != null) library = _library;
122+
if (_encoding != null) encoding = _encoding;
123+
if (_flowFormat != null) flowFormat = (FlowFormatVersion)_flowFormat;
124+
125+
var compiler = new FlowScriptCompiler(flowFormat);
126+
compiler.Library = OverrideLibraries(library);
127+
compiler.Encoding = encoding;
128+
129+
var imports = new List<string>();
130+
imports.AddRange(_flowFiles);
131+
imports.AddRange(_msgFiles);
132+
133+
return compiler.TryGetImports(imports, out foundImports);
134+
}
135+
108136
/// <summary>
109137
/// Builds a BF file.
110138
/// </summary>
111-
public unsafe Stream? Build(IntPtr originalHandle, string originalPath, FlowFormatVersion flowFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBf = false)
139+
public EmulatedBf? Build(IntPtr originalHandle, string originalPath, FlowFormatVersion flowFormat, Library library, Encoding encoding, AtlusLogListener? listener = null, bool noBaseBf = false)
112140
{
113141
_log?.Info("[BfEmulator] Building BF File | {0}", originalPath);
114142

@@ -135,21 +163,43 @@ public void AddEnumFile(string filePath)
135163
imports.AddRange(_flowFiles.GetRange(1, _flowFiles.Count - 1));
136164
imports.AddRange(_msgFiles);
137165

138-
if (!compiler.TryCompileWithImports(bfStream, imports, baseFlow, out FlowScript flowScript))
166+
try
139167
{
140-
_log?.Error("[BfEmulator] Failed to compile BF File | {0}", originalPath);
141-
return null;
142-
}
168+
if (!compiler.TryCompileWithImports(bfStream, imports, baseFlow, out FlowScript flowScript,
169+
out var sources))
170+
{
171+
_log?.Error("[BfEmulator] Failed to compile BF File | {0}", originalPath);
172+
return null;
173+
}
143174

144-
// Return the compiled bf
145-
var bfBinary = flowScript.ToBinary();
146-
var stream = StreamUtils.CreateMemoryStream(bfBinary.Header.FileSize);
147-
bfBinary.ToStream(stream, true);
148-
stream.Position = 0;
175+
// Return the compiled bf
176+
var bfBinary = flowScript.ToBinary();
177+
var stream = StreamUtils.CreateMemoryStream(bfBinary.Header.FileSize);
178+
bfBinary.ToStream(stream, true);
179+
stream.Position = 0;
149180

150-
return stream;
181+
DateTime lastWrite = sources.Where(x => x != null).Select(Fiel.GetLastWriteTimeUtc).Max();
182+
return new EmulatedBf(stream, sources, lastWrite);
183+
}
184+
catch (Exception exception)
185+
{
186+
var flows = string.Join(", ", _flowFiles.Concat(_msgFiles));
187+
_log.Error(
188+
"[BF Builder] Failed to compile bf {0} with source files: {1}. This may be due to your mods not being translated. Error: {2}",
189+
originalPath, flows, exception.Message);
190+
if (exception.StackTrace != null)
191+
_log.Error(exception.StackTrace);
192+
return null;
193+
}
151194
}
152195

196+
/// <summary>
197+
/// Applies library file overrides to the base library.
198+
/// This adds aliases to functions with different names, replaces functions with different
199+
/// return values or parameters, and adds any completely new functions.
200+
/// </summary>
201+
/// <param name="library">The base library to override</param>
202+
/// <returns>A deep copy of the base library with overrides applied</returns>
153203
private Library OverrideLibraries(Library library)
154204
{
155205
if (_libraryEnums.Count == 0 && _libraryFuncs.Count == 0) return library;
@@ -176,7 +226,15 @@ private Library OverrideLibraries(Library library)
176226

177227
if (module != -1 && index != -1)
178228
{
179-
library.FlowScriptModules[module].Functions[index] = func;
229+
var existingFunc = library.FlowScriptModules[module].Functions[index];
230+
if (FlowFunctionsSame(existingFunc, func))
231+
{
232+
existingFunc.Aliases.Add(func.Name);
233+
}
234+
else
235+
{
236+
library.FlowScriptModules[module].Functions[index] = func;
237+
}
180238
}
181239
else
182240
{
@@ -188,4 +246,17 @@ private Library OverrideLibraries(Library library)
188246
library.FlowScriptModules[0].Enums.AddRange(_libraryEnums);
189247
return library;
190248
}
191-
}
249+
250+
/// <summary>
251+
/// Checks if two flowscript functions are effectively the same.
252+
/// They are the same if the return type and parameter types are the same.
253+
/// </summary>
254+
/// <param name="func1">The first function to compare</param>
255+
/// <param name="func2">The other function to compare</param>
256+
/// <returns>Truee if the two functions are effectively the same, false otherwise</returns>
257+
private bool FlowFunctionsSame(FlowScriptModuleFunction func1, FlowScriptModuleFunction func2)
258+
{
259+
return func1.ReturnType == func2.ReturnType && func1.Parameters.Select(param => param.Type)
260+
.SequenceEqual(func2.Parameters.Select(param => param.Type));
261+
}
262+
}

Emulator/BF.File.Emulator/Bf/BfBuilderFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private void OverrideCompilerArgs(string file)
148148

149149
_flowFormat = GetFlowScriptFormatVersion(outFormat);
150150
_library = LibraryLookup.GetLibrary(args.Library);
151-
_encoding = AtlusEncoding.GetByName(args.Encoding);
151+
_encoding = AtlusEncoding.Create(args.Encoding);
152152

153153
_log.Info($"[BfBuilderFactory] Changed script compiler args to OutFormat: {args.OutFormat}, Library: {args.Library}, Encoding: {args.Encoding}");
154154
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace BF.File.Emulator.Bf;
2+
3+
/// <summary>
4+
/// Contains information about a BF file that has been emulated
5+
/// </summary>
6+
public class EmulatedBf
7+
{
8+
/// <summary>
9+
/// A list of full paths to all source files used to compile this bf.
10+
/// This includes all msg, bf, and flow files that are imported excluding the base bf.
11+
/// </summary>
12+
public List<string> Sources { get; }
13+
14+
/// <summary>
15+
/// The stream for the emulated file
16+
/// </summary>
17+
public Stream Stream { get; }
18+
19+
/// <summary>
20+
/// The last write time of the file
21+
/// This is set as the maximum last write time of all sources when the file was first emulated
22+
/// </summary>
23+
public DateTime LastWriteTime { get; }
24+
25+
public EmulatedBf(Stream stream, List<String> sources, DateTime lastWriteTime)
26+
{
27+
Sources = sources;
28+
Stream = stream;
29+
LastWriteTime = lastWriteTime;
30+
}
31+
}

Emulator/BF.File.Emulator/BfEmulator.cs

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class BfEmulator : IEmulator
2525

2626
// Note: Handle->Stream exists because hashing IntPtr is easier; thus can resolve reads faster.
2727
private readonly BfBuilderFactory _builderFactory;
28-
private readonly ConcurrentDictionary<string, Stream?> _pathToStream = new(StringComparer.OrdinalIgnoreCase);
28+
private readonly ConcurrentDictionary<string, EmulatedBf?> _pathToEmulated = new(StringComparer.OrdinalIgnoreCase);
2929
private Logger _log;
3030

3131
private FlowFormatVersion _flowFormat;
@@ -46,17 +46,17 @@ public BfEmulator(Logger log, bool dumpFiles, Game game)
4646
case Game.P3P:
4747
_flowFormat = FlowFormatVersion.Version1;
4848
_library = LibraryLookup.GetLibrary("P3P");
49-
_encoding = AtlusEncoding.GetByName("P4");
49+
_encoding = AtlusEncoding.Create("P3P_EFIGS");
5050
break;
5151
case Game.P4G:
5252
_flowFormat = FlowFormatVersion.Version1;
5353
_library = LibraryLookup.GetLibrary("P4G");
54-
_encoding = AtlusEncoding.GetByName("P4");
54+
_encoding = AtlusEncoding.Create("P4G_EFIGS");
5555
break;
5656
case Game.P5R:
5757
_flowFormat = FlowFormatVersion.Version3BigEndian;
5858
_library = LibraryLookup.GetLibrary("P5R");
59-
_encoding = AtlusEncoding.GetByName("P5R");
59+
_encoding = AtlusEncoding.Create("P5R_EFIGS");
6060
break;
6161
}
6262
}
@@ -65,13 +65,13 @@ public bool TryCreateFile(IntPtr handle, string filepath, string route, out IEmu
6565
{
6666
// Check if we already made a custom BF for this file.
6767
emulated = null!;
68-
if (_pathToStream.TryGetValue(filepath, out var stream))
68+
if (_pathToEmulated.TryGetValue(filepath, out var emulatedBf))
6969
{
7070
// Avoid recursion into same file.
71-
if (stream == null)
71+
if (emulatedBf == null)
7272
return false;
7373

74-
emulated = new EmulatedFile<Stream>(stream);
74+
emulated = new EmulatedFile<Stream>(emulatedBf.Stream, emulatedBf.LastWriteTime);
7575
return true;
7676
}
7777

@@ -109,15 +109,16 @@ public bool TryCreateEmulatedFile(IntPtr handle, string srcDataPath, string outp
109109
return false;
110110

111111
// Make the BF file.
112-
_pathToStream[outputPath] = null; // Avoid recursion into same file.
112+
_pathToEmulated[outputPath] = null; // Avoid recursion into same file.
113113

114-
stream = builder!.Build(handle, srcDataPath, _flowFormat, _library, _encoding, _listener, isEmpty);
115-
if (stream == null)
114+
var emulatedBf = builder!.Build(handle, srcDataPath, _flowFormat, _library, _encoding, _listener, isEmpty);
115+
if (emulatedBf == null)
116116
return false;
117117

118-
_pathToStream.TryAdd(outputPath, stream);
119-
emulated = new EmulatedFile<Stream>(stream);
120-
_log.Info("[BfEmulator] Created Emulated file with Path {0}", outputPath);
118+
stream = emulatedBf.Stream;
119+
_pathToEmulated.TryAdd(outputPath, emulatedBf);
120+
emulated = new EmulatedFile<Stream>(stream, emulatedBf.LastWriteTime);
121+
_log.Info("[BfEmulator] Created Emulated file with Path {0} and Last Write {1}", outputPath, emulatedBf.LastWriteTime);
121122

122123
if (DumpFiles)
123124
DumpFile(route, stream);
@@ -143,13 +144,13 @@ public void OnModLoading(string modFolder)
143144
/// <param name="bfPath">Full path to the file.</param>
144145
public void UnregisterFile(string bfPath)
145146
{
146-
_pathToStream!.Remove(bfPath, out var stream);
147-
stream?.Dispose();
147+
_pathToEmulated!.Remove(bfPath, out var emulated);
148+
emulated?.Stream.Dispose();
148149
}
149150

150-
public void RegisterFile(string destinationPath, Stream stream)
151+
public void RegisterFile(string destinationPath, Stream stream, DateTime lastWriteTime)
151152
{
152-
_pathToStream.TryAdd(destinationPath, stream);
153+
_pathToEmulated.TryAdd(destinationPath, new EmulatedBf(stream, new List<string>(), lastWriteTime));
153154
}
154155

155156
private void DumpFile(string route, Stream stream)
@@ -162,11 +163,25 @@ private void DumpFile(string route, Stream stream)
162163
_log.Info($"[BfEmulator] Written To {dumpPath}");
163164
}
164165

166+
internal bool TryGetImports(string route, out string[] imports)
167+
{
168+
if (!_builderFactory.TryCreateFromPath(route, out var builder))
169+
{
170+
imports = Array.Empty<string>();
171+
return false;
172+
}
173+
174+
return builder!.TryGetImports(_flowFormat, _library, _encoding, out imports);
175+
}
176+
165177
internal List<RouteGroupTuple> GetInput() => _builderFactory.RouteFileTuples;
166178

167179
internal void AddFromFolders(string dir) => _builderFactory.AddFromFolders(dir);
168180

169181
internal void AddFile(string file, string route) => _builderFactory.AddFile(Path.GetFileName(file), file, Path.GetDirectoryName(file)!, route);
182+
183+
internal void SetEncoding(string encoding) => _encoding = AtlusEncoding.Create(encoding);
184+
170185
}
171186

172187
public class AtlusLogListener : LogListener

Emulator/BF.File.Emulator/BfEmulatorApi.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using BF.File.Emulator.Interfaces;
1+
using System.Diagnostics;
2+
using BF.File.Emulator.Interfaces;
23
using BF.File.Emulator.Interfaces.Structures.IO;
34
using BF.File.Emulator.Utilities;
45
using FileEmulationFramework.Interfaces;
@@ -7,6 +8,9 @@
78
using Microsoft.Win32.SafeHandles;
89
using System.Runtime.InteropServices;
910

11+
// Aliasing for readability, since our assembly name has priority over 'File'
12+
using Fiel = System.IO.File;
13+
1014
namespace BF.File.Emulator;
1115

1216
public class BfEmulatorApi : IBfEmulator
@@ -57,8 +61,9 @@ public void RegisterBf(string sourcePath, string destinationPath)
5761

5862
Native.SetFilePointerEx(handle, 0, IntPtr.Zero, 0);
5963
var fileStream = new FileStream(new SafeFileHandle(handle, true), FileAccess.Read);
60-
var emulated = new EmulatedFile<FileStream>(fileStream);
61-
_bfEmulator.RegisterFile(destinationPath, fileStream);
64+
var lastWrite = Fiel.GetLastWriteTimeUtc(sourcePath);
65+
var emulated = new EmulatedFile<FileStream>(fileStream, lastWrite);
66+
_bfEmulator.RegisterFile(destinationPath, fileStream, lastWrite);
6267
_framework.RegisterVirtualFile(destinationPath, emulated, false);
6368

6469
_logger.Info("[BfEmulatorApi] Registered bf {0} at {1}", sourcePath, destinationPath);
@@ -97,4 +102,14 @@ public void AddDirectory(string dir)
97102
_bfEmulator.AddFromFolders(dir);
98103
}
99104

105+
public void SetEncoding(string encoding)
106+
{
107+
_logger.Info("Setting encoding to {0}", encoding);
108+
_bfEmulator.SetEncoding(encoding);
109+
}
110+
111+
public bool TryGetImports(string route, out string[] imports)
112+
{
113+
return _bfEmulator.TryGetImports(route, out imports);
114+
}
100115
}

0 commit comments

Comments
 (0)