Another big commit, updated to .NET 8.0 and a lot more refactoring and optimizations

This commit is contained in:
Niek Schoemaker 2024-01-06 19:41:10 +01:00
parent f21d83cf40
commit 528ab1a98c
No known key found for this signature in database
GPG Key ID: BDF9404CFECB0006
340 changed files with 52838 additions and 45035 deletions

76
.editorconfig Normal file
View File

@ -0,0 +1,76 @@
[*.cs]
# CS8618: Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
error_prone.large_struct_threshold = 128
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
[*.{cs,vb}]
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

View File

@ -1,189 +1,199 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Disassemblers;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using CodeWalker.Core.Utils;
using CodeWalker.GameFiles;
using Collections.Pooled;
using CommunityToolkit.HighPerformance;
using SharpDX;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using static System.Net.Mime.MediaTypeNames;
namespace CodeWalker.Benchmarks
{
//[InProcess]
[ShortRunJob]
[MemoryDiagnoser]
public class Benchmarks
{
public const string markup = @"<CVehicleModelInfo__InitDataList>
<residentTxd>vehshare</residentTxd>
<residentAnims />
<InitDatas>
<Item>
<modelName>brabusgt600</modelName><txdName>brabusgt600</txdName>
<handlingId>brabusgt600</handlingId>
<gameName>GT 600</gameName>
<vehicleMakeName>BRABUS</vehicleMakeName>
<expressionDictName>null</expressionDictName>
<expressionName>null</expressionName>
<animConvRoofDictName>null</animConvRoofDictName>
<animConvRoofName>null</animConvRoofName>
<animConvRoofWindowsAffected />
<ptfxAssetName>null</ptfxAssetName>
<audioNameHash>ta176m177</audioNameHash>
<layout>LAYOUT_LOW</layout>
<coverBoundOffsets>BULLET_COVER_OFFSET_INFO</coverBoundOffsets>
<explosionInfo>EXPLOSION_INFO_DEFAULT</explosionInfo>
<scenarioLayout />
<cameraName>DEFAULT_FOLLOW_VEHICLE_CAMERA</cameraName>
<aimCameraName>MID_BOX_VEHICLE_AIM_CAMERA</aimCameraName>
<bonnetCameraName>VEHICLE_BONNET_CAMERA_NEAR_EXTRA_HIGH</bonnetCameraName>
<povCameraName>DEFAULT_POV_CAMERA</povCameraName>
<FirstPersonDriveByIKOffset x=""0.000000"" y=""-0.060000"" z=""-0.005000"" />
<FirstPersonDriveByUnarmedIKOffset x=""0.000000"" y=""-0.150000"" z=""0.000000"" />
<FirstPersonProjectileDriveByIKOffset x=""0.025000"" y=""-0.100000"" z=""0.050000"" />
<FirstPersonProjectileDriveByPassengerIKOffset x=""-0.025000"" y=""-0.100000"" z=""0.050000"" />
<FirstPersonDriveByLeftPassengerIKOffset x=""0.000000"" y=""0.000000"" z=""0.000000"" />
<FirstPersonDriveByRightPassengerIKOffset x=""0.000000"" y=""-0.060000"" z=""-0.005000"" />
<FirstPersonDriveByLeftPassengerUnarmedIKOffset x=""0.000000"" y=""0.000000"" z=""0.000000"" />
<FirstPersonDriveByRightPassengerUnarmedIKOffset x=""0.000000"" y=""-0.150000"" z=""0.000000"" />
<FirstPersonMobilePhoneOffset x=""0.125000"" y=""0.175000"" z=""0.513000"" />
<FirstPersonPassengerMobilePhoneOffset x=""0.136000"" y=""0.123000"" z=""0.425000"" />
<PovCameraOffset x=""0.000000"" y=""-0.200000"" z=""0.615000"" />
<PovCameraVerticalAdjustmentForRollCage value=""0.000000"" />
<PovPassengerCameraOffset x=""0.000000"" y=""0.000000"" z=""0.000000"" />
<vfxInfoName>VFXVEHICLEINFO_CAR_BULLET</vfxInfoName>
<shouldUseCinematicViewMode value=""true"" />
<shouldCameraTransitionOnClimbUpDown value=""false"" />
<shouldCameraIgnoreExiting value=""false"" />
<AllowPretendOccupants value=""true"" />
<AllowJoyriding value=""true"" />
<AllowSundayDriving value=""true"" />
<AllowBodyColorMapping value=""true"" />
<wheelScale value=""0.293500"" />
<wheelScaleRear value=""0.293500"" />
<dirtLevelMin value=""0.000000"" />
<dirtLevelMax value=""0.450000"" />
<envEffScaleMin value=""0.000000"" />
<envEffScaleMax value=""1.000000"" />
<envEffScaleMin2 value=""0.000000"" />
<envEffScaleMax2 value=""1.000000"" />
<damageMapScale value=""0.600000"" />
<damageOffsetScale value=""0.400000"" />
<diffuseTint value=""0xAA0A0A0A"" />
<steerWheelMult value=""1.000000"" />
<HDTextureDist value=""5.000000"" />
<lodDistances content=""float_array"">
60.000000
80.000000
100.000000
120.000000
500.000000
500.000000
</lodDistances>
<minSeatHeight value=""0.813"" />
<identicalModelSpawnDistance value=""80"" />
<maxNumOfSameColor value=""1"" />
<defaultBodyHealth value=""1000.000000"" />
<pretendOccupantsScale value=""1.000000"" />
<visibleSpawnDistScale value=""1.000000"" />
<trackerPathWidth value=""2.000000"" />
<weaponForceMult value=""1.000000"" />
<frequency value=""10"" />
<swankness>SWANKNESS_3</swankness>
<maxNum value=""1"" />
<flags>FLAG_SPORTS FLAG_RICH_CAR FLAG_NO_BROKEN_DOWN_SCENARIO FLAG_RECESSED_TAILLIGHT_CORONAS FLAG_NO_HEAVY_BRAKE_ANIMATION</flags>
<type>VEHICLE_TYPE_CAR</type>
<plateType>VPT_FRONT_AND_BACK_PLATES</plateType>
<dashboardType>VDT_BANSHEE</dashboardType>
<vehicleClass>VC_SPORT</vehicleClass>
<wheelType>VWT_HIEND</wheelType>
<trailers>
<Item>docktrailer</Item>
<Item>trailers</Item>
<Item>trailers2</Item>
<Item>trailers3</Item>
<Item>trailers4</Item>
<Item>tanker</Item>
<Item>trailerlogs</Item>
<Item>tr2</Item>
<Item>trflat</Item>
</trailers>
<additionalTrailers />
<drivers>
<Item>
<driverName>S_M_Y_Cop_01</driverName>
<npcName/>
</Item>
</drivers>
<extraIncludes />
<doorsWithCollisionWhenClosed />
<driveableDoors />
<bumpersNeedToCollideWithMap value=""false"" />
<needsRopeTexture value=""false"" />
<requiredExtras />
<rewards />
<cinematicPartCamera>
<Item>WHEEL_FRONT_RIGHT_CAMERA</Item>
<Item>WHEEL_FRONT_LEFT_CAMERA</Item>
<Item>WHEEL_REAR_RIGHT_CAMERA</Item>
<Item>WHEEL_REAR_LEFT_CAMERA</Item>
</cinematicPartCamera>
<NmBraceOverrideSet />
<buoyancySphereOffset x=""0.000000"" y=""0.000000"" z=""0.000000"" />
<buoyancySphereSizeScale value=""1.000000"" />
<pOverrideRagdollThreshold type=""CVehicleModelInfo__CVehicleOverrideRagdollThreshold"">
<MinComponent value=""22"" />
<MaxComponent value=""22"" />
<ThresholdMult value=""1.500000"" />
</pOverrideRagdollThreshold>
<firstPersonDrivebyData>
<Item>LOW_BULLET_FRONT_LEFT</Item>
<Item>LOW_BULLET_FRONT_RIGHT</Item>
</firstPersonDrivebyData>
</Item>
//private class Config : ManualConfig
//{
// public Config()
// {
// AddDiagnoser(new MemoryDiagnoser(new MemoryDiagnoserConfig(true)));
// AddDiagnoser(new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig()));
// }
//}
private static string markup = "<?xml version=\"1.0\"?>\r\n<vectors><Item x=\"\"></Item></vectors>";
private List<SimpleType3> listClass;
private List<BigStruct> listStruct;
private PooledList<SimpleType3> pooledListClass;
private PooledList<BigStruct> pooledListStruct;
public class SimpleType
{
public int Value1;
}
public class SimpleType2 : SimpleType
{
public int Value2;
}
public class SimpleType3
{
public long Value1;
public long Value2;
public long Value3;
public SimpleType3()
{
Value1 = random.Next();
Value2 = random.Next();
Value3 = random.Next();
}
}
private SimpleType[] intArr;
private List<SimpleType> intList;
private static Random random = new Random(42);
public struct BigStruct
{
public long Value1;
public long Value2;
public long Value3;
public long Value4;
public long Value5;
public long Value6;
public long Value7;
public long Value8;
public BigStruct()
{
Value1 = random.Next();
Value2 = random.Next();
Value3 = random.Next();
Value4 = random.Next();
Value5 = random.Next();
Value6 = random.Next();
Value7 = random.Next();
Value8 = random.Next();
}
public BigStruct(long value1, long value2, long value3, long value4, long value5, long value6, long value7, long value8)
{
Value1 = value1;
Value2 = value2;
Value3 = value3;
Value4 = value4;
Value5 = value5;
Value6 = value6;
Value7 = value7;
Value8 = value8;
}
public static BigStruct operator +(in BigStruct left, in BigStruct right)
{
return new BigStruct(
left.Value1 + right.Value1,
left.Value2 + right.Value2,
left.Value3 + right.Value3,
left.Value4 + right.Value4,
left.Value5 + right.Value5,
left.Value6 + right.Value6,
left.Value7 + right.Value7,
left.Value8 + right.Value8
);
}
}
[Params(10, 100, 1000)]
public int Length { get; set; } = 10000;
[Params(100)]
public int Chance { get; set; } = 25;
public ArrayOfChars64 chars64;
private BigStruct[] vectors = new BigStruct[0];
private BigStruct vector;
</InitDatas>
<txdRelationships>
<Item>
<parent>vehicles_banshee_interior</parent>
<child>brabusgt600</child>
</Item>
private byte valueByte;
private uint valueUint;
private uint[] data;
private MetaName[] Values;
</txdRelationships>
</CVehicleModelInfo__InitDataList>";
private int randomValue;
private byte[] data;
private RpfFileEntry fileEntry;
private uint[] ushorts = new uint[0];
private string Str = "iakslgbhfibnrihbderpiugaehigoI BIHGVUIVDSOUFVBOUADGBOIUYfgiuywetrg872q13rh9872`134tgyihsbaopuJGUIYODGBFIOUFgvbouailksdbhnfp";
[GlobalSetup]
public void Setup()
{
data = new byte[2048];
var random = new Random(42);
for (int i = 0; i < data.Length; i++)
random = new Random(42);
valueByte = 0;
valueUint = 0;
ushorts = new uint[Length];
for (int i = 0; i < Length; i++)
{
data[i] = (byte)random.Next(byte.MinValue, byte.MaxValue);
ushorts[i] = (uint)random.Next(0, int.MaxValue);
}
GTA5Keys.LoadFromPath("C:\\Program Files\\Rockstar Games\\Grand Theft Auto V", "");
var hashes = MemoryMarshal.Cast<uint, MetaHash>(ushorts);
for (int i = 0; i < Length; i++)
{
Console.WriteLine($"{ushorts[i]} -> {hashes[i]}");
}
//Console.WriteLine("Setup done");
//XElement? result = null;
//var _doc = new XmlDocument();
//_doc.LoadXml(markup);
//var doc = XDocument.Load(new XmlNodeReader(_doc));
//Console.WriteLine(doc.Root);
//data = new byte[2048];
//var random = new Random(42);
//for (int i = 0; i < data.Length; i++)
//{
// data[i] = (byte)random.Next(byte.MinValue, byte.MaxValue);
//}
//GTA5Keys.LoadFromPath("C:\\Program Files\\Rockstar Games\\Grand Theft Auto V", "");
//rotation = new Quaternion(random.NextFloat(-1.0f, 1.0f), random.NextFloat(-1.0f, 1.0f), random.NextFloat(-1.0f, 1.0f), random.NextFloat(-1.0f, 1.0f));
//translation = new Vector3(random.NextFloat(-1.0f, 1.0f), random.NextFloat(-1.0f, 1.0f), random.NextFloat(-1.0f, 1.0f));
//scale = random.NextFloat(-1.0f, 1.0f);
//matrix = Matrix.AffineTransformation(scale, rotation, translation);
}
//[Benchmark(Baseline = true)]
@ -200,16 +210,155 @@ namespace CodeWalker.Benchmarks
// vehiclesFile.Load(data, fileEntry);
//}
[Benchmark]
public void DecryptNGSpan()
//[Benchmark(Baseline = true)]
//public uint SwapBytes()
//{
// var result = test;
// for (int i = 0; i < 1000; i++)
// {
// result = MetaTypes.SwapBytes(result);
// }
// return result;
//}
//[Benchmark]
//public uint ReverseEndianness()
//{
// var result = test;
// for (int i = 0; i < 1000; i++)
// {
// result = BinaryPrimitives.ReverseEndianness(result);
// }
// return result;
//}
//[Benchmark]
//public int IndexOf()
//{
// return Str.IndexOf('\0');
//}
//[Benchmark]
//public int IndexOfAsSpan()
//{
// return Str.AsSpan().IndexOf('\0');
//}
private PooledList<SimpleType3> getPooledListClass()
{
GTACrypto.DecryptNG(data.AsSpan(), "kaas", 2048);
var list = new PooledList<SimpleType3>();
for (int i = 0; i < Length; i++)
{
list.Add(new SimpleType3());
}
return list;
}
private PooledList<BigStruct> getPooledListStruct()
{
var list = new PooledList<BigStruct>();
for (int i = 0; i < Length; i++)
{
list.Add(new BigStruct());
}
return list;
}
private List<SimpleType3> getListClass()
{
var list = new List<SimpleType3>();
for (int i = 0; i < Length; i++)
{
list.Add(new SimpleType3());
}
return list;
}
private List<BigStruct> getListStruct()
{
var list = new List<BigStruct>();
for (int i = 0; i < Length; i++)
{
list.Add(new BigStruct());
}
return list;
}
[MethodImpl(MethodImplOptions.NoInlining)]
private void inVectorOperator()
{
var vect = Vector256.Create<uint>(data.AsSpan());
var shiftVect = Vector256.Create<uint>(10);
var leftShiftVect = Vector256.Create<uint>(6);
}
private uint joaatLower(Span<char> span)
{
uint h = 0;
for (int i = 0; i < span.Length; i++)
{
h += toLower(span[i]);
h += h << 10;
h ^= h >> 6;
}
return h;
}
private uint joaat(Span<char> span)
{
uint h = 0;
for (int i = 0; i < span.Length; i++)
{
h += span[i];
h += h << 10;
h ^= h >> 6;
}
return h;
}
private void toLowerVectorized(Span<char> span)
{
Ascii.ToLowerInPlace(span, out _);
}
private char toLower(char c)
{
return ('A' <= c && c <= 'Z') ? (char)(c | 0x20) : c;
}
private void toLowerPleb(Span<char> span)
{
for (int i = 0; i < span.Length; i++)
{
span[i] = toLower(span[i]);
}
}
[Benchmark]
public void ReverseEndianness()
{
//BinaryPrimitives.ReverseEndianness(MemoryMarshal.Cast<float, uint>(ushorts), MemoryMarshal.Cast<float, uint>(ushorts));
}
[Benchmark]
public void DecryptNG()
public void SwapBytes()
{
GTACrypto.DecryptNG(data, "kaas", 2048);
var _ushorts = ushorts;
for (int i = 0; i < _ushorts.Length; i++)
{
_ushorts[i] = MetaTypes.SwapBytes(_ushorts[i]);
}
}
}
}

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyTitle>CodeWalker.Benchmarks</AssemblyTitle>
@ -8,8 +8,12 @@
<Copyright>Copyright © 2023</Copyright>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
@ -19,37 +23,37 @@
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.10" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.11" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Gee.External.Capstone" Version="2.3.0" />
<PackageReference Include="Iced" Version="1.17.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.251802" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.332302" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.0.2" />
<PackageReference Include="Iced" Version="1.20.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.452401" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="3.1.456101" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.7" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageReference Include="Perfolizer" Version="0.2.1" />
<PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
<PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
<PackageReference Include="System.Management" Version="6.0.2" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Reflection.Metadata" Version="6.0.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="8.0.0" />
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="System.Reflection.Metadata" Version="8.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="System.Security.AccessControl" Version="6.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@ -16,6 +16,10 @@ namespace CodeWalker.Benchmarks
static void Main(string[] args)
{
#if DEBUG
var benchmarks = new Benchmarks();
benchmarks.Setup();
//var benchmarks = new Benchmarks();
//benchmarks.Setup();
@ -28,8 +32,9 @@ namespace CodeWalker.Benchmarks
//benchmarks.GlobalCleanup();
//ParseBuffer();
#else
BenchmarkRunner.Run<Benchmarks>();
#endif
}
}
}

View File

@ -1,22 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>annotations</Nullable>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Collections.Pooled" Version="1.0.82" />
<PackageReference Include="CommunityToolkit.Diagnostics" Version="8.2.2" />
<PackageReference Include="CommunityToolkit.HighPerformance" Version="8.2.2" />
<PackageReference Include="DirectXTexNet" Version="1.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="RequiredMemberAttribute" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.4.0-beta.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="ErrorProne.NET.Structs" Version="0.4.0-beta.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />

View File

@ -8,6 +8,7 @@ using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml;
using System.Runtime.InteropServices;
using CodeWalker.Core.Utils;
namespace CodeWalker.GameFiles
{
@ -345,7 +346,8 @@ namespace CodeWalker.GameFiles
}
public static void WriteXmlNode(AwcFile f, StringBuilder sb, int indent, string wavfolder, string name = "AudioWaveContainer")
{
if (f == null) return;
if (f == null)
return;
AwcXml.OpenTag(sb, indent, name);
f.WriteXml(sb, indent + 1, wavfolder);
AwcXml.CloseTag(sb, indent, name);
@ -1014,7 +1016,7 @@ namespace CodeWalker.GameFiles
{
var export = !string.IsNullOrEmpty(wavfolder);
var fname = Name?.Replace("/", "")?.Replace("\\", "") ?? "0x0";
byte[] fdata = null;
byte[] fdata;
if (MidiChunk != null)
{
fname += ".midi";
@ -1038,8 +1040,10 @@ namespace CodeWalker.GameFiles
File.WriteAllBytes(filepath, fdata);
}
}
catch
{ }
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
if (StreamFormat != null)
{
@ -1107,8 +1111,10 @@ namespace CodeWalker.GameFiles
}
}
}
catch
{ }
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
@ -1363,7 +1369,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
var hash = "0x" + (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0') + ": ";
var hash = "0x" + (StreamInfo?.Id.ToString("X8") ?? "0000000") + ": ";
if (FormatChunk != null)
{
return hash + FormatChunk?.ToString() ?? "AwcAudio";
@ -1876,7 +1882,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Id.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, headroom: " + Headroom.ToString();
return $"{Id}, {Codec}: {Samples} samples, {SamplesPerSecond} samples/sec, headroom: {Headroom}";
}
}
@ -1905,7 +1911,7 @@ namespace CodeWalker.GameFiles
resentry.SystemFlags = BitConverter.ToUInt32(data, 8);
resentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
if (rsc7 != 0x37435352)
if (rsc7 != (uint)FileHeader.RSC7)
{ } //testing..
if (version != 46) //46 is Clip Dictionary...
{ }
@ -2163,7 +2169,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "gesture: " + (Gestures?.Length ?? 0).ToString() + " items";
return $"gesture: {Gestures?.Length ?? 0} items";
}
}
@ -2198,32 +2204,26 @@ namespace CodeWalker.GameFiles
public void WriteLine(StringBuilder sb)
{
sb.Append(UnkUint1.ToString());
sb.Append(" ");
sb.Append(' ');
sb.Append(FloatUtil.ToString(UnkFloat1));
sb.Append(" ");
sb.Append(' ');
sb.Append(UnkUshort1.ToString());
sb.Append(" ");
sb.Append(' ');
sb.Append(UnkUshort2.ToString());
sb.AppendLine();
}
public void ReadLine(string s)
{
var split = s.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
var list = new List<string>();
foreach (var str in split)
Span<Range> ranges = stackalloc Range[5];
var span = s.AsSpan();
span.Split(ranges, ' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (ranges.Length >= 4)
{
var tstr = str.Trim();
if (!string.IsNullOrEmpty(tstr))
{
list.Add(tstr);
}
}
if (list.Count >= 4)
{
uint.TryParse(list[0], out uint u1);
FloatUtil.TryParse(list[1], out float f1);
ushort.TryParse(list[2], out ushort s1);
ushort.TryParse(list[3], out ushort s2);
uint.TryParse(span[ranges[0]], out uint u1);
FloatUtil.TryParse(span[ranges[1]], out float f1);
ushort.TryParse(span[ranges[2]], out ushort s1);
ushort.TryParse(span[ranges[3]], out ushort s2);
UnkUint1 = u1;
UnkFloat1 = f1;
UnkUshort1 = s1;
@ -2247,7 +2247,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return UnkUint1.ToString() + ", " + UnkFloat1.ToString() + ", " + UnkUshort1.ToString() + ", " + UnkUshort2.ToString();
return $"{UnkUint1}, {UnkFloat1}, {UnkUshort1}, {UnkUshort2}";
}
}
@ -2326,7 +2326,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "granulargrains: " + (GranularGrains?.Length ?? 0).ToString() + " items";
return $"granulargrains: {(GranularGrains?.Length ?? 0)} items";
}
}
@ -2410,7 +2410,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Identifier.ToString() + ": " + UnkUint1.ToString() + ": " + GrainCount.ToString() + " items";
return $"{Identifier}: {UnkUint1}: {GrainCount} items";
}
}
@ -2456,7 +2456,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "granularloops: " + (GranularLoops?.Length ?? 0).ToString() + " items";
return $"granularloops: {GranularLoops?.Length ?? 0} items";
}
}
@ -2576,7 +2576,7 @@ namespace CodeWalker.GameFiles
break;
}
return Name.ToString() + ": " + valstr + ", " + SampleOffset.ToString() + ", " + Unused.ToString();
return $"{Name}: {valstr}, {SampleOffset}, {Unused}";
}
}
@ -3029,15 +3029,22 @@ namespace CodeWalker.GameFiles
public static string GetXml(AwcFile awc, string outputFolder = "")
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (awc != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
AwcFile.WriteXmlNode(awc, sb, 0, outputFolder);
}
sb.AppendLine(XmlHeader);
return sb.ToString();
if (awc != null)
{
AwcFile.WriteXmlNode(awc, sb, 0, outputFolder);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
}
}

View File

@ -109,10 +109,10 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
foreach (var poly in polys) //split along borders
{
var verts = poly.Vertices;
if (verts == null)
{ continue; }//ignore empty polys..
if (verts is null)
continue;//ignore empty polys..
if (verts.Length < 3)
{ continue; }//not enough verts for a triangle!
continue;//not enough verts for a triangle!
Vector2I gprev = NavGrid.GetCellPos(verts[0]);
int split1 = 0;
@ -156,13 +156,18 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
verts1.Clear();
verts2.Clear();
for (int i = 0; i < split1; i++) verts1.Add(verts[i]);
for (int i = 0; i < split1; i++)
verts1.Add(verts[i]);
verts1.Add(sp1);
verts1.Add(sp2);
for (int i = split2end; i < verts.Length; i++) verts1.Add(verts[i]);
for (int i = split2end; i < verts.Length; i++)
verts1.Add(verts[i]);
verts2.Add(sp1);
for (int i = split1; i < split2end; i++) verts2.Add(verts[i]);
for (int i = split1; i < split2end; i++)
verts2.Add(verts[i]);
verts2.Add(sp2);
poly1.Vertices = verts1.ToArray();
@ -203,9 +208,9 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
var verts = poly.Vertices;
var ec = edges?.Length ?? 0;
if (ec <= 0)
{ continue; }//shouldn't happen - no edges?
continue;//shouldn't happen - no edges?
if (ec != poly.Vertices?.Length)
{ continue; }//shouldn't happen
continue;//shouldn't happen
var split1beg = polysplit.Split1 - 1;
var split1end = polysplit.Split1;
@ -256,22 +261,22 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
foreach (var poly in newpolys) //fix any untouched edges that joined to split polys
{
if (poly.Edges?.Length != poly.Vertices?.Length)
{ continue; }//shouldn't happen (no edges?)
if (poly.Edges is null || poly.Vertices is null || poly.Edges.Length != poly.Vertices.Length)
continue;//shouldn't happen (no edges?)
for (int i = 0; i < poly.Edges.Length; i++)
{
var edge = poly.Edges[i];
var vert = poly.Vertices[i];
if (edge == null)
{ continue; }//shouldn't happen
continue;//shouldn't happen
if (edge.Poly1 != edge.Poly2)
{ continue; }//shouldn't happen?
continue;//shouldn't happen?
if (edge.Poly1 == null)
{ continue; }//probably this edge joins to nothing
continue;//probably this edge joins to nothing
YnvPolySplit polysplit;
if (polysplits.TryGetValue(edge.Poly1, out polysplit))
if (polysplits.TryGetValue(edge.Poly1, out var polysplit))
{
var newpoly = polysplit.GetNearest(vert);
if (newpoly == null)
@ -287,7 +292,7 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
return newpolys;
}
private Vector3 GetSplitPos(Vector3 a, Vector3 b, bool xaxis)
private Vector3 GetSplitPos(in Vector3 a, in Vector3 b, bool xaxis)
{
Vector3 ca = NavGrid.GetCellRel(a);
Vector3 cb = NavGrid.GetCellRel(b);
@ -311,19 +316,19 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
return a + (b - a) * Math.Min(Math.Max(f, 0.0f), 1.0f);
}
private bool IsValidSplit(Vector3 s1, Vector3 s2, Vector3 v1a, Vector3 v1b, Vector3 v2a, Vector3 v2b)
private bool IsValidSplit(in Vector3 s1, in Vector3 s2, in Vector3 v1a, in Vector3 v1b, in Vector3 v2a, in Vector3 v2b)
{
if (XYEqual(s1, s2)) return false;
if (XYEqual(s1, v1a)) return false;
if (XYEqual(s1, v1b)) return false;
if (XYEqual(s2, v2a)) return false;
if (XYEqual(s2, v2b)) return false;
if (XYEqual(in s1, in s2)) return false;
if (XYEqual(in s1, in v1a)) return false;
if (XYEqual(in s1, in v1b)) return false;
if (XYEqual(in s2, in v2a)) return false;
if (XYEqual(in s2, in v2b)) return false;
return true;
}
private bool XYEqual(Vector3 v1, Vector3 v2)
private bool XYEqual(in Vector3 v1, in Vector3 v2)
{
return ((v1.X == v2.X) && (v1.Y == v2.Y));
return v1.X == v2.X && v1.Y == v2.Y;
}
private class YnvPolySplit
@ -335,8 +340,8 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
public int Split2;
public YnvPoly GetNearest(Vector3 v)
{
if (New1?.Vertices == null) return New2;
if (New2?.Vertices == null) return New1;
if (New1?.Vertices is null) return New2;
if (New2?.Vertices is null) return New1;
float len1 = float.MaxValue;
float len2 = float.MaxValue;
for (int i = 0; i < New1.Vertices.Length; i++)
@ -357,11 +362,11 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
return New1;
}
}
private YnvPolySplit TryGetSplit(Dictionary<YnvPoly, YnvPolySplit> polysplits, YnvPoly poly)
private YnvPolySplit? TryGetSplit(Dictionary<YnvPoly, YnvPolySplit> polysplits, YnvPoly poly)
{
if (poly == null) return null;
YnvPolySplit r = null;
polysplits.TryGetValue(poly, out r);
if (poly == null)
return null;
_ = polysplits.TryGetValue(poly, out var r);
return r;
}
@ -379,7 +384,7 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
if (ynv == null)
{
ynv = new YnvFile();
ynv.Name = "navmesh[" + cell.FileX.ToString() + "][" + cell.FileY.ToString() + "]";
ynv.Name = $"navmesh[{cell.FileX}][{cell.FileY}]";
ynv.Nav = new NavMesh();
ynv.Nav.SetDefaults(false);
ynv.Nav.AABBSize = new Vector3(NavGrid.CellSize, NavGrid.CellSize, 0.0f);
@ -411,14 +416,13 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
foreach (var poly in polys)
{
poly.CalculatePosition();
var pos = poly.Position;
var verts = poly.Vertices;
if (verts != null)
if (verts is not null)
{
foreach (var vert in verts)
foreach (ref var vert in verts.AsSpan())
{
bbmin = Vector3.Min(bbmin, vert);
bbmax = Vector3.Max(bbmax, vert);
Vector3.Min(ref bbmin, ref vert, out bbmin);
Vector3.Max(ref bbmax, ref vert, out bbmax);
}
}
}
@ -460,7 +464,9 @@ namespace CodeWalker.Core.GameFiles.FileTypes.Builders
float zmax = float.MinValue;
foreach (var poly in ynv.Polys)
{
foreach (var vert in poly.Vertices)
if (poly.Vertices is null)
continue;
foreach (ref var vert in poly.Vertices.AsSpan())
{
zmin = Math.Min(zmin, vert.Z);
zmax = Math.Max(zmax, vert.Z);

View File

@ -1,4 +1,5 @@
using SharpDX;
using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -28,6 +29,7 @@ namespace CodeWalker.GameFiles
public CInteriorProxy[] AllCInteriorProxies { get; set; }
public BoundsStoreItem[] AllBoundsStoreItems { get; set; }
[SkipLocalsInit]
public void Load(byte[] data, RpfFileEntry entry)
{
FileEntry = entry;
@ -36,6 +38,7 @@ namespace CodeWalker.GameFiles
using BinaryReader br = new BinaryReader(ms);
Span<char> charArr = stackalloc char[100];
var length = 0;
for (int i = 0; (i < 100) && (i < data.Length); i++)
{
@ -56,10 +59,10 @@ namespace CodeWalker.GameFiles
uint structcount = 0;
uint modlen;
bool indates = false;
var dates = new List<CacheFileDate>();
var allMapNodes = new List<MapDataStoreNode>();
var allCInteriorProxies = new List<CInteriorProxy>();
var allBoundsStoreItems = new List<BoundsStoreItem>();
using var dates = new PooledList<CacheFileDate>();
using var allMapNodes = new PooledList<MapDataStoreNode>();
using var allCInteriorProxies = new PooledList<CInteriorProxy>();
using var allBoundsStoreItems = new PooledList<BoundsStoreItem>();
for (int i = 100; i < data.Length; i++)
{
byte b = data[i];
@ -68,7 +71,6 @@ namespace CodeWalker.GameFiles
if (b == 0xA)
{
lastn = i;
string line = new string(charArr.Slice(0, length));
switch (charArr.Slice(0, length))
{
case "<fileDates>":
@ -125,6 +127,7 @@ namespace CodeWalker.GameFiles
{ } //just testing
else
{
string line = new string(charArr.Slice(0, length));
dates.Add(new CacheFileDate(line));//eg: 2740459947 (hash of: platform:/data/cdimages/scaleform_frontend.rpf) 130680580712018938 8944
}
break;
@ -204,7 +207,6 @@ namespace CodeWalker.GameFiles
mapnode.InteriorProxyListToArray();
}
br.Dispose();
ms.Dispose();
@ -407,7 +409,7 @@ namespace CodeWalker.GameFiles
public string ToCacheFileString()
{
return FileName.Hash.ToString() + " " + TimeStamp.ToFileTimeUtc().ToString() + " " + FileID.ToString();
return $"{FileName.Hash} {TimeStamp.ToFileTimeUtc()} {FileID}";
}
public void WriteXml(StringBuilder sb, int indent)
@ -425,7 +427,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return FileName.ToString() + ", " + TimeStamp.ToString() + ", " + FileID.ToString();
return $"{FileName}, {TimeStamp}, {FileID}";
}
}
@ -478,10 +480,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Name.ToString() + ", " +
Min.ToString() + ", " +
Max.ToString() + ", " +
Layer.ToString();
return $"{Name}, {Min}, {Max}, {Layer}";
}
}
@ -775,19 +774,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Unk01.ToString() + ", " +
Unk02.ToString() + ", " +
Unk03.ToString() + ", " +
Name.ToString() + ", " +
Parent.ToString() + ", " +
Position.ToString() + ", " +
Orientation.ToString() + ", " +
BBMin.ToString() + ", " +
BBMax.ToString() + ", " +
Unk11.ToString() + ", " +
Unk12.ToString() + ", " +
Unk13.ToString() + ", " +
Unk14.ToString();
return $"{Unk01}, {Unk02}, {Unk03}, {Name}, {Parent}, {Position}, {Orientation}, {BBMin}, {BBMax}, {Unk11}, {Unk12}, {Unk13}, {Unk14}";
}
}
@ -796,10 +783,10 @@ namespace CodeWalker.GameFiles
public MetaHash Name { get; set; }
public MetaHash ParentName { get; set; }
public uint ContentFlags { get; set; }
public Vector3 streamingExtentsMin { get; set; }
public Vector3 streamingExtentsMax { get; set; }
public Vector3 entitiesExtentsMin { get; set; }
public Vector3 entitiesExtentsMax { get; set; }
public Vector3 streamingExtentsMin;
public Vector3 streamingExtentsMax;
public Vector3 entitiesExtentsMin;
public Vector3 entitiesExtentsMax;
public byte Unk1 { get; set; }
public byte Unk2 { get; set; }
public byte Unk3 { get; set; }
@ -912,15 +899,12 @@ namespace CodeWalker.GameFiles
public void AddChildToList(MapDataStoreNode child)
{
if (ChildrenList == null)
{
ChildrenList = new List<MapDataStoreNode>();
}
ChildrenList ??= new List<MapDataStoreNode>();
ChildrenList.Add(child);
}
public void ChildrenListToArray()
{
if (ChildrenList != null)
if (ChildrenList is not null)
{
Children = ChildrenList.ToArray();
ChildrenList = null; //plz get this GC
@ -928,15 +912,12 @@ namespace CodeWalker.GameFiles
}
public void AddInteriorToList(CInteriorProxy iprx)
{
if (InteriorProxyList == null)
{
InteriorProxyList = new List<CInteriorProxy>();
}
InteriorProxyList ??= new List<CInteriorProxy>();
InteriorProxyList.Add(iprx);
}
public void InteriorProxyListToArray()
{
if (InteriorProxyList != null)
if (InteriorProxyList is not null)
{
InteriorProxies = InteriorProxyList.ToArray();
InteriorProxyList = null; //plz get this GC
@ -945,13 +926,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Name.ToString() + ", " +
ParentName.ToString() + ", " +
ContentFlags.ToString() + ", " +
streamingExtentsMin.ToString() + ", " +
streamingExtentsMax.ToString() + ", " +
entitiesExtentsMin.ToString() + ", " +
entitiesExtentsMax.ToString();// + ", " +
return $"{Name}, {ParentName}, {ContentFlags}, {streamingExtentsMin}, {streamingExtentsMax}, {entitiesExtentsMin}, {entitiesExtentsMax}";// + ", " +
}
}
@ -972,16 +947,24 @@ namespace CodeWalker.GameFiles
{
get
{
StringBuilder sb = new StringBuilder();
if (Unk02 != null)
if (Unk02 is null)
return string.Empty;
StringBuilder sb = MetaXmlBase.StringBuilderPool.Get();
try
{
for (int i = 0; i < Unk02.Length; i++)
foreach (var b in Unk02)
{
if (Unk02[i] == 0) break;
sb.Append((char)Unk02[i]);
if (b == 0)
break;
sb.Append((char)b);
}
return sb.ToString();
}
finally
{
MetaXmlBase.StringBuilderPool.Return(sb);
}
return sb.ToString();
}
}
@ -1027,7 +1010,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Unk01.ToString() + ", " + Unk02str;
return $"{Unk01}, {Unk02str}";
}
}
@ -1038,21 +1021,28 @@ namespace CodeWalker.GameFiles
public static string GetXml(CacheDatFile cdf)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (cdf != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
var name = "CacheFile";
sb.AppendLine(XmlHeader);
OpenTag(sb, 0, name);
if (cdf != null)
{
var name = "CacheFile";
cdf.WriteXml(sb, 1);
OpenTag(sb, 0, name);
CloseTag(sb, 0, name);
cdf.WriteXml(sb, 1);
CloseTag(sb, 0, name);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
return sb.ToString();
}

View File

@ -35,8 +35,7 @@ namespace CodeWalker.GameFiles
//can be PSO .ymt or XML .meta
MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms))
if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(data);
@ -59,8 +58,6 @@ namespace CodeWalker.GameFiles
var msg = ex.Message;
}
}
else
{ }
if (xdoc.DocumentElement != null)

View File

@ -10,6 +10,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using CodeWalker.Core.Utils;
namespace CodeWalker.GameFiles
{
@ -78,12 +79,11 @@ namespace CodeWalker.GameFiles
public CVehicleModelInfoVariation(XmlNode node)
{
XmlNode cnode;
cnode = node.SelectSingleNode("variationData");
if (cnode != null)
var variationNode = node.SelectSingleNode("variationData");
if (variationNode is not null)
{
var items = cnode.SelectNodes("Item");
if (items.Count > 0)
var items = variationNode.SelectNodes("Item");
if (items is not null && items.Count > 0)
{
variationData = new CVehicleModelInfoVariation_418053801[items.Count];
for (int i = 0; i < items.Count; i++)
@ -107,12 +107,11 @@ namespace CodeWalker.GameFiles
public CVehicleModelInfoVariation_418053801(XmlNode node)
{
modelName = Xml.GetChildInnerText(node, "modelName");
XmlNode cnode;
cnode = node.SelectSingleNode("colors");
if (cnode != null)
var colorsNode = node.SelectSingleNode("colors");
if (colorsNode is not null)
{
var items = cnode.SelectNodes("Item");
if (items.Count > 0)
var items = colorsNode.SelectNodes("Item");
if (items is not null && items.Count > 0)
{
colors = new CVehicleModelInfoVariation_2575850962[items.Count];
for (int i = 0; i < items.Count; i++)
@ -121,11 +120,11 @@ namespace CodeWalker.GameFiles
}
}
}
cnode = node.SelectSingleNode("kits");
if (cnode != null)
var kitsNode = node.SelectSingleNode("kits");
if (kitsNode is not null)
{
var items = cnode.SelectNodes("Item");
if (items.Count > 0)
var items = kitsNode.SelectNodes("Item");
if (items is not null && items.Count > 0)
{
kits = new MetaHash[items.Count];
for (int i = 0; i < items.Count; i++)
@ -134,11 +133,11 @@ namespace CodeWalker.GameFiles
}
}
}
cnode = node.SelectSingleNode("windowsWithExposedEdges");
if (cnode != null)
var windowsNodes = node.SelectSingleNode("windowsWithExposedEdges");
if (windowsNodes is not null)
{
var items = cnode.SelectNodes("Item");
if (items.Count > 0)
var items = windowsNodes.SelectNodes("Item");
if (items is not null && items.Count > 0)
{
windowsWithExposedEdges = new MetaHash[items.Count];
for (int i = 0; i < items.Count; i++)
@ -147,10 +146,10 @@ namespace CodeWalker.GameFiles
}
}
}
cnode = node.SelectSingleNode("plateProbabilities");
if (cnode != null)
var plateProbabilitiesNode = node.SelectSingleNode("plateProbabilities");
if (plateProbabilitiesNode is not null)
{
plateProbabilities = new PlateProbabilities(cnode);
plateProbabilities = new PlateProbabilities(plateProbabilitiesNode);
}
lightSettings = (byte)Xml.GetChildIntAttribute(node, "lightSettings", "value");
sirenSettings = (byte)Xml.GetChildIntAttribute(node, "sirenSettings", "value");
@ -168,28 +167,25 @@ namespace CodeWalker.GameFiles
public CVehicleModelInfoVariation_2575850962(XmlNode node)
{
XmlNode cnode;
cnode = node.SelectSingleNode("indices");
if (cnode != null)
var indicesNode = node.SelectSingleNode("indices");
if (indicesNode is not null)
{
var astr = cnode.InnerText;
var arrr = astr.Split(new[] { '\n', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
var astr = indicesNode.InnerText;
var alist = new List<byte>();
foreach (var item in arrr)
foreach (var item in astr.EnumerateSplitAny(['\n', ' ', '\t']))
{
var titem = item.Trim();
byte v;
if (byte.TryParse(titem, out v))
if (byte.TryParse(titem, out var v))
{
alist.Add(v);
}
}
indices = alist.ToArray();
}
cnode = node.SelectSingleNode("liveries");
if (cnode != null)
var liveriesNode = node.SelectSingleNode("liveries");
if (liveriesNode is not null)
{
var items = cnode.SelectNodes("Item");
var items = liveriesNode.SelectNodes("Item");
if (items.Count > 0)
{
liveries = new bool[items.Count];
@ -200,14 +196,12 @@ namespace CodeWalker.GameFiles
}
else
{
var astr = cnode.InnerText;
var arrr = astr.Split(new[] { '\n', ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
var astr = liveriesNode.InnerText;
var alist = new List<bool>();
foreach (var item in arrr)
foreach (var item in astr.EnumerateSplitAny(['\n', ' ', '\t']))
{
var titem = item.Trim();
byte v;
if (byte.TryParse(titem, out v))
if (byte.TryParse(titem, out var v))
{
alist.Add(v > 0);
}
@ -254,7 +248,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Name.ToString() + ": " + Value.ToString();
return $"{Name}: {Value}";
}
}

View File

@ -9,6 +9,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using Collections.Pooled;
namespace CodeWalker.GameFiles
{
@ -33,12 +34,10 @@ namespace CodeWalker.GameFiles
{
FileEntry = entry;
MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms))
if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(ms);
Pso.Load(data);
var xml = PsoXml.GetXml(Pso);
XmlDocument doc = new XmlDocument();
@ -48,10 +47,6 @@ namespace CodeWalker.GameFiles
CutsceneFile2 = new CutsceneFile2();
CutsceneFile2.ReadXml(node);
}
else
{
}
}
@ -228,7 +223,7 @@ namespace CodeWalker.GameFiles
public static CutBase ConstructObject(string type)
public static CutBase? ConstructObject(string type)
{
switch (type)
{
@ -279,7 +274,7 @@ namespace CodeWalker.GameFiles
default: return null;
}
}
public static T ReadObject<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
public static T? ReadObject<T>(XmlNode node, string name) where T : IMetaXmlItem, new()
{
var onode = node.SelectSingleNode(name);
if (onode != null)
@ -288,7 +283,7 @@ namespace CodeWalker.GameFiles
o.ReadXml(onode);
return o;
}
return default(T);
return default;
}
public static object[] ReadObjectArray(XmlNode node, string name)
{
@ -298,7 +293,7 @@ namespace CodeWalker.GameFiles
var inodes = aNode.SelectNodes("Item");
if (inodes?.Count > 0)
{
var oList = new List<object>();
using var oList = new PooledList<object>();
foreach (XmlNode inode in inodes)
{
var type = Xml.GetStringAttribute(inode, "type");

View File

@ -1,24 +1,76 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles
{
public class DlcContentFile
{
public List<DlcContentDataFile> dataFiles { get; set; } = new List<DlcContentDataFile>();
public List<DlcContentChangeSet> contentChangeSets { get; set; } = new List<DlcContentChangeSet>();
public RpfFile DlcFile { get; set; } //used by GameFileCache
public Dictionary<string, DlcExtraFolderMountFile> ExtraMounts { get; set; } = new Dictionary<string, DlcExtraFolderMountFile>();
public Dictionary<string, DlcContentDataFile> RpfDataFiles { get; set; } = new Dictionary<string, DlcContentDataFile>();
public Dictionary<string, DlcExtraFolderMountFile> ExtraMounts { get; set; } = new Dictionary<string, DlcExtraFolderMountFile>(StringComparer.OrdinalIgnoreCase);
public Dictionary<string, DlcContentDataFile> RpfDataFiles { get; set; } = new Dictionary<string, DlcContentDataFile>(StringComparer.OrdinalIgnoreCase);
public DlcExtraTitleUpdateFile ExtraTitleUpdates { get; set; }
public void Load(XmlReader reader)
{
reader.MoveToStartElement("CDataFileMgr__ContentsOfDataFileXml");
while (reader.Read())
{
switch (reader.MoveToContent())
{
case XmlNodeType.Element:
ReadElement(reader);
break;
case XmlNodeType.EndElement:
if (reader.Name == "CDataFileMgr__ContentsOfDataFileXml")
{
//Reached end of file
return;
}
break;
default:
break;
}
}
}
public void ReadElement(XmlReader reader)
{
switch(reader.Name)
{
case "disabledFiles":
case "includedXmlFiles":
case "includedDataFiles":
case "patchFiles":
reader.Skip();
return;
case "dataFiles":
foreach(var item in reader.IterateItems("dataFiles"))
{
dataFiles.Add(new DlcContentDataFile(item));
}
return;
case "contentChangeSets":
foreach(var item in reader.IterateItems("contentChangeSets"))
{
contentChangeSets.Add(new DlcContentChangeSet(item));
}
return;
}
}
public void Load(XmlDocument doc)
{
@ -32,16 +84,16 @@ namespace CodeWalker.GameFiles
switch (node.Name)
{
case "disabledFiles":
foreach (XmlNode disabledFile in node.ChildNodes)
{ } //nothing to see here..
//foreach (XmlNode disabledFile in node.ChildNodes)
//{ } //nothing to see here..
break;
case "includedXmlFiles":
foreach (XmlNode includedXmlFile in node.ChildNodes)
{ } //nothing to see here..
//foreach (XmlNode includedXmlFile in node.ChildNodes)
//{ } //nothing to see here..
break;
case "includedDataFiles":
foreach (XmlNode includedDataFile in node.ChildNodes)
{ } //nothing to see here..
//foreach (XmlNode includedDataFile in node.ChildNodes)
//{ } //nothing to see here..
break;
case "dataFiles":
foreach (XmlNode dataFile in node.ChildNodes)
@ -62,8 +114,8 @@ namespace CodeWalker.GameFiles
}
break;
case "patchFiles":
foreach (XmlNode patchFile in node.ChildNodes)
{ } //nothing to see here..
//foreach (XmlNode patchFile in node.ChildNodes)
//{ } //nothing to see here..
break;
default:
break;
@ -82,21 +134,21 @@ namespace CodeWalker.GameFiles
foreach (var datafile in dataFiles)
{
string dfn = GameFileCache.GetDlcPlatformPath(datafile.filename).ToLowerInvariant();
string dlcPlatformPath = GameFileCache.GetDlcPlatformPath(datafile.filename);
if (datafile.fileType == "EXTRA_FOLDER_MOUNT_DATA")
{
string efmdxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
string efmdxmlpath = datafile.filename.Replace($"{setupfile.deviceName}:", DlcFile.Path).Replace('/', '\\');
efmdxmlpath = gfc.GetDlcPatchedPath(efmdxmlpath);
XmlDocument efmdxml = rpfman.GetFileXml(efmdxmlpath);
DlcExtraFolderMountFile efmf = new DlcExtraFolderMountFile();
efmf.Load(efmdxml);
ExtraMounts[dfn] = efmf;
ExtraMounts[dlcPlatformPath] = efmf;
}
if (datafile.fileType == "EXTRA_TITLE_UPDATE_DATA")
{
string etudxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
string etudxmlpath = datafile.filename.Replace($"{setupfile.deviceName}:", DlcFile.Path).Replace('/', '\\');
etudxmlpath = gfc.GetDlcPatchedPath(etudxmlpath);
XmlDocument etudxml = rpfman.GetFileXml(etudxmlpath);
@ -107,7 +159,7 @@ namespace CodeWalker.GameFiles
}
if (datafile.fileType == "RPF_FILE")
{
RpfDataFiles[dfn] = datafile;
RpfDataFiles[dlcPlatformPath] = datafile;
}
}
@ -115,7 +167,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return dataFiles.Count.ToString() + " dataFiles, " + contentChangeSets.Count.ToString() + " contentChangeSets";
return $"{dataFiles.Count} dataFiles, {contentChangeSets.Count} contentChangeSets";
}
}
@ -135,6 +187,52 @@ namespace CodeWalker.GameFiles
{
Load(node);
}
public DlcContentDataFile(XElement xElement)
{
Load(xElement);
}
public void Load(XElement xElement)
{
foreach(var child in xElement.Elements())
{
switch(child.Name.LocalName)
{
case "filename":
filename = child.Value;
break;
case "fileType":
fileType = child.Value;
break;
case "contents":
contents = child.Value;
break;
case "installPartition":
installPartition = child.Value;
break;
case "overlay":
overlay = child.GetBoolAttribute("value");
break;
case "disabled":
disabled = child.GetBoolAttribute("value");
break;
case "persistent":
persistent = child.GetBoolAttribute("value");
break;
case "loadCompletely":
loadCompletely = child.GetBoolAttribute("value");
break;
case "locked":
locked = child.GetBoolAttribute("value");
break;
default:
break;
}
}
}
public void Load(XmlNode node)
{
foreach (XmlNode child in node.ChildNodes)
@ -176,36 +274,98 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return filename + ": " + fileType + ": " + contents + ": " + installPartition +
(overlay ? ", overlay" : "") +
(disabled ? ", disabled" : "") +
(persistent ? ", persistent" : "") +
(loadCompletely ? ", loadCompletely" : "") +
(locked ? ", locked" : "");
return $"{filename}: {fileType}: {contents}: {installPartition}{(overlay ? ", overlay" : "")}{(disabled ? ", disabled" : "")}{(persistent ? ", persistent" : "")}{(loadCompletely ? ", loadCompletely" : "")}{(locked ? ", locked" : "")}";
}
}
public class DlcContentChangeSet
{
public string changeSetName { get; set; }
public List<string> filesToInvalidate { get; set; }
public List<string> filesToDisable { get; set; }
public List<string> filesToEnable { get; set; }
public List<string> txdToLoad { get; set; }
public List<string> txdToUnload { get; set; }
public List<string> residentResources { get; set; }
public List<string> unregisterResources { get; set; }
public List<DlcContentChangeSet> mapChangeSetData { get; set; }
public List<string>? filesToInvalidate { get; set; }
public List<string>? filesToDisable { get; set; }
public List<string>? filesToEnable { get; set; }
public List<string>? txdToLoad { get; set; }
public List<string>? txdToUnload { get; set; }
public List<string>? residentResources { get; set; }
public List<string>? unregisterResources { get; set; }
public List<DlcContentChangeSet>? mapChangeSetData { get; set; }
public string associatedMap { get; set; }
public bool requiresLoadingScreen { get; set; }
public string loadingScreenContext { get; set; }
public bool useCacheLoader { get; set; }
public DlcContentChangeSetExecutionConditions executionConditions { get; set; }
public DlcContentChangeSet(XElement element)
{
Load(element);
}
public DlcContentChangeSet(XmlNode node)
{
Load(node);
}
public void Load(XElement element)
{
foreach(var child in element.Elements())
{
switch (child.Name.LocalName)
{
case "changeSetName":
changeSetName = child.Value;
break;
case "filesToInvalidate":
filesToInvalidate = GetChildStringArray(child);
break;
case "filesToDisable":
filesToDisable = GetChildStringArray(child);
break;
case "filesToEnable":
filesToEnable = GetChildStringArray(child);
break;
case "txdToLoad":
txdToLoad = GetChildStringArray(child);
break;
case "txdToUnload":
txdToUnload = GetChildStringArray(child);
break;
case "residentResources":
residentResources = GetChildStringArray(child);
break;
case "unregisterResources":
unregisterResources = GetChildStringArray(child);
break;
case "mapChangeSetData":
if (child.HasElements)
{
mapChangeSetData = new List<DlcContentChangeSet>();
foreach (XElement childElement in child.Elements())
{
mapChangeSetData.Add(new DlcContentChangeSet(childElement));
}
}
break;
case "associatedMap":
associatedMap = child.Value;
break;
case "requiresLoadingScreen":
requiresLoadingScreen = Xml.GetBoolAttribute(child, "value");
break;
case "loadingScreenContext":
loadingScreenContext = child.Value;
break;
case "useCacheLoader":
useCacheLoader = Xml.GetBoolAttribute(child, "value");
break;
case "executionConditions":
executionConditions = new DlcContentChangeSetExecutionConditions(child);
break;
default:
break;
}
}
}
public void Load(XmlNode node)
{
foreach (XmlNode child in node.ChildNodes)
@ -217,44 +377,33 @@ namespace CodeWalker.GameFiles
break;
case "filesToInvalidate":
filesToInvalidate = GetChildStringArray(child);
if (filesToInvalidate != null)
{ }
break;
case "filesToDisable":
filesToDisable = GetChildStringArray(child);
if (filesToDisable != null)
{ }
break;
case "filesToEnable":
filesToEnable = GetChildStringArray(child);
if (filesToEnable != null)
{ }
break;
case "txdToLoad":
txdToLoad = GetChildStringArray(child);
if (txdToLoad != null)
{ }
break;
case "txdToUnload":
txdToUnload = GetChildStringArray(child);
if (txdToUnload != null)
{ }
break;
case "residentResources":
residentResources = GetChildStringArray(child);
if (residentResources != null)
{ }
break;
case "unregisterResources":
unregisterResources = GetChildStringArray(child);
if (unregisterResources != null)
{ }
break;
case "mapChangeSetData":
mapChangeSetData = new List<DlcContentChangeSet>();
foreach (XmlNode mapChangeSetDataItem in child.ChildNodes)
if (child.HasChildNodes)
{
mapChangeSetData.Add(new DlcContentChangeSet(mapChangeSetDataItem));
mapChangeSetData = new List<DlcContentChangeSet>();
foreach (XmlNode mapChangeSetDataItem in child.ChildNodes)
{
mapChangeSetData.Add(new DlcContentChangeSet(mapChangeSetDataItem));
}
}
break;
case "associatedMap":
@ -292,6 +441,20 @@ namespace CodeWalker.GameFiles
return result;
}
private List<string>? GetChildStringArray(XElement node)
{
if (!node.HasElements) return null;
var result = new List<string>();
foreach(XElement childNode in node.Elements())
{
result.Add(childNode.Value);
}
return result;
}
public override string ToString()
{
return (changeSetName != null) ? changeSetName : (associatedMap != null) ? associatedMap : null;
@ -308,6 +471,30 @@ namespace CodeWalker.GameFiles
{
Load(node);
}
public DlcContentChangeSetExecutionConditions(XElement element)
{
Load(element);
}
public void Load(XElement element)
{
foreach (XElement child in element.Elements())
{
switch (child.Name.LocalName)
{
case "activeChangesetConditions":
activeChangesetConditions = child.Value;
break;
case "genericConditions":
genericConditions = child.Value;
break;
default:
break;
}
}
}
public void Load(XmlNode node)
{
foreach (XmlNode child in node.ChildNodes)

View File

@ -53,7 +53,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return deviceName + ", " + datFile + ", " + nameHash + ", " + type + ", " + order.ToString() + ", " + ((contentChangeSetGroups != null) ? contentChangeSetGroups.Count.ToString() : "0") + " groups, " + timeStamp;
return $"{deviceName}, {datFile}, {nameHash}, {type}, {order}, {contentChangeSetGroups?.Count ?? 0} groups, {timeStamp}";
}
}
@ -77,7 +77,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return NameHash + " (" + ((ContentChangeSets != null) ? ContentChangeSets.Count.ToString() : "0") + " changesets)";
return $"{NameHash} ({ContentChangeSets?.Count ?? 0} changesets)";
}
}
}

View File

@ -683,7 +683,8 @@ namespace CodeWalker.GameFiles
public static string ReadString(BinaryReader br)
{
byte sl = br.ReadByte();
if (sl == 0) return string.Empty;
if (sl == 0)
return string.Empty;
byte[] ba = br.ReadBytes(sl);
return (sl > 1) ? Encoding.ASCII.GetString(ba, 0, sl - 1) : string.Empty;
}
@ -1073,8 +1074,10 @@ namespace CodeWalker.GameFiles
var filepath = Path.Combine(csofolder, fname);
File.WriteAllBytes(filepath, ByteCode);
}
catch
{ }
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
@ -1840,7 +1843,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Type.ToString() + ", " + Value.ToString();
return $"{Type}, {Value}";
}
}
@ -1856,18 +1859,25 @@ namespace CodeWalker.GameFiles
public static string GetXml(FxcFile fxc, string outputFolder = "")
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if ((fxc != null) && (fxc.Shaders != null))
StringBuilder sb = StringBuilderPool.Get();
try
{
var name = "Effects";
OpenTag(sb, 0, name);
fxc.WriteXml(sb, 1, outputFolder);
CloseTag(sb, 0, name);
}
sb.AppendLine(XmlHeader);
return sb.ToString();
if ((fxc != null) && (fxc.Shaders != null))
{
var name = "Effects";
OpenTag(sb, 0, name);
fxc.WriteXml(sb, 1, outputFolder);
CloseTag(sb, 0, name);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
}
}

View File

@ -1,4 +1,7 @@
using System;
using Collections.Pooled;
using CommunityToolkit.HighPerformance;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@ -9,16 +12,12 @@ using System.Xml;
namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class GtxdFile : GameFile, PackedFile
[TypeConverter(typeof(ExpandableObjectConverter))]
public class GtxdFile : GameFile, PackedFile
{
public RbfFile Rbf { get; set; }
public Dictionary<string, string> TxdRelationships { get; set; }
public PooledDictionary<string, string> TxdRelationships { get; set; }
public GtxdFile() : base(null, GameFileType.Gtxd)
{
@ -36,13 +35,15 @@ namespace CodeWalker.GameFiles
FilePath = Name;
if (entry.Name.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
if (entry.IsExtension(".ymt"))
{
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
if (RbfFile.IsRBF(data.AsSpan(0, 4)))
{
//using MemoryStream ms = new MemoryStream(data);
var sequence = new ReadOnlySequence<byte>(data);
var reader = new SequenceReader<byte>(sequence);
Rbf = new RbfFile();
var rbfstruct = Rbf.Load(ms);
var rbfstruct = Rbf.Load(ref reader);
if (rbfstruct.Name == "CMapParentTxds")
{
@ -52,12 +53,8 @@ namespace CodeWalker.GameFiles
Loaded = true;
return;
}
else
{
//not an RBF file...
}
}
else if (entry.Name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase))
else if (entry.IsExtension(".meta"))
{
//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);
@ -73,30 +70,31 @@ namespace CodeWalker.GameFiles
private void LoadTxdRelationships(RbfStructure rbfstruct)
{
TxdRelationships = new Dictionary<string, string>();
TxdRelationships?.Clear();
if (rbfstruct.Children is null)
return;
TxdRelationships ??= new PooledDictionary<string, string>();
//StringBuilder sblist = new StringBuilder();
foreach (var child in rbfstruct.Children)
foreach (var child in rbfstruct.Children.Span)
{
var childstruct = child as RbfStructure;
if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
if (child is RbfStructure childstruct && childstruct.Name == "txdRelationships" && childstruct.Children is not null)
{
TxdRelationships.EnsureCapacity(TxdRelationships.Count + childstruct.Children.Count);
foreach (var txdrel in childstruct.Children)
{
var txdrelstruct = txdrel as RbfStructure;
if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
if (txdrel is RbfStructure txdrelstruct && txdrelstruct.Name == "item" && txdrelstruct.Children is not null)
{
string parentstr = string.Empty;
string childstr = string.Empty;
foreach (var item in txdrelstruct.Children)
{
var itemstruct = item as RbfStructure;
if ((itemstruct != null))
if (item is RbfStructure itemstruct)
{
var strbytes = itemstruct.Children[0] as RbfBytes;
string thisstr = string.Empty;
if (strbytes != null)
if (itemstruct.Children is not null && itemstruct.Children.Count > 0 && itemstruct.Children[0] is RbfBytes strbytes)
{
thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
thisstr = strbytes.GetNullTerminatedString();
}
switch (item.Name)
{
@ -108,17 +106,10 @@ namespace CodeWalker.GameFiles
break;
}
}
}
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
if (!string.IsNullOrEmpty(parentstr) && !string.IsNullOrEmpty(childstr))
{
if (!TxdRelationships.ContainsKey(childstr))
{
TxdRelationships.Add(childstr, parentstr);
}
else
{
}
_ = TxdRelationships.TryAdd(childstr, parentstr);
//sblist.AppendLine(childstr + ": " + parentstr);
}
}
@ -140,21 +131,26 @@ namespace CodeWalker.GameFiles
xmldoc.LoadXml(xml); //maybe better load xml.ToLower() and use "cmapparenttxds/txdrelationships/item" as xpath?
XmlNodeList items = xmldoc.SelectNodes("CMapParentTxds/txdRelationships/Item | CMapParentTxds/txdRelationships/item");
TxdRelationships = new Dictionary<string, string>();
TxdRelationships = new PooledDictionary<string, string>(items.Count);
for (int i = 0; i < items.Count; i++)
{
string parentstr = Xml.GetChildInnerText(items[i], "parent");
string childstr = Xml.GetChildInnerText(items[i], "child");
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
if (!string.IsNullOrEmpty(parentstr) && !string.IsNullOrEmpty(childstr))
{
if (!TxdRelationships.ContainsKey(childstr))
{
TxdRelationships.Add(childstr, parentstr);
}
_ = TxdRelationships.TryAdd(childstr, parentstr);
}
}
}
public override void Dispose()
{
TxdRelationships?.Dispose();
Rbf?.Dispose();
GC.SuppressFinalize(this);
base.Dispose();
}
}
}

View File

@ -1,8 +1,11 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using CodeWalker.Core.Utils;
using System.IO;
using System.Linq;
using System.Text;
@ -26,52 +29,45 @@ namespace CodeWalker.GameFiles
FileEntry = entry;
//Dict = new Dictionary<uint, string>();
using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
var sequence = new ReadOnlySequence<byte>(data);
var reader = new SequenceReader<byte>(sequence);
reader.TryReadLittleEndian(out uint gxt2);
if (gxt2 != 1196971058)
{
uint gxt2 = br.ReadUInt32(); //"GXT2" - 1196971058
if (gxt2 != 1196971058)
{ return; }
return;
}
EntryCount = br.ReadUInt32();
TextEntries = new Gxt2Entry[EntryCount];
for (uint i = 0; i < EntryCount; i++)
{
var e = new Gxt2Entry();
e.Hash = br.ReadUInt32();
e.Offset = br.ReadUInt32();
TextEntries[i] = e;
}
reader.TryReadLittleEndian(out uint entryCount);
EntryCount = entryCount;
TextEntries = new Gxt2Entry[EntryCount];
for (uint i = 0; i < EntryCount; i++)
{
reader.TryReadLittleEndian(out uint hash);
reader.TryReadLittleEndian(out uint offset);
TextEntries[i] = new Gxt2Entry(hash, string.Empty, offset);
}
gxt2 = br.ReadUInt32(); //another "GXT2"
if (gxt2 != 1196971058)
{ return; }
reader.TryReadLittleEndian(out gxt2); //another "GXT2"
if (gxt2 != 1196971058)
{
return;
}
uint endpos = br.ReadUInt32();
List<byte> buf = new List<byte>();
for (uint i = 0; i < EntryCount; i++)
{
var e = TextEntries[i];
br.BaseStream.Position = e.Offset;
buf.Clear();
byte b = br.ReadByte();
while ((b != 0) && (br.BaseStream.Position<endpos))
{
buf.Add(b);
b = br.ReadByte();
}
e.Text = Encoding.UTF8.GetString(buf.ToArray());
//Dict[e.Hash] = e.Text;
}
reader.TryReadLittleEndian(out uint endpos);
for (uint i = 0; i < EntryCount; i++)
{
ref var e = ref TextEntries[i];
var strReader = new SequenceReader<byte>(new ReadOnlySequence<byte>(data, (int)e.Offset, data.Length - (int)e.Offset));
strReader.TryReadTo(out ReadOnlySpan<byte> str, 0);
e = new Gxt2Entry(e.Hash, Encoding.UTF8.GetString(str), e.Offset);
}
}
public byte[] Save()
{
if (TextEntries == null) TextEntries = new Gxt2Entry[0];
if (TextEntries == null)
TextEntries = [];
EntryCount = (uint)TextEntries.Length;
uint offset = 16 + (EntryCount * 8);
List<byte[]> datas = new List<byte[]>();
@ -81,10 +77,10 @@ namespace CodeWalker.GameFiles
bw.Write(1196971058); //"GXT2"
bw.Write(EntryCount);
foreach (var e in TextEntries)
foreach (ref var e in TextEntries.AsSpan())
{
e.Offset = offset;
var d = Encoding.UTF8.GetBytes(e.Text + "\0");
e = new Gxt2Entry(e.Hash, e.Text, offset);
var d = Encoding.UTF8.GetBytes($"{e.Text}\0");
datas.Add(d);
offset += (uint)d.Length;
bw.Write(e.Hash);
@ -113,11 +109,7 @@ namespace CodeWalker.GameFiles
{
foreach (var entry in TextEntries)
{
sb.Append("0x");
sb.Append(entry.Hash.ToString("X").PadLeft(8, '0'));
sb.Append(" = ");
sb.Append(entry.Text);
sb.AppendLine();
sb.AppendLine($"0x{entry.Hash:X8} = {entry.Text}");
}
}
return sb.ToString();
@ -125,17 +117,15 @@ namespace CodeWalker.GameFiles
public static Gxt2File FromText(string text)
{
var gxt = new Gxt2File();
var lines = text?.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
var entries = new List<Gxt2Entry>();
foreach (var line in lines)
foreach (var line in text.EnumerateSplit('\n'))
{
var tline = line.Trim();
if (tline.Length < 13) continue;
if (uint.TryParse(tline.Substring(2, 8), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint hash))
if (tline.Length < 13)
continue;
if (uint.TryParse(tline.Slice(2, 8), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint hash))
{
var entry = new Gxt2Entry();
entry.Hash = hash;
entry.Text = (tline.Length > 13) ? tline.Substring(13) : "";
var entry = new Gxt2Entry(hash, (tline.Length > 13) ? tline.Slice(13).ToString() : "");
entries.Add(entry);
}
else
@ -152,15 +142,34 @@ namespace CodeWalker.GameFiles
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class Gxt2Entry
[TypeConverter(typeof(ExpandableObjectConverter))]
public readonly struct Gxt2Entry
{
public uint Hash { get; set; }
public uint Offset { get; set; }
public string Text { get; set; }
public uint Hash { get; init; }
public uint Offset { get; init; }
public string Text { get; init; }
public override string ToString()
public Gxt2Entry()
{
return Convert.ToString(Hash, 16).ToUpper().PadLeft(8, '0') + ": " + Text;
}
public Gxt2Entry(uint hash, string text)
{
Hash = hash;
Text = text;
}
public Gxt2Entry(uint hash, string text, uint offset)
{
Hash = hash;
Text = text;
Offset = offset;
}
public override readonly string ToString()
{
return $"{Hash:X8}: {Text}";
}
}
@ -190,10 +199,11 @@ namespace CodeWalker.GameFiles
return !Index.TryAdd(hash, str);
}
public static bool Ensure(string str, uint hash)
public static void Ensure(string str, uint hash)
{
if (hash == 0) return true;
return !Index.TryAdd(hash, str);
if (hash == 0)
return;
_ = Index.TryAdd(hash, str);
}
public static string GetString(uint hash)
@ -215,6 +225,8 @@ namespace CodeWalker.GameFiles
return res;
}
public static bool TryGetString(uint hash, out string res) => Index.TryGetValue(hash, out res);
public static uint TryFindHash(string text)
{
lock (syncRoot)

View File

@ -99,7 +99,7 @@ namespace CodeWalker.GameFiles
CompHeaders = new CompHeader[Height];
for (int i = 0; i < Height; i++)
{
CompHeaders[i].Read(r);
CompHeaders[i] = new CompHeader(r);
}
dlen -= (Height * 8);
}
@ -173,8 +173,7 @@ namespace CodeWalker.GameFiles
d1.Add(MaxHeights[n]);
d2.Add(MinHeights[n]);
}
var h = new CompHeader() { Start = (ushort)start, Count = (ushort)count, DataOffset = offset };
ch[y] = h;
ch[y] = new CompHeader((ushort)start, (ushort)count, offset);
}
d1.AddRange(d2);//the 2 sets of compressed data are just smushed together
d = d1.ToArray();
@ -243,7 +242,7 @@ namespace CodeWalker.GameFiles
private byte[] InvertImage(byte[] i, int w, int h)
private static byte[] InvertImage(byte[] i, int w, int h)
{
//inverts the image vertically
byte[] o = new byte[i.Length];
@ -262,7 +261,8 @@ namespace CodeWalker.GameFiles
public string GetPGM()
{
if (MaxHeights == null) return string.Empty;
if (MaxHeights == null)
return string.Empty;
var sb = new StringBuilder();
sb.AppendFormat("P2\n{0} {1}\n255\n", Width, Height);
@ -272,10 +272,10 @@ namespace CodeWalker.GameFiles
for (int x = 0; x < Width; x++)
{
var h = MaxHeights[y * Width + x];
sb.Append(h.ToString());
sb.Append(" ");
sb.Append(h);
sb.Append(' ');
}
sb.Append("\n");
sb.Append('\n');
}
return sb.ToString();
@ -283,18 +283,20 @@ namespace CodeWalker.GameFiles
public struct CompHeader
public readonly struct CompHeader(ushort start, ushort count, int dataOffset)
{
public ushort Start;
public ushort Count;
public int DataOffset;
public readonly ushort Start = start;
public readonly ushort Count = count;
public readonly int DataOffset = dataOffset;
public void Read(DataReader r)
public CompHeader(DataReader r) : this(r.ReadUInt16(), r.ReadUInt16(), r.ReadInt32())
{ }
public static CompHeader Read(DataReader r)
{
Start = r.ReadUInt16();
Count = r.ReadUInt16();
DataOffset = r.ReadInt32();
return new CompHeader(r);
}
public void Write(DataWriter w)
{
w.Write(Start);
@ -304,7 +306,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return Start.ToString() + ", " + Count.ToString() + ", " + DataOffset.ToString();
return $"{Start}, {Count}, {DataOffset}";
}
}
@ -316,24 +318,29 @@ namespace CodeWalker.GameFiles
public static string GetXml(HeightmapFile hmf)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if ((hmf != null) && (hmf.MaxHeights != null))
StringBuilder sb = StringBuilderPool.Get();
try
{
var name = "Heightmap";
sb.AppendLine(XmlHeader);
OpenTag(sb, 0, name);
if ((hmf != null) && (hmf.MaxHeights != null))
{
var name = "Heightmap";
hmf.WriteXml(sb, 1);
OpenTag(sb, 0, name);
CloseTag(sb, 0, name);
hmf.WriteXml(sb, 1);
CloseTag(sb, 0, name);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
return sb.ToString();
}
}

View File

@ -29,9 +29,6 @@ namespace CodeWalker.GameFiles
//PsoTypes.EnsurePsoTypes(Pso);
var root = PsoTypes.GetRootEntry(Pso);
if (root != null)
{
}
return;
}
else

View File

@ -5384,7 +5384,7 @@ namespace CodeWalker.GameFiles
public static void WriteNodeRef(StringBuilder sb, int indent, string name, MrfNode node)
{
Indent(sb, indent);
sb.Append("<");
sb.Append('<');
sb.Append(name);
sb.Append(" ref=\"");
sb.Append(HashString(node.Name));

View File

@ -8,6 +8,8 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Buffers.Binary;
using System.Buffers;
namespace CodeWalker.GameFiles
{
@ -18,6 +20,8 @@ namespace CodeWalker.GameFiles
public RbfFile Rbf { get; set; }
public string Xml { get; set; }
public MetaHash DlcName => VariationInfo.Data.dlcName;
public string GameDlcName { get; set; }
public MCPedVariationInfo VariationInfo { get; set; }
@ -39,8 +43,7 @@ namespace CodeWalker.GameFiles
FilePath = Name;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
NonMetaLoad(data);
Loaded = true;
@ -66,10 +69,10 @@ namespace CodeWalker.GameFiles
{
var vVariationInfo = MetaTypes.GetTypedData<CPedVariationInfo>(Meta, MetaName.CPedVariationInfo);
VariationInfo = new MCPedVariationInfo();
VariationInfo.Load(Meta, vVariationInfo);
VariationInfo.Load(Meta, in vVariationInfo);
Strings = MetaTypes.GetStrings(Meta);
if (Strings != null)
if (Strings is not null)
{
foreach (string str in Strings)
{
@ -82,7 +85,7 @@ namespace CodeWalker.GameFiles
var vVariationInfo = PsoTypes.GetRootItem<CPedVariationInfo>(Pso);
VariationInfo = new MCPedVariationInfo();
VariationInfo.Load(Pso, vVariationInfo);
VariationInfo.Load(Pso, in vVariationInfo);
}
@ -90,18 +93,24 @@ namespace CodeWalker.GameFiles
private void NonMetaLoad(byte[] data)
{
//non meta not supported yet! but see what's in there...
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
if (RbfFile.IsRBF(data.AsSpan(0, 4)))
{
Rbf = new RbfFile();
Rbf.Load(ms);
var sequence = new ReadOnlySequence<byte>(data);
var reader = new SequenceReader<byte>(sequence);
Rbf.Load(ref reader);
}
else if (PsoFile.IsPSO(ms))
else if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(ms);
Pso.Load(data);
LoadPso();
}
}
public override string ToString()
{
return Path.GetFileName(RpfFileEntry.Parent.Path);
}
}
}

View File

@ -0,0 +1,112 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml;
using System.IO;
namespace CodeWalker.Core.GameFiles.FileTypes
{
[TC(typeof(EXP))]
public class PedShopMetaFile : GameFile, PackedFile
{
public static XmlNameTableThreadSafe nameTable = new XmlNameTableThreadSafe();
public string PedName { get; set; }
public string DlcName { get; set; }
public string FullDlcName { get; set; }
public string Character { get; set; }
public string CreateMetaData { get; set; }
public PedShopMetaFile() : base(null, GameFileType.PedShopMeta)
{ }
public PedShopMetaFile(RpfFileEntry entry) : base(entry, GameFileType.PedShopMeta)
{ }
public void Load(byte[] data, RpfFileEntry entry)
{
var xml = TextUtil.GetUTF8Text(data);
using var xmlReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { NameTable = nameTable });
Load(xmlReader);
}
public void Load(XmlReader reader)
{
reader.MoveToStartElement("ShopPedApparel");
var startDepth = reader.Depth;
reader.ReadStartElement("ShopPedApparel");
while (reader.Read() && startDepth < reader.Depth)
{
switch(reader.MoveToContent())
{
case XmlNodeType.Element:
LoadElement(reader);
break;
case XmlNodeType.EndElement:
if (reader.Name == "ShopPedApparel")
{
return;
}
break;
}
}
}
public void LoadElement(XmlReader reader)
{
switch(reader.Name)
{
case "pedName":
PedName = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(PedName))
{
JenkIndex.EnsureBoth(PedName);
}
break;
case "dlcName":
DlcName = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(DlcName))
{
JenkIndex.EnsureBoth(DlcName);
}
break;
case "fullDlcName":
FullDlcName = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(FullDlcName))
{
JenkIndex.EnsureBoth(FullDlcName);
}
break;
case "eCharacter":
Character = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(Character))
{
JenkIndex.EnsureBoth(Character);
}
break;
case "createMetaData":
CreateMetaData = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(CreateMetaData))
{
JenkIndex.EnsureBoth(CreateMetaData);
}
break;
case "pedOutfits":
reader.Skip();
break;
case "pedComponents":
reader.Skip();
break;
case "pedProps":
reader.Skip();
break;
}
}
}
}

View File

@ -10,13 +10,14 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml.Linq;
using Collections.Pooled;
namespace CodeWalker.GameFiles
{
[TC(typeof(EXP))] public class PedsFile : GameFile, PackedFile
{
private static XmlNameTable cachedNameTable = new System.Xml.NameTable();
private static XmlNameTable cachedNameTable = new XmlNameTableThreadSafe();
public PsoFile Pso { get; set; }
public string Xml { get; set; }
@ -36,8 +37,8 @@ namespace CodeWalker.GameFiles
//can be PSO .ymt or XML .meta
using MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms))
//using MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(data);
@ -63,7 +64,7 @@ namespace CodeWalker.GameFiles
// }
//}
using var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { NameTable = cachedNameTable, });
using var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { NameTable = cachedNameTable });
//if (xdoc.DocumentElement != null)
@ -114,16 +115,18 @@ namespace CodeWalker.GameFiles
break;
}
reader.ReadStartElement();
var initDatasList = new List<CPedModelInfo__InitData>();
using (var initDatasList = new PooledList<CPedModelInfo__InitData>())
{
while (reader.IsItemElement())
{
initDatasList.Add(new CPedModelInfo__InitData(reader));
}
if (initDatasList.Count > 0)
{
InitDatas = initDatasList.ToArray();
}
}
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):
@ -133,17 +136,19 @@ namespace CodeWalker.GameFiles
break;
}
reader.ReadStartElement();
var txdRelationshipsList = new List<CTxdRelationship>();
using (var txdRelationshipsList = new PooledList<CTxdRelationship>())
{
while (reader.IsItemElement())
{
txdRelationshipsList.Add(new CTxdRelationship(reader));
}
reader.ReadEndElement();
if (txdRelationshipsList.Count > 0)
{
txdRelationships = txdRelationshipsList.ToArray();
}
}
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)
@ -152,16 +157,19 @@ namespace CodeWalker.GameFiles
break;
}
reader.ReadStartElement();
var multiTxdList = new List<CMultiTxdRelationship>();
while (reader.IsItemElement())
using (var multiTxdList = new PooledList<CMultiTxdRelationship>())
{
multiTxdList.Add(new CMultiTxdRelationship(reader));
}
reader.ReadEndElement();
if (multiTxdList.Count > 0)
{
multiTxdRelationships = multiTxdList.ToArray();
while (reader.IsItemElement())
{
multiTxdList.Add(new CMultiTxdRelationship(reader));
}
reader.ReadEndElement();
if (multiTxdList.Count > 0)
{
multiTxdRelationships = multiTxdList.ToArray();
}
}
break;
default:
break;
@ -334,13 +342,16 @@ namespace CodeWalker.GameFiles
MovementClipSet = Xml.GetChildInnerText(reader, "MovementClipSet");
break;
case "MovementClipSets":
var clipSetsList = new List<string>();
foreach(var item in Xml.IterateItems(reader, "MovementClipSets"))
using (var clipSetsList = new PooledList<string>())
{
clipSetsList.Add(item.Value);
foreach (var item in Xml.IterateItems(reader, "MovementClipSets"))
{
clipSetsList.Add(item.Value);
}
MovementClipSets = clipSetsList.ToArray();
}
MovementClipSets = clipSetsList.ToArray();
break;
case "StrafeClipSet":
StrafeClipSet = Xml.GetChildInnerText(reader, "StrafeClipSet");
@ -707,7 +718,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return parent + ": " + (children?.Length ?? 0).ToString() + " children";
return $"{parent}: {(children?.Length ?? 0)} children";
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,9 @@
using CodeWalker.World;
using CodeWalker.Core.Utils;
using CodeWalker.World;
using Collections.Pooled;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.HighPerformance;
using Microsoft.Extensions.ObjectPool;
using SharpDX;
using System;
using System.Collections;
@ -16,11 +21,11 @@ namespace CodeWalker.GameFiles
{
public class VehiclesFile : GameFile, PackedFile
{
private static XmlNameTable cachedNameTable = new NameTable();
private static XmlNameTableThreadSafe cachedNameTable = new XmlNameTableThreadSafe(256);
public string ResidentTxd { get; set; }
public List<VehicleInitData> InitDatas { get; set; }
public Dictionary<string, string> TxdRelationships { get; set; }
public PooledList<VehicleInitData> InitDatas { get; set; }
public PooledDictionary<string, string> TxdRelationships { get; set; }
@ -114,9 +119,14 @@ namespace CodeWalker.GameFiles
private void LoadInitDatas(XmlDocument xmldoc)
{
XmlNodeList items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/InitDatas/Item | CVehicleModelInfo__InitDataList/InitDatas/item");
XmlNodeList? items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/InitDatas/Item | CVehicleModelInfo__InitDataList/InitDatas/item");
InitDatas = new List<VehicleInitData>(items.Count);
if (items is null)
{
return;
}
InitDatas = new PooledList<VehicleInitData>(items.Count);
for (int i = 0; i < items.Count; i++)
{
var node = items[i];
@ -130,10 +140,11 @@ namespace CodeWalker.GameFiles
{
if (!reader.IsStartElement() || reader.Name != "InitDatas")
{
throw new InvalidOperationException("XmlReader is not at start element of \"InitDatas\"");
ThrowHelper.ThrowInvalidOperationException("XmlReader is not at start element of \"InitDatas\"");
return;
}
InitDatas = new List<VehicleInitData>();
InitDatas = new PooledList<VehicleInitData>();
reader.ReadStartElement("InitDatas");
@ -152,52 +163,58 @@ namespace CodeWalker.GameFiles
{
if (reader.IsEmptyElement)
{
TxdRelationships = new Dictionary<string, string>();
reader.ReadStartElement();
return;
}
TxdRelationships = new Dictionary<string, string>();
TxdRelationships ??= new PooledDictionary<string, string>();
TxdRelationships.Clear();
foreach(var item in Xml.IterateItems(reader, "txdRelationships"))
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 (!string.IsNullOrEmpty(parentstr) && !string.IsNullOrEmpty(childstr))
{
if (!TxdRelationships.ContainsKey(childstr))
{
TxdRelationships.Add(childstr, parentstr);
}
_ = TxdRelationships.TryAdd(childstr, parentstr);
}
}
}
private void LoadTxdRelationships(XmlDocument xmldoc)
{
XmlNodeList items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/txdRelationships/Item | CVehicleModelInfo__InitDataList/txdRelationships/item");
XmlNodeList? items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/txdRelationships/Item | CVehicleModelInfo__InitDataList/txdRelationships/item");
if (items is null || items.Count == 0)
{
TxdRelationships.Clear();
return;
}
TxdRelationships = new Dictionary<string, string>();
TxdRelationships ??= new PooledDictionary<string, string>();
TxdRelationships.Clear();
for (int i = 0; i < items.Count; i++)
{
string parentstr = Xml.GetChildInnerText(items[i], "parent");
string childstr = Xml.GetChildInnerText(items[i], "child");
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
if (!string.IsNullOrEmpty(parentstr) && !string.IsNullOrEmpty(childstr))
{
if (!TxdRelationships.ContainsKey(childstr))
{
TxdRelationships.Add(childstr, parentstr);
}
else
{ }
_ = TxdRelationships.TryAdd(childstr, parentstr);
}
}
}
public override void Dispose()
{
InitDatas?.Dispose();
TxdRelationships?.Dispose();
base.Dispose();
GC.SuppressFinalize(this);
}
}
public class VehicleInitData
public record VehicleInitData
{
public string modelName { get; set; } //<modelName>impaler3</modelName>
@ -455,7 +472,20 @@ namespace CodeWalker.GameFiles
damageOffsetScale = Xml.GetChildFloatAttribute(reader, "damageOffsetScale");
break;
case "diffuseTint":
diffuseTint = new Color4(Convert.ToUInt32(Xml.GetChildStringAttribute(reader, "diffuseTint", "value").Replace("0x", ""), 16)); ;
var str = Xml.GetChildStringAttribute(reader, "diffuseTint", "value").AsSpan();
if (str.StartsWith("0x"))
{
str = str.Slice(2);
}
if (str.IsEmpty)
{
diffuseTint = new Color4();
} else
{
diffuseTint = new Color4(uint.Parse(str, System.Globalization.NumberStyles.HexNumber));
}
break;
case "steerWheelMult":
steerWheelMult = Xml.GetChildFloatAttribute(reader, "steerWheelMult");
@ -530,20 +560,25 @@ namespace CodeWalker.GameFiles
break;
}
var _drivers = new List<VehicleDriver>();
foreach (var item in Xml.IterateItems(reader, "drivers"))
using (var _drivers = new PooledList<VehicleDriver>())
{
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))
foreach (var item in Xml.IterateItems(reader, "drivers"))
{
_drivers.Add(driver);
var driver = new VehicleDriver
{
DriverName = item.Element("driverName")?.Value ?? string.Empty,
NpcName = item.Element("npcName")?.Value ?? string.Empty,
};
if (!string.IsNullOrEmpty(driver.NpcName) || !string.IsNullOrEmpty(driver.DriverName))
{
_drivers.Add(driver);
}
}
drivers = _drivers.ToArray();
}
drivers = _drivers.ToArray();
break;
case "doorsWithCollisionWhenClosed":
doorsWithCollisionWhenClosed = GetStringItemArray(reader, "doorsWithCollisionWhenClosed");
@ -602,7 +637,8 @@ namespace CodeWalker.GameFiles
else if (reader.Name == "ThresholdMult")
{
pOverrideRagdollThreshold.ThresholdMult = Xml.GetChildFloatAttribute(reader, "ThresholdMult", "value");
} else
}
else
{
break;
}
@ -722,10 +758,11 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < items.Count; i++)
{
var item = items[i];
var driver = new VehicleDriver();
driver.driverName = Xml.GetChildInnerText(item, "driverName");
driver.npcName = Xml.GetChildInnerText(item, "npcName");
drivers[i] = driver;
drivers[i] = new VehicleDriver
{
DriverName = Xml.GetChildInnerText(item, "driverName"),
NpcName = Xml.GetChildInnerText(item, "npcName")
};
}
}
}
@ -760,26 +797,34 @@ namespace CodeWalker.GameFiles
firstPersonDrivebyData = GetStringItemArray(node, "firstPersonDrivebyData");
}
private string[] GetStringItemArray(XmlNode node, string childName)
private string[]? GetStringItemArray(XmlNode node, string childName)
{
var cnode = node.SelectSingleNode(childName);
if (cnode == null) return null;
if (cnode == null)
return null;
var items = cnode.SelectNodes("Item");
if (items == null) return null;
getStringArrayList.Clear();
foreach (XmlNode inode in items)
if (items == null)
return null;
lock(getStringArrayList)
{
var istr = inode.InnerText;
if (!string.IsNullOrEmpty(istr))
getStringArrayList.Clear();
foreach (XmlNode inode in items)
{
getStringArrayList.Add(istr);
var istr = inode.InnerText;
if (!string.IsNullOrEmpty(istr))
{
getStringArrayList.Add(istr);
}
}
if (getStringArrayList.Count == 0)
return null;
return getStringArrayList.ToArray();
}
if (getStringArrayList.Count == 0) return null;
return getStringArrayList.ToArray();
}
private string[] GetStringItemArray(XmlReader node, string childName)
private string[]? GetStringItemArray(XmlReader node, string childName)
{
if (node.IsEmptyElement)
{
@ -807,70 +852,79 @@ namespace CodeWalker.GameFiles
}
}
private string[] GetStringArray(string ldastr, char delimiter)
private string[]? GetStringArray(string ldastr, char delimiter)
{
var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null;
if (string.IsNullOrEmpty(ldastr))
{
return null;
}
lock(getStringArrayList)
{
getStringArrayList.Clear();
foreach (var ldstr in ldarr)
foreach (var ldstr in ldastr.EnumerateSplit(delimiter))
{
var ldt = ldstr?.Trim();
if (!string.IsNullOrEmpty(ldt))
var ldt = ldstr.Trim();
if (!ldt.IsEmpty)
{
getStringArrayList.Add(ldt);
getStringArrayList.Add(ldt.ToString());
}
}
if (getStringArrayList.Count == 0) return null;
if (getStringArrayList.Count == 0)
return null;
return getStringArrayList.ToArray();
}
}
private string[] GetStringArray(XmlNode node, string childName, char delimiter)
private string[]? GetStringArray(XmlNode node, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(node, childName);
return GetStringArray(ldastr, delimiter);
}
private string[] GetStringArray(XmlReader reader, string childName, char delimiter)
private string[]? GetStringArray(XmlReader reader, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(reader, childName);
return GetStringArray(ldastr, delimiter);
}
[SkipLocalsInit]
private float[] GetFloatArray(string ldastr, char delimiter)
private float[]? GetFloatArray(string ldastr, char delimiter)
{
var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null;
Span<float> floats = stackalloc float[ldarr.Length];
var i = 0;
foreach (var ldstr in ldarr)
if (string.IsNullOrEmpty(ldastr))
{
var ldt = ldstr?.Trim();
if (!string.IsNullOrEmpty(ldt) && FloatUtil.TryParse(ldt, out var f))
{
floats[i] = f;
i++;
}
return [];
}
if (i == 0) return null;
var ldarr = ldastr.Split(delimiter);
if (ldarr == null)
return [];
var result = new float[i];
lock (getFloatArrayList)
{
getFloatArrayList.Clear();
foreach (var ldstr in ldastr.EnumerateSplit(delimiter))
{
var ldt = ldstr.Trim();
if (!ldt.IsEmpty && FloatUtil.TryParse(ldt, out var f))
{
getFloatArrayList.Add(f);
}
}
floats.Slice(0, i).CopyTo(result);
if (getFloatArrayList.Count == 0)
{
return [];
}
return result;
return getFloatArrayList.ToArray();
}
}
private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
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)
private float[]? GetFloatArray(XmlReader reader, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(reader, childName);
return GetFloatArray(ldastr, delimiter);
@ -884,282 +938,28 @@ 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<VehicleOverrideRagdollThreshold>.Default.Equals(pOverrideRagdollThreshold, data.pOverrideRagdollThreshold) &&
StructuralComparisons.StructuralEqualityComparer.Equals(firstPersonDrivebyData, data.firstPersonDrivebyData);
}
public override int GetHashCode()
{
int hashCode = 1102137281;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(modelName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(txdName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(handlingId);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(gameName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(vehicleMakeName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(expressionDictName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(expressionName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(animConvRoofDictName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(animConvRoofName);
hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(animConvRoofWindowsAffected);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(ptfxAssetName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(audioNameHash);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(layout);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(coverBoundOffsets);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(explosionInfo);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(scenarioLayout);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(cameraName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(aimCameraName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(bonnetCameraName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.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<string>.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<string>.Default.GetHashCode(swankness);
hashCode = hashCode * -1521134295 + maxNum.GetHashCode();
hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(flags);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(type);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(plateType);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(dashboardType);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(vehicleClass);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.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<string>.Default.GetHashCode(NmBraceOverrideSet);
hashCode = hashCode * -1521134295 + buoyancySphereOffset.GetHashCode();
hashCode = hashCode * -1521134295 + buoyancySphereSizeScale.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<VehicleOverrideRagdollThreshold>.Default.GetHashCode(pOverrideRagdollThreshold);
hashCode = hashCode * -1521134295 + StructuralComparisons.StructuralEqualityComparer.GetHashCode(firstPersonDrivebyData);
return hashCode;
}
public static bool operator ==(VehicleInitData left, VehicleInitData right)
{
return EqualityComparer<VehicleInitData>.Default.Equals(left, right);
}
public static bool operator !=(VehicleInitData left, VehicleInitData right)
{
return !(left == right);
}
}
public class VehicleOverrideRagdollThreshold : IEquatable<VehicleOverrideRagdollThreshold>
public record VehicleOverrideRagdollThreshold : IEquatable<VehicleOverrideRagdollThreshold>
{
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);
return $"{MinComponent}, {MaxComponent}, {ThresholdMult}";
}
}
public class VehicleDriver : IEquatable<VehicleDriver>
public readonly record struct VehicleDriver : IEquatable<VehicleDriver>
{
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<string>.Default.GetHashCode(driverName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(npcName);
return hashCode;
}
public string DriverName { get; init; }
public string NpcName { get; init; }
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);
return $"{DriverName}, {NpcName}";
}
}

View File

@ -488,9 +488,9 @@ namespace CodeWalker.GameFiles
{
var h = GridWatermapInds[y * Width + x];
sb.Append(h.ToString());
sb.Append(" ");
sb.Append(' ');
}
sb.Append("\n");
sb.Append('\n');
}
return sb.ToString();

View File

@ -1,4 +1,5 @@
using System;
using CommunityToolkit.Diagnostics;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@ -45,10 +46,10 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -105,26 +106,34 @@ namespace CodeWalker.GameFiles
public static string GetXml(YbnFile ybn)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
var name = "BoundsFile";
OpenTag(sb, 0, name);
if (ybn?.Bounds != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
Bounds.WriteXmlNode(ybn.Bounds, sb, 1);
sb.AppendLine(XmlHeader);
var name = "BoundsFile";
OpenTag(sb, 0, name);
if (ybn?.Bounds != null)
{
Bounds.WriteXmlNode(ybn.Bounds, sb, 1);
}
CloseTag(sb, 0, name);
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
CloseTag(sb, 0, name);
return sb.ToString();
}
public static string FormatBoundMaterialColour(BoundMaterialColour c) //for use with WriteItemArray
{
return c.R.ToString() + ", " + c.G.ToString() + ", " + c.B.ToString() + ", " + c.A.ToString();
return $"{c.R}, {c.G}, {c.B}, {c.A}";
}
}

View File

@ -38,13 +38,13 @@ namespace CodeWalker.GameFiles
//Hash = entry.ShortNameHash;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
try
{
using ResourceDataReader rd = new ResourceDataReader(resentry, data);

View File

@ -31,16 +31,26 @@ namespace CodeWalker.GameFiles
Loaded = true;
}
public async Task LoadAsync(byte[] data)
{
//direct load from a raw, compressed ydd file
await RpfFile.LoadResourceFileAsync(this, data, 165);
Loaded = true;
}
public void Load(byte[] data, RpfFileEntry entry)
{
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -99,12 +109,8 @@ namespace CodeWalker.GameFiles
return data;
}
new public long MemoryUsage {
get {
return DrawableDict.MemoryUsage;
}
}
public long PhysicalMemoryUsage => DrawableDict.PhysicalMemoryUsage;
public long VirtualMemoryUsage => DrawableDict.VirtualMemoryUsage;
}
@ -112,18 +118,24 @@ namespace CodeWalker.GameFiles
public class YddXml : MetaXmlBase
{
public static string GetXml(YddFile ydd, string outputFolder = "")
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (ydd?.DrawableDict != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
DrawableDictionary.WriteXmlNode(ydd.DrawableDict, sb, 0, outputFolder);
}
sb.AppendLine(XmlHeader);
return sb.ToString();
if (ydd?.DrawableDict != null)
{
DrawableDictionary.WriteXmlNode(ydd.DrawableDict, sb, 0, outputFolder);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
}
}

View File

@ -27,16 +27,26 @@ namespace CodeWalker.GameFiles
Loaded = true;
}
public async Task LoadAsync(byte[] data)
{
//direct load from a raw, compressed ydr file
await RpfFile.LoadResourceFileAsync(this, data, 165);
Loaded = true;
}
public void Load(byte[] data, RpfFileEntry entry)
{
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -70,13 +80,8 @@ namespace CodeWalker.GameFiles
return data;
}
new public long MemoryUsage
{
get
{
return Drawable.MemoryUsage;
}
}
public long PhysicalMemoryUsage => Drawable.PhysicalMemoryUsage;
public long VirtualMemoryUsage => Drawable.VirtualMemoryUsage;
}
@ -84,20 +89,25 @@ namespace CodeWalker.GameFiles
public class YdrXml : MetaXmlBase
{
public static string GetXml(YdrFile ydr, string outputFolder = "")
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (ydr?.Drawable != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
Drawable.WriteXmlNode(ydr.Drawable, sb, 0, outputFolder);
sb.AppendLine(XmlHeader);
if (ydr?.Drawable != null)
{
Drawable.WriteXmlNode(ydr.Drawable, sb, 0, outputFolder);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
return sb.ToString();
}
}
public class XmlYdr

View File

@ -35,10 +35,10 @@ namespace CodeWalker.GameFiles
//Hash = entry.ShortNameHash;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
try

View File

@ -30,13 +30,13 @@ namespace CodeWalker.GameFiles
//Hash = entry.ShortNameHash;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
try
{
using var rd = new ResourceDataReader(resentry, data);

View File

@ -34,10 +34,10 @@ namespace CodeWalker.GameFiles
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -68,7 +68,8 @@ namespace CodeWalker.GameFiles
return data;
}
public long PhysicalMemoryUsage => Fragment.Drawable.PhysicalMemoryUsage;
public long VirtualMemoryUsage => Fragment.Drawable.VirtualMemoryUsage;
}
@ -80,17 +81,23 @@ namespace CodeWalker.GameFiles
public static string GetXml(YftFile yft, string outputFolder = "")
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(XmlHeader);
if (yft?.Fragment != null)
StringBuilder sb = StringBuilderPool.Get();
try
{
FragType.WriteXmlNode(yft.Fragment, sb, 0, outputFolder);
sb.AppendLine(XmlHeader);
if (yft?.Fragment != null)
{
FragType.WriteXmlNode(yft.Fragment, sb, 0, outputFolder);
}
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
return sb.ToString();
}
}
public class XmlYft

View File

@ -34,13 +34,13 @@ namespace CodeWalker.GameFiles
//Hash = entry.ShortNameHash;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
try
{
using var rd = new ResourceDataReader(resentry, data);

File diff suppressed because it is too large Load Diff

View File

@ -10,31 +10,29 @@ namespace CodeWalker.GameFiles
{
public class YmfFile : PackedFile
{
public RpfFileEntry FileEntry { get; set; }
public RpfFileEntry? FileEntry { get; set; }
public Meta Meta { get; set; }
public PsoFile Pso { get; set; }
public RbfFile Rbf { get; set; }
public Meta? Meta { get; set; }
public PsoFile? Pso { get; set; }
public RbfFile? Rbf { get; set; }
public YmfMapDataGroup[] MapDataGroups { get; set; }
public CImapDependency[] imapDependencies { get; set; }
public YmfImapDependency2[] imapDependencies2 { get; set; }
public YmfItypDependency2[] itypDependencies2 { get; set; }
public CHDTxdAssetBinding[] HDTxdAssetBindings { get; set; }
public YmfInterior[] Interiors { get; set; }
public YmfMapDataGroup[] MapDataGroups { get; set; } = Array.Empty<YmfMapDataGroup>();
public CImapDependency[] imapDependencies { get; set; } = Array.Empty<CImapDependency>();
public YmfImapDependency2[] imapDependencies2 { get; set; } = Array.Empty<YmfImapDependency2>();
public YmfItypDependency2[] itypDependencies2 { get; set; } = Array.Empty<YmfItypDependency2>();
public CHDTxdAssetBinding[] HDTxdAssetBindings { get; set; } = Array.Empty<CHDTxdAssetBinding>();
public YmfInterior[] Interiors { get; set; } = Array.Empty<YmfInterior>();
public void Load(byte[] data, RpfFileEntry entry)
{
FileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
if (RbfFile.IsRBF(data.AsSpan(0, 4)))
{
Rbf = new RbfFile();
Rbf.Load(ms);
Rbf.Load(data);
//x64j.rpf\\levels\\gta5\\_citye\\indust_01\\id1_props.rpf\\_manifest.ymf
//x64j.rpf\\levels\\gta5\\_citye\\indust_02\\id2_props.rpf\\_manifest.ymf
@ -43,10 +41,10 @@ namespace CodeWalker.GameFiles
return;
}
if (PsoFile.IsPSO(ms))
if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(ms);
Pso.Load(data);
//PsoTypes.EnsurePsoTypes(Pso);
@ -56,21 +54,14 @@ namespace CodeWalker.GameFiles
}
else
{
Console.WriteLine("Neither");
}
return;
}
else
{ }//doesn't get here
using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>();
}
@ -81,22 +72,22 @@ namespace CodeWalker.GameFiles
//for TIMED YMAP stuff!!!!
//check CMapDataGroup.HoursOnOff
ArgumentNullException.ThrowIfNull(Pso, nameof(Pso));
var d = PsoTypes.GetRootItem<CPackFileMetaData>(Pso);
MapDataGroups = PsoTypes.GetObjectArray<YmfMapDataGroup, CMapDataGroup>(Pso, d.MapDataGroups);
MapDataGroups = PsoTypes.GetObjectArray<YmfMapDataGroup, CMapDataGroup>(Pso, in d._MapDataGroups) ?? Array.Empty<YmfMapDataGroup>();
imapDependencies = PsoTypes.GetItemArray<CImapDependency>(Pso, d.imapDependencies).ToArray();
imapDependencies = PsoTypes.GetItemArray<CImapDependency>(Pso, in d._imapDependencies)?.ToArray() ?? Array.Empty<CImapDependency>();
imapDependencies2 = PsoTypes.GetObjectArray<YmfImapDependency2, CImapDependencies>(Pso, d.imapDependencies_2);
imapDependencies2 = PsoTypes.GetObjectArray<YmfImapDependency2, CImapDependencies>(Pso, in d._imapDependencies_2) ?? Array.Empty<YmfImapDependency2>();
itypDependencies2 = PsoTypes.GetObjectArray<YmfItypDependency2, CItypDependencies>(Pso, d.itypDependencies_2);
HDTxdAssetBindings = PsoTypes.GetItemArray<CHDTxdAssetBinding>(Pso, d.HDTxdBindingArray).ToArray();
Interiors = PsoTypes.GetObjectArray<YmfInterior, CInteriorBoundsFiles>(Pso, d.Interiors);
itypDependencies2 = PsoTypes.GetObjectArray<YmfItypDependency2, CItypDependencies>(Pso, in d._itypDependencies_2) ?? Array.Empty<YmfItypDependency2>();
HDTxdAssetBindings = PsoTypes.GetItemArray<CHDTxdAssetBinding>(Pso, in d._HDTxdBindingArray)?.ToArray() ?? Array.Empty<CHDTxdAssetBinding>();
Interiors = PsoTypes.GetObjectArray<YmfInterior, CInteriorBoundsFiles>(Pso, in d._Interiors) ?? Array.Empty<YmfInterior>();
}
@ -109,8 +100,8 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmfMapDataGroup : PsoClass<CMapDataGroup>
{
public CMapDataGroup DataGroup { get; set; } //ymap name
public MetaHash[] Bounds { get; set; }
public MetaHash[] WeatherTypes { get; set; }
public MetaHash[] Bounds { get; set; } = Array.Empty<MetaHash>();
public MetaHash[] WeatherTypes { get; set; } = Array.Empty<MetaHash>();
public MetaHash Name { get; set; }
public ushort Flags { get; set; }
public uint HoursOnOff { get; set; }
@ -120,11 +111,11 @@ namespace CodeWalker.GameFiles
return DataGroup.ToString();
}
public override void Init(PsoFile pso, ref CMapDataGroup v)
public override void Init(PsoFile pso, in CMapDataGroup v)
{
DataGroup = v;
Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
WeatherTypes = PsoTypes.GetHashArray(pso, v.WeatherTypes);
Bounds = PsoTypes.GetHashArray(pso, v.Bounds) ?? Array.Empty<MetaHash>();
WeatherTypes = PsoTypes.GetHashArray(pso, v.WeatherTypes) ?? Array.Empty<MetaHash>();
Name = v.Name;
Flags = v.Flags;
HoursOnOff = v.HoursOnOff;
@ -136,7 +127,7 @@ namespace CodeWalker.GameFiles
public CImapDependencies Dep { get; set; }
public MetaHash[] itypDepArray { get; set; }//ybn hashes?
public override void Init(PsoFile pso, ref CImapDependencies v)
public override void Init(PsoFile pso, in CImapDependencies v)
{
Dep = v;
itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
@ -153,7 +144,7 @@ namespace CodeWalker.GameFiles
public CItypDependencies Dep { get; set; }
public MetaHash[] itypDepArray { get; set; }//ytyp hashes?
public override void Init(PsoFile pso, ref CItypDependencies v)
public override void Init(PsoFile pso, in CItypDependencies v)
{
Dep = v;
itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
@ -170,15 +161,12 @@ namespace CodeWalker.GameFiles
public CInteriorBoundsFiles Interior { get; set; }
public MetaHash[] Bounds { get; set; }//ybn hashes?
public override string ToString()
{
return Interior.ToString();
}
public override string ToString() => Interior.ToString();
public override void Init(PsoFile pso, ref CInteriorBoundsFiles v)
public override void Init(PsoFile pso, in CInteriorBoundsFiles v)
{
Interior = v;
Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
Bounds = PsoTypes.GetHashArray(pso, in v._Bounds);
}
}

View File

@ -59,8 +59,7 @@ namespace CodeWalker.GameFiles
FilePath = Name;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry != null)
if (entry is RpfResourceFileEntry resentry)
{
using var rd = new ResourceDataReader(resentry, data);
@ -80,12 +79,10 @@ namespace CodeWalker.GameFiles
}
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
if (RbfFile.IsRBF(data.AsSpan(0, 4)))
{
Rbf = new RbfFile();
var rbfstruct = Rbf.Load(ms);
var rbfstruct = Rbf.Load(data);
if (rbfstruct.Name == "CMapParentTxds")
{
@ -97,30 +94,24 @@ namespace CodeWalker.GameFiles
Loaded = true;
return;
}
if (PsoFile.IsPSO(ms))
else if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(ms);
Pso.Load(data);
//PsoTypes.EnsurePsoTypes(Pso);
var root = PsoTypes.GetRootEntry(Pso);
if (root != null)
if (root.NameHash == MetaName.CScenarioPointManifest)
{
if (root.NameHash == MetaName.CScenarioPointManifest)
{
LoadScenarioPointManifest(Pso);
}
LoadScenarioPointManifest(Pso);
}
Loaded = true;
return;
}
else
{
}
@ -134,29 +125,29 @@ namespace CodeWalker.GameFiles
ContentType = YmtFileContentType.MapParentTxds;
CMapParentTxds = new Dictionary<string, string>();
if (rbfstruct.Children is null || rbfstruct.Children.Count == 0)
return;
//StringBuilder sblist = new StringBuilder();
foreach(var child in rbfstruct.Children)
{
var childstruct = child as RbfStructure;
if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
if (child is RbfStructure childstruct && childstruct.Name == "txdRelationships" && childstruct.Children is not null)
{
foreach (var txdrel in childstruct.Children)
{
var txdrelstruct = txdrel as RbfStructure;
if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
if (txdrel is RbfStructure txdrelstruct && txdrelstruct.Name == "item" && txdrelstruct.Children is not null)
{
string parentstr = string.Empty;
string childstr = string.Empty;
foreach(var item in txdrelstruct.Children)
foreach (var item in txdrelstruct.Children)
{
var itemstruct = item as RbfStructure;
if ((itemstruct != null))
if (item is RbfStructure itemstruct)
{
var strbytes = itemstruct.Children[0] as RbfBytes;
string thisstr = string.Empty;
if (strbytes != null)
if (itemstruct.Children is not null && itemstruct.Children[0] is RbfBytes strbytes)
{
thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
thisstr = strbytes.GetNullTerminatedString();
}
switch (item.Name)
{
@ -170,15 +161,9 @@ namespace CodeWalker.GameFiles
}
}
if((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
if (!string.IsNullOrEmpty(parentstr) && !string.IsNullOrEmpty(childstr))
{
if (!CMapParentTxds.ContainsKey(childstr))
{
CMapParentTxds.Add(childstr, parentstr);
}
else
{
}
_ = CMapParentTxds.TryAdd(childstr, parentstr);
//sblist.AppendLine(childstr + ": " + parentstr);
}
}
@ -211,7 +196,7 @@ namespace CodeWalker.GameFiles
CScenarioPointRegion = new MCScenarioPointRegion();
CScenarioPointRegion.Ymt = this;
CScenarioPointRegion.Load(meta, cdata);
CScenarioPointRegion.Load(meta, in cdata);
ScenarioRegion = new ScenarioRegion();
@ -225,33 +210,32 @@ namespace CodeWalker.GameFiles
public byte[] Save()
public byte[]? Save()
{
switch (ContentType)
return ContentType switch
{
case YmtFileContentType.MapParentTxds: return SaveMapParentTxds();
case YmtFileContentType.ScenarioPointManifest: return SaveScenarioPointManifest();
case YmtFileContentType.ScenarioPointRegion: return SaveScenarioPointRegion();
}
return null;
YmtFileContentType.MapParentTxds => SaveMapParentTxds(),
YmtFileContentType.ScenarioPointManifest => SaveScenarioPointManifest(),
YmtFileContentType.ScenarioPointRegion => SaveScenarioPointRegion(),
_ => null,
};
}
private byte[] SaveMapParentTxds()
private byte[]? SaveMapParentTxds()
{
return null;
}
private byte[] SaveScenarioPointManifest()
private byte[]? SaveScenarioPointManifest()
{
return null;
}
private byte[] SaveScenarioPointRegion()
private byte[]? SaveScenarioPointRegion()
{
if (ScenarioRegion != null)
if (ScenarioRegion is not null)
{
return ScenarioRegion.Save();
}
@ -267,7 +251,7 @@ namespace CodeWalker.GameFiles
private void LogSaveWarning(string w)
{
if (SaveWarnings == null) SaveWarnings = new List<string>();
SaveWarnings ??= new List<string>();
SaveWarnings.Add(w);
}
@ -276,10 +260,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return RpfFileEntry.ToString();
}
public override string? ToString() => RpfFileEntry?.ToString();
}
@ -300,10 +281,11 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmtScenarioPointManifest
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YmtScenarioPointManifest
{
public CScenarioPointManifest _Data;
public CScenarioPointManifest Data { get { return _Data; } set { _Data = value; } }
public CScenarioPointManifest Data { get => _Data; init => _Data = value; }
public CScenarioPointRegionDef[] RegionDefs { get; set; }
public CScenarioPointGroup[] Groups { get; set; }
@ -312,10 +294,10 @@ namespace CodeWalker.GameFiles
public void Load(PsoFile pso)
{
Data = PsoTypes.GetRootItem<CScenarioPointManifest>(pso);
RegionDefs = PsoTypes.ConvertDataArray<CScenarioPointRegionDef>(pso, _Data.RegionDefs);
Groups = PsoTypes.ConvertDataArray<CScenarioPointGroup>(pso, _Data.Groups);
InteriorNames = PsoTypes.GetHashArray(pso, _Data.InteriorNames);
_Data = PsoTypes.GetRootItem<CScenarioPointManifest>(pso);
RegionDefs = PsoTypes.ConvertDataArray<CScenarioPointRegionDef>(pso, in _Data._RegionDefs);
Groups = PsoTypes.ConvertDataArray<CScenarioPointGroup>(pso, in _Data._Groups);
InteriorNames = PsoTypes.GetHashArray(pso, in _Data._InteriorNames);
}
}

View File

@ -1,8 +1,10 @@
using CodeWalker.World;
using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Xml;
@ -18,9 +20,9 @@ namespace CodeWalker.GameFiles
public YndLink[] Links { get; set; }
public YndJunction[] Junctions { get; set; }
public EditorVertex[] LinkedVerts { get; set; }//populated by the space (needs to use grid of all ynd's!)
public EditorVertex[] TriangleVerts { get; set; } //used for junctions display
public Vector4[] NodePositions { get; set; }
public EditorVertex[] LinkedVerts { get; set; } = [];//populated by the space (needs to use grid of all ynd's!)
public EditorVertex[] TriangleVerts { get; set; } = []; //used for junctions display
public Vector4[] NodePositions { get; set; } = [];
public Vector3 BBMin { get; set; }
public Vector3 BBMax { get; set; }
@ -31,10 +33,7 @@ namespace CodeWalker.GameFiles
public int AreaID
{
get
{
return CellY * 32 + CellX;
}
get => CellY * 32 + CellX;
set
{
CellX = value % 32;
@ -78,10 +77,10 @@ namespace CodeWalker.GameFiles
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -219,9 +218,9 @@ namespace CodeWalker.GameFiles
public void InitNodesFromDictionary()
{
if (NodeDictionary != null)
if (NodeDictionary is not null)
{
if (NodeDictionary.Nodes != null)
if (NodeDictionary.Nodes is not null)
{
var nodes = NodeDictionary.Nodes;
Nodes = new YndNode[nodes.Length];
@ -230,11 +229,9 @@ namespace CodeWalker.GameFiles
var n = new YndNode();
n.Init(this, nodes[i]);
Nodes[i] = n;
if (n.NodeID != i)
{ } //never hit here - nodeid's have to match the index!
}
}
if ((NodeDictionary.JunctionRefs != null) && (NodeDictionary.Junctions != null))
if (NodeDictionary.JunctionRefs is not null && NodeDictionary.Junctions is not null)
{
var juncrefs = NodeDictionary.JunctionRefs;
var juncs = NodeDictionary.Junctions;
@ -243,7 +240,7 @@ namespace CodeWalker.GameFiles
{
var juncref = juncrefs[i];
if (juncref.JunctionID >= juncs.Length)
{ continue; }
continue;
var j = new YndJunction();
j.Init(this, juncs[juncref.JunctionID], juncref);
@ -295,7 +292,7 @@ namespace CodeWalker.GameFiles
Nodes = nnodes;
NodeDictionary.NodesCount = (uint)ncnt;
var links = new List<YndLink>();
using var links = new PooledList<YndLink>();
if (Links != null)
{
links.AddRange(Links);
@ -343,7 +340,7 @@ namespace CodeWalker.GameFiles
return;
}
// Sort nodes so ped nodes are at the end
var nodes = new List<YndNode>(Nodes.Length);
using var nodes = new PooledList<YndNode>(Nodes.Length);
var affectedNodesList = new List<YndNode>();
var vehicleNodes = Nodes.Where(n => !n.IsPedNode).OrderBy(n => n.NodeID).ToArray();
var pedNodes = Nodes.Where(n => n.IsPedNode).OrderBy(n => n.NodeID).ToArray();
@ -395,7 +392,7 @@ namespace CodeWalker.GameFiles
rmLinks.AddRange(nodeRmLinks);
}
if (rmLinks.Any())
if (rmLinks.Count != 0)
{
Links = Links.Where(l => !rmLinks.Contains(l)).ToArray();
return true;
@ -420,12 +417,12 @@ namespace CodeWalker.GameFiles
public void UpdateAllNodePositions()
{
int cnt = Nodes?.Length ?? 0;
if (cnt <= 0)
if (Nodes is null || Nodes.Length == 0)
{
NodePositions = null;
NodePositions = Array.Empty<Vector4>();
return;
}
int cnt = Nodes.Length;
var np = new Vector4[cnt];
for (int i = 0; i < cnt; i++)
{
@ -451,12 +448,12 @@ namespace CodeWalker.GameFiles
vc = Links.Length * 6;
}
List<EditorVertex> verts = new List<EditorVertex>(vc);
EditorVertex v0 = new EditorVertex();
EditorVertex v1 = new EditorVertex();
EditorVertex v2 = new EditorVertex();
EditorVertex v3 = new EditorVertex();
if ((Links != null) && (Nodes != null))
using PooledList<EditorVertex> verts = new PooledList<EditorVertex>(vc);
//EditorVertex v0 = new EditorVertex();
//EditorVertex v1 = new EditorVertex();
//EditorVertex v2 = new EditorVertex();
//EditorVertex v3 = new EditorVertex();
if (Links is not null && Nodes is not null)
{
foreach (var node in Nodes)
{
@ -496,16 +493,13 @@ namespace CodeWalker.GameFiles
outer += halfwidth;
}
v0.Position = p1 + ax * inner;
v1.Position = p0 + ax * inner;
v2.Position = p1 + ax * outer;
v3.Position = p0 + ax * outer;
var c = (uint)link.GetColour().ToRgba();
v0.Colour = c;
v1.Colour = c;
v2.Colour = c;
v3.Colour = c;
var v0 = new EditorVertex(p1 + ax * inner, c);
var v1 = new EditorVertex(p0 + ax * inner, c);
var v2 = new EditorVertex(p1 + ax * outer, c);
var v3 = new EditorVertex(p0 + ax * outer, c);
verts.Add(v0);
verts.Add(v1);
verts.Add(v2);
@ -523,31 +517,34 @@ namespace CodeWalker.GameFiles
}
else
{
TriangleVerts = null;
TriangleVerts = [];
}
}
private void UpdateJunctionTriangleVertices(YndNode[] selectedNodes)
private void UpdateJunctionTriangleVertices(YndNode[]? selectedNodes)
{
if (selectedNodes == null)
if (selectedNodes is null)
{
return;
}
//build triangles for the junctions bytes display....
List<EditorVertex> verts = new List<EditorVertex>();
EditorVertex v0 = new EditorVertex();
EditorVertex v1 = new EditorVertex();
EditorVertex v2 = new EditorVertex();
EditorVertex v3 = new EditorVertex();
using PooledList<EditorVertex> verts = new PooledList<EditorVertex>();
EditorVertex v0;
EditorVertex v1;
EditorVertex v2;
EditorVertex v3;
foreach (var node in selectedNodes)
{
if (node.Ynd != this) continue;
if (node.Junction == null) continue;
if (node.Ynd != this)
continue;
if (node.Junction is null)
continue;
var j = node.Junction;
var d = j.Heightmap;
if (d == null) continue;
if (d is null)
continue;
float maxz = j.MaxZ / 32.0f;
float minz = j.MinZ / 32.0f;
@ -576,14 +573,10 @@ namespace CodeWalker.GameFiles
var val2 = row1.Values[x - 1] / 255.0f;
var val3 = row1.Values[x] / 255.0f;
float offx = x * 2.0f;
v0.Position = cnr + new Vector3(offx - 2.0f, offy - 2.0f, val0 * rngz);
v1.Position = cnr + new Vector3(offx + 0.0f, offy - 2.0f, val1 * rngz);
v2.Position = cnr + new Vector3(offx - 2.0f, offy + 0.0f, val2 * rngz);
v3.Position = cnr + new Vector3(offx + 0.0f, offy + 0.0f, val3 * rngz);
v0.Colour = (uint)new Color4(val0, 1.0f - val0, 0.0f, 0.3f).ToRgba();
v1.Colour = (uint)new Color4(val1, 1.0f - val1, 0.0f, 0.3f).ToRgba();
v2.Colour = (uint)new Color4(val2, 1.0f - val2, 0.0f, 0.3f).ToRgba();
v3.Colour = (uint)new Color4(val3, 1.0f - val3, 0.0f, 0.3f).ToRgba();
v0 = new EditorVertex(cnr + new Vector3(offx - 2.0f, offy - 2.0f, val0 * rngz), (uint)new Color4(val0, 1.0f - val0, 0.0f, 0.3f).ToRgba());
v1 = new EditorVertex(cnr + new Vector3(offx + 0.0f, offy - 2.0f, val1 * rngz), (uint)new Color4(val1, 1.0f - val1, 0.0f, 0.3f).ToRgba());
v2 = new EditorVertex(cnr + new Vector3(offx - 2.0f, offy + 0.0f, val2 * rngz), (uint)new Color4(val2, 1.0f - val2, 0.0f, 0.3f).ToRgba());
v3 = new EditorVertex(cnr + new Vector3(offx + 0.0f, offy + 0.0f, val3 * rngz), (uint)new Color4(val3, 1.0f - val3, 0.0f, 0.3f).ToRgba());
verts.Add(v0);
verts.Add(v1);
verts.Add(v2);
@ -597,7 +590,7 @@ namespace CodeWalker.GameFiles
if (verts.Count > 0)
{
var vertsarr = verts.ToArray();
if (TriangleVerts != null)
if (TriangleVerts.Length > 0)
{
var nvc = vertsarr.Length;
var tvc = TriangleVerts.Length;
@ -689,18 +682,13 @@ namespace CodeWalker.GameFiles
return true;
}
affectedFiles = Array.Empty<YndFile>();
affectedFiles = [];
return false;
}
public override string ToString()
{
return RpfFileEntry?.ToString() ?? string.Empty;
}
public override string ToString() => RpfFileEntry?.ToString() ?? string.Empty;
}
public enum YndNodeSpeed
@ -725,26 +713,29 @@ namespace CodeWalker.GameFiles
OffRoadJunction = 20
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class YndNode : BasePathNode
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YndNode : BasePathNode
{
public Node _RawData;
public YndFile Ynd { get; set; }
public Node RawData { get { return _RawData; } set { _RawData = value; } }
public Vector3 Position { get; set; }
public Node RawData { get => _RawData; set => _RawData = value; }
public Vector3 _Position;
public ref Vector3 Position => ref _Position;
public int LinkCount { get; set; }
public int LinkCountUnk { get; set; }
public YndLink[] Links { get; set; }
public YndLink[]? Links { get; set; }
public ushort AreaID { get { return _RawData.AreaID; } set { _RawData.AreaID = value; } }
public ushort NodeID { get { return _RawData.NodeID; } set { _RawData.NodeID = value; } }
public ushort LinkID { get { return _RawData.LinkID; } set { _RawData.LinkID = value; } }
public FlagsByte Flags0 { get { return _RawData.Flags0; } set { _RawData.Flags0 = value; } }
public FlagsByte Flags1 { get { return _RawData.Flags1; } set { _RawData.Flags1 = value; } }
public FlagsByte Flags2 { get { return _RawData.Flags2; } set { _RawData.Flags2 = value; } }
public FlagsByte Flags3 { get { return _RawData.Flags3; } set { _RawData.Flags3 = value; } }
public FlagsByte Flags4 { get { return _RawData.Flags4; } set { _RawData.Flags4 = value; } }
public TextHash StreetName { get { return _RawData.StreetName; } set { _RawData.StreetName = value; } }
public ushort AreaID { get => _RawData.AreaID; set => _RawData.AreaID = value; }
public ushort NodeID { get => _RawData.NodeID; set => _RawData.NodeID = value; }
public ushort LinkID { get => _RawData.LinkID; set => _RawData.LinkID = value; }
public FlagsByte Flags0 { get => _RawData.Flags0; set => _RawData.Flags0 = value; }
public FlagsByte Flags1 { get => _RawData.Flags1; set => _RawData.Flags1 = value; }
public FlagsByte Flags2 { get => _RawData.Flags2; set => _RawData.Flags2 = value; }
public FlagsByte Flags3 { get => _RawData.Flags3; set => _RawData.Flags3 = value; }
public FlagsByte Flags4 { get => _RawData.Flags4; set => _RawData.Flags4 = value; }
public TextHash StreetName { get => _RawData.StreetName; set => _RawData.StreetName = value; }
public Color4 Colour { get; set; }
@ -754,14 +745,8 @@ namespace CodeWalker.GameFiles
public YndNodeSpeed Speed
{
get
{
return (YndNodeSpeed)((LinkCountUnk >> 1) & 3);
}
set
{
LinkCountUnk = (LinkCountUnk &~ 6) | (((int)value & 3) << 1);
}
get => (YndNodeSpeed)((LinkCountUnk >> 1) & 3);
set => LinkCountUnk = (LinkCountUnk & ~6) | (((int)value & 3) << 1);
}
//// Flag0 Properties
@ -843,14 +828,14 @@ namespace CodeWalker.GameFiles
}
public bool IsDisabledUnk1
{
get { return (Flags2.Value & 16) > 0; }
get => (Flags2.Value & 16) > 0;
set => Flags2 = (byte)(value ? Flags2 | 16 : Flags2 &~ 16);
}
// Flag3 Properties
public bool Tunnel
{
get { return (Flags3 & 1) > 0; }
get => (Flags3 & 1) > 0;
set => Flags3 = (byte)(value ? Flags3 | 1 : Flags3 &~ 1);
}
public int HeuristicValue
@ -894,11 +879,7 @@ namespace CodeWalker.GameFiles
{
Ynd = ynd;
RawData = node;
Vector3 p = new Vector3();
p.X = node.PositionX / 4.0f;
p.Y = node.PositionY / 4.0f;
p.Z = node.PositionZ / 32.0f;
Position = p;
Position = new Vector3(node.PositionX / 4.0f, node.PositionY / 4.0f, node.PositionZ / 32.0f);
LinkCount = node.LinkCountFlags.Value >> 3;
LinkCountUnk = node.LinkCountFlags.Value & 7;
@ -947,7 +928,8 @@ namespace CodeWalker.GameFiles
public void UpdateLinkLengths()
{
if (Links == null) return;
if (Links is null)
return;
for (int i = 0; i < Links.Length; i++)
{
var link = Links[i];
@ -1011,7 +993,7 @@ namespace CodeWalker.GameFiles
}
public YndLink AddLink(YndNode tonode = null, bool bidirectional = true)
public YndLink AddLink(YndNode? tonode = null, bool bidirectional = true)
{
if (Links == null)
{
@ -1027,13 +1009,13 @@ namespace CodeWalker.GameFiles
YndLink l = new YndLink();
l._RawData.AreaID = AreaID;
l.Node1 = this;
if (tonode != null)
if (tonode is not null)
{
l.Node2 = tonode;
l._RawData.AreaID = tonode.AreaID;
l._RawData.NodeID = tonode.NodeID;
}
else if ((Ynd.Nodes != null) && (Ynd.Nodes.Length > 0))
else if (Ynd.Nodes is not null && Ynd.Nodes.Length > 0)
{
l.Node2 = Ynd.Nodes[0];
}
@ -1066,8 +1048,13 @@ namespace CodeWalker.GameFiles
return l;
}
public bool TryGetLinkForNode(YndNode node, out YndLink link)
public bool TryGetLinkForNode(YndNode node, [MaybeNullWhen(false)] out YndLink link)
{
if (Links is null)
{
link = null;
return false;
}
for (int i = 0; i < Links.Length; i++)
{
if (Links[i].Node2 == node)
@ -1114,15 +1101,15 @@ namespace CodeWalker.GameFiles
private void FloodCopyFlags(YndNode basis, List<YndNode> seenNodes, out YndFile[] affectedFiles)
{
var affectedFilesList = new List<YndFile>();
if (Links == null || !Links.Any())
if (Links == null || Links.Length == 0)
{
affectedFiles = Array.Empty<YndFile>();
affectedFiles = [];
return;
}
if (seenNodes.Contains(this))
{
affectedFiles = Array.Empty<YndFile>();
affectedFiles = [];
return;
}
@ -1177,20 +1164,20 @@ namespace CodeWalker.GameFiles
var oldPosition = Position;
SetPosition(newPosition);
var expectedArea = space.NodeGrid.GetCellForPosition(newPosition);
var expectedArea = space.NodeGrid.GetCellForPosition(in newPosition);
if (AreaID != expectedArea.ID)
if (AreaID != expectedArea?.ID)
{
var nodeYnd = space.NodeGrid.GetCell(AreaID).Ynd;
var newYnd = expectedArea.Ynd;
if (newYnd == null)
var newYnd = expectedArea?.Ynd;
if (newYnd is null)
{
SetPosition(oldPosition);
affectedFiles = Array.Empty<YndFile>();
return;
}
if ((nodeYnd == null) ||
if ((nodeYnd is null) ||
nodeYnd.RemoveYndNode(space, this, false, out var affectedFilesFromDelete))
{
totalAffectedFiles.Add(nodeYnd);
@ -1249,7 +1236,8 @@ namespace CodeWalker.GameFiles
for (int x = 0; x < sizeX; x++)
{
var offx = x * 2.0f;
var result = space.RayIntersect(new Ray(start + new Vector3(offx, offy, 0f), new Vector3(0f, 0f, -1f)), maxDist, layers);
var ray = new Ray(start + new Vector3(offx, offy, 0f), new Vector3(0f, 0f, -1f));
var result = space.RayIntersect(ref ray, maxDist, layers);
var p = start + new Vector3(offx, offy, 0f);
//t.AppendLine($"{p.X}, {p.Y}, {p.Z}");
@ -1287,7 +1275,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
//return AreaID.ToString() + "." + NodeID.ToString();
return StreetName.ToString() + ", " + Position.X.ToString() + ", " + Position.Y.ToString() + ", " + Position.Z.ToString() + ", " + NodeID.ToString();
return $"{StreetName}, {Position.X}, {Position.Y}, {Position.Z}, {NodeID}";
}
}
@ -1357,8 +1345,8 @@ namespace CodeWalker.GameFiles
public void UpdateLength()
{
if (Node1 == null) return;
if (Node2 == null) return;
if (Node1 is null || Node2 is null)
return;
LinkLength = (byte)Math.Min(255, (Node2.Position - Node1.Position).Length());
}
@ -1366,7 +1354,8 @@ namespace CodeWalker.GameFiles
public void CopyFlags(YndLink link)
{
if (link == null) return;
if (link is null)
return;
Flags0 = link.Flags0;
Flags1 = link.Flags1;
Flags2 = link.Flags2;
@ -1678,7 +1667,7 @@ namespace CodeWalker.GameFiles
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Values.Length; i++)
{
if (i > 0) sb.Append(" ");
if (i > 0) sb.Append(' ');
sb.Append(Values[i].ToString().PadLeft(3, '0'));
//sb.Append(Convert.ToString(Values[i], 16).ToUpper().PadLeft(2, '0'));
}
@ -1694,7 +1683,7 @@ namespace CodeWalker.GameFiles
public int Depth;
public int MaxDepth;
public int Threshold;
public List<BasePathNode> Nodes;
public BasePathNode[] Nodes;
public BoundingBox Box;
public BoundingSphere Sphere;
public PathBVHNode Node1;
@ -1703,14 +1692,15 @@ namespace CodeWalker.GameFiles
public void CalcBounds()
{
if ((Nodes == null) || (Nodes.Count <= 0)) return;
if (Nodes is null || Nodes.Length == 0)
return;
Box.Minimum = new Vector3(float.MaxValue);
Box.Maximum = new Vector3(float.MinValue);
foreach (var node in Nodes)
{
Box.Minimum = Vector3.Min(Box.Minimum, node.Position);
Box.Maximum = Vector3.Max(Box.Maximum, node.Position);
Vectors.Min(in Box.Minimum, in node.Position, out Box.Minimum);
Vectors.Max(in Box.Maximum, in node.Position, out Box.Maximum);
}
Sphere.Center = (Box.Minimum + Box.Maximum) * 0.5f;
Sphere.Radius = (Box.Maximum - Box.Minimum).Length() * 0.5f;
@ -1719,24 +1709,28 @@ namespace CodeWalker.GameFiles
public void Build()
{
if ((Nodes == null) || (Nodes.Count <= Threshold) || (Depth >= MaxDepth)) return;
if (Nodes == null || Nodes.Length == 0 || Nodes.Length <= Threshold || Depth >= MaxDepth)
return;
Vector3 avgsum = Vector3.Zero;
foreach (var node in Nodes)
{
avgsum += node.Position;
}
Vector3 avg = avgsum * (1.0f / Nodes.Count);
Vector3 avg = avgsum * Nodes.Length;
int countx = 0, county = 0, countz = 0;
foreach (var node in Nodes)
{
if (node.Position.X < avg.X) countx++;
if (node.Position.Y < avg.Y) county++;
if (node.Position.Z < avg.Z) countz++;
if (node.Position.X < avg.X)
countx++;
if (node.Position.Y < avg.Y)
county++;
if (node.Position.Z < avg.Z)
countz++;
}
int target = Nodes.Count / 2;
int target = Nodes.Length / 2;
int dx = Math.Abs(target - countx);
int dy = Math.Abs(target - county);
int dz = Math.Abs(target - countz);
@ -1747,37 +1741,41 @@ namespace CodeWalker.GameFiles
else axis = 2; //z seems best
List<BasePathNode> l1 = new List<BasePathNode>();
List<BasePathNode> l2 = new List<BasePathNode>();
using PooledList<BasePathNode> l1 = new PooledList<BasePathNode>(Nodes.Length);
using PooledList<BasePathNode> l2 = new PooledList<BasePathNode>(Nodes.Length);
foreach (var node in Nodes)
{
bool s = false;
switch (axis)
var s = axis switch
{
default:
case 0: s = (node.Position.X > avg.X); break;
case 1: s = (node.Position.Y > avg.Y); break;
case 2: s = (node.Position.Z > avg.Z); break;
}
if (s) l1.Add(node);
else l2.Add(node);
1 => (node.Position.Y > avg.Y),
2 => (node.Position.Z > avg.Z),
_ => (node.Position.X > avg.X),
};
if (s)
l1.Add(node);
else
l2.Add(node);
}
var cdepth = Depth + 1;
Node1 = new PathBVHNode();
Node1.Depth = cdepth;
Node1.MaxDepth = MaxDepth;
Node1.Threshold = Threshold;
Node1.Nodes = new List<BasePathNode>(l1);
Node1 = new PathBVHNode
{
Depth = cdepth,
MaxDepth = MaxDepth,
Threshold = Threshold,
Nodes = l1.ToArray(),
};
Node1.CalcBounds();
Node1.Build();
Node2 = new PathBVHNode();
Node2.Depth = cdepth;
Node2.MaxDepth = MaxDepth;
Node2.Threshold = Threshold;
Node2.Nodes = new List<BasePathNode>(l2);
Node2 = new PathBVHNode
{
Depth = cdepth,
MaxDepth = MaxDepth,
Threshold = Threshold,
Nodes = l2.ToArray(),
};
Node2.CalcBounds();
Node2.Build();
}
@ -1785,12 +1783,13 @@ namespace CodeWalker.GameFiles
public void UpdateForNode(BasePathNode node)
{
if (!Nodes.Contains(node)) return;
Box.Minimum = Vector3.Min(Box.Minimum, node.Position);
Box.Maximum = Vector3.Max(Box.Maximum, node.Position);
if (!Nodes.Contains(node))
return;
Vectors.Min(in Box.Minimum, in node.Position, out Box.Minimum);
Vectors.Max(in Box.Maximum, in node.Position, out Box.Maximum);
if (Node1 != null) Node1.UpdateForNode(node);
if (Node2 != null) Node2.UpdateForNode(node);
Node1?.UpdateForNode(node);
Node2?.UpdateForNode(node);
}
}
@ -1802,11 +1801,19 @@ namespace CodeWalker.GameFiles
{
Threshold = threshold;
MaxDepth = maxdepth;
Nodes = (nodes != null) ? new List<BasePathNode>(nodes) : new List<BasePathNode>();
Nodes = nodes?.ToArray() ?? [];
CalcBounds();
Build();
}
public PathBVH(BasePathNode[] nodes, int threshold, int maxdepth)
{
Threshold = threshold;
MaxDepth = maxdepth;
Nodes = nodes;
CalcBounds();
Build();
}
}
@ -1814,27 +1821,19 @@ namespace CodeWalker.GameFiles
public interface BasePathNode
{
Vector3 Position { get; set; }
ref Vector3 Position { get; }
}
public interface BasePathData
{
//reuse this interface for file types that need to get paths rendered...
EditorVertex[] GetPathVertices();
EditorVertex[] GetTriangleVertices();
Vector4[] GetNodePositions();
EditorVertex[] GetPathVertices() => Array.Empty<EditorVertex>();
EditorVertex[] GetTriangleVertices() => Array.Empty<EditorVertex>();
Vector4[] GetNodePositions() => Array.Empty<Vector4>();
}
public class YndXml : MetaXmlBase
{

View File

@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Collections.Pooled;
using SharpDX;
namespace CodeWalker.GameFiles
@ -28,7 +30,7 @@ namespace CodeWalker.GameFiles
//fields used by the editor:
public bool HasChanged { get; set; } = false;
public List<string> SaveWarnings = null;
public List<string>? SaveWarnings = null;
public bool BuildStructsOnSave { get; set; } = true;
@ -38,13 +40,11 @@ namespace CodeWalker.GameFiles
public int AreaID
{
get
{
return (int)(Nav?.AreaID ?? 0);
}
get => (int)(Nav?.AreaID ?? 0);
set
{
if (Nav != null) Nav.AreaID = (uint)value;
if (Nav is not null)
Nav.AreaID = (uint)value;
}
}
public int CellX { get { return AreaID % 100; } set { AreaID = (CellY * 100) + value; } }
@ -53,12 +53,12 @@ namespace CodeWalker.GameFiles
//getters for property grids viewing of the lists
public Vector3[] AllVertices { get { return Vertices?.ToArray(); } }
public ushort[] AllIndices { get { return Indices?.ToArray(); } }
public YnvEdge[] AllEdges { get { return Edges?.ToArray(); } }
public YnvPoly[] AllPolys { get { return Polys?.ToArray(); } }
public YnvPortal[] AllPortals { get { return Portals?.ToArray(); } }
public YnvPoint[] AllPoints { get { return Points?.ToArray(); } }
public Vector3[] AllVertices => Vertices?.ToArray();
public ushort[] AllIndices => Indices?.ToArray();
public YnvEdge[] AllEdges => Edges?.ToArray();
public YnvPoly[] AllPolys => Polys?.ToArray();
public YnvPortal[] AllPortals => Portals?.ToArray();
public YnvPoint[] AllPoints => Points?.ToArray();
@ -84,10 +84,10 @@ namespace CodeWalker.GameFiles
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -104,14 +104,13 @@ namespace CodeWalker.GameFiles
BuildBVH();
Loaded = true;
LoadQueued = true;
}
public void InitFromNav()
{
if (Nav == null) return;
if (Nav is null) return;
Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero;
Vector3 aabbsize = Nav.AABBSize;
@ -294,7 +293,7 @@ namespace CodeWalker.GameFiles
{ }//debug
e._RawData._Poly1.AreaIDInd = EnsureEdgeAreaID(e.AreaID1, areadict, arealist);
e._RawData._Poly2.AreaIDInd = EnsureEdgeAreaID(e.AreaID2, areadict, arealist);
edge = e.RawData;
edge = e._RawData;
}
else
{
@ -597,7 +596,7 @@ namespace CodeWalker.GameFiles
(bmax.Y >= min.Y) && (bmin.Y <= max.Y);// &&
//(bmax.Z >= min.Z) && (bmin.Z <= max.Z);
}
private bool BoxOverlaps(NavMeshAABB a, NavMeshAABB b)
private bool BoxOverlaps(in NavMeshAABB a, in NavMeshAABB b)
{
return (a.MaxX >= b.MinX) && (a.MinX <= b.MaxX) &&
(a.MaxY >= b.MinY) && (a.MinY <= b.MaxY);
@ -622,11 +621,8 @@ namespace CodeWalker.GameFiles
public void UpdateAllNodePositions()
{
if (Nav == null) return;
Vector3 posoffset = Nav.SectorTree.AABBMin.XYZ();
Vector3 aabbsize = Nav.AABBSize;
if (Nav is null)
return;
EditorVertex v = new EditorVertex();
v.Colour = 0xFF0000FF;
@ -635,9 +631,9 @@ namespace CodeWalker.GameFiles
////### add portal positions to the node list, also add links to the link vertex array
int cnt = Portals?.Count ?? 0;
if (cnt > 0)
if (Portals is not null && Portals.Count > 0)
{
int cnt = Portals.Count;
for (int i = 0; i < cnt; i++)
{
var portal = Portals[i];
@ -649,9 +645,10 @@ namespace CodeWalker.GameFiles
////### add point positions to the node list
cnt = Points?.Count ?? 0;
if (cnt >= 0)
if (Points is not null && Points.Count > 0)
{
int cnt = Points.Count;
for (int i = 0; i < cnt; i++)
{
var point = Points[i];
@ -660,8 +657,8 @@ namespace CodeWalker.GameFiles
}
NodePositions = (nv.Count > 0) ? nv.ToArray() : null;
PathVertices = (lv.Count > 0) ? lv.ToArray() : null;
NodePositions = (nv.Count > 0) ? nv.ToArray() : Array.Empty<Vector4>();
PathVertices = (lv.Count > 0) ? lv.ToArray() : Array.Empty<EditorVertex>();
}
@ -673,10 +670,8 @@ namespace CodeWalker.GameFiles
//go through the nav mesh polys and generate verts to render...
if ((Polys == null) || (Polys.Count == 0)) return;
int vc = Vertices.Count;
if (Polys is null || Polys.Count == 0)
return;
List<EditorVertex> rverts = new List<EditorVertex>();
EditorVertex p0 = new EditorVertex();
@ -708,6 +703,12 @@ namespace CodeWalker.GameFiles
}
}
if (rverts.Count == 0)
{
TriangleVerts = Array.Empty<EditorVertex>();
return;
}
TriangleVerts = rverts.ToArray();
}
@ -717,10 +718,16 @@ namespace CodeWalker.GameFiles
public void UpdateContentFlags(bool vehicle)
{
NavMeshFlags f = NavMeshFlags.None;
if (Polys?.Count > 0) f = f | NavMeshFlags.Polygons;
if (Portals?.Count > 0) f = f | NavMeshFlags.Portals;
if (vehicle) f = f | NavMeshFlags.Vehicle;
else f = f | NavMeshFlags.Unknown8; //what exactly is this?
if (Polys?.Count > 0)
f |= NavMeshFlags.Polygons;
if (Portals?.Count > 0)
f |= NavMeshFlags.Portals;
if (vehicle)
f |= NavMeshFlags.Vehicle;
else
f |= NavMeshFlags.Unknown8; //what exactly is this?
Nav.ContentFlags = f;
}
@ -729,10 +736,14 @@ namespace CodeWalker.GameFiles
public void BuildBVH()
{
var nodes = new List<BasePathNode>();
if (Portals != null) nodes.AddRange(Portals);
if (Points != null) nodes.AddRange(Points);
BVH = new PathBVH(nodes, 10, 10);
using var nodes = new PooledList<BasePathNode>();
if (Portals is not null && Portals.Count > 0)
nodes.AddRange(Portals);
if (Points is not null && Points.Count > 0)
nodes.AddRange(Points);
BVH = new PathBVH(nodes.ToArray(), 10, 10);
}
@ -809,7 +820,7 @@ namespace CodeWalker.GameFiles
public int Index { get; set; }
public ushort[] Indices { get; set; }
public Vector3[] Vertices { get; set; }
public Vector3[]? Vertices { get; set; }
public YnvEdge[] Edges { get; set; }
public ushort[] PortalLinks { get; set; }
@ -831,18 +842,18 @@ namespace CodeWalker.GameFiles
var indices = Ynv.Indices;
var vertices = Ynv.Vertices;
var edges = Ynv.Edges;
if ((indices == null) || (vertices == null) || (edges == null))
{ return; }
if (indices == null || vertices == null || edges == null)
return;
var vc = vertices.Count;
var ic = _RawData.IndexCount;
var startid = _RawData.IndexID;
var endid = startid + ic;
if (startid >= indices.Count)
{ return; }
return;
if (endid > indices.Count)
{ return; }
return;
if (endid > edges.Count)
{ return; }
return;
Indices = new ushort[ic];
Vertices = new Vector3[ic];
@ -864,10 +875,10 @@ namespace CodeWalker.GameFiles
public void LoadPortalLinks()
{
if (PortalLinkCount == 0)
{ return; }
return;
var links = Ynv.Nav?.PortalLinks;
if (links == null)
{ return; }
if (links is null)
return;
var ll = links.Length;
@ -879,16 +890,12 @@ namespace CodeWalker.GameFiles
int idx = offset + i;
PortalLinks[i] = (idx < ll) ? links[idx] : (ushort)0;
}
if (PortalLinkCount != 2)
{ }//debug
}
public void SetPosition(Vector3 pos)
{
Vector3 delta = pos - Position;
//Vector3 delta = pos - Position;
Position = pos;
//TODO: update vertex positions!!!
}
@ -962,17 +969,18 @@ namespace CodeWalker.GameFiles
public void CalculatePosition()
{
if (Vertices is null || Vertices.Length == 0)
{
Position = Vector3.Zero;
return;
}
//calc poly center for display purposes.
Vector3 pcenter = Vector3.Zero;
if (Vertices != null)
for (int i = 0; i < Vertices.Length; i++)
{
for (int i = 0; i < Vertices.Length; i++)
{
pcenter += Vertices[i];
}
pcenter += Vertices[i];
}
float c = ((float)Vertices?.Length);
if (c == 0.0f) c = 1.0f;
float c = Vertices.Length;
Position = pcenter * (1.0f / c);
}
@ -980,18 +988,18 @@ namespace CodeWalker.GameFiles
{
Vector3 min = Vector3.Zero;
Vector3 max = Vector3.Zero;
if ((Vertices != null) && (Vertices.Length > 0))
if (Vertices is not null && Vertices.Length > 0)
{
min = new Vector3(float.MaxValue);
max = new Vector3(float.MinValue);
for (int i = 0; i < Vertices.Length; i++)
{
min = Vector3.Min(min, Vertices[i]);
max = Vector3.Max(max, Vertices[i]);
Vector3.Min(ref min, ref Vertices[i], out min);
Vector3.Max(ref max, ref Vertices[i], out max);
}
}
_RawData.CellAABB = new NavMeshAABB() { Min = min, Max = max };
_RawData.CellAABB = new NavMeshAABB(in min, in max);
}
@ -1005,7 +1013,7 @@ namespace CodeWalker.GameFiles
foreach (var e in Edges)
{
YnvXml.Indent(sb, cind);
sb.AppendFormat("{0}:{1}, {2}:{3}", e.AreaID1, e.PolyID1, e.AreaID2, e.PolyID2);
sb.AppendFormat($"{e.AreaID1}:{e.PolyID1}, {e.AreaID2}:{e.PolyID2}");
sb.AppendLine();
}
YnvXml.CloseTag(sb, indent, "Edges");
@ -1078,9 +1086,12 @@ namespace CodeWalker.GameFiles
public YnvFile Ynv { get; set; }
public NavMeshPortal RawData { get { return _RawData; } set { _RawData = value; } }
public Vector3 Position { get { return PositionFrom; } set { PositionFrom = value; } }
public Vector3 PositionFrom { get; set; }
public Vector3 PositionTo { get; set; }
public ref Vector3 Position => ref PositionFrom;
public Vector3 _PositionFrom;
public ref Vector3 PositionFrom => ref _PositionFrom;
public Vector3 _PositionTo;
public ref Vector3 PositionTo => ref _PositionTo;
public byte Angle { get { return _RawData.Angle; } set { _RawData.Angle = value; } }
public float Direction
@ -1099,7 +1110,7 @@ namespace CodeWalker.GameFiles
get { return Quaternion.RotationAxis(Vector3.UnitZ, Direction); }
set
{
Vector3 dir = value.Multiply(Vector3.UnitX);
Vector3 dir = value.Multiply(in Vector3.UnitX);
float dira = (float)Math.Atan2(dir.Y, dir.X);
Direction = dira;
}
@ -1160,14 +1171,16 @@ namespace CodeWalker.GameFiles
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoint : BasePathNode, IMetaXmlItem
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YnvPoint : BasePathNode, IMetaXmlItem
{
public NavMeshPoint _RawData;
public YnvFile Ynv { get; set; }
public NavMeshPoint RawData { get { return _RawData; } set { _RawData = value; } }
public Vector3 Position { get; set; }
public Vector3 _Position;
public ref Vector3 Position => ref _Position;
public byte Angle { get { return _RawData.Angle; } set { _RawData.Angle = value; } }
public float Direction
{
@ -1185,7 +1198,7 @@ namespace CodeWalker.GameFiles
get { return Quaternion.RotationAxis(Vector3.UnitZ, Direction); }
set
{
Vector3 dir = value.Multiply(Vector3.UnitX);
Vector3 dir = value.Multiply(in Vector3.UnitX);
float dira = (float)Math.Atan2(dir.Y, dir.X);
Direction = dira;
}
@ -1223,18 +1236,16 @@ namespace CodeWalker.GameFiles
Position = Xml.GetChildVector3Attributes(node, "Position");
}
public override string ToString()
{
return Index.ToString() + ": " + Type.ToString();
}
public override string ToString() => $"{Index}: {Type} ({Position})";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class YnvEdge
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YnvEdge
{
public NavMeshEdge _RawData;
public NavMeshEdge RawData { get { return _RawData; } set { _RawData = value; } }
public NavMeshEdge RawData => _RawData;
public YnvFile Ynv { get; set; }
@ -1261,26 +1272,20 @@ namespace CodeWalker.GameFiles
public void Init(YnvFile ynv, NavMeshEdge edge)
{
Ynv = ynv;
RawData = edge;
_RawData = edge;
if (ynv.Nav == null) return;
var n = ynv.Nav;
var ai1 = edge.Poly1.AreaIDInd;
var ai2 = edge.Poly2.AreaIDInd;
var ai1 = edge._Poly1.AreaIDInd;
var ai2 = edge._Poly2.AreaIDInd;
AreaID1 = (ai1 < n.AdjAreaIDs.Count) ? n.AdjAreaIDs.Get(ai1) : 16383;
AreaID2 = (ai2 < n.AdjAreaIDs.Count) ? n.AdjAreaIDs.Get(ai2) : 16383;
}
public override string ToString()
{
return AreaID1.ToString() + ", " + AreaID2.ToString() + ", " + PolyID1.ToString() + ", " + PolyID2.ToString() + ", " +
_RawData._Poly1.Unk2.ToString() + ", " + _RawData._Poly2.Unk2.ToString() + ", " +
_RawData._Poly1.Unk3.ToString() + ", " + _RawData._Poly2.Unk3.ToString();
}
public override string ToString() => $"{AreaID1}, {AreaID2}, {PolyID1}, {PolyID2}, {_RawData._Poly1.Unk2}, {_RawData._Poly2.Unk2}, {_RawData._Poly1.Unk3}, {_RawData.Poly2.Unk3}";
}

View File

@ -38,10 +38,10 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);

View File

@ -32,16 +32,25 @@ namespace CodeWalker.GameFiles
Loaded = true;
}
public async Task LoadAsync(byte[] data)
{
//direct load from a raw, compressed ytd file
await RpfFile.LoadResourceFileAsync(this, data, 13);
Loaded = true;
}
public void Load(byte[] data, RpfFileEntry entry)
{
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);
@ -65,13 +74,8 @@ namespace CodeWalker.GameFiles
return data;
}
new public long MemoryUsage
{
get
{
return TextureDict.MemoryUsage;
}
}
public long PhysicalMemoryUsage => TextureDict.MemoryUsage;
public long VirtualMemoryUsage => 0;
}

View File

@ -1,54 +1,55 @@
using System;
using SharpDX.Win32;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))]
[SkipLocalsInit]
public class YtypFile : GameFile, PackedFile
{
public Meta Meta { get; set; }
public PsoFile Pso { get; set; }
public RbfFile Rbf { get; set; }
public Meta? Meta { get; set; }
public PsoFile? Pso { get; set; }
public RbfFile? Rbf { get; set; }
public uint NameHash { get; set; }
public string[] Strings { get; set; }
public string[] Strings => Meta?.GetStrings() ?? [];
public CMapTypes _CMapTypes;
public CMapTypes CMapTypes { get { return _CMapTypes; } set { _CMapTypes = value; } }
public CMapTypes CMapTypes => _CMapTypes;
public Archetype[] AllArchetypes { get; set; }
public Archetype[] AllArchetypes { get => allArchetypes; set => allArchetypes = value; }
public MetaWrapper[] Extensions { get; set; } = [];
public MetaWrapper[] Extensions { get; set; }
public MloArchetype[] MloArchetypes => allArchetypes.Where(p => p is MloArchetype).Select(p => (p as MloArchetype)!).ToArray();
public CCompositeEntityType[] CompositeEntityTypes { get; set; }
public CCompositeEntityType[] CompositeEntityTypes { get; set; } = [];
//fields used by the editor:
public bool HasChanged { get; set; } = false;
public List<string> SaveWarnings = null;
public List<string>? SaveWarnings = null;
private Archetype[] allArchetypes = [];
public YtypFile() : base(null, GameFileType.Ytyp)
{
}
public YtypFile(RpfFileEntry entry) : base(entry, GameFileType.Ytyp)
{
}
public override string ToString()
{
return (RpfFileEntry != null) ? RpfFileEntry.Name : string.Empty;
}
public override string ToString() => RpfFileEntry?.Name ?? string.Empty;
public byte[] Save()
{
@ -58,30 +59,25 @@ namespace CodeWalker.GameFiles
CMapTypes mapTypes = _CMapTypes;
if (Extensions == null || Extensions.Length <= 0)
if (Extensions.Length == 0)
mapTypes.extensions = new Array_StructurePointer();
else
mapTypes.extensions = mb.AddWrapperArrayPtr(Extensions);
if ((AllArchetypes != null) && (AllArchetypes.Length > 0))
if (AllArchetypes.Length > 0)
{
for (int i = 0; i < AllArchetypes.Length; i++)
MetaPOINTER[] ptrs = new MetaPOINTER[AllArchetypes.Length];
var i = 0;
foreach(var arch in AllArchetypes)
{
var arch = AllArchetypes[i]; //save the extensions first..
if (arch._BaseArchetypeDef.extensions.Count1 > 0)
{
arch._BaseArchetypeDef.extensions = mb.AddWrapperArrayPtr(arch.Extensions);
}
}
MetaPOINTER[] ptrs = new MetaPOINTER[AllArchetypes.Length];
for (int i = 0; i < AllArchetypes.Length; i++)
{
var arch = AllArchetypes[i];
switch (arch)
{
case TimeArchetype t:
ptrs[i] = mb.AddItemPtr(MetaName.CTimeArchetypeDef, t._TimeArchetypeDef);
ptrs[i] = mb.AddItemPtr(MetaName.CTimeArchetypeDef, in t._TimeArchetypeDef);
break;
case MloArchetype m:
try
@ -92,16 +88,17 @@ namespace CodeWalker.GameFiles
m._MloArchetypeDef._MloArchetypeDef.entitySets = mb.AddWrapperArray(m.entitySets);
m._MloArchetypeDef._MloArchetypeDef.timeCycleModifiers = mb.AddItemArrayPtr(MetaName.CMloTimeCycleModifier, m.timeCycleModifiers);
}
catch/* (Exception e)*/
catch(Exception ex)
{
//todo: log save error.
Console.WriteLine(ex);
}
ptrs[i] = mb.AddItemPtr(MetaName.CMloArchetypeDef, m._MloArchetypeDef);
ptrs[i] = mb.AddItemPtr(MetaName.CMloArchetypeDef, in m._MloArchetypeDef);
break;
case Archetype a:
ptrs[i] = mb.AddItemPtr(MetaName.CBaseArchetypeDef, a._BaseArchetypeDef);
ptrs[i] = mb.AddItemPtr(MetaName.CBaseArchetypeDef, in a._BaseArchetypeDef);
break;
}
i++;
}
mapTypes.archetypes = mb.AddPointerArray(ptrs);
}
@ -110,14 +107,14 @@ namespace CodeWalker.GameFiles
mapTypes.archetypes = new Array_StructurePointer();
}
if (CompositeEntityTypes != null && CompositeEntityTypes.Length > 0)
if (CompositeEntityTypes.Length > 0)
{
var cptrs = new MetaPOINTER[CompositeEntityTypes.Length];
for (int i = 0; i < cptrs.Length; i++)
{
var cet = CompositeEntityTypes[i];
cptrs[i] = mb.AddItemPtr(MetaName.CCompositeEntityType, cet);
cptrs[i] = mb.AddItemPtr(MetaName.CCompositeEntityType, in cet);
}
mapTypes.compositeEntityTypes = mb.AddItemArrayPtr(MetaName.CCompositeEntityType, cptrs);
}
@ -127,13 +124,13 @@ namespace CodeWalker.GameFiles
mb.AddStructureInfo(MetaName.CMapTypes);
if ((AllArchetypes != null && AllArchetypes.Length > 0))
if (AllArchetypes is not null && AllArchetypes.Length > 0)
{
mb.AddStructureInfo(MetaName.CBaseArchetypeDef);
mb.AddEnumInfo(MetaName.rage__fwArchetypeDef__eAssetType); // ASSET_TYPE_
}
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is MloArchetype)))
if (AllArchetypes is not null && AllArchetypes.Any(x => x is MloArchetype))
{
mb.AddStructureInfo(MetaName.CMloArchetypeDef);
mb.AddStructureInfo(MetaName.CMloRoomDef);
@ -142,25 +139,24 @@ namespace CodeWalker.GameFiles
mb.AddStructureInfo(MetaName.CMloTimeCycleModifier);
}
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is MloArchetype m && m.entities.Length > 0)))
if (AllArchetypes is not null && AllArchetypes.Any(x => x is MloArchetype m && m.entities.Length > 0))
{
mb.AddStructureInfo(MetaName.CEntityDef);
mb.AddEnumInfo(MetaName.rage__eLodType); //LODTYPES_
mb.AddEnumInfo(MetaName.rage__ePriorityLevel); //PRI_
}
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is TimeArchetype)))
if (AllArchetypes is not null && AllArchetypes.Any(x => x is TimeArchetype))
{
mb.AddStructureInfo(MetaName.CTimeArchetypeDef);
}
if (CompositeEntityTypes?.Length > 0)
if (CompositeEntityTypes.Length > 0)
{
mb.AddStructureInfo(MetaName.CCompositeEntityType);
}
mb.AddItem(MetaName.CMapTypes, mapTypes);
mb.AddItem(MetaName.CMapTypes, in mapTypes);
Meta meta = mb.GetMeta();
byte[] data = ResourceBuilder.Build(meta, 2);
@ -179,101 +175,117 @@ namespace CodeWalker.GameFiles
Loaded = true;
}
public async ValueTask LoadAsync(byte[] data)
{
//direct load from a raw, compressed ytyp file (openIV-compatible format)
await RpfFile.LoadResourceFileAsync(this, data, 2);
Loaded = true;
}
public void Load(byte[] data, RpfFileEntry entry)
{
Name = entry.Name;
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms))
if (RbfFile.IsRBF(data.AsSpan(0, 4)))
{
Rbf = new RbfFile();
Rbf.Load(ms);
Rbf.Load(data);
}
else if (PsoFile.IsPSO(ms))
else if (PsoFile.IsPSO(data.AsSpan(0, 4)))
{
Pso = new PsoFile();
Pso.Load(ms);
//PsoTypes.EnsurePsoTypes(Pso);
}
else
{
Pso.Load(data);
}
return;
}
using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>();
var meta = rd.ReadBlock<Meta>();
Meta = meta;
_CMapTypes = MetaTypes.GetTypedData<CMapTypes>(Meta, MetaName.CMapTypes);
_CMapTypes = MetaTypes.GetTypedData<CMapTypes>(meta, MetaName.CMapTypes);
var ptrs = MetaTypes.GetPointerArray(meta, in _CMapTypes.archetypes);
//List<Archetype> allarchs = new List<Archetype>(ptrs.Length);
List<Archetype> allarchs = new List<Archetype>();
var ptrs = MetaTypes.GetPointerArray(Meta, _CMapTypes.archetypes);
if (ptrs != null)
if (ptrs.Length > 0)
{
Archetype[] allarchs = new Archetype[ptrs.Length];
var count = 0;
for (int i = 0; i < ptrs.Length; i++)
{
var ptr = ptrs[i];
ref var ptr = ref ptrs[i];
var offset = ptr.Offset;
var block = Meta.GetBlock(ptr.BlockID);
if (block == null)
{ continue; }
{
continue;
}
if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
{ continue; }
{
continue;
}
Archetype a = null;
Archetype? a;
switch (block.StructureNameHash)
{
case MetaName.CBaseArchetypeDef:
var basearch = PsoTypes.ConvertDataRaw<CBaseArchetypeDef>(block.Data, offset);
//PsoTypes.TryConvertDataRaw<CBaseArchetypeDef>(block.Data, offset, out var basearch);
a = new Archetype();
a.Init(this, ref basearch);
a.Extensions = MetaTypes.GetExtensions(Meta, basearch.extensions);
a.Init(this, block.Data.AsSpan(offset));
a.Extensions = MetaTypes.GetExtensions(meta, in a._BaseArchetypeDef.extensions) ?? [];
break;
case MetaName.CTimeArchetypeDef:
var timearch = PsoTypes.ConvertDataRaw<CTimeArchetypeDef>(block.Data, offset);
//PsoTypes.TryConvertDataRaw<CTimeArchetypeDef>(block.Data, offset, out var timearch);
var ta = new TimeArchetype();
ta.Init(this, ref timearch);
ta.Extensions = MetaTypes.GetExtensions(Meta, timearch._BaseArchetypeDef.extensions);
ta.Init(this, block.Data.AsSpan(offset));
ta.Extensions = MetaTypes.GetExtensions(meta, in ta._BaseArchetypeDef.extensions) ?? [];
a = ta;
break;
case MetaName.CMloArchetypeDef:
var mloarch = PsoTypes.ConvertDataRaw<CMloArchetypeDef>(block.Data, offset);
//PsoTypes.TryConvertDataRaw<CMloArchetypeDef>(block.Data, offset, out var mloarch);
var ma = new MloArchetype();
ma.Init(this, ref mloarch);
ma.Extensions = MetaTypes.GetExtensions(Meta, mloarch._BaseArchetypeDef.extensions);
ma.Init(this, block.Data.AsSpan(offset));
ma.Extensions = MetaTypes.GetExtensions(meta, in ma._BaseArchetypeDef.extensions) ?? [];
ma.LoadChildren(Meta);
a = ma;
break;
default:
a = null;
continue;
}
if (a != null)
if (a is not null)
{
allarchs.Add(a);
allarchs[count] = a;
count++;
}
}
if (allarchs.Length != count)
{
Console.WriteLine("Resizing array");
Array.Resize(ref allarchs, count);
}
allArchetypes = allarchs;
}
else
{
allArchetypes = [];
}
AllArchetypes = allarchs.ToArray();
Extensions = MetaTypes.GetExtensions(Meta, _CMapTypes.extensions);
if (Extensions != null)
{ }
Extensions = MetaTypes.GetExtensions(Meta, in _CMapTypes.extensions) ?? Array.Empty<MetaWrapper>();
//AudioEmitters = MetaTypes.GetTypedDataArray<CExtensionDefAudioEmitter>(Meta, MetaName.CExtensionDefAudioEmitter);
@ -283,12 +295,10 @@ namespace CodeWalker.GameFiles
//CEntityDefs = MetaTypes.GetTypedDataArray<CEntityDef>(Meta, MetaName.CEntityDef);
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, _CMapTypes.compositeEntityTypes);
if (CompositeEntityTypes != null)
{ }
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, in _CMapTypes.compositeEntityTypes) ?? Array.Empty<CCompositeEntityType>();
NameHash = _CMapTypes.name;
if ((NameHash == 0) && (entry.Name != null))
if (NameHash == 0 && entry.Name is not null)
{
int ind = entry.Name.LastIndexOf('.');
if (ind > 0)
@ -301,16 +311,11 @@ namespace CodeWalker.GameFiles
}
}
Strings = MetaTypes.GetStrings(Meta);
if (Strings != null)
foreach(var str in Strings)
{
foreach (string str in Strings)
{
JenkIndex.Ensure(str); //just shove them in there
}
JenkIndex.Ensure(str); //just shove them in there
}
//foreach (var block in Meta.DataBlocks)
//{
// switch(block.StructureNameHash)
@ -364,22 +369,26 @@ namespace CodeWalker.GameFiles
public void AddArchetype(Archetype archetype)
{
if (AllArchetypes == null)
AllArchetypes = new Archetype[0];
allArchetypes ??= Array.Empty<Archetype>();
List<Archetype> archetypes = AllArchetypes.ToList();
Array.Resize(ref allArchetypes, allArchetypes.Length + 1);
allArchetypes[allArchetypes.Length - 1] = archetype;
archetype.Ytyp = this;
archetypes.Add(archetype);
AllArchetypes = archetypes.ToArray();
}
public Archetype AddArchetype()
{
var a = new Archetype();
a._BaseArchetypeDef.assetType = rage__fwArchetypeDef__eAssetType.ASSET_TYPE_DRAWABLE;
a._BaseArchetypeDef.lodDist = 60;
a._BaseArchetypeDef.hdTextureDist = 15;
a.Ytyp = this;
var a = new Archetype
{
_BaseArchetypeDef = new CBaseArchetypeDef
{
assetType = rage__fwArchetypeDef__eAssetType.ASSET_TYPE_DRAWABLE,
lodDist = 60,
hdTextureDist = 15,
},
Ytyp = this
};
AddArchetype(a);
return a;
}

View File

@ -25,10 +25,10 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);

View File

@ -25,10 +25,10 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry;
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry == null)
if (entry is not RpfResourceFileEntry resentry)
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
ThrowFileIsNotAResourceException();
return;
}
using var rd = new ResourceDataReader(resentry, data);

View File

@ -1,58 +1,159 @@
 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
{
public abstract class GameFile : Cacheable<GameFileCacheKey>
// This would make more sense as a enum, but that would cause lots of bloat casting values
public static class LoadState
{
public volatile bool Loaded = false;
public volatile bool LoadQueued = false;
public const int None = 0;
public const int Loaded = 1;
public const int LoadQueued = 2;
}
public abstract class GameFile : Cacheable<GameFileCacheKey>, IDisposable
{
public byte LoadAttempts = 0;
public int loadState = (int)LoadState.None;
[NotifyParentProperty(true)]
public bool LoadQueued {
get => (loadState & LoadState.LoadQueued) == LoadState.LoadQueued;
set {
if (value)
{
Interlocked.Or(ref loadState, LoadState.LoadQueued);
}
else
{
Interlocked.And(ref loadState, ~LoadState.LoadQueued);
}
}
}
[NotifyParentProperty(true)]
public bool Loaded
{
get => (loadState & LoadState.Loaded) == LoadState.Loaded;
set
{
if (value)
{
Interlocked.Or(ref loadState, LoadState.Loaded);
}
else
{
Interlocked.And(ref loadState, ~LoadState.Loaded);
}
}
}
public DateTime LastLoadTime = DateTime.MinValue;
public RpfFileEntry RpfFileEntry { get; set; }
public RpfFileEntry? RpfFileEntry { get; set; }
public string Name { get; set; }
public string FilePath { get; set; } //used by the project form.
public GameFileType Type { get; set; }
public bool IsDisposed { get; set; } = false;
public GameFile(RpfFileEntry entry, GameFileType type)
public GameFile(RpfFileEntry? entry, GameFileType type)
{
RpfFileEntry = entry;
Type = type;
MemoryUsage = (entry != null) ? entry.GetFileSize() : 0;
if (entry is RpfResourceFileEntry)
if (entry is RpfResourceFileEntry resent)
{
var resent = entry as RpfResourceFileEntry;
var newuse = resent.SystemSize + resent.GraphicsSize;
MemoryUsage = newuse;
}
else if (entry is RpfBinaryFileEntry)
else if (entry is RpfBinaryFileEntry binent)
{
var binent = entry as RpfBinaryFileEntry;
var newuse = binent.FileUncompressedSize;
if (newuse > MemoryUsage)
{
MemoryUsage = newuse;
}
}
}
public bool SetLoadQueued(bool value)
{
if (value)
{
return (Interlocked.Or(ref loadState, LoadState.LoadQueued) & LoadState.LoadQueued) == 0;
}
else
{
return (Interlocked.And(ref loadState, ~LoadState.LoadQueued) & LoadState.LoadQueued) == LoadState.LoadQueued;
}
}
public override string ToString()
public bool SetLoaded(bool value)
{
return (string.IsNullOrEmpty(Name)) ? JenkIndex.GetString(Key.Hash) : Name;
if (value)
{
return (Interlocked.Or(ref loadState, LoadState.Loaded) & LoadState.Loaded) == 0;
}
else
{
return (Interlocked.And(ref loadState, ~LoadState.Loaded) & LoadState.Loaded) == LoadState.Loaded;
}
}
public override string ToString() => string.IsNullOrEmpty(Name) ? JenkIndex.GetString(Key.Hash) : Name;
public virtual void Dispose()
{
IsDisposed = true;
GC.SuppressFinalize(this);
}
[DoesNotReturn]
public static void ThrowFileIsNotAResourceException()
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
}
public class GameFileByPathComparer : IEqualityComparer<GameFile>
{
public static readonly GameFileByPathComparer Instance = new GameFileByPathComparer();
public bool Equals(GameFile? x, GameFile? y)
{
if (x is null && y is null)
return true;
if (x is null || y is null)
return false;
if (ReferenceEquals(x, y))
return true;
if (x.Type != y.Type)
return false;
if (x.RpfFileEntry is null && y.RpfFileEntry is null)
return true;
if (x.RpfFileEntry is null || y.RpfFileEntry is null)
return false;
return x.RpfFileEntry.Path.Equals(y.RpfFileEntry.Path, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode([DisallowNull] GameFile obj)
{
return HashCode.Combine(obj.RpfFileEntry?.Path.GetHashCode(StringComparison.OrdinalIgnoreCase) ?? 0, obj.Type);
}
}
public enum GameFileType : int
public enum GameFileType : byte
{
Ydd = 0,
Ydr = 1,
@ -86,52 +187,12 @@ namespace CodeWalker.GameFiles
Mrf = 29,
DistantLights = 30,
Ypdb = 31,
PedShopMeta = 32,
}
public struct GameFileCacheKey : IEquatable<GameFileCacheKey>
{
public uint Hash { get; set; }
public GameFileType Type { get; set; }
public GameFileCacheKey(uint hash, GameFileType type)
{
Hash = hash;
Type = type;
}
public override readonly bool Equals(object obj)
{
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);
}
public static bool operator !=(GameFileCacheKey first, GameFileCacheKey second)
{
return !first.Equals(second);
}
public override readonly int GetHashCode()
{
return (int)Hash;
}
}
public readonly record struct GameFileCacheKey(uint Hash, GameFileType Type);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,13 @@
using Collections.Pooled;
using SharpDX;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -14,58 +19,59 @@ namespace CodeWalker.GameFiles
public virtual MetaName Type => MetaName.CBaseArchetypeDef;
public CBaseArchetypeDef _BaseArchetypeDef;
public CBaseArchetypeDef BaseArchetypeDef => _BaseArchetypeDef; // for browsing.
public ref CBaseArchetypeDef BaseArchetypeDef => ref _BaseArchetypeDef; // for browsing.
public MetaHash Hash { get; set; }
public YtypFile Ytyp { get; set; }
public MetaHash DrawableDict { get; set; }
public MetaHash TextureDict { get; set; }
public MetaHash ClipDict { get; set; }
public Vector3 BBMin { get; set; }
public Vector3 BBMax { get; set; }
public Vector3 BSCenter { get; set; }
public float BSRadius { get; set; }
public float LodDist { get; set; }
public MetaHash DrawableDict { get => _BaseArchetypeDef.drawableDictionary; set => _BaseArchetypeDef.drawableDictionary = value; }
public MetaHash TextureDict { get => _BaseArchetypeDef.textureDictionary; set => _BaseArchetypeDef.textureDictionary = value; }
public MetaHash ClipDict { get => _BaseArchetypeDef.clipDictionary; set => _BaseArchetypeDef.clipDictionary = value; }
public ref Vector3 BBMin { get => ref _BaseArchetypeDef.bbMin; }
public ref Vector3 BBMax { get => ref _BaseArchetypeDef.bbMax; }
public ref Vector3 BSCenter { get => ref _BaseArchetypeDef.bsCentre; }
public float BSRadius { get => _BaseArchetypeDef.bsRadius; set => _BaseArchetypeDef.bsRadius = value; }
public float LodDist { get => _BaseArchetypeDef.lodDist; set => _BaseArchetypeDef.lodDist = value; }
public MetaWrapper[] Extensions { get; set; }
public string Name
public string Name => _BaseArchetypeDef.name.ToString();
public string AssetName => _BaseArchetypeDef.assetName.ToString();
protected void InitVars()
{
get
{
return _BaseArchetypeDef.name.ToString();
}
}
public string AssetName
{
get
{
return _BaseArchetypeDef.assetName.ToString();
}
Hash = _BaseArchetypeDef.assetName;
if (Hash.Hash == 0)
Hash = _BaseArchetypeDef.name;
//DrawableDict = _BaseArchetypeDef.drawableDictionary;
//TextureDict = _BaseArchetypeDef.textureDictionary;
//ClipDict = _BaseArchetypeDef.clipDictionary;
//BBMin = _BaseArchetypeDef.bbMin;
//BBMax = _BaseArchetypeDef.bbMax;
//BSCenter = _BaseArchetypeDef.bsCentre;
//BSRadius = _BaseArchetypeDef.bsRadius;
//LodDist = _BaseArchetypeDef.lodDist;
}
protected void InitVars(ref CBaseArchetypeDef arch)
protected void InitVars(in CBaseArchetypeDef arch)
{
_BaseArchetypeDef = arch;
Hash = arch.assetName;
if (Hash.Hash == 0) Hash = arch.name;
DrawableDict = arch.drawableDictionary;
TextureDict = arch.textureDictionary;
ClipDict = arch.clipDictionary;
BBMin = arch.bbMin;
BBMax = arch.bbMax;
BSCenter = arch.bsCentre;
BSRadius = arch.bsRadius;
LodDist = arch.lodDist;
InitVars();
}
public void Init(YtypFile ytyp, ref CBaseArchetypeDef arch)
public void Init(YtypFile ytyp, in CBaseArchetypeDef arch)
{
Ytyp = ytyp;
InitVars(ref arch);
InitVars(in arch);
}
public void Init(YtypFile ytyp, Span<byte> data)
{
Ytyp = ytyp;
PsoTypes.TryConvertDataRaw(data, out _BaseArchetypeDef);
InitVars();
}
public virtual bool IsActive(float hour)
@ -79,77 +85,57 @@ namespace CodeWalker.GameFiles
}
}
public readonly struct ActiveHours(uint flags)
{
public readonly uint TimeFlags = flags;
public readonly bool this[int index] => ((TimeFlags >> index) & 1) == 1;
public static implicit operator ActiveHours(uint d) => new (d);
public static implicit operator uint(ActiveHours d) => d.TimeFlags;
}
[TypeConverter(typeof(ExpandableObjectConverter))]
[SkipLocalsInit]
public class TimeArchetype : Archetype
{
public override MetaName Type => MetaName.CTimeArchetypeDef;
public CTimeArchetypeDef _TimeArchetypeDef;
public CTimeArchetypeDef TimeArchetypeDef => _TimeArchetypeDef; // for browsing.
public uint TimeFlags { get; set; }
public bool[] ActiveHours { get; set; }
private readonly Lazy<string[]> _activeHoursText;
public string[] ActiveHoursText { get => _activeHoursText.Value; }
public bool ExtraFlag { get { return ((TimeFlags >> 24) & 1) == 1; } }
public ActiveHours ActiveHours;
public bool ExtraFlag => ActiveHours[24];
public TimeArchetype()
{
_activeHoursText = new Lazy<string[]>(() =>
{
var activeHoursText = new string[24];
for (int i = 0; i < ActiveHours.Length; i++)
{
var nxth = (i < 23) ? (i + 1) : 0;
var hrs = string.Format("{0:00}:00 - {1:00}:00", i, nxth);
activeHoursText[i] = (hrs + (ActiveHours[i] ? " - On" : " - Off"));
}
{ }
return activeHoursText;
});
}
public void Init(YtypFile ytyp, ref CTimeArchetypeDef arch)
public void Init(YtypFile ytyp, in CTimeArchetypeDef arch)
{
Ytyp = ytyp;
InitVars(ref arch._BaseArchetypeDef);
InitVars(in arch._BaseArchetypeDef);
_TimeArchetypeDef = arch;
ActiveHours = arch._TimeArchetypeDef.timeFlags;
}
TimeFlags = arch.TimeArchetypeDef.timeFlags;
UpdateActiveHours();
new public void Init(YtypFile ytyp, Span<byte> data)
{
Ytyp = ytyp;
PsoTypes.TryConvertDataRaw(data, out _TimeArchetypeDef);
InitVars(in _TimeArchetypeDef._BaseArchetypeDef);
}
public override bool IsActive(float hour)
{
if (ActiveHours == null) return true;
int h = ((int)hour) % 24;
if ((h < 0) || (h > 23)) return true;
if ((h < 0) || (h > 23))
return true;
return ActiveHours[h];
}
public void UpdateActiveHours()
{
if (ActiveHours == null)
{
ActiveHours = new bool[24];
}
for (int i = 0; i < 24; i++)
{
bool v = ((TimeFlags >> i) & 1) == 1;
ActiveHours[i] = v;
}
}
public void SetTimeFlags(uint flags)
{
TimeFlags = flags;
ActiveHours = flags;
_TimeArchetypeDef._TimeArchetypeDef.timeFlags = flags;
UpdateActiveHours();
}
}
public class MloArchetype : Archetype
@ -159,7 +145,7 @@ namespace CodeWalker.GameFiles
public CMloArchetypeDef MloArchetypeDef => _MloArchetypeDef; // for browsing.
public CMloArchetypeDef _MloArchetypeDef;
public CMloArchetypeDefData _MloArchetypeDefData;
public ref CMloArchetypeDefData _MloArchetypeDefData => ref _MloArchetypeDef._MloArchetypeDef;
public MCEntityDef[] entities { get; set; }
public MCMloRoomDef[] rooms { get; set; }
@ -168,17 +154,27 @@ namespace CodeWalker.GameFiles
public CMloTimeCycleModifier[] timeCycleModifiers { get; set; }
public void Init(YtypFile ytyp, ref CMloArchetypeDef arch)
public void Init(YtypFile ytyp, in CMloArchetypeDef arch)
{
Ytyp = ytyp;
InitVars(ref arch._BaseArchetypeDef);
InitVars(in arch._BaseArchetypeDef);
_MloArchetypeDef = arch;
_MloArchetypeDefData = arch.MloArchetypeDef;
//_MloArchetypeDefData = ref arch._MloArchetypeDef;
}
public void Init(YtypFile ytyp, Span<byte> data)
{
Ytyp = ytyp;
PsoTypes.TryConvertDataRaw(data, out _MloArchetypeDef);
InitVars(in _MloArchetypeDef._BaseArchetypeDef);
//_MloArchetypeDefData = ref _MloArchetypeDef._MloArchetypeDef;
}
public bool AddEntity(YmapEntityDef ent, int roomIndex, int portalIndex = -1, int entsetIndex = -1)
{
if (ent == null) return false;
if (ent is null)
return false;
if (roomIndex >= (rooms?.Length ?? 0))
{
@ -193,7 +189,7 @@ namespace CodeWalker.GameFiles
throw new ArgumentOutOfRangeException($"EntitySet index {entsetIndex} exceeds the amount of entitySets in {Name}.");
}
var mcEntityDef = new MCEntityDef(ref ent._CEntityDef, this);
var mcEntityDef = new MCEntityDef(in ent._CEntityDef, this);
if ((roomIndex >= 0) || (portalIndex >= 0))
@ -243,7 +239,8 @@ namespace CodeWalker.GameFiles
}
public bool RemoveEntity(YmapEntityDef ent)
{
if (ent == null) return false;
if (ent is null)
return false;
if ((ent.MloEntitySet?.Entities != null) && (ent.MloEntitySet?.EntitySet != null))
{
@ -305,7 +302,8 @@ namespace CodeWalker.GameFiles
public void AddRoom(MCMloRoomDef room)
{
if (room == null) return;
if (room is null)
return;
room.OwnerMlo = this;
room.Index = rooms?.Length ?? 0;
@ -316,7 +314,8 @@ namespace CodeWalker.GameFiles
}
public void RemoveRoom(MCMloRoomDef room)
{
if (room == null) return;
if (room is null)
return;
var newrooms = rooms.ToList();
newrooms.Remove(room);
@ -332,12 +331,13 @@ namespace CodeWalker.GameFiles
public void AddPortal(MCMloPortalDef portal)
{
if (portal == null) return;
if (portal is null)
return;
portal.OwnerMlo = this;
portal.Index = portals?.Length ?? 0;
var newportals = portals?.ToList() ?? new List<MCMloPortalDef>();
using var newportals = portals?.ToPooledList() ?? new PooledList<MCMloPortalDef>();
newportals.Add(portal);
portals = newportals.ToArray();
@ -345,9 +345,10 @@ namespace CodeWalker.GameFiles
}
public void RemovePortal(MCMloPortalDef portal)
{
if (portal == null) return;
if (portal is null)
return;
var newportals = portals.ToList();
using var newportals = portals.ToPooledList();
newportals.Remove(portal);
portals = newportals.ToArray();
@ -361,7 +362,8 @@ namespace CodeWalker.GameFiles
public void AddEntitySet(MCMloEntitySet set)
{
if (set == null) return;
if (set is null)
return;
set.OwnerMlo = this;
set.Index = entitySets?.Length ?? 0;
@ -372,7 +374,8 @@ namespace CodeWalker.GameFiles
}
public void RemoveEntitySet(MCMloEntitySet set)
{
if (set == null) return;
if (set is null)
return;
var newsets = entitySets.ToList();
newsets.Remove(set);
@ -393,17 +396,19 @@ namespace CodeWalker.GameFiles
{
foreach (var portal in portals)
{
if (portal.AttachedObjects == null || portal.AttachedObjects.Length == 0)
if (portal.AttachedObjects is null || portal.AttachedObjects.Length == 0)
continue;
List<uint> newAttachedObjects = new List<uint>();
using PooledList<uint> newAttachedObjects = new PooledList<uint>(portal.AttachedObjects.Length);
foreach (var objIndex in portal.AttachedObjects)
{
if (objIndex == deletedIndex) continue;
if (objIndex == deletedIndex)
continue;
if (objIndex > deletedIndex)
newAttachedObjects.Add(objIndex - 1); // move the index back so it matches the index in the entitiy array.
else newAttachedObjects.Add(objIndex); // else just add the index to the attached objects.
}
portal.AttachedObjects = newAttachedObjects.ToArray();
}
}
@ -411,13 +416,14 @@ namespace CodeWalker.GameFiles
{
foreach (var room in rooms)
{
if (room.AttachedObjects == null || room.AttachedObjects.Length == 0)
if (room.AttachedObjects is null || room.AttachedObjects.Length == 0)
continue;
List<uint> newAttachedObjects = new List<uint>();
using PooledList<uint> newAttachedObjects = new PooledList<uint>(room.AttachedObjects.Length);
foreach (var objIndex in room.AttachedObjects)
{
if (objIndex == deletedIndex) continue;
if (objIndex == deletedIndex)
continue;
if (objIndex > deletedIndex)
newAttachedObjects.Add(objIndex - 1); // move the index back so it matches the index in the entitiy array.
else newAttachedObjects.Add(objIndex); // else just add the index to the attached objects.
@ -452,14 +458,15 @@ namespace CodeWalker.GameFiles
public void UpdatePortalCounts()
{
if ((rooms == null) || (portals == null)) return;
if (rooms is null || portals is null)
return;
foreach (var room in rooms)
{
uint count = 0;
foreach (var portal in portals)
{
if ((portal._Data.roomFrom == room.Index) || (portal._Data.roomTo == room.Index))
if (portal._Data.roomFrom == room.Index || portal._Data.roomTo == room.Index)
{
count++;
}
@ -472,50 +479,65 @@ namespace CodeWalker.GameFiles
public void LoadChildren(Meta meta)
{
var centities = MetaTypes.ConvertDataArray<CEntityDef>(meta, MetaName.CEntityDef, _MloArchetypeDefData.entities);
if (centities != null)
if (_MloArchetypeDefData.entities.Count1 > 0)
{
var centitiesBuffer = ArrayPool<CEntityDef>.Shared.Rent(_MloArchetypeDefData.entities.Count1);
MetaTypes.ConvertDataArray(meta, MetaName.CEntityDef, in _MloArchetypeDefData.entities, centitiesBuffer);
var centities = centitiesBuffer.AsSpan(0, _MloArchetypeDefData.entities.Count1);
entities = new MCEntityDef[centities.Length];
for (int i = 0; i < centities.Length; i++)
{
entities[i] = new MCEntityDef(meta, ref centities[i]) { OwnerMlo = this, Index = i };
entities[i] = new MCEntityDef(meta, centities[i]) { OwnerMlo = this, Index = i };
}
ArrayPool<CEntityDef>.Shared.Return(centitiesBuffer);
}
var crooms = MetaTypes.ConvertDataArray<CMloRoomDef>(meta, MetaName.CMloRoomDef, _MloArchetypeDefData.rooms);
if (crooms != null)
if (_MloArchetypeDefData.rooms.Count1 > 0)
{
var croomsBuffer = ArrayPool<CMloRoomDef>.Shared.Rent(_MloArchetypeDefData.rooms.Count1);
MetaTypes.ConvertDataArray(meta, MetaName.CMloRoomDef, in _MloArchetypeDefData.rooms, croomsBuffer);
var crooms = croomsBuffer.AsSpan(0, _MloArchetypeDefData.rooms.Count1);
rooms = new MCMloRoomDef[crooms.Length];
for (int i = 0; i < crooms.Length; i++)
{
rooms[i] = new MCMloRoomDef(meta, crooms[i]) { OwnerMlo = this, Index = i };
}
ArrayPool<CMloRoomDef>.Shared.Return(croomsBuffer);
}
var cportals = MetaTypes.ConvertDataArray<CMloPortalDef>(meta, MetaName.CMloPortalDef, _MloArchetypeDefData.portals);
if (cportals != null)
if (_MloArchetypeDefData.portals.Count1 > 0)
{
var cportalsBuffer = ArrayPool<CMloPortalDef>.Shared.Rent(_MloArchetypeDefData.portals.Count1);
MetaTypes.ConvertDataArray(meta, MetaName.CMloPortalDef, in _MloArchetypeDefData.portals, cportalsBuffer);
var cportals = cportalsBuffer.AsSpan(0, _MloArchetypeDefData.portals.Count1);
portals = new MCMloPortalDef[cportals.Length];
for (int i = 0; i < cportals.Length; i++)
{
portals[i] = new MCMloPortalDef(meta, cportals[i]) { OwnerMlo = this, Index = i };
}
ArrayPool<CMloPortalDef>.Shared.Return(cportalsBuffer);
}
var centitySets = MetaTypes.ConvertDataArray<CMloEntitySet>(meta, MetaName.CMloEntitySet, _MloArchetypeDefData.entitySets);
if (centitySets != null)
if (_MloArchetypeDefData.entitySets.Count1 > 0)
{
var centitySetsBuffer = ArrayPool<CMloEntitySet>.Shared.Rent(_MloArchetypeDefData.entitySets.Count1);
MetaTypes.ConvertDataArray(meta, MetaName.CMloEntitySet, in _MloArchetypeDefData.entitySets, centitySetsBuffer);
var centitySets = centitySetsBuffer.AsSpan(0, _MloArchetypeDefData.entitySets.Count1);
entitySets = new MCMloEntitySet[centitySets.Length];
for (int i = 0; i < centitySets.Length; i++)
{
entitySets[i] = new MCMloEntitySet(meta, centitySets[i], this) { OwnerMlo = this, Index = i };
entitySets[i] = new MCMloEntitySet(meta, centitySets[i], this) { Index = i };
}
ArrayPool<CMloEntitySet>.Shared.Return(centitySetsBuffer);
UpdateAllEntityIndexes();
}
timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(meta, MetaName.CMloTimeCycleModifier, _MloArchetypeDefData.timeCycleModifiers);
if (_MloArchetypeDefData.timeCycleModifiers.Count1 > 0)
{
timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(meta, MetaName.CMloTimeCycleModifier, in _MloArchetypeDefData.timeCycleModifiers);
}
}
public int GetEntityObjectIndex(MCEntityDef ent)
@ -531,7 +553,7 @@ namespace CodeWalker.GameFiles
}
return -1;
}
public MCMloRoomDef GetEntityRoom(MCEntityDef ent)
public MCMloRoomDef? GetEntityRoom(MCEntityDef ent)
{
if (rooms == null)
return null;
@ -558,17 +580,19 @@ namespace CodeWalker.GameFiles
return null;
}
public MCMloPortalDef GetEntityPortal(MCEntityDef ent)
public MCMloPortalDef? GetEntityPortal(MCEntityDef ent)
{
if (portals == null) return null;
if (portals is null)
return null;
int objectIndex = GetEntityObjectIndex(ent);
if (objectIndex < 0) return null;
if (objectIndex < 0)
return null;
for (int i = 0; i < portals.Length; i++)
{
var p = portals[i];
if (p.AttachedObjects != null)
if (p.AttachedObjects is not null)
{
for (int j = 0; j < p.AttachedObjects.Length; j++)
{
@ -583,7 +607,7 @@ namespace CodeWalker.GameFiles
return null;
}
public MCMloEntitySet GetEntitySet(MCEntityDef ent)
public MCMloEntitySet? GetEntitySet(MCEntityDef ent)
{
if (entitySets == null) return null;
@ -613,7 +637,7 @@ namespace CodeWalker.GameFiles
public YmapEntityDef Owner { get; set; }
public MloArchetype MloArch { get; set; }
public CMloInstanceDef _Instance;
public CMloInstanceDef Instance { get { return _Instance; } set { _Instance = value; } }
public ref CMloInstanceDef Instance => ref _Instance;
public uint[] defaultEntitySets { get; set; }
public YmapEntityDef[] Entities { get; set; }
@ -627,11 +651,13 @@ namespace CodeWalker.GameFiles
public void CreateYmapEntities()
{
if (Owner == null) return;
if (MloArch?.entities == null) return;
if (Owner is null)
return;
if (MloArch?.entities is null)
return;
var ec = MloArch.entities.Length;
var entlist = new List<YmapEntityDef>();
var entlist = new List<YmapEntityDef>(ec);
for (int i = 0; i < ec; i++)
{
var e = CreateYmapEntity(Owner, MloArch.entities[i], i);
@ -662,7 +688,7 @@ namespace CodeWalker.GameFiles
}
}
if ((defaultEntitySets != null) && (EntitySets != null))
if (defaultEntitySets != null && EntitySets != null)
{
for (var i = 0; i < defaultEntitySets.Length; i++)
{
@ -678,19 +704,19 @@ namespace CodeWalker.GameFiles
public void InitYmapEntityArchetypes(GameFileCache gfc)
{
if (Owner == null) return;
if (Owner is null)
return;
var arch = Owner.Archetype;
if (Entities != null)
if (Entities is not null)
{
for (int j = 0; j < Entities.Length; j++)
foreach(var ient in Entities)
{
var ient = Entities[j];
var iarch = gfc.GetArchetype(ient._CEntityDef.archetypeName);
if (iarch == null)
if (iarch is null)
{
Console.WriteLine($"Can't find archetype for {ient._CEntityDef.archetypeName}!");
Console.WriteLine($"Can't find archetype for {ient._CEntityDef.archetypeName} for ymap {Owner.Name}!");
}
else
{
@ -701,23 +727,22 @@ namespace CodeWalker.GameFiles
UpdateBBs(arch);
}
if (EntitySets != null)
if (EntitySets is not null)
{
for (int e = 0; e < EntitySets.Length; e++)
foreach(var entitySet in EntitySets)
{
var entitySet = EntitySets[e];
var entities = entitySet.Entities;
if (entities == null) continue;
if (entities is null)
continue;
for (int i = 0; i < entities.Count; i++)
{
var ient = entities[i];
var iarch = gfc.GetArchetype(ient._CEntityDef.archetypeName);
ient.SetArchetype(iarch);
if (iarch == null)
if (iarch is null)
{
Console.WriteLine($"Couldn't find archetype {ient._CEntityDef.archetypeName}");
Console.WriteLine($"Couldn't find archetype {ient._CEntityDef.archetypeName} ({ient._CEntityDef.archetypeName.Hash}) for ymap {Owner.Name}");
}
else
{
@ -731,18 +756,16 @@ namespace CodeWalker.GameFiles
public void UpdateBBs(Archetype arch)
{
//update archetype room AABB's.. bad to have this here? where else to put it?
var mloa = arch as MloArchetype;
if (mloa != null)
if (arch is MloArchetype mloa)
{
Vector3 mlobbmin = Vector3.Zero;
Vector3 mlobbmax = Vector3.Zero;
Vector3[] c = new Vector3[8];
var rooms = mloa.rooms;
if (rooms != null)
if (rooms is not null)
{
for (int j = 0; j < rooms.Length; j++)
foreach(var room in rooms)
{
var room = rooms[j];
if (room.AttachedObjects == null || room.AttachedObjects.Length == 0)
continue;
Vector3 min = new Vector3(float.MaxValue);
@ -770,9 +793,9 @@ namespace CodeWalker.GameFiles
c[7] = abmax;
for (int n = 0; n < 8; n++)
{
Vector3 corn = ori.Multiply(c[n]) + pos;
min = Vector3.Min(min, corn);
max = Vector3.Max(max, corn);
Vector3 corn = ori.Multiply(in c[n]) + pos;
Vectors.Min(in min, in corn, out min);
Vectors.Max(in max, in corn, out max);
}
}
}
@ -790,7 +813,7 @@ namespace CodeWalker.GameFiles
public YmapEntityDef CreateYmapEntity(YmapEntityDef owner, MCEntityDef ment, int index)
{
YmapEntityDef e = new YmapEntityDef(null, index, ref ment._Data);
YmapEntityDef e = new YmapEntityDef(null, index, ment._Data);
e.Extensions = ment.Extensions;
e.MloRefPosition = e.Position;
e.MloRefOrientation = e.Orientation;
@ -803,11 +826,14 @@ namespace CodeWalker.GameFiles
return e;
}
public MCEntityDef TryGetArchetypeEntity(YmapEntityDef ymapEntity)
public MCEntityDef? TryGetArchetypeEntity(YmapEntityDef ymapEntity)
{
if (ymapEntity == null) return null;
if (Owner?.Archetype == null) return null;
if (!(Owner.Archetype is MloArchetype mloa)) return null;
if (ymapEntity is null)
return null;
if (Owner?.Archetype is null)
return null;
if (Owner.Archetype is not MloArchetype mloa)
return null;
var index = Array.FindIndex(Entities, x => x == ymapEntity);
if ((index >= 0) && (index < mloa.entities.Length))
@ -817,13 +843,13 @@ namespace CodeWalker.GameFiles
if (EntitySets != null)
{
for (int e = 0; e < EntitySets.Length; e++)
foreach(var entset in EntitySets)
{
var entset = EntitySets[e];
var ents = entset.Entities;
var set = entset.EntitySet;
var setents = set?.Entities;
if ((ents == null) || (setents == null)) continue;
if (ents is null || setents is null)
continue;
var idx = ents.IndexOf(ymapEntity);
if ((idx >= 0) && (idx < setents.Length))
{
@ -835,11 +861,14 @@ namespace CodeWalker.GameFiles
return null;
}
public YmapEntityDef TryGetYmapEntity(MCEntityDef mcEntity)
public YmapEntityDef? TryGetYmapEntity(MCEntityDef mcEntity)
{
if (mcEntity == null) return null;
if (Owner?.Archetype == null) return null;
if (!(Owner.Archetype is MloArchetype mloa)) return null;
if (mcEntity is null)
return null;
if (Owner?.Archetype is null)
return null;
if (Owner.Archetype is not MloArchetype mloa)
return null;
var index = Array.FindIndex(mloa.entities, x => x == mcEntity);
if ((index >= 0) && (index < Entities.Length))
@ -847,15 +876,15 @@ namespace CodeWalker.GameFiles
return Entities[index];
}
if (EntitySets != null)
if (EntitySets is not null)
{
for (int e = 0; e < EntitySets.Length; e++)
foreach(var entset in EntitySets)
{
var entset = EntitySets[e];
var ents = entset.Entities;
var set = entset.EntitySet;
var setents = set?.Entities;
if ((ents == null) || (setents == null)) continue;
if (ents is null || setents is null)
continue;
var idx = Array.FindIndex(setents, x => x == mcEntity);
if ((idx >= 0) && (idx < ents.Count))
{
@ -883,21 +912,21 @@ namespace CodeWalker.GameFiles
public void UpdateEntities()
{
if (Entities == null) return;
if (Owner == null) return;
if (Entities is null)
return;
if (Owner is null)
return;
for (int i = 0; i < Entities.Length; i++)
foreach(var ent in Entities)
{
var ent = Entities[i];
UpdateEntity(ent);
}
if (EntitySets != null)
if (EntitySets != null && EntitySets.Length > 0)
{
for (int e = 0; e < EntitySets.Length; e++)
foreach(var entset in EntitySets)
{
var entset = EntitySets[e];
if (entset?.Entities != null)
if (entset?.Entities is not null)
{
for (int i = 0; i < entset.Entities.Count; i++)
{
@ -921,15 +950,16 @@ namespace CodeWalker.GameFiles
public void AddEntity(YmapEntityDef e)
{
if (e == null) return;
if (e is null)
return;
if (e.MloEntitySet != null)
if (e.MloEntitySet is not null)
{
e.MloEntitySet.AddEntity(e);
}
else
{
if (Entities == null) Entities = new YmapEntityDef[0];
Entities ??= Array.Empty<YmapEntityDef>();
var entities = Entities.ToList();
entities.Add(e);
Entities = entities.ToArray();
@ -937,21 +967,21 @@ namespace CodeWalker.GameFiles
UpdateAllEntityIndexes();
}
public bool DeleteEntity(YmapEntityDef ent)
public bool DeleteEntity(YmapEntityDef entityToRemove)
{
var del = false;
if (ent.MloEntitySet != null)
if (entityToRemove.MloEntitySet is not null)
{
del = ent.MloEntitySet.DeleteEntity(ent);
del = entityToRemove.MloEntitySet.DeleteEntity(entityToRemove);
UpdateAllEntityIndexes();
return del;
}
if (Entities == null)
if (Entities is null)
{
throw new NullReferenceException("The Entities list returned null in our MloInstanceData. This could be an issue with initialization. The MloInstance probably doesn't exist.");
}
if (ent.Index >= Entities.Length)
if (entityToRemove.Index >= Entities.Length)
{
throw new ArgumentOutOfRangeException("The index of the entity was greater than the amount of entities that exist in this MloInstance. Likely an issue with initializion.");
}
@ -959,14 +989,14 @@ namespace CodeWalker.GameFiles
int index = 0;
var newentities = new YmapEntityDef[Entities.Length - 1];
for (int i = 0; i < Entities.Length; i++)
{
if (i == ent.Index)
foreach(var thisEnt in Entities)
{
if (thisEnt.Index == entityToRemove.Index)
{
del = true;
continue;
}
newentities[index] = Entities[i];
newentities[index] = thisEnt;
newentities[index].Index = index;
index++;
}
@ -982,15 +1012,16 @@ namespace CodeWalker.GameFiles
public void AddEntitySet(MCMloEntitySet set)
{
var instset = new MloInstanceEntitySet(set, this);
var esets = EntitySets?.ToList() ?? new List<MloInstanceEntitySet>();
using var esets = EntitySets?.ToPooledList() ?? new PooledList<MloInstanceEntitySet>();
esets.Add(instset);
EntitySets = esets.ToArray();
}
public bool DeleteEntitySet(MCMloEntitySet set)
{
if (EntitySets == null) return false;
var esets = EntitySets.ToList();
if (EntitySets is null)
return false;
using var esets = EntitySets.ToPooledList();
var rem = esets.RemoveAll(s => s.EntitySet == set);
EntitySets = esets.ToArray();
UpdateAllEntityIndexes();
@ -1022,22 +1053,23 @@ namespace CodeWalker.GameFiles
private void UpdateAllEntityIndexes()
{
var index = 0;
if (Entities != null)
if (Entities is not null)
{
for (int i = 0; i < Entities.Length; i++)
foreach(var ent in Entities)
{
Entities[i].Index = index++;
ent.Index = index++;
}
}
if (EntitySets != null)
if (EntitySets is not null)
{
for (int e = 0; e < EntitySets.Length; e++)
foreach(var set in EntitySets)
{
var set = EntitySets[e];
if (set?.Entities == null) continue;
for (int i = 0; i < set.Entities.Count; i++)
if (set?.Entities is null)
continue;
foreach(var ent in set.Entities)
{
set.Entities[i].Index = index++;
ent.Index = index++;
}
}
}
@ -1060,8 +1092,13 @@ namespace CodeWalker.GameFiles
public uint[] Locations
{
get { return EntitySet?.Locations; }
set { if (EntitySet != null) EntitySet.Locations = value; }
get {
return EntitySet?.Locations;
}
set {
if (EntitySet != null)
EntitySet.Locations = value;
}
}
public bool Visible { get; set; }
@ -1069,8 +1106,10 @@ namespace CodeWalker.GameFiles
{
get
{
if (Visible) return true;
if (EntitySet == null) return false;
if (Visible)
return true;
if (EntitySet == null)
return false;
return EntitySet.ForceVisible;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ namespace CodeWalker.GameFiles
public class MetaBuilder
{
List<MetaBuilderBlock> Blocks = new List<MetaBuilderBlock>();
readonly List<MetaBuilderBlock> Blocks = new List<MetaBuilderBlock>();
int MaxBlockLength = 0x4000; //TODO: figure what this should be!
@ -31,16 +31,18 @@ namespace CodeWalker.GameFiles
}
public MetaBuilderBlock AddBlock(MetaName type)
{
MetaBuilderBlock b = new MetaBuilderBlock();
b.StructureNameHash = type;
b.Index = Blocks.Count;
MetaBuilderBlock b = new MetaBuilderBlock
{
StructureNameHash = type,
Index = Blocks.Count
};
Blocks.Add(b);
return b;
}
public MetaBuilderPointer AddItem<T>(MetaName type, T item) where T : struct
public MetaBuilderPointer AddItem<T>(MetaName type, in T item) where T : struct
{
byte[] data = MetaTypes.ConvertToBytes(item);
byte[] data = MetaTypes.ConvertToBytes(in item);
return AddItem(type, data);
}
@ -56,11 +58,11 @@ namespace CodeWalker.GameFiles
data = newdata; //make sure item size is multiple of 16... so pointers don't need sub offsets!
}
int idx = block.AddItem(data);
MetaBuilderPointer r = new MetaBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = (idx * data.Length);
r.Length = data.Length;
return r;
return new MetaBuilderPointer(
blockId: block.Index + 1,
offset: (idx * data.Length),
length: data.Length
);
}
public MetaBuilderPointer AddItemArray<T>(MetaName type, T[] items) where T : struct
@ -82,12 +84,13 @@ namespace CodeWalker.GameFiles
byte[] newdata = new byte[newlen];
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
int offs = block.TotalSize;
int idx = block.AddItem(newdata);
MetaBuilderPointer r = new MetaBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = offs; //(idx * data.Length);;
r.Length = length;
return r;
_ = block.AddItem(newdata);
return new MetaBuilderPointer
(
blockId: block.Index + 1,
offset: offs, //(idx * data.Length);;
length: length
);
}
public MetaBuilderPointer AddString(string str)
@ -99,17 +102,18 @@ namespace CodeWalker.GameFiles
byte[] newdata = new byte[newlen];
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
int offs = block.TotalSize;
int idx = block.AddItem(newdata);
MetaBuilderPointer r = new MetaBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = offs;// (idx * data.Length);
r.Length = datalen; //actual length of string. (not incl null terminator)
return r;
_ = block.AddItem(newdata);
return new MetaBuilderPointer
(
blockId: block.Index + 1,
offset: offs,// (idx * data.Length);
length: datalen //actual length of string. (not incl null terminator)
);
}
public MetaPOINTER AddItemPtr<T>(MetaName type, T item) where T : struct //helper method for AddItem<T>
public MetaPOINTER AddItemPtr<T>(MetaName type, in T item) where T : struct //helper method for AddItem<T>
{
var ptr = AddItem(type, item);
var ptr = AddItem(type, in item);
return new MetaPOINTER(ptr.BlockID, ptr.Offset);
}
@ -121,14 +125,16 @@ namespace CodeWalker.GameFiles
public Array_Structure AddItemArrayPtr<T>(MetaName type, T[] items) where T : struct //helper method for AddItemArray<T>
{
if ((items == null) || (items.Length == 0)) return new Array_Structure();
if (items is null || items.Length == 0)
return new Array_Structure();
var ptr = AddItemArray(type, items);
return new Array_Structure(ptr);
}
public Array_Structure AddItemArrayPtr(MetaName type, byte[][] data) //helper method for AddItemArray<T>
{
if ((data == null) || (data.Length == 0)) return new Array_Structure();
if ((data?.Length ?? 0) == 0)
return new Array_Structure();
int len = 0;
@ -153,40 +159,43 @@ namespace CodeWalker.GameFiles
public Array_Vector3 AddPaddedVector3ArrayPtr(Vector4[] items)
{
if (items == null || items.Length == 0)
if ((items?.Length ?? 0) == 0)
return new Array_Vector3();
var ptr = AddItemArray((MetaName)MetaTypeName.VECTOR4, items); //padded to vec4...
return new Array_Vector3(ptr);
}
public Array_uint AddHashArrayPtr(MetaHash[] items)
{
if (items == null || items.Length == 0)
if ((items?.Length ?? 0) == 0)
return new Array_uint();
var ptr = AddItemArray((MetaName)MetaTypeName.HASH, items);
return new Array_uint(ptr);
}
public Array_uint AddUintArrayPtr(uint[] items)
{
if (items == null || items.Length == 0)
if ((items?.Length ?? 0) == 0)
return new Array_uint();
var ptr = AddItemArray((MetaName)MetaTypeName.UINT, items);
return new Array_uint(ptr);
}
public Array_ushort AddUshortArrayPtr(ushort[] items)
{
if ((items == null) || (items.Length == 0)) return new Array_ushort();
if ((items?.Length ?? 0) == 0)
return new Array_ushort();
var ptr = AddItemArray((MetaName)MetaTypeName.USHORT, items);
return new Array_ushort(ptr);
}
public Array_byte AddByteArrayPtr(byte[] items)
{
if ((items == null) || (items.Length == 0)) return new Array_byte();
if ((items?.Length ?? 0) == 0)
return new Array_byte();
var ptr = AddItemArray((MetaName)MetaTypeName.BYTE, items);
return new Array_byte(ptr);
}
public Array_float AddFloatArrayPtr(float[] items)
{
if ((items == null) || (items.Length == 0)) return new Array_float();
if ((items?.Length ?? 0) == 0)
return new Array_float();
var ptr = AddItemArray((MetaName)MetaTypeName.FLOAT, items);
return new Array_float(ptr);
}
@ -199,55 +208,45 @@ namespace CodeWalker.GameFiles
{
var block = AddBlock(type);
int offs = block.TotalSize;//should always be 0...
int idx = block.AddItem(data);
_ = block.AddItem(data);
var ptr = new DataBlockPointer(block.Index + 1, offs);
return ptr;
}
public Array_StructurePointer AddPointerArray(MetaPOINTER[] arr)
public Array_StructurePointer AddPointerArray(MetaPOINTER[]? arr)
{
if ((arr == null) || (arr.Length == 0)) return new Array_StructurePointer();
if (arr is null || arr.Length == 0)
return new Array_StructurePointer();
var ptr = AddItemArray((MetaName)MetaTypeName.POINTER, arr);
Array_StructurePointer sp = new Array_StructurePointer();
sp.Count1 = (ushort)arr.Length;
sp.Count2 = sp.Count1;
sp.Pointer = ptr.Pointer;
Array_StructurePointer sp = new Array_StructurePointer {
Count1 = (ushort)arr.Length,
Count2 = (ushort)arr.Length,
Pointer = ptr.Pointer,
};
return sp;
}
public Array_StructurePointer AddItemPointerArrayPtr<T>(MetaName type, T[] items) where T : struct
public Array_StructurePointer AddItemPointerArrayPtr<T>(MetaName type, T[]? items) where T : struct
{
//helper method for creating a pointer array
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
if (items is null || items.Length == 0)
return new Array_StructurePointer();
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
for (int i = 0; i < items.Length; i++)
{
ptrs[i] = AddItemPtr(type, items[i]);
ptrs[i] = AddItemPtr(type, in items[i]);
}
return AddPointerArray(ptrs);
//Array_StructurePointer sp = new Array_StructurePointer();
//sp.Count1 = (ushort)items.Length;
//sp.Count2 = sp.Count1;
//for (int i = 0; i < items.Length; i++)
//{
// var item = items[i];
// var meptr = AddItemPtr(type, item);
// var mptr = AddItem((MetaName)MetaTypeName.POINTER, meptr);
// if (i == 0)
// {
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
// }
//}
//return sp;
}
public Array_StructurePointer AddWrapperArrayPtr(MetaWrapper[] items)
public Array_StructurePointer AddWrapperArrayPtr(MetaWrapper[]? items)
{
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
if (items is null || items.Length == 0)
return new Array_StructurePointer();
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
@ -256,43 +255,31 @@ namespace CodeWalker.GameFiles
ptrs[i] = items[i].Save(this);
}
return AddPointerArray(ptrs);
//Array_StructurePointer sp = new Array_StructurePointer();
//sp.Count1 = (ushort)items.Length;
//sp.Count2 = sp.Count1;
//for (int i = 0; i < items.Length; i++)
//{
// var item = items[i];
// var meptr = item.Save(this);
// var mptr = AddItem((MetaName)MetaTypeName.POINTER, meptr);
// if (i == 0)
// {
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
// }
//}
//return sp;
}
public Array_Structure AddWrapperArray(MetaWrapper[] items)
public Array_Structure AddWrapperArray(MetaWrapper[]? items)
{
if ((items == null) || (items.Length == 0)) return new Array_Structure();
if (items is null || items.Length == 0)
return new Array_Structure();
var sa = new Array_Structure();
sa.Count1 = (ushort)items.Length;
sa.Count2 = sa.Count1;
var pointer = 0UL;
for (int i = 0; i < items.Length; i++)
{
var item = items[i];
var meptr = item.Save(this);
if (i == 0)
{
MetaBuilderPointer mbp = new MetaBuilderPointer();
mbp.BlockID = meptr.BlockID;
mbp.Offset = meptr.Offset;
sa.Pointer = mbp.Pointer;
MetaBuilderPointer mbp = new MetaBuilderPointer(meptr.BlockID, meptr.Offset, 0);
pointer = mbp.Pointer;
}
}
return sa;
return new Array_Structure
{
Count1 = (ushort)items.Length,
Count2 = (ushort)items.Length,
Pointer = pointer,
};
}
@ -315,15 +302,13 @@ namespace CodeWalker.GameFiles
offset += bdata.Length;
}
}
if (offset != data.Length)
{ }
return data;
}
Dictionary<MetaName, MetaStructureInfo> StructureInfos = new Dictionary<MetaName, MetaStructureInfo>();
Dictionary<MetaName, MetaEnumInfo> EnumInfos = new Dictionary<MetaName, MetaEnumInfo>();
readonly Dictionary<MetaName, MetaStructureInfo> StructureInfos = new Dictionary<MetaName, MetaStructureInfo>();
readonly Dictionary<MetaName, MetaEnumInfo> EnumInfos = new Dictionary<MetaName, MetaEnumInfo>();
public void AddStructureInfo(MetaName name)
{
@ -363,11 +348,7 @@ namespace CodeWalker.GameFiles
if (StructureInfos.Count > 0)
{
m.StructureInfos = new ResourceSimpleArray<MetaStructureInfo>();
foreach (var si in StructureInfos.Values)
{
m.StructureInfos.Add(si);
}
m.StructureInfos = new ResourceSimpleArray<MetaStructureInfo>(StructureInfos.Values);
m.StructureInfosCount = (short)m.StructureInfos.Count;
}
else
@ -378,11 +359,7 @@ namespace CodeWalker.GameFiles
if (EnumInfos.Count > 0)
{
m.EnumInfos = new ResourceSimpleArray<MetaEnumInfo>();
foreach (var ei in EnumInfos.Values)
{
m.EnumInfos.Add(ei);
}
m.EnumInfos = new ResourceSimpleArray<MetaEnumInfo>(EnumInfos.Values);
m.EnumInfosCount = (short)m.EnumInfos.Count;
}
else
@ -431,9 +408,10 @@ namespace CodeWalker.GameFiles
}
public MetaDataBlock GetMetaDataBlock()
public MetaDataBlock? GetMetaDataBlock()
{
if (TotalSize <= 0) return null;
if (TotalSize <= 0)
return null;
byte[] data = new byte[TotalSize];
int offset = 0;
@ -455,12 +433,12 @@ namespace CodeWalker.GameFiles
}
public struct MetaBuilderPointer
public readonly struct MetaBuilderPointer(int blockId, int offset, int length)
{
public int BlockID { get; set; } //1-based id
public int Offset { get; set; } //byte offset
public int Length { get; set; } //for temp use...
public uint Pointer
public int BlockID { get; init; } = blockId; //1-based id
public int Offset { get; init; } = offset; //byte offset
public int Length { get; init; } = length; //for temp use...
public readonly uint Pointer
{
get
{

View File

@ -1,8 +1,10 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@ -11,21 +13,46 @@ namespace CodeWalker.GameFiles
public static class MetaNames
{
public static ConcurrentDictionary<uint, string> stringCache = new ConcurrentDictionary<uint, string>();
public static bool TryGetString(uint h, out string str)
public static ConcurrentDictionary<uint, string?>? stringCache = null;
[MethodImpl(MethodImplOptions.Synchronized)]
[MemberNotNull(nameof(stringCache))]
public static void InitializeCache()
{
if (stringCache is not null)
{
return;
}
var values = Enum.GetValues<MetaName>();
var names = Enum.GetNames<MetaName>();
stringCache = new ConcurrentDictionary<uint, string?>(Environment.ProcessorCount, names.Length);
for (int i = 0; i < values.Length; i++)
{
var str = names[i];
if (str?.StartsWith('@') ?? false)
{
str = str.Substring(1);
}
stringCache.TryAdd((uint)values[i], str);
}
}
public static bool TryGetString(uint h, [MaybeNullWhen(false)] out string str)
{
if (stringCache is null)
{
InitializeCache();
}
if (stringCache.TryGetValue(h, out str))
{
return str != null;
return str is not null;
}
if (Enum.IsDefined(typeof(MetaName), h))
{
str = ((MetaName)h).ToString();
if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry
stringCache.TryAdd(h, str);
return true;
}
stringCache.TryAdd(h, str);
str = null;
return false;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@ namespace CodeWalker.GameFiles
public PsoBuilderPointer AddItem<T>(MetaName type, T item) where T : struct
{
byte[] data = MetaTypes.ConvertToBytes(item);
byte[] data = MetaTypes.ConvertToBytes(in item);
return AddItem(type, data);
}
@ -62,10 +62,12 @@ namespace CodeWalker.GameFiles
// data = newdata; //make sure item size is multiple of 16... so pointers don't need sub offsets!
//}
int idx = block.AddItem(data);
PsoBuilderPointer r = new PsoBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = (idx * data.Length);
r.Length = data.Length;
PsoBuilderPointer r = new PsoBuilderPointer
{
BlockID = block.Index + 1,
Offset = (idx * data.Length),
Length = data.Length
};
return r;
}
@ -89,10 +91,12 @@ namespace CodeWalker.GameFiles
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
int offs = block.TotalSize;
int idx = block.AddItem(newdata);
PsoBuilderPointer r = new PsoBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = offs; //(idx * data.Length);;
r.Length = length;
PsoBuilderPointer r = new PsoBuilderPointer
{
BlockID = block.Index + 1,
Offset = offs, //(idx * data.Length);;
Length = length
};
return r;
}
@ -148,10 +152,12 @@ namespace CodeWalker.GameFiles
{
if ((arr == null) || (arr.Length == 0)) return new Array_StructurePointer();
var ptr = AddItemArray((MetaName)MetaTypeName.PsoPOINTER, arr);
Array_StructurePointer sp = new Array_StructurePointer();
sp.Count1 = (ushort)arr.Length;
sp.Count2 = sp.Count1;
sp.Pointer = ptr.Pointer;
Array_StructurePointer sp = new Array_StructurePointer
{
Count1 = (ushort)arr.Length,
Count2 = (ushort)arr.Length,
Pointer = ptr.Pointer,
};
return sp;
}
@ -172,10 +178,12 @@ namespace CodeWalker.GameFiles
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
int offs = block.TotalSize;
int idx = block.AddItem(newdata);
PsoBuilderPointer r = new PsoBuilderPointer();
r.BlockID = block.Index + 1;
r.Offset = offs;// (idx * data.Length);
r.Length = datalen; //actual length of string.
PsoBuilderPointer r = new PsoBuilderPointer
{
BlockID = block.Index + 1,
Offset = offs,// (idx * data.Length);
Length = datalen //actual length of string.
};
return r;
}
@ -248,7 +256,7 @@ namespace CodeWalker.GameFiles
{
if (!StructureInfos.ContainsKey(name))
{
PsoStructureInfo si = PsoTypes.GetStructureInfo(name);
PsoStructureInfo si = PsoTypesStructureInfo.GetStructureInfo(name);
if (si != null)
{
StructureInfos[name] = si;
@ -259,7 +267,7 @@ namespace CodeWalker.GameFiles
{
if (!EnumInfos.ContainsKey(name))
{
PsoEnumInfo ei = PsoTypes.GetEnumInfo(name);
PsoEnumInfo ei = PsoTypesEnumInfo.GetEnumInfo(name);
if (ei != null)
{
EnumInfos[name] = ei;
@ -274,7 +282,7 @@ namespace CodeWalker.GameFiles
if (valType == 0)
{
inf = PsoTypes.GetStructureInfo((MetaName)MetaTypeName.ARRAYINFO); //default ARRAYINFO with pointer
inf = PsoTypesStructureInfo.GetStructureInfo((MetaName)MetaTypeName.ARRAYINFO); //default ARRAYINFO with pointer
if (!StructureInfos.ContainsKey(inf.IndexInfo.NameHash))
{
StructureInfos[inf.IndexInfo.NameHash] = inf;
@ -282,7 +290,7 @@ namespace CodeWalker.GameFiles
return inf;
}
var structInfo = PsoTypes.GetStructureInfo(valType);
var structInfo = PsoTypesStructureInfo.GetStructureInfo(valType);
if (structInfo == null)
{ }//error?
@ -396,8 +404,7 @@ namespace CodeWalker.GameFiles
pso.SchemaSection.EntriesIdx = new PsoElementIndexInfo[schEntries.Count];
for (int i = 0; i < schEntries.Count; i++)
{
pso.SchemaSection.EntriesIdx[i] = new PsoElementIndexInfo();
pso.SchemaSection.EntriesIdx[i].NameHash = schEntries[i].IndexInfo.NameHash;
pso.SchemaSection.EntriesIdx[i] = new PsoElementIndexInfo(schEntries[i].IndexInfo.NameHash, 0);
}
if (STRFStrings.Count > 0)
@ -422,10 +429,12 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < Blocks.Count; i++)
{
var b = Blocks[i];
var e = new PsoDataMappingEntry();
e.NameHash = b.StructureNameHash;
e.Length = b.TotalSize;
e.Offset = offset;
var e = new PsoDataMappingEntry
{
NameHash = b.StructureNameHash,
Length = b.TotalSize,
Offset = offset
};
offset += b.TotalSize;
pso.DataMapSection.Entries[i] = e;
}
@ -454,13 +463,7 @@ namespace CodeWalker.GameFiles
return idx;
}
public uint BasePointer
{
get
{
return (((uint)Index + 1) & 0xFFF);
}
}
public uint BasePointer => (((uint)Index + 1) & 0xFFF);
//public MetaDataBlock GetMetaDataBlock()
@ -484,16 +487,16 @@ namespace CodeWalker.GameFiles
}
public struct PsoBuilderPointer
public readonly struct PsoBuilderPointer
{
public int BlockID { get; set; } //1-based id
public int Offset { get; set; } //byte offset
public int Length { get; set; } //for temp use...
public int BlockID { get; init; } //1-based id
public int Offset { get; init; } //byte offset
public int Length { get; init; } //for temp use...
public uint Pointer
{
get
{
uint bidx = (((uint)BlockID) & 0xFFF);
uint bidx = ((uint)BlockID) & 0xFFF;
uint offs = (((uint)Offset) & 0xFFFFF) << 12;
return bidx + offs;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,59 +24,72 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using CodeWalker.Core.Utils;
using Collections.Pooled;
using CommunityToolkit.Diagnostics;
using Microsoft.Extensions.ObjectPool;
namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFile
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfFile : IDisposable
{
private const int RBF_IDENT = 0x30464252;
public const uint RBF_IDENT_LITTLE_ENDIAN = 0x30464252;
public RbfStructure current { get; set; }
public Stack<RbfStructure> stack { get; set; }
public List<RbfEntryDescription> descriptors { get; set; }
public Dictionary<string, int> outDescriptors { get; private set; } = new Dictionary<string, int>();
public void Load(byte[] data)
public RbfStructure Load(byte[] data)
{
using (var ms = new MemoryStream(data))
Load(ms);
var sequence = new ReadOnlySequence<byte>(data);
var reader = new SequenceReader<byte>(sequence);
return Load(ref reader);
}
public RbfStructure Load(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Open))
{
return Load(fileStream);
}
var data = File.ReadAllBytes(fileName);
return Load(data);
}
public RbfStructure Load(Stream stream)
public RbfStructure Load(ref SequenceReader<byte> reader)
{
stack = new Stack<RbfStructure>();
var stack = new Stack<RbfStructure>();
descriptors = new List<RbfEntryDescription>();
var reader = new DataReader(stream);
//var reader = new DataReader(stream);
var ident = reader.ReadInt32();
if (ident != RBF_IDENT)
throw new Exception("The file identifier does not match.");
if (ident != RBF_IDENT_LITTLE_ENDIAN)
{
ThrowHelper.ThrowInvalidOperationException("The file identifier does not match.");
return default;
}
while (reader.Position < reader.Length)
while (reader.Consumed < reader.Length)
{
var descriptorIndex = reader.ReadByte();
if (descriptorIndex == 0xFF) // close tag
{
var b = reader.ReadByte();
if (b != 0xFF)
throw new Exception("Expected 0xFF but was " + b.ToString("X2"));
{
ThrowHelper.ThrowInvalidOperationException($"Expected 0xFF but was {b:X2}");
return default;
}
if (stack.Count > 0)
{
@ -84,8 +97,12 @@ namespace CodeWalker.GameFiles
}
else
{
if (reader.Position != reader.Length)
throw new Exception("Expected end of stream but was not.");
if (reader.Consumed != reader.Length)
{
ThrowHelper.ThrowInvalidOperationException("Expected end of stream but was not.");
return default;
}
return current;
}
}
@ -93,14 +110,17 @@ namespace CodeWalker.GameFiles
{
var b = reader.ReadByte();
if (b != 0xFF)
throw new Exception("Expected 0xFF but was " + b.ToString("X2"));
{
ThrowHelper.ThrowInvalidOperationException($"Expected 0xFF but was {b:X2}");
return default;
}
var dataLength = reader.ReadInt32();
var data = reader.ReadBytes(dataLength);
var bytesValue = new RbfBytes();
bytesValue.Value = data;
current.Children.Add(bytesValue);
bytesValue.Value = data.ToArray();
current.AddChild(bytesValue);
}
else
{
@ -116,10 +136,15 @@ namespace CodeWalker.GameFiles
descriptor.Type = dataType;
descriptors.Add(descriptor);
ParseElement(reader, descriptors.Count - 1, dataType);
ParseElement(ref reader, descriptors.Count - 1, dataType, stack);
}
else // existing descriptor + data
{
if (descriptorIndex >= descriptors.Count)
{
ThrowHelper.ThrowInvalidOperationException("Index out of range");
return default;
}
if (dataType != descriptors[descriptorIndex].Type)
{
//throw new Exception("Data type does not match. Expected "
@ -127,15 +152,16 @@ namespace CodeWalker.GameFiles
// + dataType.ToString() + ". Descriptor: " + descriptors[descriptorIndex].Name);
}
ParseElement(reader, descriptorIndex, dataType);
ParseElement(ref reader, descriptorIndex, dataType, stack);
}
}
}
throw new Exception("Unexpected end of stream.");
ThrowHelper.ThrowInvalidOperationException("Unexpected end of stream.");
return default;
}
private void ParseElement(DataReader reader, int descriptorIndex, byte dataType)
private void ParseElement(ref SequenceReader<byte> reader, int descriptorIndex, byte dataType, Stack<RbfStructure> stack)
{
var descriptor = descriptors[descriptorIndex];
switch (dataType)
@ -145,9 +171,9 @@ namespace CodeWalker.GameFiles
var structureValue = new RbfStructure();
structureValue.Name = descriptor.Name;
if (current != null)
if (current is not null)
{
current.AddChild(structureValue);
current.AddChildOrAttribute(structureValue);
stack.Push(current);
}
@ -163,7 +189,7 @@ namespace CodeWalker.GameFiles
var intValue = new RbfUint32();
intValue.Name = descriptor.Name;
intValue.Value = reader.ReadUInt32();
current.AddChild(intValue);
current.AddChildOrAttribute(intValue);
break;
}
case 0x20:
@ -171,7 +197,7 @@ namespace CodeWalker.GameFiles
var booleanValue = new RbfBoolean();
booleanValue.Name = descriptor.Name;
booleanValue.Value = true;
current.AddChild(booleanValue);
current.AddChildOrAttribute(booleanValue);
break;
}
case 0x30:
@ -179,7 +205,7 @@ namespace CodeWalker.GameFiles
var booleanValue = new RbfBoolean();
booleanValue.Name = descriptor.Name;
booleanValue.Value = false;
current.AddChild(booleanValue);
current.AddChildOrAttribute(booleanValue);
break;
}
case 0x40:
@ -187,7 +213,7 @@ namespace CodeWalker.GameFiles
var floatValue = new RbfFloat();
floatValue.Name = descriptor.Name;
floatValue.Value = reader.ReadSingle();
current.AddChild(floatValue);
current.AddChildOrAttribute(floatValue);
break;
}
case 0x50:
@ -197,7 +223,7 @@ namespace CodeWalker.GameFiles
floatVectorValue.X = reader.ReadSingle();
floatVectorValue.Y = reader.ReadSingle();
floatVectorValue.Z = reader.ReadSingle();
current.AddChild(floatVectorValue);
current.AddChildOrAttribute(floatVectorValue);
break;
}
case 0x60:
@ -207,24 +233,38 @@ namespace CodeWalker.GameFiles
var stringValue = new RbfString();
stringValue.Name = descriptor.Name;
stringValue.Value = value;
current.AddChild(stringValue);
current.AddChildOrAttribute(stringValue);
break;
}
default:
throw new Exception("Unsupported data type.");
ThrowHelper.ThrowInvalidOperationException("Unsupported data type.");
return;
}
}
public static bool IsRBF(Stream stream)
{
var reader = new DataReader(stream);
var origpos = stream.Position;
var ident = reader.ReadInt32();
var isrbf = (ident == RBF_IDENT);
Span<byte> buffer = stackalloc byte[4];
stream.Read(buffer);
stream.Position = origpos;
return isrbf;
return IsRBF(buffer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsRBF(Span<byte> ident)
{
return IsRBF(BinaryPrimitives.ReadUInt32LittleEndian(ident));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsRBF(uint ident)
{
return ident == RBF_IDENT_LITTLE_ENDIAN;
}
public byte GetDescriptorIndex(IRbfType t, out bool isNew)
{
@ -268,12 +308,11 @@ namespace CodeWalker.GameFiles
outDescriptors = new Dictionary<string, int>();
var writer = new DataWriter(stream);
writer.Write(RBF_IDENT);
writer.Write(RBF_IDENT_LITTLE_ENDIAN);
current.Save(this, writer);
}
public void WriteRecordId(IRbfType type, DataWriter writer)
{
writer.Write(GetDescriptorIndex(type, out var isNew));
@ -285,21 +324,36 @@ namespace CodeWalker.GameFiles
writer.Write(Encoding.ASCII.GetBytes(type.Name));
}
}
public void Dispose()
{
current?.Dispose();
GC.SuppressFinalize(this);
}
~RbfFile()
{
Dispose();
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfEntryDescription
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfEntryDescription
{
public string Name { get; set; }
public int Type { get; set; }
public override string ToString() { return Name + ": " + Type.ToString(); }
public override string ToString() => $"{Name}: {Type}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public interface IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public interface IRbfType
{
string Name { get; set; }
byte DataType { get; }
void Save(RbfFile file, DataWriter writer);
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBytes : IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfBytes : IRbfType
{
public string Name { get; set; }
public byte[] Value { get; set; }
@ -311,8 +365,20 @@ namespace CodeWalker.GameFiles
writer.Write(Value.Length);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
public string GetNullTerminatedString()
{
var span = Value.AsSpan();
var index = span.IndexOf((byte)0);
if (index == -1)
return Encoding.ASCII.GetString(span);
return Encoding.ASCII.GetString(span.Slice(0, index));
}
public override string ToString() => $"{Name}: {Value}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfUint32 : IRbfType
{
public string Name { get; set; }
@ -323,9 +389,11 @@ namespace CodeWalker.GameFiles
file.WriteRecordId(this, writer);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
public override string ToString() => $"{Name}: {Value}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBoolean : IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfBoolean : IRbfType
{
public string Name { get; set; }
public bool Value { get; set; }
@ -334,7 +402,7 @@ namespace CodeWalker.GameFiles
{
file.WriteRecordId(this, writer);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
public override string ToString() => $"{Name}: {Value}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat : IRbfType
{
@ -346,9 +414,10 @@ namespace CodeWalker.GameFiles
file.WriteRecordId(this, writer);
writer.Write(Value);
}
public override string ToString() { return Name + ": " + Value.ToString(); }
public override string ToString() => $"{Name}: {Value}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat3 : IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfFloat3 : IRbfType
{
public string Name { get; set; }
public float X { get; set; }
@ -362,9 +431,10 @@ namespace CodeWalker.GameFiles
writer.Write(Y);
writer.Write(Z);
}
public override string ToString() { return string.Format("{0}: X:{1}, Y:{2}, Z:{3}", Name, X, Y, Z); }
public override string ToString() => $"{Name}: X:{X}, Y:{Y}, Z:{Z}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfString : IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfString : IRbfType
{
public string Name { get; set; }
public string Value { get; set; }
@ -375,32 +445,47 @@ namespace CodeWalker.GameFiles
writer.Write((short)Value.Length);
writer.Write(Encoding.ASCII.GetBytes(Value));
}
public override string ToString() { return Name + ": " + Value.ToString(); }
public override string ToString() => $"{Name}: {Value}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfStructure : IRbfType
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RbfStructure : IRbfType, IDisposable
{
private static ObjectPool<PooledList<IRbfType>> listPool = ObjectPool.Create(new DefaultPooledObjectPolicy<PooledList<IRbfType>>());
public string Name { get; set; }
public List<IRbfType> Children { get; set; } = new List<IRbfType>();
public List<IRbfType> Attributes { get; set; } = new List<IRbfType>();
public PooledList<IRbfType>? Children { get; set; }
public PooledList<IRbfType>? Attributes { get; set; }
internal int PendingAttributes { get; set; }
public byte DataType => 0;
public override string ToString() { return Name + ": {" + Children.Count.ToString() + "}"; }
public IRbfType FindChild(string name)
public override string ToString() => $"{ Name }: {{{ Children?.Count ?? 0 }}}";
public IRbfType? FindChild(string name)
{
if (Children is null || Children.Count == 0)
return null;
foreach (var child in Children)
{
if (child == null) continue;
if (child.Name == name) return child;
if (child is null)
continue;
if (child.Name == name)
return child;
}
return null;
}
public IRbfType FindAttribute(string name)
public IRbfType? FindAttribute(string name)
{
if (Attributes is null || Attributes.Count == 0)
return null;
foreach (var attr in Attributes)
{
if (attr == null) continue;
if (attr.Name == name) return attr;
if (attr is null)
continue;
if (attr.Name == name)
return attr;
}
return null;
}
public void Save(RbfFile root, DataWriter writer)
@ -410,32 +495,71 @@ namespace CodeWalker.GameFiles
writer.Write(new byte[4]); // 00
// count of non-primitive fields in this (... attributes??)
writer.Write((short)Attributes.Count); //writer.Write((short)Children.TakeWhile(a => !(a is RbfBytes || a is RbfStructure)).Count());
writer.Write((short)(Attributes?.Count ?? 0)); //writer.Write((short)Children.TakeWhile(a => !(a is RbfBytes || a is RbfStructure)).Count());
foreach (var attr in Attributes)
if (Attributes is not null)
{
attr.Save(root, writer);
foreach (var attr in Attributes)
{
attr.Save(root, writer);
}
}
foreach (var child in Children)
if (Children is not null)
{
child.Save(root, writer);
foreach (var child in Children)
{
child.Save(root, writer);
}
}
writer.Write((byte)0xFF);
writer.Write((byte)0xFF);
}
internal void AddChild(IRbfType value)
{
Children ??= listPool.Get();
Children.Add(value);
}
internal void AddAttribute(IRbfType value)
{
Attributes ??= listPool.Get();
Attributes.Add(value);
}
internal void AddChildOrAttribute(IRbfType value)
{
if (PendingAttributes > 0)
{
PendingAttributes--;
Attributes.Add(value);
AddAttribute(value);
}
else
{
Children.Add(value);
AddChild(value);
}
}
public void Dispose()
{
if (Children is PooledList<IRbfType> children)
{
listPool.Return(children);
}
if (Attributes is PooledList<IRbfType> attributes)
{
listPool.Return(attributes);
}
GC.SuppressFinalize(this);
}
~RbfStructure()
{
Dispose();
}
}

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;
using CodeWalker.Core.Utils;
using Collections.Pooled;
using SharpDX;
namespace CodeWalker.GameFiles
@ -64,150 +66,174 @@ namespace CodeWalker.GameFiles
case MetaFormat.Mrf:
return GetMrfData(doc);
}
return null;
return [];
}
public static byte[] GetRSCData(XmlDocument doc)
{
var meta = GetMeta(doc);
if ((meta.DataBlocks?.Data == null) || (meta.DataBlocks.Count == 0)) return null;
if (meta.DataBlocks?.Data is null || meta.DataBlocks.Count == 0)
return [];
return ResourceBuilder.Build(meta, 2); //meta is RSC V:2
}
public static byte[] GetPSOData(XmlDocument doc)
{
var pso = XmlPso.GetPso(doc);
if ((pso.DataSection == null) || (pso.DataMapSection == null) || (pso.SchemaSection == null)) return null;
if (pso.DataSection == null || pso.DataMapSection == null || pso.SchemaSection == null)
return [];
return pso.Save();
}
public static byte[] GetRBFData(XmlDocument doc)
{
var rbf = XmlRbf.GetRbf(doc);
if (rbf.current == null) return null;
if (rbf.current is null)
return [];
return rbf.Save();
}
public static byte[] GetRelData(XmlDocument doc)
{
var rel = XmlRel.GetRel(doc);
if ((rel.RelDatasSorted == null) || (rel.RelDatas == null)) return null;
if (rel.RelDatas == null)
return [];
return rel.Save();
}
public static byte[] GetYndData(XmlDocument doc)
{
var ynd = XmlYnd.GetYnd(doc);
if (ynd.NodeDictionary == null) return null;
if (ynd.NodeDictionary == null)
return [];
return ynd.Save();
}
public static byte[] GetYnvData(XmlDocument doc)
{
var ynv = XmlYnv.GetYnv(doc);
if (ynv.Nav == null) return null;
if (ynv.Nav == null)
return [];
return ynv.Save();
}
public static byte[] GetYcdData(XmlDocument doc)
{
var ycd = XmlYcd.GetYcd(doc);
if (ycd.ClipDictionary == null) return null;
if (ycd.ClipDictionary == null)
return [];
return ycd.Save();
}
public static byte[] GetYbnData(XmlDocument doc)
{
var ybn = XmlYbn.GetYbn(doc);
if (ybn.Bounds == null) return null;
if (ybn.Bounds == null)
return [];
return ybn.Save();
}
public static byte[] GetYtdData(XmlDocument doc, string fpathin)
{
var ytd = XmlYtd.GetYtd(doc, fpathin);
if (ytd.TextureDict == null) return null;
if (ytd.TextureDict == null)
return [];
return ytd.Save();
}
public static byte[] GetYdrData(XmlDocument doc, string fpathin)
{
var ydr = XmlYdr.GetYdr(doc, fpathin);
if (ydr.Drawable == null) return null;
if (ydr.Drawable == null)
return [];
return ydr.Save();
}
public static byte[] GetYddData(XmlDocument doc, string fpathin)
{
var ydd = XmlYdd.GetYdd(doc, fpathin);
if (ydd.DrawableDict == null) return null;
if (ydd.DrawableDict == null)
return [];
return ydd.Save();
}
public static byte[] GetYftData(XmlDocument doc, string fpathin)
{
var yft = XmlYft.GetYft(doc, fpathin);
if (yft.Fragment == null) return null;
if (yft.Fragment == null)
return [];
return yft.Save();
}
public static byte[] GetYptData(XmlDocument doc, string fpathin)
{
var ypt = XmlYpt.GetYpt(doc, fpathin);
if (ypt.PtfxList == null) return null;
if (ypt.PtfxList == null)
return [];
return ypt.Save();
}
public static byte[] GetYldData(XmlDocument doc, string fpathin)
{
var yld = XmlYld.GetYld(doc, fpathin);
if (yld.ClothDictionary == null) return null;
if (yld.ClothDictionary == null)
return [];
return yld.Save();
}
public static byte[] GetYedData(XmlDocument doc, string fpathin)
{
var yed = XmlYed.GetYed(doc, fpathin);
if (yed.ExpressionDictionary == null) return null;
if (yed.ExpressionDictionary == null)
return [];
return yed.Save();
}
public static byte[] GetYwrData(XmlDocument doc, string fpathin)
{
var ywr = XmlYwr.GetYwr(doc, fpathin);
if (ywr.Waypoints == null) return null;
if (ywr.Waypoints == null)
return [];
return ywr.Save();
}
public static byte[] GetYvrData(XmlDocument doc, string fpathin)
{
var yvr = XmlYvr.GetYvr(doc, fpathin);
if (yvr.Records == null) return null;
if (yvr.Records == null)
return [];
return yvr.Save();
}
public static byte[] GetAwcData(XmlDocument doc, string fpathin)
{
var awc = XmlAwc.GetAwc(doc, fpathin);
if (awc.Streams == null) return null;
if (awc.Streams == null)
return [];
return awc.Save();
}
public static byte[] GetFxcData(XmlDocument doc, string fpathin)
{
var fxc = XmlFxc.GetFxc(doc, fpathin);
if (fxc.Shaders == null) return null;
if (fxc.Shaders == null)
return [];
return fxc.Save();
}
public static byte[] GetCacheFileData(XmlDocument doc)
{
var cdf = XmlCacheDat.GetCacheDat(doc);
if (cdf == null) return null;
if (cdf == null)
return [];
return cdf.Save();
}
public static byte[] GetHeightmapData(XmlDocument doc)
{
var hmf = XmlHmap.GetHeightmap(doc);
if (hmf.MaxHeights == null) return null;
if (hmf.MaxHeights == null)
return [];
return hmf.Save();
}
public static byte[] GetYpdbData(XmlDocument doc)
{
var ypdb = XmlYpdb.GetYpdb(doc);
if (ypdb.WeightSet == null) return null;
if (ypdb.WeightSet == null)
return [];
return ypdb.Save();
}
public static byte[] GetYfdData(XmlDocument doc)
{
var yfd = XmlYfd.GetYfd(doc);
if (yfd.FrameFilterDictionary == null) return null;
if (yfd.FrameFilterDictionary == null)
return [];
return yfd.Save();
}
public static byte[] GetMrfData(XmlDocument doc)
{
var mrf = XmlMrf.GetMrf(doc);
if (mrf == null) return null;
if (mrf == null)
return [];
return mrf.Save();
}
@ -463,7 +489,7 @@ namespace CodeWalker.GameFiles
if (!string.IsNullOrEmpty(cnode.InnerText))
{
var ptr = mb.AddStringPtr(cnode.InnerText);
var val = MetaTypes.ConvertToBytes(ptr);
var val = MetaTypes.ConvertToBytes(in ptr);
Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
}
@ -481,6 +507,7 @@ namespace CodeWalker.GameFiles
var bytes = new List<byte>();
for (int j = 0; j < split.Length; j++)
{
Console.WriteLine("Parsing byte");
byte val;// = Convert.ToByte(split[j], 10);
if (byte.TryParse(split[j].Trim(), ns, ic, out val))
{
@ -488,7 +515,7 @@ namespace CodeWalker.GameFiles
}
}
var ptr = mb.AddDataBlockPtr(bytes.ToArray(), (MetaName)MetaTypeName.BYTE);
var byt = MetaTypes.ConvertToBytes(ptr);
var byt = MetaTypes.ConvertToBytes(in ptr);
Buffer.BlockCopy(byt, 0, data, entry.DataOffset, byt.Length);
break;
}
@ -1014,7 +1041,7 @@ namespace CodeWalker.GameFiles
private static Array_uint TraverseHashArray(XmlNode node, MetaBuilder mb)
{
var items = new List<MetaHash>();
using var items = new PooledList<MetaHash>();
foreach (XmlNode cnode in node.ChildNodes)
{
@ -1216,7 +1243,7 @@ namespace CodeWalker.GameFiles
var inodes = vnode.SelectNodes("Item");
if (inodes?.Count > 0)
{
var vlist = new List<MetaHash>();
using var vlist = new PooledList<MetaHash>();
foreach (XmlNode inode in inodes)
{
vlist.Add(GetHash(inode.InnerText));
@ -1258,7 +1285,7 @@ namespace CodeWalker.GameFiles
public Dictionary<int, Array_Vector3> Float_XYZs;
public Dictionary<int, Array_uint> Hashes;
public void WriteArrays(byte[] data)
public readonly void WriteArrays(byte[] data)
{
foreach (KeyValuePair<int, Array_Structure> ptr in Structures)
{

View File

@ -32,7 +32,7 @@ namespace CodeWalker.GameFiles
type = (MetaName)(uint)GetHash(node.Name);
}
var infos = PsoTypes.GetStructureInfo(type);
var infos = PsoTypesStructureInfo.GetStructureInfo(type);
if (infos != null)
{
byte[] data = new byte[infos.StructureLength];
@ -49,7 +49,7 @@ namespace CodeWalker.GameFiles
Array.Clear(data, 0, infos.StructureLength); //shouldn't really be necessary...
PsoStructureEntryInfo arrEntry = null;
PsoStructureEntryInfo arrEntry = default;
//if (isRoot)
@ -78,7 +78,7 @@ namespace CodeWalker.GameFiles
{
case PsoDataType.Array:
{
TraverseArray(cnode, pb, entry, arrEntry, arrayResults, data, infos);
TraverseArray(cnode, pb, in entry, in arrEntry, in arrayResults, data, infos);
break;
}
case PsoDataType.Structure:
@ -110,8 +110,8 @@ namespace CodeWalker.GameFiles
var bptr = pb.AddItem(stype, struc);
var ptr = new PsoPOINTER(bptr.BlockID, bptr.Offset);
ptr.SwapEnd();
var ptrb = MetaTypes.ConvertToBytes(ptr);
ptr = ptr.SwapEnd();
var ptrb = MetaTypes.ConvertToBytes(in ptr);
Buffer.BlockCopy(ptrb, 0, data, entry.DataOffset, ptrb.Length);
@ -263,24 +263,16 @@ namespace CodeWalker.GameFiles
//uint fCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
uint fEntry = (entry.ReferenceKey & 0xFFF);
var fEnt = (fEntry != 0xFFF) ? infos.GetEntry((int)fEntry) : null;
PsoEnumInfo flagsInfo = null;
PsoEnumInfo? flagsInfo = null;
MetaName fEnum = (MetaName)(fEnt?.ReferenceKey ?? 0);
if ((fEnt != null) && (fEnt.EntryNameHash == (MetaName)MetaTypeName.ARRAYINFO))
if (fEnt is not null && (fEnt?.EntryNameHash == (MetaName)MetaTypeName.ARRAYINFO))
{
flagsInfo = PsoTypes.GetEnumInfo(fEnum);
}
if (flagsInfo == null)
{
if (fEntry != 0xFFF)
{ }
//flagsInfo = PsoTypes.GetEnumInfo(entry.EntryNameHash);
flagsInfo = PsoTypesEnumInfo.GetEnumInfo(fEnum);
}
if (flagsInfo != null)
{
pb.AddEnumInfo(flagsInfo.IndexInfo.NameHash);
}
else
{ }//error?
switch (entry.Unk_5h)
{
@ -377,11 +369,8 @@ namespace CodeWalker.GameFiles
var xStruct = pb.AddMapNodeStructureInfo((MetaName)mapreftype2.ReferenceKey);
var xName = xStruct.IndexInfo.NameHash;
var kEntry = xStruct?.FindEntry(MetaName.Key);
var iEntry = xStruct?.FindEntry(MetaName.Item);
if (kEntry.Type != PsoDataType.String)
{ }
var kEntry = xStruct?.FindEntry(MetaName.Key) ?? default;
var iEntry = xStruct?.FindEntry(MetaName.Item) ?? default;
@ -408,8 +397,8 @@ namespace CodeWalker.GameFiles
{
//normal ARRAYINFO with pointer value
var itemptr = pb.AddItemPtr(thash, strucBytes);
itemptr.SwapEnd(); //big schmigg
var ptrbytes = MetaTypes.ConvertToBytes(itemptr);
itemptr = itemptr.SwapEnd(); //big schmigg
var ptrbytes = MetaTypes.ConvertToBytes(in itemptr);
Buffer.BlockCopy(ptrbytes, 0, nodeBytes, iEntry.DataOffset, ptrbytes.Length);
}
@ -425,7 +414,7 @@ namespace CodeWalker.GameFiles
arrayResults.Structures[entry.DataOffset + 8] = pb.AddItemArrayPtr(xName, nodesData.ToArray()); //pb.AddPointerArray(nodeptrs);
}
private static void TraverseArray(XmlNode node, PsoBuilder pb, PsoStructureEntryInfo entry, PsoStructureEntryInfo arrEntry, PsoArrayResults results, byte[] data, PsoStructureInfo structInfo)
private static void TraverseArray(XmlNode node, PsoBuilder pb, in PsoStructureEntryInfo entry, in PsoStructureEntryInfo arrEntry, in PsoArrayResults results, byte[] data, PsoStructureInfo structInfo)
{
int offset = entry.DataOffset;
uint aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
@ -643,20 +632,17 @@ namespace CodeWalker.GameFiles
if (arrEntry.ReferenceKey != 0)
{
var _infos = PsoTypes.GetEnumInfo((MetaName)arrEntry.ReferenceKey);
var _infos = PsoTypesEnumInfo.GetEnumInfo((MetaName)arrEntry.ReferenceKey);
pb.AddEnumInfo(_infos.IndexInfo.NameHash);
var values = new uint[hashes.Length];
for (int i = 0; i < hashes.Length; i++)
{
var enumname = (MetaName)MetaTypes.SwapBytes(hashes[i]);//yeah swap it back to little endian..!
var enuminf = _infos.FindEntryByName(enumname);
if (enuminf != null)
if (_infos.TryFindEntryByName(enumname, out var enuminf))
{
values[i] = MetaTypes.SwapBytes((uint)enuminf.EntryKey);
}
else
{ } //error?
}
if (embedded)
@ -682,18 +668,19 @@ namespace CodeWalker.GameFiles
var rk1 = arrEntry.ReferenceKey & 0x0000FFFF;
if (rk0 > 0) //should be count of items
{
var subarrEntry = structInfo.GetEntry((int)rk1);
var subarrType = (MetaName)subarrEntry.ReferenceKey;
var subarrEntry = structInfo.GetEntry((int)rk1) ?? throw new InvalidOperationException("SubarrEntry not found!");
//var subarrType = (MetaName)subarrEntry.ReferenceKey;
var origOffset = arrEntry.DataOffset;
arrEntry.DataOffset = entry.DataOffset;//slight hack for traversing array array
//var origOffset = arrEntry.DataOffset;
var _arrEntry = arrEntry;
_arrEntry = _arrEntry with { DataOffset = entry.DataOffset };//slight hack for traversing array array
foreach (XmlNode cnode in node.ChildNodes)
{
TraverseArray(cnode, pb, arrEntry, subarrEntry, results, data, structInfo);
TraverseArray(cnode, pb, in arrEntry, in subarrEntry, in results, data, structInfo);
arrEntry.DataOffset += 16;//ptr size... todo: what if not pointer array?
_arrEntry = _arrEntry with { DataOffset = (ushort)(arrEntry.DataOffset + 16) };//ptr size... todo: what if not pointer array?
}
arrEntry.DataOffset = origOffset;
//arrEntry = arrEntry with { DataOffset = origOffset };
}
@ -748,9 +735,8 @@ namespace CodeWalker.GameFiles
if (!string.IsNullOrEmpty(str))
{
var bptr = pb.AddString(str);
var ptr = new PsoPOINTER(bptr.BlockID, bptr.Offset);
ptr.SwapEnd();
var val = MetaTypes.ConvertToBytes(ptr);
var ptr = new PsoPOINTER(bptr.BlockID, bptr.Offset).SwapEnd();
var val = MetaTypes.ConvertToBytes(in ptr);
Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
}
break;
@ -758,9 +744,8 @@ namespace CodeWalker.GameFiles
if (!string.IsNullOrEmpty(str))
{
var bptr = pb.AddString(str);
var ptr = new CharPointer(bptr.Pointer, str.Length);
ptr.SwapEnd();
var val = MetaTypes.ConvertToBytes(ptr);
var ptr = new CharPointer(bptr.Pointer, str.Length).SwapEnd();
var val = MetaTypes.ConvertToBytes(in ptr);
Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
}
break;
@ -816,7 +801,7 @@ namespace CodeWalker.GameFiles
if (struc != null)
{
var ptr = pb.AddItemPtr(type, struc);
ptr.SwapEnd(); //big schmigg
ptr = ptr.SwapEnd(); //big schmigg
ptrs.Add(ptr);
}
else
@ -1107,7 +1092,7 @@ namespace CodeWalker.GameFiles
private static int GetEnumInt(MetaName type, string enumString, PsoDataType dataType)
{
var infos = PsoTypes.GetEnumInfo(type);
var infos = PsoTypesEnumInfo.GetEnumInfo(type);
if (infos == null)
{
@ -1175,69 +1160,61 @@ namespace CodeWalker.GameFiles
public Dictionary<int, Array_Vector3> Float_XYZs;
public Dictionary<int, Array_uint> Hashes;
public void WriteArrays(byte[] data)
public readonly void WriteArrays(byte[] data)
{
foreach (KeyValuePair<int, Array_Structure> ptr in Structures)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_StructurePointer> ptr in StructurePointers)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_uint> ptr in UInts)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_ushort> ptr in UShorts)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_byte> ptr in UBytes)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_float> ptr in Floats)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_Vector3> ptr in Float_XYZs)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
foreach (KeyValuePair<int, Array_uint> ptr in Hashes)
{
var val = ptr.Value;
val.SwapEnd();
var _data = MetaTypes.ConvertToBytes(val);
var val = ptr.Value.SwapEnd();
var _data = MetaTypes.ConvertToBytes(in val);
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
}
}

View File

@ -1,4 +1,5 @@
using SharpDX;
using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -27,13 +28,13 @@ namespace CodeWalker.GameFiles
return rbf;
}
private static IRbfType Traverse(XNode node)
private static IRbfType? Traverse(XNode node)
{
if (node is XElement element)
{
if (element.Attribute("value") != null)
if (element.TryGetAttribute("value", out var value))
{
var val = element.Attribute("value").Value;
var val = value.Value;
if (!string.IsNullOrEmpty(val))
{
var rval = CreateValueNode(element.Name.LocalName, val);
@ -43,11 +44,11 @@ namespace CodeWalker.GameFiles
}
}
}
else if ((element.Attributes().Count() == 3) && (element.Attribute("x") != null) && (element.Attribute("y") != null) && (element.Attribute("z") != null))
else if (element.Attributes().Count() == 3 && element.TryGetAttribute("x", out var xAttr) && element.TryGetAttribute("y", out var yAttr) && element.TryGetAttribute("z", out var zAttr))
{
FloatUtil.TryParse(element.Attribute("x").Value, out float x);
FloatUtil.TryParse(element.Attribute("y").Value, out float y);
FloatUtil.TryParse(element.Attribute("z").Value, out float z);
FloatUtil.TryParse(xAttr.Value, out float x);
FloatUtil.TryParse(yAttr.Value, out float y);
FloatUtil.TryParse(zAttr.Value, out float z);
return new RbfFloat3()
{
Name = element.Name.LocalName,
@ -56,7 +57,7 @@ namespace CodeWalker.GameFiles
Z = z
};
}
else if ((element.Elements().Count() == 0) && (element.Attributes().Count() == 0) && (!element.IsEmpty)) //else if (element.Name == "type" || element.Name == "key" || element.Name == "platform")
else if (!element.HasElements && !element.HasAttributes && !element.IsEmpty) //else if (element.Name == "type" || element.Name == "key" || element.Name == "platform")
{
var bytearr = Encoding.ASCII.GetBytes(element.Value);
var bytearrnt = new byte[bytearr.Length + 1];
@ -69,7 +70,7 @@ namespace CodeWalker.GameFiles
var n = new RbfStructure();
n.Name = element.Name.LocalName;
n.Children = element.Nodes().Select(c => Traverse(c)).ToList();
n.Children = element.Nodes().Select(Traverse).ToPooledList();
foreach (var attr in element.Attributes())
{
@ -85,9 +86,8 @@ namespace CodeWalker.GameFiles
}
else if (node is XText text)
{
byte[] bytes = null;
var contentAttr = node.Parent?.Attribute("content");
if (contentAttr != null)
byte[]? bytes = null;
if (node.Parent?.TryGetAttribute("content", out var contentAttr) ?? false)
{
if (contentAttr.Value == "char_array")
{
@ -97,14 +97,12 @@ namespace CodeWalker.GameFiles
{
bytes = GetUshortArray(text.Value);
}
else
{ }
}
else
{
bytes = Encoding.ASCII.GetBytes(text.Value).Concat(new byte[] { 0x00 }).ToArray();
}
if (bytes != null)
if (bytes is not null)
{
return new RbfBytes()
{
@ -138,7 +136,7 @@ namespace CodeWalker.GameFiles
}
else if (val.StartsWith("0x"))
{
uint.TryParse(val.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint u);
uint.TryParse(val.AsSpan(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint u);
return new RbfUint32()
{
Name = name,
@ -167,37 +165,32 @@ namespace CodeWalker.GameFiles
private static byte[] GetByteArray(string text)
private static byte[]? GetByteArray(string text)
{
if (string.IsNullOrEmpty(text)) return null;
var data = new List<byte>();
if (string.IsNullOrEmpty(text))
return null;
using var data = new PooledList<byte>();
var split = Regex.Split(text, @"[\s\r\n\t]");
for (int i = 0; i < split.Length; i++)
foreach(var str in split)
{
if (!string.IsNullOrEmpty(split[i]))
{
var str = split[i];
if (string.IsNullOrEmpty(str)) continue;
var val = Convert.ToByte(str);
data.Add(val);
}
if (string.IsNullOrEmpty(str))
continue;
var val = Convert.ToByte(str);
data.Add(val);
}
return data.ToArray();
}
private static byte[] GetUshortArray(string text)
{
var data = new List<byte>();
using var data = new PooledList<byte>();
var split = Regex.Split(text, @"[\s\r\n\t]");
for (int i = 0; i < split.Length; i++)
foreach(var str in split)
{
if (!string.IsNullOrEmpty(split[i]))
{
var str = split[i];
if (string.IsNullOrEmpty(str)) continue;
var val = Convert.ToUInt16(str);
data.Add((byte)((val >> 0) & 0xFF));
data.Add((byte)((val >> 8) & 0xFF));
}
if (string.IsNullOrEmpty(str))
continue;
var val = Convert.ToUInt16(str);
data.Add((byte)((val >> 0) & 0xFF));
data.Add((byte)((val >> 8) & 0xFF));
}
return data.ToArray();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,5 @@
using SharpDX;
using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -6,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static CodeWalker.GameFiles.MetaXmlBase;
/*
Copyright(c) 2017 Neodymium
@ -101,8 +103,8 @@ namespace CodeWalker.GameFiles
}
public void ReadXml(XmlNode node)
{
var clothes = new List<CharacterCloth>();
var clothhashes = new List<MetaHash>();
using var clothes = new PooledList<CharacterCloth>();
using var clothhashes = new PooledList<MetaHash>();
var inodes = node.SelectNodes("Item");
if (inodes != null)
@ -144,17 +146,18 @@ namespace CodeWalker.GameFiles
return cd;
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, ClothNameHashes),
new Tuple<long, IResourceBlock>(0x30, Clothes)
return new (long, IResourceBlock)[] {
(0x20, ClothNameHashes),
(0x30, Clothes)
};
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ClothController : ResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ClothController : ResourceSystemBlock
{
// clothController
public override long BlockLength => 0x80;
@ -333,12 +336,17 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
if (BridgeSimGfx != null) list.Add(BridgeSimGfx);
if (MorphController != null) list.Add(MorphController);
if (VerletCloth1 != null) list.Add(VerletCloth1);
if (VerletCloth2 != null) list.Add(VerletCloth2);
if (VerletCloth3 != null) list.Add(VerletCloth3);
using var list = new PooledList<IResourceBlock>();
if (BridgeSimGfx is not null)
list.Add(BridgeSimGfx);
if (MorphController is not null)
list.Add(MorphController);
if (VerletCloth1 is not null)
list.Add(VerletCloth1);
if (VerletCloth2 is not null)
list.Add(VerletCloth2);
if (VerletCloth3 is not null)
list.Add(VerletCloth3);
return list.ToArray();
}
}
@ -535,55 +543,55 @@ namespace CodeWalker.GameFiles
YldXml.ValueTag(sb, indent, "VertexCount", VertexCount.ToString());
YldXml.ValueTag(sb, indent, "Unknown14", Unknown_14h.ToString());
YldXml.ValueTag(sb, indent, "Unknown18", Unknown_18h.ToString());
if (Unknown_20h?.data_items != null)
if (Unknown_20h?.data_items is not null && Unknown_20h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_20h.data_items, indent, "Unknown20", "", FloatUtil.ToString);
}
if (Unknown_30h?.data_items != null)
if (Unknown_30h?.data_items != null && Unknown_30h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_30h.data_items, indent, "Unknown30", "", FloatUtil.ToString);
}
if (Unknown_40h?.data_items != null)
if (Unknown_40h?.data_items != null && Unknown_40h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_40h.data_items, indent, "Unknown40", "", FloatUtil.ToString);
}
if (Unknown_60h?.data_items != null)
if (Unknown_60h?.data_items != null && Unknown_60h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_60h.data_items, indent, "Unknown60", "", FloatUtil.ToString);
}
if (Unknown_70h?.data_items != null)
if (Unknown_70h?.data_items != null && Unknown_70h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_70h.data_items, indent, "Unknown70", "");
}
if (Unknown_80h?.data_items != null)
if (Unknown_80h?.data_items != null && Unknown_80h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_80h.data_items, indent, "Unknown80", "");
}
if (Unknown_A0h?.data_items != null)
if (Unknown_A0h?.data_items != null && Unknown_A0h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_A0h.data_items, indent, "UnknownA0", "", FloatUtil.ToString);
}
if (Unknown_B0h?.data_items != null)
if (Unknown_B0h?.data_items != null && Unknown_B0h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_B0h.data_items, indent, "UnknownB0", "");
}
if (Unknown_C0h?.data_items != null)
if (Unknown_C0h?.data_items != null && Unknown_C0h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_C0h.data_items, indent, "UnknownC0", "");
}
if (Unknown_E0h?.data_items != null)
if (Unknown_E0h?.data_items != null && Unknown_E0h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_E0h.data_items, indent, "UnknownE0", "");
}
if (Unknown_F0h?.data_items != null)
if (Unknown_F0h?.data_items != null && Unknown_F0h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_F0h.data_items, indent, "UnknownF0", "");
}
if (Unknown_100h?.data_items != null)
if (Unknown_100h?.data_items != null && Unknown_100h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_100h.data_items, indent, "Unknown100", "");
}
if (Unknown_128h?.data_items != null)
if (Unknown_128h?.data_items != null && Unknown_128h.data_items.Length > 0)
{
YldXml.WriteRawArray(sb, Unknown_128h.data_items, indent, "Unknown128", "");
}
@ -622,22 +630,22 @@ namespace CodeWalker.GameFiles
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, Unknown_20h),
new Tuple<long, IResourceBlock>(0x30, Unknown_30h),
new Tuple<long, IResourceBlock>(0x40, Unknown_40h),
new Tuple<long, IResourceBlock>(0x60, Unknown_60h),
new Tuple<long, IResourceBlock>(0x70, Unknown_70h),
new Tuple<long, IResourceBlock>(0x80, Unknown_80h),
new Tuple<long, IResourceBlock>(0xA0, Unknown_A0h),
new Tuple<long, IResourceBlock>(0xB0, Unknown_B0h),
new Tuple<long, IResourceBlock>(0xC0, Unknown_C0h),
new Tuple<long, IResourceBlock>(0xE0, Unknown_E0h),
new Tuple<long, IResourceBlock>(0xF0, Unknown_F0h),
new Tuple<long, IResourceBlock>(0x100, Unknown_100h),
new Tuple<long, IResourceBlock>(0x128, Unknown_128h)
return new (long, IResourceBlock)[] {
(0x20, Unknown_20h),
(0x30, Unknown_30h),
(0x40, Unknown_40h),
(0x60, Unknown_60h),
(0x70, Unknown_70h),
(0x80, Unknown_80h),
(0xA0, Unknown_A0h),
(0xB0, Unknown_B0h),
(0xC0, Unknown_C0h),
(0xE0, Unknown_E0h),
(0xF0, Unknown_F0h),
(0x100, Unknown_100h),
(0x128, Unknown_128h)
};
}
}
@ -1335,20 +1343,23 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
if (Bound != null) list.Add(Bound);
if (Behavior != null) list.Add(Behavior);
if (Unknown_140h_Data != null) list.Add(Unknown_140h_Data);
using var list = new PooledList<IResourceBlock>();
if (Bound is not null)
list.Add(Bound);
if (Behavior is not null)
list.Add(Behavior);
if (Unknown_140h_Data is not null)
list.Add(Unknown_140h_Data);
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x70, Vertices2),
new Tuple<long, IResourceBlock>(0x80, Vertices),
new Tuple<long, IResourceBlock>(0x100, Constraints2),
new Tuple<long, IResourceBlock>(0x110, Constraints)
return new (long, IResourceBlock)[] {
(0x70, Vertices2),
(0x80, Vertices),
(0x100, Constraints2),
(0x110, Constraints)
};
}
}
@ -1794,12 +1805,12 @@ namespace CodeWalker.GameFiles
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x10, Unknown_10h),
new Tuple<long, IResourceBlock>(0x30, Unknown_30h),
new Tuple<long, IResourceBlock>(0x90, Unknown_90h)
return new (long, IResourceBlock)[] {
(0x10, Unknown_10h),
(0x30, Unknown_30h),
(0x90, Unknown_90h)
};
}
}
@ -1923,14 +1934,14 @@ namespace CodeWalker.GameFiles
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x80, Indices),
new Tuple<long, IResourceBlock>(0x90, Vertices),
new Tuple<long, IResourceBlock>(0xB0, Unknown_B0h),
new Tuple<long, IResourceBlock>(0xC0, BoneWeightsInds),
new Tuple<long, IResourceBlock>(0xE0, BoneIds)
return new (long, IResourceBlock)[] {
(0x80, Indices),
(0x90, Vertices),
(0xB0, Unknown_B0h),
(0xC0, BoneWeightsInds),
(0xE0, BoneIds)
};
}
}
@ -2339,7 +2350,7 @@ namespace CodeWalker.GameFiles
YldXml.ValueTag(sb, indent, "Unknown180", FloatUtil.ToString(Unknown_180h));
if (Unknown_50h?.data_items != null)
{
YldXml.WriteRawArray(sb, Unknown_50h.data_items, indent, "Unknown50", "", v => FloatUtil.GetVector4String(v), 1);
YldXml.WriteRawArray(sb, Unknown_50h.data_items, indent, "Unknown50", "", FloatUtil.GetVector4String, 1);
}
if (Unknown_60h?.data_items != null)
{
@ -2359,7 +2370,7 @@ namespace CodeWalker.GameFiles
}
if (Unknown_A0h?.data_items != null)
{
YldXml.WriteRawArray(sb, Unknown_A0h.data_items, indent, "UnknownA0", "", v => FloatUtil.GetVector4String(v), 1);
YldXml.WriteRawArray(sb, Unknown_A0h.data_items, indent, "UnknownA0", "", FloatUtil.GetVector4String, 1);
}
if (Unknown_B0h?.data_items != null)
{
@ -2415,21 +2426,21 @@ namespace CodeWalker.GameFiles
Unknown_160h.data_items = Xml.GetChildRawUshortArrayNullable(node, "Unknown160");
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x50, Unknown_50h),
new Tuple<long, IResourceBlock>(0x60, Unknown_60h),
new Tuple<long, IResourceBlock>(0x70, Unknown_70h),
new Tuple<long, IResourceBlock>(0x80, Unknown_80h),
new Tuple<long, IResourceBlock>(0x90, Unknown_90h),
new Tuple<long, IResourceBlock>(0xA0, Unknown_A0h),
new Tuple<long, IResourceBlock>(0xB0, Unknown_B0h),
new Tuple<long, IResourceBlock>(0xC0, Unknown_C0h),
new Tuple<long, IResourceBlock>(0xD0, Unknown_D0h),
new Tuple<long, IResourceBlock>(0xE0, Unknown_E0h),
new Tuple<long, IResourceBlock>(0x150, Unknown_150h),
new Tuple<long, IResourceBlock>(0x160, Unknown_160h)
return new (long, IResourceBlock)[] {
(0x50, Unknown_50h),
(0x60, Unknown_60h),
(0x70, Unknown_70h),
(0x80, Unknown_80h),
(0x90, Unknown_90h),
(0xA0, Unknown_A0h),
(0xB0, Unknown_B0h),
(0xC0, Unknown_C0h),
(0xD0, Unknown_D0h),
(0xE0, Unknown_E0h),
(0x150, Unknown_150h),
(0x160, Unknown_160h)
};
}
}

File diff suppressed because it is too large Load Diff

View File

@ -147,11 +147,11 @@ namespace CodeWalker.GameFiles
var list = new List<IResourceBlock>(base.GetReferences());
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, ExpressionNameHashes),
new Tuple<long, IResourceBlock>(0x30, Expressions)
return new (long, IResourceBlock)[] {
(0x20, ExpressionNameHashes),
(0x30, Expressions)
};
}
@ -416,13 +416,13 @@ namespace CodeWalker.GameFiles
if (Name != null) list.Add(Name);
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, Streams),
new Tuple<long, IResourceBlock>(0x30, BoneTracks),
new Tuple<long, IResourceBlock>(0x40, JiggleData),
new Tuple<long, IResourceBlock>(0x50, ItemHashes)
return new (long, IResourceBlock)[] {
(0x20, Streams),
(0x30, BoneTracks),
(0x40, JiggleData),
(0x50, ItemHashes)
};
}

View File

@ -90,11 +90,11 @@ namespace CodeWalker.GameFiles
writer.WriteBlock(this.Filters);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, FilterNameHashes),
new Tuple<long, IResourceBlock>(0x30, Filters)
return new (long, IResourceBlock)[] {
(0x20, FilterNameHashes),
(0x30, Filters)
};
}
@ -215,9 +215,9 @@ namespace CodeWalker.GameFiles
writer.Write(this.Unknown_14h);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return Array.Empty<Tuple<long, IResourceBlock>>();
return Array.Empty<(long, IResourceBlock)>();
}
public IResourceSystemBlock GetType(ResourceDataReader reader, params object[] parameters)
@ -364,11 +364,11 @@ namespace CodeWalker.GameFiles
writer.Write(this.Unknown_38h);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x18, Entries),
new Tuple<long, IResourceBlock>(0x28, Weights)
return new (long, IResourceBlock)[] {
(0x18, Entries),
(0x28, Weights)
};
}
@ -396,7 +396,7 @@ namespace CodeWalker.GameFiles
public void SortEntries()
{
if (Entries?.data_items == null)
if (Entries?.data_items == null || Entries.data_items.Length == 0)
{
return;
}
@ -408,12 +408,12 @@ namespace CodeWalker.GameFiles
{
// CRC-32 hash of the Entries and Weights arrays
uint s = 0;
if (Entries?.data_items != null && Entries?.data_items.Length > 0)
if (Entries?.data_items is not null && Entries?.data_items.Length > 0)
{
var data = MetaTypes.ConvertArrayToBytes(Entries.data_items);
s = Crc32Hash(data, s);
}
if (Weights?.data_items != null && Weights?.data_items.Length > 0)
if (Weights?.data_items is not null && Weights?.data_items.Length > 0)
{
var data = MetaTypes.ConvertArrayToBytes(Weights.data_items);
s = Crc32Hash(data, s);
@ -430,7 +430,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return BoneId + ", " + Track + ": " + WeightIndex;
return $"{BoneId}, {Track}: {WeightIndex}";
}
public uint GetSortKey() => (uint)(BoneId | (Track << 16));

View File

@ -32,6 +32,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static CodeWalker.GameFiles.MetaXmlBase;
namespace CodeWalker.GameFiles
{
@ -736,11 +737,11 @@ namespace CodeWalker.GameFiles
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x60, Cloths),
new Tuple<long, IResourceBlock>(0x110, LightAttributes)
return new (long, IResourceBlock)[] {
(0x60, Cloths),
(0x110, LightAttributes)
};
}
}
@ -2854,11 +2855,11 @@ namespace CodeWalker.GameFiles
{
if (ItemIndices != null)
{
YftXml.WriteRawArray(sb, ItemIndices, indent, "ItemIndices", "", null, 22);
YftXml.WriteRawArray(sb, ItemIndices, indent, "ItemIndices", "", (FormatterRef<uint>?)null, 22);
}
if (ItemFlags != null)
{
YftXml.WriteRawArray(sb, ItemFlags, indent, "ItemFlags", "", null, 22);
YftXml.WriteRawArray(sb, ItemFlags, indent, "ItemFlags", "", (FormatterRef<byte>?)null, 22);
}
if (UnknownVectors != null)
{
@ -4362,7 +4363,7 @@ namespace CodeWalker.GameFiles
Unknown_24h = u(36);
}
public override string ToString()
public override readonly string ToString()
{
UintStringBuilder usb = new UintStringBuilder();
usb.Add(Unknown_00h);

View File

@ -350,12 +350,13 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "(Size: " + FloatUtil.GetVector3String(AABBSize) + ")";
return $"(Size: {FloatUtil.GetVector3String(AABBSize)})";
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshUintArray
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct NavMeshUintArray
{
public uint Count { get; set; }
public uint v00;
@ -391,7 +392,7 @@ namespace CodeWalker.GameFiles
public uint v30; // 0x00000000
public uint v31; // 0x00000000
public uint[] RawValues
public readonly uint[] RawValues
{
get
{
@ -404,7 +405,7 @@ namespace CodeWalker.GameFiles
public uint[] Values
{
get
readonly get
{
uint[] vals = new uint[Count];
uint[] rvals = RawValues;
@ -452,44 +453,43 @@ namespace CodeWalker.GameFiles
}
}
public uint Get(uint i)
public readonly uint Get(uint i)
{
switch (i)
return i switch
{
default:
case 0: return v00;
case 1: return v01;
case 2: return v02;
case 3: return v03;
case 4: return v04;
case 5: return v05;
case 6: return v06;
case 7: return v07;
case 8: return v08;
case 9: return v09;
case 10: return v10;
case 11: return v11;
case 12: return v12;
case 13: return v13;
case 14: return v14;
case 15: return v15;
case 16: return v16;
case 17: return v17;
case 18: return v18;
case 19: return v19;
case 20: return v20;
case 21: return v21;
case 22: return v22;
case 23: return v23;
case 24: return v24;
case 25: return v25;
case 26: return v26;
case 27: return v27;
case 28: return v28;
case 29: return v29;
case 30: return v30;
case 31: return v31;
}
1 => v01,
2 => v02,
3 => v03,
4 => v04,
5 => v05,
6 => v06,
7 => v07,
8 => v08,
9 => v09,
10 => v10,
11 => v11,
12 => v12,
13 => v13,
14 => v14,
15 => v15,
16 => v16,
17 => v17,
18 => v18,
19 => v19,
20 => v20,
21 => v21,
22 => v22,
23 => v23,
24 => v24,
25 => v25,
26 => v26,
27 => v27,
28 => v28,
29 => v29,
30 => v30,
31 => v31,
_ => v00,
};
}
public void Set(uint[] arr)
@ -497,19 +497,17 @@ namespace CodeWalker.GameFiles
Values = arr;
}
public override string ToString()
public override readonly string ToString()
{
return "(Count: " + Count.ToString() + ")";
return $"(Count: {Count})";
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshList<T> : ResourceSystemBlock where T : struct
[TypeConverter(typeof(ExpandableObjectConverter))]
public class NavMeshList<T> : ResourceSystemBlock where T : struct
{
public override long BlockLength
{
get { return 48; }
}
public override long BlockLength => 48;
public uint VFT { get; set; }
public uint Unknown_04h { get; set; } // 0x00000001
@ -525,16 +523,9 @@ namespace CodeWalker.GameFiles
public ResourceSimpleArray<NavMeshListPart<T>> ListParts { get; set; }
public uint[] ListOffsets { get; set; }
private ResourceSystemStructBlock<uint> ListOffsetsBlock = null;
public int ItemSize { get { return System.Runtime.InteropServices.Marshal.SizeOf<T>(); } }
public uint ByteCount
{
get
{
return ItemCount * (uint)ItemSize;
}
}
private ResourceSystemStructBlock<uint>? ListOffsetsBlock = null;
public int ItemSize => System.Runtime.InteropServices.Marshal.SizeOf<T>();
public uint ByteCount => ItemCount * (uint)ItemSize;
@ -577,13 +568,18 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
if (ListParts != null) list.Add(ListParts);
if (ListParts is not null)
list.Add(ListParts);
if ((ListOffsets != null) && (ListOffsets.Length > 0))
if (ListOffsets is not null && ListOffsets.Length > 0)
{
ListOffsetsBlock = new ResourceSystemStructBlock<uint>(ListOffsets);
list.Add(ListOffsetsBlock);
}
else
{
ListOffsetsBlock = null;
}
return list.ToArray();
}
@ -594,11 +590,11 @@ namespace CodeWalker.GameFiles
{
List<T> list = new List<T>((int)ItemCount);
if (ListParts != null)
if (ListParts is not null)
{
foreach (var part in ListParts)
{
if (part.Items != null)
if (part.Items is not null)
{
list.AddRange(part.Items);
}
@ -646,16 +642,14 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "(" + ItemCount.ToString() + " total items, " + ListPartsCount.ToString() + " parts)";
return $"({ItemCount} total items, {ListPartsCount} parts)";
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshListPart<T> : ResourceSystemBlock where T : struct
[TypeConverter(typeof(ExpandableObjectConverter))]
public class NavMeshListPart<T> : ResourceSystemBlock where T : struct
{
public override long BlockLength
{
get { return 16; }
}
public override long BlockLength => 16;
public ulong Pointer { get; set; }
public uint Count { get; set; }
@ -701,7 +695,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "(" + Count.ToString() + " items)";
return $"({Count} items)";
}
}
@ -714,14 +708,14 @@ namespace CodeWalker.GameFiles
public ushort Z { get; set; }
public Vector3 Position { get { return ToVector3(); } set { FromVector3(value); } }
public Vector3 Position { readonly get { return ToVector3(); } set { FromVector3(value); } }
public Vector3 ToVector3()
public readonly Vector3 ToVector3()
{
const float usmax = ushort.MaxValue;
return new Vector3(X / usmax, Y / usmax, Z / usmax);
}
public void FromVector3(Vector3 v)
public void FromVector3(in Vector3 v)
{
const float usmax = ushort.MaxValue;
X = (ushort)Math.Round(v.X * usmax);
@ -729,44 +723,53 @@ namespace CodeWalker.GameFiles
Z = (ushort)Math.Round(v.Z * usmax);
}
public static NavMeshVertex Create(Vector3 v)
public static NavMeshVertex Create(in Vector3 v)
{
var nmv = new NavMeshVertex();
nmv.FromVector3(v);
nmv.FromVector3(in v);
return nmv;
}
public override string ToString()
{
return X.ToString() + ", " + Y.ToString() + ", " + Z.ToString();
}
public override readonly string ToString() => $"{X}, {Y}, {Z}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAABB
[TypeConverter(typeof(ExpandableObjectConverter))]
public readonly struct NavMeshAABB(in Vector3 min, in Vector3 max)
{
public short MinX { get; set; }
public short MaxX { get; set; }
public short MinY { get; set; }
public short MaxY { get; set; }
public short MinZ { get; set; }
public short MaxZ { get; set; }
public short MinX { get; init; } = (short)Math.Floor(min.X * 4.0f);
public short MaxX { get; init; } = (short)Math.Ceiling(max.X * 4.0f);
public short MinY { get; init; } = (short)Math.Floor(min.Y * 4.0f);
public short MaxY { get; init; } = (short)Math.Ceiling(max.Y * 4.0f);
public short MinZ { get; init; } = (short)Math.Floor(min.Z * 4.0f);
public short MaxZ { get; init; } = (short)Math.Ceiling(max.Z * 4.0f);
public Vector3 Min
public readonly Vector3 Min
{
get { return new Vector3(MinX / 4.0f, MinY / 4.0f, MinZ / 4.0f); }
set { var v = value * 4.0f; MinX = (short)Math.Floor(v.X); MinY = (short)Math.Floor(v.Y); MinZ = (short)Math.Floor(v.Z); }
get => new Vector3(MinX / 4.0f, MinY / 4.0f, MinZ / 4.0f);
//set
//{
// var v = value * 4.0f;
// MinX = (short)Math.Floor(v.X);
// MinY = (short)Math.Floor(v.Y);
// MinZ = (short)Math.Floor(v.Z);
//}
}
public Vector3 Max
public readonly Vector3 Max
{
get { return new Vector3(MaxX / 4.0f, MaxY / 4.0f, MaxZ / 4.0f); }
set { var v = value * 4.0f; MaxX = (short)Math.Ceiling(v.X); MaxY = (short)Math.Ceiling(v.Y); MaxZ = (short)Math.Ceiling(v.Z); }
get => new Vector3(MaxX / 4.0f, MaxY / 4.0f, MaxZ / 4.0f);
//set {
// var v = value * 4.0f;
// MaxX = (short)Math.Ceiling(v.X);
// MaxY = (short)Math.Ceiling(v.Y);
// MaxZ = (short)Math.Ceiling(v.Z);
//}
}
public override string ToString()
public override readonly string ToString()
{
Vector3 min = Min;
Vector3 max = Max;
return string.Format("({0}, {1}, {2}) | ({3}, {4}, {5})", min.X, min.Y, min.Z, max.X, max.Y, max.Z);
return $"({min.X}, {min.Y}, {min.Z}) | ({max.X}, {max.Y}, {max.Z})";
//return string.Format("({0}, {1}, {2}) | ({3}, {4}, {5})", MinX, MinY, MinZ, MaxX, MaxY, MaxZ);
}
}
@ -777,43 +780,35 @@ namespace CodeWalker.GameFiles
{
public NavMeshEdgePart _Poly1;
public NavMeshEdgePart _Poly2;
public NavMeshEdgePart Poly1 { get { return _Poly1; } set { _Poly1 = value; } }
public NavMeshEdgePart Poly2 { get { return _Poly2; } set { _Poly2 = value; } }
public readonly NavMeshEdgePart Poly1 => _Poly1;
public readonly NavMeshEdgePart Poly2 => _Poly2;
public override string ToString()
{
return //Poly1.Bin + " | " + Poly2.Bin + " | " +
_Poly1.ToString() + " | " + _Poly2.ToString();
}
public override readonly string ToString() => $"{_Poly1} | {_Poly2}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdgePart
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct NavMeshEdgePart
{
public uint Value { get; set; }
public string Bin
{
get
{
return Convert.ToString(Value, 2).PadLeft(32, '0');
}
}
public readonly string Bin => Value.ToString("b32");
public uint AreaIDInd { get { return (Value >> 0) & 0x1F; } set { Value = (Value & 0xFFFFFFE0) | (value & 0x1F); } }
public uint PolyID { get { return (Value >> 5) & 0x3FFF; } set { Value = (Value & 0xFFF8001F) | ((value & 0x3FFF) << 5); } }
public uint Unk2 { get { return (Value >> 19) & 0x3; } set { Value = (Value & 0xFFE7FFFF) | ((value & 0x3) << 19); } }
public uint Unk3 { get { return (Value >> 21) & 0x7FF; } set { Value = (Value & 0x001FFFFF) | ((value & 0x7FF) << 21); } }
public uint AreaIDInd { readonly get { return (Value >> 0) & 0x1F; } set { Value = (Value & 0xFFFFFFE0) | (value & 0x1F); } }
public uint PolyID { readonly get { return (Value >> 5) & 0x3FFF; } set { Value = (Value & 0xFFF8001F) | ((value & 0x3FFF) << 5); } }
public uint Unk2 { readonly get { return (Value >> 19) & 0x3; } set { Value = (Value & 0xFFE7FFFF) | ((value & 0x3) << 19); } }
public uint Unk3 { readonly get { return (Value >> 21) & 0x7FF; } set { Value = (Value & 0x001FFFFF) | ((value & 0x7FF) << 21); } }
public override string ToString()
public override readonly string ToString()
{
string pid = (PolyID == 0x3FFF) ? "-" : PolyID.ToString();
return AreaIDInd.ToString() + ", " + pid + ", " + Unk2.ToString() + ", " + Unk3.ToString();
return $"{AreaIDInd}, {pid}, {Unk2}, {Unk3}";
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPoly
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct NavMeshPoly
{
public ushort PolyFlags0 { get; set; }
public ushort IndexFlags { get; set; }
@ -830,52 +825,36 @@ namespace CodeWalker.GameFiles
//public int IndexUnk { get { return (IndexFlags >> 0) & 31; } } //always 0
public int IndexCount { get { return (IndexFlags >> 5); } set { IndexFlags = (ushort)((IndexFlags & 31) | ((value & 0x7FF) << 5)); } }
public int IndexCount { readonly get { return (IndexFlags >> 5); } set { IndexFlags = (ushort)((IndexFlags & 31) | ((value & 0x7FF) << 5)); } }
//public int PartUnk1 { get { return (PartFlags >> 0) & 0xF; } } //always 0
public ushort PartID { get { return (ushort)((PartFlags >> 4) & 0xFF); } set { PartFlags = ((PartFlags & 0xFFFFF00F) | (((uint)value & 0xFF) << 4)); } }
public byte PortalLinkCount { get { return (byte)((PartFlags >> 12) & 0x7); } set { PartFlags = ((PartFlags & 0xFFFF8FFF) | (((uint)value & 0x7) << 12)); } }
public uint PortalLinkID { get { return ((PartFlags >> 15) & 0x1FFFF); } set { PartFlags = ((PartFlags & 0x7FFF) | ((value & 0x1FFFF) << 15)); } }
public ushort PartID { readonly get { return (ushort)((PartFlags >> 4) & 0xFF); } set { PartFlags = ((PartFlags & 0xFFFFF00F) | (((uint)value & 0xFF) << 4)); } }
public byte PortalLinkCount { readonly get { return (byte)((PartFlags >> 12) & 0x7); } set { PartFlags = ((PartFlags & 0xFFFF8FFF) | (((uint)value & 0x7) << 12)); } }
public uint PortalLinkID { readonly get { return ((PartFlags >> 15) & 0x1FFFF); } set { PartFlags = ((PartFlags & 0x7FFF) | ((value & 0x1FFFF) << 15)); } }
public byte UnkX { get { return (byte)((PolyFlags2 >> 0) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFFFF00) | ((value & 0xFFu)<<0); } }
public byte UnkY { get { return (byte)((PolyFlags2 >> 8) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFF00FF) | ((value & 0xFFu)<<8); } }
public byte UnkX { readonly get { return (byte)((PolyFlags2 >> 0) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFFFF00) | ((value & 0xFFu)<<0); } }
public byte UnkY { readonly get { return (byte)((PolyFlags2 >> 8) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFF00FF) | ((value & 0xFFu)<<8); } }
public byte Flags1 { get { return (byte)(PolyFlags0 & 0xFF); } set { PolyFlags0 = (ushort)((PolyFlags0 & 0xFF00) | (value & 0xFF)); } }
public byte Flags2 { get { return (byte)((PolyFlags1 >> 0) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFFFF00u) | ((value & 0xFFu) << 0)); } }
public byte Flags3 { get { return (byte)((PolyFlags1 >> 9) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFE01FFu) | ((value & 0xFFu) << 9)); } }
public byte Flags4 { get { return (byte)((PolyFlags2 >> 16) & 0xFF); } set { PolyFlags2 = ((PolyFlags2 & 0xFF00FFFFu) | ((value & 0xFFu) << 16)); } }
public byte Flags1 { readonly get { return (byte)(PolyFlags0 & 0xFF); } set { PolyFlags0 = (ushort)((PolyFlags0 & 0xFF00) | (value & 0xFF)); } }
public byte Flags2 { readonly get { return (byte)((PolyFlags1 >> 0) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFFFF00u) | ((value & 0xFFu) << 0)); } }
public byte Flags3 { readonly get { return (byte)((PolyFlags1 >> 9) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFE01FFu) | ((value & 0xFFu) << 9)); } }
public byte Flags4 { readonly get { return (byte)((PolyFlags2 >> 16) & 0xFF); } set { PolyFlags2 = ((PolyFlags2 & 0xFF00FFFFu) | ((value & 0xFFu) << 16)); } }
//public uint UnkFlags0 { get { return (uint)((PolyFlags0 >> 8) & 0xFF); } } //always 0
//public uint UnkFlags1 { get { return (uint)((PolyFlags1 >> 17) & 0xFFFF); } } //always 0
//public uint UnkFlags2 { get { return (uint)((PolyFlags2 >> 24) & 0xFF); } } //always 0
public override string ToString()
{
return
PolyFlags0.ToString() + ", " +
//IndexFlags.ToString() + ", " +
IndexCount.ToString() + ", " + //IndexUnk.ToString() + ", " +
IndexID.ToString() + ", " + AreaID.ToString() + ", " +
CellAABB.ToString() + ", " +
//PolyFlags1.ToString() + ", " +
//PolyFlags2.ToString() + ", " +
//PartFlags.ToString() + ", " + //PartUnk1.ToString() + ", " +
PartID.ToString() + ", " +
PortalLinkCount.ToString() + ", " +
PortalLinkID.ToString();
}
public override readonly string ToString() => $"{PolyFlags0}, {IndexCount}, {IndexID}, {AreaID}, {CellAABB}, {PartID}, {PortalLinkCount}, {PortalLinkID}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshSector : ResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public class NavMeshSector : ResourceSystemBlock
{
public override long BlockLength
{
get { return 96; }
}
public override long BlockLength => 96;
public Vector4 AABBMin { get; set; } //W==NaN
public Vector4 AABBMax { get; set; } //W==NaN
@ -913,7 +892,8 @@ namespace CodeWalker.GameFiles
{
get
{
if (Data == null) return 0;
if (Data == null)
return 0;
return Data.PointsCount;
}
}
@ -976,22 +956,17 @@ namespace CodeWalker.GameFiles
{
AABBMin = new Vector4(min, float.NaN);
AABBMax = new Vector4(max, float.NaN);
CellAABB = new NavMeshAABB() { Min = min, Max = max };
CellAABB = new NavMeshAABB(in min, in max);
}
public override string ToString()
{
return "[Min: "+AABBMin.ToString() + "], [Max:" + AABBMax.ToString() + "]";
}
public override string ToString() => $"[Min: {AABBMin}], [Max:{AABBMax}]";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshSectorData : ResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public class NavMeshSectorData : ResourceSystemBlock
{
public override long BlockLength
{
get { return 32; }
}
public override long BlockLength => 32;
public uint PointsStartID { get; set; }
public uint Unused_04h { get; set; } // 0x00000000
@ -1004,8 +979,8 @@ namespace CodeWalker.GameFiles
public ushort[] PolyIDs { get; set; }
public NavMeshPoint[] Points { get; set; }
public ResourceSystemStructBlock<ushort> PolyIDsBlock = null;
public ResourceSystemStructBlock<NavMeshPoint> PointsBlock = null;
public ResourceSystemStructBlock<ushort>? PolyIDsBlock = null;
public ResourceSystemStructBlock<NavMeshPoint>? PointsBlock = null;
public override void Read(ResourceDataReader reader, params object[] parameters)
{
@ -1043,12 +1018,12 @@ namespace CodeWalker.GameFiles
{
var list = new List<IResourceBlock>();
if ((PolyIDs != null) && (PolyIDs.Length > 0))
if (PolyIDs != null && PolyIDs.Length > 0)
{
PolyIDsBlock = new ResourceSystemStructBlock<ushort>(PolyIDs);
list.Add(PolyIDsBlock);
}
if ((Points != null) && (Points.Length > 0))
if (Points != null && Points.Length > 0)
{
PointsBlock = new ResourceSystemStructBlock<NavMeshPoint>(Points);
list.Add(PointsBlock);
@ -1060,7 +1035,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return "(Polys: " + PolyIDsCount.ToString() + ", PointsCount: " + PointsCount.ToString() + ", PointsStartID: " + PointsStartID.ToString() + ")";
return $"(Polys: {PolyIDsCount}, PointsCount: {PointsCount}, PointsStartID: {PointsStartID})";
}
}
@ -1075,7 +1050,7 @@ namespace CodeWalker.GameFiles
public Vector3 Position
{
get
readonly get
{
const float usmax = ushort.MaxValue;
return new Vector3(X / usmax, Y / usmax, Z / usmax);
@ -1089,9 +1064,9 @@ namespace CodeWalker.GameFiles
}
}
public override string ToString()
public override readonly string ToString()
{
return Type.ToString() + ": " + Angle.ToString() + ", " + Position.ToString();
return $"{Type}: {Angle}, {Position}";
}
@ -1099,7 +1074,8 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPortal
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct NavMeshPortal
{
public byte Type { get; set; }//1,2,3
public byte Angle { get; set; }
@ -1112,18 +1088,11 @@ namespace CodeWalker.GameFiles
public ushort PolyIDTo2 { get; set; } //always same as PolyIDTo1
public uint AreaFlags { get; set; }
public ushort AreaIDFrom { get { return (ushort)(AreaFlags & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xFFFFC000) | (value & 0x3FFFu); } }//always Ynv.AreaID
public ushort AreaIDTo { get { return (ushort)((AreaFlags >> 14) & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xF0003FFF) | ((value & 0x3FFFu) << 14); } }//always Ynv.AreaID
public byte AreaUnk { get { return (byte)((AreaFlags >> 28) & 0xF); } set { AreaFlags = (AreaFlags & 0x0FFFFFFF) | ((value & 0xFu) << 28); } }//always 0
public ushort AreaIDFrom { readonly get { return (ushort)(AreaFlags & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xFFFFC000) | (value & 0x3FFFu); } }//always Ynv.AreaID
public ushort AreaIDTo { readonly get { return (ushort)((AreaFlags >> 14) & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xF0003FFF) | ((value & 0x3FFFu) << 14); } }//always Ynv.AreaID
public byte AreaUnk { readonly get { return (byte)((AreaFlags >> 28) & 0xF); } set { AreaFlags = (AreaFlags & 0x0FFFFFFF) | ((value & 0xFu) << 28); } }//always 0
public override string ToString()
{
return AreaIDFrom.ToString() + ", " + AreaIDTo.ToString() + ", " + AreaUnk.ToString() + ", " +
PolyIDFrom1.ToString() + ", " + PolyIDFrom2.ToString() + ", " +
PolyIDTo1.ToString() + ", " + PolyIDTo2.ToString() + ", " +
Type.ToString() + ", " + Angle.ToString() + ", " + FlagsUnk.ToString() + ", " +
"(" + PositionFrom.ToString() + " | " + PositionTo.ToString() + ")";
}
public override readonly string ToString() => $"{AreaIDFrom}, {AreaIDTo}, {AreaUnk}, {PolyIDFrom1}, {PolyIDFrom2}, {PolyIDTo1}, {PolyIDTo2}, {Type}, {Angle}, {FlagsUnk}, ({PositionFrom} | {PositionTo})";
}

View File

@ -23,6 +23,7 @@
//mangled to fit
using Collections.Pooled;
using SharpDX;
using System;
using System.Collections.Generic;
@ -36,10 +37,7 @@ namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class NodeDictionary : ResourceFileBase, IMetaXmlItem
{
public override long BlockLength
{
get { return 112; }
}
public override long BlockLength => 112;
public ulong NodesPointer { get; set; }
public uint NodesCount { get; set; }
@ -62,18 +60,18 @@ namespace CodeWalker.GameFiles
public uint Unk68 { get; set; } // 0x00000000
public uint Unk6C { get; set; } // 0x00000000
public Node[] Nodes { get; set; }
public NodeLink[] Links { get; set; }
public NodeJunction[] Junctions { get; set; }
public byte[] JunctionHeightmapBytes { get; set; }
public NodeJunctionRef[] JunctionRefs { get; set; }
public Node[]? Nodes { get; set; }
public NodeLink[]? Links { get; set; }
public NodeJunction[]? Junctions { get; set; }
public byte[]? JunctionHeightmapBytes { get; set; }
public NodeJunctionRef[]? JunctionRefs { get; set; }
private ResourceSystemStructBlock<Node> NodesBlock = null;
private ResourceSystemStructBlock<NodeLink> LinksBlock = null;
private ResourceSystemStructBlock<NodeJunction> JunctionsBlock = null;
private ResourceSystemStructBlock<byte> JunctionHeightmapBytesBlock = null;
private ResourceSystemStructBlock<NodeJunctionRef> JunctionRefsBlock = null;
private ResourceSystemStructBlock<Node>? NodesBlock = null;
private ResourceSystemStructBlock<NodeLink>? LinksBlock = null;
private ResourceSystemStructBlock<NodeJunction>? JunctionsBlock = null;
private ResourceSystemStructBlock<byte>? JunctionHeightmapBytesBlock = null;
private ResourceSystemStructBlock<NodeJunctionRef>? JunctionRefsBlock = null;
@ -155,29 +153,29 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>(base.GetReferences());
using var list = new PooledList<IResourceBlock>(base.GetReferences());
if ((JunctionRefs != null) && (JunctionRefs.Length > 0))
if (JunctionRefs is not null && JunctionRefs.Length > 0)
{
JunctionRefsBlock = new ResourceSystemStructBlock<NodeJunctionRef>(JunctionRefs);
list.Add(JunctionRefsBlock);
}
if ((JunctionHeightmapBytes != null) && (JunctionHeightmapBytes.Length > 0))
if (JunctionHeightmapBytes is not null && JunctionHeightmapBytes.Length > 0)
{
JunctionHeightmapBytesBlock = new ResourceSystemStructBlock<byte>(JunctionHeightmapBytes);
list.Add(JunctionHeightmapBytesBlock);
}
if ((Junctions != null) && (Junctions.Length > 0))
if (Junctions is not null && Junctions.Length > 0)
{
JunctionsBlock = new ResourceSystemStructBlock<NodeJunction>(Junctions);
list.Add(JunctionsBlock);
}
if ((Links != null) && (Links.Length > 0))
if (Links is not null && Links.Length > 0)
{
LinksBlock = new ResourceSystemStructBlock<NodeLink>(Links);
list.Add(LinksBlock);
}
if ((Nodes != null) && (Nodes.Length > 0))
if (Nodes is not null && Nodes.Length > 0)
{
NodesBlock = new ResourceSystemStructBlock<Node>(Nodes);
list.Add(NodesBlock);
@ -195,7 +193,7 @@ namespace CodeWalker.GameFiles
YndXml.ValueTag(sb, indent, "VehicleNodeCount", NodesCountVehicle.ToString());
YndXml.ValueTag(sb, indent, "PedNodeCount", NodesCountPed.ToString());
XmlNodeWrapper[] nodes = null;
XmlNodeWrapper[]? nodes = null;
int nodecount = Nodes?.Length ?? 0;
if (nodecount > 0)
{
@ -208,7 +206,7 @@ namespace CodeWalker.GameFiles
YndXml.WriteItemArray(sb, nodes, indent, "Nodes");
XmlJunctionWrapper[] juncs = null;
XmlJunctionWrapper[]? juncs = null;
int junccount = Junctions?.Length ?? 0;
if (junccount > 0)
{
@ -337,7 +335,8 @@ namespace CodeWalker.GameFiles
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct Node
[TypeConverter(typeof(ExpandableObjectConverter))]
public struct Node
{
public uint Unused0 { get; set; } // 0x00000000
public uint Unused1 { get; set; } // 0x00000000
@ -358,7 +357,7 @@ namespace CodeWalker.GameFiles
public FlagsByte Flags3 { get; set; }
public FlagsByte Flags4 { get; set; }
public override string ToString()
public override readonly string ToString()
{
//return Unused0.ToString() + ", " + Unused1.ToString() + ", " + Unused2.ToString() + ", " +
// Unused3.ToString() + ", " + AreaID.ToString() + ", " + NodeID.ToString() + ", " +
@ -366,7 +365,7 @@ namespace CodeWalker.GameFiles
// PositionX.ToString() + ", " + PositionY.ToString() + ", " + Unk20.ToString() + ", " + Unk21.ToString() + ", " +
// Unk22.ToString() + ", " + Unk24.ToString() + ", " + Unk26.ToString();
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + StreetName.ToString();// + ", X:" +
return $"{AreaID}, {NodeID}, {StreetName}";// + ", X:" +
//PositionX.ToString() + ", Y:" + PositionY.ToString() + ", " + PositionZ.ToString();// + ", " +
//Flags0.ToString() + ", " + Flags1.ToString() + ", Z:" +
//Flags2.ToString() + ", " + LinkCountFlags.ToString() + ", " +
@ -374,7 +373,7 @@ namespace CodeWalker.GameFiles
}
public void WriteXml(StringBuilder sb, int indent, NodeLink[] allLinks)
public readonly void WriteXml(StringBuilder sb, int indent, NodeLink[] allLinks)
{
Vector3 p = new Vector3();
p.X = PositionX / 4.0f;
@ -449,9 +448,9 @@ namespace CodeWalker.GameFiles
public FlagsByte Flags2 { get; set; }
public FlagsByte LinkLength { get; set; }
public override string ToString()
public override readonly string ToString()
{
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + Flags0.Value.ToString() + ", " + Flags1.Value.ToString() + ", " + Flags2.Value.ToString() + ", " + LinkLength.Value.ToString();
return $"{AreaID}, {NodeID}, {Flags0.Value}, {Flags1.Value}, {Flags2.Value}, {LinkLength.Value}";
}
public void WriteXml(StringBuilder sb, int indent)
@ -486,7 +485,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return PositionX.ToString() + ", " + PositionY.ToString() + ": " + MinZ.ToString() + ", " + MaxZ.ToString() + ": " + HeightmapDimX.ToString() + " x " + HeightmapDimY.ToString();
return $"{PositionX}, {PositionY}: {MinZ}, {MaxZ}: {HeightmapDimX} x {HeightmapDimY}";
}
public void WriteXml(StringBuilder sb, int indent, byte[] allHeightmapData)
@ -544,7 +543,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + JunctionID.ToString();
return $"{AreaID}, {NodeID}, {JunctionID}";
}
public void WriteXml(StringBuilder sb, int indent)

View File

@ -453,11 +453,11 @@ namespace CodeWalker.GameFiles
ParticleRules.data_items = rules.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, ParticleRuleNameHashes),
new Tuple<long, IResourceBlock>(0x30, ParticleRules)
return new (long, IResourceBlock)[] {
(0x20, ParticleRuleNameHashes),
(0x30, ParticleRules)
};
}
}
@ -551,11 +551,11 @@ namespace CodeWalker.GameFiles
EffectRules.data_items = rules.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, EffectRuleNameHashes),
new Tuple<long, IResourceBlock>(0x30, EffectRules)
return new (long, IResourceBlock)[] {
(0x20, EffectRuleNameHashes),
(0x30, EffectRules)
};
}
}
@ -652,11 +652,11 @@ namespace CodeWalker.GameFiles
EmitterRules.data_items = rules.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, EmitterRuleNameHashes),
new Tuple<long, IResourceBlock>(0x30, EmitterRules)
return new (long, IResourceBlock)[] {
(0x20, EmitterRuleNameHashes),
(0x30, EmitterRules)
};
}
}
@ -1439,19 +1439,19 @@ namespace CodeWalker.GameFiles
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(88, Spawner1),
new Tuple<long, IResourceBlock>(96, Spawner2),
new Tuple<long, IResourceBlock>(0x128, BehaviourList1),
new Tuple<long, IResourceBlock>(0x138, BehaviourList2),
new Tuple<long, IResourceBlock>(0x148, BehaviourList3),
new Tuple<long, IResourceBlock>(0x158, BehaviourList4),
new Tuple<long, IResourceBlock>(0x168, BehaviourList5),
new Tuple<long, IResourceBlock>(0x188, UnknownList1),
new Tuple<long, IResourceBlock>(0x1F0, ShaderVars),
new Tuple<long, IResourceBlock>(0x210, Drawables)
return new (long, IResourceBlock)[] {
(88, Spawner1),
(96, Spawner2),
(0x128, BehaviourList1),
(0x138, BehaviourList2),
(0x148, BehaviourList3),
(0x158, BehaviourList4),
(0x168, BehaviourList5),
(0x188, UnknownList1),
(0x1F0, ShaderVars),
(0x210, Drawables)
};
}
@ -1556,10 +1556,10 @@ namespace CodeWalker.GameFiles
Unknown_40h.data_items = XmlMeta.ReadHashItemArray(node, "Unknown40");
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x40, Unknown_40h)
return new (long, IResourceBlock)[] {
(0x40, Unknown_40h)
};
}
@ -2685,14 +2685,14 @@ namespace CodeWalker.GameFiles
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(192, KeyframeProp0),
new Tuple<long, IResourceBlock>(336, KeyframeProp1),
new Tuple<long, IResourceBlock>(480, KeyframeProp2),
new Tuple<long, IResourceBlock>(624, KeyframeProp3),
new Tuple<long, IResourceBlock>(768, KeyframeProp4)
return new (long, IResourceBlock)[] {
(192, KeyframeProp0),
(336, KeyframeProp1),
(480, KeyframeProp2),
(624, KeyframeProp3),
(768, KeyframeProp4)
};
}
@ -3134,12 +3134,12 @@ namespace CodeWalker.GameFiles
}
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0, EventEmitterFlags),
new Tuple<long, IResourceBlock>(0x10, Unknown_10h),
new Tuple<long, IResourceBlock>(0x28, Unknown_28h)
return new (long, IResourceBlock)[] {
(0, EventEmitterFlags),
(0x10, Unknown_10h),
(0x28, Unknown_28h)
};
}
@ -3307,10 +3307,10 @@ namespace CodeWalker.GameFiles
Unknown_0h.data_items = XmlMeta.ReadItemArray<ParticleUnknown3>(node, "Items");
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0, Unknown_0h)
return new (long, IResourceBlock)[] {
(0, Unknown_0h)
};
}
@ -3397,10 +3397,10 @@ namespace CodeWalker.GameFiles
Unknown_0h.data_items = XmlMeta.ReadItemArray<ParticleKeyframePropValue>(node, "Keyframes");
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0, Unknown_0h)
return new (long, IResourceBlock)[] {
(0, Unknown_0h)
};
}
@ -3675,19 +3675,19 @@ namespace CodeWalker.GameFiles
return list.ToArray();
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(120, KeyframeProps1[0]),
new Tuple<long, IResourceBlock>(264, KeyframeProps1[1]),
new Tuple<long, IResourceBlock>(408, KeyframeProps1[2]),
new Tuple<long, IResourceBlock>(552, KeyframeProps1[3]),
new Tuple<long, IResourceBlock>(696, KeyframeProps1[4]),
new Tuple<long, IResourceBlock>(840, KeyframeProps1[5]),
new Tuple<long, IResourceBlock>(984, KeyframeProps1[6]),
new Tuple<long, IResourceBlock>(1128, KeyframeProps1[7]),
new Tuple<long, IResourceBlock>(1272, KeyframeProps1[8]),
new Tuple<long, IResourceBlock>(1416, KeyframeProps1[9]),
return new (long, IResourceBlock)[] {
(120, KeyframeProps1[0]),
(264, KeyframeProps1[1]),
(408, KeyframeProps1[2]),
(552, KeyframeProps1[3]),
(696, KeyframeProps1[4]),
(840, KeyframeProps1[5]),
(984, KeyframeProps1[6]),
(1128, KeyframeProps1[7]),
(1272, KeyframeProps1[8]),
(1416, KeyframeProps1[9]),
};
}
@ -3728,13 +3728,15 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
var str = ParticleKeyframeProp.GetName(Hash);
if (!string.IsNullOrEmpty(str)) return str;
if (!string.IsNullOrEmpty(str))
return str;
return YptXml.HashString((MetaHash)Hash);
}
public string ToCleanString()
{
if (Hash == 0) return string.Empty;
if (Hash == 0)
return string.Empty;
return ToString();
}
@ -3754,7 +3756,8 @@ namespace CodeWalker.GameFiles
}
[TC(typeof(EXP))] public class ParticleKeyframeProp : ResourceSystemBlock, IMetaXmlItem
[TC(typeof(EXP))]
public class ParticleKeyframeProp : ResourceSystemBlock, IMetaXmlItem
{
// datBase
// ptxKeyframeProp
@ -3917,16 +3920,16 @@ namespace CodeWalker.GameFiles
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x70, Values)
return new (long, IResourceBlock)[] {
(0x70, Values)
};
}
public override string ToString()
{
return Name.ToString() + " (" + (Values?.data_items?.Length ?? 0).ToString() + " values)";
return $"{Name} ({ Values?.data_items?.Length ?? 0} values)";
}
@ -4014,10 +4017,12 @@ namespace CodeWalker.GameFiles
d[0x851d3d14] = "ptxAttractorDomain:m_sizeInnerKFP";
NameDict = d;
}
if (NameDict.TryGetValue(hash, out string str))
{
return str;
}
return YptXml.HashString((MetaHash)hash);
}
private static Dictionary<uint, string> NameDict;
@ -4193,7 +4198,8 @@ namespace CodeWalker.GameFiles
Attractor = 3,
}
[TC(typeof(EXP))] public class ParticleDomain : ResourceSystemBlock, IResourceXXSystemBlock, IMetaXmlItem
[TC(typeof(EXP))]
public class ParticleDomain : ResourceSystemBlock, IResourceXXSystemBlock, IMetaXmlItem
{
// datBase
// ptxDomain
@ -4397,21 +4403,21 @@ namespace CodeWalker.GameFiles
}
public static void WriteXmlNode(ParticleDomain d, StringBuilder sb, int indent, string name)
{
if (d != null)
if (d is not null)
{
YptXml.OpenTag(sb, indent, name);
d.WriteXml(sb, indent + 1);
YptXml.CloseTag(sb, indent, name);
}
}
public static ParticleDomain ReadXmlNode(XmlNode node)
public static ParticleDomain? ReadXmlNode(XmlNode node)
{
if (node != null)
{
var typestr = Xml.GetChildStringAttribute(node, "Type");
var type = Xml.GetEnumValue<ParticleDomainType>(typestr);
var s = Create(type);
if (s != null)
if (s is not null)
{
s.ReadXml(node);
}
@ -4420,19 +4426,19 @@ namespace CodeWalker.GameFiles
return null;
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
KeyframeProps.ManualCountOverride = true;
KeyframeProps.ManualReferenceOverride = true;
KeyframeProps.EntriesCount = 4;
KeyframeProps.EntriesCapacity = 16;
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(24, KeyframeProp0),
new Tuple<long, IResourceBlock>(168, KeyframeProp1),
new Tuple<long, IResourceBlock>(312, KeyframeProp2),
new Tuple<long, IResourceBlock>(456, KeyframeProp3),
new Tuple<long, IResourceBlock>(0x260, KeyframeProps)
return new (long, IResourceBlock)[] {
(24, KeyframeProp0),
(168, KeyframeProp1),
(312, KeyframeProp2),
(456, KeyframeProp3),
(0x260, KeyframeProps)
};
}
@ -4591,14 +4597,14 @@ namespace CodeWalker.GameFiles
YptXml.CloseTag(sb, indent, name);
}
}
public static ParticleBehaviour ReadXmlNode(XmlNode node)
public static ParticleBehaviour? ReadXmlNode(XmlNode node)
{
if (node != null)
{
var typestr = Xml.GetChildStringAttribute(node, "Type");
var type = Xml.GetEnumValue<ParticleBehaviourType>(typestr);
var s = Create(type);
if (s != null)
if (s is not null)
{
s.ReadXml(node);
}
@ -4673,10 +4679,10 @@ namespace CodeWalker.GameFiles
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x10, KeyframeProps)
return new (long, IResourceBlock)[] {
(0x10, KeyframeProps)
};
}
@ -4814,12 +4820,12 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x10, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1)
return new (long, IResourceBlock)[] {
(0x10, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1)
};
}
}
@ -5011,14 +5017,14 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2, KeyframeProp3);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2),
new Tuple<long, IResourceBlock>(480, KeyframeProp3)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2),
(480, KeyframeProp3)
};
}
}
@ -5154,14 +5160,14 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2, KeyframeProp3);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2),
new Tuple<long, IResourceBlock>(480, KeyframeProp3)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2),
(480, KeyframeProp3)
};
}
}
@ -5263,12 +5269,12 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1)
};
}
}
@ -5344,11 +5350,11 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0)
};
}
}
@ -5496,12 +5502,12 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1)
};
}
}
@ -5622,11 +5628,11 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0)
};
}
}
@ -5746,13 +5752,13 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2)
};
}
}
@ -6146,11 +6152,11 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0)
};
}
}
@ -6405,19 +6411,19 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2, KeyframeProp3, KeyframeProp4, KeyframeProp5, KeyframeProp6, KeyframeProp7, KeyframeProp8);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2),
new Tuple<long, IResourceBlock>(480, KeyframeProp3),
new Tuple<long, IResourceBlock>(624, KeyframeProp4),
new Tuple<long, IResourceBlock>(768, KeyframeProp5),
new Tuple<long, IResourceBlock>(912, KeyframeProp6),
new Tuple<long, IResourceBlock>(1056, KeyframeProp7),
new Tuple<long, IResourceBlock>(1200, KeyframeProp8)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2),
(480, KeyframeProp3),
(624, KeyframeProp4),
(768, KeyframeProp5),
(912, KeyframeProp6),
(1056, KeyframeProp7),
(1200, KeyframeProp8)
};
}
}
@ -6706,12 +6712,12 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1)
};
}
}
@ -6825,12 +6831,12 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1)
};
}
}
@ -6956,14 +6962,14 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2, KeyframeProp3);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2),
new Tuple<long, IResourceBlock>(480, KeyframeProp3)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2),
(480, KeyframeProp3)
};
}
}
@ -7016,11 +7022,11 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0)
};
}
}
@ -7215,11 +7221,11 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0)
};
}
}
@ -7415,17 +7421,17 @@ namespace CodeWalker.GameFiles
CreateKeyframeProps(KeyframeProp0, KeyframeProp1, KeyframeProp2, KeyframeProp3, KeyframeProp4, KeyframeProp5, KeyframeProp6);
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, KeyframeProps),
new Tuple<long, IResourceBlock>(48, KeyframeProp0),
new Tuple<long, IResourceBlock>(192, KeyframeProp1),
new Tuple<long, IResourceBlock>(336, KeyframeProp2),
new Tuple<long, IResourceBlock>(480, KeyframeProp3),
new Tuple<long, IResourceBlock>(624, KeyframeProp4),
new Tuple<long, IResourceBlock>(768, KeyframeProp5),
new Tuple<long, IResourceBlock>(912, KeyframeProp6)
return new (long, IResourceBlock)[] {
(16, KeyframeProps),
(48, KeyframeProp0),
(192, KeyframeProp1),
(336, KeyframeProp2),
(480, KeyframeProp3),
(624, KeyframeProp4),
(768, KeyframeProp5),
(912, KeyframeProp6)
};
}
}
@ -8113,8 +8119,10 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>(base.GetReferences());
if (Texture != null) list.Add(Texture);
if (TextureName != null) list.Add(TextureName);
if (Texture != null)
list.Add(Texture);
if (TextureName != null)
list.Add(TextureName);
return list.ToArray();
}
}
@ -8199,10 +8207,10 @@ namespace CodeWalker.GameFiles
Items.data_items = XmlMeta.ReadItemArray<ParticleShaderVarKeyframeItem>(node, "Items");
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x28, Items)
return new (long, IResourceBlock)[] {
(0x28, Items)
};
}
}

View File

@ -0,0 +1,158 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.GameFiles.Resources
{
public class CustomTypeConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
Console.WriteLine($"{context}: {value}");
var properties = TypeDescriptor.GetProperties(value, attributes);
Console.WriteLine(properties);
return properties;
}
}
public class DictionaryTypeConverter<TKey, TValue> : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
Console.WriteLine($"{context}: {value}");
var properties = TypeDescriptor.GetProperties(value, attributes);
if (value is IDictionary<TKey, TValue> dict)
{
foreach (var v in dict)
{
properties.Add(TypeDescriptor.GetDefaultProperty(v.Value));
}
}
Console.WriteLine(properties);
return properties;
}
}
public class DictionaryConvert<TKey, TValue> : TypeConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
Console.WriteLine($"{context}: {value}");
var properties = TypeDescriptor.GetProperties(value, attributes);
Console.WriteLine(properties);
return properties;
}
}
[TypeConverter(typeof(CustomTypeConverter))]
public class PedsDlcFiles
{
public MetaHash DlcName { get; set; }
public PedsDlcFiles(MetaHash dlcName)
{
DlcName = dlcName;
}
[Browsable(true)]
public PedFile PedFile { get; set; }
[Browsable(true)]
public ConcurrentDictionary<MetaHash, RpfFileEntry> Drawables { get; set; } = new ConcurrentDictionary<MetaHash, RpfFileEntry>();
[Browsable(true)]
public ConcurrentDictionary<MetaHash, RpfFileEntry> TextureDicts { get; set; } = new ConcurrentDictionary<MetaHash, RpfFileEntry>();
[Browsable(true)]
public ConcurrentDictionary<MetaHash, RpfFileEntry> ClothDicts { get; set; } = new ConcurrentDictionary<MetaHash, RpfFileEntry>();
public int Index
{
get {
if (!GameFileCache.Instance.DlcNameLookup.TryGetValue(DlcName, out var dlcName))
{
return -1;
}
return GameFileCache.Instance.DlcNameList.FindIndex(0, (value) => value.Equals(dlcName, StringComparison.OrdinalIgnoreCase));
}
}
}
[TypeConverter(typeof(CustomTypeConverter))]
public class PedsFiles
{
[Browsable(true)]
[TypeConverter(typeof(DictionaryTypeConverter<MetaHash, PedsDlcFiles>))]
public ConcurrentDictionary<MetaHash, PedsDlcFiles> Dlcs { get; set; } = new ConcurrentDictionary<MetaHash, PedsDlcFiles>();
[Browsable(true)]
[TypeConverter(typeof(CollectionConverter))]
public ICollection<PedFile> Ymts { get; set; } = new HashSet<PedFile>(GameFileByPathComparer.Instance);
public string kaas = "Kaas";
public PedsDlcFiles GetPedsDlcFiles(PedFile pedFile)
{
var pedsDlcFiles = GetPedsDlcFiles(pedFile.DlcName);
pedsDlcFiles.PedFile = pedFile;
return pedsDlcFiles;
}
public PedsDlcFiles GetPedsDlcFiles(MetaHash dlcName)
{
if (!Dlcs.TryGetValue(dlcName, out var pedsFiles))
{
_ = Dlcs.TryAdd(dlcName, new PedsDlcFiles(dlcName));
pedsFiles = Dlcs[dlcName];
}
return pedsFiles;
}
public bool TryGetPedsDlcFiles(PedFile pedFile, out PedsDlcFiles pedsDlcFiles)
{
return TryGetPedsDlcFiles(pedFile.DlcName, out pedsDlcFiles);
}
public bool TryGetPedsDlcFiles(MetaHash dlcName, out PedsDlcFiles pedsDlcFiles)
{
return Dlcs.TryGetValue(dlcName, out pedsDlcFiles);
}
public void AddDrawable(PedFile pedFile, RpfFileEntry entry)
{
var pedsFiles = GetPedsDlcFiles(pedFile);
pedsFiles.Drawables.TryAdd(entry.ShortNameHash, entry);
}
public void AddTextureDict(PedFile pedFile, RpfFileEntry entry)
{
var pedsFiles = GetPedsDlcFiles(pedFile);
pedsFiles.TextureDicts.TryAdd(entry.ShortNameHash, entry);
}
public void AddClothsDict(PedFile pedFile, RpfFileEntry entry)
{
var pedsFiles = GetPedsDlcFiles(pedFile);
pedsFiles.ClothDicts.TryAdd(entry.ShortNameHash, entry);
}
}
}

View File

@ -63,6 +63,7 @@ namespace CodeWalker.GameFiles
GraphicsPages = FileEntry?.GraphicsFlags.Pages;
var dlist = new List<ResourceAnalyzerItem>();
#if DEBUG
foreach (var kvp in reader.blockPool)
{
var item = new ResourceAnalyzerItem();
@ -98,6 +99,7 @@ namespace CodeWalker.GameFiles
}
dlist.Add(item);
}
#endif
dlist.Sort((a, b) => a.Position.CompareTo(b.Position));

File diff suppressed because it is too large Load Diff

View File

@ -459,7 +459,7 @@ namespace CodeWalker.GameFiles
{
using (MemoryStream ms = RpfFile.recyclableMemoryStreamManager.GetStream())
{
DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true);
DeflateStream ds = new DeflateStream(ms, CompressionLevel.SmallestSize, true);
ds.Write(data, 0, data.Length);
ds.Close();
return ms.ToArray();
@ -470,12 +470,23 @@ namespace CodeWalker.GameFiles
using (MemoryStream ms = new MemoryStream(data))
{
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length);
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length * 2);
ds.CopyTo(outstr, 524288);
return outstr.ToArray();
}
}
public static async Task<byte[]> DecompressAsync(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length * 2);
await ds.CopyToAsync(outstr, 524288);
return outstr.ToArray();
}
}
public static DeflateStream Decompress(Stream stream)
{
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress);

View File

@ -22,16 +22,21 @@
//shamelessly stolen and mangled
using CodeWalker.Utils;
using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
@ -47,29 +52,23 @@ namespace CodeWalker.GameFiles
private readonly Stream systemStream;
private readonly Stream graphicsStream;
private readonly long systemSize = 0;
private readonly long graphicsSize = 0;
private readonly int systemSize = 0;
private readonly int graphicsSize = 0;
public RpfResourceFileEntry FileEntry { get; set; }
// this is a dictionary that contains all the resource blocks
// which were read from this resource reader
public Dictionary<long, IResourceBlock> blockPool
{
get
{
return _blockPool ??= new Dictionary<long, IResourceBlock>();
}
}
public Dictionary<long, object> arrayPool {
get
{
return _arrayPool ??= new Dictionary<long, object>();
}
}
// This is needed to avoid a stack overflow because a ResourcePointerArray will try to read itself
private Dictionary<long, IResourceBlock> _blockPool;
public Dictionary<long, IResourceBlock> blockPool => _blockPool ??= new Dictionary<long, IResourceBlock>(17);
#if DEBUG
public Dictionary<long, object> arrayPool => _arrayPool ??= new Dictionary<long, object>();
private Dictionary<long, object> _arrayPool;
private Dictionary<long, IResourceBlock> _blockPool;
#endif
private long position = SYSTEM_BASE;
/// <summary>
@ -109,16 +108,16 @@ namespace CodeWalker.GameFiles
/// Initializes a new resource data reader for the specified system- and graphics-stream.
/// </summary>
public ResourceDataReader(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
: base(null, endianess)
: base((Stream?)null, endianess)
{
this.systemStream = systemStream;
this.graphicsStream = graphicsStream;
this.systemSize = systemStream.Length;
this.graphicsSize = graphicsStream.Length;
this.systemSize = (int)systemStream.Length;
this.graphicsSize = (int)graphicsStream.Length;
}
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base(null, endianess)
: this(resentry.SystemSize, resentry.GraphicsSize, data, endianess)
{
FileEntry = resentry;
this.systemSize = resentry.SystemSize;
@ -137,11 +136,11 @@ namespace CodeWalker.GameFiles
// }
//}
if ((int)systemSize > data.Length)
if (systemSize > data.Length)
{
throw new ArgumentException($"systemSize {systemSize} is larger than data length ({data.Length})", nameof(resentry));
}
if ((int)graphicsSize > data.Length)
if (graphicsSize > data.Length)
{
throw new ArgumentException($"graphicsSize {graphicsSize} is larger than data length ({data.Length})", nameof(resentry));
}
@ -150,8 +149,21 @@ namespace CodeWalker.GameFiles
}
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base(null, endianess)
: base((Stream)null, endianess)
{
if (systemSize > data.Length)
{
throw new ArgumentException($"systemSize {systemSize} is larger than data length ({data.Length})", nameof(systemSize));
}
if (graphicsSize > data.Length)
{
throw new ArgumentException($"graphicsSize {graphicsSize} is larger than data length ({data.Length})", nameof(graphicsSize));
}
if (systemSize + graphicsSize > data.Length)
{
throw new ArgumentException($"systemSize + graphicsSize {systemSize} is larger than data length ({data.Length})", nameof(systemSize));
}
this.systemStream = new MemoryStream(data, 0, systemSize);
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
}
@ -188,22 +200,38 @@ namespace CodeWalker.GameFiles
}
}
private static ConcurrentDictionary<Type, bool> cacheableTypes = new ConcurrentDictionary<Type, bool>();
private static bool fetchUsePool(Type type)
{
return !typeof(IResourceNoCacheBlock).IsAssignableFrom(type);
}
private static bool usePool<T>() where T : IResourceBlock, new()
{
return cacheableTypes.GetOrAdd(typeof(T), fetchUsePool);
}
public static T validate<T>(Func<T> instantiator)
where T : IResourceBlock
{
return instantiator();
}
/// <summary>
/// Reads a block.
/// </summary>
public T ReadBlock<T>(params object[] parameters) where T : IResourceBlock, new()
{
var usepool = !typeof(IResourceNoCacheBlock).IsAssignableFrom(typeof(T));
var usepool = usePool<T>();
if (usepool)
{
// make sure to return the same object if the same
// block is read again...
if (blockPool.ContainsKey(Position))
if (blockPool.TryGetValue(Position, out IResourceBlock? value))
{
var block = blockPool[Position];
if (block is T tblk)
var cachedBlock = value;
if (cachedBlock is T tblk)
{
Position += block.BlockLength;
Position += cachedBlock.BlockLength;
return tblk;
}
else
@ -217,14 +245,9 @@ namespace CodeWalker.GameFiles
// replace with correct type...
if (result is IResourceXXSystemBlock)
if (result is IResourceXXSystemBlock block)
{
result = (T)((IResourceXXSystemBlock)result).GetType(this, parameters);
}
if (result == null)
{
return default;
result = (T)block.GetType(this, parameters);
}
if (usepool)
@ -240,27 +263,28 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a block at a specified position.
/// </summary>
public T ReadBlockAt<T>(ulong position, params object[] parameters) where T : IResourceBlock, new()
public T? ReadBlockAt<T>(ulong position, params object[] parameters) where T : IResourceBlock, new()
{
if (position != 0)
if (position == 0)
{
var positionBackup = Position;
Position = (long)position;
var result = ReadBlock<T>(parameters);
Position = positionBackup;
return result;
}
else
{
return default(T);
return default;
}
var positionBackup = Position;
Position = (long)position;
var result = ReadBlock<T>(parameters);
Position = positionBackup;
return result;
}
public T[] ReadBlocks<T>(ulong[] pointers) where T : IResourceBlock, new()
[return: NotNullIfNotNull(nameof(pointers))]
public T[]? ReadBlocks<T>(ulong[]? pointers) where T : IResourceBlock, new()
{
if (pointers == null) return null;
if (pointers is null)
return null;
var count = pointers.Length;
var items = new T[count];
for (int i = 0; i < count; i++)
@ -270,20 +294,40 @@ namespace CodeWalker.GameFiles
return items;
}
public unsafe byte[] ReadBytesAt(ulong position, uint count, bool cache = true, byte[] buffer = null)
#if DEBUG
public static int EntryAddedToCache = 0;
#endif
// Only used for ResourceAnalyzer so can be made conditional, this optimizes away the if branch and arrayPool adition
[Conditional("DEBUG")]
private void AddEntryToArrayPool(long position, object result)
{
#if DEBUG
Interlocked.Increment(ref EntryAddedToCache);
arrayPool[position] = result;
#endif
}
public unsafe byte[]? ReadBytesAt(ulong position, uint count, bool cache = true, byte[]? buffer = null)
{
long pos = (long)position;
if ((pos <= 0) || (count == 0)) return null;
if ((pos <= 0) || (count == 0))
return null;
var posbackup = Position;
Position = pos;
var result = ReadBytes((int)count, buffer);
var result = ReadFromStream((int)count, true, buffer);
Position = posbackup;
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public ushort[] ReadUshortsAt(ulong position, uint count, bool cache = true)
public ushort[]? ReadUshortsAt(ulong position, uint count, bool cache = true)
{
if ((position <= 0) || (count == 0)) return null;
if ((position <= 0) || (count == 0))
return null;
var result = new ushort[count];
var length = count * sizeof(ushort);
@ -308,11 +352,14 @@ namespace CodeWalker.GameFiles
//}
//Position = posbackup;
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public short[] ReadShortsAt(ulong position, uint count, bool cache = true)
public short[]? ReadShortsAt(ulong position, uint count, bool cache = true)
{
if ((position <= 0) || (count == 0)) return null;
var result = new short[count];
@ -329,13 +376,17 @@ namespace CodeWalker.GameFiles
}
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public uint[] ReadUintsAt(ulong position, uint count, bool cache = true)
public uint[]? ReadUintsAt(ulong position, uint count, bool cache = true)
{
if ((position <= 0) || (count == 0)) return null;
if ((position <= 0) || (count == 0))
return null;
var result = new uint[count];
var length = count * sizeof(uint);
@ -350,13 +401,17 @@ namespace CodeWalker.GameFiles
}
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public ulong[] ReadUlongsAt(ulong position, uint count, bool cache = true)
public ulong[]? ReadUlongsAt(ulong position, uint count, bool cache = true)
{
if ((position <= 0) || (count == 0)) return null;
if (position <= 0 || count == 0)
return null;
var result = new ulong[count];
var length = count * sizeof(ulong);
@ -371,13 +426,17 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(data);
}
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public float[] ReadFloatsAt(ulong position, uint count, bool cache = true)
public float[]? ReadFloatsAt(ulong position, uint count, bool cache = true)
{
if ((position <= 0) || (count == 0)) return null;
if ((position <= 0) || (count == 0))
return null;
var result = new float[count];
var length = count * sizeof(float);
@ -392,13 +451,17 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(data);
}
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
public T[] ReadStructsAt<T>(ulong position, uint count, bool cache = true) where T : struct
public T[]? ReadStructsAt<T>(ulong position, uint count, bool cache = true) where T : struct
{
if ((position <= 0) || (count == 0)) return null;
if ((position <= 0) || (count == 0))
return null;
uint structsize = (uint)Marshal.SizeOf(typeof(T));
var length = count * structsize;
@ -412,7 +475,10 @@ namespace CodeWalker.GameFiles
var resultSpan = MemoryMarshal.Cast<byte, T>(data.AsSpan(0, (int)length));
resultSpan.CopyTo(result);
if (cache) arrayPool[(long)position] = result;
if (cache)
{
AddEntryToArrayPool((long)position, result);
}
return result;
}
@ -421,7 +487,7 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(data);
}
}
public T[] ReadStructs<T>(uint count) where T : struct
public T[]? ReadStructs<T>(uint count) where T : struct
{
uint structsize = (uint)Marshal.SizeOf(typeof(T));
var result = new T[count];
@ -443,43 +509,66 @@ namespace CodeWalker.GameFiles
}
public T ReadStruct<T>() where T : struct
public bool TryReadStruct<T>(out T result) where T : struct
{
uint structsize = (uint)Marshal.SizeOf(typeof(T));
var structsize = Marshal.SizeOf(typeof(T));
var length = structsize;
var data = ArrayPool<byte>.Shared.Rent((int)length);
var data = ArrayPool<byte>.Shared.Rent(length);
try
{
ReadBytes((int)length, data);
MemoryMarshal.TryRead<T>(data, out var value);
return value;
} finally
var buffer = data.AsSpan(0, length);
ReadBytes(buffer);
return MemoryMarshal.TryRead(buffer, out result);
}
finally
{
ArrayPool<byte>.Shared.Return(data);
}
}
public T ReadStruct<T>() where T : struct
{
TryReadStruct<T>(out var result);
return result;
}
public bool TryReadStructAt<T>(long position, out T result) where T : struct
{
if (position <= 0)
{
result = default;
return false;
}
var posbackup = Position;
try
{
Position = position;
return TryReadStruct(out result);
}
finally
{
Position = posbackup;
}
}
public T ReadStructAt<T>(long position) where T : struct
{
if ((position <= 0)) return default(T);
var posbackup = Position;
Position = (long)position;
var result = ReadStruct<T>();
Position = posbackup;
TryReadStructAt<T>(position, out var result);
return result;
}
public string ReadStringAt(ulong position)
public string? ReadStringAt(ulong position)
{
long newpos = (long)position;
if ((newpos <= 0)) return null;
if (newpos <= 0)
return null;
var lastpos = Position;
Position = newpos;
var result = ReadString();
Position = lastpos;
arrayPool[newpos] = result;
AddEntryToArrayPool((long)position, result);
return result;
}
@ -587,7 +676,7 @@ namespace CodeWalker.GameFiles
var arr = new byte[size];
MemoryMarshal.TryWrite(arr, ref val);
MemoryMarshal.TryWrite(arr, in val);
Write(arr);
@ -654,7 +743,15 @@ namespace CodeWalker.GameFiles
public interface IResourceBlockSpan
{
void Read(ref SequenceReader<byte> reader, params object[] parameters);
}
public interface IResourceXXSytemBlockSpan
{
IResourceSystemBlock GetType(ref SequenceReader<byte> reader, params object[] parameters);
}
/// <summary>
/// Represents a data block in a resource file.
@ -690,7 +787,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Returns a list of data blocks that are part of this block.
/// </summary>
Tuple<long, IResourceBlock>[] GetParts();
(long, IResourceBlock)[] GetParts();
/// <summary>
/// Returns a list of data blocks that are referenced by this block.
@ -721,7 +818,8 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Represents a data block of the system segement in a resource file.
/// </summary>
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class ResourceSystemBlock : IResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public abstract class ResourceSystemBlock : IResourceSystemBlock
{
private long position;
@ -765,9 +863,9 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Returns a list of data blocks that are part of this block.
/// </summary>
public virtual Tuple<long, IResourceBlock>[] GetParts()
public virtual (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[0];
return Array.Empty<(long, IResourceBlock)>();
}
/// <summary>
@ -775,7 +873,7 @@ namespace CodeWalker.GameFiles
/// </summary>
public virtual IResourceBlock[] GetReferences()
{
return new IResourceBlock[0];
return Array.Empty<IResourceBlock>();
}
}

View File

@ -23,8 +23,10 @@
//shamelessly stolen and mangled
using CodeWalker.Core.Utils;
using SharpDX;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
@ -35,12 +37,10 @@ using System.Threading.Tasks;
namespace CodeWalker.GameFiles
{
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourceFileBase : ResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ResourceFileBase : ResourceSystemBlock
{
public override long BlockLength
{
get { return 16; }
}
public override long BlockLength => 16;
// structure data
public uint FileVFT { get; set; }
@ -66,6 +66,19 @@ namespace CodeWalker.GameFiles
);
}
public void Read(ref SequenceReader<byte> reader, params object[] parameters)
{
// read structure data
this.FileVFT = reader.ReadUInt32();
this.FileUnknown = reader.ReadUInt32();
this.FilePagesInfoPointer = reader.ReadUInt64();
// read reference data
this.FilePagesInfo = reader.ReadBlockAt<ResourcePagesInfo>(
this.FilePagesInfoPointer // offset
);
}
/// <summary>
/// Writes the data-block to a stream.
/// </summary>
@ -85,19 +98,20 @@ namespace CodeWalker.GameFiles
/// </summary>
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
if (FilePagesInfo != null) list.Add(FilePagesInfo);
return list.ToArray();
if (FilePagesInfo is null)
{
return [];
}
return [FilePagesInfo];
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourcePagesInfo : ResourceSystemBlock
[TypeConverter(typeof(ExpandableObjectConverter))]
public class ResourcePagesInfo : ResourceSystemBlock, IResourceBlockSpan
{
public override long BlockLength
{
get { return 20 + (256 * 16); }
}
public override long BlockLength => 20 + (256 * 16);
// structure data
public uint Unknown_0h { get; set; }
@ -123,6 +137,18 @@ namespace CodeWalker.GameFiles
this.Unknown_10h = reader.ReadUInt32();
}
public void Read(ref SequenceReader<byte> reader, params object[] parameters)
{
// read structure data
this.Unknown_0h = reader.ReadUInt32();
this.Unknown_4h = reader.ReadUInt32();
this.SystemPagesCount = reader.ReadByte();
this.GraphicsPagesCount = reader.ReadByte();
this.Unknown_Ah = reader.ReadUInt16();
this.Unknown_Ch = reader.ReadUInt32();
this.Unknown_10h = reader.ReadUInt32();
}
/// <summary>
/// Writes the data-block to a stream.
/// </summary>
@ -143,7 +169,7 @@ namespace CodeWalker.GameFiles
public override string ToString()
{
return SystemPagesCount.ToString() + ", " + GraphicsPagesCount.ToString();
return $"{SystemPagesCount}, {GraphicsPagesCount}";
}
}

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using static CodeWalker.Utils.DDSIO;
namespace CodeWalker.GameFiles
{
@ -136,11 +137,11 @@ namespace CodeWalker.GameFiles
}
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(0x20, TextureNameHashes),
new Tuple<long, IResourceBlock>(0x30, Textures)
return new (long, IResourceBlock)[] {
(0x20, TextureNameHashes),
(0x30, Textures)
};
}
@ -422,27 +423,20 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>();
if (!string.IsNullOrEmpty(Name))
{
NameBlock = (string_r)Name;
list.Add(NameBlock);
}
return list.ToArray();
if (string.IsNullOrEmpty(Name))
return [];
NameBlock = (string_r)Name;
return [NameBlock];
}
public override string ToString()
{
return "TextureBase: " + Name;
}
public override string ToString() => $"TextureBase: {Name}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class Texture : TextureBase
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Texture : TextureBase
{
public override long BlockLength
{
get { return 144; }
}
public override long BlockLength => 144;
// structure data
public ushort Width { get; set; }
@ -469,18 +463,7 @@ namespace CodeWalker.GameFiles
// reference data
public TextureData Data { get; set; }
public long MemoryUsage
{
get
{
long val = 0;
if (Data != null)
{
val += Data.FullData.LongLength;
}
return val;
}
}
public long MemoryUsage => Data?.FullData?.LongLength ?? 0;
public override void Read(ResourceDataReader reader, params object[] parameters)
{
@ -560,7 +543,9 @@ namespace CodeWalker.GameFiles
File.WriteAllBytes(filepath, dds);
}
}
catch { }
catch(Exception ex) {
Console.WriteLine(ex);
}
}
public override void ReadXml(XmlNode node, string ddsfolder)
{
@ -608,26 +593,19 @@ namespace CodeWalker.GameFiles
public override IResourceBlock[] GetReferences()
{
var list = new List<IResourceBlock>(base.GetReferences());
list.Add(Data);
var list = new List<IResourceBlock>(base.GetReferences())
{
Data
};
return list.ToArray();
}
public override string ToString()
{
return "Texture: " + Width.ToString() + "x" + Height.ToString() + ": " + Name;
}
public override string ToString() => $"Texture: {Width}x{Height}: {Name}";
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureData : ResourceGraphicsBlock
{
public override long BlockLength
{
get
{
return FullData.Length;
}
}
public override long BlockLength => FullData.Length;
public byte[] FullData { get; set; }
@ -636,17 +614,34 @@ namespace CodeWalker.GameFiles
/// </summary>
public override void Read(ResourceDataReader reader, params object[] parameters)
{
//uint format = Convert.ToUInt32(parameters[0]);
//int Width = Convert.ToInt32(parameters[1]);
uint format = Convert.ToUInt32(parameters[0]);
var dxgiFormat = DDSIO.GetDXGIFormat((TextureFormat)format);
int Width = Convert.ToInt32(parameters[1]);
int Height = Convert.ToInt32(parameters[2]);
int Levels = Convert.ToInt32(parameters[3]);
int Stride = Convert.ToInt32(parameters[4]);
int div = 1;
int fullLength = 0;
int length = Stride * Height;
for (int i = 0; i < Levels; i++)
{
fullLength += Math.Max(length, 4 * 4);
//var thisLength = Math.Max(length, 4 * 4);
//if (thisLength % 16 != 0)
//{
// thisLength += (16 - (thisLength % 16));
//}
var width = Math.Max(Width / div, 1);
var height = Math.Max(Height / div, 1);
DXTex.ComputePitch(dxgiFormat, width, height, out var ddsRowPitch, out var ddsSlicePitch, 0);
div *= 2;
fullLength += ddsSlicePitch;
length /= 4;
}
@ -670,6 +665,7 @@ namespace CodeWalker.GameFiles
D3DFMT_A1R5G5B5 = 25,
D3DFMT_A8 = 28,
D3DFMT_A8B8G8R8 = 32,
D3DFMT_A16R16G16B16 = 36,
D3DFMT_L8 = 50,
// fourCC

View File

@ -89,10 +89,10 @@ namespace CodeWalker.GameFiles
public override Tuple<long, IResourceBlock>[] GetParts()
public override (long, IResourceBlock)[] GetParts()
{
return new Tuple<long, IResourceBlock>[] {
new Tuple<long, IResourceBlock>(16, Entries)
return new (long, IResourceBlock)[] {
(16, Entries)
};
}
}

View File

@ -185,10 +185,15 @@ namespace CodeWalker.GameFiles
public Half4 Tangent;
}
public struct EditorVertex //vertex data to be used by the editor. TODO: maybe move somewhere else.
public struct EditorVertex(Vector3 position, uint colour) //vertex data to be used by the editor. TODO: maybe move somewhere else.
{
public Vector3 Position;
public uint Colour;
public Vector3 Position = position;
public uint Colour = colour;
public EditorVertex(): this(default, default)
{
}
}

View File

@ -1,5 +1,7 @@
using SharpDX;
using CodeWalker.Core.Utils;
using SharpDX;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -141,6 +143,17 @@ namespace CodeWalker.GameFiles
this.Unk2 = reader.ReadUInt16();
this.Unk3 = reader.ReadUInt16();
}
public void Read(ref SequenceReader<byte> reader, params object[] parameters)
{
// read structure data
this.Position = reader.ReadVector3();
this.Unk0 = reader.ReadUInt16();
this.Unk1 = reader.ReadUInt16();
this.Unk2 = reader.ReadUInt16();
this.Unk3 = reader.ReadUInt16();
}
public override void Write(ResourceDataWriter writer, params object[] parameters)
{
// write structure data

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,16 @@

using CodeWalker.Core.Utils;
using CodeWalker.World;
using CommunityToolkit.HighPerformance;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -49,7 +53,7 @@ namespace CodeWalker.GameFiles
public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true)
{
using var _ = new DisposableTimer("RpfManager.Init");
using var timer = new DisposableTimer("RpfManager.Init");
UpdateStatus += updateStatus;
ErrorLog += errorLog;
@ -89,7 +93,7 @@ namespace CodeWalker.GameFiles
if (excl) return; //skip files in exclude paths.
}
rf.ScanStructure(updateStatus, errorLog);
rf.ScanStructure(updateStatus, errorLog, out _);
if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF)
{
@ -101,7 +105,8 @@ namespace CodeWalker.GameFiles
}
catch (Exception ex)
{
errorLog(rpfpath + ": " + ex.ToString());
Console.WriteLine(ex);
errorLog?.Invoke($"{rpfpath}: {ex}");
}
});
@ -180,7 +185,7 @@ namespace CodeWalker.GameFiles
private void AddRpfFile(RpfFile file, bool isdlc, bool ismod)
{
if (file.AllEntries == null && file.Children == null)
if (file.AllEntries is null && file.Children is null)
return;
isdlc = isdlc || (file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase) && (file.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase) || file.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase)));
@ -246,7 +251,7 @@ namespace CodeWalker.GameFiles
}
}
if (file.Children != null)
if (file.Children is not null)
{
foreach (RpfFile cfile in file.Children)
{
@ -256,13 +261,12 @@ namespace CodeWalker.GameFiles
}
public RpfFile FindRpfFile(string path) => FindRpfFile(path, false);
public RpfFile? FindRpfFile(string path) => FindRpfFile(path, false);
public RpfFile FindRpfFile(string path, bool exactPathOnly)
public RpfFile? FindRpfFile(string path, bool exactPathOnly)
{
RpfFile file;
if (EnableMods && ModRpfDict.TryGetValue(path, out file))
if (EnableMods && ModRpfDict.TryGetValue(path, out RpfFile? file))
{
return file;
}
@ -288,15 +292,14 @@ namespace CodeWalker.GameFiles
}
public RpfEntry GetEntry(string path)
public RpfEntry? GetEntry(string path)
{
RpfEntry entry;
if (EnableMods && ModEntryDict.TryGetValue(path, out entry))
if (EnableMods && ModEntryDict.TryGetValue(path, out RpfEntry? entry))
{
return entry;
}
EntryDict.TryGetValue(path, out entry);
if (entry == null)
if (entry is null)
{
path = path.Replace("/", "\\");
path = path.Replace("common:", "common.rpf");
@ -308,11 +311,10 @@ namespace CodeWalker.GameFiles
}
return entry;
}
public byte[] GetFileData(string path)
public byte[]? GetFileData(string path)
{
byte[] data = null;
RpfFileEntry entry = GetEntry(path) as RpfFileEntry;
if (entry != null)
byte[]? data = null;
if (GetEntry(path) is RpfFileEntry entry)
{
data = entry.File.ExtractFile(entry);
}
@ -320,8 +322,17 @@ namespace CodeWalker.GameFiles
}
public string GetFileUTF8Text(string path)
{
byte[] bytes = GetFileData(path);
return TextUtil.GetUTF8Text(bytes);
byte[]? bytes = GetFileData(path);
var text = TextUtil.GetUTF8Text(bytes);
return text;
}
public StreamReader GetFileUTF8TextStream(string path)
{
byte[]? bytes = GetFileData(path);
return new StreamReader(bytes.AsMemory().AsStream(), new UTF8Encoding(false), true); ;
}
public XmlDocument GetFileXml(string path)
@ -336,50 +347,66 @@ namespace CodeWalker.GameFiles
return doc;
}
public T GetFile<T>(string path) where T : class, PackedFile, new()
public XmlReader GetFileXmlReader(string path, XmlNameTable nameTable)
{
T file = null;
byte[] data = null;
RpfFileEntry entry = GetEntry(path) as RpfFileEntry;
if (entry != null)
{
data = entry.File.ExtractFile(entry);
}
if (data != null)
var text = GetFileUTF8TextStream(path);
var reader = XmlReader.Create(text, new XmlReaderSettings { NameTable = nameTable });
return reader;
}
public XmlReader GetFileXmlReader(string path)
{
var text = GetFileUTF8TextStream(path);
var reader = XmlReader.Create(text);
return reader;
}
public T? GetFile<T>(string path) where T : class, PackedFile, new()
{
if (GetEntry(path) is not RpfFileEntry entry)
return null;
return GetFile<T>(entry);
}
public static T? GetFile<T>(RpfEntry e) where T : class, PackedFile, new()
{
if (e is not RpfFileEntry entry)
return null;
byte[]? data = entry.File.ExtractFile(entry);
T? file = null;
if (data is not null)
{
file = new T();
file.Load(data, entry);
}
return file;
}
public T GetFile<T>(RpfEntry e) where T : class, PackedFile, new()
public ValueTask<T?> GetFileAsync<T>(string path) where T : class, PackedFile, new()
{
T file = null;
byte[] data = null;
RpfFileEntry entry = e as RpfFileEntry;
if (entry != null)
{
data = entry.File.ExtractFile(entry);
}
if (data != null)
{
file = new T();
file.Load(data, entry);
}
return file;
RpfFileEntry? entry = GetEntry(path) as RpfFileEntry;
if (entry is null)
return ValueTask.FromResult((T?)null);
return GetFileAsync<T>(entry);
}
public async Task<T> GetFileAsync<T>(RpfEntry e) where T : class, PackedFile, new()
public static async ValueTask<T?> GetFileAsync<T>(RpfEntry e) where T : class, PackedFile, new()
{
if (e is not RpfFileEntry entry)
return null;
try
{
T file = null;
byte[] data = null;
RpfFileEntry entry = e as RpfFileEntry;
if (entry != null)
{
data = await entry.File.ExtractFileAsync(entry).ConfigureAwait(false);
}
if (data != null)
byte[]? data = await entry.File.ExtractFileAsync(entry);
T? file = null;
if (data is not null && data.Length > 0)
{
file = new T();
file.Load(data, entry);
@ -394,13 +421,14 @@ namespace CodeWalker.GameFiles
}
public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile
{
byte[] data = null;
RpfFileEntry entry = e as RpfFileEntry;
if (entry != null)
byte[]? data = null;
RpfFileEntry? entry = e as RpfFileEntry;
if (entry is not null)
{
data = entry.File.ExtractFile(entry);
}
if (data != null)
if (data is not null && data.Length > 0)
{
try
{
@ -409,7 +437,7 @@ namespace CodeWalker.GameFiles
}
catch(Exception ex)
{
Console.WriteLine($"Error occured while loading {entry.Name} at {entry.Path}:\n{ex}");
Console.WriteLine($"Error occured while loading {e.Name} at {e.Path}:\n{ex}");
throw;
}
}
@ -418,13 +446,13 @@ namespace CodeWalker.GameFiles
public async ValueTask<bool> LoadFileAsync<T>(T file, RpfEntry e) where T : class, PackedFile
{
byte[] data = null;
RpfFileEntry entry = e as RpfFileEntry;
if (entry != null)
byte[]? data = null;
RpfFileEntry? entry = e as RpfFileEntry;
if (entry is not null)
{
data = await entry.File.ExtractFileAsync(entry).ConfigureAwait(false);
data = await entry.File.ExtractFileAsync(entry);
}
if (data != null && data.Length > 0)
if (data is not null && data.Length > 0)
{
try
{
@ -433,7 +461,7 @@ namespace CodeWalker.GameFiles
}
catch(Exception ex)
{
Console.WriteLine($"Error occured while loading {entry.Name} at {entry.Path}:\n{ex}");
Console.WriteLine($"Error occured while loading {e.Name} at {e.Path}:\n{ex}");
throw;
}
}
@ -441,6 +469,9 @@ namespace CodeWalker.GameFiles
}
private ConcurrentDictionary<string, int> counts = new ConcurrentDictionary<string, int>();
private string[] stringLookup;
[SkipLocalsInit]
public void AddAllLods(string name)
{
var idx = name.LastIndexOf('_');
@ -452,88 +483,120 @@ namespace CodeWalker.GameFiles
{
// Filter some peds and clothing models (ydd's) which don't have LOD hashes we're interested in.
// This saves about 50% of the time it takes to do initial hashing
var str2 = str1.Slice(0, idx2 + 1);
var str2 = str1.Slice(0, idx2 + 1).ToString();
if (str2.Length <= 2)
{
return;
}
switch(str2)
var ignore = str2 switch
{
case "uppr_":
case "p_":
case "accs_":
case "decl_":
case "berd_":
case "hair_":
case "teef_":
case "lowr_":
case "jbib_":
case "hand_":
case "feet_":
case "task_":
case "head_":
case "s_m_y_":
case "s_m_":
case "s_m_m_":
case "s_f_y_":
case "a_m_y_":
case "ig_":
case "u_m_y_":
case "u_m_m_":
case "u_m_":
case "minimap_":
case "a_":
case "u_f_":
case "csb_":
case "g_m_y_":
case "a_f_m_":
case "a_m_m_":
case "g_m_m_":
case "mp_m_":
case "mp_f_":
case "hand_000_":
case "hand_001_":
case "hair_000_":
case "a_f_y_":
return;
default:
break;
}
"p_" => true,
"s_m_y_" => true,
"s_m_" => true,
"s_m_m_" => true,
"s_f_y_" => true,
"a_m_y_" => true,
"ig_" => true,
"u_m_y_" => true,
"u_m_m_" => true,
"u_m_" => true,
"minimap_" => true,
"a_" => true,
"u_f_" => true,
"csb_" => true,
"g_m_y_" => true,
"a_f_m_" => true,
"a_m_m_" => true,
"g_m_m_" => true,
"mp_m_" => true,
"mp_f_" => true,
"mp_" => true,
"a_f_y_" => true,
var str when str.StartsWith("accs_") => true,
var str when str.StartsWith("decl_") => true,
var str when str.StartsWith("berd_") => true,
var str when str.StartsWith("hair_") => true,
var str when str.StartsWith("teef_") => true,
var str when str.StartsWith("lowr_") => true,
var str when str.StartsWith("jbib_") => true,
var str when str.StartsWith("hand_") => true,
var str when str.StartsWith("feet_") => true,
var str when str.StartsWith("task_") => true,
var str when str.StartsWith("head_") => true,
var str when str.StartsWith("uppr_") => true,
_ => false,
};
if (ignore)
return;
Span<char> buff = stackalloc char[str2.Length + 2 + 4];
str2.CopyTo(buff.Slice(0, str2.Length));
"lod".AsSpan().CopyTo(buff.Slice(str2.Length, 3));
stackalloc char[] { 'l', 'o', 'd' }.CopyTo(buff.Slice(str2.Length, 3));
//Console.WriteLine(buff.Slice(0, str2.Length + 3).ToString());
JenkIndex.EnsureLower(buff.Slice(0, str2.Length + 3));
var maxi = 99;
const int maxi = 99;
stackalloc char[] { '0', '0', '_', 'l', 'o', 'd' }.CopyTo(buff.Slice(str2.Length, 6));
if (stringLookup is null)
{
stringLookup = new string[maxi + 1];
for (int i = 0; i <= maxi; i++)
{
stringLookup[i] = i.ToString().PadLeft(2, '0');
}
}
"00_lod".AsSpan().CopyTo(buff.Slice(str2.Length, 6));
for (int i = 1; i <= maxi; i++)
{
if (i < 10)
{
i.ToString().AsSpan().CopyTo(buff.Slice(str2.Length + 1, 1));
}
else
{
i.ToString().AsSpan().CopyTo(buff.Slice(str2.Length, 2));
}
stringLookup[i].AsSpan().CopyTo(buff.Slice(str2.Length, 2));
var hash = JenkHash.GenHashLower(buff);
//Console.WriteLine(buff.ToString());
//JenkIndex.Ensure(buff);
JenkIndex.EnsureLower(buff);
JenkIndex.Ensure(str2, hash);
}
}
}
}
[SkipLocalsInit]
private void parseAwc(string path)
{
var enumerator = path.ReverseEnumerateSplit('\\');
if (enumerator.MoveNext())
{
ReadOnlySpan<char> fn = enumerator.Current;
if (enumerator.MoveNext())
{
ReadOnlySpan<char> fd = enumerator.Current;
fn = fn.Slice(0, fn.Length - 4);
if (fd.EndsWith(['.', 'r', 'p', 'f'], StringComparison.OrdinalIgnoreCase))
{
fd = fd.Slice(0, fd.Length - 4);
}
Span<char> hpath = stackalloc char[fd.Length + fn.Length + 1];
fd.CopyTo(hpath);
hpath[fd.Length] = '/';
fn.CopyTo(hpath.Slice(fd.Length + 1));
JenkIndex.EnsureLower(hpath);
}
}
}
public void BuildBaseJenkIndex()
{
using var _ = new DisposableTimer("BuildBaseJenkIndex");
Parallel.ForEach(AllRpfs, (file) =>
var yddFiles = new ConcurrentBag<string>();
using var timer = new DisposableTimer("BuildBaseJenkIndex");
Parallel.ForEach(AllRpfs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, [SkipLocalsInit] (file) =>
{
try
{
StringBuilder sb = new StringBuilder();
JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries)
{
@ -555,10 +618,10 @@ namespace CodeWalker.GameFiles
if (name.EndsWith(".ydr", StringComparison.OrdinalIgnoreCase))// || nlow.EndsWith(".yft")) //do yft's get lods?
{
var sname = entry.ShortName;
var nameLod = sname + "_lod";
var nameLod = $"{sname}_lod";
JenkIndex.EnsureLower(nameLod);
JenkIndex.EnsureLower(nameLod + 'a');
JenkIndex.EnsureLower(nameLod + 'b');
JenkIndex.EnsureLower($"{nameLod}a");
JenkIndex.EnsureLower($"{nameLod}b");
}
else if (name.EndsWith(".ydd", StringComparison.OrdinalIgnoreCase))
{
@ -566,11 +629,13 @@ namespace CodeWalker.GameFiles
{
var strn = entry.Name.Substring(0, name.Length - 13);
JenkIndex.EnsureLower(strn);
var nameChildrenLod = strn + "_lod";
var nameChildrenLod = $"{strn}_lod";
JenkIndex.EnsureLower(nameChildrenLod);
JenkIndex.EnsureLower(nameChildrenLod + 'a');
JenkIndex.EnsureLower(nameChildrenLod + 'b');
JenkIndex.EnsureLower($"{nameChildrenLod}a");
JenkIndex.EnsureLower($"{nameChildrenLod}b");
}
yddFiles.Add(name);
//var idx = name.LastIndexOf('_');
//if (idx > 0)
//{
@ -590,7 +655,7 @@ namespace CodeWalker.GameFiles
// }
// }
//}
AddAllLods(name);
//AddAllLods(name);
}
else if(name.EndsWith(".sps", StringComparison.OrdinalIgnoreCase))
{
@ -598,56 +663,57 @@ namespace CodeWalker.GameFiles
}
else if(name.EndsWith(".awc", StringComparison.OrdinalIgnoreCase)) //create audio container path hashes...
{
string[] parts = entry.Path.Split('\\');
int pl = parts.Length;
if (pl > 2)
{
string fn = parts[pl - 1];
string fd = parts[pl - 2];
string hpath = fn.Substring(0, fn.Length - 4);
if (fd.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
{
fd = fd.Substring(0, fd.Length - 4);
}
hpath = fd + '/' + hpath;
JenkIndex.EnsureLower(hpath);
}
parseAwc(entry.Path);
}
else if(name.EndsWith(".nametable", StringComparison.OrdinalIgnoreCase))
{
if (entry is RpfBinaryFileEntry binfe)
{
byte[] data = file.ExtractFile(binfe);
if (data != null)
byte[] rawData = file.ExtractFile(binfe);
if (rawData != null)
{
sb.Clear();
for (int i = 0; i < data.Length; i++)
foreach(var bytes in rawData.AsSpan().EnumerateSplit((byte)0))
{
byte c = data[i];
if (c == 0)
string str = Encoding.ASCII.GetString(bytes);
if (!string.IsNullOrEmpty(str))
{
string str = sb.ToString();
if (!string.IsNullOrEmpty(str))
{
//JenkIndex.Ensure(str);
JenkIndex.EnsureLower(str);
//JenkIndex.Ensure(str);
////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
////no luck here yet though
//if (strl.StartsWith("dirmod_sounds_") && (strl.Length > 14))
//{
// strl = strl.Substring(14);
// JenkIndex.Ensure(strl);
//}
}
sb.Clear();
}
else
{
sb.Append((char)c);
JenkIndex.EnsureLower(str);
////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
////no luck here yet though
//if (strl.StartsWith("dirmod_sounds_") && (strl.Length > 14))
//{
// strl = strl.Substring(14);
// JenkIndex.Ensure(strl);
//}
}
}
//int startIndex = 0;
//for (int i = 0; i < rawData.Length; i++)
//{
// byte c = rawData[i];
// if (c == 0)
// {
// string str = Encoding.ASCII.GetString(rawData.AsSpan(startIndex, i - startIndex));
// if (!string.IsNullOrEmpty(str))
// {
// //JenkIndex.Ensure(str);
// JenkIndex.EnsureLower(str);
// ////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
// ////no luck here yet though
// //if (strl.StartsWith("dirmod_sounds_") && (strl.Length > 14))
// //{
// // strl = strl.Substring(14);
// // JenkIndex.Ensure(strl);
// //}
// }
// startIndex = i + 1;
// }
//}
}
}
}
@ -672,6 +738,14 @@ namespace CodeWalker.GameFiles
JenkIndex.Ensure(i.ToString("00"));
}
_ = Task.Run(() => {
using var timer2 = new DisposableTimer("BuildBaseJenkIndex -> AddAllLods");
foreach (var name in yddFiles)
{
AddAllLods(name);
}
});
//Task.Run(() =>
//{
// foreach (var count in counts.OrderBy(p => p.Value))

View File

@ -141,11 +141,6 @@ namespace CodeWalker.Utils
int ddsRowPitch, ddsSlicePitch;
DXTex.ComputePitch(meta.format, i0.width, i0.height, out ddsRowPitch, out ddsSlicePitch, 0);
if (i0.slicePitch == ddsSlicePitch)
{ }
else
{ }
int w = i0.width;
int h = i0.height;
int imglen = i0.slicePitch;// h * i0.rowPitch;
@ -153,16 +148,19 @@ namespace CodeWalker.Utils
byte[] px = null;// = new byte[w * h * 4];
if (i0.pixels + imglen > img.Data.Length)
{ throw new Exception("Mipmap not in texture!"); }
{
throw new Exception("Mipmap not in texture!");
}
Buffer.BlockCopy(img.Data, i0.pixels, imgdata, 0, imglen);
bool swaprb = true;
if (DirectXTexNet.TexHelper.Instance.IsCompressed((DirectXTexNet.DXGI_FORMAT)format))
if (TexHelper.Instance.IsCompressed((DirectXTexNet.DXGI_FORMAT)format))
{
px = Decompress(imgdata, w, h, format);
} else
}
else
{
switch (format)
{
@ -530,51 +528,59 @@ namespace CodeWalker.Utils
private static TextureFormat GetTextureFormat(DXGI_FORMAT f)
public static TextureFormat GetTextureFormat(this DXGI_FORMAT f)
{
var format = (TextureFormat)0;
switch (f)
{
// compressed
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: format = TextureFormat.D3DFMT_DXT1; break;
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: format = TextureFormat.D3DFMT_DXT3; break;
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: format = TextureFormat.D3DFMT_DXT5; break;
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: format = TextureFormat.D3DFMT_ATI1; break;
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: format = TextureFormat.D3DFMT_ATI2; break;
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: format = TextureFormat.D3DFMT_BC7; break;
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: return TextureFormat.D3DFMT_DXT1;
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: return TextureFormat.D3DFMT_DXT3;
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: return TextureFormat.D3DFMT_DXT5;
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: return TextureFormat.D3DFMT_ATI1;
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: return TextureFormat.D3DFMT_ATI2;
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: return TextureFormat.D3DFMT_BC7;
// uncompressed
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: format = TextureFormat.D3DFMT_A1R5G5B5; break;
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: format = TextureFormat.D3DFMT_A8; break;
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: format = TextureFormat.D3DFMT_A8B8G8R8; break;
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: format = TextureFormat.D3DFMT_L8; break;
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: format = TextureFormat.D3DFMT_A8R8G8B8; break;
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: format = TextureFormat.D3DFMT_X8R8G8B8; break;
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: return TextureFormat.D3DFMT_A1R5G5B5;
case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: return TextureFormat.D3DFMT_A8;
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: return TextureFormat.D3DFMT_A8B8G8R8;
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: return TextureFormat.D3DFMT_L8;
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: return TextureFormat.D3DFMT_A8R8G8B8;
case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: return TextureFormat.D3DFMT_X8R8G8B8;
case DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM: return TextureFormat.D3DFMT_A16R16G16B16;
}
return format;
}
private static DXGI_FORMAT GetDXGIFormat(TextureFormat f)
public static DXGI_FORMAT GetDXGIFormat(this TextureFormat f)
{
var format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
switch (f)
{
// compressed
case TextureFormat.D3DFMT_DXT1: format = DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM; break;
case TextureFormat.D3DFMT_DXT3: format = DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM; break;
case TextureFormat.D3DFMT_DXT5: format = DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM; break;
case TextureFormat.D3DFMT_ATI1: format = DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM; break;
case TextureFormat.D3DFMT_ATI2: format = DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM; break;
case TextureFormat.D3DFMT_BC7: format = DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM; break;
case TextureFormat.D3DFMT_DXT1: return DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM;
case TextureFormat.D3DFMT_DXT3: return DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM;
case TextureFormat.D3DFMT_DXT5: return DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM;
case TextureFormat.D3DFMT_ATI1: return DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM;
case TextureFormat.D3DFMT_ATI2: return DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM;
case TextureFormat.D3DFMT_BC7: return DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM;
// uncompressed
case TextureFormat.D3DFMT_A1R5G5B5: format = DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM; break;
case TextureFormat.D3DFMT_A8: format = DXGI_FORMAT.DXGI_FORMAT_A8_UNORM; break;
case TextureFormat.D3DFMT_A8B8G8R8: format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM; break;
case TextureFormat.D3DFMT_L8: format = DXGI_FORMAT.DXGI_FORMAT_R8_UNORM; break;
case TextureFormat.D3DFMT_A8R8G8B8: format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; break;
case TextureFormat.D3DFMT_X8R8G8B8: format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM; break;
case TextureFormat.D3DFMT_A1R5G5B5: return DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM;
case TextureFormat.D3DFMT_A8: return DXGI_FORMAT.DXGI_FORMAT_A8_UNORM;
case TextureFormat.D3DFMT_A8B8G8R8: return DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM;
case TextureFormat.D3DFMT_L8: return DXGI_FORMAT.DXGI_FORMAT_R8_UNORM;
case TextureFormat.D3DFMT_A8R8G8B8: return DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
case TextureFormat.D3DFMT_X8R8G8B8: return DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM;
case TextureFormat.D3DFMT_A16R16G16B16: return DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_UNORM;
}
if (format == DXGI_FORMAT.DXGI_FORMAT_UNKNOWN)
{
Console.WriteLine($"Format: {f} ({(int)f}) is unknown!");
}
return format;
}
@ -830,7 +836,7 @@ namespace CodeWalker.Utils
break;
default:
assert(IsValid(fmt), $"{fmt} is not a valid texture format");
assert(IsValid(fmt), $"{fmt} ({(int)fmt}) is not a valid texture format");
assert(!IsCompressed(fmt) && !IsPacked(fmt) && !IsPlanar(fmt));
{

View File

@ -37,6 +37,9 @@ using System.Buffers;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Threading;
using SharpDX.Win32;
using CommunityToolkit.HighPerformance;
using System.Linq;
namespace CodeWalker.GameFiles
{
@ -60,6 +63,7 @@ namespace CodeWalker.GameFiles
String = 9,
}
[SkipLocalsInit]
public class DataReader : IDisposable
{
private Stream baseStream;
@ -67,32 +71,20 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Gets or sets the endianess of the underlying stream.
/// </summary>
public Endianess Endianess { get; set; } = Endianess.LittleEndian;
public readonly Endianess Endianess = Endianess.LittleEndian;
/// <summary>
/// Gets the length of the underlying stream.
/// </summary>
public virtual long Length
{
get
{
return baseStream.Length;
}
}
public virtual long Length => baseStream.Length;
/// <summary>
/// Gets or sets the position within the underlying stream.
/// </summary>
public virtual long Position
{
get
{
return baseStream.Position;
}
set
{
baseStream.Position = value;
}
get => baseStream.Position;
set => baseStream.Position = value;
}
public DataReader(Stream stream)
@ -106,22 +98,25 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Initializes a new data reader for the specified stream.
/// </summary>
public DataReader(Stream stream, Endianess endianess) : this(stream)
public DataReader(Stream stream, Endianess endianess)
: this(stream)
{
this.Endianess = endianess;
}
public virtual Stream GetStream()
public DataReader(byte[] data, Endianess endianess = Endianess.LittleEndian)
: this(new MemoryStream(data))
{
return baseStream;
this.Endianess = endianess;
}
public virtual Stream GetStream() => baseStream;
internal virtual void SetPositionAfterRead(Stream stream)
{
return;
}
protected virtual void ReadFromStream(Span<byte> buffer, bool ignoreEndianess = false)
public virtual void ReadFromStream(Span<byte> buffer, bool ignoreEndianess = false)
{
var stream = GetStream();
@ -134,7 +129,7 @@ namespace CodeWalker.GameFiles
SetPositionAfterRead(stream);
}
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
if (Endianess == Endianess.BigEndian && !ignoreEndianess)
{
buffer.Reverse();
}
@ -144,10 +139,10 @@ namespace CodeWalker.GameFiles
/// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
/// </summary>
protected virtual byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[] buffer = null)
protected virtual byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[]? buffer = null)
{
var stream = GetStream();
buffer ??= new byte[count];
buffer ??= GC.AllocateUninitializedArray<byte>(count);
try
{
@ -159,7 +154,7 @@ namespace CodeWalker.GameFiles
}
// handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
if (Endianess == Endianess.BigEndian && !ignoreEndianess)
{
Array.Reverse(buffer, 0, count);
}
@ -199,9 +194,15 @@ namespace CodeWalker.GameFiles
return ReadFromStream(count, true, buffer);
}
public void ReadBytes(Span<byte> buffer)
{
ReadFromStream(buffer, true);
}
/// <summary>
/// Reads a signed 16-bit value.
/// </summary>
[SkipLocalsInit]
public short ReadInt16()
{
Span<byte> _buffer = stackalloc byte[sizeof(short)];
@ -220,6 +221,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a signed 32-bit value.
/// </summary>
[SkipLocalsInit]
public int ReadInt32()
{
Span<byte> _buffer = stackalloc byte[sizeof(int)];
@ -238,6 +240,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a signed 64-bit value.
/// </summary>
[SkipLocalsInit]
public long ReadInt64()
{
Span<byte> _buffer = stackalloc byte[sizeof(long)];
@ -256,6 +259,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads an unsigned 16-bit value.
/// </summary>
[SkipLocalsInit]
public ushort ReadUInt16()
{
Span<byte> _buffer = stackalloc byte[sizeof(ushort)];
@ -274,6 +278,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads an unsigned 32-bit value.
/// </summary>
[SkipLocalsInit]
public uint ReadUInt32()
{
Span<byte> _buffer = stackalloc byte[sizeof(uint)];
@ -292,6 +297,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads an unsigned 64-bit value.
/// </summary>
[SkipLocalsInit]
public ulong ReadUInt64()
{
Span<byte> _buffer = stackalloc byte[sizeof(ulong)];
@ -311,6 +317,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a single precision floating point value.
/// </summary>
[SkipLocalsInit]
public float ReadSingle()
{
Span<byte> _buffer = stackalloc byte[sizeof(float)];
@ -329,6 +336,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a double precision floating point value.
/// </summary>
[SkipLocalsInit]
public double ReadDouble()
{
Span<byte> _buffer = stackalloc byte[sizeof(double)];
@ -354,13 +362,13 @@ namespace CodeWalker.GameFiles
{
return string.Empty;
}
var bytes = stackalloc byte[length];
Span<byte> bytes = stackalloc byte[length];
for (int i = 0; i < length; i++)
{
bytes[i] = ReadByte();
}
return new string((sbyte*)bytes, 0, length, Encoding.ASCII);
return Encoding.UTF8.GetString(bytes);
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
}
@ -371,7 +379,6 @@ namespace CodeWalker.GameFiles
unsafe public string ReadString(int maxLength = 1024)
{
Span<byte> bytes = stackalloc byte[Math.Min(maxLength, 1024)];
Span<char> chars = stackalloc char[Math.Min(maxLength, 1024)];
var temp = ReadByte();
var bytesRead = 0;
var length = Length;
@ -385,13 +392,11 @@ namespace CodeWalker.GameFiles
bytesRead++;
}
var charsRead = Encoding.UTF8.GetChars(bytes.Slice(0, bytesRead), chars);
return chars.Slice(0, charsRead).ToString();
return Encoding.UTF8.GetString(bytes.Slice(0, bytesRead));
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
}
[SkipLocalsInit]
public Vector3 ReadVector3()
{
Vector3 v = new Vector3();
@ -400,6 +405,8 @@ namespace CodeWalker.GameFiles
v.Z = ReadSingle();
return v;
}
[SkipLocalsInit]
public Vector4 ReadVector4()
{
Vector4 v = new Vector4();
@ -410,6 +417,7 @@ namespace CodeWalker.GameFiles
return v;
}
[SkipLocalsInit]
public Matrix ReadMatrix()
{
Matrix m = new Matrix();
@ -470,7 +478,7 @@ namespace CodeWalker.GameFiles
public Endianess Endianess
{
get;
set;
init;
}
/// <summary>
@ -519,6 +527,8 @@ namespace CodeWalker.GameFiles
this.Endianess = endianess;
}
protected bool ShouldReverse(bool ignoreEndianness) => !ignoreEndianness && Endianess == Endianess.BigEndian;
/// <summary>
/// Writes data to the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
@ -530,7 +540,7 @@ namespace CodeWalker.GameFiles
{
count = value.Length;
}
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
if (ShouldReverse(ignoreEndianess))
{
var buffer = ArrayPool<byte>.Shared.Rent(count);
try
@ -551,13 +561,26 @@ namespace CodeWalker.GameFiles
SetPositionAfterWrite(stream);
}
protected virtual void WriteToStream(Span<byte> value, bool ignoreEndianess = false)
protected virtual void WriteToStream(ReadOnlySpan<byte> value, bool ignoreEndianess = false)
{
if (!ShouldReverse(ignoreEndianess))
{
var stream = GetStream();
stream.Write(value);
SetPositionAfterWrite(stream);
return;
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(value.Length);
try
{
value.CopyTo(sharedBuffer);
WriteToStream(sharedBuffer, count: value.Length);
Array.Reverse(sharedBuffer, 0, value.Length);
var stream = GetStream();
stream.Write(sharedBuffer.AsSpan());
SetPositionAfterWrite(stream);
}
finally
{
@ -565,23 +588,31 @@ namespace CodeWalker.GameFiles
}
}
protected virtual void WriteToStream(Memory<byte> buffer, bool ignoreEndianess = false)
protected virtual async ValueTask WriteToStreamAsync(ReadOnlyMemory<byte> buffer, bool ignoreEndianess = false)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
if (!ShouldReverse(ignoreEndianess))
{
WriteToStream(array.Array!, offset: array.Offset, count: array.Count);
var stream = GetStream();
await stream.WriteAsync(buffer);
SetPositionAfterWrite(stream);
return;
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
var arr = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.Span.CopyTo(sharedBuffer);
WriteToStream(sharedBuffer, count: buffer.Length);
buffer.CopyTo(arr);
Array.Reverse(arr, 0, buffer.Length);
var stream = GetStream();
await stream.WriteAsync(arr.AsMemory(0, buffer.Length));
SetPositionAfterWrite(stream);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
ArrayPool<byte>.Shared.Return(arr);
}
}
@ -601,14 +632,14 @@ namespace CodeWalker.GameFiles
WriteToStream(value, true);
}
public void Write(Span<byte> value)
public void Write(ReadOnlySpan<byte> value)
{
WriteToStream(value, true);
}
public void Write(Memory<byte> value)
public ValueTask WriteAsync(Memory<byte> value)
{
WriteToStream(value, true);
return WriteToStreamAsync(value, true);
}
/// <summary>

View File

@ -23,6 +23,7 @@
//shamelessly stolen
using CommunityToolkit.HighPerformance.Helpers;
using System;
using System.Buffers;
using System.Collections.Generic;
@ -31,9 +32,11 @@ using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace CodeWalker.GameFiles
{
@ -108,7 +111,9 @@ namespace CodeWalker.GameFiles
{
var encryptor = rijndael.CreateEncryptor();
for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
{
encryptor.TransformBlock(buffer, 0, length, buffer, 0);
}
}
return buffer;
@ -130,11 +135,11 @@ namespace CodeWalker.GameFiles
{
var key = GetNGKey(name, fileSize);
length ??= data.Length;
DecryptNG(data.AsSpan(offset, length.Value), key);
DecryptNG(data.AsMemory(offset, length.Value), key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNG(Span<byte> data, string name, uint fileSize)
public static void DecryptNG(Memory<byte> data, string name, uint fileSize)
{
var key = GetNGKey(name, fileSize);
DecryptNG(data, key);
@ -144,16 +149,36 @@ namespace CodeWalker.GameFiles
public unsafe static void DecryptNG(byte[] data, uint[][] key, int offset = 0, int? length = null)
{
length ??= data.Length;
DecryptNG(data.AsSpan(offset, length.Value), key);
DecryptNG(data.AsMemory(offset, length.Value), key);
}
public readonly struct DecryptNGBLock : IAction
{
private readonly Memory<byte> arr;
private readonly uint[][] key;
public DecryptNGBLock(Memory<byte> array, uint[][] key)
{
this.arr = array;
this.key = key;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Invoke(int blockIndex)
{
DecryptNGBlock(arr.Slice(16 * blockIndex, 16).Span, key);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNG(Span<byte> data, uint[][] key)
public static void DecryptNG(in Memory<byte> data, uint[][] key)
{
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
{
DecryptNGBlock(data.Slice(16 * blockIndex, 16), key);
}
var action = new DecryptNGBLock(data, key);
ParallelHelper.For(new Range(0, data.Length / 16), in action, 25);
//for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
//{
// DecryptNGBlock(data.Slice(16 * blockIndex, 16), key);
//}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -161,8 +186,21 @@ namespace CodeWalker.GameFiles
{
DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
DecryptNGRoundA(data, key[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
for (int k = 2; k <= 15; k++)
DecryptNGRoundB(data, key[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]);
//for (int k = 2; k <= 15; k++)
DecryptNGRoundB(data, key[2], GTA5Keys.PC_NG_DECRYPT_TABLES[2]);
DecryptNGRoundB(data, key[3], GTA5Keys.PC_NG_DECRYPT_TABLES[3]);
DecryptNGRoundB(data, key[4], GTA5Keys.PC_NG_DECRYPT_TABLES[4]);
DecryptNGRoundB(data, key[5], GTA5Keys.PC_NG_DECRYPT_TABLES[5]);
DecryptNGRoundB(data, key[6], GTA5Keys.PC_NG_DECRYPT_TABLES[6]);
DecryptNGRoundB(data, key[7], GTA5Keys.PC_NG_DECRYPT_TABLES[7]);
DecryptNGRoundB(data, key[8], GTA5Keys.PC_NG_DECRYPT_TABLES[8]);
DecryptNGRoundB(data, key[9], GTA5Keys.PC_NG_DECRYPT_TABLES[9]);
DecryptNGRoundB(data, key[10], GTA5Keys.PC_NG_DECRYPT_TABLES[10]);
DecryptNGRoundB(data, key[11], GTA5Keys.PC_NG_DECRYPT_TABLES[11]);
DecryptNGRoundB(data, key[12], GTA5Keys.PC_NG_DECRYPT_TABLES[12]);
DecryptNGRoundB(data, key[13], GTA5Keys.PC_NG_DECRYPT_TABLES[13]);
DecryptNGRoundB(data, key[14], GTA5Keys.PC_NG_DECRYPT_TABLES[14]);
DecryptNGRoundB(data, key[15], GTA5Keys.PC_NG_DECRYPT_TABLES[15]);
DecryptNGRoundA(data, key[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
}
@ -194,16 +232,19 @@ namespace CodeWalker.GameFiles
table[15][data[15]] ^
key[3];
MemoryMarshal.Write(data.Slice(0, 4), ref x1);
MemoryMarshal.Write(data.Slice(4, 4), ref x2);
MemoryMarshal.Write(data.Slice(8, 4), ref x3);
MemoryMarshal.Write(data.Slice(12, 4), ref x4);
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..8]), x1 | (ulong)x2 << 32);
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 8)), x3 | (ulong)x4 << 32);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..4]), x1);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(4, 4)), x2);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 4)), x3);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(12, 4)), x4);
}
// round 3-15
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void DecryptNGRoundB(Span<byte> data, uint[] key, uint[][] table)
public unsafe static void DecryptNGRoundB(Span<byte> data, Span<uint> key, uint[][] table)
{
var x1 =
table[0][data[0]] ^
@ -230,10 +271,13 @@ namespace CodeWalker.GameFiles
table[12][data[12]] ^
key[3];
MemoryMarshal.Write(data.Slice(0, 4), ref x1);
MemoryMarshal.Write(data.Slice(4, 4), ref x2);
MemoryMarshal.Write(data.Slice(8, 4), ref x3);
MemoryMarshal.Write(data.Slice(12, 4), ref x4);
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..8]), x1 | (ulong)x2 << 32);
Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 8)), x3 | (ulong)x4 << 32);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data[..4]), x1);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(4, 4)), x2);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(8, 4)), x3);
//Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(data.Slice(12, 4)), x4);
}

View File

@ -196,8 +196,10 @@ namespace CodeWalker.GameFiles
var exeStr = new MemoryStream(exeData);
updateStatus?.Invoke("Searching for AES key...");
Console.WriteLine("Searching for AES key...");
PC_AES_KEY = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_AES_KEY_HASH, 0x20);
Console.WriteLine("Complete.");
updateStatus?.Invoke("Complete.");
}
@ -290,9 +292,9 @@ namespace CodeWalker.GameFiles
private static void UseMagicData(string path, string key)
{
if (string.IsNullOrEmpty(key))
{
Console.WriteLine($"Reading keys from {path}\\gta5.exe");
byte[] exedata = File.ReadAllBytes(path + "\\gta5.exe");
GenerateV2(exedata, null);
}
@ -302,6 +304,11 @@ namespace CodeWalker.GameFiles
}
//GenerateMagicData();
if (PC_AES_KEY is null)
{
Console.WriteLine($"PC_AES_KEY is null!");
}
Random rnd = new Random((int)JenkHash.GenHash(PC_AES_KEY));
byte[] m = Resources.magic;
@ -782,7 +789,7 @@ namespace CodeWalker.GameFiles
public static class HashSearch
{
private const long BLOCK_LENGTH = 1048576;
private const long ALIGN_LENGTH = 16;
private const long ALIGN_LENGTH = 8;
public static byte[] SearchHash(Stream stream, byte[] hash, int length = 32)
{
@ -794,10 +801,12 @@ namespace CodeWalker.GameFiles
stream = Stream.Synchronized(stream);
var result = new byte[hashes.Count][];
Parallel.For(0, (stream.Length / BLOCK_LENGTH), (long k) => {
Parallel.For(0, (stream.Length / BLOCK_LENGTH), [SkipLocalsInit] (long k) => {
var hashProvider = new SHA1CryptoServiceProvider();
// TODO: Convert to stack alloc when length appropriate (1024 or lower probs)
var buffer = new byte[length];
Span<byte> buffer = length < 1024 ? stackalloc byte[length] : new byte[length];
Span<byte> hash = length < 1024 ? stackalloc byte[length] : new byte[length];
//var buffer = new byte[length];
for (long i = 0; i < (BLOCK_LENGTH / ALIGN_LENGTH); i++)
{
var position = k * BLOCK_LENGTH + i * ALIGN_LENGTH;
@ -806,15 +815,18 @@ namespace CodeWalker.GameFiles
stream.Position = position;
stream.Read(buffer, 0, length);
stream.Read(buffer);
var hash = hashProvider.ComputeHash(buffer);
hashProvider.TryComputeHash(buffer, hash, out var bytesWritten);
//var hash = hashProvider.ComputeHash(buffer);
for (int j = 0; j < hashes.Count; j++)
if (hash.SequenceEqual(hashes[j]))
result[j] = (byte[])buffer.Clone();
{
if (hash.Slice(0, bytesWritten).SequenceEqual(hashes[j]))
{
result[j] = buffer.ToArray();
}
}
}
});
return result;
@ -867,6 +879,7 @@ namespace CodeWalker.GameFiles
{
private const int TEST_ITERATIONS = 100000;
[SkipLocalsInit]
public static bool[] Solve(
uint[][] tables,
int inByte0, int inByte1, int inByte2, int inByte3,
@ -1510,9 +1523,8 @@ namespace CodeWalker.GameFiles
uint result = 0;
for (int index = 0; index < text.Length; index++)
{
var temp = 1025 * (LUT[text[index]] + result);
foreach(var c in text) {
var temp = 1025 * (LUT[c] + result);
result = (temp >> 6) ^ temp;
}

View File

@ -1,13 +1,19 @@
using System;
using CommunityToolkit.HighPerformance;
using CommunityToolkit.HighPerformance.Buffers;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static System.Net.Mime.MediaTypeNames;
namespace CodeWalker.GameFiles
{
@ -29,10 +35,19 @@ namespace CodeWalker.GameFiles
HashHex = "0x" + HashUint.ToString("X");
}
private const int minInclusive = 'A';
private const int maxInclusive = 'Z' - minInclusive;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToLower(char c)
public static byte ToLower(char c)
{
return (c >= 'A' && c <= 'Z') ? (char)(c - 'A' + 'a') : c;
return ToLower((byte)c);
//return (c >= 'A' && c <= 'Z') ? (byte)(c - 'A' + 'a') : (byte)c;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte ToLower(byte c)
{
return ('A' <= c && c <= 'Z') ? (byte)(c | 0x20) : c;
}
public static uint GenHash(string text, JenkHashInputEncoding encoding)
@ -54,105 +69,126 @@ namespace CodeWalker.GameFiles
for (uint i = 0; i < chars.Length; i++)
{
h += chars[i];
h += (h << 10);
h ^= (h >> 6);
h += h << 10;
h ^= h >> 6;
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHashLowerInline(string text)
{
return GenHashLower(text.AsSpan());
}
public static uint GenHashLower(string text)
{
if (text == null) return 0;
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)ToLower(text[i]);
h += (h << 10);
h ^= (h >> 6);
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
return h;
}
public static uint GenHashLower(ReadOnlySpan<char> text, ReadOnlySpan<char> str2 = default)
{
if (text == null) return 0;
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)ToLower(text[i]);
h += (h << 10);
h ^= (h >> 6);
}
for (int i = 0; i < str2.Length; i++)
{
h += (byte)ToLower(str2[i]);
h += (h << 10);
h ^= (h >> 6);
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
return h;
return GenHashLower(text.AsSpan());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHash(ReadOnlySpan<char> text)
{
if (text == null) return 0;
uint h = 0;
for (int i = 0; i < text.Length; i++)
foreach(var c in text)
{
h += (byte)text[i];
h += (h << 10);
h ^= (h >> 6);
h += (byte)c;
h += h << 10;
h ^= h >> 6;
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHashInline(string text)
{
return GenHash(text.AsSpan());
}
public static uint GenHash(string text)
{
if (text == null) return 0;
return GenHash(text.AsSpan());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHashLower(ReadOnlySpan<byte> data)
{
uint h = 0;
foreach(var b in data)
{
h += ToLower(b);
h += h << 10;
h ^= h >> 6;
}
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHashLower(ReadOnlySpan<char> text)
{
uint h = 0;
foreach(var c in text)
{
h += ToLower(c);
h += h << 10;
h ^= h >> 6;
}
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
public static uint GenHashLower(ReadOnlySpan<char> text, ReadOnlySpan<char> str2)
{
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)text[i];
h += (h << 10);
h ^= (h >> 6);
h += ToLower(text[i]);
h += h << 10;
h ^= h >> 6;
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
for (int i = 0; i < str2.Length; i++)
{
h += ToLower(str2[i]);
h += h << 10;
h ^= h >> 6;
}
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
public static uint GenHash(byte[] data)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint GenHash(ReadOnlySpan<byte> data)
{
uint h = 0;
for (uint i = 0; i < data.Length; i++)
foreach(var c in data)
{
h += data[i];
h += (h << 10);
h ^= (h >> 6);
h += c;
h += h << 10;
h ^= h >> 6;
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
h += h << 3;
h ^= h >> 11;
h += h << 15;
return h;
}
}
public enum JenkHashInputEncoding
@ -256,68 +292,65 @@ namespace CodeWalker.GameFiles
}
}
public static class DictionaryExtension
{
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
{
if (dict.ContainsKey(key))
{
return false;
}
dict[key] = value;
return true;
}
}
public static class JenkIndex
{
//public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount * 2, 2000000);
public static Dictionary<uint, string> Index = new Dictionary<uint, string>(2000000);
public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount, 2097152);
public static void Ensure(string str)
{
uint hash = JenkHash.GenHash(str);
Ensure(str, hash);
uint hash = JenkHash.GenHashInline(str);
addString(str, hash);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void addString(string str, uint hash)
{
//lock(Index)
//{
Index.TryAdd(hash, str);
//}
}
public static void Ensure(string str, uint hash)
{
if (hash == 0) return;
if (Index.ContainsKey(hash))
{
if (hash == 0)
return;
}
lock(Index)
{
Index[hash] = str;
}
addString(str, hash);
}
public static void Ensure(ReadOnlySpan<char> str, uint hash)
public static void Ensure(ReadOnlySpan<char> span, uint hash)
{
if (hash == 0) return;
if (hash == 0)
return;
if (Index.ContainsKey(hash))
{
return;
}
lock(Index)
var str = StringPool.Shared.GetOrAdd(span);
addString(str, hash);
}
public static void Ensure(ReadOnlySpan<byte> str, uint hash)
{
if (hash == 0)
return;
if (Index.ContainsKey(hash))
{
Index[hash] = str.ToString();
return;
}
addString(Encoding.ASCII.GetString(str), hash);
}
public static void EnsureLower(string str)
{
uint hash = JenkHash.GenHashLower(str);
uint hash = JenkHash.GenHashLowerInline(str);
Ensure(str, hash);
}
@ -329,8 +362,8 @@ namespace CodeWalker.GameFiles
public static void EnsureBoth(string str)
{
uint hash = JenkHash.GenHash(str);
uint hashLower = JenkHash.GenHashLower(str);
uint hash = JenkHash.GenHashInline(str);
uint hashLower = JenkHash.GenHashLowerInline(str);
Ensure(str, hash);
if (hash != hashLower)
{
@ -338,14 +371,23 @@ namespace CodeWalker.GameFiles
}
}
public static void EnsureBoth(ReadOnlySpan<char> str)
public static void EnsureBoth(ReadOnlySpan<char> strSpan)
{
uint hash = JenkHash.GenHash(str);
uint hashLower = JenkHash.GenHashLower(str);
Ensure(str, hash);
uint hash = JenkHash.GenHash(strSpan);
uint hashLower = JenkHash.GenHashLower(strSpan);
var contains = Index.ContainsKey(hash);
var containsLower = Index.ContainsKey(hashLower);
if (contains && containsLower)
{
return;
}
var str = StringPool.Shared.GetOrAdd(strSpan);
addString(str, hash);
if (hash != hashLower)
{
Ensure(str, hashLower);
addString(str, hashLower);
}
}
@ -368,6 +410,8 @@ namespace CodeWalker.GameFiles
return res;
}
public static bool TryGetString(uint hash, [MaybeNullWhen(false)] out string res) => Index.TryGetValue(hash, out res);
public static ICollection<string> GetAllStrings()
{
var res = Index.Values;

View File

@ -0,0 +1,8 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "<Pending>", Scope = "member", Target = "~M:CodeWalker.GameFiles.YmapFile.EnsureEntities(CodeWalker.GameFiles.Meta)")]

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<SelfContained>false</SelfContained>
</PropertyGroup>
</Project>

View File

@ -22,10 +22,10 @@ namespace CodeWalker.GameFiles
if (entry.Name.EndsWith("cache_y.dat", StringComparison.OrdinalIgnoreCase))// || entry.NameLower.EndsWith("cache_y_bank.dat"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
var cdfile = RpfMan.GetFile<CacheDatFile>(entry);
if (cdfile != null)
var cdfile = RpfManager.GetFile<CacheDatFile>(entry);
if (cdfile is not null)
{
var odata = entry.File.ExtractFile(entry as RpfFileEntry);
var odata = entry.File.ExtractFile((RpfFileEntry)entry);
//var ndata = cdfile.Save();
var xml = CacheDatXml.GetXml(cdfile);
@ -67,7 +67,7 @@ namespace CodeWalker.GameFiles
{
UpdateStatus?.Invoke(string.Format(entry.Path));
HeightmapFile hmf = null;
hmf = RpfMan.GetFile<HeightmapFile>(entry);
hmf = RpfManager.GetFile<HeightmapFile>(entry);
var d1 = hmf.RawFileData;
//var d2 = hmf.Save();
var xml = HmapXml.GetXml(hmf);
@ -104,7 +104,7 @@ namespace CodeWalker.GameFiles
{
UpdateStatus?.Invoke(string.Format(entry.Path));
WatermapFile wmf = null;
wmf = RpfMan.GetFile<WatermapFile>(entry);
wmf = RpfManager.GetFile<WatermapFile>(entry);
//var d1 = wmf.RawFileData;
//var d2 = wmf.Save();
//var xml = WatermapXml.GetXml(wmf);
@ -144,8 +144,7 @@ namespace CodeWalker.GameFiles
foreach (RpfEntry entry in rpf.AllEntries)
{
var rfe = entry as RpfFileEntry;
var rbfe = rfe as RpfBinaryFileEntry;
if ((rfe == null) || (rbfe == null)) continue;
if ((rfe == null) || (rfe is not RpfBinaryFileEntry rbfe)) continue;
if (rfe.IsExtension(".rel"))
{
@ -162,7 +161,7 @@ namespace CodeWalker.GameFiles
{
data = rel.Save();
if (data != null)
if (data.Length > 0)
{
if (data.Length != rbfe.FileUncompressedSize)
{ }
@ -172,22 +171,15 @@ namespace CodeWalker.GameFiles
{
for (int i = 0; i < data.Length; i++) //raw file test
if (data[i] != rel.RawFileData[i])
{ break; }
{
break;
}
}
RelFile rel2 = new RelFile();
rel2.Load(data, rfe);//roundtrip test
if (rel2.IndexCount != rel.IndexCount)
{ }
if (rel2.RelDatas == null)
{ }
}
else
{ }
}
if (xmltest)
@ -210,7 +202,9 @@ namespace CodeWalker.GameFiles
{
for (int i = 0; i < data.Length; i++) //raw file test
if (data[i] != rel.RawFileData[i])
{ break; }
{
break;
}
}
var relxml2 = RelXml.GetXml(rel4); //full insanity
@ -336,7 +330,7 @@ namespace CodeWalker.GameFiles
uint s2 = (un / 80000);
float f1 = s1 / 5000.0f;
float f2 = s2 / 5000.0f;
sb.AppendFormat("{0}, {1}, 0, {2}\r\n", f1, f2, sn);
sb.AppendFormat("{0}, {1}, 0, {2}\r\n", f1, f2, sn.ToString());
}
}
@ -386,7 +380,7 @@ namespace CodeWalker.GameFiles
if (entry.IsExtension(".awc"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
var awcfile = RpfMan.GetFile<AwcFile>(entry);
var awcfile = RpfManager.GetFile<AwcFile>(entry);
if (awcfile != null)
{ }
}
@ -443,8 +437,7 @@ namespace CodeWalker.GameFiles
if (n.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase) || n.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase) || n.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
{
var rfe = entry as RpfResourceFileEntry;
if (rfe == null) continue;
if (entry is not RpfResourceFileEntry rfe) continue;
UpdateStatus?.Invoke(string.Format(entry.Path));
@ -603,55 +596,52 @@ namespace CodeWalker.GameFiles
var data = entry.File.ExtractFile(fentry);
if (data != null)
{
using (MemoryStream ms = new MemoryStream(data))
if (RbfFile.IsRBF(data))
{
if (RbfFile.IsRBF(ms))
UpdateStatus?.Invoke(string.Format(entry.Path));
var rbf = new RbfFile();
_ = rbf.Load(data);
allrbfs.Add(fentry.Path);
var xml = RbfXml.GetXml(rbf);
if (!string.IsNullOrEmpty(xml))
{ }
var xdoc = new XmlDocument();
xdoc.LoadXml(xml);
var rbf2 = XmlRbf.GetRbf(xdoc);
var rbf2b = rbf2.Save();
var rbf3 = new RbfFile();
rbf3.Load(rbf2b);
var xml3 = RbfXml.GetXml(rbf3);
if (xml.Length != xml3.Length)
{ }
if (xml != xml3)
{
UpdateStatus?.Invoke(string.Format(entry.Path));
diffrbfs.Add(fentry.Path);
}
var rbf = new RbfFile();
rbf.Load(ms);
allrbfs.Add(fentry.Path);
var xml = RbfXml.GetXml(rbf);
if (!string.IsNullOrEmpty(xml))
{ }
var xdoc = new XmlDocument();
xdoc.LoadXml(xml);
var rbf2 = XmlRbf.GetRbf(xdoc);
var rbf2b = rbf2.Save();
var rbf3 = new RbfFile();
rbf3.Load(rbf2b);
var xml3 = RbfXml.GetXml(rbf3);
if (xml.Length != xml3.Length)
{ }
if (xml != xml3)
if (data.Length != rbf2b.Length)
{
//File.WriteAllBytes("C:\\GitHub\\CodeWalkerResearch\\RBF\\" + fentry.Name + ".dat0", data);
//File.WriteAllBytes("C:\\GitHub\\CodeWalkerResearch\\RBF\\" + fentry.Name + ".dat1", rbf2b);
}
else
{
for (int i = 0; i < data.Length; i++)
{
diffrbfs.Add(fentry.Path);
}
if (data.Length != rbf2b.Length)
{
//File.WriteAllBytes("C:\\GitHub\\CodeWalkerResearch\\RBF\\" + fentry.Name + ".dat0", data);
//File.WriteAllBytes("C:\\GitHub\\CodeWalkerResearch\\RBF\\" + fentry.Name + ".dat1", rbf2b);
}
else
{
for (int i = 0; i < data.Length; i++)
if (data[i] != rbf2b[i])
{
if (data[i] != rbf2b[i])
{
diffrbfs.Add(fentry.Path);
break;
}
diffrbfs.Add(fentry.Path);
break;
}
}
}
}
}
@ -677,8 +667,7 @@ namespace CodeWalker.GameFiles
try
#endif
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
if (rfe.IsExtension(".cut"))
{
@ -720,8 +709,7 @@ namespace CodeWalker.GameFiles
try
#endif
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
if (rfe.IsExtension(".yld"))
{
@ -760,8 +748,7 @@ namespace CodeWalker.GameFiles
try
#endif
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
if (rfe.IsExtension(".yed"))
{
@ -810,7 +797,7 @@ namespace CodeWalker.GameFiles
if (entry.IsExtension(".ycd"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
YcdFile ycd1 = RpfMan.GetFile<YcdFile>(entry);
YcdFile ycd1 = RpfManager.GetFile<YcdFile>(entry);
if (ycd1 == null)
{
errorentries.Add(entry);
@ -1084,7 +1071,7 @@ namespace CodeWalker.GameFiles
YtdFile ytdfile = null;
try
{
ytdfile = RpfMan.GetFile<YtdFile>(entry);
ytdfile = RpfManager.GetFile<YtdFile>(entry);
}
catch (Exception ex)
{
@ -1110,8 +1097,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ytdfile != null) && (ytdfile.TextureDict != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = ytdfile.Save();
@ -1181,7 +1167,7 @@ namespace CodeWalker.GameFiles
YbnFile ybn = null;
try
{
ybn = RpfMan.GetFile<YbnFile>(entry);
ybn = RpfManager.GetFile<YbnFile>(entry);
}
catch (Exception ex)
{
@ -1198,8 +1184,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ybn != null) && (ybn.Bounds != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = ybn.Save();
@ -1225,32 +1210,28 @@ namespace CodeWalker.GameFiles
case BoundsType.Sphere:
{
var a = ybn.Bounds as BoundSphere;
var b = ybn2.Bounds as BoundSphere;
if (b == null)
if (ybn2.Bounds is not BoundSphere b)
{ continue; }
break;
}
case BoundsType.Capsule:
{
var a = ybn.Bounds as BoundCapsule;
var b = ybn2.Bounds as BoundCapsule;
if (b == null)
if (ybn2.Bounds is not BoundCapsule b)
{ continue; }
break;
}
case BoundsType.Box:
{
var a = ybn.Bounds as BoundBox;
var b = ybn2.Bounds as BoundBox;
if (b == null)
if (ybn2.Bounds is not BoundBox b)
{ continue; }
break;
}
case BoundsType.Geometry:
{
var a = ybn.Bounds as BoundGeometry;
var b = ybn2.Bounds as BoundGeometry;
if (b == null)
if (ybn2.Bounds is not BoundGeometry b)
{ continue; }
if (a.Polygons?.Length != b.Polygons?.Length)
{ continue; }
@ -1266,8 +1247,7 @@ namespace CodeWalker.GameFiles
case BoundsType.GeometryBVH:
{
var a = ybn.Bounds as BoundBVH;
var b = ybn2.Bounds as BoundBVH;
if (b == null)
if (ybn2.Bounds is not BoundBVH b)
{ continue; }
if (a.BVH?.Nodes?.data_items?.Length != b.BVH?.Nodes?.data_items?.Length)
{ }
@ -1285,8 +1265,7 @@ namespace CodeWalker.GameFiles
case BoundsType.Composite:
{
var a = ybn.Bounds as BoundComposite;
var b = ybn2.Bounds as BoundComposite;
if (b == null)
if (ybn2.Bounds is not BoundComposite b)
{ continue; }
if (a.Children?.data_items?.Length != b.Children?.data_items?.Length)
{ }
@ -1295,24 +1274,21 @@ namespace CodeWalker.GameFiles
case BoundsType.Disc:
{
var a = ybn.Bounds as BoundDisc;
var b = ybn2.Bounds as BoundDisc;
if (b == null)
if (ybn2.Bounds is not BoundDisc b)
{ continue; }
break;
}
case BoundsType.Cylinder:
{
var a = ybn.Bounds as BoundCylinder;
var b = ybn2.Bounds as BoundCylinder;
if (b == null)
if (ybn2.Bounds is not BoundCylinder b)
{ continue; }
break;
}
case BoundsType.Cloth:
{
var a = ybn.Bounds as BoundCloth;
var b = ybn2.Bounds as BoundCloth;
if (b == null)
if (ybn2.Bounds is not BoundCloth b)
{ continue; }
break;
}
@ -1353,7 +1329,7 @@ namespace CodeWalker.GameFiles
YdrFile ydr = null;
try
{
ydr = RpfMan.GetFile<YdrFile>(entry);
ydr = RpfManager.GetFile<YdrFile>(entry);
}
catch (Exception ex)
{
@ -1362,8 +1338,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ydr != null) && (ydr.Drawable != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
if (boundsonly && (ydr.Drawable.Bound == null))
@ -1412,7 +1387,7 @@ namespace CodeWalker.GameFiles
YddFile ydd = null;
try
{
ydd = RpfMan.GetFile<YddFile>(entry);
ydd = RpfManager.GetFile<YddFile>(entry);
}
catch (Exception ex)
{
@ -1421,8 +1396,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ydd != null) && (ydd.DrawableDict != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = ydd.Save();
@ -1481,7 +1455,7 @@ namespace CodeWalker.GameFiles
YftFile yft = null;
try
{
yft = RpfMan.GetFile<YftFile>(entry);
yft = RpfManager.GetFile<YftFile>(entry);
}
catch (Exception ex)
{
@ -1490,8 +1464,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (yft != null) && (yft.Fragment != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = yft.Save();
@ -1558,7 +1531,7 @@ namespace CodeWalker.GameFiles
YptFile ypt = null;
try
{
ypt = RpfMan.GetFile<YptFile>(entry);
ypt = RpfManager.GetFile<YptFile>(entry);
}
catch (Exception ex)
{
@ -1567,8 +1540,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ypt != null) && (ypt.PtfxList != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = ypt.Save();
@ -1616,7 +1588,7 @@ namespace CodeWalker.GameFiles
YnvFile ynv = null;
try
{
ynv = RpfMan.GetFile<YnvFile>(entry);
ynv = RpfManager.GetFile<YnvFile>(entry);
}
catch (Exception ex)
{
@ -1646,8 +1618,7 @@ namespace CodeWalker.GameFiles
}
if (savetest && (ynv != null) && (ynv.Nav != null))
{
var fentry = entry as RpfFileEntry;
if (fentry == null)
if (entry is not RpfFileEntry fentry)
{ continue; } //shouldn't happen
var bytes = ynv.Save();
@ -1688,8 +1659,7 @@ namespace CodeWalker.GameFiles
try
#endif
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
if (rfe.IsExtension(".yvr"))
{
@ -1785,7 +1755,7 @@ namespace CodeWalker.GameFiles
if (entry.IsExtension(".ymap"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
YmapFile ymapfile = RpfMan.GetFile<YmapFile>(entry);
YmapFile ymapfile = RpfManager.GetFile<YmapFile>(entry);
if ((ymapfile != null))// && (ymapfile.Meta != null))
{ }
}
@ -1805,15 +1775,14 @@ namespace CodeWalker.GameFiles
{
foreach (RpfEntry entry in file.AllEntries)
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
try
{
if (rfe.IsExtension(".ypdb"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
YpdbFile ypdb = RpfMan.GetFile<YpdbFile>(entry);
YpdbFile ypdb = RpfManager.GetFile<YpdbFile>(entry);
if (ypdb != null)
{
var odata = entry.File.ExtractFile(entry as RpfFileEntry);
@ -1854,15 +1823,14 @@ namespace CodeWalker.GameFiles
{
foreach (RpfEntry entry in file.AllEntries)
{
var rfe = entry as RpfFileEntry;
if (rfe == null) continue;
if (entry is not RpfFileEntry rfe) continue;
try
{
if (rfe.IsExtension(".yfd"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
YfdFile yfd = RpfMan.GetFile<YfdFile>(entry);
YfdFile yfd = RpfManager.GetFile<YfdFile>(entry);
if (yfd != null)
{
if (yfd.FrameFilterDictionary != null)
@ -1909,7 +1877,7 @@ namespace CodeWalker.GameFiles
if (entry.IsExtension(".mrf"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
MrfFile mrffile = RpfMan.GetFile<MrfFile>(entry);
MrfFile mrffile = RpfManager.GetFile<MrfFile>(entry);
if (mrffile != null)
{
var odata = entry.File.ExtractFile(entry as RpfFileEntry);
@ -2079,10 +2047,10 @@ namespace CodeWalker.GameFiles
if (entry.IsExtension(".fxc"))
{
UpdateStatus?.Invoke(string.Format(entry.Path));
var fxcfile = RpfMan.GetFile<FxcFile>(entry);
if (fxcfile != null)
var fxcfile = RpfManager.GetFile<FxcFile>(entry);
if (fxcfile is not null)
{
var odata = entry.File.ExtractFile(entry as RpfFileEntry);
var odata = entry.File.ExtractFile((RpfFileEntry)entry);
var ndata = fxcfile.Save();
if (ndata.Length == odata.Length)
{
@ -2294,7 +2262,7 @@ namespace CodeWalker.GameFiles
if (doydr && entry.IsExtension(".ydr"))
{
UpdateStatus?.Invoke(entry.Path);
YdrFile ydr = RpfMan.GetFile<YdrFile>(entry);
YdrFile ydr = RpfManager.GetFile<YdrFile>(entry);
if (ydr == null)
{
@ -2323,7 +2291,7 @@ namespace CodeWalker.GameFiles
else if (doydd & entry.IsExtension(".ydd"))
{
UpdateStatus?.Invoke(entry.Path);
YddFile ydd = RpfMan.GetFile<YddFile>(entry);
YddFile ydd = RpfManager.GetFile<YddFile>(entry);
if (ydd == null)
{
@ -2355,7 +2323,7 @@ namespace CodeWalker.GameFiles
else if (doyft && entry.IsExtension(".yft"))
{
UpdateStatus?.Invoke(entry.Path);
YftFile yft = RpfMan.GetFile<YftFile>(entry);
YftFile yft = RpfManager.GetFile<YftFile>(entry);
if (yft == null)
{

View File

@ -5,7 +5,7 @@ namespace CodeWalker.Core.Utils
{
public static class BoundingBoxExtensions
{
public static Vector3 Size(this BoundingBox bounds)
public static Vector3 Size(in this BoundingBox bounds)
{
return new Vector3(
Math.Abs(bounds.Maximum.X - bounds.Minimum.X),
@ -13,25 +13,25 @@ namespace CodeWalker.Core.Utils
Math.Abs(bounds.Maximum.Z - bounds.Minimum.Z));
}
public static Vector3 Center(this BoundingBox bounds)
public static Vector3 Center(in this BoundingBox bounds)
{
return (bounds.Minimum + bounds.Maximum) * 0.5F;
}
public static BoundingBox Encapsulate(this BoundingBox box, BoundingBox bounds)
public static BoundingBox Encapsulate(ref this BoundingBox box, ref BoundingBox bounds)
{
box.Minimum = Vector3.Min(box.Minimum, bounds.Minimum);
box.Maximum = Vector3.Max(box.Maximum, bounds.Maximum);
Vector3.Min(ref box.Minimum, ref bounds.Minimum, out box.Minimum);
Vector3.Max(ref box.Maximum, ref bounds.Maximum, out box.Maximum);
return box;
}
public static float Radius(this BoundingBox box)
public static float Radius(in this BoundingBox box)
{
var extents = (box.Maximum - box.Minimum) * 0.5F;
return extents.Length();
}
public static BoundingBox Expand(this BoundingBox b, float amount)
public static BoundingBox Expand(in this BoundingBox b, float amount)
{
return new BoundingBox(b.Minimum - Vector3.One * amount, b.Maximum + Vector3.One * amount);
}

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace CodeWalker
{
public class Cache<TKey, TVal> where TVal : Cacheable<TKey>
public class Cache<TKey, TVal> where TVal : Cacheable<TKey> where TKey : notnull
{
public long MaxMemoryUsage = 536870912; //512mb
public long CurrentMemoryUsage = 0;
@ -21,13 +21,7 @@ namespace CodeWalker
private object loadedListLock = new object();
private ConcurrentDictionary<TKey, LinkedListNode<TVal>> loadedListDict = new ConcurrentDictionary<TKey, LinkedListNode<TVal>>();
public int Count
{
get
{
return loadedList.Count;
}
}
public int Count => loadedList.Count;
public Cache()
{
@ -40,11 +34,16 @@ namespace CodeWalker
public void BeginFrame()
{
var now = DateTime.Now;
if (now - CurrentTime < TimeSpan.FromSeconds(0.05))
{
return;
}
CurrentTime = DateTime.Now;
Compact();
}
public TVal TryGet(TKey key)
public TVal? TryGet(in TKey key)
{
lock (loadedListLock)
{
@ -55,7 +54,7 @@ namespace CodeWalker
lln.Value.LastUseTime = CurrentTime;
}
return (lln != null) ? lln.Value : null;
return lln?.Value;
}
}
public bool TryAdd(TKey key, TVal item)
@ -80,9 +79,9 @@ namespace CodeWalker
var oldlln = loadedList.First;
var cachetime = LoadingCacheTime;
int iter = 0, maxiter = 2;
while (!CanAdd() && (iter<maxiter))
while (!CanAdd() && iter<maxiter)
{
while ((!CanAdd()) && (oldlln != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime))
while (!CanAdd() && oldlln is not null && (CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime)
{
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
lock (loadedListLock)
@ -92,8 +91,11 @@ namespace CodeWalker
loadedListDict.TryRemove(oldlln.Value.Key, out _);
oldlln.Value = null;
oldlln = null;
if (oldlln.Value is IDisposable disposable)
{
disposable.Dispose();
}
//GC.Collect();
oldlln = loadedList.First;
}
@ -119,10 +121,7 @@ namespace CodeWalker
return false;
}
public bool CanAdd()
{
return Interlocked.Read(ref CurrentMemoryUsage) < MaxMemoryUsage;
}
public bool CanAdd() => CurrentMemoryUsage < MaxMemoryUsage;
public void Clear()
@ -135,7 +134,7 @@ namespace CodeWalker
Interlocked.Exchange(ref CurrentMemoryUsage, 0);
}
public void Remove(TKey key)
public void Remove(in TKey key)
{
if (!loadedListDict.ContainsKey(key))
{
@ -148,6 +147,10 @@ namespace CodeWalker
{
loadedList.Remove(n);
}
if (n is IDisposable disposable)
{
disposable.Dispose();
}
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
}
}
@ -158,15 +161,19 @@ namespace CodeWalker
lock(loadedListLock)
{
var oldlln = loadedList.First;
while (oldlln != null)
while (oldlln is not null)
{
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break;
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime)
break;
var nextln = oldlln.Next;
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.TryRemove(oldlln.Value.Key, out _);
loadedListDict.TryRemove(oldlln.Value.Key, out var n);
if (n is IDisposable disposable)
{
disposable.Dispose();
}
loadedList.Remove(oldlln); //gc should free up memory later..
oldlln.Value = null;
oldlln = nextln;
}
}
@ -175,7 +182,7 @@ namespace CodeWalker
}
public abstract class Cacheable<TKey>
public abstract class Cacheable<TKey> where TKey : notnull
{
public TKey Key;
public DateTime LastUseTime;

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace CodeWalker.Core.Utils;
[EventSource(Name = "CodeWalker-Diagnostics")]
[EventSource(Name = "CodeWalker-Diagnostics", Guid = "911cf260-f98c-5a05-7f16-f11db360be50")]
public class ETWEvents : EventSource
{
public static class Keywords
@ -25,7 +25,7 @@ public class ETWEvents : EventSource
}
public ETWEvents(bool throwOnEventWriteErrors) : base(throwOnEventWriteErrors)
private ETWEvents(bool throwOnEventWriteErrors) : base(throwOnEventWriteErrors)
{ }
[Event(1, Message = "Starting up.", Keywords = Keywords.Performance, Level = EventLevel.Informational)]
@ -33,11 +33,32 @@ public class ETWEvents : EventSource
WriteEvent(1);
}
[Event(2, Message = "Creating form {0}", Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void CreatingForm(string form) { WriteEvent(2, form); }
[Event(2, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void CreatingFormStart(string form) { WriteEvent(2, form); }
[Event(3, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void CreatingFormStop() { WriteEvent(3); }
[Event(4, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void LoadingForm(string form) { WriteEvent(4, form); }
[Event(5, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void RefreshingMainTreeViewStart(string path) { WriteEvent(5, path); }
[Event(6, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void RefreshingMainTreeViewStop() { WriteEvent(6); }
[Event(7, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void RefreshMainTreeViewStart() { WriteEvent(7); }
[Event(8, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void RefreshMainTreeViewStop() { WriteEvent(8); }
[Event(9, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void InitFileCacheStart() {
WriteEvent(9);
}
[Event(10, Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void InitFileCacheStop() { WriteEvent(10); }
[Event(3, Message = "Loading form {0}", Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void LoadingForm(string form) { WriteEvent(3, form); }
public static readonly ETWEvents Log = new ETWEvents(true);
}

View File

@ -239,8 +239,7 @@ namespace CodeWalker
public override bool Equals(object obj)
{
var id = obj as Identifier;
if (id != null)
if (obj is Identifier id)
return String == id.String;
return false;
}
@ -383,8 +382,7 @@ namespace CodeWalker
{
ret = ReadTokenSingle();
} while (ret == null);
var id = ret as Identifier;
if (id != null)
if (ret is Identifier id)
{
object colon;
do
@ -553,8 +551,7 @@ namespace CodeWalker
public FbxNode ReadNode()
{
var first = ReadToken();
var id = first as Identifier;
if (id == null)
if (first is not Identifier id)
{
if (first is EndOfStream)
return null;
@ -1311,7 +1308,7 @@ namespace CodeWalker
private delegate void PropertyWriter(BinaryWriter sw, object obj);
struct WriterInfo
readonly struct WriterInfo
{
public readonly char id;
public readonly PropertyWriter writer;

View File

@ -110,7 +110,7 @@ namespace CodeWalker
var vc = g.VertexData.VertexCount;
for (int i = 0; i < vc; i++)
{
var vp = MetaTypes.ConvertData<Vector3>(vb, i * vs);//position offset should always be 0!
MetaTypes.TryConvertData<Vector3>(vb, i * vs, out var vp);
allVerts.Add(vp);
bbMin = Vector3.Min(bbMin, vp);
bbMax = Vector3.Max(bbMax, vp);
@ -228,7 +228,6 @@ namespace CodeWalker
private FbxModel TryConvertModel(FbxNode mnode)
{
FbxNode geonode = null;
var matnodes = new List<FbxNode>();
foreach (var cnode in mnode.Connections)
@ -250,10 +249,8 @@ namespace CodeWalker
return null; //need atleast one material...
var fnEdges = geonode["Edges"]?.Value as int[];//do we need this? maybe for collision/navmesh
var fnVerts = geonode["Vertices"]?.Value as double[];
var fnIndices = geonode["PolygonVertexIndex"]?.Value as int[];
if ((fnVerts == null) || (fnIndices == null))
if ((geonode["Vertices"]?.Value is not double[] fnVerts) || (geonode["PolygonVertexIndex"]?.Value is not int[] fnIndices))
{ return null; } //no mesh data.. abort!
var fnNormals = new List<FbxNode>();
@ -476,12 +473,18 @@ namespace CodeWalker
if (dGeomAABBs.Count > 1)//need to include whole model AABB first, if more than one geometry..
{
var dGeomAABBs2 = new List<AABB_s>();
dModelAABB.Min = new Vector4(float.MaxValue);
dModelAABB.Max = new Vector4(float.MinValue);
dModelAABB = new AABB_s
{
Min = new Vector4(float.MaxValue),
Max = new Vector4(float.MinValue),
};
foreach (var aabb in dGeomAABBs)
{
dModelAABB.Min = Vector4.Min(dModelAABB.Min, aabb.Min);
dModelAABB.Max = Vector4.Max(dModelAABB.Max, aabb.Max);
dModelAABB = new AABB_s
{
Min = Vector4.Min(dModelAABB.Min, aabb.Min),
Max = Vector4.Max(dModelAABB.Max, aabb.Max)
};
}
dGeomAABBs2.Add(dModelAABB);
dGeomAABBs2.AddRange(dGeomAABBs);
@ -577,13 +580,20 @@ namespace CodeWalker
if (vList.Count > 0)
{
aabb.Min = new Vector4(float.MaxValue);
aabb.Max = new Vector4(float.MinValue);
aabb = new AABB_s
{
Min = new Vector4(float.MaxValue),
Max = new Vector4(float.MinValue),
};
foreach (var vert in vList)
{
var v = new Vector4(vert.Position, vert.Position.X);
aabb.Min = Vector4.Min(aabb.Min, v);
aabb.Max = Vector4.Max(aabb.Max, v);
aabb = new AABB_s
{
Min = Vector4.Min(aabb.Min, v),
Max = Vector4.Max(aabb.Max, v)
};
}
}
@ -664,15 +674,9 @@ namespace CodeWalker
{
texConns.Add(conn);
var texName = GetStringFromObjectList(conn.Properties, 1)?.Replace("Texture::", "");
var ftexName = conn["FileName"]?.Value as string;
if (ftexName != null)
if (conn["FileName"]?.Value is string ftexName)
{
try
{
texName = Path.GetFileNameWithoutExtension(ftexName);
}
catch
{ }
texName = Path.GetFileNameWithoutExtension(ftexName);
}
texNames.Add(texName);
}
@ -959,7 +963,7 @@ namespace CodeWalker
}
private void WriteBytes<T>(T val, int offset) where T : struct
{
var data = MetaTypes.ConvertToBytes(val);
var data = MetaTypes.ConvertToBytes(in val);
for (int i = 0; i < data.Length; i++)
{
Bytes[offset + i] = data[i];

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils
{
public static class FileUtils
{
public static bool TryFindFolder(string dirToFind, out string folder)
{
return TryFindFolder(dirToFind, null, out folder);
}
public static bool TryFindFolder(string dirToFind, string? basePath, [NotNullWhen(true)]out string folder)
{
basePath ??= Assembly.GetExecutingAssembly().Location;
// Search up directory tree starting at assembly path looking for 'Images' dir.
var searchPath = Path.GetDirectoryName(basePath);
ArgumentNullException.ThrowIfNullOrEmpty(basePath, nameof(basePath));
while (true)
{
var testPath = Path.Combine(searchPath, dirToFind);
if (Directory.Exists(testPath))
{
// Found it!
folder = testPath;
return true;
}
// Move up one directory.
var newSearchPath = Path.GetFullPath(Path.Combine(searchPath, ".."));
if (newSearchPath == searchPath)
{
// Didn't move up, so we're at the root.
folder = null;
return false;
}
searchPath = newSearchPath;
}
}
}
}

View File

@ -5,12 +5,14 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MatrixSystem = System.Numerics.Matrix4x4;
namespace CodeWalker
{
public static class MatrixExtensions
{
public static Vector3 MultiplyW(this Matrix m, Vector3 v)
public static Vector3 MultiplyW(in this Matrix m, Vector3 v)
{
float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41;
float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42;
@ -19,7 +21,18 @@ namespace CodeWalker
float iw = 1.0f / Math.Abs(w);
return new Vector3(x * iw, y * iw, z * iw);
}
public static Vector3 Multiply(this Matrix m, Vector3 v)
public static Vector3 MultiplyW(in this Matrix m, in Vector3 v)
{
float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41;
float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42;
float z = (((m.M13 * v.X) + (m.M23 * v.Y)) + (m.M33 * v.Z)) + m.M43;
float w = (((m.M14 * v.X) + (m.M24 * v.Y)) + (m.M34 * v.Z)) + m.M44;
float iw = 1.0f / Math.Abs(w);
return new Vector3(x * iw, y * iw, z * iw);
}
public static Vector3 Multiply(in this Matrix m, Vector3 v)
{
float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41;
float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42;
@ -27,7 +40,7 @@ namespace CodeWalker
return new Vector3(x, y, z);
//this quick mul ignores W...
}
public static Vector3 MultiplyRot(this Matrix m, Vector3 v)
public static Vector3 MultiplyRot(in this Matrix m, Vector3 v)
{
float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z));// + m.M41;
float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z));// + m.M42;
@ -36,7 +49,7 @@ namespace CodeWalker
//this quick mul ignores W and translation...
}
public static Vector4 Multiply(this Matrix m, Vector4 v)
public static Vector4 Multiply(in this Matrix m, Vector4 v)
{
float x = (((m.M11 * v.X) + (m.M21 * v.Y)) + (m.M31 * v.Z)) + m.M41;
float y = (((m.M12 * v.X) + (m.M22 * v.Y)) + (m.M32 * v.Z)) + m.M42;
@ -45,11 +58,12 @@ namespace CodeWalker
return new Vector4(x, y, z, w);
}
public static Quaternion ToQuaternion(this Matrix m)
public static Quaternion ToQuaternion(in this Matrix m)
{
var rmat = m;
rmat.TranslationVector = Vector3.Zero;
return Quaternion.RotationMatrix(rmat);
Quaternion.RotationMatrix(ref rmat, out var result);
return result;
}
}

View File

@ -12,7 +12,7 @@ namespace CodeWalker
public static class QuaternionExtension
{
public static Vector3 Multiply(this Quaternion a, Vector3 b)
public static Vector3 Multiply(in this Quaternion a, in Vector3 b)
{
float axx = a.X * 2.0f;
float ayy = a.Y * 2.0f;
@ -31,7 +31,7 @@ namespace CodeWalker
((b.X * (axzz - awyy)) + (b.Y * (ayzz + awxx))) + (b.Z * ((1.0f - axxx) - ayyy)));
}
public static Matrix ToMatrix(this Quaternion q)
public static Matrix ToMatrix(in this Quaternion q)
{
float xx = q.X * q.X;
float yy = q.Y * q.Y;
@ -62,12 +62,12 @@ namespace CodeWalker
return result;
}
public static Vector4 ToVector4(this Quaternion q)
public static Vector4 ToVector4(in this Quaternion q)
{
return new Vector4(q.X, q.Y, q.Z, q.W);
}
public static Quaternion FastLerp(Quaternion a, Quaternion b, float v)
public static Quaternion FastLerp(in Quaternion a, in Quaternion b, float v)
{
var r = new Quaternion();
var vi = 1.0f - v;

View File

@ -0,0 +1,59 @@
using Collections.Pooled;
using CommunityToolkit.HighPerformance.Buffers;
using Microsoft.Extensions.ObjectPool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils
{
public class SharedObjectPool<T> where T : class, new()
{
private static readonly ObjectPool<T> s_shared = ObjectPool.Create<T>();
public static ObjectPool<T> Shared => s_shared;
}
public class PooledListObjectPolicy<T> : PooledObjectPolicy<PooledList<T>>
{
public PooledList<T> Get()
{
return new PooledList<T>();
}
public override PooledList<T> Create()
{
return new PooledList<T>();
}
public override bool Return(PooledList<T> list)
{
foreach (var entry in list.Span)
{
if (entry is IDisposable disposable)
disposable.Dispose();
if (entry is IResettable resettable)
resettable.TryReset();
}
list.Clear();
return true;
}
}
public static class StringPoolExtension
{
[SkipLocalsInit]
public static string GetStringPooled(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
Span<char> buffer = stackalloc char[bytes.Length];
var charsWritten = encoding.GetChars(bytes, buffer);
return StringPool.Shared.GetOrAdd(buffer.Slice(0, charsWritten));
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -12,14 +13,15 @@ namespace CodeWalker.Core.Utils
/// <remarks>
/// To get an instance of this type, use <see cref="MemoryExtensions.EnumerateLines(ReadOnlySpan{char})"/>.
/// </remarks>
public ref struct SpanSplitEnumerator
public ref struct SpanSplitEnumerator<T> where T : IEquatable<T>?
{
private ReadOnlySpan<char> _remaining;
private ReadOnlySpan<char> _current;
private ReadOnlySpan<T> _remaining;
private ReadOnlySpan<T> _current;
private bool _isEnumeratorActive;
private char _splitBy;
private readonly T _splitBy;
private readonly ReadOnlySpan<T> _splitBySpan;
internal SpanSplitEnumerator(ReadOnlySpan<char> buffer, char splitBy)
internal SpanSplitEnumerator(ReadOnlySpan<T> buffer, T splitBy)
{
_remaining = buffer;
_current = default;
@ -30,12 +32,12 @@ namespace CodeWalker.Core.Utils
/// <summary>
/// Gets the line at the current position of the enumerator.
/// </summary>
public ReadOnlySpan<char> Current => _current;
public ReadOnlySpan<T> Current => _current;
/// <summary>
/// Returns this instance as an enumerator.
/// </summary>
public SpanSplitEnumerator GetEnumerator() => this;
public readonly SpanSplitEnumerator<T> GetEnumerator() => this;
/// <summary>
/// Advances the enumerator to the next line of the span.
@ -51,7 +53,7 @@ namespace CodeWalker.Core.Utils
return false; // EOF previously reached or enumerator was never initialized
}
ReadOnlySpan<char> remaining = _remaining;
ReadOnlySpan<T> remaining = _remaining;
int idx = remaining.IndexOf(_splitBy);
@ -74,21 +76,175 @@ namespace CodeWalker.Core.Utils
}
}
public ref struct SpanSplitEnumeratorAny<T> where T : IEquatable<T>?
{
private ReadOnlySpan<T> _remaining;
private ReadOnlySpan<T> _current;
private bool _isEnumeratorActive;
private readonly ReadOnlySpan<T> _splitBy;
internal SpanSplitEnumeratorAny(ReadOnlySpan<T> buffer, ReadOnlySpan<T> splitBy)
{
_remaining = buffer;
_current = default;
_isEnumeratorActive = true;
_splitBy = splitBy;
}
/// <summary>
/// Gets the line at the current position of the enumerator.
/// </summary>
public readonly ReadOnlySpan<T> Current => _current;
/// <summary>
/// Returns this instance as an enumerator.
/// </summary>
public readonly SpanSplitEnumeratorAny<T> GetEnumerator() => this;
/// <summary>
/// Advances the enumerator to the next line of the span.
/// </summary>
/// <returns>
/// True if the enumerator successfully advanced to the next line; false if
/// the enumerator has advanced past the end of the span.
/// </returns>
public bool MoveNext()
{
if (!_isEnumeratorActive)
{
return false; // EOF previously reached or enumerator was never initialized
}
ReadOnlySpan<T> remaining = _remaining;
int idx = remaining.IndexOfAny(_splitBy);
if ((uint)idx < (uint)remaining.Length)
{
_current = remaining.Slice(0, idx);
_remaining = remaining.Slice(idx + 1);
}
else
{
// We've reached EOF, but we still need to return 'true' for this final
// iteration so that the caller can query the Current property once more.
_current = remaining;
_remaining = default;
_isEnumeratorActive = false;
}
return true;
}
}
public static class EnumerateSplitExtensions
{
public static SpanSplitEnumerator EnumerateSplit(this ReadOnlySpan<char> span, char splitBy)
public static SpanSplitEnumerator<T> EnumerateSplit<T>(this ReadOnlySpan<T> span, T splitBy) where T : IEquatable<T>
{
return new SpanSplitEnumerator(span, splitBy);
return new SpanSplitEnumerator<T>(span, splitBy);
}
public static SpanSplitEnumerator EnumerateSplit(this Span<char> span, char splitBy)
public static SpanSplitEnumeratorAny<T> EnumerateSplitAny<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> splitBy) where T : IEquatable<T>
{
return new SpanSplitEnumerator(span, splitBy);
return new SpanSplitEnumeratorAny<T>(span, splitBy);
}
public static SpanSplitEnumerator EnumerateSplit(this string str, char splitBy)
public static SpanSplitEnumerator<T> EnumerateSplit<T>(this Span<T> span, T splitBy) where T : IEquatable<T>
{
return new SpanSplitEnumerator(str.AsSpan(), splitBy);
return new SpanSplitEnumerator<T>(span, splitBy);
}
public static SpanSplitEnumeratorAny<T> EnumerateSplitAny<T>(this Span<T> span, ReadOnlySpan<T> splitBy) where T : IEquatable<T>
{
return new SpanSplitEnumeratorAny<T>(span, splitBy);
}
public static SpanSplitEnumerator<char> EnumerateSplit(this string str, char splitBy)
{
return EnumerateSplit(str.AsSpan(), splitBy);
}
public static SpanSplitEnumeratorAny<char> EnumerateSplitAny(this string str, ReadOnlySpan<char> splitBy)
{
return EnumerateSplitAny(str.AsSpan(), splitBy);
}
public static ReverseSpanSplitEnumerator<T> ReverseEnumerateSplit<T>(this ReadOnlySpan<T> span, T splitBy) where T : IEquatable<T>
{
return new ReverseSpanSplitEnumerator<T>(span, splitBy);
}
public static ReverseSpanSplitEnumerator<T> ReverseEnumerateSplit<T>(this Span<T> span, T splitBy) where T : IEquatable<T>
{
return new ReverseSpanSplitEnumerator<T>(span, splitBy);
}
public static ReverseSpanSplitEnumerator<char> ReverseEnumerateSplit(this string str, char splitBy)
{
return ReverseEnumerateSplit(str.AsSpan(), splitBy);
}
}
public ref struct ReverseSpanSplitEnumerator<T> where T : IEquatable<T>?
{
private ReadOnlySpan<T> _remaining;
private ReadOnlySpan<T> _current;
private bool _isEnumeratorActive;
private T _splitBy;
internal ReverseSpanSplitEnumerator(ReadOnlySpan<T> buffer, T splitBy)
{
_remaining = buffer;
_current = default;
_isEnumeratorActive = true;
_splitBy = splitBy;
}
/// <summary>
/// Gets the line at the current position of the enumerator.
/// </summary>
public readonly ReadOnlySpan<T> Current => _current;
/// <summary>
/// Returns this instance as an enumerator.
/// </summary>
public readonly ReverseSpanSplitEnumerator<T> GetEnumerator() => this;
/// <summary>
/// Advances the enumerator to the next line of the span.
/// </summary>
/// <returns>
/// True if the enumerator successfully advanced to the next line; false if
/// the enumerator has advanced past the end of the span.
/// </returns>
public bool MoveNext()
{
if (!_isEnumeratorActive)
{
return false; // EOF previously reached or enumerator was never initialized
}
ReadOnlySpan<T> remaining = _remaining;
int idx = remaining.LastIndexOf(_splitBy);
if ((uint)idx < (uint)remaining.Length)
{
_current = remaining.Slice(idx + 1);
_remaining = remaining.Slice(0, idx);
}
else
{
// We've reached EOF, but we still need to return 'true' for this final
// iteration so that the caller can query the Current property once more.
_current = remaining;
_remaining = default;
_isEnumeratorActive = false;
}
return true;
}
}
}

Some files were not shown because too many files have changed in this diff Show More