mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-16 20:17:30 +08:00
Another big commit, updated to .NET 8.0 and a lot more refactoring and optimizations
This commit is contained in:
parent
f21d83cf40
commit
528ab1a98c
76
.editorconfig
Normal file
76
.editorconfig
Normal 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
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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\" />
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -29,9 +29,6 @@ namespace CodeWalker.GameFiles
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
|
||||
var root = PsoTypes.GetRootEntry(Pso);
|
||||
if (root != null)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
112
CodeWalker.Core/GameFiles/FileTypes/PedShopMetaFile.cs
Normal file
112
CodeWalker.Core/GameFiles/FileTypes/PedShopMetaFile.cs
Normal 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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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}";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
||||
|
@ -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}";
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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
@ -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
|
||||
{
|
||||
|
@ -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
@ -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
1453
CodeWalker.Core/GameFiles/MetaTypes/PsoTypes/GetEnumInfo.cs
Normal file
1453
CodeWalker.Core/GameFiles/MetaTypes/PsoTypes/GetEnumInfo.cs
Normal file
File diff suppressed because it is too large
Load Diff
14000
CodeWalker.Core/GameFiles/MetaTypes/PsoTypes/GetStructureInfo.cs
Normal file
14000
CodeWalker.Core/GameFiles/MetaTypes/PsoTypes/GetStructureInfo.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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})";
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
158
CodeWalker.Core/GameFiles/Resources/PedsFiles.cs
Normal file
158
CodeWalker.Core/GameFiles/Resources/PedsFiles.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
@ -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);
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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}";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
@ -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))
|
||||
|
@ -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));
|
||||
{
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
8
CodeWalker.Core/GlobalSuppressions.cs
Normal file
8
CodeWalker.Core/GlobalSuppressions.cs
Normal 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)")]
|
@ -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>
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
48
CodeWalker.Core/Utils/FileUtils.cs
Normal file
48
CodeWalker.Core/Utils/FileUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
59
CodeWalker.Core/Utils/SharedObjectPool.cs
Normal file
59
CodeWalker.Core/Utils/SharedObjectPool.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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
Loading…
Reference in New Issue
Block a user