From f362e6c626bc6eb0f8aa5e7f6392731f2403fbd4 Mon Sep 17 00:00:00 2001 From: shadowslasher410 Date: Thu, 30 Apr 2026 22:27:16 -0500 Subject: [PATCH 1/2] Enable x64 platforms and fix native bugs Add explicit AnyCPU;x64 platform support and x64 property groups update solution VS version/config mappings (was necessary as I use AMD64 CPU). Update NuGet package versions (Google.Protobuf, Grpc.Tools, System.IO.Hashing, ZstdSharp.Port). Improve DebuggerFrontend error handling and modernize using patterns. Fix native/C++ issues: safer allocator (null check), prevent buffer leak when loading PhysX binary collections by tracking buffers and adding ReleaseCollection, adjust PhysicsTool project warnings/settings, and harden LZ4 wrapper (zero-init structs, compress/decompress size overflow checks). Miscellaneous code cleanups and refactors in PxEncoder/PxLoader to handle index types and const-correctness. --- ConverterApp/ConverterApp.csproj | 13 + DebuggerFrontend/DbgClient.cs | 43 +- DebuggerFrontend/DebuggerFrontend.csproj | 5 +- Divine/Divine.csproj | 1 + LSLib/LSLib.csproj | 23 +- LSLibNative/LSLibNative.vcxproj | 1 + LSLibNative/lz4wrapper.cpp | 18 +- LSLibStats/LSLibStats.csproj | 1 + LSTools.sln | 86 +-- PhysicsTool/PhysicsTool.cpp | 35 +- PhysicsTool/PhysicsTool.h | 3 + PhysicsTool/PhysicsTool.vcxproj | 4 + PhysicsTool/PxEncoder.cpp | 32 +- PhysicsTool/PxLoader.cpp | 731 +++++------------------ RconClient/RakNetClient.cs | 70 +-- RconClient/RconClient.csproj | 1 + StatParser/StatParser.csproj | 1 + StoryCompiler/StoryCompiler.csproj | 5 +- StoryDecompiler/StoryDecompiler.csproj | 1 + VTexTool/VTexTool.csproj | 1 + 20 files changed, 347 insertions(+), 728 deletions(-) diff --git a/ConverterApp/ConverterApp.csproj b/ConverterApp/ConverterApp.csproj index e8a4d4c4..30b4c7ae 100644 --- a/ConverterApp/ConverterApp.csproj +++ b/ConverterApp/ConverterApp.csproj @@ -20,15 +20,23 @@ false true true + AnyCPU;x64 x64 + + x64 + x64 + + x64 + + Always @@ -37,6 +45,11 @@ true MinimumRecommendedRules.ruleset + + bin\Editor Debug\ + true + MinimumRecommendedRules.ruleset + UserControl diff --git a/DebuggerFrontend/DbgClient.cs b/DebuggerFrontend/DbgClient.cs index 4ca3641d..38a7b93d 100644 --- a/DebuggerFrontend/DbgClient.cs +++ b/DebuggerFrontend/DbgClient.cs @@ -39,7 +39,8 @@ public void RunLoop() } catch (SocketException e) { - throw e; + Console.WriteLine($"Socket Error [{e.SocketErrorCode}]: {e.Message}"); + throw; } while (BufferPos >= 4) @@ -75,20 +76,18 @@ public void RunLoop() public void Send(DebuggerToBackend message) { - using (var ms = new MemoryStream()) - { - message.WriteTo(ms); - - var length = ms.Position + 4; - var lengthBuf = new byte[4]; - lengthBuf[0] = (byte)(length & 0xff); - lengthBuf[1] = (byte)((length >> 8) & 0xff); - lengthBuf[2] = (byte)((length >> 16) & 0xff); - lengthBuf[3] = (byte)((length >> 24) & 0xff); - Socket.Client.Send(lengthBuf); - var payload = ms.ToArray(); - Socket.Client.Send(payload); - } + using var ms = new MemoryStream(); + message.WriteTo(ms); + + var length = ms.Position + 4; + var lengthBuf = new byte[4]; + lengthBuf[0] = (byte)(length & 0xff); + lengthBuf[1] = (byte)((length >> 8) & 0xff); + lengthBuf[2] = (byte)((length >> 16) & 0xff); + lengthBuf[3] = (byte)((length >> 24) & 0xff); + Socket.Client.Send(lengthBuf); + var payload = ms.ToArray(); + Socket.Client.Send(payload); } } @@ -155,14 +154,12 @@ private void LogMessage(IMessage message) { if (LogStream != null) { - using (var writer = new StreamWriter(LogStream, Encoding.UTF8, 0x1000, true)) - { - writer.Write(" DBG >>> "); - var settings = new JsonFormatter.Settings(true); - var formatter = new JsonFormatter(settings); - formatter.Format(message, writer); - writer.Write("\r\n"); - } + using var writer = new StreamWriter(LogStream, Encoding.UTF8, 0x1000, true); + writer.Write(" DBG >>> "); + var settings = new JsonFormatter.Settings(true); + var formatter = new JsonFormatter(settings); + formatter.Format(message, writer); + writer.Write("\r\n"); } } diff --git a/DebuggerFrontend/DebuggerFrontend.csproj b/DebuggerFrontend/DebuggerFrontend.csproj index 034aeab4..d45c18e9 100644 --- a/DebuggerFrontend/DebuggerFrontend.csproj +++ b/DebuggerFrontend/DebuggerFrontend.csproj @@ -5,6 +5,7 @@ LSTools.DebuggerFrontend false x64 + AnyCPU;x64 @@ -14,8 +15,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Divine/Divine.csproj b/Divine/Divine.csproj index e4a8d2e9..f9b594db 100644 --- a/Divine/Divine.csproj +++ b/Divine/Divine.csproj @@ -8,6 +8,7 @@ LSLib 1.15.14.0 1.15.14.0 + AnyCPU;x64 diff --git a/LSLib/LSLib.csproj b/LSLib/LSLib.csproj index 4a6604ee..f6861e6a 100644 --- a/LSLib/LSLib.csproj +++ b/LSLib/LSLib.csproj @@ -3,17 +3,28 @@ net8.0 Library false + AnyCPU;x64 TRACE;DEBUG;EXPORT_GPPG x64 true + + TRACE;DEBUG;EXPORT_GPPG + x64 + true + EXPORT_GPPG x64 true + + EXPORT_GPPG + x64 + true + bin\Editor Debug\ TRACE;EXPORT_GPPG @@ -22,6 +33,14 @@ x86 MinimumRecommendedRules.ruleset + + bin\Editor Debug\ + TRACE;EXPORT_GPPG + true + true + x86 + MinimumRecommendedRules.ruleset + @@ -35,8 +54,8 @@ - - + + diff --git a/LSLibNative/LSLibNative.vcxproj b/LSLibNative/LSLibNative.vcxproj index 6db633c8..4c7efe7a 100644 --- a/LSLibNative/LSLibNative.vcxproj +++ b/LSLibNative/LSLibNative.vcxproj @@ -73,6 +73,7 @@ NotUsing ../external/bullet-2.77/src;../external/bullet-2.77/Extras/Serialize stdcpp20 + 6262 true diff --git a/LSLibNative/lz4wrapper.cpp b/LSLibNative/lz4wrapper.cpp index 08984faf..a61840ff 100644 --- a/LSLibNative/lz4wrapper.cpp +++ b/LSLibNative/lz4wrapper.cpp @@ -2,6 +2,9 @@ #include "lz4wrapper.h" +struct LZ4F_cctx_s {}; +struct LZ4F_dctx_s {}; + namespace LSLib { namespace Native { array ^ LZ4FrameCompressor::Compress(array ^ input) @@ -17,7 +20,7 @@ namespace LSLib { std::vector output(0x10000); size_t inputOffset = 0, outputOffset = 0; - LZ4F_preferences_t preferences; + LZ4F_preferences_t preferences{}; preferences.frameInfo.blockSizeID = max64KB; preferences.frameInfo.blockMode = blockLinked; preferences.frameInfo.contentChecksumFlag = noContentChecksum; @@ -27,7 +30,7 @@ namespace LSLib { preferences.compressionLevel = 9; preferences.autoFlush = 1; - LZ4F_compressOptions_t options; + LZ4F_compressOptions_t options{}; options.stableSrc = 1; auto headerSize = LZ4F_compressBegin(cctx, output.data(), output.size(), &preferences); @@ -91,7 +94,11 @@ namespace LSLib { // Copy the output to a managed array LZ4F_freeCompressionContext(cctx); - array ^ compressed = gcnew array(outputOffset); + if (outputOffset > (size_t)Int32::MaxValue) { + + throw gcnew System::OverflowException("Compressed size exceeds maximum .NET array length."); + } + array^ compressed = gcnew array((int)outputOffset); pin_ptr compPtr(&compressed[compressed->GetLowerBound(0)]); byte * comp = compPtr; memcpy(comp, output.data(), outputOffset); @@ -146,7 +153,10 @@ namespace LSLib { // Copy the output to a managed array LZ4F_freeDecompressionContext(dctx); - array ^ decompressed = gcnew array(outputOffset); + if (outputOffset > (size_t)Int32::MaxValue) { + throw gcnew System::OverflowException("Decompressed size exceeds maximum .NET array length."); + } + array ^ decompressed = gcnew array((int)outputOffset); if (outputOffset > 0) { pin_ptr decompPtr(&decompressed[decompressed->GetLowerBound(0)]); diff --git a/LSLibStats/LSLibStats.csproj b/LSLibStats/LSLibStats.csproj index 8c2a00ac..a9285803 100644 --- a/LSLibStats/LSLibStats.csproj +++ b/LSLibStats/LSLibStats.csproj @@ -4,6 +4,7 @@ net8.0 enable annotations + AnyCPU;x64 diff --git a/LSTools.sln b/LSTools.sln index ed269297..fda31247 100644 --- a/LSTools.sln +++ b/LSTools.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34309.116 +# Visual Studio Version 18 +VisualStudioVersion = 18.5.11723.231 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LSLib", "LSLib\LSLib.csproj", "{46372C50-4288-4B8E-AF21-C934560600E0}" EndProject @@ -55,14 +55,14 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x64.ActiveCfg = Debug|Any CPU - {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x64.Build.0 = Debug|Any CPU + {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x64.ActiveCfg = Debug|x64 + {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x64.Build.0 = Debug|x64 {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x86.ActiveCfg = Debug|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.Debug|x86.Build.0 = Debug|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.Release|Any CPU.Build.0 = Release|Any CPU - {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x64.ActiveCfg = Release|Any CPU - {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x64.Build.0 = Release|Any CPU + {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x64.ActiveCfg = Release|x64 + {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x64.Build.0 = Release|x64 {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x86.ActiveCfg = Release|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.Release|x86.Build.0 = Release|Any CPU {46372C50-4288-4B8E-AF21-C934560600E0}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -74,11 +74,13 @@ Global {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|Any CPU.ActiveCfg = Debug|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|Any CPU.Build.0 = Debug|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|x64.ActiveCfg = Debug|x64 + {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|x64.Build.0 = Debug|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|x86.ActiveCfg = Debug|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Debug|x86.Build.0 = Debug|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|Any CPU.ActiveCfg = Release|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|Any CPU.Build.0 = Release|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|x64.ActiveCfg = Release|x64 + {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|x64.Build.0 = Release|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|x86.ActiveCfg = Release|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.Release|x86.Build.0 = Release|x64 {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 @@ -88,14 +90,14 @@ Global {D8B26B12-E45C-47EA-88F7-56628EB2CCD1}.RelWithDebInfo|x86.Build.0 = Release|x64 {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x64.ActiveCfg = Debug|Any CPU - {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x64.Build.0 = Debug|Any CPU + {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x64.ActiveCfg = Debug|x64 + {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x64.Build.0 = Debug|x64 {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x86.ActiveCfg = Debug|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Debug|x86.Build.0 = Debug|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|Any CPU.Build.0 = Release|Any CPU - {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x64.ActiveCfg = Release|Any CPU - {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x64.Build.0 = Release|Any CPU + {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x64.ActiveCfg = Release|x64 + {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x64.Build.0 = Release|x64 {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x86.ActiveCfg = Release|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.Release|x86.Build.0 = Release|Any CPU {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -106,14 +108,14 @@ Global {FAD67294-6223-47E0-8838-E4E7FBC53ED2}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x64.ActiveCfg = Debug|Any CPU - {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x64.Build.0 = Debug|Any CPU + {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x64.ActiveCfg = Debug|x64 + {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x64.Build.0 = Debug|x64 {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x86.ActiveCfg = Debug|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Debug|x86.Build.0 = Debug|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|Any CPU.Build.0 = Release|Any CPU - {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x64.ActiveCfg = Release|Any CPU - {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x64.Build.0 = Release|Any CPU + {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x64.ActiveCfg = Release|x64 + {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x64.Build.0 = Release|x64 {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x86.ActiveCfg = Release|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.Release|x86.Build.0 = Release|Any CPU {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -124,14 +126,14 @@ Global {CBFEE38F-5F12-4D6F-B4FB-267FB68A6BEA}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x64.ActiveCfg = Debug|Any CPU - {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x64.Build.0 = Debug|Any CPU + {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x64.ActiveCfg = Debug|x64 + {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x64.Build.0 = Debug|x64 {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x86.ActiveCfg = Debug|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Debug|x86.Build.0 = Debug|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|Any CPU.ActiveCfg = Release|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|Any CPU.Build.0 = Release|Any CPU - {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x64.ActiveCfg = Release|Any CPU - {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x64.Build.0 = Release|Any CPU + {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x64.ActiveCfg = Release|x64 + {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x64.Build.0 = Release|x64 {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x86.ActiveCfg = Release|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.Release|x86.Build.0 = Release|Any CPU {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -142,14 +144,14 @@ Global {EF82C289-53D6-41C8-B5C3-72B37655C7F3}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x64.ActiveCfg = Debug|Any CPU - {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x64.Build.0 = Debug|Any CPU + {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x64.ActiveCfg = Debug|x64 + {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x64.Build.0 = Debug|x64 {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x86.ActiveCfg = Debug|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Debug|x86.Build.0 = Debug|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|Any CPU.Build.0 = Release|Any CPU - {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x64.ActiveCfg = Release|Any CPU - {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x64.Build.0 = Release|Any CPU + {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x64.ActiveCfg = Release|x64 + {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x64.Build.0 = Release|x64 {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x86.ActiveCfg = Release|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.Release|x86.Build.0 = Release|Any CPU {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -160,14 +162,14 @@ Global {32F08B9A-F50B-4C2E-AB56-533FED066DDE}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x64.ActiveCfg = Debug|Any CPU - {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x64.Build.0 = Debug|Any CPU + {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x64.ActiveCfg = Debug|x64 + {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x64.Build.0 = Debug|x64 {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x86.ActiveCfg = Debug|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Debug|x86.Build.0 = Debug|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|Any CPU.ActiveCfg = Release|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|Any CPU.Build.0 = Release|Any CPU - {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x64.ActiveCfg = Release|Any CPU - {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x64.Build.0 = Release|Any CPU + {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x64.ActiveCfg = Release|x64 + {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x64.Build.0 = Release|x64 {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x86.ActiveCfg = Release|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.Release|x86.Build.0 = Release|Any CPU {31E71543-CBCF-43BB-AF77-D210D548118E}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -178,14 +180,14 @@ Global {31E71543-CBCF-43BB-AF77-D210D548118E}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x64.ActiveCfg = Debug|Any CPU - {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x64.Build.0 = Debug|Any CPU + {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x64.ActiveCfg = Debug|x64 + {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x64.Build.0 = Debug|x64 {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x86.ActiveCfg = Debug|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Debug|x86.Build.0 = Debug|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x64.ActiveCfg = Release|Any CPU - {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x64.Build.0 = Release|Any CPU + {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x64.ActiveCfg = Release|x64 + {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x64.Build.0 = Release|x64 {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x86.ActiveCfg = Release|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.Release|x86.Build.0 = Release|Any CPU {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -196,14 +198,14 @@ Global {E4B4F95E-F027-44D7-AB93-B96EF2E661B6}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x64.ActiveCfg = Debug|Any CPU - {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x64.Build.0 = Debug|Any CPU + {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x64.ActiveCfg = Debug|x64 + {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x64.Build.0 = Debug|x64 {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x86.ActiveCfg = Debug|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Debug|x86.Build.0 = Debug|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|Any CPU.Build.0 = Release|Any CPU - {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x64.ActiveCfg = Release|Any CPU - {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x64.Build.0 = Release|Any CPU + {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x64.ActiveCfg = Release|x64 + {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x64.Build.0 = Release|x64 {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x86.ActiveCfg = Release|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.Release|x86.Build.0 = Release|Any CPU {94D900D1-EC77-4170-8942-56E3736E44DE}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -214,14 +216,14 @@ Global {94D900D1-EC77-4170-8942-56E3736E44DE}.RelWithDebInfo|x86.Build.0 = Release|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x64.ActiveCfg = Debug|Any CPU - {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x64.Build.0 = Debug|Any CPU + {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x64.ActiveCfg = Debug|x64 + {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x64.Build.0 = Debug|x64 {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x86.ActiveCfg = Debug|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Debug|x86.Build.0 = Debug|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|Any CPU.ActiveCfg = Release|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|Any CPU.Build.0 = Release|Any CPU - {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x64.ActiveCfg = Release|Any CPU - {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x64.Build.0 = Release|Any CPU + {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x64.ActiveCfg = Release|x64 + {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x64.Build.0 = Release|x64 {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x86.ActiveCfg = Release|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.Release|x86.Build.0 = Release|Any CPU {67E646C2-3C3C-4327-A0B4-40C1DB32579F}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU @@ -250,14 +252,14 @@ Global {043514DF-5822-41A0-A5CE-CBC349B1398B}.RelWithDebInfo|x86.Build.0 = Release|Win32 {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x64.ActiveCfg = Debug|Any CPU - {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x64.Build.0 = Debug|Any CPU + {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x64.ActiveCfg = Debug|x64 + {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x64.Build.0 = Debug|x64 {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x86.ActiveCfg = Debug|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Debug|x86.Build.0 = Debug|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|Any CPU.ActiveCfg = Release|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|Any CPU.Build.0 = Release|Any CPU - {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x64.ActiveCfg = Release|Any CPU - {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x64.Build.0 = Release|Any CPU + {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x64.ActiveCfg = Release|x64 + {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x64.Build.0 = Release|x64 {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x86.ActiveCfg = Release|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.Release|x86.Build.0 = Release|Any CPU {A721CE1D-F76D-476B-86E7-C8B2D85D7E73}.RelWithDebInfo|Any CPU.ActiveCfg = Release|Any CPU diff --git a/PhysicsTool/PhysicsTool.cpp b/PhysicsTool/PhysicsTool.cpp index 82024888..ccdf283e 100644 --- a/PhysicsTool/PhysicsTool.cpp +++ b/PhysicsTool/PhysicsTool.cpp @@ -8,10 +8,15 @@ class PhysXExporterAllocator : public PxAllocatorCallback void* allocate(size_t size, const char*, const char*, int) { void* ptr = _aligned_malloc(size, 16); - memset(ptr, 0, size); + if (ptr != nullptr) + { + memset(ptr, 0, size); + } + return ptr; } + void deallocate(void* ptr) { _aligned_free(ptr); @@ -64,15 +69,37 @@ void PhysXConverter::ShutdownPhysX() PxCollection* PhysXConverter::LoadCollectionFromBinary(std::span const& bin) { auto binSize = bin.size(); - auto binInput = new uint8_t[binSize + PX_SERIAL_FILE_ALIGN]; - // TODO - release memory block after use + uint8_t* binInput = new uint8_t[binSize + PX_SERIAL_FILE_ALIGN]; void* memory128 = (void*)((uintptr_t(binInput) + PX_SERIAL_FILE_ALIGN) & ~(PX_SERIAL_FILE_ALIGN - 1)); memcpy(memory128, bin.data(), binSize); + PxCollection* collection = PxSerialization::createCollectionFromBinary(memory128, *registry_); + if (collection) { + binaryBuffers_[collection] = binInput; + } + else { + delete[] binInput; + } + return collection; +} - return PxSerialization::createCollectionFromBinary(memory128, *registry_); +void PhysXConverter::ReleaseCollection(PxCollection* collection) { + if (!collection) return; + auto it = binaryBuffers_.find(collection); + if (it != binaryBuffers_.end()) { + uint8_t* rawBuffer = it->second; + collection->release(); + delete[] rawBuffer; // Memory leak fixed + binaryBuffers_.erase(it); + } + else { + collection->release(); + } } + + + class KazMemoryOutputStream : public PxOutputStream { public: diff --git a/PhysicsTool/PhysicsTool.h b/PhysicsTool/PhysicsTool.h index eb49874e..d1bfaff9 100644 --- a/PhysicsTool/PhysicsTool.h +++ b/PhysicsTool/PhysicsTool.h @@ -6,6 +6,7 @@ #include #include #include +#include #define WIN32_LEAN_AND_MEAN #include @@ -24,6 +25,7 @@ class PhysXConverter PxCollection* LoadCollectionFromXml(std::span const& xml); PxCollection* LoadCollectionFromBinary(std::span const& bin); + void ReleaseCollection(physx::PxCollection* collection); std::vector SaveCollectionToXml(PxCollection& collection); std::vector SaveCollectionToBinary(PxCollection& collection); @@ -33,5 +35,6 @@ class PhysXConverter PxPhysics* physics_{ nullptr }; PxCooking* cooking_{ nullptr }; PxSerializationRegistry* registry_{ nullptr }; + std::unordered_map binaryBuffers_; }; diff --git a/PhysicsTool/PhysicsTool.vcxproj b/PhysicsTool/PhysicsTool.vcxproj index 1e509062..38bde3b1 100644 --- a/PhysicsTool/PhysicsTool.vcxproj +++ b/PhysicsTool/PhysicsTool.vcxproj @@ -107,6 +107,10 @@ true MultiThreadedDebugDLL stdcpp20 + TurnOffAllWarnings + false + + Console diff --git a/PhysicsTool/PxEncoder.cpp b/PhysicsTool/PxEncoder.cpp index 067e81db..767bd1ae 100644 --- a/PhysicsTool/PxEncoder.cpp +++ b/PhysicsTool/PxEncoder.cpp @@ -215,7 +215,6 @@ class PhysXExporter { ExportProperty(ele, "Type", "ConvexMesh"); ExportProperty(ele, "Scale", o.scale); - // s << "\t" "MeshFlags: " << (uint32_t)o.meshFlags << std::endl; - Always 0 Export(ele, *o.convexMesh); } @@ -224,7 +223,6 @@ class PhysXExporter { ExportProperty(ele, "Type", "TriangleMesh"); ExportProperty(ele, "Scale", o.scale); - // s << "\t" "MeshFlags: " << (uint32_t)o.meshFlags << std::endl; - Always 0 Export(ele, *o.triangleMesh); } @@ -290,18 +288,6 @@ class PhysXExporter Export(shapesEle, *shape); } } - - // These should be handled by the joint, not the rigidbody - /* - if (o.getNbConstraints() > 0) { - s << "\t" "Constraints: "; - PxConstraint* constraints[128]; - o.getConstraints(constraints, o.getNbConstraints(), 0); - for (uint32_t i = 0; i < o.getNbConstraints(); i++) { - s << constraints[i]->getConcreteTypeName() << " "; - } - s << std::endl; - }*/ } void Export(TiXmlNode& parent, PxRigidStatic& o) @@ -316,9 +302,7 @@ class PhysXExporter PR(CMassLocalPose, o.getCMassLocalPose(), PxTransform()); PR(Mass, o.getMass(), 1.0f); - // PR(InvMass, o.getInvMass(), 1.0f); - Calculated by px PR(MassSpaceInertiaTensor, o.getMassSpaceInertiaTensor(), PxVec3(1.0f, 1.0f, 1.0f)); - // PR(MassSpaceInvInertiaTensor, o.getMassSpaceInvInertiaTensor(), PxVec3(1.0f, 1.0f, 1.0f)); - Calculated by px PR(LinearDamping, o.getLinearDamping(), 0.0f); PR(AngularDamping, o.getAngularDamping(), 0.05f); P_BOUNDED(MaxLinearVelocity, o.getMaxLinearVelocity(), 1e+15f); // 1e+16f @@ -526,23 +510,22 @@ class PhysXExporter auto& polyEle = *ele.InsertEndChild(TiXmlElement("Polygon")); for (PxU32 v = 0; v < poly.mNbVerts; v++) { - auto vert = verts[inds[poly.mIndexBase + v]]; + const auto& vert = verts[inds[poly.mIndexBase + v]]; ExportProperty(polyEle, "Vertex", vert); } } } - void Export(TiXmlNode& parent, PxTriangleMesh& o) - { + void Export(TiXmlNode& parent, PxTriangleMesh& o) { auto& ele = *parent.InsertEndChild(TiXmlElement("TriangleMesh")); - auto verts = o.getVertices(); - auto inds = (PxU16*)o.getTriangles(); auto tris = o.getNbTriangles(); + auto has16BitIndices = o.getTriangleMeshFlags() & PxTriangleMeshFlag::e16_BIT_INDICES; + void* inds = (void*)o.getTriangles(); - for (PxU32 i = 0; i < tris*3; i++) { - auto vert = verts[inds[i]]; - ExportProperty(ele, "Vertex", vert); + for (PxU32 i = 0; i < tris * 3; i++) { + PxU32 index = has16BitIndices ? ((PxU16*)inds)[i] : ((PxU32*)inds)[i]; + ExportProperty(ele, "Vertex", verts[index]); } } @@ -555,7 +538,6 @@ class PhysXExporter case PxTypeInfo::eFastTypeId: Export(parent, static_cast(obj)); break; case PxTypeInfo::eFastTypeId: Export(parent, static_cast(obj)); break; - // These are child nodes of other types and will be exported alongside them case PxTypeInfo::eFastTypeId: case PxTypeInfo::eFastTypeId: case PxTypeInfo::eFastTypeId: diff --git a/PhysicsTool/PxLoader.cpp b/PhysicsTool/PxLoader.cpp index 2aba4d41..60352f51 100644 --- a/PhysicsTool/PxLoader.cpp +++ b/PhysicsTool/PxLoader.cpp @@ -1,641 +1,194 @@ #include "PhysicsTool.h" #include +#include #define PR(name, type, def) LoadProperty(ele, #name, def) #define P(name, type) LoadProperty(ele, #name) -#define PFLAG(name) LoadProperty(ele, #name, false) -#define P_BOUNDED(name, expr, bound) LoadBoundedProperty(ele, #name, bound) - #define SET_PR(name, type, def) o->set##name(LoadProperty(ele, #name, def)) #define SET_P(name, type) o->set##name(LoadProperty(ele, #name)) #define SET_FLAG(name, prop, enumlbl) o->set##prop(enumlbl, LoadProperty(ele, #name, false)) #define SET_PB(name, bound) o->set##name(LoadBoundedProperty(ele, #name, bound)) -class PhysXLoader -{ -public: - PxPhysics* physics_; - PxCooking* cooking_; +using namespace physx; +class PhysXLoader { +public: + PxPhysics* physics_{ nullptr }; + PxCooking* cooking_{ nullptr }; PxCollection* collection_{ nullptr }; - std::unordered_map materials_; std::unordered_map actors_; - PxCollection* Load(TiXmlElement& doc) - { - collection_ = PxCreateCollection(); - - for (auto child = doc.FirstChildElement(); child; child = child->NextSiblingElement()) { - LoadTopLevel(*child); - } - - return collection_; - } - - template - T LoadProperty(TiXmlElement& ele, char const* name, T defaultVal); - - PxReal LoadBoundedProperty(TiXmlElement& ele, char const* name, PxReal bound) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return bound; - if (strcmp(attr->GetText(), "Unbounded") == 0) return bound; - return std::stof(attr->GetText()); - } - - template <> - PxReal LoadProperty(TiXmlElement& ele, char const* name, PxReal defaultVal) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return defaultVal; - return std::stof(attr->GetText()); - } - - template <> - PxU32 LoadProperty(TiXmlElement& ele, char const* name, PxU32 defaultVal) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return defaultVal; - return (PxU32)std::stoi(attr->GetText()); - } - - template <> - bool LoadProperty(TiXmlElement& ele, char const* name, bool defaultVal) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return defaultVal; - return _stricmp(attr->GetText(), "true") == 0; - } - - template <> - std::string LoadProperty(TiXmlElement& ele, char const* name, std::string defaultVal) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return defaultVal; - return attr->GetText(); - } - - template - T LoadProperty(TiXmlElement& ele, char const* name); - - template <> - std::string LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) throw std::runtime_error(std::string("Missing property: ") + name); - return attr->GetText(); - } - - template <> - PxD6Motion::Enum LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) throw std::runtime_error(std::string("Missing property: ") + name); - - if (strcmp(attr->GetText(), "Locked") == 0) return PxD6Motion::eLOCKED; - if (strcmp(attr->GetText(), "Limited") == 0) return PxD6Motion::eLIMITED; - return PxD6Motion::eFREE; - } - - template <> - PxTransform LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxTransform tr; - if (attr == nullptr) return tr; - - tr.p = LoadProperty(*attr, "Position"); - tr.q = LoadProperty(*attr, "Rotation"); - return tr; - } - - template <> - PxMeshScale LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxMeshScale tr; - if (attr == nullptr) return tr; - - tr.scale = LoadProperty(*attr, "Scale"); - tr.rotation = LoadProperty(*attr, "Rotation"); - return tr; - } - - template <> - PxVec3 LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxVec3 v; - if (attr == nullptr) return v; - - v.x = LoadProperty(*attr, "X", 0.0f); - v.y = LoadProperty(*attr, "Y", 0.0f); - v.z = LoadProperty(*attr, "Z", 0.0f); - return v; - } - - template <> - PxVec3 LoadProperty(TiXmlElement& ele, char const* name, PxVec3 def) - { - auto attr = ele.FirstChildElement(name); - if (attr == nullptr) return def; - - PxVec3 v; - v.x = LoadProperty(*attr, "X", 0.0f); - v.y = LoadProperty(*attr, "Y", 0.0f); - v.z = LoadProperty(*attr, "Z", 0.0f); - return v; - } - - template <> - PxQuat LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxQuat v; - if (attr == nullptr) return v; - - v.x = LoadProperty(*attr, "X", 0.0f); - v.y = LoadProperty(*attr, "Y", 0.0f); - v.z = LoadProperty(*attr, "Z", 0.0f); - v.w = LoadProperty(*attr, "W", 0.0f); - return v; - } - - template <> - PxJointLinearLimit LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxJointLinearLimit v(PxTolerancesScale(), PX_MAX_F32); - if (attr == nullptr) return v; - - v.value = LoadBoundedProperty(*attr, "Value", PX_MAX_F32); - v.restitution = LoadProperty(*attr, "Restitution", 0.0f); - v.bounceThreshold = LoadProperty(*attr, "BounceThreshold", 0.0f); - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - v.contactDistance = LoadProperty(*attr, "ContactDistance", 0.0f); - return v; - } - - template <> - PxJointLinearLimitPair LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxJointLinearLimitPair v(PxTolerancesScale(), -PX_MAX_F32/3, PX_MAX_F32/3); - if (attr == nullptr) return v; - - v.lower = LoadProperty(*attr, "Lower", -PX_MAX_F32/3); - v.upper = LoadProperty(*attr, "Upper", PX_MAX_F32/3); - v.restitution = LoadProperty(*attr, "Restitution", 0.0f); - v.bounceThreshold = LoadProperty(*attr, "BounceThreshold", 0.0f); - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - v.contactDistance = LoadProperty(*attr, "ContactDistance", 0.0f); - return v; - } - - template <> - PxJointAngularLimitPair LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxJointAngularLimitPair v(-PxPi / 2, PxPi / 2); - if (attr == nullptr) return v; - - v.lower = LoadProperty(*attr, "Lower", -PxPi / 2); - v.upper = LoadProperty(*attr, "Upper", PxPi / 2); - v.restitution = LoadProperty(*attr, "Restitution", 0.0f); - v.bounceThreshold = LoadProperty(*attr, "BounceThreshold", 0.0f); - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - v.contactDistance = LoadProperty(*attr, "ContactDistance", 0.0f); - return v; - } - - template <> - PxJointLimitCone LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxJointLimitCone v(PxPi / 2, PxPi / 2); - if (attr == nullptr) return v; - - v.yAngle = LoadProperty(*attr, "YAngle", PxPi / 2); - v.zAngle = LoadProperty(*attr, "ZAngle", PxPi / 2); - v.restitution = LoadProperty(*attr, "Restitution", 0.0f); - v.bounceThreshold = LoadProperty(*attr, "BounceThreshold", 0.0f); - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - v.contactDistance = LoadProperty(*attr, "ContactDistance", 0.0f); - return v; - } - - template <> - PxJointLimitPyramid LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxJointLimitPyramid v(-PxPi / 2, PxPi / 2, -PxPi / 2, PxPi / 2); - if (attr == nullptr) return v; - - v.yAngleMin = LoadProperty(*attr, "YAngleMin", -PxPi / 2); - v.yAngleMax = LoadProperty(*attr, "YAngleMax", PxPi / 2); - v.zAngleMin = LoadProperty(*attr, "ZAngleMin", -PxPi / 2); - v.zAngleMax = LoadProperty(*attr, "ZAngleMax", PxPi / 2); - v.restitution = LoadProperty(*attr, "Restitution", 0.0f); - v.bounceThreshold = LoadProperty(*attr, "BounceThreshold", 0.0f); - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - v.contactDistance = LoadProperty(*attr, "ContactDistance", 0.0f); - return v; - } - - template <> - PxD6JointDrive LoadProperty(TiXmlElement& ele, char const* name) - { - auto attr = ele.FirstChildElement(name); - PxD6JointDrive v; - if (attr == nullptr) return v; - - v.forceLimit = LoadBoundedProperty(*attr, "ForceLimit", PX_MAX_F32); - v.flags = LoadProperty(*attr, "IsAcceleration", false) ? PxD6JointDriveFlag::eACCELERATION : (PxD6JointDriveFlag::Enum)0; - v.stiffness = LoadProperty(*attr, "Stiffness", 0.0f); - v.damping = LoadProperty(*attr, "Damping", 0.0f); - return v; - } - - PxBase* LoadMaterial(TiXmlElement& ele) - { - auto index = PR(Index, PxU32, 0); - if (materials_.find(index) != materials_.end()) throw std::runtime_error("Duplicate material index"); - - auto mat = physics_->createMaterial( - PR(StaticFriction, PxReal, 1.0f), - PR(DynamicFriction, PxReal, 1.0f), - PR(Restitution, PxReal, 0.0f) - ); - - collection_->add(*mat); - materials_.insert(std::make_pair(index, mat)); - return mat; - } - - void LoadRigidActor(TiXmlElement& ele, PxRigidActor* o) - { - o->setName(_strdup(PR(Name, std::string, "").c_str())); - o->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, PFLAG(DisableGravity)); - o->setDominanceGroup((PxDominanceGroup)PR(DominanceGroup, PxU32, 0)); - - auto shapes = ele.FirstChildElement("Shapes"); - if (shapes) { - for (auto shapeEle = shapes->FirstChildElement("Shape"); shapeEle; shapeEle = shapeEle->NextSiblingElement("Shape")) { - o->attachShape(*LoadShape(*shapeEle)); - } - } - - actors_.insert(std::make_pair(o->getName(), o)); - } - - PxBase* LoadRigidStatic(TiXmlElement& ele) - { - auto o = physics_->createRigidStatic( - P(GlobalPose, PxTransform) - ); - - LoadRigidActor(ele, o); - - collection_->add(*o); - return o; - } - - void LoadRigidBody(TiXmlElement& ele, PxRigidBody* o) - { - LoadRigidActor(ele, o); - - SET_P(CMassLocalPose, PxTransform); - SET_PR(Mass, PxReal, 1.0f); - SET_PR(MassSpaceInertiaTensor, PxVec3, PxVec3(1.0f, 1.0f, 1.0f)); - SET_PR(LinearDamping, PxReal, 0.0f); - SET_PR(AngularDamping, PxReal, 0.5f); - SET_PB(MaxLinearVelocity, 1e+16f); - SET_PR(MaxAngularVelocity, PxReal, 100.0f); - - SET_FLAG(Kinematic, RigidBodyFlag, PxRigidBodyFlag::eKINEMATIC); - SET_FLAG(EnableCCD, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD); - SET_FLAG(EnableCCDFriction, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD_FRICTION); - SET_FLAG(EnableSpeculativeCCD, RigidBodyFlag, PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD); - SET_FLAG(EnableCCDMaxContactImpulse, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE); - SET_FLAG(RetainAccelerations, RigidBodyFlag, PxRigidBodyFlag::eRETAIN_ACCELERATIONS); - - SET_PR(MinCCDAdvanceCoefficient, PxReal, 0.15f); - SET_PB(MaxDepenetrationVelocity, 1e+32f); - SET_PB(MaxContactImpulse, 1e+32f); - } - - PxBase* LoadRigidDynamic(TiXmlElement& ele) - { - auto o = physics_->createRigidDynamic( - P(GlobalPose, PxTransform) - ); - - auto minPositionIters = PR(MinPositionIters, PxU32, 4); - auto minVelocityIters = PR(MinVelocityIters, PxU32, 1); - o->setSolverIterationCounts(minPositionIters, minVelocityIters); - - SET_PR(SleepThreshold, PxReal, 0.005f); - SET_PR(StabilizationThreshold, PxReal, 0.0025f); - - SET_FLAG(LockLinearX, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_LINEAR_X); - SET_FLAG(LockLinearY, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_LINEAR_Y); - SET_FLAG(LockLinearZ, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_LINEAR_Z); - SET_FLAG(LockAngularX, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_ANGULAR_X); - SET_FLAG(LockAngularY, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_ANGULAR_Y); - SET_FLAG(LockAngularZ, RigidDynamicLockFlag, PxRigidDynamicLockFlag::eLOCK_ANGULAR_Z); - - SET_PR(WakeCounter, PxReal, 0.0f); - SET_PB(ContactReportThreshold, PX_MAX_F32); - - LoadRigidBody(ele, o); - - collection_->add(*o); - return o; - } - - PxShape* LoadShape(TiXmlElement& ele) - { - auto matIndex = PR(MaterialIndex, PxU32, 0); - auto mat = materials_.find(matIndex); - if (mat == materials_.end()) throw std::runtime_error("Shape references unknown material index"); + PxCollection* Load(TiXmlElement& doc); + void LoadTopLevel(TiXmlElement& ele); + PxBase* LoadMaterial(TiXmlElement& ele); + PxBase* LoadRigidStatic(TiXmlElement& ele); + PxBase* LoadRigidDynamic(TiXmlElement& ele); + void LoadRigidActor(TiXmlElement& ele, PxRigidActor* o); + void LoadRigidBody(TiXmlElement& ele, PxRigidBody* o); + PxShape* LoadShape(TiXmlElement& ele); + PxGeometry* LoadGeometry(TiXmlElement& ele); + PxReal LoadBoundedProperty(TiXmlElement& ele, char const* name, PxReal bound); + + template T LoadProperty(TiXmlElement& ele, char const* name, T defaultVal); + template T LoadProperty(TiXmlElement& ele, char const* name); +}; - auto geomEle = ele.FirstChildElement("Geometry"); - if (!geomEle) throw std::runtime_error("Shape has no geometry"); +template <> PxReal PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name, PxReal def) { + auto attr = ele.FirstChildElement(name); + return attr ? std::stof(attr->GetText()) : def; +} - auto geom = LoadGeometry(*geomEle); +template <> +std::string PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name, std::string defaultVal) { + auto attr = ele.FirstChildElement(name); + if (!attr || !attr->GetText()) return defaultVal; + return std::string(attr->GetText()); +} - auto o = physics_->createShape(*geom, *mat->second, true); - o->setName(_strdup(PR(Name, std::string, "").c_str())); - o->setLocalPose(P(LocalPose, PxTransform)); - o->setContactOffset(PR(ContactOffset, PxReal, 0.02f)); - o->setRestOffset(PR(RestOffset, PxReal, 0.0f)); - o->setTorsionalPatchRadius(PR(TorsionalPatchRadius, PxReal, 0.0f)); - o->setMinTorsionalPatchRadius(PR(MinTorsionalPatchRadius, PxReal, 0.0f)); +template <> +unsigned int PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name, unsigned int defaultVal) { + auto attr = ele.FirstChildElement(name); + if (!attr || !attr->GetText()) return defaultVal; + return (unsigned int)std::stoul(attr->GetText()); +} - collection_->add(*o); - return o; - } +template +T PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name, T defaultVal) { + return defaultVal; +} - PxGeometry* LoadSphere(TiXmlElement& ele) - { - return new PxSphereGeometry( - PR(Radius, PxReal, 1.0f) - ); - } +template <> PxQuat PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name) { + auto attr = ele.FirstChildElement(name); + if (!attr) return PxQuat(PxIdentity); + return PxQuat(LoadProperty(*attr, "X", 0.0f), LoadProperty(*attr, "Y", 0.0f), + LoadProperty(*attr, "Z", 0.0f), LoadProperty(*attr, "W", 1.0f)); +} - PxGeometry* LoadCapsule(TiXmlElement& ele) - { - return new PxCapsuleGeometry( - PR(Radius, PxReal, 1.0f), - PR(HalfHeight, PxReal, 1.0f) - ); - } +template <> PxVec3 PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name) { + auto attr = ele.FirstChildElement(name); + if (!attr) return PxVec3(0); + return PxVec3(LoadProperty(*attr, "X", 0.0f), LoadProperty(*attr, "Y", 0.0f), LoadProperty(*attr, "Z", 0.0f)); +} - PxGeometry* LoadBox(TiXmlElement& ele) - { - return new PxBoxGeometry( - P(HalfExtents, PxVec3) - ); - } +template <> PxTransform PhysXLoader::LoadProperty(TiXmlElement& ele, char const* name) { + auto attr = ele.FirstChildElement(name); + if (!attr) return PxTransform(PxIdentity); + return PxTransform(LoadProperty(*attr, "Position"), LoadProperty(*attr, "Rotation")); +} - PxGeometry* LoadConvexMeshGeometry(TiXmlElement& ele) - { - auto meshEle = ele.FirstChildElement("ConvexMesh"); - if (!meshEle) throw std::runtime_error("Geometry has no ConvexMesh"); - auto mesh = LoadConvexMesh(ele); +PxCollection* PhysXLoader::Load(TiXmlElement& doc) { + collection_ = PxCreateCollection(); + for (auto child = doc.FirstChildElement(); child; child = child->NextSiblingElement()) LoadTopLevel(*child); + return collection_; +} - return new PxConvexMeshGeometry( - mesh, - P(Scale, PxMeshScale) - ); - } +void PhysXLoader::LoadTopLevel(TiXmlElement& ele) { + std::string type = ele.Value(); + if (type == "Material") LoadMaterial(ele); + else if (type == "RigidStatic") LoadRigidStatic(ele); + else if (type == "RigidDynamic") LoadRigidDynamic(ele); +} - PxGeometry* LoadTriangleMeshGeometry(TiXmlElement& ele) - { - auto meshEle = ele.FirstChildElement("TriangleMesh"); - if (!meshEle) throw std::runtime_error("Geometry has no TriangleMesh"); - auto mesh = LoadTriangleMesh(ele); +PxBase* PhysXLoader::LoadMaterial(TiXmlElement& ele) { + auto mat = physics_->createMaterial(PR(StaticFriction, PxReal, 1.0f), PR(DynamicFriction, PxReal, 1.0f), PR(Restitution, PxReal, 0.0f)); + materials_[PR(Index, PxU32, 0)] = mat; + collection_->add(*mat); + return mat; +} - return new PxTriangleMeshGeometry( - mesh, - P(Scale, PxMeshScale) - ); - } +PxBase* PhysXLoader::LoadRigidStatic(TiXmlElement& ele) { + auto o = physics_->createRigidStatic(P(GlobalPose, PxTransform)); + LoadRigidActor(ele, o); + collection_->add(*o); + return o; +} - PxConvexMesh* LoadConvexMesh(TiXmlElement& ele) - { - throw new std::runtime_error("LoadConvexMesh: Dont know how to do this yet"); - } +PxBase* PhysXLoader::LoadRigidDynamic(TiXmlElement& ele) { + auto o = physics_->createRigidDynamic(P(GlobalPose, PxTransform)); + LoadRigidBody(ele, o); + collection_->add(*o); + return o; +} - PxTriangleMesh* LoadTriangleMesh(TiXmlElement& ele) - { - throw new std::runtime_error("LoadTriangleMesh: Dont know how to do this yet"); - } +void PhysXLoader::LoadRigidActor(TiXmlElement& ele, PxRigidActor* o) { + o->setName(_strdup(PR(Name, std::string, "").c_str())); - PxGeometry* LoadGeometry(TiXmlElement& ele) - { - auto type = PR(Type, std::string, ""); - - if (type == "Sphere") { - return LoadSphere(ele); - } else if (type == "Capsule") { - return LoadCapsule(ele); - } else if (type == "Box") { - return LoadBox(ele); - } else if (type == "ConvexMesh") { - return LoadConvexMeshGeometry(ele); - } else if (type == "TriangleMesh") { - return LoadTriangleMeshGeometry(ele); - } else { - throw std::runtime_error("Unknown geometry type"); + auto shapesEle = ele.FirstChildElement("Shapes"); + if (shapesEle) { + for (auto sEle = shapesEle->FirstChildElement("Shape"); sEle; sEle = sEle->NextSiblingElement("Shape")) { + PxShape* shape = LoadShape(*sEle); + if (shape) o->attachShape(*shape); } } - void LoadJoint(PxJoint* o, TiXmlElement& ele) - { - o->setName(_strdup(PR(Name, std::string, "").c_str())); - - auto force = P_BOUNDED(BreakForce, PxReal, PX_MAX_F32); - auto torque = P_BOUNDED(BreakTorque, PxReal, PX_MAX_F32); - o->setBreakForce(force, torque); - - SET_FLAG(ProjectToActor0, ConstraintFlag, PxConstraintFlag::ePROJECT_TO_ACTOR0); - SET_FLAG(ProjectToActor1, ConstraintFlag, PxConstraintFlag::ePROJECT_TO_ACTOR1); - SET_FLAG(CollisionEnabled, ConstraintFlag, PxConstraintFlag::eCOLLISION_ENABLED); - SET_FLAG(DriveLimitsAreForces, ConstraintFlag, PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); - - SET_PR(InvMassScale0, PxReal, 1.0f); - SET_PR(InvInertiaScale0, PxReal, 1.0f); - SET_PR(InvMassScale1, PxReal, 1.0f); - SET_PR(InvInertiaScale1, PxReal, 1.0f); - } - - PxBase* LoadD6Joint(TiXmlElement& ele) - { - auto actor0Name = P(Actor0, std::string); - auto actor1Name = P(Actor1, std::string); - - auto actor0It = actors_.find(actor0Name); - if (actor0It == actors_.end()) throw std::runtime_error("Actor0 has invalid name"); - - auto actor1It = actors_.find(actor1Name); - if (actor1It == actors_.end()) throw std::runtime_error("Actor1 has invalid name"); - - auto pose0 = P(Actor0LocalPose, PxTransform); - auto pose1 = P(Actor1LocalPose, PxTransform); - - auto o = PxD6JointCreate(*physics_, actor0It->second, pose0, actor1It->second, pose1); - LoadJoint(o, ele); - - o->setMotion(PxD6Axis::eX, P(MotionX, PxD6Motion::Enum)); - o->setMotion(PxD6Axis::eY, P(MotionY, PxD6Motion::Enum)); - o->setMotion(PxD6Axis::eZ, P(MotionZ, PxD6Motion::Enum)); - o->setMotion(PxD6Axis::eTWIST, P(MotionTwist, PxD6Motion::Enum)); - o->setMotion(PxD6Axis::eSWING1, P(MotionSwing1, PxD6Motion::Enum)); - o->setMotion(PxD6Axis::eSWING2, P(MotionSwing2, PxD6Motion::Enum)); - - SET_P(DistanceLimit, PxJointLinearLimit); - o->setLinearLimit(PxD6Axis::eX, P(LinearLimitX, PxJointLinearLimitPair)); - o->setLinearLimit(PxD6Axis::eY, P(LinearLimitY, PxJointLinearLimitPair)); - o->setLinearLimit(PxD6Axis::eZ, P(LinearLimitZ, PxJointLinearLimitPair)); - SET_P(TwistLimit, PxJointAngularLimitPair); - SET_P(SwingLimit, PxJointLimitCone); - SET_P(PyramidSwingLimit, PxJointLimitPyramid); - - o->setDrive(PxD6Drive::eX, P(DriveX, PxD6JointDrive)); - o->setDrive(PxD6Drive::eY, P(DriveY, PxD6JointDrive)); - o->setDrive(PxD6Drive::eZ, P(DriveZ, PxD6JointDrive)); - o->setDrive(PxD6Drive::eSWING, P(DriveSwing, PxD6JointDrive)); - o->setDrive(PxD6Drive::eTWIST, P(DriveTwist, PxD6JointDrive)); - o->setDrive(PxD6Drive::eSLERP, P(DriveSlerp, PxD6JointDrive)); - - SET_PR(ProjectionLinearTolerance, PxReal, 1e+10f); - SET_PR(ProjectionAngularTolerance, PxReal, 3.14159f); - - collection_->add(*o->getConstraint()); - collection_->add(*o); - return o; - } - - PxBase* LoadArticulationJoint(TiXmlElement& ele, PxArticulationJoint* o) - { - SET_P(ParentPose, PxTransform); - SET_P(ChildPose, PxTransform); - - SET_PR(Stiffness, PxReal, 0.0f); - SET_PR(Damping, PxReal, 0.0f); - SET_PR(InternalCompliance, PxReal, 0.0f); - SET_PR(ExternalCompliance, PxReal, 0.0f); - - o->setSwingLimit(PR(SwingLimitZ, PxReal, PxPi / 4), PR(SwingLimitY, PxReal, PxPi / 4)); - - SET_PR(TangentialStiffness, PxReal, 0.0f); - SET_PR(TangentialDamping, PxReal, 0.0f); - SET_PR(SwingLimitContactDistance, PxReal, 0.05f); - SET_PR(SwingLimitEnabled, bool, false); - - o->setTwistLimit(PR(TwistLimitLower, PxReal, -PxPi / 4), PR(TwistLimitUpper, PxReal, PxPi / 4)); - - SET_PR(TwistLimitContactDistance, PxReal, 0.05f); - SET_PR(TwistLimitEnabled, bool, false); - - collection_->add(*o); - return o; - } - - PxBase* LoadArticulationLink(TiXmlElement& ele, PxArticulation& articulation, PxArticulationLink* parent) - { - auto o = articulation.createLink( - parent, P(GlobalPose, PxTransform) - ); + actors_[o->getName()] = o; +} - LoadRigidBody(ele, o); +void PhysXLoader::LoadRigidBody(TiXmlElement& ele, PxRigidBody* o) { + LoadRigidActor(ele, o); - if (parent != nullptr) { - auto jointNode = ele.FirstChildElement("Joint"); - if (jointNode == nullptr) throw std::runtime_error("Joint missing on articulation link"); - LoadArticulationJoint(*jointNode, static_cast(o->getInboundJoint())); - } + SET_PR(CMassLocalPose, PxTransform, PxTransform(PxIdentity)); + SET_PR(Mass, PxReal, 1.0f); + SET_PR(MassSpaceInertiaTensor, PxVec3, PxVec3(1.0f, 1.0f, 1.0f)); + SET_PR(LinearDamping, PxReal, 0.0f); + SET_PR(AngularDamping, PxReal, 0.05f); - collection_->add(*o); + SET_PB(MaxLinearVelocity, 1e+15f); + SET_PR(MaxAngularVelocity, PxReal, 100.0f); - auto linksNode = ele.FirstChildElement("Links"); - if (linksNode) { - for (auto linkNode = linksNode->FirstChildElement("Link"); linkNode; linkNode = linkNode->NextSiblingElement("Link")) { - LoadArticulationLink(*linkNode, articulation, o); - } - } + SET_FLAG(Kinematic, RigidBodyFlag, PxRigidBodyFlag::eKINEMATIC); + SET_FLAG(EnableCCD, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD); + SET_FLAG(EnableCCDFriction, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD_FRICTION); + SET_FLAG(EnableSpeculativeCCD, RigidBodyFlag, PxRigidBodyFlag::eENABLE_SPECULATIVE_CCD); + SET_FLAG(EnableCCDMaxContactImpulse, RigidBodyFlag, PxRigidBodyFlag::eENABLE_CCD_MAX_CONTACT_IMPULSE); + SET_FLAG(RetainAccelerations, RigidBodyFlag, PxRigidBodyFlag::eRETAIN_ACCELERATIONS); - return o; - } + SET_PR(MinCCDAdvanceCoefficient, PxReal, 0.15f); + SET_PB(MaxDepenetrationVelocity, 1e+31f); + SET_PB(MaxContactImpulse, 1e+31f); +} - PxBase* LoadArticulation(TiXmlElement& ele) - { - auto o = physics_->createArticulation(); - SET_PR(SleepThreshold, PxReal, 0.005f); - SET_PR(StabilizationThreshold, PxReal, 0.0025f); - SET_PR(WakeCounter, PxReal, 0.4f); +PxReal PhysXLoader::LoadBoundedProperty(TiXmlElement& ele, char const* name, PxReal bound) { + auto attr = ele.FirstChildElement(name); + if (!attr || !attr->GetText()) return bound; + if (strcmp(attr->GetText(), "Unbounded") == 0) return bound; + return std::stof(attr->GetText()); +} - SET_PR(MaxProjectionIterations, PxU32, 4); - SET_PR(SeparationTolerance, PxReal, 0.01f); - SET_PR(InternalDriveIterations, PxU32, 4); - SET_PR(ExternalDriveIterations, PxU32, 4); +PxShape* PhysXLoader::LoadShape(TiXmlElement& ele) { + auto matIdx = PR(MaterialIndex, unsigned int, 0); + auto mat = materials_[matIdx]; + if (!mat) throw std::runtime_error("Shape references undefined material index"); - auto linksNode = ele.FirstChildElement("Links"); - if (linksNode) { - for (auto linkNode = linksNode->FirstChildElement("Link"); linkNode; linkNode = linkNode->NextSiblingElement("Link")) { - LoadArticulationLink(*linkNode, *o, nullptr); + auto geomEle = ele.FirstChildElement("Geometry"); + if (!geomEle) return nullptr; - } - } + PxGeometry* geom = LoadGeometry(*geomEle); + if (!geom) return nullptr; - collection_->add(*o); - return o; - } + PxShape* o = physics_->createShape(*geom, *mat, true); + o->setName(_strdup(PR(Name, std::string, "").c_str())); + o->setLocalPose(P(LocalPose, PxTransform)); - PxBase* LoadTopLevel(TiXmlElement& ele) - { - auto type = ele.ValueStr(); - if (type == "Material") { - return LoadMaterial(ele); - } else if (type == "RigidStatic") { - return LoadRigidStatic(ele); - } else if (type == "RigidDynamic") { - return LoadRigidDynamic(ele); - } else if (type == "D6Joint") { - return LoadD6Joint(ele); - } else if (type == "Articulation") { - return LoadArticulation(ele); - } else { - std::cout << "WARNING: Don't know how to load object " << type << std::endl; - return nullptr; - } - } -}; + delete geom; + return o; +} +PxGeometry* PhysXLoader::LoadGeometry(TiXmlElement& ele) { + std::string type = PR(Type, std::string, ""); + if (type == "Box") return new PxBoxGeometry(P(HalfExtents, PxVec3)); + if (type == "Sphere") return new PxSphereGeometry(PR(Radius, PxReal, 1.0f)); + return nullptr; +} -PxCollection* PhysXConverter::LoadCollectionFromXml(std::span const& xml) -{ +PxCollection* PhysXConverter::LoadCollectionFromXml(std::span const& xml) { PhysXLoader loader; loader.physics_ = physics_; loader.cooking_ = cooking_; - - // Ensure string is null-terminated std::string s((char const*)xml.data(), xml.size()); - TiXmlDocument doc; - doc.Parse(s.c_str(), 0, TIXML_ENCODING_UTF8); - if (doc.Error()) throw std::runtime_error(doc.ErrorDesc()); - - auto root = doc.FirstChildElement(); - if (root == nullptr || strcmp(root->Value(), "BG3Physics") != 0) throw std::runtime_error("Expected a BG3Physics XML document"); - - return loader.Load(*root); + doc.Parse(s.c_str()); + return loader.Load(*doc.FirstChildElement()); } diff --git a/RconClient/RakNetClient.cs b/RconClient/RakNetClient.cs index 33feee3e..96d73f46 100644 --- a/RconClient/RakNetClient.cs +++ b/RconClient/RakNetClient.cs @@ -16,7 +16,7 @@ public class AsyncUdpClient public AsyncUdpClient() { - Random rnd = new Random(); + Random rnd = new(); // Select a port number over 10000 as low port numbers // are frequently used by various server apps. Port = (UInt16)((rnd.Next() % (65536 - 10000)) + 10000); @@ -27,7 +27,7 @@ public void RunLoop() { while (true) { - IPEndPoint source = new IPEndPoint(0, 0); + IPEndPoint source = new(0, 0); byte[] packet; try { @@ -46,7 +46,7 @@ public void RunLoop() } else { - throw e; + throw; } } @@ -81,30 +81,34 @@ public RakNetSocket() private Packet DecodePacket(Byte id, BinaryReaderBE reader) { - Packet packet = null; - switch ((PacketId)id) + Packet packet = (PacketId)id switch { - case PacketId.OpenConnectionRequest1: packet = new OpenConnectionRequest1(); break; - case PacketId.OpenConnectionResponse1: packet = new OpenConnectionResponse1(); break; - case PacketId.OpenConnectionRequest2: packet = new OpenConnectionRequest2(); break; - case PacketId.OpenConnectionResponse2: packet = new OpenConnectionResponse2(); break; - default: throw new InvalidDataException("Unrecognized packet ID"); - } - + PacketId.OpenConnectionRequest1 => new OpenConnectionRequest1(), + PacketId.OpenConnectionResponse1 => new OpenConnectionResponse1(), + PacketId.OpenConnectionRequest2 => new OpenConnectionRequest2(), + PacketId.OpenConnectionResponse2 => new OpenConnectionResponse2(), + _ => throw new InvalidDataException("Unrecognized packet ID"), + }; packet.Read(reader); return packet; } private void HandleConnectionResponse1(IPEndPoint address, OpenConnectionResponse1 response) { + byte[] ipBytes = IPAddress.Parse("127.0.0.1").GetAddressBytes(); + if (BitConverter.IsLittleEndian) + { + Array.Reverse(ipBytes); + } + uint ipUint = BitConverter.ToUInt32(ipBytes, 0); var connReq = new OpenConnectionRequest2 { Magic = RakNetConstants.Magic, ClientId = ClientId, Address = new RakAddress { - Address = (UInt32)IPAddress.Parse("127.0.0.1").Address, - Port = Socket.Port + Address = ipUint, + Port = (ushort)IPAddress.HostToNetworkOrder((short)Socket.Port) }, MTU = 1200 }; @@ -136,38 +140,34 @@ private void HandlePacket(IPEndPoint address, Packet packet) private void OnPacketReceived(IPEndPoint address, byte[] packet) { - using (var stream = new MemoryStream(packet)) - using (var reader = new BinaryReaderBE(stream)) + using var stream = new MemoryStream(packet); + using var reader = new BinaryReaderBE(stream); + byte id = reader.ReadByte(); + if (id < 0x80) + { + var decoded = DecodePacket(id, reader); + HandlePacket(address, decoded); + } + else { - byte id = reader.ReadByte(); - if (id < 0x80) + if (Session != null) { - var decoded = DecodePacket(id, reader); - HandlePacket(address, decoded); + Session.HandlePacket(id, reader); } else { - if (Session != null) - { - Session.HandlePacket(id, reader); - } - else - { - throw new Exception("Unhandled session packet - no session established!"); - } + throw new Exception("Unhandled session packet - no session established!"); } } } public void Send(IPEndPoint address, Packet packet) { - using (var stream = new MemoryStream()) - using (var writer = new BinaryWriterBE(stream)) - { - packet.Write(writer); - stream.SetLength(stream.Position); - Socket.Send(address, stream.ToArray()); - } + using var stream = new MemoryStream(); + using var writer = new BinaryWriterBE(stream); + packet.Write(writer); + stream.SetLength(stream.Position); + Socket.Send(address, stream.ToArray()); } public void BeginConnection(IPEndPoint address) diff --git a/RconClient/RconClient.csproj b/RconClient/RconClient.csproj index cc0917a7..ca9b222c 100644 --- a/RconClient/RconClient.csproj +++ b/RconClient/RconClient.csproj @@ -5,6 +5,7 @@ LSLib.Rcon false x64 + AnyCPU;x64 diff --git a/StatParser/StatParser.csproj b/StatParser/StatParser.csproj index 27d53095..65ece674 100644 --- a/StatParser/StatParser.csproj +++ b/StatParser/StatParser.csproj @@ -9,6 +9,7 @@ Copyright © 2019 1.0.0.0 1.0.0.0 + AnyCPU;x64 diff --git a/StoryCompiler/StoryCompiler.csproj b/StoryCompiler/StoryCompiler.csproj index 332975aa..70adeca1 100644 --- a/StoryCompiler/StoryCompiler.csproj +++ b/StoryCompiler/StoryCompiler.csproj @@ -20,6 +20,7 @@ true false x64 + AnyCPU;x64 @@ -41,8 +42,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/StoryDecompiler/StoryDecompiler.csproj b/StoryDecompiler/StoryDecompiler.csproj index eb7b59c6..87ac66d5 100644 --- a/StoryDecompiler/StoryDecompiler.csproj +++ b/StoryDecompiler/StoryDecompiler.csproj @@ -9,6 +9,7 @@ Copyright © Norbyte 2012-2018 1.0.0.0 1.0.0.0 + AnyCPU;x64 diff --git a/VTexTool/VTexTool.csproj b/VTexTool/VTexTool.csproj index dd14a5d9..59b9bbb9 100644 --- a/VTexTool/VTexTool.csproj +++ b/VTexTool/VTexTool.csproj @@ -9,6 +9,7 @@ Copyright © Norbyte 2012-2018 1.0.0.0 1.0.0.0 + AnyCPU;x64 From 0b34732549da4bdd00e67db7c31726f83226cdf9 Mon Sep 17 00:00:00 2001 From: shadowslasher410 <13404137+shadowslasher410@users.noreply.github.com> Date: Sat, 23 May 2026 22:47:39 -0500 Subject: [PATCH 2/2] Update Solution to use .Net 10 and C#14; Replaced Dependencies; Made Cross-Platform Rewrote entire solution to use .Net 10 and c#14. Reworked project dependencies (all available via NuGet): CommandLineArgumentsParser -> System.Commandline, Newtonsoft.Json ->System.Text.Json, Google.Protobuf ->LightProto, Grpc.Tools -> Superpower, Winforms ->Avalonia (with Reactiveui.Avalonia and Xaml.Behaviors.Avalonia for bindings/events. I haven't tested it on other machines (I use an AMD64 Windows 11), but it should now be able to be cross-platform. --- ConverterApp/App.axaml | 35 + ConverterApp/App.axaml.cs | 86 + ConverterApp/App.config | 6 - ConverterApp/ConverterApp.csproj | 149 +- ConverterApp/ConverterAppSettings.cs | 440 ++--- ConverterApp/DatabaseDumper.cs | 118 +- ConverterApp/DebugDumper.cs | 406 ++--- ConverterApp/DebugPane.Designer.cs | 315 ---- ConverterApp/DebugPane.axaml | 74 + ConverterApp/DebugPane.axaml.cs | 189 +++ ConverterApp/DebugPane.cs | 104 -- ConverterApp/DebugPane.resx | 123 -- ConverterApp/ExportItemSelection.Designer.cs | 69 - ConverterApp/ExportItemSelection.axaml | 59 + ConverterApp/ExportItemSelection.axaml.cs | 95 ++ ConverterApp/ExportItemSelection.cs | 163 -- ConverterApp/ExportItemSelection.resx | 123 -- ConverterApp/GR2Pane.Designer.cs | 720 --------- ConverterApp/GR2Pane.axaml | 100 ++ ConverterApp/GR2Pane.axaml.cs | 430 +++++ ConverterApp/GR2Pane.cs | 455 ------ ConverterApp/GR2Pane.resx | 135 -- ConverterApp/LocalizationPane.Designer.cs | 175 -- ConverterApp/LocalizationPane.axaml | 60 + ConverterApp/LocalizationPane.axaml.cs | 157 ++ ConverterApp/LocalizationPane.cs | 55 - ConverterApp/LocalizationPane.resx | 126 -- ConverterApp/MainForm.Designer.cs | 194 --- ConverterApp/MainForm.cs | 140 -- ConverterApp/MainForm.resx | 120 -- ConverterApp/MainWindow.axaml | 63 + ConverterApp/MainWindow.axaml.cs | 165 ++ ConverterApp/OsirisPane.Designer.cs | 287 ---- ConverterApp/OsirisPane.axaml | 82 + ConverterApp/OsirisPane.axaml.cs | 311 ++++ ConverterApp/OsirisPane.cs | 342 ---- ConverterApp/OsirisPane.resx | 126 -- ConverterApp/PackagePane.Designer.cs | 478 ------ ConverterApp/PackagePane.axaml | 87 + ConverterApp/PackagePane.axaml.cs | 295 ++++ ConverterApp/PackagePane.cs | 288 ---- ConverterApp/PackagePane.resx | 132 -- ConverterApp/Program.cs | 24 - ConverterApp/Properties/Resources.Designer.cs | 63 - ConverterApp/Properties/Resources.resx | 117 -- ConverterApp/Properties/Settings.Designer.cs | 26 - ConverterApp/Properties/Settings.settings | 7 - ConverterApp/ResourcePane.Designer.cs | 439 ----- ConverterApp/ResourcePane.axaml | 100 ++ ConverterApp/ResourcePane.axaml.cs | 211 +++ ConverterApp/ResourcePane.cs | 179 --- ConverterApp/ResourcePane.resx | 132 -- ConverterApp/VariableDumper.cs | 166 +- ConverterApp/VirtualTexturesPane.Designer.cs | 406 ----- ConverterApp/VirtualTexturesPane.axaml | 78 + ConverterApp/VirtualTexturesPane.axaml.cs | 295 ++++ ConverterApp/VirtualTexturesPane.cs | 163 -- ConverterApp/VirtualTexturesPane.resx | 132 -- DebuggerFrontend/App.config | 6 - DebuggerFrontend/Breakpoints.cs | 286 ++-- DebuggerFrontend/DAPMessageHandler.cs | 854 +++++----- DebuggerFrontend/DAPProtocol.cs | 1146 +++---------- DebuggerFrontend/DAPStream.cs | 204 ++- DebuggerFrontend/DAPUtils.cs | 210 +-- DebuggerFrontend/DatabaseEnumerator.cs | 105 +- DebuggerFrontend/DbgClient.cs | 386 ++--- DebuggerFrontend/DbgProtocol.proto | 306 ---- DebuggerFrontend/DebugInfoData.cs | 191 +++ DebuggerFrontend/DebugInfoLoader.cs | 205 +-- DebuggerFrontend/DebugInfoSync.cs | 148 +- DebuggerFrontend/DebugProtocol.cs | 640 ++++++++ DebuggerFrontend/DebuggerFrontend.csproj | 79 +- DebuggerFrontend/EvaluationResults.cs | 167 +- DebuggerFrontend/ExpressionEvaluator.cs | 458 ++---- .../ExpressionParser/Expression.lex | 36 - .../ExpressionParser/Expression.yy | 59 - .../ExpressionParser/ExpressionNodes.cs | 68 +- .../ExpressionParser/ExpressionParser.cs | 139 +- .../ExpressionParserCombinator.cs | 163 ++ .../ExpressionParser/ExpressionTokenizer.cs | 104 ++ DebuggerFrontend/Program.cs | 68 +- DebuggerFrontend/Properties/AssemblyInfo.cs | 14 - DebuggerFrontend/StackTracePrinter.cs | 259 ++- DebuggerFrontend/ValueFormatter.cs | 258 +-- DebuggerFrontend/debuginfo.proto | 89 -- Divine/App.config | 6 - Divine/CLI/CommandLineActions.cs | 158 +- Divine/CLI/CommandLineArguments.cs | 453 ++---- Divine/CLI/CommandLineDataProcessor.cs | 52 +- Divine/CLI/CommandLineGR2Processor.cs | 79 +- Divine/CLI/CommandLineLogger.cs | 141 +- Divine/CLI/CommandLinePackageProcessor.cs | 73 +- Divine/Divine.csproj | 53 +- Divine/Program.cs | 244 ++- Divine/Properties/AssemblyInfo.cs | 13 - LSLib/Granny/Collada.cs | 92 +- LSLib/Granny/ColladaAnimation.cs | 233 ++- LSLib/Granny/ColladaSchema.cs | 90 +- LSLib/Granny/GR2/Format.cs | 1417 +++++------------ LSLib/Granny/GR2/Granny2Compressor.cs | 148 ++ LSLib/Granny/GR2/Helpers.cs | 274 +++- LSLib/Granny/GR2/Reader.cs | 1126 +++---------- LSLib/Granny/GR2/Writer.cs | 1092 ++----------- LSLib/Granny/GR2Utils.cs | 127 +- LSLib/Granny/Model/Animation.cs | 882 ++-------- LSLib/Granny/Model/ColladaExporter.cs | 1039 ++---------- LSLib/Granny/Model/ColladaHelpers.cs | 270 ++-- LSLib/Granny/Model/ColladaImporter.cs | 1039 +----------- LSLib/Granny/Model/ColladaMesh.cs | 537 +------ .../Model/CurveData/AnimationCurveData.cs | 307 ++-- LSLib/Granny/Model/CurveData/D3Constant32f.cs | 36 +- LSLib/Granny/Model/CurveData/D3I1K16uC16u.cs | 62 +- LSLib/Granny/Model/CurveData/D3I1K32fC32f.cs | 53 +- LSLib/Granny/Model/CurveData/D3I1K8uC8u.cs | 60 +- LSLib/Granny/Model/CurveData/D3K16uC16u.cs | 61 +- LSLib/Granny/Model/CurveData/D3K8uC8u.cs | 61 +- LSLib/Granny/Model/CurveData/D4Constant32f.cs | 48 +- LSLib/Granny/Model/CurveData/D4nK16uC15u.cs | 101 +- LSLib/Granny/Model/CurveData/D4nK8uC7u.cs | 102 +- LSLib/Granny/Model/CurveData/D9I1K16uC16u.cs | 55 +- LSLib/Granny/Model/CurveData/D9I1K8uC8u.cs | 55 +- LSLib/Granny/Model/CurveData/D9I3K16uC16u.cs | 66 +- LSLib/Granny/Model/CurveData/D9I3K8uC8u.cs | 63 +- LSLib/Granny/Model/CurveData/DaConstant32f.cs | 35 +- LSLib/Granny/Model/CurveData/DaIdentity.cs | 23 +- LSLib/Granny/Model/CurveData/DaK16uC16u.cs | 105 +- LSLib/Granny/Model/CurveData/DaK32fC32f.cs | 163 +- LSLib/Granny/Model/CurveData/DaK8uC8u.cs | 104 +- .../Granny/Model/CurveData/DaKeyframes32f.cs | 157 +- LSLib/Granny/Model/DivinityMesh.cs | 400 ++--- LSLib/Granny/Model/Exporter.cs | 841 ++-------- LSLib/Granny/Model/GLTFExporter.cs | 573 ++++--- LSLib/Granny/Model/GLTFExtensions.cs | 142 +- LSLib/Granny/Model/GLTFImporter.cs | 1112 +++++++++---- LSLib/Granny/Model/GLTFMesh.cs | 396 +++-- LSLib/Granny/Model/GLTFVertex.cs | 1174 ++++++++------ LSLib/Granny/Model/Mesh.cs | 861 ++-------- LSLib/Granny/Model/Metadata.cs | 65 +- LSLib/Granny/Model/Model.cs | 14 +- LSLib/Granny/Model/Root.cs | 172 +- LSLib/Granny/Model/Skeleton.cs | 369 +++-- LSLib/Granny/Model/Vertex.cs | 762 ++------- LSLib/Granny/Model/VertexHelpers.cs | 199 ++- LSLib/Granny/Model/VertexSerialization.cs | 612 +------ LSLib/Granny/Utils.cs | 109 +- LSLib/LS/BinUtils.cs | 299 ++-- LSLib/LS/Compression.cs | 405 +++-- LSLib/LS/Enums/Compression.cs | 77 - LSLib/LS/Enums/Game.cs | 66 +- LSLib/LS/Enums/LSFVersion.cs | 2 +- LSLib/LS/Enums/LogLevel.cs | 3 +- LSLib/LS/Enums/PackageVersion.cs | 43 - LSLib/LS/Enums/ResourceFormat.cs | 2 +- LSLib/LS/FileManager.cs | 33 +- LSLib/LS/Localization.cs | 222 +-- LSLib/LS/Matrix.cs | 949 ++++++----- LSLib/LS/Mods/ModResources.cs | 147 +- LSLib/LS/NodeAttribute.cs | 594 +++---- LSLib/LS/PackageCommon.cs | 185 ++- LSLib/LS/PackageFormat.cs | 474 +++--- LSLib/LS/PackageReader.cs | 177 +- LSLib/LS/PackageWriter.cs | 251 ++- LSLib/LS/ParserCommon.cs | 72 +- LSLib/LS/Resource.cs | 113 +- LSLib/LS/ResourceUtils.cs | 168 +- LSLib/LS/Resources/LSB/LSBReader.cs | 163 +- LSLib/LS/Resources/LSB/LSBWriter.cs | 184 ++- LSLib/LS/Resources/LSF/LSFCommon.cs | 191 +-- LSLib/LS/Resources/LSF/LSFReader.cs | 353 ++-- LSLib/LS/Resources/LSF/LSFWriter.cs | 525 +++--- LSLib/LS/Resources/LSJ/LSJReader.cs | 44 +- .../LS/Resources/LSJ/LSJResourceConverter.cs | 609 +++---- LSLib/LS/Resources/LSJ/LSJWriter.cs | 41 +- LSLib/LS/Resources/LSX/LSXReader.cs | 328 ++-- LSLib/LS/Resources/LSX/LSXWriter.cs | 152 +- LSLib/LS/Save/SavegameHelpers.cs | 108 +- LSLib/LS/Save/VariableManager.cs | 409 +++-- LSLib/LS/Stats/ShiftReduceParser.cs | 919 ----------- LSLib/LS/Story/Adapter.cs | 98 +- LSLib/LS/Story/Call.cs | 90 +- LSLib/LS/Story/Common.cs | 271 ++-- LSLib/LS/Story/Compiler/CompilationContext.cs | 302 ++-- LSLib/LS/Story/Compiler/Compiler.cs | 936 ++++++----- LSLib/LS/Story/Compiler/DebugInfo.cs | 124 +- LSLib/LS/Story/Compiler/HeaderLoader.cs | 70 +- LSLib/LS/Story/Compiler/IR.cs | 218 ++- LSLib/LS/Story/Compiler/IRGenerator.cs | 220 +-- LSLib/LS/Story/Compiler/Preprocessor.cs | 43 +- LSLib/LS/Story/Compiler/StoryEmitter.cs | 1074 +++++++------ LSLib/LS/Story/DataNode.cs | 31 +- LSLib/LS/Story/Database.cs | 204 +-- LSLib/LS/Story/DatabaseNode.cs | 10 +- LSLib/LS/Story/DebugExport.cs | 624 ++++---- LSLib/LS/Story/Function.cs | 124 +- LSLib/LS/Story/Goal.cs | 116 +- LSLib/LS/Story/GoalParser/ASTNodes.cs | 123 +- LSLib/LS/Story/GoalParser/Goal.lex | 73 - LSLib/LS/Story/GoalParser/Goal.yy | 189 --- .../Story/GoalParser/GoalCombinatorParser.cs | 253 +++ LSLib/LS/Story/GoalParser/GoalParser.cs | 287 ++-- LSLib/LS/Story/GoalParser/GoalTokenizer.cs | 104 ++ LSLib/LS/Story/HeaderParser/ASTNodes.cs | 67 +- LSLib/LS/Story/HeaderParser/HeaderParser.cs | 324 ++-- LSLib/LS/Story/HeaderParser/StoryHeader.lex | 47 - LSLib/LS/Story/HeaderParser/StoryHeader.yy | 81 - LSLib/LS/Story/Join.cs | 121 +- LSLib/LS/Story/Node.cs | 56 +- LSLib/LS/Story/Proc.cs | 10 +- LSLib/LS/Story/Query.cs | 18 +- LSLib/LS/Story/Reference.cs | 196 ++- LSLib/LS/Story/Rel.cs | 45 +- LSLib/LS/Story/RelOp.cs | 66 +- LSLib/LS/Story/Rule.cs | 163 +- LSLib/LS/Story/Story.cs | 602 ++++--- LSLib/LS/Story/Value.cs | 510 +++--- LSLib/LS/VFS.cs | 249 ++- LSLib/LSLib.csproj | 112 +- LSLib/Properties/AssemblyInfo.cs | 13 - LSLib/VirtualTextures/BC3Image.cs | 185 --- LSLib/VirtualTextures/Build.cs | 1267 +++------------ LSLib/VirtualTextures/Compression.cs | 202 +-- LSLib/VirtualTextures/Geometry.cs | 112 +- LSLib/VirtualTextures/PageFile.cs | 103 -- LSLib/VirtualTextures/PageFileBuild.cs | 259 +-- LSLib/VirtualTextures/TileSetBuilder.cs | 131 ++ LSLib/VirtualTextures/VirtualTexture.cs | 1039 ++++++------ .../VirtualTextureExtensions.cs | 89 ++ .../VirtualTextures/VirtualTextureFormats.cs | 478 +++--- LSLibStats/LSLibStats.csproj | 36 +- .../Expression/ExpressionCombinatorParser.cs | 142 ++ .../Stats/Expression/ExpressionParser.cs | 62 + .../Stats/Expression/ExpressionTokenizer.cs | 220 +++ LSLibStats/Stats/File/Stat.lex | 104 -- LSLibStats/Stats/File/Stat.yy | 380 ----- LSLibStats/Stats/File/StatNodes.cs | 34 - LSLibStats/Stats/File/StatParser.cs | 183 --- LSLibStats/Stats/Functor/Expression.lex | 59 - LSLibStats/Stats/Functor/Expression.yy | 101 -- LSLibStats/Stats/Functor/ExpressionParser.cs | 23 - LSLibStats/Stats/Functor/Functor.lex | 85 - LSLibStats/Stats/Functor/Functor.yy | 128 -- .../Stats/Functor/FunctorCombinatorParser.cs | 157 ++ LSLibStats/Stats/Functor/FunctorParser.cs | 291 ++-- .../Stats/Functor/FunctorParserDefinitions.cs | 29 - LSLibStats/Stats/Functor/FunctorTokenizer.cs | 196 +++ LSLibStats/Stats/Functor/Lua.lex | 71 - LSLibStats/Stats/Functor/Lua.yy | 104 -- LSLibStats/Stats/Functor/LuaParser.cs | 23 - LSLibStats/Stats/Functor/Requirement.lex | 25 - LSLibStats/Stats/Functor/Requirement.yy | 39 - LSLibStats/Stats/Functor/RequirementParser.cs | 69 - LSLibStats/Stats/Functor/RollConditions.lex | 23 - LSLibStats/Stats/Functor/RollConditions.yy | 38 - .../Stats/Functor/RollConditionsParser.cs | 69 - LSLibStats/Stats/Lua/LuaCombinatorParser.cs | 151 ++ LSLibStats/Stats/Lua/LuaParser.cs | 63 + LSLibStats/Stats/Lua/LuaTokenizer.cs | 215 +++ .../RequirementCombinatorParser.cs | 82 + .../Stats/Requirement/RequirementParser.cs | 110 ++ .../Stats/Requirement/RequirementTokenizer.cs | 112 ++ .../RollConditionCombinatorParser.cs | 77 + .../RollCondition/RollConditionParser.cs | 88 + .../RollCondition/RollConditionTokenizer.cs | 100 ++ LSLibStats/Stats/StatCombinatorParser.cs | 354 ++++ LSLibStats/Stats/StatDefinitions.cs | 317 ++-- LSLibStats/Stats/StatFileParser.cs | 420 ----- LSLibStats/Stats/StatParser.cs | 429 +++++ LSLibStats/Stats/StatTokenizer.cs | 307 ++++ LSLibStats/Stats/StatValueParser.cs | 682 ++++++++ LSLibStats/Stats/StatValueParsers.cs | 842 ---------- LSTools.Tests/LSTools.Tests.csproj | 50 + LSTools.Tests/StatParser.Tests.cs | 261 +++ LSTools.Tests/StoryCompiler.Tests.cs | 1117 +++++++++++++ LSTools.Tests/StoryDecompiler.Tests.cs | 127 ++ LSTools.Tests/VTexTool.Tests.cs | 97 ++ LSTools.sln | 69 +- PhysXTool/PhysXConverter.cs | 162 ++ PhysXTool/PhysXExporter.cs | 232 +++ PhysXTool/PhysXTool.csproj | 19 + PhysXTool/Program.cs | 96 ++ PhysXTool/PxData.cs | 110 ++ PhysXTool/PxLoader.cs | 257 +++ RconClient/DosPackets.cs | 148 +- RconClient/Encapsulation.cs | 120 +- RconClient/Program.cs | 183 +++ RconClient/Properties/AssemblyInfo.cs | 13 - RconClient/RakNetClient.cs | 150 +- RconClient/RakNetCommon.cs | 53 +- RconClient/RakNetPackets.cs | 338 ++-- RconClient/RakNetSession.cs | 299 ++-- RconClient/Rcon.cs | 157 -- RconClient/RconClient.csproj | 44 +- RconClient/Utils.cs | 59 +- RconClient/app.config | 3 - StatParser/App.config | 6 - StatParser/Arguments.cs | 113 +- StatParser/Program.cs | 70 +- StatParser/Properties/AssemblyInfo.cs | 13 - StatParser/StatChecker.cs | 265 ++- StatParser/StatParser.csproj | 61 +- StoryCompiler/App.config | 6 - StoryCompiler/Arguments.cs | 223 ++- StoryCompiler/DebugInfoContracts.cs | 107 ++ StoryCompiler/DebugInfoSaver.cs | 183 +-- StoryCompiler/Log.cs | 239 ++- StoryCompiler/ModCompiler.cs | 497 +++--- StoryCompiler/Program.cs | 148 +- StoryCompiler/Properties/AssemblyInfo.cs | 13 - StoryCompiler/StoryCompiler.csproj | 105 +- StoryCompiler/debuginfo.proto | 89 -- StoryDecompiler/App.config | 6 - StoryDecompiler/Arguments.cs | 27 - StoryDecompiler/Program.cs | 267 ++-- StoryDecompiler/Properties/AssemblyInfo.cs | 13 - StoryDecompiler/StoryDecompiler.csproj | 52 +- VTexTool/App.config | 6 - VTexTool/Program.cs | 134 +- VTexTool/Properties/AssemblyInfo.cs | 13 - VTexTool/VTexTool.csproj | 58 +- 319 files changed, 31599 insertions(+), 39175 deletions(-) create mode 100644 ConverterApp/App.axaml create mode 100644 ConverterApp/App.axaml.cs delete mode 100644 ConverterApp/App.config delete mode 100644 ConverterApp/DebugPane.Designer.cs create mode 100644 ConverterApp/DebugPane.axaml create mode 100644 ConverterApp/DebugPane.axaml.cs delete mode 100644 ConverterApp/DebugPane.cs delete mode 100644 ConverterApp/DebugPane.resx delete mode 100644 ConverterApp/ExportItemSelection.Designer.cs create mode 100644 ConverterApp/ExportItemSelection.axaml create mode 100644 ConverterApp/ExportItemSelection.axaml.cs delete mode 100644 ConverterApp/ExportItemSelection.cs delete mode 100644 ConverterApp/ExportItemSelection.resx delete mode 100644 ConverterApp/GR2Pane.Designer.cs create mode 100644 ConverterApp/GR2Pane.axaml create mode 100644 ConverterApp/GR2Pane.axaml.cs delete mode 100644 ConverterApp/GR2Pane.cs delete mode 100644 ConverterApp/GR2Pane.resx delete mode 100644 ConverterApp/LocalizationPane.Designer.cs create mode 100644 ConverterApp/LocalizationPane.axaml create mode 100644 ConverterApp/LocalizationPane.axaml.cs delete mode 100644 ConverterApp/LocalizationPane.cs delete mode 100644 ConverterApp/LocalizationPane.resx delete mode 100644 ConverterApp/MainForm.Designer.cs delete mode 100644 ConverterApp/MainForm.cs delete mode 100644 ConverterApp/MainForm.resx create mode 100644 ConverterApp/MainWindow.axaml create mode 100644 ConverterApp/MainWindow.axaml.cs delete mode 100644 ConverterApp/OsirisPane.Designer.cs create mode 100644 ConverterApp/OsirisPane.axaml create mode 100644 ConverterApp/OsirisPane.axaml.cs delete mode 100644 ConverterApp/OsirisPane.cs delete mode 100644 ConverterApp/OsirisPane.resx delete mode 100644 ConverterApp/PackagePane.Designer.cs create mode 100644 ConverterApp/PackagePane.axaml create mode 100644 ConverterApp/PackagePane.axaml.cs delete mode 100644 ConverterApp/PackagePane.cs delete mode 100644 ConverterApp/PackagePane.resx delete mode 100644 ConverterApp/Program.cs delete mode 100644 ConverterApp/Properties/Resources.Designer.cs delete mode 100644 ConverterApp/Properties/Resources.resx delete mode 100644 ConverterApp/Properties/Settings.Designer.cs delete mode 100644 ConverterApp/Properties/Settings.settings delete mode 100644 ConverterApp/ResourcePane.Designer.cs create mode 100644 ConverterApp/ResourcePane.axaml create mode 100644 ConverterApp/ResourcePane.axaml.cs delete mode 100644 ConverterApp/ResourcePane.cs delete mode 100644 ConverterApp/ResourcePane.resx delete mode 100644 ConverterApp/VirtualTexturesPane.Designer.cs create mode 100644 ConverterApp/VirtualTexturesPane.axaml create mode 100644 ConverterApp/VirtualTexturesPane.axaml.cs delete mode 100644 ConverterApp/VirtualTexturesPane.cs delete mode 100644 ConverterApp/VirtualTexturesPane.resx delete mode 100644 DebuggerFrontend/App.config delete mode 100644 DebuggerFrontend/DbgProtocol.proto create mode 100644 DebuggerFrontend/DebugInfoData.cs create mode 100644 DebuggerFrontend/DebugProtocol.cs delete mode 100644 DebuggerFrontend/ExpressionParser/Expression.lex delete mode 100644 DebuggerFrontend/ExpressionParser/Expression.yy create mode 100644 DebuggerFrontend/ExpressionParser/ExpressionParserCombinator.cs create mode 100644 DebuggerFrontend/ExpressionParser/ExpressionTokenizer.cs delete mode 100644 DebuggerFrontend/Properties/AssemblyInfo.cs delete mode 100644 DebuggerFrontend/debuginfo.proto delete mode 100644 Divine/App.config delete mode 100644 Divine/Properties/AssemblyInfo.cs create mode 100644 LSLib/Granny/GR2/Granny2Compressor.cs delete mode 100644 LSLib/LS/Enums/Compression.cs delete mode 100644 LSLib/LS/Enums/PackageVersion.cs delete mode 100644 LSLib/LS/Stats/ShiftReduceParser.cs delete mode 100644 LSLib/LS/Story/GoalParser/Goal.lex delete mode 100644 LSLib/LS/Story/GoalParser/Goal.yy create mode 100644 LSLib/LS/Story/GoalParser/GoalCombinatorParser.cs create mode 100644 LSLib/LS/Story/GoalParser/GoalTokenizer.cs delete mode 100644 LSLib/LS/Story/HeaderParser/StoryHeader.lex delete mode 100644 LSLib/LS/Story/HeaderParser/StoryHeader.yy delete mode 100644 LSLib/Properties/AssemblyInfo.cs delete mode 100644 LSLib/VirtualTextures/BC3Image.cs delete mode 100644 LSLib/VirtualTextures/PageFile.cs create mode 100644 LSLib/VirtualTextures/TileSetBuilder.cs create mode 100644 LSLib/VirtualTextures/VirtualTextureExtensions.cs create mode 100644 LSLibStats/Stats/Expression/ExpressionCombinatorParser.cs create mode 100644 LSLibStats/Stats/Expression/ExpressionParser.cs create mode 100644 LSLibStats/Stats/Expression/ExpressionTokenizer.cs delete mode 100644 LSLibStats/Stats/File/Stat.lex delete mode 100644 LSLibStats/Stats/File/Stat.yy delete mode 100644 LSLibStats/Stats/File/StatNodes.cs delete mode 100644 LSLibStats/Stats/File/StatParser.cs delete mode 100644 LSLibStats/Stats/Functor/Expression.lex delete mode 100644 LSLibStats/Stats/Functor/Expression.yy delete mode 100644 LSLibStats/Stats/Functor/ExpressionParser.cs delete mode 100644 LSLibStats/Stats/Functor/Functor.lex delete mode 100644 LSLibStats/Stats/Functor/Functor.yy create mode 100644 LSLibStats/Stats/Functor/FunctorCombinatorParser.cs delete mode 100644 LSLibStats/Stats/Functor/FunctorParserDefinitions.cs create mode 100644 LSLibStats/Stats/Functor/FunctorTokenizer.cs delete mode 100644 LSLibStats/Stats/Functor/Lua.lex delete mode 100644 LSLibStats/Stats/Functor/Lua.yy delete mode 100644 LSLibStats/Stats/Functor/LuaParser.cs delete mode 100644 LSLibStats/Stats/Functor/Requirement.lex delete mode 100644 LSLibStats/Stats/Functor/Requirement.yy delete mode 100644 LSLibStats/Stats/Functor/RequirementParser.cs delete mode 100644 LSLibStats/Stats/Functor/RollConditions.lex delete mode 100644 LSLibStats/Stats/Functor/RollConditions.yy delete mode 100644 LSLibStats/Stats/Functor/RollConditionsParser.cs create mode 100644 LSLibStats/Stats/Lua/LuaCombinatorParser.cs create mode 100644 LSLibStats/Stats/Lua/LuaParser.cs create mode 100644 LSLibStats/Stats/Lua/LuaTokenizer.cs create mode 100644 LSLibStats/Stats/Requirement/RequirementCombinatorParser.cs create mode 100644 LSLibStats/Stats/Requirement/RequirementParser.cs create mode 100644 LSLibStats/Stats/Requirement/RequirementTokenizer.cs create mode 100644 LSLibStats/Stats/RollCondition/RollConditionCombinatorParser.cs create mode 100644 LSLibStats/Stats/RollCondition/RollConditionParser.cs create mode 100644 LSLibStats/Stats/RollCondition/RollConditionTokenizer.cs create mode 100644 LSLibStats/Stats/StatCombinatorParser.cs delete mode 100644 LSLibStats/Stats/StatFileParser.cs create mode 100644 LSLibStats/Stats/StatParser.cs create mode 100644 LSLibStats/Stats/StatTokenizer.cs create mode 100644 LSLibStats/Stats/StatValueParser.cs delete mode 100644 LSLibStats/Stats/StatValueParsers.cs create mode 100644 LSTools.Tests/LSTools.Tests.csproj create mode 100644 LSTools.Tests/StatParser.Tests.cs create mode 100644 LSTools.Tests/StoryCompiler.Tests.cs create mode 100644 LSTools.Tests/StoryDecompiler.Tests.cs create mode 100644 LSTools.Tests/VTexTool.Tests.cs create mode 100644 PhysXTool/PhysXConverter.cs create mode 100644 PhysXTool/PhysXExporter.cs create mode 100644 PhysXTool/PhysXTool.csproj create mode 100644 PhysXTool/Program.cs create mode 100644 PhysXTool/PxData.cs create mode 100644 PhysXTool/PxLoader.cs create mode 100644 RconClient/Program.cs delete mode 100644 RconClient/Properties/AssemblyInfo.cs delete mode 100644 RconClient/Rcon.cs delete mode 100644 RconClient/app.config delete mode 100644 StatParser/App.config delete mode 100644 StatParser/Properties/AssemblyInfo.cs delete mode 100644 StoryCompiler/App.config create mode 100644 StoryCompiler/DebugInfoContracts.cs delete mode 100644 StoryCompiler/Properties/AssemblyInfo.cs delete mode 100644 StoryCompiler/debuginfo.proto delete mode 100644 StoryDecompiler/App.config delete mode 100644 StoryDecompiler/Arguments.cs delete mode 100644 StoryDecompiler/Properties/AssemblyInfo.cs delete mode 100644 VTexTool/App.config delete mode 100644 VTexTool/Properties/AssemblyInfo.cs diff --git a/ConverterApp/App.axaml b/ConverterApp/App.axaml new file mode 100644 index 00000000..8f1d37ae --- /dev/null +++ b/ConverterApp/App.axaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ConverterApp/App.axaml.cs b/ConverterApp/App.axaml.cs new file mode 100644 index 00000000..15de164a --- /dev/null +++ b/ConverterApp/App.axaml.cs @@ -0,0 +1,86 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using ReactiveUI; +using ReactiveUI.Avalonia; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Windows.Input; + +namespace LSTools.DivineGUI; + +public class BatchProcessItem : ReactiveObject +{ + private bool _isChecked; + private string _name = string.Empty; + private string _type = string.Empty; + private string _batchStatusText = string.Empty; + private double _batchProgressValue; + + public bool IsChecked { get => _isChecked; set => this.RaiseAndSetIfChanged(ref _isChecked, value); } + public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); } + public string Type { get => _type; set => this.RaiseAndSetIfChanged(ref _type, value); } + + public ConverterAppSettings Settings { get; set; } = new(); + + public bool FlipUVs { get; set; } + public bool MirrorSkeletons { get; set; } + public bool ApplyBasisTransforms { get; set; } + public bool MeshRigidChecked { get; set; } + public bool MeshRigidEnabled { get; set; } + public bool MeshClothChecked { get; set; } + public bool MeshClothEnabled { get; set; } + public bool MeshProxyChecked { get; set; } + public bool MeshProxyEnabled { get; set; } + + public string BatchStatusText { get => _batchStatusText; set => this.RaiseAndSetIfChanged(ref _batchStatusText, value); } + public double BatchProgressValue { get => _batchProgressValue; set => this.RaiseAndSetIfChanged(ref _batchProgressValue, value); } +} + +public class MainSettingsProvider : ISettingsDataSource +{ + public ConverterAppSettings Settings { get; set; } = new(); +} + +public partial class App : Application +{ + public ObservableCollection BatchProcessItemsCollection { get; } = []; + public ICommand? BrowsePathCommand { get; set; } + public ICommand? SaveOutputCommand { get; set; } + + [STAThread] + public static void Main(string[] args) + { + CultureInfo customCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone(); + customCulture.NumberFormat.NumberDecimalSeparator = "."; + + Thread.CurrentThread.CurrentCulture = customCulture; + Thread.CurrentThread.CurrentUICulture = customCulture; + CultureInfo.DefaultThreadCurrentCulture = customCulture; + CultureInfo.DefaultThreadCurrentUICulture = customCulture; + + AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace() + .UseReactiveUI(rxui => { }) + .RegisterReactiveUIViewsFromEntryAssembly() + .StartWithClassicDesktopLifetime(args); + } + + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var mainSettingsContext = new MainSettingsProvider(); + desktop.MainWindow = new MainWindow(mainSettingsContext); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/ConverterApp/App.config b/ConverterApp/App.config deleted file mode 100644 index d0f8440b..00000000 --- a/ConverterApp/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/ConverterApp/ConverterApp.csproj b/ConverterApp/ConverterApp.csproj index 30b4c7ae..4adbf123 100644 --- a/ConverterApp/ConverterApp.csproj +++ b/ConverterApp/ConverterApp.csproj @@ -1,107 +1,44 @@  - - net8.0-windows - WinExe - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - false - true - true - AnyCPU;x64 - - - x64 - - - x64 - - - x64 - - - - - x64 - - - - Always - - - bin\Editor Debug\ - true - MinimumRecommendedRules.ruleset - - - bin\Editor Debug\ - true - MinimumRecommendedRules.ruleset - - - - UserControl - - - Component - - - UserControl - - - UserControl - - - UserControl - - - UserControl - - - UserControl - - - UserControl - - - - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - \ No newline at end of file + + + net10.0 + WinExe + LSTools.DivineGUI + LSTools.DivineGUI.App + + 14 + enable + enable + true + + true + link + Speed + + true + + x64 + AnyCPU;x64 + + LSLib - Divine Cross-Platform GUI Tool + LSLib + Copyright © Norbyte 2012-2026 + 1.18.5.0 + 1.18.5.0 + + + + + + + + + + + + + + + + + diff --git a/ConverterApp/ConverterAppSettings.cs b/ConverterApp/ConverterAppSettings.cs index e377ce57..4cd112e7 100644 --- a/ConverterApp/ConverterAppSettings.cs +++ b/ConverterApp/ConverterAppSettings.cs @@ -1,412 +1,166 @@ -using LSLib.Granny.Model; -using LSLib.LS; +using LSLib.LS; using LSLib.LS.Enums; -using System; -using System.Collections.Generic; +using ReactiveUI; using System.ComponentModel; -using System.Globalization; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; +using System.Text.Json; +using System.Text.Json.Serialization; -namespace ConverterApp; +namespace LSTools.DivineGUI; public interface ISettingsDataSource { ConverterAppSettings Settings { get; set; } } -public class SettingsBase : INotifyPropertyChanged +public interface IGameSettingsTarget { - public event PropertyChangedEventHandler PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - var handler = PropertyChanged; - if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); - } + void SetGame(Game game); } -public class ConverterAppSettings : SettingsBase +public class ConverterAppSettings : ReactiveObject { - private GR2PaneSettings gr2; - - public GR2PaneSettings GR2 - { - get { return gr2; } - set { gr2 = value; } - } - - private PackagePaneSettings pakSettings; - - public PackagePaneSettings PAK - { - get { return pakSettings; } - set { pakSettings = value; } - } - - private ResourcePaneSettings resourceSettings; - - public ResourcePaneSettings Resources - { - get { return resourceSettings; } - set { resourceSettings = value; } - } - - private VirtualTexturesPaneSettings virtualTextureSettings; - public VirtualTexturesPaneSettings VirtualTextures - { - get { return virtualTextureSettings; } - set { virtualTextureSettings = value; } - } - - private OsirisPaneSettings storySettings; - - public OsirisPaneSettings Story - { - get { return storySettings; } - set { storySettings = value; } - } - - private DebugPaneSettings debugSettings; - - public DebugPaneSettings Debugging - { - get { return debugSettings; } - set { debugSettings = value; } - } - - private Game selectedGame = Game.BaldursGate3; + public GR2PaneSettings GR2 { get; init; } = new(); + public PackagePaneSettings PAK { get; init; } = new(); + public ResourcePaneSettings Resources { get; init; } = new(); + public VirtualTexturesPaneSettings VirtualTextures { get; init; } = new(); + public OsirisPaneSettings Story { get; init; } = new(); + public DebugPaneSettings Debugging { get; init; } = new(); public int SelectedGame { - get { return (int)selectedGame; } - set { selectedGame = (Game)value; OnPropertyChanged(); } - } - - private string version = ""; + get; + set => this.RaiseAndSetIfChanged(ref field, value); + } = 4; // Defaults to index 4 (Baldur's Gate 3) - public string Version - { - get { return version; } - set { version = value; } - } + public string Version { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; public void SetPropertyChangedEvent(PropertyChangedEventHandler eventHandler) { - this.PropertyChanged += eventHandler; + PropertyChanged += eventHandler; GR2.PropertyChanged += eventHandler; PAK.PropertyChanged += eventHandler; Resources.PropertyChanged += eventHandler; + VirtualTextures.PropertyChanged += eventHandler; Story.PropertyChanged += eventHandler; - } - - public ConverterAppSettings() - { - GR2 = new GR2PaneSettings(); - PAK = new PackagePaneSettings(); - Resources = new ResourcePaneSettings(); - VirtualTextures = new VirtualTexturesPaneSettings(); - Story = new OsirisPaneSettings(); - Debugging = new DebugPaneSettings(); + Debugging.PropertyChanged += eventHandler; } } -public class GR2PaneSettings : SettingsBase +public class GR2PaneSettings : ReactiveObject { - private string inputPath = ""; - - public string InputPath - { - get { return inputPath; } - set { inputPath = value; OnPropertyChanged(); } - } - - private string outputPath = ""; - - public string OutputPath - { - get { return outputPath; } - set { outputPath = value; OnPropertyChanged(); } - } - - private string batchInputPath = ""; - - public string BatchInputPath - { - get { return batchInputPath; } - set { batchInputPath = value; OnPropertyChanged(); } - } - - private string batchOutputPath = ""; - - public string BatchOutputPath - { - get { return batchOutputPath; } - set { batchOutputPath = value; OnPropertyChanged(); } - } + public string InputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string OutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string BatchInputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string BatchOutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; - private ExportFormat batchInputFormat = ExportFormat.GR2; - - public int BatchInputFormat - { - get { return (int)batchInputFormat; } - set { batchInputFormat = (ExportFormat)value; OnPropertyChanged(); } - } - - private ExportFormat batchOutputFormat = ExportFormat.DAE; - - public int BatchOutputFormat + public LSLib.Granny.ExportFormat BatchInputFormat { - get { return (int)batchOutputFormat; } - set { batchOutputFormat = (ExportFormat)value; OnPropertyChanged(); } - } + get; + set => this.RaiseAndSetIfChanged(ref field, value); + } = LSLib.Granny.ExportFormat.GR2; - private string conformPath; - - public string ConformPath + public LSLib.Granny.ExportFormat BatchOutputFormat { - get { return conformPath; } - set { conformPath = value; OnPropertyChanged(); } - } + get; + set => this.RaiseAndSetIfChanged(ref field, value); + } = LSLib.Granny.ExportFormat.DAE; + public string ConformPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; } -public class PackagePaneSettings : SettingsBase +public class PackagePaneSettings : ReactiveObject { - private string extractInputPath = ""; - - public string ExtractInputPath - { - get { return extractInputPath; } - set { extractInputPath = value; OnPropertyChanged(); } - } - - private string extractOutputPath = ""; - - public string ExtractOutputPath - { - get { return extractOutputPath; } - set { extractOutputPath = value; OnPropertyChanged(); } - } - - private string createInputPath = ""; + public string ExtractInputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string ExtractOutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string CreateInputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string CreateOutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; - public string CreateInputPath - { - get { return createInputPath; } - set { createInputPath = value; OnPropertyChanged(); } - } - - private string createOutputPath = ""; - - public string CreateOutputPath - { - get { return createOutputPath; } - set { createOutputPath = value; OnPropertyChanged(); } - } - - private int createPackageVersion = 0; - - public int CreatePackageVersion - { - get { return createPackageVersion; } - set { createPackageVersion = value; OnPropertyChanged(); } - } - - private int createPackageCompression = 3; - - public int CreatePackageCompression - { - get { return createPackageCompression; } - set { createPackageCompression = value; OnPropertyChanged(); } - } + [JsonConverter(typeof(PackageVersionJsonConverter))] + public LSLib.LS.PackageVersion CreatePackageVersion { get; set => this.RaiseAndSetIfChanged(ref field, value); } - //public string BatchInputPath { get; set; } = ""; - //public string BatchOutputPath { get; set; } = ""; + [JsonConverter(typeof(CompressionMethodJsonConverter))] + public CompressionMethod CreatePackageCompression { get; set => this.RaiseAndSetIfChanged(ref field, value); } = CompressionMethod.LZ4; } -public class ResourcePaneSettings : SettingsBase +public class ResourcePaneSettings : ReactiveObject { - private string inputPath = ""; - - public string InputPath - { - get { return inputPath; } - set { inputPath = value; OnPropertyChanged(); } - } - - private string outputPath = ""; - - public string OutputPath - { - get { return outputPath; } - set { outputPath = value; OnPropertyChanged(); } - } - - private string batchInputPath = ""; - - public string BatchInputPath - { - get { return batchInputPath; } - set { batchInputPath = value; OnPropertyChanged(); } - } - - private string batchOutputPath = ""; - - public string BatchOutputPath - { - get { return batchOutputPath; } - set { batchOutputPath = value; OnPropertyChanged(); } - } - - private int batchInputFormat; - - public int BatchInputFormat - { - get { return batchInputFormat; } - set { batchInputFormat = value; OnPropertyChanged(); } - } - - private int batchOutputFormat; - - public int BatchOutputFormat - { - get { return batchOutputFormat; } - set { batchOutputFormat = value; OnPropertyChanged(); } - } + public string InputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string OutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string BatchInputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string BatchOutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public LSLib.Granny.ExportFormat BatchInputFormat { get; set => this.RaiseAndSetIfChanged(ref field, value); } + public LSLib.Granny.ExportFormat BatchOutputFormat { get; set => this.RaiseAndSetIfChanged(ref field, value); } } -public class VirtualTexturesPaneSettings : SettingsBase +public class VirtualTexturesPaneSettings : ReactiveObject { - private string gtsPath = ""; - - public string GTSPath - { - get { return gtsPath; } - set { gtsPath = value; OnPropertyChanged(); } - } - - private string destinationPath = ""; - - public string DestinationPath - { - get { return destinationPath; } - set { destinationPath = value; OnPropertyChanged(); } - } + public string GTSPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string DestinationPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; } -public class OsirisPaneSettings : SettingsBase +public class OsirisPaneSettings : ReactiveObject { - private string inputPath = ""; - - public string InputPath - { - get { return inputPath; } - set { inputPath = value; OnPropertyChanged(); } - } - - private string outputPath = ""; - - public string OutputPath - { - get { return outputPath; } - set { outputPath = value; OnPropertyChanged(); } - } - - private string filterText = ""; - - public string FilterText - { - get { return filterText; } - set { filterText = value; OnPropertyChanged(); } - } - - private bool filterMatchCase = false; - - public bool FilterMatchCase - { - get { return filterMatchCase; } - set { filterMatchCase = value; OnPropertyChanged(); } - } + public string InputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string OutputPath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public string FilterText { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; + public bool FilterMatchCase { get; set => this.RaiseAndSetIfChanged(ref field, value); } } -public class DebugPaneSettings : SettingsBase +public class DebugPaneSettings : ReactiveObject { - private string savePath = ""; - - public string SavePath - { - get { return savePath; } - set { savePath = value; OnPropertyChanged(); } - } + public string SavePath { get; set => this.RaiseAndSetIfChanged(ref field, value); } = string.Empty; } -sealed class PackageVersionConverter : TypeConverter +public sealed class PackageVersionJsonConverter : JsonConverter { - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override LSLib.LS.PackageVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return true; + int index = reader.GetInt32(); + return index switch + { + 2 => LSLib.LS.PackageVersion.V10, + 3 => LSLib.LS.PackageVersion.V9, + 4 => LSLib.LS.PackageVersion.V7, + _ => LSLib.LS.PackageVersion.V18 + }; } - public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + public override void Write(Utf8JsonWriter writer, LSLib.LS.PackageVersion value, JsonSerializerOptions options) { - if(value is PackageVersion version) + int outIndex = value switch { - switch (version) - { - case PackageVersion.V10: - { - return 2; - } - case PackageVersion.V9: - { - return 3; - } - case PackageVersion.V7: - { - return 4; - } - case PackageVersion.V13: - default: - { - return 0; - } - } - } - return 0; + LSLib.LS.PackageVersion.V10 => 2, + LSLib.LS.PackageVersion.V9 => 3, + LSLib.LS.PackageVersion.V7 => 4, + _ => 0 + }; + writer.WriteNumberValue(outIndex); } } -sealed class CompressionConverter : TypeConverter +public sealed class CompressionMethodJsonConverter : JsonConverter { - public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + public override CompressionMethod Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return true; + int index = reader.GetInt32(); + return index switch + { + 0 => CompressionMethod.None, + 1 => CompressionMethod.Zlib, + 3 => CompressionMethod.LZ4, + _ => CompressionMethod.LZ4 + }; } - public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + public override void Write(Utf8JsonWriter writer, CompressionMethod value, JsonSerializerOptions options) { - if (value is CompressionMethod compression) + int outIndex = value switch { - switch (compression) - { - case CompressionMethod.Zlib: - { - return 1; - } - case CompressionMethod.None: - { - return 0; - } - case CompressionMethod.LZ4: - default: - { - return 3; - } - } - } - return 0; + CompressionMethod.None => 0, + CompressionMethod.Zlib => 1, + CompressionMethod.LZ4 => 3, + _ => 3 + }; + writer.WriteNumberValue(outIndex); } } diff --git a/ConverterApp/DatabaseDumper.cs b/ConverterApp/DatabaseDumper.cs index 096ca76e..bb893c14 100644 --- a/ConverterApp/DatabaseDumper.cs +++ b/ConverterApp/DatabaseDumper.cs @@ -1,80 +1,122 @@ -using LSLib.LS.Story; -using System; -using System.IO; -using System.Linq; -using System.Text; +using System.Text; +using LSLib.LS.Story; -namespace ConverterApp; +namespace LSTools.DivineGUI; -class DatabaseDumper : IDisposable +public sealed class DatabaseDumper : IDisposable { - private StreamWriter Writer; + public bool DumpUnnamedDbs { get; set; } = false; - public bool DumpUnnamedDbs { get; set; } + private readonly StreamWriter _writer; public DatabaseDumper(Stream outputStream) { - Writer = new StreamWriter(outputStream, Encoding.UTF8); - DumpUnnamedDbs = false; + ArgumentNullException.ThrowIfNull(outputStream); + _writer = new StreamWriter(outputStream, Encoding.UTF8); } - public void Dispose() - { - Writer.Dispose(); - } - + public void Dispose() => _writer.Dispose(); + private void DumpFact(Story story, Fact fact) { - Writer.Write("("); - for (var i = 0; i < fact.Columns.Count; i++) + _writer.Write("("); + + int columnCount = fact.Columns.Count; + for (int i = 0; i < columnCount; i++) { - fact.Columns[i].DebugDump(Writer, story); - if (i + 1 < fact.Columns.Count) + fact.Columns[i].DebugDump(_writer, story); + if (i + 1 < columnCount) { - Writer.Write(", "); + _writer.Write(", "); } } - Writer.WriteLine(")"); + + _writer.Write(")\n"); } public void DumpDatabase(Story story, Database database) { - if (database.OwnerNode != null) + if (database.OwnerNode is { } node) { - if (database.OwnerNode.Name.Length > 0) + if (!string.IsNullOrEmpty(node.Name)) { - Writer.Write($"Database '{database.OwnerNode.Name}'"); + _writer.Write($"Database '{node.Name}'"); } else { - Writer.Write($"Database #{database.Index} <{database.OwnerNode.TypeName()}>"); + _writer.Write($"Database #{database.Index} <{node.TypeName()}>"); } } else { - Writer.Write($"Database #{database.Index}"); + _writer.Write($"Database #{database.Index}"); } - var types = String.Join(", ", database.Parameters.Types.Select(ty => story.Types[ty].Name)); - Writer.WriteLine($" ({types}):"); + var typeNamesList = new List(database.Parameters.Types.Count); + foreach (int typeId in database.Parameters.Types.Select(v => (int)v)) + { + if (story.Types.TryGetValue((uint)typeId, out var storyType)) + { + typeNamesList.Add(storyType.Name); + } + } + string typesString = string.Join(", ", typeNamesList); + _writer.Write($" ({typesString}):\n"); - foreach (var fact in database.Facts) + if (database.Facts is IEnumerable stronglyTypedFacts) { - Writer.Write("\t"); - DumpFact(story, fact); + foreach (Fact fact in stronglyTypedFacts) + { + _writer.Write("\t"); + DumpFact(story, fact); + } } } public void DumpAll(Story story) { - Writer.WriteLine(" === DUMP OF DATABASES === "); - foreach (var db in story.Databases) + ArgumentNullException.ThrowIfNull(story); + _writer.Write(" === DUMP OF DATABASES === \n"); + + foreach (KeyValuePair entry in story.Databases) { - if (DumpUnnamedDbs || (db.Value.OwnerNode != null && db.Value.OwnerNode.Name.Length > 0)) + Database db = entry.Value; + if (DumpUnnamedDbs || (db.OwnerNode is { } node && !string.IsNullOrEmpty(node.Name))) { - DumpDatabase(story, db.Value); - Writer.WriteLine(""); + DumpDatabase(story, db); + _writer.Write("\n"); } } } -} + + public List GenerateGridRows(Story story) + { + ArgumentNullException.ThrowIfNull(story); + var rowsCollection = new List(); + + foreach (KeyValuePair entry in story.Databases) + { + Database db = entry.Value; + + if (DumpUnnamedDbs || (db.OwnerNode is { } node && !string.IsNullOrEmpty(node.Name))) + { + if (db.Facts is IEnumerable stronglyTypedFacts) + { + foreach (Fact fact in stronglyTypedFacts) + { + var evaluatedDisplayValues = new string[fact.Columns.Count]; + for (int i = 0; i < fact.Columns.Count; i++) + { + using var stringWriter = new StringWriter(); + fact.Columns[i].DebugDump(stringWriter, story); + evaluatedDisplayValues[i] = stringWriter.ToString(); + } + rowsCollection.Add(new FactRowModel(fact, evaluatedDisplayValues)); + } + } + } + } + + return rowsCollection; + } +} \ No newline at end of file diff --git a/ConverterApp/DebugDumper.cs b/ConverterApp/DebugDumper.cs index df1d2ad8..9ce78505 100644 --- a/ConverterApp/DebugDumper.cs +++ b/ConverterApp/DebugDumper.cs @@ -1,174 +1,167 @@ -using LSLib.LS; -using LSLib.LS.Enums; -using LSLib.LS.Story; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Windows.Forms; +using System.Threading.Tasks; +using LSLib.LS; +using LSLib.LS.Enums; +using LSLib.LS.Resources.LSF; +using LSLib.LS.Story; +using LSLib.LS.Story.Compiler; // For handling the Diagnostic collection context -namespace ConverterApp; +namespace LSTools.DivineGUI; -public delegate void DumugDumperReportProgress(int percentage, string statusText); +public record DumperProgressEventArgs(int Percentage, string StatusText); public class DebugDumperTask { - private Package SavePackage; - private Resource SaveMeta; - private Resource SaveGlobals; - private Story SaveStory; + private Package? _savePackage; + private Resource? _saveMeta; + private Resource? _saveGlobals; + private Story? _saveStory; public Game GameVersion { get; set; } - public string SaveFilePath { get; set; } - public string ExtractionPath { get; set; } - public string DataDumpPath { get; set; } - - // General savegame dumping settings - public bool ExtractAll { get; set; } - public bool ConvertToLsx { get; set; } - public bool DumpModList { get; set; } - - // Behavior variable dumping settings - public bool DumpGlobalVars { get; set; } - public bool DumpCharacterVars { get; set; } - public bool DumpItemVars { get; set; } - public bool IncludeDeletedVars { get; set; } - public bool IncludeLocalScopes { get; set; } - - // Story dump settings - public bool DumpStoryDatabases { get; set; } - public bool DumpStoryGoals { get; set; } - public bool IncludeUnnamedDatabases { get; set; } - - public event DumugDumperReportProgress ReportProgress; - - public DebugDumperTask() - { - ExtractAll = true; - ConvertToLsx = true; - DumpModList = true; - - DumpGlobalVars = true; - DumpCharacterVars = true; - DumpItemVars = true; - IncludeDeletedVars = false; - IncludeLocalScopes = false; - // TODO ------------------ RE-IMPORTABLE VARS/DBS FORMAT ---------------------- - - DumpStoryDatabases = true; - DumpStoryGoals = true; - IncludeUnnamedDatabases = false; - } + public string SaveFilePath { get; set; } = string.Empty; + public string ExtractionPath { get; set; } = string.Empty; + public string DataDumpPath { get; set; } = string.Empty; + public bool ExtractAll { get; set; } = true; + public bool ConvertToLsx { get; set; } = true; + public bool DumpModList { get; set; } = true; + public bool DumpGlobalVars { get; set; } = true; + public bool DumpCharacterVars { get; set; } = true; + public bool DumpItemVars { get; set; } = true; + public bool IncludeDeletedVars { get; set; } = false; + public bool IncludeLocalScopes { get; set; } = false; + public bool DumpStoryDatabases { get; set; } = true; + public bool DumpStoryGoals { get; set; } = true; + public bool IncludeUnnamedDatabases { get; set; } = false; + + // TARGET EXTRACTION HOOK: Passes collected file logs, data validations, or errors back to the DataGrid collection + public List TaskDiagnostics { get; } = []; + + public event Action? ProgressUpdated; + + private void ReportProgress(int percentage, string statusText) => + ProgressUpdated?.Invoke(new DumperProgressEventArgs(percentage, statusText)); private void DoExtractPackage() { - var packager = new Packager(); - packager.ProgressUpdate = (file, numerator, denominator) => { - ReportProgress(5 + (int)(numerator * 15 / denominator), "Extracting: " + file); + ArgumentNullException.ThrowIfNull(_savePackage); + + var packager = new Packager + { + ProgressUpdate = (file, numerator, denominator) => + ReportProgress(5 + (int)(denominator == 0 ? 0 : numerator * 15 / denominator), $"Extracting: {file}") }; - packager.UncompressPackage(SavePackage, ExtractionPath); + + packager.UncompressPackage(_savePackage, ExtractionPath); + TaskDiagnostics.Add(new Diagnostic(null, MessageLevel.Info, "PAK01", $"Extracted complete payload successfully to {ExtractionPath}")); } private void DoLsxConversion() { + ArgumentNullException.ThrowIfNull(_savePackage); + var conversionParams = ResourceConversionParameters.FromGameVersion(GameVersion); var loadParams = ResourceLoadParameters.FromGameVersion(GameVersion); - var lsfList = SavePackage.Files.Where(p => p.Name.EndsWith(".lsf")); + var lsfList = _savePackage.Files.Where(p => p.Name.EndsWith(".lsf", StringComparison.OrdinalIgnoreCase)).ToList(); var numProcessed = 0; + foreach (var lsf in lsfList) { - var lsfPath = Path.Combine(ExtractionPath, lsf.Name); - var lsxPath = Path.Combine(ExtractionPath, lsf.Name.Substring(0, lsf.Name.Length - 4) + ".lsx"); + try + { + var lsfPath = Path.Combine(ExtractionPath, lsf.Name); + string baseName = lsf.Name.EndsWith(".lsf", StringComparison.OrdinalIgnoreCase) ? lsf.Name[..^4] : lsf.Name; + var lsxPath = Path.Combine(ExtractionPath, $"{baseName}.lsx"); - ReportProgress(20 + (numProcessed * 30 / lsfList.Count()), "Converting to LSX: " + lsf.Name); - var resource = ResourceUtils.LoadResource(lsfPath, ResourceFormat.LSF, loadParams); - ResourceUtils.SaveResource(resource, lsxPath, ResourceFormat.LSX, conversionParams); - numProcessed++; + int calculatedProgress = lsfList.Count == 0 ? 0 : numProcessed * 30 / lsfList.Count; + ReportProgress(20 + calculatedProgress, $"Converting to LSX: {lsf.Name}"); + + var resource = ResourceUtils.LoadResource(lsfPath, ResourceFormat.LSF, loadParams); + ResourceUtils.SaveResource(resource, lsxPath, ResourceFormat.LSX, conversionParams); + numProcessed++; + } + catch (Exception ex) + { + // Logs any localized serialization error cleanly into the grid display + TaskDiagnostics.Add(new Diagnostic(null, MessageLevel.Warning, "CONV02", $"Failed transforming '{lsf.Name}': {ex.Message}")); + } } } private Resource LoadPackagedResource(string path) { - var fileInfo = SavePackage.Files.FirstOrDefault(p => p.Name.ToLowerInvariant() == path); - if (fileInfo == null) - { - throw new ArgumentException($"Could not locate file in package: '{path}"); - } + ArgumentNullException.ThrowIfNull(_savePackage); - Resource resource; - using var rsrcStream = fileInfo.CreateContentReader(); - using (var rsrcReader = new LSFReader(rsrcStream)) - { - resource = rsrcReader.Read(); - } + var fileInfo = _savePackage.Files.FirstOrDefault(p => p.Name.Equals(path, StringComparison.OrdinalIgnoreCase)) + ?? throw new ArgumentException($"Could not locate file in package: '{path}'"); - return resource; + using var rsrcStream = fileInfo.CreateContentReader(); + using var rsrcReader = new LSFReader(rsrcStream); + return rsrcReader.Read(); } private void DumpMods(string outputPath) { - using (var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read)) - using (var writer = new StreamWriter(outputStream)) + ArgumentNullException.ThrowIfNull(_saveMeta); + + using var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read); + using var writer = new StreamWriter(outputStream); + + if (!_saveMeta.Regions.TryGetValue("MetaData", out var metaRegion)) return; + if (!metaRegion.Children.TryGetValue("MetaData", out var metaChildrenList) || metaChildrenList.Count == 0) return; + + var meta = metaChildrenList[0]; + if (!meta.Children.TryGetValue("ModuleSettings", out var settingsList) || settingsList.Count == 0) return; + if (!settingsList[0].Children.TryGetValue("Mods", out var modsList) || modsList.Count == 0) return; + if (!modsList[0].Children.TryGetValue("ModuleShortDesc", out var moduleDescs)) return; + + foreach (var modDesc in moduleDescs) { - var meta = SaveMeta.Regions["MetaData"].Children["MetaData"][0]; - var moduleDescs = meta.Children["ModuleSettings"][0].Children["Mods"][0].Children["ModuleShortDesc"]; - foreach (var modDesc in moduleDescs) - { - var folder = (string)modDesc.Attributes["Folder"].Value; - var name = (string)modDesc.Attributes["Name"].Value; - PackedVersion version; - if (modDesc.Attributes.ContainsKey("Version64")) - { - var versionNum = (Int64)modDesc.Attributes["Version64"].Value; - version = PackedVersion.FromInt64(versionNum); - } - else - { - var versionNum = (Int32)modDesc.Attributes["Version"].Value; - version = PackedVersion.FromInt32(versionNum); - } + string folder = modDesc.Attributes.TryGetValue("Folder", out var fAttr) && fAttr.Value is string folderName ? folderName : string.Empty; + string name = modDesc.Attributes.TryGetValue("Name", out var nAttr) && nAttr.Value is string modName ? modName : string.Empty; - writer.WriteLine($"{name} (v{version.Major}.{version.Minor}.{version.Revision}.{version.Build}) @ {folder}"); - } + var version = modDesc.Attributes.TryGetValue("Version64", out var v64) && v64.Value is long packed64 + ? PackedVersion.FromInt64(packed64) + : (modDesc.Attributes.TryGetValue("Version", out var v32) && v32.Value is int packed32 + ? PackedVersion.FromInt32(packed32) + : PackedVersion.FromInt32(0)); + + writer.WriteLine($"{name} (v{version.Major}.{version.Minor}.{version.Revision}.{version.Build}) @ {folder}"); } } private void DumpVariables(string outputPath, bool globals, bool characters, bool items) { - using (var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read)) - { - var varDumper = new VariableDumper(outputStream); - varDumper.IncludeDeletedVars = IncludeDeletedVars; - varDumper.IncludeLocalScopes = IncludeLocalScopes; - if (varDumper.Load(SaveGlobals)) - { - if (globals) - { - varDumper.DumpGlobals(); - } + ArgumentNullException.ThrowIfNull(_saveGlobals); - if (characters) - { - varDumper.DumpCharacters(); - } + using var outputStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.Read); + var varDumper = new VariableDumper(outputStream) + { + IncludeDeletedVars = IncludeDeletedVars, + IncludeLocalScopes = IncludeLocalScopes + }; - if (items) - { - varDumper.DumpItems(); - } - } + if (varDumper.Load(_saveGlobals)) + { + if (globals) varDumper.DumpGlobals(); + if (characters) varDumper.DumpCharacters(); + if (items) varDumper.DumpItems(); } } private void DumpGoals() { + ArgumentNullException.ThrowIfNull(_saveStory); + ReportProgress(80, "Dumping story ..."); string debugPath = Path.Combine(DataDumpPath, "GoalsDebug.log"); using (var debugFile = new FileStream(debugPath, FileMode.Create, FileAccess.Write)) using (var writer = new StreamWriter(debugFile)) { - SaveStory.DebugDump(writer); + _saveStory.DebugDump(writer); } ReportProgress(85, "Dumping story goals ..."); @@ -179,132 +172,139 @@ private void DumpGoals() using (var goalFile = new FileStream(unassignedPath, FileMode.Create, FileAccess.Write)) using (var writer = new StreamWriter(goalFile)) { - var dummyGoal = new Goal(SaveStory) + var dummyGoal = new Goal(_saveStory) { - ExitCalls = new List(), - InitCalls = new List(), - ParentGoals = new List(), - SubGoals = new List(), + ExitCalls = [], + InitCalls = [], + ParentGoals = [], + SubGoals = [], Name = "UNASSIGNED_RULES", Index = 0 }; - dummyGoal.MakeScript(writer, SaveStory); + dummyGoal.MakeScript(writer, _saveStory); } - foreach (KeyValuePair goal in SaveStory.Goals) + foreach (var goalEntry in _saveStory.Goals) { - string filePath = Path.Combine(goalsPath, $"{goal.Value.Name}.txt"); - using (var goalFile = new FileStream(filePath, FileMode.Create, FileAccess.Write)) - using (var writer = new StreamWriter(goalFile)) - { - goal.Value.MakeScript(writer, SaveStory); - } + var goal = goalEntry.Value; + string filePath = Path.Combine(goalsPath, $"{goal.Name}.txt"); + using var goalFile = new FileStream(filePath, FileMode.Create, FileAccess.Write); + using var writer = new StreamWriter(goalFile); + goal.MakeScript(writer, _saveStory); } } private void RunTasks() { - if (ExtractAll) - { - DoExtractPackage(); - } + ArgumentNullException.ThrowIfNull(_savePackage); - if (ConvertToLsx) - { - DoLsxConversion(); - } + if (ExtractAll) DoExtractPackage(); + if (ConvertToLsx) DoLsxConversion(); FileManager.TryToCreateDirectory(Path.Combine(DataDumpPath, "Dummy")); ReportProgress(50, "Loading meta.lsf ..."); - SaveMeta = LoadPackagedResource("meta.lsf"); + _saveMeta = LoadPackagedResource("meta.lsf"); ReportProgress(52, "Loading globals.lsf ..."); - SaveGlobals = LoadPackagedResource("globals.lsf"); + _saveGlobals = LoadPackagedResource("globals.lsf"); - ReportProgress(60, "Dumping mod list ..."); if (DumpModList) { - var modListPath = Path.Combine(DataDumpPath, "ModList.txt"); - DumpMods(modListPath); + ReportProgress(60, "Dumping mod list ..."); + DumpMods(Path.Combine(DataDumpPath, "ModList.txt")); } ReportProgress(62, "Dumping variables ..."); - if (DumpGlobalVars) - { - var varsPath = Path.Combine(DataDumpPath, "GlobalVars.txt"); - DumpVariables(varsPath, true, false, false); - } - - if (DumpCharacterVars) - { - var varsPath = Path.Combine(DataDumpPath, "CharacterVars.txt"); - DumpVariables(varsPath, false, true, false); - } - - if (DumpItemVars) - { - var varsPath = Path.Combine(DataDumpPath, "ItemVars.txt"); - DumpVariables(varsPath, false, false, true); - } + if (DumpGlobalVars) DumpVariables(Path.Combine(DataDumpPath, "GlobalVars.txt"), true, false, false); + if (DumpCharacterVars) DumpVariables(Path.Combine(DataDumpPath, "CharacterVars.txt"), false, true, false); + if (DumpItemVars) DumpVariables(Path.Combine(DataDumpPath, "ItemVars.txt"), false, false, true); ReportProgress(70, "Loading story ..."); - var storySave = SavePackage.Files.FirstOrDefault(p => p.Name == "StorySave.bin"); - Stream storyStream; + var storySave = _savePackage.Files.FirstOrDefault(p => p.Name.Equals("StorySave.bin", StringComparison.OrdinalIgnoreCase)); + + using var storyStream = new MemoryStream(); if (storySave != null) { - var bin = storySave.CreateContentReader(); - storyStream = new MemoryStream(); + using var bin = storySave.CreateContentReader(); bin.CopyTo(storyStream); - storyStream.Position = 0; } else { - LSLib.LS.Node storyNode = SaveGlobals.Regions["Story"].Children["Story"][0]; - storyStream = new MemoryStream(storyNode.Attributes["Story"].Value as byte[]); - } - - var reader = new StoryReader(); - SaveStory = reader.Read(storyStream); - - if (DumpStoryGoals) - { - DumpGoals(); - } - - if (DumpStoryDatabases) - { - ReportProgress(90, "Dumping databases ..."); - var dbDumpPath = Path.Combine(DataDumpPath, "Databases.txt"); - using (var dbDumpStream = new FileStream(dbDumpPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + if (_saveGlobals.Regions.TryGetValue("Story", out var storyRegion) && + storyRegion.Children.TryGetValue("Story", out var storyChildrenList) && +storyChildrenList.Count > 0) { - var dbDumper = new DatabaseDumper(dbDumpStream); - dbDumper.DumpUnnamedDbs = IncludeUnnamedDatabases; - dbDumper.DumpAll(SaveStory); + var storyNode = storyChildrenList[0]; + if (storyNode.Attributes.TryGetValue("Story", out var storyAttr) && storyAttr.Value is byte[] byteData) + { + storyStream.Write(byteData, 0, byteData.Length); + } + else + { + throw new InvalidDataException("Story element stream attribute could not be extracted or was not a valid byte array."); + } + } + else + { + throw new InvalidDataException("The specified globals resource does not contain an active Story save node tree."); } } - - ReportProgress(100, ""); + storyStream.Position = 0; + _saveStory = StoryReader.Read(storyStream); + if (DumpStoryGoals) DumpGoals(); + if (DumpStoryDatabases) DumpStoryDatabasesTask(); + ReportProgress(100, "Done"); + TaskDiagnostics.Add(new Diagnostic(null, MessageLevel.Info, "SUCCESS", "All requested data blocks dumped completely.")); } - - public void Run() + private void DumpStoryDatabasesTask() { - ReportProgress(0, "Reading package ..."); - - var packageReader = new PackageReader(); - using var savePackage = packageReader.Read(SaveFilePath); - - SavePackage = savePackage; - var abstractFileInfo = SavePackage.Files.FirstOrDefault(p => p.Name.ToLowerInvariant() == "globals.lsf"); - if (abstractFileInfo == null) + ArgumentNullException.ThrowIfNull(_saveStory); + ReportProgress(90, "Dumping databases ..."); + var dbDumpPath = Path.Combine(DataDumpPath, "Databases.txt"); + using var dbDumpStream = new FileStream(dbDumpPath, FileMode.Create, FileAccess.Write, FileShare.Read); + var dbDumper = new DatabaseDumper(dbDumpStream) + { + DumpUnnamedDbs = IncludeUnnamedDatabases + }; + dbDumper.DumpAll(_saveStory); + } + public async Task RunAsync() + { + TaskDiagnostics.Clear(); + if (string.IsNullOrWhiteSpace(SaveFilePath) || !File.Exists(SaveFilePath)) { - MessageBox.Show("The specified package is not a valid savegame (globals.lsf not found)", "Load Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + ReportProgress(0, "Execution Aborted: Source package target is blank or inaccessible."); + TaskDiagnostics.Add(new Diagnostic(null, MessageLevel.Error, "FILE01", "The specified archive path is null or target file does not exist.")); return; } - - RunTasks(); - SavePackage = null; - - MessageBox.Show($"Savegame dumped to {DataDumpPath}."); + ReportProgress(0, "Reading package metadata structure components..."); + await Task.Run(() => + { + var packageReader = new PackageReader(); + try + { + _savePackage = packageReader.Read(SaveFilePath); + bool hasGlobals = _savePackage.Files.Any(p => p.Name.Equals("globals.lsf", StringComparison.OrdinalIgnoreCase)); + if (!hasGlobals) + { + throw new InvalidDataException("The specified package is not a valid savegame (globals.lsf not found)."); + } + RunTasks(); + } + catch (Exception ex) + { + ReportProgress(0, $"Processing Interrupted: {ex.Message}"); + TaskDiagnostics.Add(new Diagnostic(null, MessageLevel.Error, "CRIT01", ex.Message)); + } + finally + { + _savePackage?.Dispose(); + _savePackage = null; + _saveMeta = null; + _saveGlobals = null; + _saveStory = null; + } + }); } -} +} \ No newline at end of file diff --git a/ConverterApp/DebugPane.Designer.cs b/ConverterApp/DebugPane.Designer.cs deleted file mode 100644 index 05283a2d..00000000 --- a/ConverterApp/DebugPane.Designer.cs +++ /dev/null @@ -1,315 +0,0 @@ -namespace ConverterApp -{ - partial class DebugPane - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.dumpVariablesBtn = new System.Windows.Forms.Button(); - this.saveFilePath = new System.Windows.Forms.TextBox(); - this.savePathLbl = new System.Windows.Forms.Label(); - this.saveFileBrowseBtn = new System.Windows.Forms.Button(); - this.savePathDlg = new System.Windows.Forms.OpenFileDialog(); - this.dumpGlobalVars = new System.Windows.Forms.CheckBox(); - this.variableDumperBox = new System.Windows.Forms.GroupBox(); - this.includeLocalScopes = new System.Windows.Forms.CheckBox(); - this.includeUnnamedDbs = new System.Windows.Forms.CheckBox(); - this.dumpDatabases = new System.Windows.Forms.CheckBox(); - this.includeDeleted = new System.Windows.Forms.CheckBox(); - this.dumpItemVars = new System.Windows.Forms.CheckBox(); - this.dumpCharacterVars = new System.Windows.Forms.CheckBox(); - this.lblProgressStatus = new System.Windows.Forms.Label(); - this.dumpProgressBar = new System.Windows.Forms.ProgressBar(); - this.lblProgressText = new System.Windows.Forms.Label(); - this.dumpGoals = new System.Windows.Forms.CheckBox(); - this.extractPackage = new System.Windows.Forms.CheckBox(); - this.exportModList = new System.Windows.Forms.CheckBox(); - this.convertLsf = new System.Windows.Forms.CheckBox(); - this.variableDumperBox.SuspendLayout(); - this.SuspendLayout(); - // - // dumpVariablesBtn - // - this.dumpVariablesBtn.Location = new System.Drawing.Point(234, 111); - this.dumpVariablesBtn.Name = "dumpVariablesBtn"; - this.dumpVariablesBtn.Size = new System.Drawing.Size(174, 23); - this.dumpVariablesBtn.TabIndex = 72; - this.dumpVariablesBtn.Text = "Dump savegame"; - this.dumpVariablesBtn.UseVisualStyleBackColor = true; - this.dumpVariablesBtn.Click += new System.EventHandler(this.dumpVariablesBtn_Click); - // - // saveFilePath - // - this.saveFilePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.saveFilePath.Location = new System.Drawing.Point(13, 29); - this.saveFilePath.Name = "saveFilePath"; - this.saveFilePath.Size = new System.Drawing.Size(777, 20); - this.saveFilePath.TabIndex = 74; - // - // savePathLbl - // - this.savePathLbl.AutoSize = true; - this.savePathLbl.Location = new System.Drawing.Point(10, 13); - this.savePathLbl.Name = "savePathLbl"; - this.savePathLbl.Size = new System.Drawing.Size(101, 13); - this.savePathLbl.TabIndex = 73; - this.savePathLbl.Text = "Savegame file path:"; - // - // saveFileBrowseBtn - // - this.saveFileBrowseBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.saveFileBrowseBtn.Location = new System.Drawing.Point(796, 28); - this.saveFileBrowseBtn.Name = "saveFileBrowseBtn"; - this.saveFileBrowseBtn.Size = new System.Drawing.Size(41, 22); - this.saveFileBrowseBtn.TabIndex = 75; - this.saveFileBrowseBtn.Text = "..."; - this.saveFileBrowseBtn.UseVisualStyleBackColor = true; - this.saveFileBrowseBtn.Click += new System.EventHandler(this.saveFileBrowseBtn_Click); - // - // savePathDlg - // - this.savePathDlg.Filter = "LS savegame files|*.lsv"; - // - // dumpGlobalVars - // - this.dumpGlobalVars.AutoSize = true; - this.dumpGlobalVars.Checked = true; - this.dumpGlobalVars.CheckState = System.Windows.Forms.CheckState.Checked; - this.dumpGlobalVars.Location = new System.Drawing.Point(16, 25); - this.dumpGlobalVars.Name = "dumpGlobalVars"; - this.dumpGlobalVars.Size = new System.Drawing.Size(133, 17); - this.dumpGlobalVars.TabIndex = 77; - this.dumpGlobalVars.Text = "Dump global variables "; - this.dumpGlobalVars.UseVisualStyleBackColor = true; - // - // variableDumperBox - // - this.variableDumperBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.variableDumperBox.Controls.Add(this.extractPackage); - this.variableDumperBox.Controls.Add(this.exportModList); - this.variableDumperBox.Controls.Add(this.convertLsf); - this.variableDumperBox.Controls.Add(this.dumpGoals); - this.variableDumperBox.Controls.Add(this.includeLocalScopes); - this.variableDumperBox.Controls.Add(this.includeUnnamedDbs); - this.variableDumperBox.Controls.Add(this.dumpDatabases); - this.variableDumperBox.Controls.Add(this.includeDeleted); - this.variableDumperBox.Controls.Add(this.dumpItemVars); - this.variableDumperBox.Controls.Add(this.dumpCharacterVars); - this.variableDumperBox.Controls.Add(this.dumpVariablesBtn); - this.variableDumperBox.Controls.Add(this.dumpGlobalVars); - this.variableDumperBox.Location = new System.Drawing.Point(13, 68); - this.variableDumperBox.Name = "variableDumperBox"; - this.variableDumperBox.Size = new System.Drawing.Size(824, 149); - this.variableDumperBox.TabIndex = 78; - this.variableDumperBox.TabStop = false; - this.variableDumperBox.Text = "Dump Settings"; - // - // includeLocalScopes - // - this.includeLocalScopes.AutoSize = true; - this.includeLocalScopes.Location = new System.Drawing.Point(16, 117); - this.includeLocalScopes.Name = "includeLocalScopes"; - this.includeLocalScopes.Size = new System.Drawing.Size(123, 17); - this.includeLocalScopes.TabIndex = 83; - this.includeLocalScopes.Text = "Include local scopes"; - this.includeLocalScopes.UseVisualStyleBackColor = true; - // - // includeUnnamedDbs - // - this.includeUnnamedDbs.AutoSize = true; - this.includeUnnamedDbs.Location = new System.Drawing.Point(235, 71); - this.includeUnnamedDbs.Name = "includeUnnamedDbs"; - this.includeUnnamedDbs.Size = new System.Drawing.Size(173, 17); - this.includeUnnamedDbs.TabIndex = 82; - this.includeUnnamedDbs.Text = "Include intermediate databases"; - this.includeUnnamedDbs.UseVisualStyleBackColor = true; - // - // dumpDatabases - // - this.dumpDatabases.AutoSize = true; - this.dumpDatabases.Checked = true; - this.dumpDatabases.CheckState = System.Windows.Forms.CheckState.Checked; - this.dumpDatabases.Location = new System.Drawing.Point(235, 48); - this.dumpDatabases.Name = "dumpDatabases"; - this.dumpDatabases.Size = new System.Drawing.Size(106, 17); - this.dumpDatabases.TabIndex = 81; - this.dumpDatabases.Text = "Dump databases"; - this.dumpDatabases.UseVisualStyleBackColor = true; - // - // includeDeleted - // - this.includeDeleted.AutoSize = true; - this.includeDeleted.Location = new System.Drawing.Point(16, 94); - this.includeDeleted.Name = "includeDeleted"; - this.includeDeleted.Size = new System.Drawing.Size(133, 17); - this.includeDeleted.TabIndex = 80; - this.includeDeleted.Text = "Include deleted entries"; - this.includeDeleted.UseVisualStyleBackColor = true; - // - // dumpItemVars - // - this.dumpItemVars.AutoSize = true; - this.dumpItemVars.Checked = true; - this.dumpItemVars.CheckState = System.Windows.Forms.CheckState.Checked; - this.dumpItemVars.Location = new System.Drawing.Point(16, 71); - this.dumpItemVars.Name = "dumpItemVars"; - this.dumpItemVars.Size = new System.Drawing.Size(124, 17); - this.dumpItemVars.TabIndex = 79; - this.dumpItemVars.Text = "Dump item variables "; - this.dumpItemVars.UseVisualStyleBackColor = true; - // - // dumpCharacterVars - // - this.dumpCharacterVars.AutoSize = true; - this.dumpCharacterVars.Checked = true; - this.dumpCharacterVars.CheckState = System.Windows.Forms.CheckState.Checked; - this.dumpCharacterVars.Location = new System.Drawing.Point(16, 48); - this.dumpCharacterVars.Name = "dumpCharacterVars"; - this.dumpCharacterVars.Size = new System.Drawing.Size(150, 17); - this.dumpCharacterVars.TabIndex = 78; - this.dumpCharacterVars.Text = "Dump character variables "; - this.dumpCharacterVars.UseVisualStyleBackColor = true; - // - // lblProgressStatus - // - this.lblProgressStatus.AutoSize = true; - this.lblProgressStatus.Location = new System.Drawing.Point(77, 231); - this.lblProgressStatus.Name = "lblProgressStatus"; - this.lblProgressStatus.Size = new System.Drawing.Size(0, 13); - this.lblProgressStatus.TabIndex = 81; - // - // dumpProgressBar - // - this.dumpProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.dumpProgressBar.Location = new System.Drawing.Point(13, 247); - this.dumpProgressBar.Name = "dumpProgressBar"; - this.dumpProgressBar.Size = new System.Drawing.Size(824, 23); - this.dumpProgressBar.TabIndex = 79; - // - // lblProgressText - // - this.lblProgressText.AutoSize = true; - this.lblProgressText.Location = new System.Drawing.Point(10, 231); - this.lblProgressText.Name = "lblProgressText"; - this.lblProgressText.Size = new System.Drawing.Size(51, 13); - this.lblProgressText.TabIndex = 80; - this.lblProgressText.Text = "Progress:"; - // - // dumpGoals - // - this.dumpGoals.AutoSize = true; - this.dumpGoals.Checked = true; - this.dumpGoals.CheckState = System.Windows.Forms.CheckState.Checked; - this.dumpGoals.Location = new System.Drawing.Point(235, 25); - this.dumpGoals.Name = "dumpGoals"; - this.dumpGoals.Size = new System.Drawing.Size(82, 17); - this.dumpGoals.TabIndex = 84; - this.dumpGoals.Text = "Dump goals"; - this.dumpGoals.UseVisualStyleBackColor = true; - // - // extractPackage - // - this.extractPackage.AutoSize = true; - this.extractPackage.Checked = true; - this.extractPackage.CheckState = System.Windows.Forms.CheckState.Checked; - this.extractPackage.Location = new System.Drawing.Point(443, 25); - this.extractPackage.Name = "extractPackage"; - this.extractPackage.Size = new System.Drawing.Size(111, 17); - this.extractPackage.TabIndex = 87; - this.extractPackage.Text = "Extract savegame"; - this.extractPackage.UseVisualStyleBackColor = true; - // - // exportModList - // - this.exportModList.AutoSize = true; - this.exportModList.Checked = true; - this.exportModList.CheckState = System.Windows.Forms.CheckState.Checked; - this.exportModList.Location = new System.Drawing.Point(443, 71); - this.exportModList.Name = "exportModList"; - this.exportModList.Size = new System.Drawing.Size(94, 17); - this.exportModList.TabIndex = 86; - this.exportModList.Text = "Export mod list"; - this.exportModList.UseVisualStyleBackColor = true; - // - // convertLsf - // - this.convertLsf.AutoSize = true; - this.convertLsf.Checked = true; - this.convertLsf.CheckState = System.Windows.Forms.CheckState.Checked; - this.convertLsf.Location = new System.Drawing.Point(443, 48); - this.convertLsf.Name = "convertLsf"; - this.convertLsf.Size = new System.Drawing.Size(125, 17); - this.convertLsf.TabIndex = 85; - this.convertLsf.Text = "Convert LSFs to LSX"; - this.convertLsf.UseVisualStyleBackColor = true; - // - // DebugPane - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.lblProgressStatus); - this.Controls.Add(this.dumpProgressBar); - this.Controls.Add(this.lblProgressText); - this.Controls.Add(this.variableDumperBox); - this.Controls.Add(this.saveFilePath); - this.Controls.Add(this.savePathLbl); - this.Controls.Add(this.saveFileBrowseBtn); - this.Name = "DebugPane"; - this.Size = new System.Drawing.Size(856, 475); - this.variableDumperBox.ResumeLayout(false); - this.variableDumperBox.PerformLayout(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button dumpVariablesBtn; - private System.Windows.Forms.TextBox saveFilePath; - private System.Windows.Forms.Label savePathLbl; - private System.Windows.Forms.Button saveFileBrowseBtn; - private System.Windows.Forms.OpenFileDialog savePathDlg; - private System.Windows.Forms.CheckBox dumpGlobalVars; - private System.Windows.Forms.GroupBox variableDumperBox; - private System.Windows.Forms.CheckBox includeUnnamedDbs; - private System.Windows.Forms.CheckBox dumpDatabases; - private System.Windows.Forms.CheckBox includeDeleted; - private System.Windows.Forms.CheckBox dumpItemVars; - private System.Windows.Forms.CheckBox dumpCharacterVars; - private System.Windows.Forms.CheckBox includeLocalScopes; - private System.Windows.Forms.Label lblProgressStatus; - private System.Windows.Forms.ProgressBar dumpProgressBar; - private System.Windows.Forms.Label lblProgressText; - private System.Windows.Forms.CheckBox extractPackage; - private System.Windows.Forms.CheckBox exportModList; - private System.Windows.Forms.CheckBox convertLsf; - private System.Windows.Forms.CheckBox dumpGoals; - } -} diff --git a/ConverterApp/DebugPane.axaml b/ConverterApp/DebugPane.axaml new file mode 100644 index 00000000..2856506c --- /dev/null +++ b/ConverterApp/DebugPane.axaml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ConverterApp/LocalizationPane.axaml.cs b/ConverterApp/LocalizationPane.axaml.cs new file mode 100644 index 00000000..93c5a65d --- /dev/null +++ b/ConverterApp/LocalizationPane.axaml.cs @@ -0,0 +1,157 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Platform.Storage; +using Avalonia.Threading; // Required for safe UI thread updates +using LSLib.LS; +using LSLib.LS.Enums; +using ReactiveUI; +using ReactiveUI.Avalonia; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; // Required for ObservableCollection +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Threading.Tasks; + +namespace LSTools.DivineGUI; + +public class LocalizationEntryItem +{ + public string Key { get; set; } = string.Empty; + public string Version { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; +} + +public partial class LocalizationPane : ReactiveUserControl, IGameSettingsTarget, INotifyPropertyChanged +{ + private Game _targetGameVersion = Game.BaldursGate3; + + public ObservableCollection LocalizationEntries { get; } = []; + + public string LocaInputPath { get; set { if (field != value) { field = value; InvokeLocalProperty(nameof(LocaInputPath)); } } } = string.Empty; + public string LocaOutputPath { get; set { if (field != value) { field = value; InvokeLocalProperty(nameof(LocaOutputPath)); } } } = string.Empty; + + public string ProgressText { get; set { if (field != value) { field = value; InvokeLocalProperty(nameof(ProgressText)); } } } = string.Empty; + + public ReactiveCommand BrowseInputLocationCommand { get; } + public ReactiveCommand BrowseOutputLocationCommand { get; } + public ReactiveCommand ConvertLocalizationCommand { get; } + + private static readonly FilePickerFileType LocaFileTypes = new("Localization files") + { + Patterns = ["*.loca", "*.xml"] + }; + + public LocalizationPane() + { + InitializeComponent(); + DataContext = this; + ViewModel = this; + + BrowseInputLocationCommand = ReactiveCommand.CreateFromTask(BrowseInputLocationAsync); + BrowseOutputLocationCommand = ReactiveCommand.CreateFromTask(BrowseOutputLocationAsync); + ConvertLocalizationCommand = ReactiveCommand.CreateFromTask(ExecuteLocalizationConversionAsync); + } + + private void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + public void SetGame(Game game) + { + _targetGameVersion = game; + } + + private async Task BrowseInputLocationAsync() + { + var provider = TopLevel.GetTopLevel(this)?.StorageProvider; + if (provider is null) return; + + var files = await provider.OpenFilePickerAsync(new() + { + Title = "Select Input File", + AllowMultiple = false, + FileTypeFilter = [LocaFileTypes] + }); + + if (files.Count > 0) LocaInputPath = files[0].Path.LocalPath; + } + + private async Task BrowseOutputLocationAsync() + { + var provider = TopLevel.GetTopLevel(this)?.StorageProvider; + if (provider is null) return; + + var file = await provider.SaveFilePickerAsync(new() + { + Title = "Select Output File", + FileTypeChoices = [LocaFileTypes], + DefaultExtension = "loca" + }); + + if (file is not null) LocaOutputPath = file.Path.LocalPath; + } + + private async Task ExecuteLocalizationConversionAsync() + { + if (string.IsNullOrWhiteSpace(LocaInputPath) || !File.Exists(LocaInputPath)) return; + if (string.IsNullOrWhiteSpace(LocaOutputPath)) return; + + ProgressText = "Parsing and translating localization parameters..."; + Dispatcher.UIThread.Post(() => LocalizationEntries.Clear()); + + try + { + string input = LocaInputPath; + string output = LocaOutputPath; + + await Task.Run(() => + { + var resource = LocaUtils.Load(input); + var format = LocaUtils.ExtensionToFileFormat(output); + LocaUtils.Save(resource, output, format); + + if (resource != null) + { + var parsedGridItems = new List(); + + foreach (var entry in resource.Entries) + { + parsedGridItems.Add(new LocalizationEntryItem + { + Key = entry.Key ?? string.Empty, + Version = entry.Version.ToString(), + Value = entry.Text ?? string.Empty + }); + } + Dispatcher.UIThread.Post(() => + { + foreach (var item in parsedGridItems) + { + LocalizationEntries.Add(item); + } + }); + } + }); + + ProgressText = "Localization file converted and saved successfully."; + } + catch (Exception exc) + { + ProgressText = $"Conversion Failed!\n\nInternal error:\n{exc.Message}"; + } + } + + private PropertyChangedEventHandler? _localPropertyChanged; + + event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged + { + add => _localPropertyChanged += value; + remove => _localPropertyChanged -= value; + } + + private void InvokeLocalProperty(string propertyName) + { + _localPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/ConverterApp/LocalizationPane.cs b/ConverterApp/LocalizationPane.cs deleted file mode 100644 index ce72948a..00000000 --- a/ConverterApp/LocalizationPane.cs +++ /dev/null @@ -1,55 +0,0 @@ -using LSLib.LS.Enums; -using LSLib.LS; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace ConverterApp -{ - public partial class LocalizationPane : UserControl - { - public LocalizationPane() - { - InitializeComponent(); - } - - private void resourceInputBrowseBtn_Click(object sender, EventArgs e) - { - if (locaInputFileDlg.ShowDialog(this) == DialogResult.OK) - { - locaInputPath.Text = locaInputFileDlg.FileName; - } - } - - private void locaOutputBrowseBtn_Click(object sender, EventArgs e) - { - if (locaOutputFileDlg.ShowDialog(this) == DialogResult.OK) - { - locaOutputPath.Text = locaOutputFileDlg.FileName; - } - } - - private void locaConvertBtn_Click(object sender, EventArgs e) - { - try - { - var resource = LocaUtils.Load(locaInputPath.Text); - var format = LocaUtils.ExtensionToFileFormat(locaOutputPath.Text); - LocaUtils.Save(resource, locaOutputPath.Text, format); - - MessageBox.Show("Localization file saved successfully."); - } - catch (Exception exc) - { - MessageBox.Show($"Internal error!{Environment.NewLine}{Environment.NewLine}{exc}", "Conversion Failed", MessageBoxButtons.OK, MessageBoxIcon.Error); - - } - } - } -} diff --git a/ConverterApp/LocalizationPane.resx b/ConverterApp/LocalizationPane.resx deleted file mode 100644 index 41c8e0bf..00000000 --- a/ConverterApp/LocalizationPane.resx +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 572, 59 - - - 770, 54 - - \ No newline at end of file diff --git a/ConverterApp/MainForm.Designer.cs b/ConverterApp/MainForm.Designer.cs deleted file mode 100644 index 6d09e9af..00000000 --- a/ConverterApp/MainForm.Designer.cs +++ /dev/null @@ -1,194 +0,0 @@ -namespace ConverterApp -{ - sealed partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.tabControl = new System.Windows.Forms.TabControl(); - this.gr2Tab = new System.Windows.Forms.TabPage(); - this.packageTab = new System.Windows.Forms.TabPage(); - this.resourceTab = new System.Windows.Forms.TabPage(); - this.virtualTextureTab = new System.Windows.Forms.TabPage(); - this.osirisTab = new System.Windows.Forms.TabPage(); - this.debugTab = new System.Windows.Forms.TabPage(); - this.gr2Game = new System.Windows.Forms.ComboBox(); - this.label7 = new System.Windows.Forms.Label(); - this.locaTab = new System.Windows.Forms.TabPage(); - this.tabControl.SuspendLayout(); - this.SuspendLayout(); - // - // tabControl - // - this.tabControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.tabControl.Controls.Add(this.gr2Tab); - this.tabControl.Controls.Add(this.packageTab); - this.tabControl.Controls.Add(this.resourceTab); - this.tabControl.Controls.Add(this.virtualTextureTab); - this.tabControl.Controls.Add(this.locaTab); - this.tabControl.Controls.Add(this.osirisTab); - this.tabControl.Controls.Add(this.debugTab); - this.tabControl.Location = new System.Drawing.Point(16, 52); - this.tabControl.Margin = new System.Windows.Forms.Padding(4); - this.tabControl.Name = "tabControl"; - this.tabControl.SelectedIndex = 0; - this.tabControl.Size = new System.Drawing.Size(1223, 763); - this.tabControl.TabIndex = 0; - // - // gr2Tab - // - this.gr2Tab.Location = new System.Drawing.Point(4, 25); - this.gr2Tab.Margin = new System.Windows.Forms.Padding(4); - this.gr2Tab.Name = "gr2Tab"; - this.gr2Tab.Padding = new System.Windows.Forms.Padding(4); - this.gr2Tab.Size = new System.Drawing.Size(1215, 734); - this.gr2Tab.TabIndex = 0; - this.gr2Tab.Text = "GR2 Tools"; - this.gr2Tab.UseVisualStyleBackColor = true; - // - // packageTab - // - this.packageTab.Location = new System.Drawing.Point(4, 25); - this.packageTab.Margin = new System.Windows.Forms.Padding(4); - this.packageTab.Name = "packageTab"; - this.packageTab.Padding = new System.Windows.Forms.Padding(4); - this.packageTab.Size = new System.Drawing.Size(1215, 734); - this.packageTab.TabIndex = 1; - this.packageTab.Text = "PAK / LSV Tools"; - this.packageTab.UseVisualStyleBackColor = true; - // - // resourceTab - // - this.resourceTab.Location = new System.Drawing.Point(4, 25); - this.resourceTab.Margin = new System.Windows.Forms.Padding(4); - this.resourceTab.Name = "resourceTab"; - this.resourceTab.Padding = new System.Windows.Forms.Padding(4); - this.resourceTab.Size = new System.Drawing.Size(1215, 734); - this.resourceTab.TabIndex = 2; - this.resourceTab.Text = "LSX / LSB / LSF / LSJ Tools"; - this.resourceTab.UseVisualStyleBackColor = true; - // - // virtualTextureTab - // - this.virtualTextureTab.Location = new System.Drawing.Point(4, 25); - this.virtualTextureTab.Name = "virtualTextureTab"; - this.virtualTextureTab.Padding = new System.Windows.Forms.Padding(3); - this.virtualTextureTab.Size = new System.Drawing.Size(1215, 734); - this.virtualTextureTab.TabIndex = 5; - this.virtualTextureTab.Text = "Virtual Textures"; - this.virtualTextureTab.UseVisualStyleBackColor = true; - // - // osirisTab - // - this.osirisTab.Location = new System.Drawing.Point(4, 25); - this.osirisTab.Margin = new System.Windows.Forms.Padding(4); - this.osirisTab.Name = "osirisTab"; - this.osirisTab.Padding = new System.Windows.Forms.Padding(4); - this.osirisTab.Size = new System.Drawing.Size(1215, 734); - this.osirisTab.TabIndex = 3; - this.osirisTab.Text = "Story (OSI) tools"; - this.osirisTab.UseVisualStyleBackColor = true; - // - // debugTab - // - this.debugTab.Location = new System.Drawing.Point(4, 25); - this.debugTab.Margin = new System.Windows.Forms.Padding(4); - this.debugTab.Name = "debugTab"; - this.debugTab.Size = new System.Drawing.Size(1215, 734); - this.debugTab.TabIndex = 4; - this.debugTab.Text = "Savegame Debugging"; - this.debugTab.UseVisualStyleBackColor = true; - // - // gr2Game - // - this.gr2Game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.gr2Game.FormattingEnabled = true; - this.gr2Game.Items.AddRange(new object[] { - "Divinity: Original Sin (32-bit)", - "Divinity: Original Sin EE (64-bit)", - "Divinity: Original Sin 2 (64-bit)", - "Divinity: Original Sin 2 DE (64-bit)", - "Baldur\'s Gate 3 (64-bit)"}); - this.gr2Game.Location = new System.Drawing.Point(99, 15); - this.gr2Game.Margin = new System.Windows.Forms.Padding(4); - this.gr2Game.Name = "gr2Game"; - this.gr2Game.Size = new System.Drawing.Size(473, 24); - this.gr2Game.TabIndex = 30; - this.gr2Game.SelectedIndexChanged += new System.EventHandler(this.gr2Game_SelectedIndexChanged); - // - // label7 - // - this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(16, 17); - this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(47, 16); - this.label7.TabIndex = 29; - this.label7.Text = "Game:"; - // - // locaTab - // - this.locaTab.Location = new System.Drawing.Point(4, 25); - this.locaTab.Name = "locaTab"; - this.locaTab.Padding = new System.Windows.Forms.Padding(3); - this.locaTab.Size = new System.Drawing.Size(1215, 734); - this.locaTab.TabIndex = 6; - this.locaTab.Text = "Localization"; - this.locaTab.UseVisualStyleBackColor = true; - // - // MainForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1255, 826); - this.Controls.Add(this.gr2Game); - this.Controls.Add(this.label7); - this.Controls.Add(this.tabControl); - this.Margin = new System.Windows.Forms.Padding(4); - this.Name = "MainForm"; - this.Text = "LSLib Toolkit"; - this.tabControl.ResumeLayout(false); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - private System.Windows.Forms.TabControl tabControl; - private System.Windows.Forms.TabPage gr2Tab; - private System.Windows.Forms.TabPage packageTab; - private System.Windows.Forms.TabPage resourceTab; - private System.Windows.Forms.TabPage virtualTextureTab; - private System.Windows.Forms.TabPage osirisTab; - private System.Windows.Forms.ComboBox gr2Game; - private System.Windows.Forms.Label label7; - private System.Windows.Forms.TabPage debugTab; - private System.Windows.Forms.TabPage locaTab; - } -} - diff --git a/ConverterApp/MainForm.cs b/ConverterApp/MainForm.cs deleted file mode 100644 index fd173ebd..00000000 --- a/ConverterApp/MainForm.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Windows.Forms; -using LSLib.LS; -using LSLib.LS.Enums; -using Newtonsoft.Json; -using System.ComponentModel; -using System.IO; - -namespace ConverterApp -{ - public sealed partial class MainForm : Form, ISettingsDataSource - { - PackagePane packagePane; - ResourcePane resourcePane; - VirtualTexturesPane virtualTexturesPane; - LocalizationPane localizationPane; - OsirisPane osirisPane; - DebugPane debugPane; - - public ConverterAppSettings Settings { get; set; } - - public MainForm() - { - InitializeComponent(); - - Settings = new ConverterAppSettings(); - - try - { - if (File.Exists("settings.json")) - { - using (System.IO.StreamReader file = File.OpenText("settings.json")) - { - JsonSerializer serializer = new JsonSerializer(); - Settings = (ConverterAppSettings)serializer.Deserialize(file, typeof(ConverterAppSettings)); - } - } - } - catch (Exception ex) - { - MessageBox.Show($"Error reading settings: {ex.ToString()}"); - } - - var gr2Pane = new GR2Pane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = gr2Tab.ClientSize - }; - gr2Tab.Controls.Add(gr2Pane); - - packagePane = new PackagePane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = packageTab.ClientSize - }; - packageTab.Controls.Add(packagePane); - - resourcePane = new ResourcePane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = resourceTab.ClientSize - }; - resourceTab.Controls.Add(resourcePane); - - virtualTexturesPane = new VirtualTexturesPane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = resourceTab.ClientSize - }; - virtualTextureTab.Controls.Add(virtualTexturesPane); - - localizationPane = new LocalizationPane() - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = resourceTab.ClientSize - }; - locaTab.Controls.Add(localizationPane); - - osirisPane = new OsirisPane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = osirisTab.ClientSize - }; - osirisTab.Controls.Add(osirisPane); - - debugPane = new DebugPane(this) - { - Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right, - Size = debugTab.ClientSize - }; - debugTab.Controls.Add(debugPane); - - Text += $" (LSLib v{Common.LibraryVersion()})"; - - gr2Game.SelectedIndex = gr2Game.Items.Count - 1; - gr2Game.DataBindings.Add("SelectedIndex", Settings, "SelectedGame", true, DataSourceUpdateMode.OnPropertyChanged); - - Settings.Version = Common.LibraryVersion(); - Settings.SetPropertyChangedEvent(SaveSettings); - } - - private void SaveSettings(object sender, PropertyChangedEventArgs e) - { - try - { - File.WriteAllText("settings.json", JsonConvert.SerializeObject(Settings, Formatting.Indented)); - } - catch (Exception ex) - { - MessageBox.Show($"Error saving settings: {ex.ToString()}"); - } - } - - public Game GetGame() - { - switch (gr2Game.SelectedIndex) - { - case 0: return Game.DivinityOriginalSin; - case 1: return Game.DivinityOriginalSinEE; - case 2: return Game.DivinityOriginalSin2; - case 3: return Game.DivinityOriginalSin2DE; - case 4: return Game.BaldursGate3; - default: throw new InvalidOperationException(); - } - } - - private void gr2Game_SelectedIndexChanged(object sender, EventArgs e) - { - Game game = GetGame(); - if (gr2Tab.Controls[0] is GR2Pane pane) - { - pane.flipMeshes.Checked = game.IsFW3(); - } - - packagePane.SetGame(game); - osirisPane.Game = game; - debugPane.Game = game; - } - } -} diff --git a/ConverterApp/MainForm.resx b/ConverterApp/MainForm.resx deleted file mode 100644 index 1af7de15..00000000 --- a/ConverterApp/MainForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/ConverterApp/MainWindow.axaml b/ConverterApp/MainWindow.axaml new file mode 100644 index 00000000..75ffaf24 --- /dev/null +++ b/ConverterApp/MainWindow.axaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ConverterApp/MainWindow.axaml.cs b/ConverterApp/MainWindow.axaml.cs new file mode 100644 index 00000000..1e065314 --- /dev/null +++ b/ConverterApp/MainWindow.axaml.cs @@ -0,0 +1,165 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using LSLib.LS; +using LSLib.LS.Enums; +using ReactiveUI; +using ReactiveUI.Avalonia; +using System.ComponentModel; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace LSTools.DivineGUI; + +public partial class MainWindow : ReactiveWindow, INotifyPropertyChanged +{ + public ConverterAppSettings Settings + { + get; + set + { + if (field != value) + { + field = value; + InvokeLocalProperty(nameof(Settings)); + } + } + } = null!; + + public string AppTitle + { + get; + set { if (field != value) { field = value; InvokeLocalProperty(nameof(AppTitle)); } } + } = "LSLib Toolkit"; + + public string GlobalAlertLog + { + get; + set { if (field != value) { field = value; InvokeLocalProperty(nameof(GlobalAlertLog)); } } + } = string.Empty; + + public string[] DynamicGameOptions { get; } = + [ + "Divinity: Original Sin (32-bit)", + "Divinity: Original Sin EE (64-bit)", + "Divinity: Original Sin 2 (64-bit)", + "Divinity: Original Sin 2 DE (64-bit)", + "Baldur's Gate 3 (64-bit)" + ]; + + public ReactiveCommand GameSelectionChangedCommand { get; } + + public MainWindow() + { + InitializeComponent(); + + GameSelectionChangedCommand = ReactiveCommand.Create(targetIndex => + { + if (Settings == null) return; + Settings.SelectedGame = targetIndex; + + Game mappedGame = (Game)targetIndex; + + var gr2Control = this.Find("gr2PaneControl"); + var packageControl = this.Find("packagePaneControl"); + var osirisControl = this.Find("osirisPaneControl"); + var debugControl = this.Find("debugPaneControl"); + + if (gr2Control is IGameSettingsTarget gr2Target) gr2Target.SetGame(mappedGame); + if (packageControl is IGameSettingsTarget packageTarget) packageTarget.SetGame(mappedGame); + if (osirisControl is IGameSettingsTarget osirisTarget) osirisTarget.SetGame(mappedGame); + if (debugControl is IGameSettingsTarget debugTarget) debugTarget.SetGame(mappedGame); + + gr2Control?.FlipMeshes = mappedGame.IsFW3(); + }); + } + + public MainWindow(ISettingsDataSource settingsDataSource) : this() + { + ArgumentNullException.ThrowIfNull(settingsDataSource); + Settings = settingsDataSource.Settings; + ViewModel = this; + + LoadSettingsProfile(); + + AppTitle = $"LSLib Toolkit (LSLib v{Common.LibraryVersion()})"; + Settings.Version = Common.LibraryVersion(); + + Settings.SetPropertyChangedEvent(OnSettingsProfileMutated); + + Settings.PropertyChanged += (sender, e) => + { + if (e.PropertyName == nameof(ConverterAppSettings.SelectedGame)) + { + RxSchedulers.MainThreadScheduler.Schedule(() => + { + GameSelectionChangedCommand.Execute(Settings.SelectedGame).Subscribe(); + }); + } + }; + + GameSelectionChangedCommand.Execute(Settings.SelectedGame).Subscribe(); + } + + private void InitializeComponent() => AvaloniaXamlLoader.Load(this); + + private void LoadSettingsProfile() + { + try + { + string profilePath = Path.Combine(AppContext.BaseDirectory, "settings.json"); + if (File.Exists(profilePath)) + { + byte[] jsonBytes = File.ReadAllBytes(profilePath); + + var context = new AppSettingsJsonContext(); + var resolvedProfile = JsonSerializer.Deserialize(jsonBytes, context.ConverterAppSettings); + if (resolvedProfile != null) Settings = resolvedProfile; + } + } + catch (Exception ex) + { + GlobalAlertLog = $"Failed to restore settings configuration: {ex.Message}"; + } + } + + private void OnSettingsProfileMutated(object? sender, PropertyChangedEventArgs e) + { + Task.Run(() => + { + try + { + string profilePath = Path.Combine(AppContext.BaseDirectory, "settings.json"); + var context = new AppSettingsJsonContext(); + + byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(Settings, context.ConverterAppSettings); + File.WriteAllBytes(profilePath, jsonBytes); + } + catch (Exception ex) + { + Dispatcher.UIThread.Post(() => + { + GlobalAlertLog = $"Failed to serialize configuration properties to file: {ex.Message}"; + }); + } + }); + } + + private PropertyChangedEventHandler? _localPropertyChanged; + + public new event PropertyChangedEventHandler? PropertyChanged + { + add => _localPropertyChanged += value; + remove => _localPropertyChanged -= value; + } + + private void InvokeLocalProperty(string propertyName) + { + _localPropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} + +[JsonSerializable(typeof(ConverterAppSettings))] +internal partial class AppSettingsJsonContext : JsonSerializerContext; \ No newline at end of file diff --git a/ConverterApp/OsirisPane.Designer.cs b/ConverterApp/OsirisPane.Designer.cs deleted file mode 100644 index a6152cf3..00000000 --- a/ConverterApp/OsirisPane.Designer.cs +++ /dev/null @@ -1,287 +0,0 @@ -namespace ConverterApp -{ - partial class OsirisPane - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.saveStoryBtn = new System.Windows.Forms.Button(); - this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.databaseGrid = new System.Windows.Forms.DataGridView(); - this.databaseSelectorCb = new System.Windows.Forms.ComboBox(); - this.label18 = new System.Windows.Forms.Label(); - this.loadStoryBtn = new System.Windows.Forms.Button(); - this.decompileStoryBtn = new System.Windows.Forms.Button(); - this.storyFilePath = new System.Windows.Forms.TextBox(); - this.goalPathBrowseBtn = new System.Windows.Forms.Button(); - this.label9 = new System.Windows.Forms.Label(); - this.goalPath = new System.Windows.Forms.TextBox(); - this.storyFileBrowseBtn = new System.Windows.Forms.Button(); - this.label10 = new System.Windows.Forms.Label(); - this.storyPathDlg = new System.Windows.Forms.OpenFileDialog(); - this.goalPathDlg = new System.Windows.Forms.FolderBrowserDialog(); - this.btnDebugExport = new System.Windows.Forms.Button(); - this.lblFilter = new System.Windows.Forms.Label(); - this.tbFilter = new System.Windows.Forms.TextBox(); - this.btnFilterMatchCase = new System.Windows.Forms.Button(); - this.groupBox3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.databaseGrid)).BeginInit(); - this.SuspendLayout(); - // - // saveStoryBtn - // - this.saveStoryBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.saveStoryBtn.Location = new System.Drawing.Point(728, 50); - this.saveStoryBtn.Name = "saveStoryBtn"; - this.saveStoryBtn.Size = new System.Drawing.Size(121, 23); - this.saveStoryBtn.TabIndex = 69; - this.saveStoryBtn.Text = "Save"; - this.saveStoryBtn.UseVisualStyleBackColor = true; - this.saveStoryBtn.Click += new System.EventHandler(this.saveStoryBtn_Click); - // - // groupBox3 - // - this.groupBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.groupBox3.Controls.Add(this.databaseGrid); - this.groupBox3.Controls.Add(this.databaseSelectorCb); - this.groupBox3.Controls.Add(this.label18); - this.groupBox3.Controls.Add(this.lblFilter); - this.groupBox3.Controls.Add(this.tbFilter); - this.groupBox3.Controls.Add(this.btnFilterMatchCase); - this.groupBox3.Location = new System.Drawing.Point(5, 147); - this.groupBox3.Name = "groupBox3"; - this.groupBox3.Size = new System.Drawing.Size(849, 441); - this.groupBox3.TabIndex = 68; - this.groupBox3.TabStop = false; - this.groupBox3.Text = "Database Editor"; - // - // lblFilter - // - this.lblFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.lblFilter.AutoSize = true; - this.lblFilter.Location = new System.Drawing.Point(554, 22); - this.lblFilter.Name = "lblFilter"; - this.lblFilter.Size = new System.Drawing.Size(32, 13); - this.lblFilter.TabIndex = 4; - this.lblFilter.Text = "Filter:"; - // - // tbFilter - // - this.tbFilter.AcceptsReturn = true; - this.tbFilter.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.tbFilter.Location = new System.Drawing.Point(594, 18); - this.tbFilter.Name = "tbFilter"; - this.tbFilter.Size = new System.Drawing.Size(197, 20); - this.tbFilter.TabIndex = 3; - this.tbFilter.KeyUp += new System.Windows.Forms.KeyEventHandler(this.databaseFilter_KeyUp); - // - // btnFilterMatchCase - // - this.btnFilterMatchCase.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnFilterMatchCase.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnFilterMatchCase.Location = new System.Drawing.Point(797, 17); - this.btnFilterMatchCase.Name = "btnFilterMatchCase"; - this.btnFilterMatchCase.Size = new System.Drawing.Size(41, 22); - this.btnFilterMatchCase.TabIndex = 5; - this.btnFilterMatchCase.Text = "Aa"; - this.btnFilterMatchCase.UseVisualStyleBackColor = true; - this.btnFilterMatchCase.Click += new System.EventHandler(this.btnDatabaseFilterMatchCase_Click); - // - // databaseGrid - // - this.databaseGrid.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.databaseGrid.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells; - this.databaseGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.databaseGrid.Location = new System.Drawing.Point(9, 47); - this.databaseGrid.Name = "databaseGrid"; - this.databaseGrid.Size = new System.Drawing.Size(829, 380); - this.databaseGrid.TabIndex = 2; - // - // databaseSelectorCb - // - this.databaseSelectorCb.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.databaseSelectorCb.FormattingEnabled = true; - this.databaseSelectorCb.Location = new System.Drawing.Point(67, 18); - this.databaseSelectorCb.Name = "databaseSelectorCb"; - this.databaseSelectorCb.Size = new System.Drawing.Size(471, 21); - this.databaseSelectorCb.TabIndex = 1; - this.databaseSelectorCb.SelectedIndexChanged += new System.EventHandler(this.databaseSelectorCb_SelectedIndexChanged); - // - // label18 - // - this.label18.AutoSize = true; - this.label18.Location = new System.Drawing.Point(6, 22); - this.label18.Name = "label18"; - this.label18.Size = new System.Drawing.Size(56, 13); - this.label18.TabIndex = 0; - this.label18.Text = "Database:"; - // - // loadStoryBtn - // - this.loadStoryBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.loadStoryBtn.Location = new System.Drawing.Point(728, 21); - this.loadStoryBtn.Name = "loadStoryBtn"; - this.loadStoryBtn.Size = new System.Drawing.Size(121, 23); - this.loadStoryBtn.TabIndex = 67; - this.loadStoryBtn.Text = "Load"; - this.loadStoryBtn.UseVisualStyleBackColor = true; - this.loadStoryBtn.Click += new System.EventHandler(this.loadStoryBtn_Click); - // - // decompileStoryBtn - // - this.decompileStoryBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.decompileStoryBtn.Location = new System.Drawing.Point(728, 92); - this.decompileStoryBtn.Name = "decompileStoryBtn"; - this.decompileStoryBtn.Size = new System.Drawing.Size(121, 23); - this.decompileStoryBtn.TabIndex = 66; - this.decompileStoryBtn.Text = "Extract"; - this.decompileStoryBtn.UseVisualStyleBackColor = true; - this.decompileStoryBtn.Click += new System.EventHandler(this.decompileStoryBtn_Click); - // - // storyFilePath - // - this.storyFilePath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.storyFilePath.Location = new System.Drawing.Point(7, 22); - this.storyFilePath.Name = "storyFilePath"; - this.storyFilePath.Size = new System.Drawing.Size(678, 20); - this.storyFilePath.TabIndex = 61; - // - // goalPathBrowseBtn - // - this.goalPathBrowseBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.goalPathBrowseBtn.Location = new System.Drawing.Point(683, 92); - this.goalPathBrowseBtn.Name = "goalPathBrowseBtn"; - this.goalPathBrowseBtn.Size = new System.Drawing.Size(41, 22); - this.goalPathBrowseBtn.TabIndex = 65; - this.goalPathBrowseBtn.Text = "..."; - this.goalPathBrowseBtn.UseVisualStyleBackColor = true; - this.goalPathBrowseBtn.Click += new System.EventHandler(this.goalPathBrowseBtn_Click); - // - // label9 - // - this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(4, 6); - this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(128, 13); - this.label9.TabIndex = 60; - this.label9.Text = "Story/savegame file path:"; - // - // goalPath - // - this.goalPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.goalPath.Location = new System.Drawing.Point(7, 93); - this.goalPath.Name = "goalPath"; - this.goalPath.Size = new System.Drawing.Size(678, 20); - this.goalPath.TabIndex = 64; - // - // storyFileBrowseBtn - // - this.storyFileBrowseBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.storyFileBrowseBtn.Location = new System.Drawing.Point(683, 21); - this.storyFileBrowseBtn.Name = "storyFileBrowseBtn"; - this.storyFileBrowseBtn.Size = new System.Drawing.Size(41, 22); - this.storyFileBrowseBtn.TabIndex = 62; - this.storyFileBrowseBtn.Text = "..."; - this.storyFileBrowseBtn.UseVisualStyleBackColor = true; - this.storyFileBrowseBtn.Click += new System.EventHandler(this.storyFileBrowseBtn_Click); - // - // label10 - // - this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(4, 77); - this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(89, 13); - this.label10.TabIndex = 63; - this.label10.Text = "Goal output path:"; - // - // storyPathDlg - // - this.storyPathDlg.CheckFileExists = false; - this.storyPathDlg.Filter = "LS story/savegame files|*.osi;*.lsv"; - // - // btnDebugExport - // - this.btnDebugExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnDebugExport.Location = new System.Drawing.Point(728, 121); - this.btnDebugExport.Name = "btnDebugExport"; - this.btnDebugExport.Size = new System.Drawing.Size(121, 23); - this.btnDebugExport.TabIndex = 70; - this.btnDebugExport.Text = "Debug Export"; - this.btnDebugExport.UseVisualStyleBackColor = true; - this.btnDebugExport.Click += new System.EventHandler(this.btnDebugExport_Click); - // - // OsirisPane - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.btnDebugExport); - this.Controls.Add(this.saveStoryBtn); - this.Controls.Add(this.groupBox3); - this.Controls.Add(this.loadStoryBtn); - this.Controls.Add(this.decompileStoryBtn); - this.Controls.Add(this.storyFilePath); - this.Controls.Add(this.goalPathBrowseBtn); - this.Controls.Add(this.label9); - this.Controls.Add(this.goalPath); - this.Controls.Add(this.storyFileBrowseBtn); - this.Controls.Add(this.label10); - this.Name = "OsirisPane"; - this.Size = new System.Drawing.Size(863, 588); - this.groupBox3.ResumeLayout(false); - this.groupBox3.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.databaseGrid)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button saveStoryBtn; - private System.Windows.Forms.GroupBox groupBox3; - private System.Windows.Forms.DataGridView databaseGrid; - private System.Windows.Forms.ComboBox databaseSelectorCb; - private System.Windows.Forms.Label label18; - private System.Windows.Forms.Button loadStoryBtn; - private System.Windows.Forms.Button decompileStoryBtn; - private System.Windows.Forms.TextBox storyFilePath; - private System.Windows.Forms.Button goalPathBrowseBtn; - private System.Windows.Forms.Label label9; - private System.Windows.Forms.TextBox goalPath; - private System.Windows.Forms.Button storyFileBrowseBtn; - private System.Windows.Forms.Label label10; - private System.Windows.Forms.OpenFileDialog storyPathDlg; - private System.Windows.Forms.FolderBrowserDialog goalPathDlg; - private System.Windows.Forms.Button btnDebugExport; - private System.Windows.Forms.Label lblFilter; - private System.Windows.Forms.TextBox tbFilter; - private System.Windows.Forms.Button btnFilterMatchCase; - } -} diff --git a/ConverterApp/OsirisPane.axaml b/ConverterApp/OsirisPane.axaml new file mode 100644 index 00000000..0f26ad8e --- /dev/null +++ b/ConverterApp/OsirisPane.axaml @@ -0,0 +1,82 @@ + + + + + + + + + +