diff --git a/CodeWalker.Benchmarks/App.config b/CodeWalker.Benchmarks/App.config
new file mode 100644
index 0000000..9146286
--- /dev/null
+++ b/CodeWalker.Benchmarks/App.config
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CodeWalker.Benchmarks/Benchmarks.cs b/CodeWalker.Benchmarks/Benchmarks.cs
new file mode 100644
index 0000000..d88c9f1
--- /dev/null
+++ b/CodeWalker.Benchmarks/Benchmarks.cs
@@ -0,0 +1,200 @@
+using BenchmarkDotNet.Attributes;
+using BenchmarkDotNet.Configs;
+using BenchmarkDotNet.Environments;
+using BenchmarkDotNet.Jobs;
+using CodeWalker.GameFiles;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.Benchmarks
+{
+ [MemoryDiagnoser]
+ [Config(typeof(JitsConfig))]
+ public class Benchmarks
+ {
+ private class JitsConfig : ManualConfig
+ {
+ public JitsConfig()
+ {
+ AddJob(Job.Default.WithJit(Jit.RyuJit).WithPlatform(Platform.X64));
+ }
+ }
+ public static string markup = @"
+ vehshare
+
+
+ -
+ brabusgt600brabusgt600
+ brabusgt600
+ GT 600
+ BRABUS
+ null
+ null
+ null
+ null
+
+ null
+ ta176m177
+ LAYOUT_LOW
+ BULLET_COVER_OFFSET_INFO
+ EXPLOSION_INFO_DEFAULT
+
+ DEFAULT_FOLLOW_VEHICLE_CAMERA
+ MID_BOX_VEHICLE_AIM_CAMERA
+ VEHICLE_BONNET_CAMERA_NEAR_EXTRA_HIGH
+ DEFAULT_POV_CAMERA
+
+
+
+
+
+
+
+
+
+
+
+
+
+ VFXVEHICLEINFO_CAR_BULLET
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 60.000000
+ 80.000000
+ 100.000000
+ 120.000000
+ 500.000000
+ 500.000000
+
+
+
+
+
+
+
+
+
+
+ SWANKNESS_3
+
+ FLAG_SPORTS FLAG_RICH_CAR FLAG_NO_BROKEN_DOWN_SCENARIO FLAG_RECESSED_TAILLIGHT_CORONAS FLAG_NO_HEAVY_BRAKE_ANIMATION
+ VEHICLE_TYPE_CAR
+ VPT_FRONT_AND_BACK_PLATES
+ VDT_BANSHEE
+ VC_SPORT
+ VWT_HIEND
+
+
- docktrailer
+ - trailers
+ - trailers2
+ - trailers3
+ - trailers4
+ - tanker
+ - trailerlogs
+ - tr2
+ - trflat
+
+
+
+ -
+ S_M_Y_Cop_01
+
+
+
+
+
+
+
+
+
+
+
+ - WHEEL_FRONT_RIGHT_CAMERA
+ - WHEEL_FRONT_LEFT_CAMERA
+ - WHEEL_REAR_RIGHT_CAMERA
+ - WHEEL_REAR_LEFT_CAMERA
+
+
+
+
+
+
+
+
+
+
+ - LOW_BULLET_FRONT_LEFT
+ - LOW_BULLET_FRONT_RIGHT
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+ vehicles_banshee_interior
+ brabusgt600
+
+
+
+
+
+
+";
+
+ private byte[] data;
+ private RpfFileEntry fileEntry;
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ data = Encoding.UTF8.GetBytes(markup);
+ fileEntry = RpfFile.CreateFileEntry("kaas.meta", "saak.meta", ref data);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void RunLoad()
+ {
+ var vehiclesFileExpected = new VehiclesFile();
+ vehiclesFileExpected.LoadOld(data, fileEntry);
+ }
+
+ [Benchmark]
+ public void RunLoadNew()
+ {
+ var vehiclesFile = new VehiclesFile();
+ vehiclesFile.Load(data, fileEntry);
+ }
+ }
+}
diff --git a/CodeWalker.Benchmarks/CodeWalker.Benchmarks.csproj b/CodeWalker.Benchmarks/CodeWalker.Benchmarks.csproj
new file mode 100644
index 0000000..4bf9dff
--- /dev/null
+++ b/CodeWalker.Benchmarks/CodeWalker.Benchmarks.csproj
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+ Debug
+ AnyCPU
+ {EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}
+ Exe
+ CodeWalker.Benchmarks
+ CodeWalker.Benchmarks
+ v4.8
+ 512
+ true
+ true
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\BenchmarkDotNet.0.13.10\lib\netstandard2.0\BenchmarkDotNet.dll
+
+
+ ..\packages\BenchmarkDotNet.Annotations.0.13.10\lib\netstandard2.0\BenchmarkDotNet.Annotations.dll
+
+
+ ..\packages\CommandLineParser.2.9.1\lib\net461\CommandLine.dll
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Dia2Lib.dll
+ True
+
+
+ ..\packages\Gee.External.Capstone.2.3.0\lib\netstandard2.0\Gee.External.Capstone.dll
+
+
+ ..\packages\Iced.1.17.0\lib\net45\Iced.dll
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+
+ ..\packages\Microsoft.CodeAnalysis.Common.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll
+
+
+ ..\packages\Microsoft.CodeAnalysis.CSharp.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Microsoft.Diagnostics.FastSerialization.dll
+
+
+ ..\packages\Microsoft.Diagnostics.NETCore.Client.0.2.251802\lib\netstandard2.0\Microsoft.Diagnostics.NETCore.Client.dll
+
+
+ ..\packages\Microsoft.Diagnostics.Runtime.2.2.332302\lib\netstandard2.0\Microsoft.Diagnostics.Runtime.dll
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Microsoft.Diagnostics.Tracing.TraceEvent.dll
+
+
+ ..\packages\Microsoft.DotNet.PlatformAbstractions.3.1.6\lib\net45\Microsoft.DotNet.PlatformAbstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Configuration.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll
+
+
+ ..\packages\Microsoft.Extensions.Configuration.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Configuration.Binder.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll
+
+
+ ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Logging.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.dll
+
+
+ ..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll
+
+
+ ..\packages\Microsoft.Extensions.Options.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Options.dll
+
+
+ ..\packages\Microsoft.Extensions.Primitives.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll
+
+
+ ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\OSExtensions.dll
+
+
+ ..\packages\Perfolizer.0.2.1\lib\netstandard2.0\Perfolizer.dll
+
+
+ ..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll
+
+
+ ..\packages\SharpDX.Mathematics.4.2.0\lib\net45\SharpDX.Mathematics.dll
+
+
+
+ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
+
+
+ ..\packages\System.Collections.Immutable.5.0.0\lib\net461\System.Collections.Immutable.dll
+
+
+
+
+ ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Reflection.Metadata.5.0.0\lib\net461\System.Reflection.Metadata.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll
+ True
+ True
+
+
+ ..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll
+
+
+ ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
+
+
+ ..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
+
+
+
+
+
+
+
+ ..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\TraceReloggerLib.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {FF6B9F41-14BE-474E-9ED0-549C3BEB7E00}
+ CodeWalker.Core
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CodeWalker.Benchmarks/Program.cs b/CodeWalker.Benchmarks/Program.cs
new file mode 100644
index 0000000..06047a2
--- /dev/null
+++ b/CodeWalker.Benchmarks/Program.cs
@@ -0,0 +1,17 @@
+using BenchmarkDotNet.Running;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeWalker.Benchmarks
+{
+ internal class Program
+ {
+ static void Main(string[] args)
+ {
+ BenchmarkRunner.Run();
+ }
+ }
+}
diff --git a/CodeWalker.Benchmarks/Properties/AssemblyInfo.cs b/CodeWalker.Benchmarks/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..bea5153
--- /dev/null
+++ b/CodeWalker.Benchmarks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CodeWalker.Benchmarks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CodeWalker.Benchmarks")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("edda8a8e-5333-4e28-8221-a31e3b70eb7a")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CodeWalker.Benchmarks/packages.config b/CodeWalker.Benchmarks/packages.config
new file mode 100644
index 0000000..027c21b
--- /dev/null
+++ b/CodeWalker.Benchmarks/packages.config
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CodeWalker.Core/CodeWalker.Core.csproj b/CodeWalker.Core/CodeWalker.Core.csproj
index b515aad..526603e 100644
--- a/CodeWalker.Core/CodeWalker.Core.csproj
+++ b/CodeWalker.Core/CodeWalker.Core.csproj
@@ -7,11 +7,16 @@
+
+
+
+
+
diff --git a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs
index cc79536..4918930 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/AwcFile.cs
@@ -70,10 +70,10 @@ namespace CodeWalker.GameFiles
if (!string.IsNullOrEmpty(Name))
{
- var nl = Name.ToLowerInvariant();
+ var nl = Name;
var fn = Path.GetFileNameWithoutExtension(nl);
- JenkIndex.Ensure(fn + "_left");
- JenkIndex.Ensure(fn + "_right");
+ JenkIndex.EnsureLower(fn + "_left");
+ JenkIndex.EnsureLower(fn + "_right");
}
if ((data == null) || (data.Length < 8))
diff --git a/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs b/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs
index 0ddc67d..29b8444 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/DistantLightsFile.cs
@@ -40,7 +40,7 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
Name = entry.Name;
- if (!entry.NameLower.EndsWith("_hd.dat"))
+ if (!entry.Name.EndsWith("_hd.dat", StringComparison.OrdinalIgnoreCase))
{
HD = false;
GridSize = 16;
diff --git a/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs b/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs
index cefbaf7..49c0517 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/FxcFile.cs
@@ -1186,7 +1186,7 @@ namespace CodeWalker.GameFiles
SlotGS = br.ReadUInt16(); //6, 5
SlotHS = br.ReadUInt16(); //6, 5
Name = FxcFile.ReadString(br); // _locals //"rage_matrices", "misc_globals", "lighting_globals", "more_stuff"
- JenkIndex.Ensure(Name?.ToLowerInvariant()); //why not :P
+ JenkIndex.EnsureLower(Name); //why not :P
}
public void Write(BinaryWriter bw)
{
diff --git a/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs b/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs
index 1d7932e..3c5c319 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/GtxdFile.cs
@@ -36,7 +36,7 @@ namespace CodeWalker.GameFiles
FilePath = Name;
- if (entry.NameLower.EndsWith(".ymt"))
+ if (entry.Name.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
{
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
@@ -57,7 +57,7 @@ namespace CodeWalker.GameFiles
//not an RBF file...
}
}
- else if (entry.NameLower.EndsWith(".meta"))
+ else if (entry.Name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase))
{
//required for update\x64\dlcpacks\mpheist\dlc.rpf\common\data\gtxd.meta and update\x64\dlcpacks\mpluxe\dlc.rpf\common\data\gtxd.meta
string xml = TextUtil.GetUTF8Text(data);
diff --git a/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs b/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs
index 5e4731c..58f656c 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/Gxt2File.cs
@@ -6,6 +6,7 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
@@ -172,7 +173,7 @@ namespace CodeWalker.GameFiles
public static class GlobalText
{
- public static ConcurrentDictionary Index = new ();
+ public static ConcurrentDictionary Index = new (4, 24000);
private static object syncRoot = new object();
public static volatile bool FullIndexBuilt = false;
diff --git a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs
index 0651bbf..cd98e7f 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/MrfFile.cs
@@ -798,9 +798,13 @@ namespace CodeWalker.GameFiles
{
}
- protected override void WriteToStream(byte[] value, bool ignoreEndianess = false)
+ protected override void WriteToStream(byte[] value, bool ignoreEndianess = false, int count = -1, int offset = 0)
{
- position += value.Length;
+ if (count == -1)
+ {
+ count = value.Length;
+ }
+ position += count;
length = Math.Max(length, position);
}
}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/PedFile.cs b/CodeWalker.Core/GameFiles/FileTypes/PedFile.cs
index 1c60cb3..82f0788 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/PedFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/PedFile.cs
@@ -87,7 +87,6 @@ namespace CodeWalker.GameFiles
}
-
private void NonMetaLoad(byte[] data)
{
//non meta not supported yet! but see what's in there...
@@ -103,22 +102,6 @@ namespace CodeWalker.GameFiles
Pso.Load(ms);
LoadPso();
}
- else
- {
- }
-
}
-
-
-
-
-
-
-
-
-
-
-
-
}
}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/PedsFile.cs b/CodeWalker.Core/GameFiles/FileTypes/PedsFile.cs
index c11583b..4e778ee 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/PedsFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/PedsFile.cs
@@ -9,6 +9,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
+using System.Xml.Linq;
namespace CodeWalker.GameFiles
@@ -34,7 +35,7 @@ namespace CodeWalker.GameFiles
//can be PSO .ymt or XML .meta
- MemoryStream ms = new MemoryStream(data);
+ using MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms))
{
Pso = new PsoFile();
@@ -46,26 +47,28 @@ namespace CodeWalker.GameFiles
Xml = TextUtil.GetUTF8Text(data);
}
- XmlDocument xdoc = new XmlDocument();
- if (!string.IsNullOrEmpty(Xml))
- {
- try
- {
- xdoc.LoadXml(Xml);
- }
- catch (Exception ex)
- {
- var msg = ex.Message;
- }
- }
- else
- { }
+ using var textReader = new StringReader(Xml);
+
+ //XmlDocument xdoc = new XmlDocument();
+ //if (!string.IsNullOrEmpty(Xml))
+ //{
+ // try
+ // {
+ // xdoc.LoadXml(Xml);
+ // }
+ // catch (Exception ex)
+ // {
+ // var msg = ex.Message;
+ // }
+ //}
+
+ using var xmlReader = XmlReader.Create(textReader);
- if (xdoc.DocumentElement != null)
- {
- InitDataList = new CPedModelInfo__InitDataList(xdoc.DocumentElement);
- }
+ //if (xdoc.DocumentElement != null)
+ //{
+ InitDataList = new CPedModelInfo__InitDataList(xmlReader);
+ //}
@@ -84,6 +87,87 @@ namespace CodeWalker.GameFiles
public CTxdRelationship[] txdRelationships { get; set; }
public CMultiTxdRelationship[] multiTxdRelationships { get; set; }
+ public CPedModelInfo__InitDataList(XmlReader reader)
+ {
+ while (reader.Read())
+ {
+ reader.MoveToContent();
+
+ //var _ = xmlReader.Name switch
+ //{
+ // "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
+ // "InitDatas" => LoadInitDatas(xmlReader),
+ // "txdRelationships" => LoadTxdRelationships(xmlReader),
+ // _ => throw new Exception()
+ //};
+
+ switch (reader.Name)
+ {
+ case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
+ residentTxd = Xml.GetChildInnerText(reader, "residentTxd");
+ break;
+ case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
+ if (reader.IsEmptyElement)
+ {
+ reader.ReadStartElement();
+ break;
+ }
+ reader.ReadStartElement();
+ var initDatasList = new List();
+
+ while (reader.IsItemElement())
+ {
+ initDatasList.Add(new CPedModelInfo__InitData(reader));
+ }
+ if (initDatasList.Count > 0)
+ {
+ InitDatas = initDatasList.ToArray();
+ }
+ reader.ReadEndElement();
+ break;
+ case string Name when Name.Equals("txdRelationships", StringComparison.OrdinalIgnoreCase):
+ if (reader.IsEmptyElement)
+ {
+ reader.ReadStartElement();
+ break;
+ }
+ reader.ReadStartElement();
+ var txdRelationshipsList = new List();
+
+ while (reader.IsItemElement())
+ {
+ txdRelationshipsList.Add(new CTxdRelationship(reader));
+ }
+ reader.ReadEndElement();
+ if (txdRelationshipsList.Count > 0)
+ {
+ txdRelationships = txdRelationshipsList.ToArray();
+ }
+ break;
+ case string Name when Name.Equals("multiTxdRelationships", StringComparison.OrdinalIgnoreCase):
+ if (reader.IsEmptyElement)
+ {
+ reader.ReadStartElement();
+ break;
+ }
+ reader.ReadStartElement();
+ var multiTxdList = new List();
+ while (reader.IsItemElement())
+ {
+ multiTxdList.Add(new CMultiTxdRelationship(reader));
+ }
+ reader.ReadEndElement();
+ if (multiTxdList.Count > 0)
+ {
+ multiTxdRelationships = multiTxdList.ToArray();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
public CPedModelInfo__InitDataList(XmlNode node)
{
XmlNodeList items;
@@ -126,7 +210,6 @@ namespace CodeWalker.GameFiles
multiTxdRelationships[i] = new CMultiTxdRelationship(items[i]);
}
}
-
}
}
@@ -204,6 +287,249 @@ namespace CodeWalker.GameFiles
public float DefaultRemoveRangeMultiplier { get; set; }
public bool AllowCloseSpawning { get; set; }
+ public CPedModelInfo__InitData(XmlReader reader)
+ {
+ reader.ReadStartElement("Item");
+ while (reader.Name != "Item" && reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Item")
+ {
+ reader.ReadEndElement();
+ return;
+ }
+
+ while (reader.MoveToContent() != XmlNodeType.Element && reader.Read())
+ {
+
+ }
+
+ switch (reader.Name)
+ {
+ case "Name":
+ Name = Xml.GetChildInnerText(reader, "Name");
+ break;
+ case "PropsName":
+ PropsName = Xml.GetChildInnerText(reader, "PropsName");
+ break;
+ case "ClipDictionaryName":
+ ClipDictionaryName = Xml.GetChildInnerText(reader, "ClipDictionaryName");
+ break;
+ case "BlendShapeFileName":
+ BlendShapeFileName = Xml.GetChildInnerText(reader, "BlendShapeFileName");
+ break;
+ case "ExpressionSetName":
+ ExpressionSetName = Xml.GetChildInnerText(reader, "ExpressionSetName");
+ break;
+ case "ExpressionDictionaryName":
+ ExpressionDictionaryName = Xml.GetChildInnerText(reader, "ExpressionDictionaryName");
+ break;
+ case "ExpressionName":
+ ExpressionName = Xml.GetChildInnerText(reader, "ExpressionName");
+ break;
+ case "Pedtype":
+ Pedtype = Xml.GetChildInnerText(reader, "Pedtype");
+ break;
+ case "MovementClipSet":
+ MovementClipSet = Xml.GetChildInnerText(reader, "MovementClipSet");
+ break;
+ case "MovementClipSets":
+ var clipSetsList = new List();
+ foreach(var item in Xml.IterateItems(reader, "MovementClipSets"))
+ {
+ clipSetsList.Add(item.Value);
+ }
+
+ MovementClipSets = clipSetsList.ToArray();
+ break;
+ case "StrafeClipSet":
+ StrafeClipSet = Xml.GetChildInnerText(reader, "StrafeClipSet");
+ break;
+ case "MovementToStrafeClipSet":
+ MovementToStrafeClipSet = Xml.GetChildInnerText(reader, "MovementToStrafeClipSet");
+ break;
+ case "InjuredStrafeClipSet":
+ InjuredStrafeClipSet = Xml.GetChildInnerText(reader, "InjuredStrafeClipSet");
+ break;
+ case "FullBodyDamageClipSet":
+ FullBodyDamageClipSet = Xml.GetChildInnerText(reader, "FullBodyDamageClipSet");
+ break;
+ case "AdditiveDamageClipSet":
+ AdditiveDamageClipSet = Xml.GetChildInnerText(reader, "AdditiveDamageClipSet");
+ break;
+ case "DefaultGestureClipSet":
+ DefaultGestureClipSet = Xml.GetChildInnerText(reader, "DefaultGestureClipSet");
+ break;
+ case "FacialClipsetGroupName":
+ FacialClipsetGroupName = Xml.GetChildInnerText(reader, "FacialClipsetGroupName");
+ break;
+ case "DefaultVisemeClipSet":
+ DefaultVisemeClipSet = Xml.GetChildInnerText(reader, "DefaultVisemeClipSet");
+ break;
+ case "SidestepClipSet":
+ SidestepClipSet = Xml.GetChildInnerText(reader, "SidestepClipSet");
+ break;
+ case "PoseMatcherName":
+ PoseMatcherName = Xml.GetChildInnerText(reader, "PoseMatcherName");
+ break;
+ case "PoseMatcherProneName":
+ PoseMatcherProneName = Xml.GetChildInnerText(reader, "PoseMatcherProneName");
+ break;
+ case "GetupSetHash":
+ GetupSetHash = Xml.GetChildInnerText(reader, "GetupSetHash");
+ break;
+ case "CreatureMetadataName":
+ CreatureMetadataName = Xml.GetChildInnerText(reader, "CreatureMetadataName");
+ break;
+ case "DecisionMakerName":
+ DecisionMakerName = Xml.GetChildInnerText(reader, "DecisionMakerName");
+ break;
+ case "MotionTaskDataSetName":
+ MotionTaskDataSetName = Xml.GetChildInnerText(reader, "MotionTaskDataSetName");
+ break;
+ case "DefaultTaskDataSetName":
+ DefaultTaskDataSetName = Xml.GetChildInnerText(reader, "DefaultTaskDataSetName");
+ break;
+ case "PedCapsuleName":
+ PedCapsuleName = Xml.GetChildInnerText(reader, "PedCapsuleName");
+ break;
+ case "PedLayoutName":
+ PedLayoutName = Xml.GetChildInnerText(reader, "PedLayoutName");
+ break;
+ case "PedComponentSetName":
+ PedComponentSetName = Xml.GetChildInnerText(reader, "PedComponentSetName");
+ break;
+ case "PedComponentClothName":
+ PedComponentClothName = Xml.GetChildInnerText(reader, "PedComponentClothName");
+ break;
+ case "PedIKSettingsName":
+ PedIKSettingsName = Xml.GetChildInnerText(reader, "PedIKSettingsName");
+ break;
+ case "TaskDataName":
+ TaskDataName = Xml.GetChildInnerText(reader, "TaskDataName");
+ break;
+ case "IsStreamedGfx":
+ IsStreamedGfx = Xml.GetChildBoolAttribute(reader, "IsStreamedGfx", "value");
+ break;
+ case "AmbulanceShouldRespondTo":
+ AmbulanceShouldRespondTo = Xml.GetChildBoolAttribute(reader, "AmbulanceShouldRespondTo", "value");
+ break;
+ case "CanRideBikeWithNoHelmet":
+ CanRideBikeWithNoHelmet = Xml.GetChildBoolAttribute(reader, "CanRideBikeWithNoHelmet", "value");
+ break;
+ case "CanSpawnInCar":
+ CanSpawnInCar = Xml.GetChildBoolAttribute(reader, "CanSpawnInCar", "value");
+ break;
+ case "IsHeadBlendPed":
+ IsHeadBlendPed = Xml.GetChildBoolAttribute(reader, "IsHeadBlendPed", "value");
+ break;
+ case "bOnlyBulkyItemVariations":
+ bOnlyBulkyItemVariations = Xml.GetChildBoolAttribute(reader, "bOnlyBulkyItemVariations", "value");
+ break;
+ case "RelationshipGroup":
+ RelationshipGroup = Xml.GetChildInnerText(reader, "RelationshipGroup");
+ break;
+ case "NavCapabilitiesName":
+ NavCapabilitiesName = Xml.GetChildInnerText(reader, "NavCapabilitiesName");
+ break;
+ case "PerceptionInfo":
+ PerceptionInfo = Xml.GetChildInnerText(reader, "PerceptionInfo");
+ break;
+ case "DefaultBrawlingStyle":
+ DefaultBrawlingStyle = Xml.GetChildInnerText(reader, "DefaultBrawlingStyle");
+ break;
+ case "DefaultUnarmedWeapon":
+ DefaultUnarmedWeapon = Xml.GetChildInnerText(reader, "DefaultUnarmedWeapon");
+ break;
+ case "Personality":
+ Personality = Xml.GetChildInnerText(reader, "Personality");
+ break;
+ case "CombatInfo":
+ CombatInfo = Xml.GetChildInnerText(reader, "CombatInfo");
+ break;
+ case "VfxInfoName":
+ VfxInfoName = Xml.GetChildInnerText(reader, "VfxInfoName");
+ break;
+ case "AmbientClipsForFlee":
+ AmbientClipsForFlee = Xml.GetChildInnerText(reader, "AmbientClipsForFlee");
+ break;
+ case "Radio1":
+ Radio1 = Xml.GetChildInnerText(reader, "Radio1"); // MetaName.ePedRadioGenre
+ break;
+ case "Radio2":
+ Radio2 = Xml.GetChildInnerText(reader, "Radio2"); // MetaName.ePedRadioGenre
+ break;
+ case "FUpOffset":
+ FUpOffset = Xml.GetChildFloatAttribute(reader, "FUpOffset", "value");
+ break;
+ case "RUpOffset":
+ RUpOffset = Xml.GetChildFloatAttribute(reader, "RUpOffset", "value");
+ break;
+ case "FFrontOffset":
+ FFrontOffset = Xml.GetChildFloatAttribute(reader, "FFrontOffset", "value");
+ break;
+ case "RFrontOffset":
+ RFrontOffset = Xml.GetChildFloatAttribute(reader, "RFrontOffset", "value");
+ break;
+ case "MinActivationImpulse":
+ MinActivationImpulse = Xml.GetChildFloatAttribute(reader, "MinActivationImpulse", "value");
+ break;
+ case "Stubble":
+ Stubble = Xml.GetChildFloatAttribute(reader, "Stubble", "value");
+ break;
+ case "HDDist":
+ HDDist = Xml.GetChildFloatAttribute(reader, "HDDist", "value");
+ break;
+ case "TargetingThreatModifier":
+ TargetingThreatModifier = Xml.GetChildFloatAttribute(reader, "TargetingThreatModifier", "value");
+ break;
+ case "KilledPerceptionRangeModifer":
+ KilledPerceptionRangeModifer = Xml.GetChildFloatAttribute(reader, "KilledPerceptionRangeModifer", "value");
+ break;
+ case "Sexiness":
+ Sexiness = Xml.GetChildInnerText(reader, "Sexiness"); // MetaTypeName.ARRAYINFO MetaName.eSexinessFlags
+ break;
+ case "Age":
+ Age = (byte)Xml.GetChildUIntAttribute(reader, "Age", "value");
+ break;
+ case "MaxPassengersInCar":
+ MaxPassengersInCar = (byte)Xml.GetChildUIntAttribute(reader, "MaxPassengersInCar", "value");
+ break;
+ case "ExternallyDrivenDOFs":
+ ExternallyDrivenDOFs = Xml.GetChildInnerText(reader, "ExternallyDrivenDOFs"); // MetaTypeName.ARRAYINFO MetaName.eExternallyDrivenDOFs
+ break;
+ case "PedVoiceGroup":
+ PedVoiceGroup = Xml.GetChildInnerText(reader, "PedVoiceGroup");
+ break;
+ case "AnimalAudioObject":
+ AnimalAudioObject = Xml.GetChildInnerText(reader, "AnimalAudioObject");
+ break;
+ case "AbilityType":
+ AbilityType = Xml.GetChildInnerText(reader, "AbilityType"); // MetaName.SpecialAbilityType
+ break;
+ case "ThermalBehaviour":
+ ThermalBehaviour = Xml.GetChildInnerText(reader, "ThermalBehaviour"); // MetaName.ThermalBehaviour
+ break;
+ case "SuperlodType":
+ SuperlodType = Xml.GetChildInnerText(reader, "SuperlodType"); // MetaName.eSuperlodType
+ break;
+ case "ScenarioPopStreamingSlot":
+ ScenarioPopStreamingSlot = Xml.GetChildInnerText(reader, "ScenarioPopStreamingSlot"); // MetaName.eScenarioPopStreamingSlot
+ break;
+ case "DefaultSpawningPreference":
+ DefaultSpawningPreference = Xml.GetChildInnerText(reader, "DefaultSpawningPreference"); // MetaName.DefaultSpawnPreference
+ break;
+ case "DefaultRemoveRangeMultiplier":
+ DefaultRemoveRangeMultiplier = Xml.GetChildFloatAttribute(reader, "DefaultRemoveRangeMultiplier", "value");
+ break;
+ case "AllowCloseSpawning":
+ AllowCloseSpawning = Xml.GetChildBoolAttribute(reader, "AllowCloseSpawning", "value");
+ break;
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
public CPedModelInfo__InitData(XmlNode node)
{
@@ -300,6 +626,25 @@ namespace CodeWalker.GameFiles
public string parent { get; set; }
public string child { get; set; }
+
+ public CTxdRelationship(XmlReader reader)
+ {
+ if (reader.Name == "parent")
+ {
+ parent = Xml.GetChildInnerText(reader, "parent");
+ } else if (reader.Name == "child")
+ {
+ child = Xml.GetChildInnerText(reader, "child");
+ }
+ if (reader.Name == "parent")
+ {
+ parent = Xml.GetChildInnerText(reader, "parent");
+ }
+ else if (reader.Name == "child")
+ {
+ child = Xml.GetChildInnerText(reader, "child");
+ }
+ }
public CTxdRelationship(XmlNode node)
{
parent = Xml.GetChildInnerText(node, "parent");
@@ -331,6 +676,34 @@ namespace CodeWalker.GameFiles
}
}
+ public CMultiTxdRelationship(XmlReader reader)
+ {
+ reader.ReadStartElement("Item");
+ while (reader.MoveToContent() == XmlNodeType.Element && reader.Name != "Item")
+ {
+ switch (reader.Name)
+ {
+ case "children":
+ var childrenList = new List();
+ foreach (var item in Xml.IterateItems(reader, "children"))
+ {
+ childrenList.Add(item.Value);
+ }
+ if (childrenList.Count > 0)
+ {
+ children = childrenList.ToArray();
+ }
+ break;
+ case "parent":
+ parent = Xml.GetChildInnerText(reader, "parent");
+ break;
+ default:
+ throw new InvalidOperationException($"Found invalid XML Element \"{reader.Name}\" of type \"{reader.NodeType}\"");
+ }
+ }
+ reader.ReadEndElement();
+ }
+
public override string ToString()
{
return parent + ": " + (children?.Length ?? 0).ToString() + " children";
diff --git a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs
index 0411c26..bd73988 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs
@@ -105,7 +105,26 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
}
- public void Load(byte[] data, RpfFileEntry entry)
+ public unsafe string ReadString(BinaryReader br, int length, bool ignoreNullTerminator = false)
+ {
+ var bytes = stackalloc char[length];
+ var currentLength = 0;
+ while (currentLength < length)
+ {
+ var c = (char)br.ReadByte();
+ if (c != 0)
+ {
+ bytes[currentLength] = c;
+ currentLength++;
+ }
+ else if (!ignoreNullTerminator)
+ break;
+ }
+
+ return new string(bytes, 0, currentLength);
+ }
+
+ public unsafe void Load(byte[] data, RpfFileEntry entry)
{
RawFileData = data;
if (entry != null)
@@ -114,8 +133,8 @@ namespace CodeWalker.GameFiles
Name = entry.Name;
}
- MemoryStream ms = new MemoryStream(data);
- BinaryReader br = new BinaryReader(ms);
+ using MemoryStream ms = new MemoryStream(data);
+ using BinaryReader br = new BinaryReader(ms);
StringBuilder sb = new StringBuilder();
RelType = (RelDatFileType)br.ReadUInt32(); //type
@@ -134,19 +153,25 @@ namespace CodeWalker.GameFiles
}
NameTableOffsets = ntoffsets;
string[] names = new string[NameTableCount];
+ var remainingLength = (int)NameTableLength;
for (uint i = 0; i < NameTableCount; i++)
{
- sb.Clear();
- while (true)
+ int length = 0;
+ if (i < NameTableCount - 1)
{
- char c = (char)br.ReadByte();
- if (c != 0) sb.Append(c);
- else break;
+ length = (int)(NameTableOffsets[i + 1] - NameTableOffsets[i]);
+
}
- names[i] = sb.ToString();
+ else
+ {
+ length = remainingLength;
+ }
+
+ names[i] = ReadString(br, length);
//JenkIndex.Ensure(names[i]); //really need both here..?
- JenkIndex.Ensure(names[i].ToLowerInvariant());
+ JenkIndex.EnsureLower(names[i]);
+ remainingLength -= length;
}
NameTable = names;
}
@@ -164,18 +189,13 @@ namespace CodeWalker.GameFiles
for (uint i = 0; i < IndexCount; i++)
{
byte sl = br.ReadByte();
- sb.Clear();
- for (int j = 0; j < sl; j++)
- {
- char c = (char)br.ReadByte();
- if (c != 0) sb.Append(c);
- }
+ var str = ReadString(br, (int)sl, true);
RelIndexString ristr = new RelIndexString();
- ristr.Name = sb.ToString();
+ ristr.Name = str;
ristr.Offset = br.ReadUInt32();
ristr.Length = br.ReadUInt32();
indexstrs[i] = ristr;
- JenkIndex.Ensure(ristr.Name.ToLowerInvariant());
+ JenkIndex.EnsureLower(ristr.Name);
}
IndexStrings = indexstrs;
}
@@ -235,9 +255,6 @@ namespace CodeWalker.GameFiles
{ }
//EOF!
- br.Dispose();
- ms.Dispose();
-
ParseDataBlock();
@@ -253,8 +270,8 @@ namespace CodeWalker.GameFiles
- MemoryStream ms = new MemoryStream(DataBlock);
- BinaryReader br = new BinaryReader(ms);
+ using MemoryStream ms = new MemoryStream(DataBlock);
+ using BinaryReader br = new BinaryReader(ms);
DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version? flags?
#region DataUnkVal unk values test
@@ -315,12 +332,6 @@ namespace CodeWalker.GameFiles
RelDatasSorted = reldatas.ToArray();
- br.Dispose();
- ms.Dispose();
-
-
-
-
RelDataDict.Clear();
foreach (var reldata in RelDatas)
{
@@ -328,7 +339,7 @@ namespace CodeWalker.GameFiles
{
reldata.NameHash = JenkHash.GenHash(reldata.Name); //should this be lower case?
JenkIndex.Ensure(reldata.Name);
- JenkIndex.Ensure(reldata.Name.ToLowerInvariant()); //which one to use?
+ JenkIndex.EnsureLower(reldata.Name); //which one to use?
}
//if (reldata.NameHash == 0)
@@ -349,24 +360,10 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < snd.ChildSoundsCount; i++)
{
var audhash = snd.ChildSoundsHashes[i];
- RelData auddata = null;
- if (RelDataDict.TryGetValue(audhash, out auddata))
+ if (RelDataDict.TryGetValue(audhash, out var auddata))
{
snd.ChildSounds[i] = auddata;
}
- else
- { }
- }
- }
- if (snd.AudioContainers != null)
- {
- foreach (var cnt in snd.AudioContainers)
- {
- string cname = JenkIndex.TryGetString(cnt.Hash);
- if (!string.IsNullOrEmpty(cname))
- { }
- else
- { }
}
}
}
@@ -385,8 +382,6 @@ namespace CodeWalker.GameFiles
{
speechDict[speechData.DataOffset] = speechData;
}
- else
- { }
speechData.Type = Dat4SpeechType.ByteArray;
speechData.TypeID = 0; //will be set again after this
@@ -397,32 +392,24 @@ namespace CodeWalker.GameFiles
var hashOffset = HashTableOffsets[i];
var hash = HashTable[i];
var itemOffset = hashOffset - 8;
- Dat4SpeechData speechData = null;
- speechDict.TryGetValue(itemOffset, out speechData);
- if (speechData != null)
+ if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
{
speechData.Type = Dat4SpeechType.Hash;
speechData.TypeID = 4;
speechData.Hash = hash;
}
- else
- { }
}
for (uint i = 0; i < PackTableCount; i++)
{
var packOffset = PackTableOffsets[i];
var pack = PackTable[i];
var itemOffset = packOffset - 12;
- Dat4SpeechData speechData = null;
- speechDict.TryGetValue(itemOffset, out speechData);
- if (speechData != null)
+ if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
{
speechData.Type = Dat4SpeechType.Container;
speechData.TypeID = 8;
speechData.ContainerHash = pack;
}
- else
- { }//shouldn't happen!
}
@@ -456,33 +443,31 @@ namespace CodeWalker.GameFiles
d.Data = data;
- using (BinaryReader dbr = new BinaryReader(new MemoryStream(data)))
- {
- d.ReadType(dbr);
+ using BinaryReader dbr = new BinaryReader(new MemoryStream(data));
+ d.ReadType(dbr);
- switch (RelType)
- {
- case RelDatFileType.Dat4: //speech.dat4.rel, audioconfig.dat4.rel
- return ReadData4(d, dbr);
- case RelDatFileType.Dat10ModularSynth: //amp.dat10.rel
- return ReadData10(d, dbr);
- case RelDatFileType.Dat15DynamicMixer: //mix.dat15.rel
- return ReadData15(d, dbr);
- case RelDatFileType.Dat16Curves: //curves.dat16.rel
- return ReadData16(d, dbr);
- case RelDatFileType.Dat22Categories: //categories.dat22.rel
- return ReadData22(d, dbr);
- case RelDatFileType.Dat54DataEntries: //sounds.dat54.rel
- return ReadData54(d, dbr);
- case RelDatFileType.Dat149: //game.dat149.rel
- return ReadData149(d, dbr);
- case RelDatFileType.Dat150: //game.dat150.rel
- return ReadData150(d, dbr);
- case RelDatFileType.Dat151: //game.dat151.rel
- return ReadData151(d, dbr);
- default:
- return d; //shouldn't get here...
- }
+ switch (RelType)
+ {
+ case RelDatFileType.Dat4: //speech.dat4.rel, audioconfig.dat4.rel
+ return ReadData4(d, dbr);
+ case RelDatFileType.Dat10ModularSynth: //amp.dat10.rel
+ return ReadData10(d, dbr);
+ case RelDatFileType.Dat15DynamicMixer: //mix.dat15.rel
+ return ReadData15(d, dbr);
+ case RelDatFileType.Dat16Curves: //curves.dat16.rel
+ return ReadData16(d, dbr);
+ case RelDatFileType.Dat22Categories: //categories.dat22.rel
+ return ReadData22(d, dbr);
+ case RelDatFileType.Dat54DataEntries: //sounds.dat54.rel
+ return ReadData54(d, dbr);
+ case RelDatFileType.Dat149: //game.dat149.rel
+ return ReadData149(d, dbr);
+ case RelDatFileType.Dat150: //game.dat150.rel
+ return ReadData150(d, dbr);
+ case RelDatFileType.Dat151: //game.dat151.rel
+ return ReadData151(d, dbr);
+ default:
+ return d; //shouldn't get here...
}
}
@@ -25334,7 +25319,7 @@ namespace CodeWalker.GameFiles
{
return 0;
}
- if (str.StartsWith("hash_"))
+ if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{
return Convert.ToUInt32(str.Substring(5), 16);
}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/Stats.cs b/CodeWalker.Core/GameFiles/FileTypes/Stats.cs
index 09bacf7..1118ece 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/Stats.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/Stats.cs
@@ -26,16 +26,14 @@ namespace CodeWalker.GameFiles
public static bool Ensure(string str)
{
uint hash = JenkHash.GenHash(str);
- if (hash == 0) return true;
- lock (syncRoot)
- {
- if (!Index.ContainsKey(hash))
- {
- Index.Add(hash, str);
- return false;
- }
- }
- return true;
+ return Ensure(str, hash);
+ }
+
+ public static bool EnsureLower(string str)
+ {
+ uint hash = JenkHash.GenHashLower(str);
+
+ return Ensure(str, hash);
}
public static bool Ensure(string str, uint hash)
diff --git a/CodeWalker.Core/GameFiles/FileTypes/VehiclesFile.cs b/CodeWalker.Core/GameFiles/FileTypes/VehiclesFile.cs
index dad02cc..946ed09 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/VehiclesFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/VehiclesFile.cs
@@ -1,10 +1,15 @@
-using SharpDX;
+using CodeWalker.World;
+using SharpDX;
using System;
+using System.Collections;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
+using System.Xml.Linq;
namespace CodeWalker.GameFiles
{
@@ -28,14 +33,14 @@ namespace CodeWalker.GameFiles
- public void Load(byte[] data, RpfFileEntry entry)
+ public void LoadOld(byte[] data, RpfFileEntry entry)
{
RpfFileEntry = entry;
Name = entry.Name;
FilePath = Name;
- if (entry.NameLower.EndsWith(".meta"))
+ if (entry.IsExtension(".meta"))
{
string xml = TextUtil.GetUTF8Text(data);
@@ -53,12 +58,64 @@ namespace CodeWalker.GameFiles
}
}
+ public void Load(byte[] data, RpfFileEntry entry)
+ {
+ RpfFileEntry = entry;
+ Name = entry.Name;
+ FilePath = Name;
+
+
+ if (entry.IsExtension(".meta"))
+ {
+ using var textReader = new StreamReader(new MemoryStream(data), Encoding.UTF8);
+
+ using var xmlReader = XmlReader.Create(textReader);
+
+ while (xmlReader.Read())
+ {
+ xmlReader.MoveToContent();
+
+ //var _ = xmlReader.Name switch
+ //{
+ // "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
+ // "InitDatas" => LoadInitDatas(xmlReader),
+ // "txdRelationships" => LoadTxdRelationships(xmlReader),
+ // _ => throw new Exception()
+ //};
+
+ switch (xmlReader.Name)
+ {
+ case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
+ ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd");
+ break;
+ case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
+ LoadInitDatas(xmlReader);
+ break;
+ case string Name when Name.Equals("txdRelationships", StringComparison.OrdinalIgnoreCase):
+ LoadTxdRelationships(xmlReader);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ //ResidentTxd = Xml.GetChildInnerText(xmldoc.SelectSingleNode("CVehicleModelInfo__InitDataList"), "residentTxd");
+
+ //LoadInitDatas(xmldoc);
+
+ //LoadTxdRelationships(xmldoc);
+
+ Loaded = true;
+ }
+ }
+
private void LoadInitDatas(XmlDocument xmldoc)
{
XmlNodeList items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/InitDatas/Item | CVehicleModelInfo__InitDataList/InitDatas/item");
- InitDatas = new List();
+ InitDatas = new List(items.Count);
for (int i = 0; i < items.Count; i++)
{
var node = items[i];
@@ -68,6 +125,53 @@ namespace CodeWalker.GameFiles
}
}
+ private void LoadInitDatas(XmlReader reader)
+ {
+ if (!reader.IsStartElement() || reader.Name != "InitDatas")
+ {
+ throw new InvalidOperationException("XmlReader is not at start element of \"InitDatas\"");
+ }
+
+ InitDatas = new List();
+
+ reader.ReadStartElement("InitDatas");
+
+ while (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "Item")
+ {
+ if (reader.IsStartElement())
+ {
+ VehicleInitData d = new VehicleInitData();
+ d.Load(reader);
+ InitDatas.Add(d);
+ }
+ }
+ }
+
+ private void LoadTxdRelationships(XmlReader reader)
+ {
+ if (reader.IsEmptyElement)
+ {
+ TxdRelationships = new Dictionary();
+ reader.ReadStartElement();
+ return;
+ }
+ TxdRelationships = new Dictionary();
+
+ foreach(var item in Xml.IterateItems(reader, "txdRelationships"))
+ {
+ var childstr = item.Element("child")?.Value;
+ var parentstr = item.Element("parent")?.Value;
+
+ if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
+ {
+ if (!TxdRelationships.ContainsKey(childstr))
+ {
+ TxdRelationships.Add(childstr, parentstr);
+ }
+ }
+ }
+ }
+
private void LoadTxdRelationships(XmlDocument xmldoc)
{
XmlNodeList items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/txdRelationships/Item | CVehicleModelInfo__InitDataList/txdRelationships/item");
@@ -89,8 +193,6 @@ namespace CodeWalker.GameFiles
}
}
}
-
-
}
@@ -106,7 +208,7 @@ namespace CodeWalker.GameFiles
public string expressionName { get; set; } //null
public string animConvRoofDictName { get; set; } //null
public string animConvRoofName { get; set; } //null
- public string animConvRoofWindowsAffected { get; set; } //
+ public string[] animConvRoofWindowsAffected { get; set; } //
public string ptfxAssetName { get; set; } //weap_xs_vehicle_weapons
public string audioNameHash { get; set; } //
public string layout { get; set; } //LAYOUT_STD_ARENA_1HONLY
@@ -185,6 +287,354 @@ namespace CodeWalker.GameFiles
public VehicleOverrideRagdollThreshold pOverrideRagdollThreshold { get; set; } //
public string[] firstPersonDrivebyData { get; set; } //// - STD_IMPALER2_FRONT_LEFT
// - STD_IMPALER2_FRONT_RIGHT
//
+ public void Load(XmlReader reader)
+ {
+ reader.ReadStartElement("Item");
+ while (reader.Name != "Item" && reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Item")
+ {
+ reader.ReadEndElement();
+ return;
+ }
+ if (reader.IsStartElement())
+ {
+ while (reader.IsStartElement())
+ {
+ switch (reader.Name)
+ {
+ case "modelName":
+ modelName = Xml.GetChildInnerText(reader, "modelName");
+ break;
+ case "txdName":
+ txdName = Xml.GetChildInnerText(reader, "txdName");
+ break;
+ case "handlingId":
+ handlingId = Xml.GetChildInnerText(reader, "handlingId");
+ break;
+ case "gameName":
+ gameName = Xml.GetChildInnerText(reader, "gameName");
+ break;
+ case "vehicleMakeName":
+ vehicleMakeName = Xml.GetChildInnerText(reader, "vehicleMakeName");
+ break;
+ case "expressionDictName":
+ expressionDictName = Xml.GetChildInnerText(reader, "expressionDictName");
+ break;
+ case "expressionName":
+ expressionName = Xml.GetChildInnerText(reader, "expressionName");
+ break;
+ case "animConvRoofDictName":
+ animConvRoofDictName = Xml.GetChildInnerText(reader, "animConvRoofDictName");
+ break;
+ case "animConvRoofName":
+ animConvRoofName = Xml.GetChildInnerText(reader, "animConvRoofName");
+ break;
+ case "animConvRoofWindowsAffected":
+ animConvRoofWindowsAffected = GetStringItemArray(reader, "animConvRoofWindowsAffected");
+ break;
+ case "ptfxAssetName":
+ ptfxAssetName = Xml.GetChildInnerText(reader, "ptfxAssetName");
+ break;
+ case "audioNameHash":
+ audioNameHash = Xml.GetChildInnerText(reader, "audioNameHash");
+ break;
+ case "layout":
+ layout = Xml.GetChildInnerText(reader, "layout");
+ break;
+ case "coverBoundOffsets":
+ coverBoundOffsets = Xml.GetChildInnerText(reader, "coverBoundOffsets");
+ break;
+ case "explosionInfo":
+ explosionInfo = Xml.GetChildInnerText(reader, "explosionInfo");
+ break;
+ case "scenarioLayout":
+ scenarioLayout = Xml.GetChildInnerText(reader, "scenarioLayout");
+ break;
+ case "cameraName":
+ cameraName = Xml.GetChildInnerText(reader, "cameraName");
+ break;
+ case "aimCameraName":
+ aimCameraName = Xml.GetChildInnerText(reader, "aimCameraName");
+ break;
+ case "bonnetCameraName":
+ bonnetCameraName = Xml.GetChildInnerText(reader, "bonnetCameraName");
+ break;
+ case "povCameraName":
+ povCameraName = Xml.GetChildInnerText(reader, "povCameraName");
+ break;
+ case "FirstPersonDriveByIKOffset":
+ FirstPersonDriveByIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByIKOffset");
+ break;
+ case "FirstPersonDriveByUnarmedIKOffset":
+ FirstPersonDriveByUnarmedIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByUnarmedIKOffset");
+ break;
+ case "FirstPersonProjectileDriveByIKOffset":
+ FirstPersonProjectileDriveByIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonProjectileDriveByIKOffset");
+ break;
+ case "FirstPersonProjectileDriveByPassengerIKOffset":
+ FirstPersonProjectileDriveByPassengerIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonProjectileDriveByPassengerIKOffset");
+ break;
+ case "FirstPersonDriveByRightPassengerIKOffset":
+ FirstPersonDriveByRightPassengerIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByRightPassengerIKOffset");
+ break;
+ case "FirstPersonDriveByRightPassengerUnarmedIKOffset":
+ FirstPersonDriveByRightPassengerUnarmedIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByRightPassengerUnarmedIKOffset");
+ break;
+ case "FirstPersonMobilePhoneOffset":
+ FirstPersonMobilePhoneOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonMobilePhoneOffset");
+ break;
+ case "FirstPersonPassengerMobilePhoneOffset":
+ FirstPersonPassengerMobilePhoneOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonPassengerMobilePhoneOffset");
+ break;
+ case "PovCameraOffset":
+ PovCameraOffset = Xml.GetChildVector3Attributes(reader, "PovCameraOffset");
+ break;
+ case "PovCameraVerticalAdjustmentForRollCage":
+ PovCameraVerticalAdjustmentForRollCage = Xml.GetChildVector3Attributes(reader, "PovCameraVerticalAdjustmentForRollCage");
+ break;
+ case "PovPassengerCameraOffset":
+ PovPassengerCameraOffset = Xml.GetChildVector3Attributes(reader, "PovPassengerCameraOffset");
+ break;
+ case "PovRearPassengerCameraOffset":
+ PovRearPassengerCameraOffset = Xml.GetChildVector3Attributes(reader, "PovRearPassengerCameraOffset");
+ break;
+ case "vfxInfoName":
+ vfxInfoName = Xml.GetChildInnerText(reader, "vfxInfoName");
+ break;
+ case "shouldUseCinematicViewMode":
+ shouldUseCinematicViewMode = Xml.GetChildBoolAttribute(reader, "shouldUseCinematicViewMode");
+ break;
+ case "shouldCameraTransitionOnClimbUpDown":
+ shouldCameraTransitionOnClimbUpDown = Xml.GetChildBoolAttribute(reader, "shouldCameraTransitionOnClimbUpDown");
+ break;
+ case "shouldCameraIgnoreExiting":
+ shouldCameraIgnoreExiting = Xml.GetChildBoolAttribute(reader, "shouldCameraIgnoreExiting");
+ break;
+ case "AllowPretendOccupants":
+ AllowPretendOccupants = Xml.GetChildBoolAttribute(reader, "AllowPretendOccupants");
+ break;
+ case "AllowJoyriding":
+ AllowJoyriding = Xml.GetChildBoolAttribute(reader, "AllowJoyriding");
+ break;
+ case "AllowSundayDriving":
+ AllowSundayDriving = Xml.GetChildBoolAttribute(reader, "AllowSundayDriving");
+ break;
+ case "AllowBodyColorMapping":
+ AllowBodyColorMapping = Xml.GetChildBoolAttribute(reader, "AllowBodyColorMapping");
+ break;
+ case "wheelScale":
+ wheelScale = Xml.GetChildFloatAttribute(reader, "wheelScale");
+ break;
+ case "wheelScaleRear":
+ wheelScaleRear = Xml.GetChildFloatAttribute(reader, "wheelScaleRear");
+ break;
+ case "dirtLevelMin":
+ dirtLevelMin = Xml.GetChildFloatAttribute(reader, "dirtLevelMin");
+ break;
+ case "dirtLevelMax":
+ dirtLevelMax = Xml.GetChildFloatAttribute(reader, "dirtLevelMax");
+ break;
+ case "envEffScaleMin":
+ envEffScaleMin = Xml.GetChildFloatAttribute(reader, "envEffScaleMin");
+ break;
+ case "envEffScaleMax":
+ envEffScaleMax = Xml.GetChildFloatAttribute(reader, "envEffScaleMax");
+ break;
+ case "envEffScaleMin2":
+ envEffScaleMin2 = Xml.GetChildFloatAttribute(reader, "envEffScaleMin2");
+ break;
+ case "envEffScaleMax2":
+ envEffScaleMax2 = Xml.GetChildFloatAttribute(reader, "envEffScaleMax2");
+ break;
+ case "damageMapScale":
+ damageMapScale = Xml.GetChildFloatAttribute(reader, "damageMapScale");
+ break;
+ case "damageOffsetScale":
+ damageOffsetScale = Xml.GetChildFloatAttribute(reader, "damageOffsetScale");
+ break;
+ case "diffuseTint":
+ diffuseTint = new Color4(Convert.ToUInt32(Xml.GetChildStringAttribute(reader, "diffuseTint", "value").Replace("0x", ""), 16)); ;
+ break;
+ case "steerWheelMult":
+ steerWheelMult = Xml.GetChildFloatAttribute(reader, "steerWheelMult");
+ break;
+ case "HDTextureDist":
+ HDTextureDist = Xml.GetChildFloatAttribute(reader, "HDTextureDist");
+ break;
+ case "lodDistances":
+ lodDistances = GetFloatArray(reader, "lodDistances", '\n');
+ break;
+ case "minSeatHeight":
+ minSeatHeight = Xml.GetChildFloatAttribute(reader, "minSeatHeight");
+ break;
+ case "identicalModelSpawnDistance":
+ identicalModelSpawnDistance = Xml.GetChildFloatAttribute(reader, "identicalModelSpawnDistance");
+ break;
+ case "maxNumOfSameColor":
+ maxNumOfSameColor = Xml.GetChildIntAttribute(reader, "maxNumOfSameColor");
+ break;
+ case "defaultBodyHealth":
+ defaultBodyHealth = Xml.GetChildFloatAttribute(reader, "defaultBodyHealth");
+ break;
+ case "pretendOccupantsScale":
+ pretendOccupantsScale = Xml.GetChildFloatAttribute(reader, "pretendOccupantsScale");
+ break;
+ case "visibleSpawnDistScale":
+ visibleSpawnDistScale = Xml.GetChildFloatAttribute(reader, "visibleSpawnDistScale");
+ break;
+ case "trackerPathWidth":
+ trackerPathWidth = Xml.GetChildFloatAttribute(reader, "trackerPathWidth");
+ break;
+ case "weaponForceMult":
+ weaponForceMult = Xml.GetChildFloatAttribute(reader, "weaponForceMult");
+ break;
+ case "frequency":
+ frequency = Xml.GetChildFloatAttribute(reader, "frequency");
+ break;
+ case "swankness":
+ swankness = Xml.GetChildInnerText(reader, "swankness");
+ break;
+ case "maxNum":
+ maxNum = Xml.GetChildIntAttribute(reader, "maxNum", "value");
+ break;
+ case "flags":
+ flags = GetStringArray(reader, "flags", ' ');
+ break;
+ case "type":
+ type = Xml.GetChildInnerText(reader, "type");
+ break;
+ case "plateType":
+ plateType = Xml.GetChildInnerText(reader, "plateType");
+ break;
+ case "dashboardType":
+ dashboardType = Xml.GetChildInnerText(reader, "dashboardType");
+ break;
+ case "vehicleClass":
+ vehicleClass = Xml.GetChildInnerText(reader, "vehicleClass");
+ break;
+ case "wheelType":
+ wheelType = Xml.GetChildInnerText(reader, "wheelType");
+ break;
+ case "trailers":
+ trailers = GetStringItemArray(reader, "trailers");
+ break;
+ case "additionalTrailers":
+ additionalTrailers = GetStringItemArray(reader, "additionalTrailers");
+ break;
+ case "drivers":
+ if (reader.IsEmptyElement)
+ {
+ reader.ReadStartElement();
+ break;
+ }
+
+ var _drivers = new List();
+
+ foreach (var item in Xml.IterateItems(reader, "drivers"))
+ {
+ var driver = new VehicleDriver();
+ driver.driverName = item.Element("driverName")?.Value ?? string.Empty;
+ driver.npcName = item.Element("npcName")?.Value ?? string.Empty;
+
+ if (!string.IsNullOrEmpty(driver.npcName) || !string.IsNullOrEmpty(driver.driverName))
+ {
+ _drivers.Add(driver);
+ }
+ }
+ drivers = _drivers.ToArray();
+ break;
+ case "doorsWithCollisionWhenClosed":
+ doorsWithCollisionWhenClosed = GetStringItemArray(reader, "doorsWithCollisionWhenClosed");
+ break;
+ case "driveableDoors":
+ driveableDoors = GetStringItemArray(reader, "driveableDoors");
+ break;
+ case "bumpersNeedToCollideWithMap":
+ bumpersNeedToCollideWithMap = Xml.GetChildBoolAttribute(reader, "bumpersNeedToCollideWithMap", "value");
+ break;
+ case "needsRopeTexture":
+ needsRopeTexture = Xml.GetChildBoolAttribute(reader, "needsRopeTexture", "value");
+ break;
+ case "requiredExtras":
+ requiredExtras = GetStringArray(reader, "requiredExtras", ' ');
+ break;
+ case "rewards":
+ rewards = GetStringItemArray(reader, "rewards");
+ break;
+ case "cinematicPartCamera":
+ cinematicPartCamera = GetStringItemArray(reader, "cinematicPartCamera");
+ break;
+ case "NmBraceOverrideSet":
+ NmBraceOverrideSet = Xml.GetChildInnerText(reader, "NmBraceOverrideSet");
+ break;
+ case "buoyancySphereOffset":
+ buoyancySphereOffset = Xml.GetChildVector3Attributes(reader, "buoyancySphereOffset");
+ break;
+ case "buoyancySphereSizeScale":
+ buoyancySphereSizeScale = Xml.GetChildFloatAttribute(reader, "buoyancySphereSizeScale", "value");
+ break;
+ case "pOverrideRagdollThreshold":
+ if (reader.IsEmptyElement)
+ {
+ reader.ReadStartElement();
+ break;
+ }
+
+ switch (reader.GetAttribute("type"))
+ {
+ case "NULL":
+ break;
+ case "CVehicleModelInfo__CVehicleOverrideRagdollThreshold":
+ reader.ReadStartElement();
+ pOverrideRagdollThreshold = new VehicleOverrideRagdollThreshold();
+ while (reader.MoveToContent() == XmlNodeType.Element)
+ {
+ if (reader.Name == "MinComponent")
+ {
+ pOverrideRagdollThreshold.MinComponent = Xml.GetChildIntAttribute(reader, "MinComponent", "value");
+ }
+ else if (reader.Name == "MaxComponent")
+ {
+ pOverrideRagdollThreshold.MaxComponent = Xml.GetChildIntAttribute(reader, "MaxComponent", "value");
+ }
+ else if (reader.Name == "ThresholdMult")
+ {
+ pOverrideRagdollThreshold.ThresholdMult = Xml.GetChildFloatAttribute(reader, "ThresholdMult", "value");
+ } else
+ {
+ break;
+ }
+ }
+
+ reader.ReadEndElement();
+ break;
+ default:
+ break;
+ }
+ break;
+ case "firstPersonDrivebyData":
+ firstPersonDrivebyData = GetStringItemArray(reader, "firstPersonDrivebyData");
+ break;
+ case "extraIncludes":
+ extraIncludes = GetStringItemArray(reader, "extraIncludes");
+ break;
+ case "FirstPersonDriveByLeftPassengerIKOffset":
+ case "FirstPersonDriveByLeftPassengerUnarmedIKOffset":
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ else
+ {
+ reader.Read();
+ }
+ }
+
+ reader.ReadEndElement();
+ }
public void Load(XmlNode node)
{
@@ -197,7 +647,7 @@ namespace CodeWalker.GameFiles
expressionName = Xml.GetChildInnerText(node, "expressionName");
animConvRoofDictName = Xml.GetChildInnerText(node, "animConvRoofDictName");
animConvRoofName = Xml.GetChildInnerText(node, "animConvRoofName");
- animConvRoofWindowsAffected = Xml.GetChildInnerText(node, "animConvRoofWindowsAffected");//?
+ animConvRoofWindowsAffected = GetStringItemArray(node, "animConvRoofWindowsAffected");//?
ptfxAssetName = Xml.GetChildInnerText(node, "ptfxAssetName");
audioNameHash = Xml.GetChildInnerText(node, "audioNameHash");
layout = Xml.GetChildInnerText(node, "layout");
@@ -327,29 +777,73 @@ namespace CodeWalker.GameFiles
if (getStringArrayList.Count == 0) return null;
return getStringArrayList.ToArray();
}
+
+ private string[] GetStringItemArray(XmlReader node, string childName)
+ {
+ if (node.IsEmptyElement)
+ {
+ node.ReadStartElement();
+ return null;
+ }
+
+ lock(getStringArrayList)
+ {
+ getStringArrayList.Clear();
+ node.ReadStartElement();
+ while (node.MoveToContent() == XmlNodeType.Element && node.Name == "Item")
+ {
+ var istr = node.ReadElementContentAsString();
+ if (!string.IsNullOrEmpty(istr))
+ {
+ getStringArrayList.Add(istr);
+ }
+ }
+ node.ReadEndElement();
+
+ if (getStringArrayList.Count == 0)
+ return null;
+ return getStringArrayList.ToArray();
+ }
+ }
+
+ private string[] GetStringArray(string ldastr, char delimiter)
+ {
+ var ldarr = ldastr?.Split(delimiter);
+ if (ldarr == null) return null;
+ lock(getStringArrayList)
+ {
+ getStringArrayList.Clear();
+ foreach (var ldstr in ldarr)
+ {
+ var ldt = ldstr?.Trim();
+ if (!string.IsNullOrEmpty(ldt))
+ {
+ getStringArrayList.Add(ldt);
+ }
+ }
+ if (getStringArrayList.Count == 0) return null;
+ return getStringArrayList.ToArray();
+ }
+ }
+
private string[] GetStringArray(XmlNode node, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(node, childName);
- var ldarr = ldastr?.Split(delimiter);
- if (ldarr == null) return null;
- getStringArrayList.Clear();
- foreach (var ldstr in ldarr)
- {
- var ldt = ldstr?.Trim();
- if (!string.IsNullOrEmpty(ldt))
- {
- getStringArrayList.Add(ldt);
- }
- }
- if (getStringArrayList.Count == 0) return null;
- return getStringArrayList.ToArray();
+ return GetStringArray(ldastr, delimiter);
}
- private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
+
+ private string[] GetStringArray(XmlReader reader, string childName, char delimiter)
+ {
+ var ldastr = Xml.GetChildInnerText(reader, childName);
+ return GetStringArray(ldastr, delimiter);
+ }
+
+ private unsafe float[] GetFloatArray(string ldastr, char delimiter)
{
- var ldastr = Xml.GetChildInnerText(node, childName);
var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null;
- getFloatArrayList.Clear();
+ var floats = stackalloc float[ldarr.Length];
+ var i = 0;
foreach (var ldstr in ldarr)
{
var ldt = ldstr?.Trim();
@@ -358,12 +852,30 @@ namespace CodeWalker.GameFiles
float f;
if (FloatUtil.TryParse(ldt, out f))
{
- getFloatArrayList.Add(f);
+ floats[i] = f;
+ i++;
}
}
}
- if (getFloatArrayList.Count == 0) return null;
- return getFloatArrayList.ToArray();
+ if (i == 0) return null;
+
+ var result = new float[i];
+
+ Marshal.Copy((IntPtr)floats, result, 0, i);
+
+ return result;
+ }
+
+ private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
+ {
+ var ldastr = Xml.GetChildInnerText(node, childName);
+ return GetFloatArray(ldastr, delimiter);
+ }
+
+ private float[] GetFloatArray(XmlReader reader, string childName, char delimiter)
+ {
+ var ldastr = Xml.GetChildInnerText(reader, childName);
+ return GetFloatArray(ldastr, delimiter);
}
private static List getStringArrayList = new List(); //kinda hacky..
@@ -374,28 +886,283 @@ namespace CodeWalker.GameFiles
{
return modelName;
}
+
+ public override bool Equals(object obj)
+ {
+ return obj is VehicleInitData data &&
+ modelName == data.modelName &&
+ txdName == data.txdName &&
+ handlingId == data.handlingId &&
+ gameName == data.gameName &&
+ vehicleMakeName == data.vehicleMakeName &&
+ expressionDictName == data.expressionDictName &&
+ expressionName == data.expressionName &&
+ animConvRoofDictName == data.animConvRoofDictName &&
+ animConvRoofName == data.animConvRoofName &&
+ animConvRoofWindowsAffected == data.animConvRoofWindowsAffected &&
+ ptfxAssetName == data.ptfxAssetName &&
+ audioNameHash == data.audioNameHash &&
+ layout == data.layout &&
+ coverBoundOffsets == data.coverBoundOffsets &&
+ explosionInfo == data.explosionInfo &&
+ scenarioLayout == data.scenarioLayout &&
+ cameraName == data.cameraName &&
+ aimCameraName == data.aimCameraName &&
+ bonnetCameraName == data.bonnetCameraName &&
+ povCameraName == data.povCameraName &&
+ FirstPersonDriveByIKOffset.Equals(data.FirstPersonDriveByIKOffset) &&
+ FirstPersonDriveByUnarmedIKOffset.Equals(data.FirstPersonDriveByUnarmedIKOffset) &&
+ FirstPersonProjectileDriveByIKOffset.Equals(data.FirstPersonProjectileDriveByIKOffset) &&
+ FirstPersonProjectileDriveByPassengerIKOffset.Equals(data.FirstPersonProjectileDriveByPassengerIKOffset) &&
+ FirstPersonDriveByRightPassengerIKOffset.Equals(data.FirstPersonDriveByRightPassengerIKOffset) &&
+ FirstPersonDriveByRightPassengerUnarmedIKOffset.Equals(data.FirstPersonDriveByRightPassengerUnarmedIKOffset) &&
+ FirstPersonMobilePhoneOffset.Equals(data.FirstPersonMobilePhoneOffset) &&
+ FirstPersonPassengerMobilePhoneOffset.Equals(data.FirstPersonPassengerMobilePhoneOffset) &&
+ PovCameraOffset.Equals(data.PovCameraOffset) &&
+ PovCameraVerticalAdjustmentForRollCage.Equals(data.PovCameraVerticalAdjustmentForRollCage) &&
+ PovPassengerCameraOffset.Equals(data.PovPassengerCameraOffset) &&
+ PovRearPassengerCameraOffset.Equals(data.PovRearPassengerCameraOffset) &&
+ vfxInfoName == data.vfxInfoName &&
+ shouldUseCinematicViewMode == data.shouldUseCinematicViewMode &&
+ shouldCameraTransitionOnClimbUpDown == data.shouldCameraTransitionOnClimbUpDown &&
+ shouldCameraIgnoreExiting == data.shouldCameraIgnoreExiting &&
+ AllowPretendOccupants == data.AllowPretendOccupants &&
+ AllowJoyriding == data.AllowJoyriding &&
+ AllowSundayDriving == data.AllowSundayDriving &&
+ AllowBodyColorMapping == data.AllowBodyColorMapping &&
+ wheelScale == data.wheelScale &&
+ wheelScaleRear == data.wheelScaleRear &&
+ dirtLevelMin == data.dirtLevelMin &&
+ dirtLevelMax == data.dirtLevelMax &&
+ envEffScaleMin == data.envEffScaleMin &&
+ envEffScaleMax == data.envEffScaleMax &&
+ envEffScaleMin2 == data.envEffScaleMin2 &&
+ envEffScaleMax2 == data.envEffScaleMax2 &&
+ damageMapScale == data.damageMapScale &&
+ damageOffsetScale == data.damageOffsetScale &&
+ diffuseTint.Equals(data.diffuseTint) &&
+ steerWheelMult == data.steerWheelMult &&
+ HDTextureDist == data.HDTextureDist &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(lodDistances, data.lodDistances) &&
+ minSeatHeight == data.minSeatHeight &&
+ identicalModelSpawnDistance == data.identicalModelSpawnDistance &&
+ maxNumOfSameColor == data.maxNumOfSameColor &&
+ defaultBodyHealth == data.defaultBodyHealth &&
+ pretendOccupantsScale == data.pretendOccupantsScale &&
+ visibleSpawnDistScale == data.visibleSpawnDistScale &&
+ trackerPathWidth == data.trackerPathWidth &&
+ weaponForceMult == data.weaponForceMult &&
+ frequency == data.frequency &&
+ swankness == data.swankness &&
+ maxNum == data.maxNum &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(flags, data.flags) &&
+ type == data.type &&
+ plateType == data.plateType &&
+ dashboardType == data.dashboardType &&
+ vehicleClass == data.vehicleClass &&
+ wheelType == data.wheelType &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(trailers, data.trailers) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(additionalTrailers, data.additionalTrailers) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(drivers, data.drivers) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(extraIncludes, data.extraIncludes) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(doorsWithCollisionWhenClosed, data.doorsWithCollisionWhenClosed) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(driveableDoors, data.driveableDoors) &&
+ bumpersNeedToCollideWithMap == data.bumpersNeedToCollideWithMap &&
+ needsRopeTexture == data.needsRopeTexture &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(requiredExtras, data.requiredExtras) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(rewards, data.rewards) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(cinematicPartCamera, data.cinematicPartCamera) &&
+ NmBraceOverrideSet == data.NmBraceOverrideSet &&
+ buoyancySphereOffset.Equals(data.buoyancySphereOffset) &&
+ buoyancySphereSizeScale == data.buoyancySphereSizeScale &&
+ EqualityComparer.Default.Equals(pOverrideRagdollThreshold, data.pOverrideRagdollThreshold) &&
+ StructuralComparisons.StructuralEqualityComparer.Equals(firstPersonDrivebyData, data.firstPersonDrivebyData);
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = 1102137281;
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(modelName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(txdName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(handlingId);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(gameName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(vehicleMakeName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(expressionDictName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(expressionName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(animConvRoofDictName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(animConvRoofName);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(animConvRoofWindowsAffected);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ptfxAssetName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(audioNameHash);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(layout);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(coverBoundOffsets);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(explosionInfo);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(scenarioLayout);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(cameraName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(aimCameraName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(bonnetCameraName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(povCameraName);
+ hashCode = hashCode * -1521134295 + FirstPersonDriveByIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonDriveByUnarmedIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonProjectileDriveByIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonProjectileDriveByPassengerIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonDriveByRightPassengerIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonDriveByRightPassengerUnarmedIKOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonMobilePhoneOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + FirstPersonPassengerMobilePhoneOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + PovCameraOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + PovCameraVerticalAdjustmentForRollCage.GetHashCode();
+ hashCode = hashCode * -1521134295 + PovPassengerCameraOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + PovRearPassengerCameraOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(vfxInfoName);
+ hashCode = hashCode * -1521134295 + shouldUseCinematicViewMode.GetHashCode();
+ hashCode = hashCode * -1521134295 + shouldCameraTransitionOnClimbUpDown.GetHashCode();
+ hashCode = hashCode * -1521134295 + shouldCameraIgnoreExiting.GetHashCode();
+ hashCode = hashCode * -1521134295 + AllowPretendOccupants.GetHashCode();
+ hashCode = hashCode * -1521134295 + AllowJoyriding.GetHashCode();
+ hashCode = hashCode * -1521134295 + AllowSundayDriving.GetHashCode();
+ hashCode = hashCode * -1521134295 + AllowBodyColorMapping.GetHashCode();
+ hashCode = hashCode * -1521134295 + wheelScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + wheelScaleRear.GetHashCode();
+ hashCode = hashCode * -1521134295 + dirtLevelMin.GetHashCode();
+ hashCode = hashCode * -1521134295 + dirtLevelMax.GetHashCode();
+ hashCode = hashCode * -1521134295 + envEffScaleMin.GetHashCode();
+ hashCode = hashCode * -1521134295 + envEffScaleMax.GetHashCode();
+ hashCode = hashCode * -1521134295 + envEffScaleMin2.GetHashCode();
+ hashCode = hashCode * -1521134295 + envEffScaleMax2.GetHashCode();
+ hashCode = hashCode * -1521134295 + damageMapScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + damageOffsetScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + diffuseTint.GetHashCode();
+ hashCode = hashCode * -1521134295 + steerWheelMult.GetHashCode();
+ hashCode = hashCode * -1521134295 + HDTextureDist.GetHashCode();
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(lodDistances);
+ hashCode = hashCode * -1521134295 + minSeatHeight.GetHashCode();
+ hashCode = hashCode * -1521134295 + identicalModelSpawnDistance.GetHashCode();
+ hashCode = hashCode * -1521134295 + maxNumOfSameColor.GetHashCode();
+ hashCode = hashCode * -1521134295 + defaultBodyHealth.GetHashCode();
+ hashCode = hashCode * -1521134295 + pretendOccupantsScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + visibleSpawnDistScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + trackerPathWidth.GetHashCode();
+ hashCode = hashCode * -1521134295 + weaponForceMult.GetHashCode();
+ hashCode = hashCode * -1521134295 + frequency.GetHashCode();
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(swankness);
+ hashCode = hashCode * -1521134295 + maxNum.GetHashCode();
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(flags);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(type);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(plateType);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(dashboardType);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(vehicleClass);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(wheelType);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(trailers);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(additionalTrailers);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(drivers);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(extraIncludes);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(doorsWithCollisionWhenClosed);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(driveableDoors);
+ hashCode = hashCode * -1521134295 + bumpersNeedToCollideWithMap.GetHashCode();
+ hashCode = hashCode * -1521134295 + needsRopeTexture.GetHashCode();
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(requiredExtras);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(rewards);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(cinematicPartCamera);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(NmBraceOverrideSet);
+ hashCode = hashCode * -1521134295 + buoyancySphereOffset.GetHashCode();
+ hashCode = hashCode * -1521134295 + buoyancySphereSizeScale.GetHashCode();
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(pOverrideRagdollThreshold);
+ hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(firstPersonDrivebyData);
+ return hashCode;
+ }
+
+ public static bool operator ==(VehicleInitData left, VehicleInitData right)
+ {
+ return EqualityComparer.Default.Equals(left, right);
+ }
+
+ public static bool operator !=(VehicleInitData left, VehicleInitData right)
+ {
+ return !(left == right);
+ }
}
- public class VehicleOverrideRagdollThreshold
+ public class VehicleOverrideRagdollThreshold : IEquatable
{
public int MinComponent { get; set; }
public int MaxComponent { get; set; }
public float ThresholdMult { get; set; }
+ public override bool Equals(object obj)
+ {
+ return obj is VehicleOverrideRagdollThreshold threshold && Equals(threshold);
+ }
+
+ public bool Equals(VehicleOverrideRagdollThreshold other)
+ {
+ return MinComponent == other.MinComponent &&
+ MaxComponent == other.MaxComponent &&
+ ThresholdMult == other.ThresholdMult;
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = 1172526364;
+ hashCode = hashCode * -1521134295 + MinComponent.GetHashCode();
+ hashCode = hashCode * -1521134295 + MaxComponent.GetHashCode();
+ hashCode = hashCode * -1521134295 + ThresholdMult.GetHashCode();
+ return hashCode;
+ }
+
public override string ToString()
{
return MinComponent.ToString() + ", " + MaxComponent.ToString() + ", " + ThresholdMult.ToString();
}
+
+ public static bool operator ==(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
+ {
+ return !(left == right);
+ }
}
- public class VehicleDriver
+ public class VehicleDriver : IEquatable
{
public string driverName { get; set; }
public string npcName { get; set; }
+ public override bool Equals(object obj)
+ {
+ return obj is VehicleDriver driver && Equals(driver);
+ }
+
+ public bool Equals(VehicleDriver other)
+ {
+ return driverName == other.driverName &&
+ npcName == other.npcName;
+ }
+
+ public override int GetHashCode()
+ {
+ int hashCode = -1906737521;
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(driverName);
+ hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(npcName);
+ return hashCode;
+ }
+
public override string ToString()
{
return driverName + ", " + npcName;
}
+
+ public static bool operator ==(VehicleDriver left, VehicleDriver right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(VehicleDriver left, VehicleDriver right)
+ {
+ return !(left == right);
+ }
}
}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs
index 0ed54f0..8acbfcf 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/YddFile.cs
@@ -75,20 +75,17 @@ namespace CodeWalker.GameFiles
var drawable = drawables[i];
var hash = hashes[i];
drawable.Hash = hash;
- if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
+ if (drawable.Name == null || drawable.Name.EndsWith("#dd", StringComparison.OrdinalIgnoreCase))
{
string hstr = JenkIndex.TryGetString(hash);
if (!string.IsNullOrEmpty(hstr))
{
drawable.Name = hstr;
}
- else
- { }
}
}
Drawables = Dict.Values.ToArray();
-
}
Loaded = true;
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs
index fc99f3c..ef5fe03 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/YmapFile.cs
@@ -862,6 +862,7 @@ namespace CodeWalker.GameFiles
ChildYmaps[i] = gfc.GetYmap(chash);
if (ChildYmaps[i] == null)
{
+ Console.WriteLine($"Couldn't find child ymap! {chash} for {Name}");
//couldn't find child ymap..
}
}
@@ -895,7 +896,10 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < ChildYmaps.Length; i++)
{
var cmap = ChildYmaps[i];
- if (cmap == null) continue; //nothing here..
+ if (cmap == null)
+ {
+ continue; //nothing here..
+ }
//cmap.EnsureChildYmaps();
if ((cmap.Loaded) && (!cmap.MergedWithParent))
{
@@ -956,7 +960,7 @@ namespace CodeWalker.GameFiles
YmapEntityDef p = null;
if ((pymap != null) && (pymap.AllEntities != null))
{
- if ((pind < pymap.AllEntities.Length))
+ if (pind < pymap.AllEntities.Length)
{
p = pymap.AllEntities[pind];
ent.Parent = p;
@@ -964,7 +968,9 @@ namespace CodeWalker.GameFiles
}
}
else
- { }//should only happen if parent ymap not loaded yet...
+ {
+ Console.WriteLine($"Parent not loaded yet for {pymap.Name}");
+ }
}
}
}
@@ -974,8 +980,6 @@ namespace CodeWalker.GameFiles
{
LODLights.Init(Parent.DistantLODLights);
}
- else
- { }
}
}
@@ -987,8 +991,11 @@ namespace CodeWalker.GameFiles
{
//used by the editor to add to the ymap.
- List allents = new List();
- if (AllEntities != null) allents.AddRange(AllEntities);
+ List allents;
+ if (AllEntities != null)
+ allents = new List(AllEntities);
+ else
+ allents = new List();
ent.Index = allents.Count;
ent.Ymap = this;
allents.Add(ent);
@@ -999,8 +1006,11 @@ namespace CodeWalker.GameFiles
{
//root entity, add to roots.
- List rootents = new List();
- if (RootEntities != null) rootents.AddRange(RootEntities);
+ List rootents;
+ if (RootEntities != null)
+ rootents = new List(RootEntities);
+ else
+ rootents = new List();
rootents.Add(ent);
RootEntities = rootents.ToArray();
}
@@ -1012,7 +1022,8 @@ namespace CodeWalker.GameFiles
public bool RemoveEntity(YmapEntityDef ent)
{
//used by the editor to remove from the ymap.
- if (ent == null) return false;
+ if (ent == null)
+ return false;
var res = true;
@@ -1020,23 +1031,32 @@ namespace CodeWalker.GameFiles
List newAllEntities = new List();
List newRootEntities = new List();
- for (int i = 0; i < AllEntities.Length; i++)
+ if (AllEntities != null)
{
- var oent = AllEntities[i];
- oent.Index = newAllEntities.Count;
- if (oent != ent) newAllEntities.Add(oent);
- else if (i != idx)
+ for (int i = 0; i < AllEntities.Length; i++)
{
- res = false; //indexes didn't match.. this shouldn't happen!
+ var oent = AllEntities[i];
+ oent.Index = newAllEntities.Count;
+ if (oent != ent)
+ {
+ newAllEntities.Add(oent);
+ }
+ else if (i != idx)
+ {
+ res = false; //indexes didn't match.. this shouldn't happen!
+ }
}
}
- for (int i = 0; i < RootEntities.Length; i++)
+ if (RootEntities != null)
{
- var oent = RootEntities[i];
- if (oent != ent) newRootEntities.Add(oent);
+ for (int i = 0; i < RootEntities.Length; i++)
+ {
+ var oent = RootEntities[i];
+ if (oent != ent) newRootEntities.Add(oent);
+ }
}
- if ((AllEntities.Length == newAllEntities.Count) || (RootEntities.Length == newRootEntities.Count))
+ if (AllEntities == null || AllEntities.Length == newAllEntities.Count || RootEntities == null || RootEntities.Length == newRootEntities.Count)
{
res = false;
}
@@ -1055,7 +1075,8 @@ namespace CodeWalker.GameFiles
public void AddCarGen(YmapCarGen cargen)
{
List cargens = new List();
- if (CarGenerators != null) cargens.AddRange(CarGenerators);
+ if (CarGenerators != null)
+ cargens.AddRange(CarGenerators);
cargen.Ymap = this;
cargens.Add(cargen);
CarGenerators = cargens.ToArray();
@@ -1334,10 +1355,9 @@ namespace CodeWalker.GameFiles
public void SetName(string newname)
{
- var newnamel = newname.ToLowerInvariant();
var newnamex = newname + ".ymap";
- var newhash = JenkHash.GenHash(newnamel);
- JenkIndex.Ensure(newnamel);
+ var newhash = JenkHash.GenHashLower(newname);
+ JenkIndex.EnsureLower(newname);
if (RpfFileEntry != null)
{
RpfFileEntry.Name = newnamex;
@@ -1692,7 +1712,7 @@ namespace CodeWalker.GameFiles
public int Index { get; set; }
public float Distance { get; set; } //used for rendering
- public bool IsVisible; //used for rendering
+ public bool IsWithinLodDist; //used for rendering
public bool ChildrenVisible; //used for rendering
public bool ChildrenRendered; //used when rendering ymap mode to reduce LOD flashing...
public YmapEntityDef Parent { get; set; } //for browsing convenience, also used/updated for rendering
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs
index d86fecc..24b924d 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/YndFile.cs
@@ -1890,7 +1890,7 @@ namespace CodeWalker.GameFiles
{
return 0;
}
- if (str.StartsWith("hash_"))
+ if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{
return Convert.ToUInt32(str.Substring(5), 16);
}
diff --git a/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs
index 6d22181..ed79a47 100644
--- a/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs
+++ b/CodeWalker.Core/GameFiles/FileTypes/YtypFile.cs
@@ -288,16 +288,16 @@ namespace CodeWalker.GameFiles
{ }
NameHash = _CMapTypes.name;
- if ((NameHash == 0) && (entry.NameLower != null))
+ if ((NameHash == 0) && (entry.Name != null))
{
- int ind = entry.NameLower.LastIndexOf('.');
+ int ind = entry.Name.LastIndexOf('.');
if (ind > 0)
{
- NameHash = JenkHash.GenHash(entry.NameLower.Substring(0, ind));
+ NameHash = JenkHash.GenHashLower(entry.Name.AsSpan(0, ind));
}
else
{
- NameHash = JenkHash.GenHash(entry.NameLower);
+ NameHash = JenkHash.GenHashLower(entry.Name);
}
}
diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs
index a61f02b..aa115a1 100644
--- a/CodeWalker.Core/GameFiles/GameFile.cs
+++ b/CodeWalker.Core/GameFiles/GameFile.cs
@@ -1,4 +1,4 @@
-using System;
+ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -10,6 +10,7 @@ namespace CodeWalker.GameFiles
{
public volatile bool Loaded = false;
public volatile bool LoadQueued = false;
+ public DateTime LastLoadTime = DateTime.MinValue;
public RpfFileEntry RpfFileEntry { get; set; }
public string Name { get; set; }
public string FilePath { get; set; } //used by the project form.
@@ -91,7 +92,7 @@ namespace CodeWalker.GameFiles
- public struct GameFileCacheKey
+ public struct GameFileCacheKey : IEquatable
{
public uint Hash { get; set; }
public GameFileType Type { get; set; }
@@ -102,13 +103,22 @@ namespace CodeWalker.GameFiles
Type = type;
}
- public override bool Equals(object obj)
+ public override readonly bool Equals(object obj)
{
- if (obj == null) return false;
- if (obj is not GameFileCacheKey gameFileCacheKey) return false;
+ if (obj == null)
+ return false;
+ if (obj is not GameFileCacheKey gameFileCacheKey)
+ return false;
return gameFileCacheKey.Hash == Hash && gameFileCacheKey.Type == Type;
}
+ public readonly bool Equals(GameFileCacheKey obj)
+ {
+ if (obj == null)
+ return false;
+ return obj.Hash == Hash && obj.Type == Type;
+ }
+
public static bool operator ==(GameFileCacheKey first, GameFileCacheKey second)
{
return first.Equals(second);
@@ -119,12 +129,9 @@ namespace CodeWalker.GameFiles
return !first.Equals(second);
}
- public override int GetHashCode()
+ public override readonly int GetHashCode()
{
return (int)Hash;
}
}
-
-
-
}
diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs
index ea5fc4f..8e3ee66 100644
--- a/CodeWalker.Core/GameFiles/GameFileCache.cs
+++ b/CodeWalker.Core/GameFiles/GameFileCache.cs
@@ -1,9 +1,10 @@
using CodeWalker.Core.Utils;
+using CodeWalker.World;
+using Dasync.Collections;
using SharpDX;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@@ -34,10 +35,10 @@ namespace CodeWalker.GameFiles
private object updateSyncRoot = new object();
private object requestSyncRoot = new object();
-
+ private ReaderWriterLockSlim archetypeLock = new ReaderWriterLockSlim();
private ConcurrentDictionary projectFiles = new ConcurrentDictionary(); //for cache files loaded in project window: ydr,ydd,ytd,yft
- private ConcurrentDictionary projectArchetypes = new ConcurrentDictionary(); //used to override archetypes in world view with project ones
+ private Dictionary projectArchetypes = new Dictionary(); //used to override archetypes in world view with project ones
@@ -59,7 +60,7 @@ namespace CodeWalker.GameFiles
//static cached data loaded at init
- public ConcurrentDictionary YtypDict { get; set; }
+ public Dictionary YtypDict { get; set; }
public List AllCacheFiles { get; set; }
public Dictionary YmapHierarchyDict { get; set; }
@@ -113,6 +114,10 @@ namespace CodeWalker.GameFiles
public bool LoadVehicles = true;
public bool LoadPeds = true;
public bool LoadAudio = true;
+
+ public bool PedsLoaded = false;
+ public bool VehiclesLoaded = false;
+
private bool PreloadedMode = true;
private string GTAFolder;
@@ -142,6 +147,14 @@ namespace CodeWalker.GameFiles
}
}
+ public long MaxMemoryUsage
+ {
+ get
+ {
+ return mainCache.MaxMemoryUsage;
+ }
+ }
+
public GameFileCache(long size, double cacheTime, string folder, string dlc, bool mods, string excludeFolders)
@@ -174,113 +187,146 @@ namespace CodeWalker.GameFiles
GTAFolder = folder;
}
- public void Init(Action updateStatus = null, Action errorLog = null)
+ public volatile bool IsIniting = false;
+ public void Init(Action updateStatus = null, Action errorLog = null, bool force = true)
{
- using var _ = new DisposableTimer("GameFileCache.Init");
- if (updateStatus != null) UpdateStatus += updateStatus;
- if (errorLog != null) ErrorLog += errorLog;
-
- Clear();
-
-
- if (RpfMan == null)
+ if (IsIniting)
{
- //EnableDlc = !string.IsNullOrEmpty(SelectedDlc);
-
-
-
- RpfMan = RpfManager.GetInstance();
- RpfMan.ExcludePaths = GetExcludePaths();
- RpfMan.EnableMods = EnableMods;
- RpfMan.BuildExtendedJenkIndex = BuildExtendedJenkIndex;
- RpfMan.Init(GTAFolder, UpdateStatus, ErrorLog);//, true);
-
-
- InitGlobal();
-
- InitDlc();
-
-
-
- //RE test area!
- //TestAudioRels();
- //TestAudioYmts();
- //TestAudioAwcs();
- //TestMetas();
- //TestPsos();
- //TestRbfs();
- //TestCuts();
- //TestYlds();
- //TestYeds();
- //TestYcds();
- //TestYtds();
- //TestYbns();
- //TestYdrs();
- //TestYdds();
- //TestYfts();
- //TestYpts();
- //TestYnvs();
- //TestYvrs();
- //TestYwrs();
- //TestYmaps();
- //TestYpdbs();
- //TestYfds();
- //TestMrfs();
- //TestFxcs();
- //TestPlacements();
- //TestDrawables();
- //TestCacheFiles();
- //TestHeightmaps();
- //TestWatermaps();
- //GetShadersXml();
- //GetArchetypeTimesList();
- //string typestr = PsoTypes.GetTypesString();
+ return;
}
- else
+ IsIniting = true;
+ try
{
- GC.Collect(); //try free up some of the previously used memory..
+ using var _ = new DisposableTimer("GameFileCache.Init");
+ if (updateStatus != null) UpdateStatus += updateStatus;
+ if (errorLog != null) ErrorLog += errorLog;
+
+
+ if (RpfMan == null)
+ {
+ //EnableDlc = !string.IsNullOrEmpty(SelectedDlc);
+
+ RpfMan = RpfManager.GetInstance();
+ RpfMan.ExcludePaths = GetExcludePaths();
+ RpfMan.EnableMods = EnableMods;
+ RpfMan.BuildExtendedJenkIndex = BuildExtendedJenkIndex;
+ RpfMan.Init(GTAFolder, UpdateStatus, ErrorLog);//, true);
+
+
+ InitGlobal();
+
+ InitDlc();
+
+
+
+
+ //RE test area!
+ //TestAudioRels();
+ //TestAudioYmts();
+ //TestAudioAwcs();
+ //TestMetas();
+ //TestPsos();
+ //TestRbfs();
+ //TestCuts();
+ //TestYlds();
+ //TestYeds();
+ //TestYcds();
+ //TestYtds();
+ //TestYbns();
+ //TestYdrs();
+ //TestYdds();
+ //TestYfts();
+ //TestYpts();
+ //TestYnvs();
+ //TestYvrs();
+ //TestYwrs();
+ //TestYmaps();
+ //TestYpdbs();
+ //TestYfds();
+ //TestMrfs();
+ //TestFxcs();
+ //TestPlacements();
+ //TestDrawables();
+ //TestCacheFiles();
+ //TestHeightmaps();
+ //TestWatermaps();
+ //GetShadersXml();
+ //GetArchetypeTimesList();
+ //string typestr = PsoTypes.GetTypesString();
+ }
+ else
+ {
+ //GC.Collect(); //try free up some of the previously used memory..
+ }
+
+ UpdateStatus?.Invoke("Scan complete");
+
+
+ IsInited = true;
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine(ex);
+ throw;
+ }
+ finally
+ {
+ IsIniting = false;
}
-
- UpdateStatus?.Invoke("Scan complete");
-
-
- IsInited = true;
}
- public void Init(Action updateStatus, Action errorLog, List allRpfs)
+ public void Init(Action updateStatus, Action errorLog, List allRpfs, bool force = true)
{
- using var _ = new DisposableTimer("GameFileCache.Init");
- if (updateStatus != null)
+ if (IsIniting)
{
- UpdateStatus += updateStatus;
+ return;
}
- if (errorLog != null)
+ IsIniting = true;
+ try
{
- ErrorLog += errorLog;
+ using var _ = new DisposableTimer("GameFileCache.Init");
+ if (updateStatus != null)
+ {
+ UpdateStatus += updateStatus;
+ }
+ if (errorLog != null)
+ {
+ ErrorLog += errorLog;
+ }
+ Clear();
+
+ PreloadedMode = true;
+ EnableDlc = true;//just so everything (mainly archetypes) will load..
+ EnableMods = false;
+ RpfMan ??= RpfManager.GetInstance();
+ RpfMan.Init(allRpfs);
+
+
+ AllRpfs = allRpfs;
+ BaseRpfs = allRpfs;
+ DlcRpfs = new List();
+
+
+ Task.WhenAll(
+ Task.Run(() => InitGlobalDicts(force)),
+ Task.Run(() => InitManifestDicts(force)),
+ Task.Run(() => InitGtxds(force)),
+ Task.Run(() => InitArchetypeDicts(force)),
+ Task.Run(() => InitStringDictsAsync(force)),
+ Task.Run(() => InitAudio(force))
+ ).GetAwaiter().GetResult();
+
+ vehicleFiles.Clear();
+
+ IsInited = true;
+ }
+ catch (Exception ex) {
+ ErrorLog?.Invoke(ex.ToString());
+ throw;
+ }
+ finally
+ {
+ IsIniting = false;
}
-
- Clear();
-
- PreloadedMode = true;
- EnableDlc = true;//just so everything (mainly archetypes) will load..
- EnableMods = false;
- RpfMan ??= RpfManager.GetInstance();
- RpfMan.Init(allRpfs);
-
-
- AllRpfs = allRpfs;
- BaseRpfs = allRpfs;
- DlcRpfs = new List();
-
- Task.WhenAll(
- Task.Run(InitGlobalDicts),
- Task.Run(InitManifestDicts),
- Task.Run(InitGtxds),
- Task.Run(InitArchetypeDicts),
- Task.Run(InitStringDicts),
- Task.Run(InitAudio)
- ).GetAwaiter().GetResult();
-
- IsInited = true;
}
private void InitGlobal()
@@ -293,23 +339,27 @@ namespace CodeWalker.GameFiles
InitGlobalDicts();
}
- private void InitDlc()
+ private void InitDlc(bool force = true)
{
InitDlcList();
InitActiveMapRpfFiles();
Task.WhenAll(
- Task.Run(InitMapDicts),
- Task.Run(InitManifestDicts),
- Task.Run(InitGtxds),
- Task.Run(InitMapCaches),
- Task.Run(InitArchetypeDicts),
- Task.Run(InitStringDicts),
- Task.Run(InitVehicles),
- Task.Run(InitPeds),
- Task.Run(InitAudio)
+ Task.Run(() => {
+ InitMapDicts(force);
+ InitMapCaches(force);
+ }),
+ Task.Run(() => InitManifestDicts(force)),
+ Task.Run(() => InitArchetypeDicts(force)),
+ Task.Run(() => InitStringDictsAsync(force)),
+ Task.Run(() => InitVehicles(force)),
+ Task.Run(() => InitPeds(force)),
+ Task.Run(() => InitAudio(force)),
+ Task.Run(() => InitGtxds(force))
).GetAwaiter().GetResult();
+
+ vehicleFiles.Clear();
}
private void InitDlcList()
@@ -343,11 +393,12 @@ namespace CodeWalker.GameFiles
//get dlc path names in the appropriate format for reference by the dlclist paths
- Dictionary dlcDict = new Dictionary(StringComparer.OrdinalIgnoreCase);
- Dictionary dlcDict2 = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ Dictionary dlcDict = new Dictionary(80, StringComparer.OrdinalIgnoreCase);
+ Dictionary dlcDict2 = new Dictionary(80, StringComparer.OrdinalIgnoreCase);
foreach (RpfFile dlcrpf in DlcRpfs)
{
- if (dlcrpf == null) continue;
+ if (dlcrpf == null)
+ continue;
if (dlcrpf.Name.Equals("dlc.rpf", StringComparison.OrdinalIgnoreCase))
{
string path = GetDlcRpfVirtualPath(dlcrpf.Path);
@@ -359,7 +410,6 @@ namespace CodeWalker.GameFiles
-
//find all the paths for patched files in update.rpf and build the dict
DlcPatchedPaths.Clear();
string updrpfpath = "update\\update.rpf";
@@ -547,7 +597,7 @@ namespace CodeWalker.GameFiles
foreach (var rpf in DlcRpfs)
{
- if (rpf.NameLower == "update.rpf")//include this so that files not in child rpf's can be used..
+ if (rpf.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase))//include this so that files not in child rpf's can be used..
{
string path = rpf.Path.Replace('\\', '/');
ActiveMapRpfFiles[path] = rpf;
@@ -868,7 +918,7 @@ namespace CodeWalker.GameFiles
{
if ((fm.platform == null) || (fm.platform == "x64"))
{
- if (path.StartsWith(fm.path))
+ if (path.StartsWith(fm.path, StringComparison.OrdinalIgnoreCase))
{
path = path.Replace(fm.path, fm.mountAs);
}
@@ -885,7 +935,7 @@ namespace CodeWalker.GameFiles
{
if ((fm.platform == null) || (fm.platform == "x64"))
{
- if (path.StartsWith(fm.mountAs))
+ if (path.StartsWith(fm.mountAs, StringComparison.OrdinalIgnoreCase))
{
path = path.Replace(fm.mountAs, fm.path);
}
@@ -913,7 +963,7 @@ namespace CodeWalker.GameFiles
{
foreach (var file in list)
{
- if (!file.Path.StartsWith("mods"))
+ if (!file.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{
rlist.Add(file);
}
@@ -929,7 +979,7 @@ namespace CodeWalker.GameFiles
}
else
{
- if (file.Path.StartsWith("mods"))
+ if (file.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{
var basepath = file.Path.Substring(5);
if (!RpfMan.RpfDict.ContainsKey(basepath)) //this file isn't overriding anything
@@ -948,45 +998,44 @@ namespace CodeWalker.GameFiles
}
- private void InitGlobalDicts()
+ private void InitGlobalDicts(bool force = false)
{
UpdateStatus?.Invoke("Building global dictionaries...");
using var timer = new DisposableTimer("InitGlobalDicts");
- YdrDict = new Dictionary(80000);
- YddDict = new Dictionary(11000);
- YtdDict = new Dictionary(40000);
- YftDict = new Dictionary(40000);
- YcdDict = new Dictionary(20000);
- YedDict = new Dictionary(300);
+ YdrDict ??= new Dictionary(80000);
+ YddDict ??= new Dictionary(11000);
+ YtdDict ??= new Dictionary(40000);
+ YftDict ??= new Dictionary(40000);
+ YcdDict ??= new Dictionary(20000);
+ YedDict ??= new Dictionary(300);
foreach (var rpffile in AllRpfs)
{
if (rpffile.AllEntries == null) continue;
foreach (var entry in rpffile.AllEntries)
{
- if (entry is RpfFileEntry)
+ if (entry is RpfFileEntry fentry)
{
- RpfFileEntry fentry = entry as RpfFileEntry;
- if (entry.NameLower.EndsWith(".ydr"))
+ if (entry.IsExtension(".ydr"))
{
YdrDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".ydd"))
+ else if (entry.IsExtension(".ydd"))
{
YddDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".ytd"))
+ else if (entry.IsExtension(".ytd"))
{
YtdDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".yft"))
+ else if (entry.IsExtension(".yft"))
{
YftDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".ycd"))
+ else if (entry.IsExtension(".ycd"))
{
YcdDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".yed"))
+ else if (entry.IsExtension(".yed"))
{
YedDict[entry.ShortNameHash] = fentry;
}
@@ -996,32 +1045,33 @@ namespace CodeWalker.GameFiles
Console.WriteLine($"YdrDict: {YdrDict.Count}; YddDict: {YddDict.Count}; YtdDict: {YtdDict.Count}; YftDict: {YftDict.Count}; YcdDict: {YcdDict.Count}; YedDict: {YedDict.Count}");
}
- private void InitMapDicts()
+ private void InitMapDicts(bool force = true)
{
UpdateStatus?.Invoke("Building map dictionaries...");
using var _ = new DisposableTimer("InitMapDicts");
- YmapDict = new Dictionary();
- YbnDict = new Dictionary();
- YnvDict = new Dictionary();
+ YmapDict ??= new Dictionary(4600);
+ YbnDict ??= new Dictionary(8800);
+ YnvDict ??= new Dictionary(4500);
+ AllYmapsDict = new Dictionary(11000);
foreach (var rpffile in ActiveMapRpfFiles.Values) //RpfMan.BaseRpfs)
{
if (rpffile.AllEntries == null) continue;
foreach (var entry in rpffile.AllEntries)
{
- if (entry is RpfFileEntry)
+ if (entry is RpfFileEntry fentry)
{
- RpfFileEntry fentry = entry as RpfFileEntry;
- if (entry.NameLower.EndsWith(".ymap"))
+ if (entry.IsExtension(".ymap"))
{
//YmapDict[entry.NameHash] = fentry;
YmapDict[entry.ShortNameHash] = fentry;
+ AllYmapsDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".ybn"))
+ else if (entry.IsExtension(".ybn"))
{
//YbnDict[entry.NameHash] = fentry;
YbnDict[entry.ShortNameHash] = fentry;
}
- else if (entry.NameLower.EndsWith(".ynv"))
+ else if (entry.IsExtension(".ynv"))
{
YnvDict[entry.ShortNameHash] = fentry;
}
@@ -1029,31 +1079,15 @@ namespace CodeWalker.GameFiles
}
}
- AllYmapsDict = new Dictionary();
- foreach (var rpffile in AllRpfs)
- {
- if (rpffile.AllEntries == null) continue;
- foreach (var entry in rpffile.AllEntries)
- {
- if (entry is RpfFileEntry)
- {
- RpfFileEntry fentry = entry as RpfFileEntry;
- if (entry.NameLower.EndsWith(".ymap"))
- {
- AllYmapsDict[entry.ShortNameHash] = fentry;
- }
- }
- }
- }
-
+ Console.WriteLine($"YmapDict: {YmapDict.Count}; YbnDict: {YbnDict.Count}; YnvDict: {YnvDict.Count}; AllYmapsDict: {AllYmapsDict.Count};");
}
- private void InitManifestDicts()
+ private void InitManifestDicts(bool force = true)
{
UpdateStatus?.Invoke("Loading manifests...");
using var _ = new DisposableTimer("InitManifestDicts");
- AllManifests = new List();
- hdtexturelookup = new Dictionary();
+ AllManifests = new List(2000);
+ hdtexturelookup = new Dictionary(24000);
IEnumerable rpfs = PreloadedMode ? AllRpfs : ActiveMapRpfFiles.Values;
foreach (RpfFile file in rpfs)
{
@@ -1061,130 +1095,101 @@ namespace CodeWalker.GameFiles
//manifest and meta parsing..
foreach (RpfEntry entry in file.AllEntries)
{
- //sp_manifest.ymt
- //if (entry.NameLower.EndsWith("zonebind.ymt")/* || entry.Name.EndsWith("vinewood.ymt")*/)
- //{
- // YmtFile ymt = GetFile(entry);
- //}
- if (entry.Name.EndsWith(".ymf"))// || entry.Name.EndsWith(".ymt"))
+ if (!entry.IsExtension(".ymf"))
{
- try
+ continue;
+ }
+ try
+ {
+ UpdateStatus?.Invoke(string.Format(entry.Path));
+ YmfFile ymffile = RpfMan.GetFile(entry);
+ if (ymffile != null)
{
- UpdateStatus?.Invoke(string.Format(entry.Path));
- YmfFile ymffile = RpfMan.GetFile(entry);
- if (ymffile != null)
+ AllManifests.Add(ymffile);
+
+ if (ymffile.HDTxdAssetBindings != null)
{
- AllManifests.Add(ymffile);
-
- if (ymffile.Pso != null)
- { }
- else if (ymffile.Rbf != null)
- { }
- else if (ymffile.Meta != null)
- { }
- else
- { }
-
-
- if (ymffile.HDTxdAssetBindings != null)
+ for (int i = 0; i < ymffile.HDTxdAssetBindings.Length; i++)
{
- for (int i = 0; i < ymffile.HDTxdAssetBindings.Length; i++)
- {
- var b = ymffile.HDTxdAssetBindings[i];
- var targetasset = JenkHash.GenHash(b.targetAsset.ToString().ToLowerInvariant());
- var hdtxd = JenkHash.GenHash(b.HDTxd.ToString().ToLowerInvariant());
- hdtexturelookup[targetasset] = hdtxd;
- }
+ var b = ymffile.HDTxdAssetBindings[i];
+ var targetasset = JenkHash.GenHashLower(b.targetAsset.ToString());
+ var hdtxd = JenkHash.GenHashLower(b.HDTxd.ToString());
+ hdtexturelookup[targetasset] = hdtxd;
}
-
}
- }
- catch (Exception ex)
- {
- string errstr = entry.Path + "\n" + ex.ToString();
- ErrorLog(errstr);
+
}
}
-
+ catch (Exception ex)
+ {
+ string errstr = entry.Path + "\n" + ex.ToString();
+ ErrorLog?.Invoke(errstr);
+ Console.WriteLine(errstr);
+ }
}
-
}
+
+ Console.WriteLine($"hdtexturelookup: {hdtexturelookup.Count}; AllManifests: {AllManifests.Count};");
}
- private void InitGtxds()
+ private static ConcurrentDictionary vehicleFiles = new(4, 72, StringComparer.OrdinalIgnoreCase);
+ private async Task InitGtxds(bool force = true)
{
UpdateStatus?.Invoke("Loading global texture list...");
using var timer = new DisposableTimer("InitGtxds");
- var parentTxds = new Dictionary();
+ var parentTxds = new ConcurrentDictionary(4, 4000);
IEnumerable rpfs = PreloadedMode ? AllRpfs : (IEnumerable)ActiveMapRpfFiles.Values;
- var addTxdRelationships = new Action>((from) =>
- {
+ static void addTxdRelationships(Dictionary from, ConcurrentDictionary parentTxds) {
foreach (var kvp in from)
{
- uint chash = JenkHash.GenHash(kvp.Key.ToLowerInvariant());
- uint phash = JenkHash.GenHash(kvp.Value.ToLowerInvariant());
- if (!parentTxds.ContainsKey(chash))
- {
- parentTxds.Add(chash, phash);
- }
- else
- {
- }
+ uint chash = JenkHash.GenHashLower(kvp.Key);
+ uint phash = JenkHash.GenHashLower(kvp.Value);
+
+ parentTxds.TryAdd(chash, phash);
}
- });
-
- var addRpfTxdRelationships = new Action>((from) =>
- {
- foreach (RpfFile file in from)
- {
- if (file.AllEntries == null) continue;
- foreach (RpfEntry entry in file.AllEntries)
- {
- try
- {
- if ((entry.NameLower == "gtxd.ymt") || (entry.NameLower == "gtxd.meta") || (entry.NameLower == "mph4_gtxd.ymt"))
- {
- GtxdFile ymt = RpfMan.GetFile(entry);
- if (ymt.TxdRelationships != null)
- {
- addTxdRelationships(ymt.TxdRelationships);
- }
- }
- else if (entry.NameLower == "vehicles.meta")
- {
- VehiclesFile vf = RpfMan.GetFile(entry);//could also get loaded in InitVehicles...
- if (vf.TxdRelationships != null)
- {
- addTxdRelationships(vf.TxdRelationships);
- }
- }
- }
- catch (Exception ex)
- {
- string errstr = entry.Path + "\n" + ex.ToString();
- ErrorLog(errstr);
- }
- }
- }
-
- });
-
-
- addRpfTxdRelationships(rpfs);
-
-
- if (EnableDlc)
- {
- addRpfTxdRelationships(DlcActiveRpfs);
}
-
- textureParents = parentTxds;
+ IEnumerable allRpfs = rpfs;
+ if (EnableDlc && DlcActiveRpfs.Count > 0)
+ {
+ allRpfs = allRpfs.Concat(DlcActiveRpfs);
+ }
+ await allRpfs.Where(p => p.AllEntries != null).ParallelForEachAsync(async (file) =>
+ {
+ foreach (RpfEntry entry in file.AllEntries)
+ {
+ if (
+ entry.Name.Equals("gtxd.ymt", StringComparison.OrdinalIgnoreCase)
+ || entry.Name.Equals("gtxd.meta", StringComparison.OrdinalIgnoreCase)
+ || entry.Name.Equals("mph4_gtxd.ymt", StringComparison.OrdinalIgnoreCase)
+ )
+ {
+ GtxdFile ymt = await RpfMan.GetFileAsync(entry).ConfigureAwait(false);
+ if (ymt.TxdRelationships != null)
+ {
+ addTxdRelationships(ymt.TxdRelationships, parentTxds);
+ }
+ }
+ else if (entry.Name.Equals("vehicles.meta", StringComparison.OrdinalIgnoreCase))
+ {
+ if (!vehicleFiles.TryGetValue(entry.Path, out var vf))
+ {
+ vf = vehicleFiles[entry.Path] = await RpfMan.GetFileAsync(entry).ConfigureAwait(false);
+ }
+ if (vf.TxdRelationships != null)
+ {
+ addTxdRelationships(vf.TxdRelationships, parentTxds);
+ }
+ }
+ }
+ });
+
+ textureParents = parentTxds.ToDictionary(p => p.Key, p => p.Value);
//ensure resident global texture dicts:
YtdFile ytd1 = new YtdFile(GetYtdEntry(JenkHash.GenHash("mapdetail")));
@@ -1196,15 +1201,15 @@ namespace CodeWalker.GameFiles
AddTextureLookups(ytd2);
-
+ Console.WriteLine($"textureParents: {textureParents.Count}");
}
- private void InitMapCaches()
+ private void InitMapCaches(bool force = true)
{
UpdateStatus?.Invoke("Loading cache...");
using var _ = new DisposableTimer("InitMapCaches");
- AllCacheFiles = new List();
- YmapHierarchyDict = new Dictionary();
+ AllCacheFiles = new List(1);
+ YmapHierarchyDict = new Dictionary(5000);
CacheDatFile loadCacheFile(string path, bool finalAttempt)
@@ -1222,7 +1227,9 @@ namespace CodeWalker.GameFiles
YmapHierarchyDict[node.Name] = node;
}
else
- { } //ymap not found...
+ {
+ Console.WriteLine($"Couldn't find {node.Name} in YmapDict");
+ } //ymap not found...
}
}
else if (finalAttempt)
@@ -1267,60 +1274,139 @@ namespace CodeWalker.GameFiles
}
}
-
+ Console.WriteLine($"AllCacheFiles: {AllCacheFiles.Count}; YmapHierarchyDict: {YmapHierarchyDict.Count};");
}
- private void InitArchetypeDicts()
+ private ConcurrentBag ytypCache;
+ private async Task InitArchetypeDicts(bool force = true)
{
UpdateStatus?.Invoke("Loading archetypes...");
- YtypDict = new ConcurrentDictionary();
+
+ ytypCache = new ConcurrentBag();
archetypesLoaded = false;
- archetypeDict.Clear();
-
- if (!LoadArchetypes) return;
-
- using var timer = new DisposableTimer("InitArchetypeDicts");
-
- var rpfs = EnableDlc ? AllRpfs : BaseRpfs;
-
- var allYtypes = rpfs
- .Where(p => p.AllEntries != null && (EnableDlc || !p.Path.StartsWith("update")))
- .SelectMany(p => p.AllEntries.Where(p => p.NameLower.EndsWith(".ytyp")));
-
- Parallel.ForEach(allYtypes, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (entry) =>
+ try
{
- try
+ archetypeDict.Clear();
+ using var timer = new DisposableTimer("InitArchetypeDicts");
+
+ var rpfs = EnableDlc ? AllRpfs : BaseRpfs;
+
+ var allYtypes = rpfs
+ .Where(p => p.AllEntries != null && (EnableDlc || !p.Path.StartsWith("update", StringComparison.OrdinalIgnoreCase)));
+ //.SelectMany(p => p.AllEntries.Where(p => p.IsExtension(".ytyp")));
+
+ //await allYtypes.ParallelForEachAsync(async (file) =>
+ //{
+ // foreach (var entry in file.AllEntries) {
+ // if (!entry.Name.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase)) continue;
+ // try
+ // {
+ // AddYtypToDictionary(entry);
+ // }
+ // catch (Exception ex)
+ // {
+ // ErrorLog(entry.Path + ": " + ex.ToString());
+ // }
+ // }
+ //}, maxDegreeOfParallelism: 8);
+
+ await allYtypes.ParallelForEachAsync(async (file) =>
{
- AddYtypToDictionary(entry);
- }
- catch (Exception ex)
+ foreach (var entry in file.AllEntries)
+ {
+ if (entry.IsExtension(".ytyp"))
+ {
+ try
+ {
+ ytypCache.Add(await RpfMan.GetFileAsync(entry).ConfigureAwait(false));
+ }
+ catch (Exception ex)
+ {
+ ErrorLog(entry.Path + ": " + ex.ToString());
+ }
+ }
+ }
+ }).ConfigureAwait(false);
+
+ YtypDict = new Dictionary(ytypCache.Count);
+
+ foreach (var ytyp in ytypCache)
{
- ErrorLog(entry.Path + ": " + ex.ToString());
+ AddYtypToDictionary(ytyp.RpfFileEntry, ytyp);
}
- });
+
+ //Parallel.ForEach(allYtypes, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (file) =>
+ //{
+ // foreach(var entry in file.AllEntries)
+ // {
+ // if (entry.IsExtension(".ytyp"))
+ // {
+ // try
+ // {
+ // AddYtypToDictionary(entry);
+ // }
+ // catch (Exception ex)
+ // {
+ // ErrorLog(entry.Path + ": " + ex.ToString());
+ // }
+ // }
+ // }
+ //});
+
+ //Parallel.ForEach(allYtypes, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (entry) =>
+ //{
+ // try
+ // {
+ // AddYtypToDictionary(entry);
+ // }
+ // catch (Exception ex)
+ // {
+ // ErrorLog(entry.Path + ": " + ex.ToString());
+ // }
+ //});
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine(ex);
+ throw;
+ }
archetypesLoaded = true;
}
- private void AddYtypToDictionary(RpfEntry entry)
+ private void AddYtypToDictionary(RpfEntry entry, YtypFile ytypFile)
{
- //UpdateStatus?.Invoke(string.Format(entry.Path));
- YtypFile ytypfile = RpfMan.GetFile(entry);
- if (ytypfile == null)
+ if (ytypFile == null)
{
throw new Exception("Couldn't load ytyp file."); //couldn't load the file for some reason... shouldn't happen..
}
- if (ytypfile.Meta == null)
+ if (ytypFile.Meta == null)
{
- if (ytypfile.Pso == null && ytypfile.Rbf == null)
+ if (ytypFile.Pso == null && ytypFile.Rbf == null)
{
throw new Exception("ytyp file was not in meta format.");
}
return;
}
- YtypDict[ytypfile.NameHash] = ytypfile;
+
+ ytypCache.Add(ytypFile);
+ YtypDict[ytypFile.NameHash] = ytypFile;
+
+ if ((ytypFile.AllArchetypes == null) || (ytypFile.AllArchetypes.Length == 0))
+ {
+ ErrorLog(entry.Path + ": no archetypes found");
+ }
+ else
+ {
+ foreach (var arch in ytypFile.AllArchetypes)
+ {
+ uint hash = arch.Hash;
+ if (hash == 0) continue;
+ archetypeDict[hash] = arch;
+ }
+ }
//if (YtypDict.ContainsKey(ytypfile.NameHash))
//{
@@ -1335,19 +1421,7 @@ namespace CodeWalker.GameFiles
- if ((ytypfile.AllArchetypes == null) || (ytypfile.AllArchetypes.Length == 0))
- {
- ErrorLog(entry.Path + ": no archetypes found");
- }
- else
- {
- foreach (var arch in ytypfile.AllArchetypes)
- {
- uint hash = arch.Hash;
- if (hash == 0) continue;
- archetypeDict[hash] = arch;
- }
- }
+
////if (ytypfile.AudioEmitters != null)
@@ -1375,7 +1449,7 @@ namespace CodeWalker.GameFiles
}
- public void InitStringDicts()
+ public async ValueTask InitStringDictsAsync(bool force = true)
{
UpdateStatus?.Invoke("Loading strings...");
using var timer = new DisposableTimer("InitStringDicts");
@@ -1383,22 +1457,22 @@ namespace CodeWalker.GameFiles
string langstr2 = "americandlc.rpf";
string langstr3 = "american.rpf";
- Gxt2Dict = new Dictionary();
+ Gxt2Dict = new Dictionary(1000);
var gxt2files = new List();
- foreach (var rpf in AllRpfs)
+ foreach (var rpf in AllRpfs.Where(p => p.AllEntries != null))
{
foreach (var entry in rpf.AllEntries)
{
if (entry is RpfFileEntry fentry)
{
var p = entry.Path;
- if (entry.NameLower.EndsWith(".gxt2") && (p.Contains(langstr) || p.Contains(langstr2) || p.Contains(langstr3)))
+ if (entry.IsExtension(".gxt2") && (p.Contains(langstr, StringComparison.OrdinalIgnoreCase) || p.Contains(langstr2, StringComparison.OrdinalIgnoreCase) || p.Contains(langstr3, StringComparison.OrdinalIgnoreCase)))
{
Gxt2Dict[entry.ShortNameHash] = fentry;
if (DoFullStringIndex)
{
- var gxt2 = RpfMan.GetFile(entry);
+ var gxt2 = await RpfMan.GetFileAsync(entry).ConfigureAwait(false);
if (gxt2 != null)
{
for (int i = 0; i < gxt2.TextEntries.Length; i++)
@@ -1440,7 +1514,7 @@ namespace CodeWalker.GameFiles
{
foreach (var entry in rpf.AllEntries)
{
- if (entry.NameLower.EndsWith("statssetup.xml"))
+ if (entry.Name.EndsWith("statssetup.xml", StringComparison.OrdinalIgnoreCase))
{
var xml = RpfMan.GetFileXml(entry.Path);
if (xml == null)
@@ -1456,13 +1530,13 @@ namespace CodeWalker.GameFiles
if (string.IsNullOrEmpty(statname))
{ continue; }
- var statnamel = statname.ToLowerInvariant();
- StatsNames.Ensure(statname);
- StatsNames.Ensure(statnamel);
+ var statnamel = statname;
+ StatsNames.EnsureLower(statname);
+ StatsNames.EnsureLower(statnamel);
- StatsNames.Ensure("sp_" + statnamel);
- StatsNames.Ensure("mp0_" + statnamel);
- StatsNames.Ensure("mp1_" + statnamel);
+ StatsNames.EnsureLower("sp_" + statnamel);
+ StatsNames.EnsureLower("mp0_" + statnamel);
+ StatsNames.EnsureLower("mp1_" + statnamel);
}
}
@@ -1472,9 +1546,10 @@ namespace CodeWalker.GameFiles
StatsNames.FullIndexBuilt = true;
}
- public void InitVehicles()
+ public void InitVehicles(bool force = true)
{
if (!LoadVehicles) return;
+ if (!force && VehiclesLoaded) return;
//Neos7
@@ -1498,157 +1573,141 @@ namespace CodeWalker.GameFiles
IEnumerable rpfs = PreloadedMode ? AllRpfs : (IEnumerable)ActiveMapRpfFiles.Values;
- var allVehicles = new Dictionary();
- var allCarCols = new List();
- var allCarModCols = new List();
- var allCarVariations = new List();
- var allCarVariationsDict = new Dictionary();
- var allVehicleLayouts = new List();
+ var allVehicles = new Dictionary(900);
+ //var allCarCols = new List();
+ //var allCarModCols = new List();
+ //var allCarVariations = new List();
+ //var allCarVariationsDict = new Dictionary();
+ //var allVehicleLayouts = new List();
- var addVehicleFiles = new Action>((from) =>
+ IEnumerable allRpfs = rpfs;
+
+ if (EnableDlc && DlcActiveRpfs.Count > 0)
{
- foreach (RpfFile file in from)
- {
- if (file.AllEntries == null) continue;
- foreach (RpfEntry entry in file.AllEntries)
- {
-#if !DEBUG
- try
-#endif
- {
- if (entry.NameLower == "vehicles.meta")
- {
- VehiclesFile vf = RpfMan.GetFile(entry);
- if (vf.InitDatas != null)
- {
- foreach (var initData in vf.InitDatas)
- {
- var name = initData.modelName.ToLowerInvariant();
- var hash = JenkHash.GenHash(name);
- if (allVehicles.ContainsKey(hash))
- { }
- allVehicles[hash] = initData;
- }
- }
- }
- if ((entry.NameLower == "carcols.ymt") || (entry.NameLower == "carcols.meta"))
- {
- var cf = RpfMan.GetFile(entry);
- if (cf.VehicleModelInfo != null)
- { }
- allCarCols.Add(cf);
- }
- if (entry.NameLower == "carmodcols.ymt")
- {
- var cf = RpfMan.GetFile(entry);
- if (cf.VehicleModColours != null)
- { }
- allCarModCols.Add(cf);
- }
- if ((entry.NameLower == "carvariations.ymt") || (entry.NameLower == "carvariations.meta"))
- {
- var cf = RpfMan.GetFile(entry);
- if (cf.VehicleModelInfo?.variationData != null)
- {
- foreach (var variation in cf.VehicleModelInfo.variationData)
- {
- var name = variation.modelName.ToLowerInvariant();
- var hash = JenkHash.GenHash(name);
- allCarVariationsDict[hash] = variation;
- }
- }
- allCarVariations.Add(cf);
- }
- if (entry.NameLower.StartsWith("vehiclelayouts") && entry.NameLower.EndsWith(".meta"))
- {
- var lf = RpfMan.GetFile(entry);
- if (lf.Xml != null)
- { }
- allVehicleLayouts.Add(lf);
- }
- }
-#if !DEBUG
- catch (Exception ex)
- {
- string errstr = entry.Path + "\n" + ex.ToString();
- ErrorLog(errstr);
- }
-#endif
- }
- }
-
- });
-
-
- addVehicleFiles(rpfs);
-
- if (EnableDlc)
- {
- addVehicleFiles(DlcActiveRpfs);
+ allRpfs = allRpfs.Concat(DlcActiveRpfs);
}
- Console.WriteLine($"allVehicles: {allVehicles.Count()}");
+ Parallel.ForEach(allRpfs.Where(p => p.AllEntries != null).SelectMany(p => p.AllEntries), (entry) =>
+ {
+ if (!entry.Name.Equals("vehicles.meta", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ try
+ {
+ vehicleFiles[entry.Path] = RpfMan.GetFile(entry);
+ //if ((entry.Name.Equals("carcols.ymt", StringComparison.OrdinalIgnoreCase)) || (entry.Name.Equals("carcols.meta", StringComparison.OrdinalIgnoreCase)))
+ //{
+ // var cf = RpfMan.GetFile(entry);
+ // if (cf.VehicleModelInfo != null)
+ // { }
+ // allCarCols.Add(cf);
+ //}
+ //if (entry.Name.Equals("carmodcols.ymt", StringComparison.OrdinalIgnoreCase))
+ //{
+ // var cf = RpfMan.GetFile(entry);
+ // if (cf.VehicleModColours != null)
+ // { }
+ // allCarModCols.Add(cf);
+ //}
+ //if ((entry.Name.Equals("carvariations.ymt", StringComparison.OrdinalIgnoreCase)) || (entry.Name.Equals("carvariations.meta", StringComparison.OrdinalIgnoreCase)))
+ //{
+ // var cf = RpfMan.GetFile(entry);
+ // if (cf.VehicleModelInfo?.variationData != null)
+ // {
+ // foreach (var variation in cf.VehicleModelInfo.variationData)
+ // {
+ // var name = variation.modelName.ToLowerInvariant();
+ // var hash = JenkHash.GenHash(name);
+ // allCarVariationsDict[hash] = variation;
+ // }
+ // }
+ // allCarVariations.Add(cf);
+ //}
+ //if (entry.Name.StartsWith("vehiclelayouts", StringComparison.OrdinalIgnoreCase) && entry.Name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase))
+ //{
+ // var lf = RpfMan.GetFile(entry);
+ // if (lf.Xml != null)
+ // { }
+ // allVehicleLayouts.Add(lf);
+ //}
+ }
+ catch (Exception ex)
+ {
+ string errstr = entry.Path + "\n" + ex.ToString();
+ ErrorLog(errstr);
+ Console.WriteLine(errstr);
+ }
+ });
+
+ foreach(var vf in vehicleFiles.Values)
+ {
+ if (vf.InitDatas != null)
+ {
+ foreach (var initData in vf.InitDatas)
+ {
+ var name = initData.modelName;
+ var hash = JenkHash.GenHashLower(name);
+ if (!allVehicles.ContainsKey(hash))
+ {
+ allVehicles[hash] = initData;
+ }
+ }
+ }
+ }
+
+ Console.WriteLine($"allVehicles: {allVehicles.Count}; vehicleFiles: {vehicleFiles.Count}");
VehiclesInitDict = allVehicles;
-
}
- public void InitPeds()
+ public void InitPeds(bool force = true)
{
if (!LoadPeds) return;
+ if (!force && PedsLoaded) return;
UpdateStatus?.Invoke("Loading peds...");
using var _ = new DisposableTimer("InitPeds");
IEnumerable rpfs = PreloadedMode ? AllRpfs : (IEnumerable)ActiveMapRpfFiles.Values;
- List dlcrpfs = new List();
+ List dlcrpfs = null;
if (EnableDlc)
{
+ dlcrpfs = new List();
foreach (var rpf in DlcActiveRpfs)
{
dlcrpfs.Add(rpf);
if (rpf.Children == null) continue;
- foreach (var crpf in rpf.Children)
- {
- dlcrpfs.Add(crpf);
- if (crpf.Children?.Count > 0)
- { }
- }
+ dlcrpfs.AddRange(rpf.Children);
}
}
- var allPeds = new Dictionary();
- var allPedsFiles = new List();
- var allPedYmts = new Dictionary();
- var allPedDrwDicts = new Dictionary>();
- var allPedTexDicts = new Dictionary>();
- var allPedClothDicts = new Dictionary>();
+ var allPeds = new ConcurrentDictionary(4, 1100);
+ //var allPedsFiles = new List();
+ var allPedYmts = new ConcurrentDictionary(4, 1100);
+ var allPedDrwDicts = new ConcurrentDictionary>(4, 1100);
+ var allPedTexDicts = new ConcurrentDictionary>(4, 1100);
+ var allPedClothDicts = new ConcurrentDictionary>(4, 200);
- Dictionary ensureDict(Dictionary> coll, MetaHash hash)
+ Dictionary ensureDict(ConcurrentDictionary> coll, MetaHash hash)
{
- Dictionary dict;
- if (!coll.TryGetValue(hash, out dict))
- {
- dict = new Dictionary();
- coll[hash] = dict;
- }
- return dict;
+ return coll.GetOrAdd(hash, (key) => new Dictionary());
}
- var addPedDicts = new Action((namel, hash, dir) =>
+ var addPedDicts = new Action((name, hash, dir) =>
{
- Dictionary dict = null;
+ Dictionary pedClotsDict = null;
var files = dir?.Files;
if (files != null)
{
foreach (var file in files)
{
- if (file.NameLower == namel + ".yld")
+ if (file.Name.Equals(name + ".yld", StringComparison.OrdinalIgnoreCase))
{
- dict = ensureDict(allPedClothDicts, hash);
- dict[file.ShortNameHash] = file;
+ pedClotsDict ??= ensureDict(allPedClothDicts, hash);
+ pedClotsDict[file.ShortNameHash] = file;
}
}
}
@@ -1657,7 +1716,7 @@ namespace CodeWalker.GameFiles
{
foreach (var cdir in dir.Directories)
{
- if (cdir.NameLower == namel)
+ if (cdir.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
dir = cdir;
break;
@@ -1666,128 +1725,123 @@ namespace CodeWalker.GameFiles
files = dir?.Files;
if (files != null)
{
+ Dictionary pedDrwDicts = null;
+ Dictionary pedTextDicts = null;
foreach (var file in files)
{
- if (file?.NameLower == null) continue;
- if (file.NameLower.EndsWith(".ydd"))
+ if (file?.Name == null) continue;
+ if (file.IsExtension(".ydd"))
{
- dict = ensureDict(allPedDrwDicts, hash);
- dict[file.ShortNameHash] = file;
+ pedDrwDicts ??= ensureDict(allPedDrwDicts, hash);
+ pedDrwDicts[file.ShortNameHash] = file;
}
- else if (file.NameLower.EndsWith(".ytd"))
+ else if (file.IsExtension(".ytd"))
{
- dict = ensureDict(allPedTexDicts, hash);
- dict[file.ShortNameHash] = file;
+ pedTextDicts ??= ensureDict(allPedTexDicts, hash);
+ pedTextDicts[file.ShortNameHash] = file;
}
- else if (file.NameLower.EndsWith(".yld"))
+ else if (file.IsExtension(".yld"))
{
- dict = ensureDict(allPedClothDicts, hash);
- dict[file.ShortNameHash] = file;
+ pedClotsDict ??= ensureDict(allPedClothDicts, hash);
+ pedClotsDict[file.ShortNameHash] = file;
}
}
}
}
});
- var addPedsFiles = new Action>((from) =>
+ var numDlcs = dlcrpfs?.Count ?? 0;
+
+ var allRpfs = rpfs;
+
+ if (numDlcs > 0)
{
- foreach (RpfFile file in from)
- {
- if (file.AllEntries == null) continue;
- foreach (RpfEntry entry in file.AllEntries)
- {
- try
- {
- if ((entry.NameLower == "peds.ymt") || (entry.NameLower == "peds.meta"))
- {
- var pf = RpfMan.GetFile(entry);
- if (pf.InitDataList?.InitDatas != null)
- {
- foreach (var initData in pf.InitDataList.InitDatas)
- {
- var name = initData.Name.ToLowerInvariant();
- var hash = JenkHash.GenHash(name);
- if (allPeds.ContainsKey(hash))
- { }
- allPeds[hash] = initData;
- }
- }
- allPedsFiles.Add(pf);
- }
- }
- catch (Exception ex)
- {
- string errstr = entry.Path + "\n" + ex.ToString();
- ErrorLog?.Invoke(errstr);
- Console.WriteLine(errstr);
- }
- }
- }
- });
-
- var addPedFiles = new Action>((from) =>
- {
- foreach (RpfFile file in from)
- {
- if (file.AllEntries == null) continue;
- foreach (RpfEntry entry in file.AllEntries)
- {
- try
- {
- if (entry.Name.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
- {
- var testname = entry.ShortName;
- var testhash = JenkHash.GenHashLower(testname);
- if (allPeds.ContainsKey(testhash))
- {
- var pf = RpfMan.GetFile(entry);
- if (pf != null)
- {
- allPedYmts[testhash] = pf;
- addPedDicts(testname, testhash, entry.Parent);
- }
- }
- }
- }
- catch (Exception ex)
- {
- string errstr = entry.Path + "\n" + ex.ToString();
- ErrorLog?.Invoke(errstr);
- Console.WriteLine(errstr);
- }
- }
- }
- });
-
-
-
- addPedsFiles(rpfs);
- addPedsFiles(dlcrpfs);
-
- addPedFiles(rpfs);
- addPedFiles(dlcrpfs);
-
-
-
- PedsInitDict = allPeds;
- PedVariationsDict = allPedYmts;
- PedDrawableDicts = allPedDrwDicts;
- PedTextureDicts = allPedTexDicts;
- PedClothDicts = allPedClothDicts;
-
-
- foreach (var kvp in PedsInitDict)
- {
- if (!PedVariationsDict.ContainsKey(kvp.Key))
- { }//checking we found them all!
+ allRpfs = rpfs.Concat(dlcrpfs);
}
+ var allEntries = allRpfs
+ .Where(p => p.AllEntries != null)
+ .SelectMany(p => p.AllEntries)
+ .Where(p => p.Name.EndsWithAny(".ymt", ".meta"));
+
+ Parallel.ForEach(allEntries, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (entry) =>
+ {
+ if (!entry.Name.Equals("peds.ymt", StringComparison.OrdinalIgnoreCase) && !entry.Name.Equals("peds.meta", StringComparison.OrdinalIgnoreCase))
+ {
+ return;
+ }
+ try
+ {
+ var pf = RpfMan.GetFile(entry);
+ if (pf.InitDataList?.InitDatas != null)
+ {
+ foreach (var initData in pf.InitDataList.InitDatas)
+ {
+ var name = initData.Name;
+ var hash = JenkHash.GenHashLower(name);
+ allPeds.TryAdd(hash, initData);
+ }
+ }
+ //allPedsFiles.Add(pf);
+ }
+ catch (Exception ex)
+ {
+ string errstr = entry.Path + "\n" + ex.ToString();
+ ErrorLog?.Invoke(errstr);
+ Console.WriteLine(errstr);
+ }
+ });
+
+ Parallel.ForEach(allEntries, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (entry) =>
+ {
+ if (!entry.IsExtension(".ymt"))
+ {
+ return;
+ }
+ try
+ {
+ var shortName = entry.ShortName;
+ var shortHash = entry.ShortNameHash;
+ if (allPeds.ContainsKey(shortHash))
+ {
+ var pf = RpfMan.GetFile(entry);
+ if (pf != null)
+ {
+ allPedYmts.TryAdd(shortHash, pf);
+ addPedDicts(shortName, shortHash, entry.Parent);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ string errstr = entry.Path + "\n" + ex.ToString();
+ ErrorLog?.Invoke(errstr);
+ Console.WriteLine(errstr);
+ }
+ });
+
+ PedsInitDict = allPeds.ToDictionary(p => p.Key, p => p.Value);
+ PedVariationsDict = allPedYmts.ToDictionary(p => p.Key, p => p.Value);
+ PedDrawableDicts = allPedDrwDicts.ToDictionary(p => p.Key, p => p.Value);
+ PedTextureDicts = allPedTexDicts.ToDictionary(p => p.Key, p => p.Value);
+ PedClothDicts = allPedClothDicts.ToDictionary(p => p.Key, p => p.Value);
+
+
+ //foreach (var kvp in PedsInitDict)
+ //{
+ // if (!PedVariationsDict.ContainsKey(kvp.Key))
+ // { }//checking we found them all!
+ //}
+
}
- public void InitAudio()
+ private bool AudioLoaded = false;
+ public readonly ReaderWriterLockSlim AudioDatRelFilesLock = new ReaderWriterLockSlim();
+ public async ValueTask InitAudio(bool force = true)
{
if (!LoadAudio) return;
+ if (AudioLoaded && !force) return;
UpdateStatus?.Invoke("Loading audio...");
using var timer = new DisposableTimer("InitAudio");
@@ -1797,10 +1851,9 @@ namespace CodeWalker.GameFiles
if (rpffile.AllEntries == null) return;
foreach (var entry in rpffile.AllEntries)
{
- if (entry is RpfFileEntry)
+ if (entry is RpfFileEntry fentry)
{
- RpfFileEntry fentry = entry as RpfFileEntry;
- if (entry.NameLower.EndsWith(".rel"))
+ if (entry.IsExtension(".rel"))
{
datrelentries[entry.NameHash] = fentry;
}
@@ -1829,7 +1882,7 @@ namespace CodeWalker.GameFiles
{
foreach (var rpf in AllRpfs) //this is a bit of a hack - DLC orders won't be correct so likely will select wrong versions of things
{
- if (rpf.NameLower.StartsWith("dlc"))
+ if (rpf.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase))
{
addRpfDatRelEntries(rpf);
}
@@ -1837,79 +1890,99 @@ namespace CodeWalker.GameFiles
}
}
-
- var audioDatRelFiles = new List();
- var audioConfigDict = new Dictionary();
- var audioSpeechDict = new Dictionary();
- var audioSynthsDict = new Dictionary();
- var audioMixersDict = new Dictionary();
- var audioCurvesDict = new Dictionary();
- var audioCategsDict = new Dictionary();
- var audioSoundsDict = new Dictionary();
- var audioGameDict = new Dictionary();
-
-
-
- foreach (var datrelentry in datrelentries.Values)
+ try
{
- var relfile = RpfMan.GetFile(datrelentry);
- if (relfile == null) continue;
+ AudioDatRelFiles ??= new List(20);
+ AudioConfigDict ??= new Dictionary(150);
+ AudioSpeechDict ??= new Dictionary(90000);
+ AudioSynthsDict ??= new Dictionary(1500);
+ AudioMixersDict ??= new Dictionary(3500);
+ AudioCurvesDict ??= new Dictionary(1000);
+ AudioCategsDict ??= new Dictionary(250);
+ AudioSoundsDict ??= new Dictionary(60000);
+ AudioGameDict ??= new Dictionary(20000);
- audioDatRelFiles.Add(relfile);
+ var audioDatRelFiles = AudioDatRelFiles;
+ var audioConfigDict = AudioConfigDict;
+ var audioSpeechDict = AudioSpeechDict;
+ var audioSynthsDict = AudioSynthsDict;
+ var audioMixersDict = AudioMixersDict;
+ var audioCurvesDict = AudioCurvesDict;
+ var audioCategsDict = AudioCategsDict;
+ var audioSoundsDict = AudioSoundsDict;
+ var audioGameDict = AudioGameDict;
- var d = audioGameDict;
- var t = relfile.RelType;
- switch (t)
+ var relFiles = new ConcurrentBag();
+
+ await datrelentries.Values.ParallelForEachAsync(async (datrelentry) =>
{
- case RelDatFileType.Dat4:
- d = relfile.IsAudioConfig ? audioConfigDict : audioSpeechDict;
- break;
- case RelDatFileType.Dat10ModularSynth:
- d = audioSynthsDict;
- break;
- case RelDatFileType.Dat15DynamicMixer:
- d = audioMixersDict;
- break;
- case RelDatFileType.Dat16Curves:
- d = audioCurvesDict;
- break;
- case RelDatFileType.Dat22Categories:
- d = audioCategsDict;
- break;
- case RelDatFileType.Dat54DataEntries:
- d = audioSoundsDict;
- break;
- case RelDatFileType.Dat149:
- case RelDatFileType.Dat150:
- case RelDatFileType.Dat151:
- default:
- d = audioGameDict;
- break;
- }
+ var relfile = await RpfMan.GetFileAsync(datrelentry).ConfigureAwait(false);
+ if (relfile == null)
+ return;
- foreach (var reldata in relfile.RelDatas)
+ relFiles.Add(relfile);
+ }).ConfigureAwait(false);
+
+ foreach (var relfile in relFiles)
{
- if (reldata.NameHash == 0) continue;
- //if (d.TryGetValue(reldata.NameHash, out var exdata) && (exdata.TypeID != reldata.TypeID))
- //{ }//sanity check
- d[reldata.NameHash] = reldata;
- }
+ audioDatRelFiles.Add(relfile);
+ var d = audioGameDict;
+ var t = relfile.RelType;
+ switch (t)
+ {
+ case RelDatFileType.Dat4:
+ d = relfile.IsAudioConfig ? audioConfigDict : audioSpeechDict;
+ break;
+ case RelDatFileType.Dat10ModularSynth:
+ d = audioSynthsDict;
+ break;
+ case RelDatFileType.Dat15DynamicMixer:
+ d = audioMixersDict;
+ break;
+ case RelDatFileType.Dat16Curves:
+ d = audioCurvesDict;
+ break;
+ case RelDatFileType.Dat22Categories:
+ d = audioCategsDict;
+ break;
+ case RelDatFileType.Dat54DataEntries:
+ d = audioSoundsDict;
+ break;
+ case RelDatFileType.Dat149:
+ case RelDatFileType.Dat150:
+ case RelDatFileType.Dat151:
+ default:
+ d = audioGameDict;
+ break;
+ }
+
+ foreach (var reldata in relfile.RelDatas)
+ {
+ if (reldata.NameHash == 0)
+ continue;
+ d[reldata.NameHash] = reldata;
+ }
+
+ }
+ } catch(Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
}
+ Console.WriteLine(
+ $"AudioDatRelFiles: {AudioDatRelFiles.Count}; " +
+ $"AudioConfigDict: {AudioConfigDict.Count}; " +
+ $"AudioSpeechDict: {AudioSpeechDict.Count}; " +
+ $"AudioSynthsDict: {AudioSynthsDict.Count}; " +
+ $"AudioMixersDict: {AudioMixersDict.Count}; " +
+ $"AudioCurvesDict: {AudioCurvesDict.Count}; " +
+ $"AudioCategsDict: {AudioCategsDict.Count}; " +
+ $"AudioSoundsDict: {AudioSoundsDict.Count}; " +
+ $"AudioGameDict: {AudioGameDict.Count};");
-
-
- AudioDatRelFiles = audioDatRelFiles;
- AudioConfigDict = audioConfigDict;
- AudioSpeechDict = audioSpeechDict;
- AudioSynthsDict = audioSynthsDict;
- AudioMixersDict = audioMixersDict;
- AudioCurvesDict = audioCurvesDict;
- AudioCategsDict = audioCategsDict;
- AudioSoundsDict = audioSoundsDict;
- AudioGameDict = audioGameDict;
-
+ AudioLoaded = true;
}
@@ -2011,19 +2084,36 @@ namespace CodeWalker.GameFiles
public void RemoveProjectArchetype(Archetype a)
{
if ((a?.Hash ?? 0) == 0) return;
- Archetype tarch = null;
- projectArchetypes.TryGetValue(a.Hash, out tarch);
- if (tarch == a)
+ try
{
- projectArchetypes.TryRemove(a.Hash, out _);
+ projectArchetypes.TryGetValue(a.Hash, out var tarch);
+ if (tarch == a)
+ {
+ projectArchetypes.Remove(a.Hash);
+ }
}
+ finally
+ {
+ archetypeLock.ExitWriteLock();
+ }
+
}
public void ClearProjectArchetypes()
{
- projectArchetypes.Clear();
+ archetypeLock.EnterWriteLock();
+ try
+ {
+ projectArchetypes.Clear();
+ }
+ finally
+ {
+ archetypeLock.ExitWriteLock();
+ }
+
}
- public void TryLoadEnqueue(GameFile gf)
+ private readonly SemaphoreSlim maxCacheLock = new SemaphoreSlim(Environment.ProcessorCount / 2, Environment.ProcessorCount / 2);
+ public async Task TryLoadEnqueue(GameFile gf)
{
if (gf.Loaded || gf.LoadQueued)
{
@@ -2032,7 +2122,27 @@ namespace CodeWalker.GameFiles
if (requestQueue.Count < 20)// && (!gf.LoadQueued)
{
gf.LoadQueued = true;
- requestQueue.Enqueue(gf);
+ gf.LastLoadTime = DateTime.Now;
+
+ await Task.Run(async () =>
+ {
+ await maxCacheLock.WaitAsync();
+ try
+ {
+ await LoadFileAsync(gf).ConfigureAwait(false);
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine(ex);
+ throw ex;
+ }
+ finally
+ {
+ maxCacheLock.Release();
+ }
+ });
+
+ //requestQueue.Enqueue(gf);
}
}
@@ -2040,12 +2150,20 @@ namespace CodeWalker.GameFiles
public Archetype GetArchetype(uint hash)
{
if (!archetypesLoaded) return null;
- if (projectArchetypes.TryGetValue(hash, out var arch) && arch != null)
+ archetypeLock.EnterReadLock();
+ try
{
+ if (projectArchetypes.TryGetValue(hash, out var arch) && arch != null)
+ {
+ return arch;
+ }
+ archetypeDict.TryGetValue(hash, out arch);
return arch;
}
- archetypeDict.TryGetValue(hash, out arch);
- return arch;
+ finally
+ {
+ archetypeLock.ExitReadLock();
+ }
}
public MapDataStoreNode GetMapNode(uint hash)
{
@@ -2054,10 +2172,11 @@ namespace CodeWalker.GameFiles
return node;
}
- public YdrFile GetYdr(uint hash)
+ private readonly object ydrLock = new object();
+ public YdrFile GetYdr(uint hash, bool immediate = false)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (ydrLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ydr);
if (projectFiles.TryGetValue(key, out GameFile pgf))
@@ -2076,8 +2195,6 @@ namespace CodeWalker.GameFiles
ydr = new YdrFile(e);
if (!mainCache.TryAdd(key, ydr))
{
- //ErrorLog("Out of cache space - couldn't load drawable: " + JenkIndex.GetString(hash)); //too spammy...
- ydr.LoadQueued = false;
return null;
}
}
@@ -2089,10 +2206,12 @@ namespace CodeWalker.GameFiles
return ydr;
}
}
+
+ private readonly object yddLock = new object();
public YddFile GetYdd(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (yddLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ydd);
if (projectFiles.TryGetValue(key, out GameFile pgf))
@@ -2103,36 +2222,29 @@ namespace CodeWalker.GameFiles
if (ydd == null)
{
var e = GetYddEntry(hash);
- if (e != null)
+ if (e == null) return null;
+
+ ydd = new YddFile(e);
+ if (!mainCache.TryAdd(key, ydd))
{
- ydd = new YddFile(e);
- if (mainCache.TryAdd(key, ydd))
- {
- TryLoadEnqueue(ydd);
- }
- else
- {
- ydd.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load drawable dictionary: " + JenkIndex.GetString(hash)); //too spammy...
- }
- }
- else
- {
- //ErrorLog("Drawable dictionary not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ydd.Loaded)
+
+ if (!ydd.Loaded && !ydd.LoadQueued)
{
TryLoadEnqueue(ydd);
}
return ydd;
}
}
+
+ private readonly SemaphoreSlim ytdLock = new SemaphoreSlim(1, 1);
public YtdFile GetYtd(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
- {
+ ytdLock.Wait();
+ try {
var key = new GameFileCacheKey(hash, GameFileType.Ytd);
if (projectFiles.TryGetValue(key, out GameFile pgf))
{
@@ -2142,70 +2254,61 @@ namespace CodeWalker.GameFiles
if (ytd == null)
{
var e = GetYtdEntry(hash);
- if (e != null)
+ if (e == null)
{
- ytd = new YtdFile(e);
- if (mainCache.TryAdd(key, ytd))
- {
- TryLoadEnqueue(ytd);
- }
- else
- {
- ytd.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load texture dictionary: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ ytd = new YtdFile(e);
+ if (!mainCache.TryAdd(key, ytd))
{
- //ErrorLog("Texture dictionary not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ytd.Loaded)
+ if (!ytd.Loaded && !ytd.LoadQueued)
{
- TryLoadEnqueue(ytd);
+ _ = TryLoadEnqueue(ytd);
}
return ytd;
+ } finally
+ {
+ ytdLock.Release();
}
}
+
+ private readonly object ymapLock = new object();
public YmapFile GetYmap(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (ymapLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ymap);
YmapFile ymap = mainCache.TryGet(key) as YmapFile;
if (ymap == null)
{
var e = GetYmapEntry(hash);
- if (e != null)
+ if (e == null)
{
- ymap = new YmapFile(e);
- if (mainCache.TryAdd(key, ymap))
- {
- TryLoadEnqueue(ymap);
- }
- else
- {
- ymap.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load ymap: " + JenkIndex.GetString(hash));
- }
+ return null;
}
- else
+ ymap = new YmapFile(e);
+ if (!mainCache.TryAdd(key, ymap))
{
- //ErrorLog("Ymap not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ymap.Loaded)
+ if (!ymap.Loaded && !ymap.LoadQueued)
{
TryLoadEnqueue(ymap);
}
return ymap;
}
}
+
+ private readonly object yftLock = new object();
public YftFile GetYft(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (yftLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Yft);
YftFile yft = mainCache.TryGet(key) as YftFile;
@@ -2216,165 +2319,133 @@ namespace CodeWalker.GameFiles
if (yft == null)
{
var e = GetYftEntry(hash);
- if (e != null)
+ if (e == null)
{
- yft = new YftFile(e);
- if (mainCache.TryAdd(key, yft))
- {
- TryLoadEnqueue(yft);
- }
- else
- {
- yft.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load yft: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ yft = new YftFile(e);
+ if (!mainCache.TryAdd(key, yft))
{
- //ErrorLog("Yft not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!yft.Loaded)
+ if (!yft.Loaded && !yft.LoadQueued)
{
TryLoadEnqueue(yft);
}
return yft;
}
}
+
+ private readonly object ybnLock = new object();
public YbnFile GetYbn(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (ybnLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ybn);
YbnFile ybn = mainCache.TryGet(key) as YbnFile;
if (ybn == null)
{
var e = GetYbnEntry(hash);
- if (e != null)
+ if (e == null)
{
- ybn = new YbnFile(e);
- if (mainCache.TryAdd(key, ybn))
- {
- TryLoadEnqueue(ybn);
- }
- else
- {
- ybn.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load ybn: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ ybn = new YbnFile(e);
+ if (!mainCache.TryAdd(key, ybn))
{
- //ErrorLog("Ybn not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ybn.Loaded)
+ if (!ybn.Loaded && !ybn.LoadQueued)
{
TryLoadEnqueue(ybn);
}
return ybn;
}
}
+
+ private readonly object ycdLock = new object();
public YcdFile GetYcd(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (ycdLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ycd);
YcdFile ycd = mainCache.TryGet(key) as YcdFile;
if (ycd == null)
{
var e = GetYcdEntry(hash);
- if (e != null)
+ if (e == null)
{
- ycd = new YcdFile(e);
- if (mainCache.TryAdd(key, ycd))
- {
- TryLoadEnqueue(ycd);
- }
- else
- {
- ycd.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load ycd: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ ycd = new YcdFile(e);
+ if (!mainCache.TryAdd(key, ycd))
{
- //ErrorLog("Ycd not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ycd.Loaded)
+ if (!ycd.Loaded && !ycd.LoadQueued)
{
TryLoadEnqueue(ycd);
}
return ycd;
}
}
+
+ private readonly object yedLock = new object();
public YedFile GetYed(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (yedLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Yed);
YedFile yed = mainCache.TryGet(key) as YedFile;
if (yed == null)
{
var e = GetYedEntry(hash);
- if (e != null)
+ if (e == null)
{
- yed = new YedFile(e);
- if (mainCache.TryAdd(key, yed))
- {
- TryLoadEnqueue(yed);
- }
- else
- {
- yed.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load yed: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ yed = new YedFile(e);
+ if (!mainCache.TryAdd(key, yed))
{
- //ErrorLog("Yed not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!yed.Loaded)
+ if (!yed.Loaded && !yed.LoadQueued)
{
TryLoadEnqueue(yed);
}
return yed;
}
}
+
+ private readonly object yvnLock = new object();
public YnvFile GetYnv(uint hash)
{
if (!IsInited) return null;
- lock (requestSyncRoot)
+ lock (yvnLock)
{
var key = new GameFileCacheKey(hash, GameFileType.Ynv);
YnvFile ynv = mainCache.TryGet(key) as YnvFile;
if (ynv == null)
{
var e = GetYnvEntry(hash);
- if (e != null)
+ if (e == null)
{
- ynv = new YnvFile(e);
- if (mainCache.TryAdd(key, ynv))
- {
- TryLoadEnqueue(ynv);
- }
- else
- {
- ynv.LoadQueued = false;
- //ErrorLog("Out of cache space - couldn't load ycd: " + JenkIndex.GetString(hash)); //too spammy...
- }
+ return null;
}
- else
+ ynv = new YnvFile(e);
+ if (!mainCache.TryAdd(key, ynv))
{
- //ErrorLog("Ycd not found: " + JenkIndex.GetString(hash)); //too spammy...
+ return null;
}
}
- else if (!ynv.Loaded)
+ if (!ynv.Loaded && !ynv.LoadQueued)
{
TryLoadEnqueue(ynv);
}
@@ -2406,7 +2477,10 @@ namespace CodeWalker.GameFiles
RpfFileEntry entry;
if (!YmapDict.TryGetValue(hash, out entry))
{
- AllYmapsDict.TryGetValue(hash, out entry);
+ if (!AllYmapsDict.TryGetValue(hash, out entry))
+ {
+ Console.WriteLine($"Couldn't find ymap {JenkIndex.GetString(hash)}");
+ }
}
return entry;
}
@@ -2454,6 +2528,17 @@ namespace CodeWalker.GameFiles
return false;
}
+ public ValueTask LoadFileAsync(T file) where T : GameFile, PackedFile
+ {
+ if (file == null) return new ValueTask(false);
+ RpfFileEntry entry = file.RpfFileEntry;
+ if (entry != null)
+ {
+ return RpfMan.LoadFileAsync(file, entry);
+ }
+ return new ValueTask(false);
+ }
+
public T GetFileUncached(RpfFileEntry e) where T : GameFile, new()
{
@@ -2466,23 +2551,19 @@ namespace CodeWalker.GameFiles
public void BeginFrame()
{
- lock (requestSyncRoot)
- {
- mainCache.BeginFrame();
- }
+ mainCache.BeginFrame();
}
- private void LoadFile(GameFile req)
+ private async ValueTask LoadFileAsync(GameFile req)
{
+ //process content requests.
+ if (req.Loaded)
+ return; //it's already loaded... (somehow)
+
+ if ((req.LastUseTime - DateTime.Now).TotalSeconds > 3.0)
+ return; //hasn't been requested lately..! ignore, will try again later if necessary
try
{
- //process content requests.
- if (req.Loaded)
- return; //it's already loaded... (somehow)
-
- if ((req.LastUseTime - DateTime.Now).TotalSeconds > 3.0)
- return; //hasn't been requested lately..! ignore, will try again later if necessary
-
//if (!loadedsomething)
//{
//UpdateStatus?.Invoke("Loading " + req.RpfFileEntry.Name + "...");
@@ -2491,37 +2572,37 @@ namespace CodeWalker.GameFiles
switch (req.Type)
{
case GameFileType.Ydr:
- req.Loaded = LoadFile(req as YdrFile);
+ req.Loaded = await LoadFileAsync(req as YdrFile);
break;
case GameFileType.Ydd:
- req.Loaded = LoadFile(req as YddFile);
+ req.Loaded = await LoadFileAsync(req as YddFile);
break;
case GameFileType.Ytd:
- req.Loaded = LoadFile(req as YtdFile);
+ req.Loaded = await LoadFileAsync(req as YtdFile);
//if (req.Loaded) AddTextureLookups(req as YtdFile);
break;
case GameFileType.Ymap:
YmapFile y = req as YmapFile;
- req.Loaded = LoadFile(y);
+ req.Loaded = await LoadFileAsync(y);
if (req.Loaded) y.InitYmapEntityArchetypes(this);
break;
case GameFileType.Yft:
- req.Loaded = LoadFile(req as YftFile);
+ req.Loaded = await LoadFileAsync(req as YftFile);
break;
case GameFileType.Ybn:
- req.Loaded = LoadFile(req as YbnFile);
+ req.Loaded = await LoadFileAsync(req as YbnFile);
break;
case GameFileType.Ycd:
- req.Loaded = LoadFile(req as YcdFile);
+ req.Loaded = await LoadFileAsync(req as YcdFile);
break;
case GameFileType.Yed:
- req.Loaded = LoadFile(req as YedFile);
+ req.Loaded = await LoadFileAsync(req as YedFile);
break;
case GameFileType.Ynv:
- req.Loaded = LoadFile(req as YnvFile);
+ req.Loaded = await LoadFileAsync(req as YnvFile);
break;
case GameFileType.Yld:
- req.Loaded = LoadFile(req as YldFile);
+ req.Loaded = await LoadFileAsync(req as YldFile);
break;
default:
break;
@@ -2537,26 +2618,103 @@ namespace CodeWalker.GameFiles
ErrorLog("Error loading " + req.ToString());
}
}
- catch(Exception e)
+ catch (Exception e)
{
req.LoadQueued = false;
req.Loaded = false;
Console.WriteLine(e);
+ TryLoadEnqueue(req);
}
finally
{
req.LoadQueued = false;
}
-
}
+ private void LoadFile(GameFile req)
+ {
+ lock (req)
+ {
+ try
+ {
+ //process content requests.
+ if (req.Loaded)
+ return; //it's already loaded... (somehow)
+
+ if ((req.LastUseTime - DateTime.Now).TotalSeconds > 3.0)
+ return; //hasn't been requested lately..! ignore, will try again later if necessary
+
+ //if (!loadedsomething)
+ //{
+ //UpdateStatus?.Invoke("Loading " + req.RpfFileEntry.Name + "...");
+ //}
+
+ switch (req.Type)
+ {
+ case GameFileType.Ydr:
+ req.Loaded = LoadFile(req as YdrFile);
+ break;
+ case GameFileType.Ydd:
+ req.Loaded = LoadFile(req as YddFile);
+ break;
+ case GameFileType.Ytd:
+ req.Loaded = LoadFile(req as YtdFile);
+ //if (req.Loaded) AddTextureLookups(req as YtdFile);
+ break;
+ case GameFileType.Ymap:
+ YmapFile y = req as YmapFile;
+ req.Loaded = LoadFile(y);
+ if (req.Loaded) y.InitYmapEntityArchetypes(this);
+ break;
+ case GameFileType.Yft:
+ req.Loaded = LoadFile(req as YftFile);
+ break;
+ case GameFileType.Ybn:
+ req.Loaded = LoadFile(req as YbnFile);
+ break;
+ case GameFileType.Ycd:
+ req.Loaded = LoadFile(req as YcdFile);
+ break;
+ case GameFileType.Yed:
+ req.Loaded = LoadFile(req as YedFile);
+ break;
+ case GameFileType.Ynv:
+ req.Loaded = LoadFile(req as YnvFile);
+ break;
+ case GameFileType.Yld:
+ req.Loaded = LoadFile(req as YldFile);
+ break;
+ default:
+ break;
+ }
+
+ string str = (req.Loaded ? "Loaded " : "Error loading ") + req.ToString();
+ //string str = string.Format("{0}: {1}: {2}", requestQueue.Count, (req.Loaded ? "Loaded" : "Error loading"), req);
+
+ UpdateStatus?.Invoke(str);
+ //ErrorLog(str);
+ if (!req.Loaded)
+ {
+ ErrorLog("Error loading " + req.ToString());
+ }
+ }
+ catch (Exception e)
+ {
+ req.LoadQueued = false;
+ req.Loaded = false;
+ Console.WriteLine(e);
+ throw;
+ }
+ finally
+ {
+ req.LoadQueued = false;
+ }
+ }
+ }
public bool ContentThreadProc()
{
- lock (requestSyncRoot)
- {
- mainCache.BeginFrame();
- }
+ mainCache.BeginFrame();
int itemcount = 0;
lock (updateSyncRoot)
{
@@ -2855,7 +3013,7 @@ namespace CodeWalker.GameFiles
{
try
{
- if (doydr && entry.NameLower.EndsWith(".ydr"))
+ if (doydr && entry.IsExtension(".ydr"))
{
UpdateStatus?.Invoke(entry.Path);
YdrFile ydr = RpfMan.GetFile(entry);
@@ -2864,7 +3022,7 @@ namespace CodeWalker.GameFiles
if (ydr.Drawable == null) { continue; }
collectDrawable(ydr.Drawable);
}
- else if (doydd & entry.NameLower.EndsWith(".ydd"))
+ else if (doydd & entry.IsExtension(".ydd"))
{
UpdateStatus?.Invoke(entry.Path);
YddFile ydd = RpfMan.GetFile(entry);
@@ -2876,7 +3034,7 @@ namespace CodeWalker.GameFiles
collectDrawable(drawable);
}
}
- else if (doyft && entry.NameLower.EndsWith(".yft"))
+ else if (doyft && entry.IsExtension(".yft"))
{
UpdateStatus?.Invoke(entry.Path);
YftFile yft = RpfMan.GetFile(entry);
@@ -2902,7 +3060,7 @@ namespace CodeWalker.GameFiles
}
}
}
- else if (doypt && entry.NameLower.EndsWith(".ypt"))
+ else if (doypt && entry.IsExtension(".ypt"))
{
UpdateStatus?.Invoke(entry.Path);
YptFile ypt = RpfMan.GetFile(entry);
@@ -3018,19 +3176,30 @@ namespace CodeWalker.GameFiles
}
- private class ShaderXmlDataCollection
+ public class ShaderTextureData
+ {
+ public HashSet Textures { get; set; } = new HashSet();
+ public HashSet Geometries { get; set; } = new HashSet();
+ public int Count { get; set; } = 0;
+ }
+
+ public class ShaderXmlDataCollection
{
public MetaHash Name { get; set; }
public Dictionary FileNames { get; set; } = new Dictionary