Many optimizations and bug fixes

This commit is contained in:
Niek Schoemaker 2023-11-11 18:59:17 +01:00
parent aed7a1e051
commit ba72dadd16
No known key found for this signature in database
GPG Key ID: BDF9404CFECB0006
201 changed files with 8938 additions and 2799 deletions

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -0,0 +1,200 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs;
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Benchmarks
{
[MemoryDiagnoser]
[Config(typeof(JitsConfig))]
public class Benchmarks
{
private class JitsConfig : ManualConfig
{
public JitsConfig()
{
AddJob(Job.Default.WithJit(Jit.RyuJit).WithPlatform(Platform.X64));
}
}
public static string markup = @"<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>
</InitDatas>
<txdRelationships>
<Item>
<parent>vehicles_banshee_interior</parent>
<child>brabusgt600</child>
</Item>
</txdRelationships>
</CVehicleModelInfo__InitDataList>";
private byte[] data;
private RpfFileEntry fileEntry;
[GlobalSetup]
public void Setup()
{
data = Encoding.UTF8.GetBytes(markup);
fileEntry = RpfFile.CreateFileEntry("kaas.meta", "saak.meta", ref data);
}
[Benchmark(Baseline = true)]
public void RunLoad()
{
var vehiclesFileExpected = new VehiclesFile();
vehiclesFileExpected.LoadOld(data, fileEntry);
}
[Benchmark]
public void RunLoadNew()
{
var vehiclesFile = new VehiclesFile();
vehiclesFile.Load(data, fileEntry);
}
}
}

View File

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\build\Microsoft.Diagnostics.Tracing.TraceEvent.props" Condition="Exists('..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\build\Microsoft.Diagnostics.Tracing.TraceEvent.props')" />
<Import Project="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.props" Condition="Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>CodeWalker.Benchmarks</RootNamespace>
<AssemblyName>CodeWalker.Benchmarks</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="BenchmarkDotNet, Version=0.13.10.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.0.13.10\lib\netstandard2.0\BenchmarkDotNet.dll</HintPath>
</Reference>
<Reference Include="BenchmarkDotNet.Annotations, Version=0.13.10.0, Culture=neutral, PublicKeyToken=aa0ca2f9092cefc4, processorArchitecture=MSIL">
<HintPath>..\packages\BenchmarkDotNet.Annotations.0.13.10\lib\netstandard2.0\BenchmarkDotNet.Annotations.dll</HintPath>
</Reference>
<Reference Include="CommandLine, Version=2.9.1.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL">
<HintPath>..\packages\CommandLineParser.2.9.1\lib\net461\CommandLine.dll</HintPath>
</Reference>
<Reference Include="Dia2Lib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Dia2Lib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Gee.External.Capstone, Version=2.3.0.0, Culture=neutral, PublicKeyToken=b117d7a2dc33ffa6, processorArchitecture=MSIL">
<HintPath>..\packages\Gee.External.Capstone.2.3.0\lib\netstandard2.0\Gee.External.Capstone.dll</HintPath>
</Reference>
<Reference Include="Iced, Version=1.17.0.0, Culture=neutral, PublicKeyToken=5baba79f4264913b, processorArchitecture=MSIL">
<HintPath>..\packages\Iced.1.17.0\lib\net45\Iced.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeAnalysis.Common.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeAnalysis.CSharp, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeAnalysis.CSharp.4.1.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Diagnostics.FastSerialization, Version=3.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Microsoft.Diagnostics.FastSerialization.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Diagnostics.NETCore.Client, Version=0.2.5.1802, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.NETCore.Client.0.2.251802\lib\netstandard2.0\Microsoft.Diagnostics.NETCore.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Diagnostics.Runtime, Version=2.2.6.32302, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Runtime.2.2.332302\lib\netstandard2.0\Microsoft.Diagnostics.Runtime.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Diagnostics.Tracing.TraceEvent, Version=3.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\Microsoft.Diagnostics.Tracing.TraceEvent.dll</HintPath>
</Reference>
<Reference Include="Microsoft.DotNet.PlatformAbstractions, Version=3.1.6.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.DotNet.PlatformAbstractions.3.1.6\lib\net45\Microsoft.DotNet.PlatformAbstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Options, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Options.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Options.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Primitives, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Primitives.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Registry, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="OSExtensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\OSExtensions.dll</HintPath>
</Reference>
<Reference Include="Perfolizer, Version=0.2.1.0, Culture=neutral, PublicKeyToken=e864f2ec9c0b6d4c, processorArchitecture=MSIL">
<HintPath>..\packages\Perfolizer.0.2.1\lib\netstandard2.0\Perfolizer.dll</HintPath>
</Reference>
<Reference Include="SharpDX, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.4.2.0\lib\net45\SharpDX.dll</HintPath>
</Reference>
<Reference Include="SharpDX.Mathematics, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
<HintPath>..\packages\SharpDX.Mathematics.4.2.0\lib\net45\SharpDX.Mathematics.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Collections.Immutable.5.0.0\lib\net461\System.Collections.Immutable.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Reflection.Metadata, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reflection.Metadata.5.0.0\lib\net461\System.Reflection.Metadata.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.InteropServices.RuntimeInformation.4.0.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>True</Private>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.AccessControl, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encoding.CodePages, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="TraceReloggerLib, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\lib\net462\TraceReloggerLib.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Benchmarks.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj">
<Project>{FF6B9F41-14BE-474E-9ED0-549C3BEB7E00}</Project>
<Name>CodeWalker.Core</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.targets'))" />
<Error Condition="!Exists('..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\build\Microsoft.Diagnostics.Tracing.TraceEvent.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Diagnostics.Tracing.TraceEvent.3.0.2\build\Microsoft.Diagnostics.Tracing.TraceEvent.props'))" />
</Target>
<Import Project="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.targets" Condition="Exists('..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\build\Microsoft.CodeAnalysis.Analyzers.targets')" />
</Project>

View File

@ -0,0 +1,17 @@
using BenchmarkDotNet.Running;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Benchmarks
{
internal class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<Benchmarks>();
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CodeWalker.Benchmarks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CodeWalker.Benchmarks")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("edda8a8e-5333-4e28-8221-a31e3b70eb7a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BenchmarkDotNet" version="0.13.10" targetFramework="net48" />
<package id="BenchmarkDotNet.Annotations" version="0.13.10" targetFramework="net48" />
<package id="CommandLineParser" version="2.9.1" targetFramework="net48" />
<package id="Gee.External.Capstone" version="2.3.0" targetFramework="net48" />
<package id="Iced" version="1.17.0" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.0" targetFramework="net48" />
<package id="Microsoft.CodeAnalysis.Analyzers" version="3.3.3" targetFramework="net48" developmentDependency="true" />
<package id="Microsoft.CodeAnalysis.Common" version="4.1.0" targetFramework="net48" />
<package id="Microsoft.CodeAnalysis.CSharp" version="4.1.0" targetFramework="net48" />
<package id="Microsoft.Diagnostics.NETCore.Client" version="0.2.251802" targetFramework="net48" />
<package id="Microsoft.Diagnostics.Runtime" version="2.2.332302" targetFramework="net48" />
<package id="Microsoft.Diagnostics.Tracing.TraceEvent" version="3.0.2" targetFramework="net48" />
<package id="Microsoft.DotNet.PlatformAbstractions" version="3.1.6" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.Abstractions" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Configuration.Binder" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Options" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Extensions.Primitives" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.Win32.Registry" version="5.0.0" targetFramework="net48" />
<package id="Perfolizer" version="0.2.1" targetFramework="net48" />
<package id="SharpDX" version="4.2.0" targetFramework="net48" />
<package id="SharpDX.Mathematics" version="4.2.0" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Collections.Immutable" version="5.0.0" targetFramework="net48" />
<package id="System.Management" version="5.0.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Reflection.Emit" version="4.7.0" targetFramework="net48" />
<package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net48" />
<package id="System.Reflection.Metadata" version="5.0.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net48" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.0.0" targetFramework="net48" />
<package id="System.Security.AccessControl" version="5.0.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" />
<package id="System.Text.Encoding.CodePages" version="4.5.1" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
</packages>

View File

@ -7,11 +7,16 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" />
<PackageReference Include="directxtex_desktop_win10" Version="2023.3.30.1" /> <PackageReference Include="directxtex_desktop_win10" Version="2023.3.30.1" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -70,10 +70,10 @@ namespace CodeWalker.GameFiles
if (!string.IsNullOrEmpty(Name)) if (!string.IsNullOrEmpty(Name))
{ {
var nl = Name.ToLowerInvariant(); var nl = Name;
var fn = Path.GetFileNameWithoutExtension(nl); var fn = Path.GetFileNameWithoutExtension(nl);
JenkIndex.Ensure(fn + "_left"); JenkIndex.EnsureLower(fn + "_left");
JenkIndex.Ensure(fn + "_right"); JenkIndex.EnsureLower(fn + "_right");
} }
if ((data == null) || (data.Length < 8)) if ((data == null) || (data.Length < 8))

View File

@ -40,7 +40,7 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry; RpfFileEntry = entry;
Name = entry.Name; Name = entry.Name;
if (!entry.NameLower.EndsWith("_hd.dat")) if (!entry.Name.EndsWith("_hd.dat", StringComparison.OrdinalIgnoreCase))
{ {
HD = false; HD = false;
GridSize = 16; GridSize = 16;

View File

@ -1186,7 +1186,7 @@ namespace CodeWalker.GameFiles
SlotGS = br.ReadUInt16(); //6, 5 SlotGS = br.ReadUInt16(); //6, 5
SlotHS = br.ReadUInt16(); //6, 5 SlotHS = br.ReadUInt16(); //6, 5
Name = FxcFile.ReadString(br); // <fxc name> _locals //"rage_matrices", "misc_globals", "lighting_globals", "more_stuff" Name = FxcFile.ReadString(br); // <fxc name> _locals //"rage_matrices", "misc_globals", "lighting_globals", "more_stuff"
JenkIndex.Ensure(Name?.ToLowerInvariant()); //why not :P JenkIndex.EnsureLower(Name); //why not :P
} }
public void Write(BinaryWriter bw) public void Write(BinaryWriter bw)
{ {

View File

@ -36,7 +36,7 @@ namespace CodeWalker.GameFiles
FilePath = Name; FilePath = Name;
if (entry.NameLower.EndsWith(".ymt")) if (entry.Name.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
{ {
MemoryStream ms = new MemoryStream(data); MemoryStream ms = new MemoryStream(data);
if (RbfFile.IsRBF(ms)) if (RbfFile.IsRBF(ms))
@ -57,7 +57,7 @@ namespace CodeWalker.GameFiles
//not an RBF file... //not an RBF file...
} }
} }
else if (entry.NameLower.EndsWith(".meta")) else if (entry.Name.EndsWith(".meta", StringComparison.OrdinalIgnoreCase))
{ {
//required for update\x64\dlcpacks\mpheist\dlc.rpf\common\data\gtxd.meta and update\x64\dlcpacks\mpluxe\dlc.rpf\common\data\gtxd.meta //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); string xml = TextUtil.GetUTF8Text(data);

View File

@ -6,6 +6,7 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
@ -172,7 +173,7 @@ namespace CodeWalker.GameFiles
public static class GlobalText public static class GlobalText
{ {
public static ConcurrentDictionary<uint, string> Index = new (); public static ConcurrentDictionary<uint, string> Index = new (4, 24000);
private static object syncRoot = new object(); private static object syncRoot = new object();
public static volatile bool FullIndexBuilt = false; public static volatile bool FullIndexBuilt = false;

View File

@ -798,9 +798,13 @@ namespace CodeWalker.GameFiles
{ {
} }
protected override void WriteToStream(byte[] value, bool ignoreEndianess = false) protected override void WriteToStream(byte[] value, bool ignoreEndianess = false, int count = -1, int offset = 0)
{ {
position += value.Length; if (count == -1)
{
count = value.Length;
}
position += count;
length = Math.Max(length, position); length = Math.Max(length, position);
} }
} }

View File

@ -87,7 +87,6 @@ namespace CodeWalker.GameFiles
} }
private void NonMetaLoad(byte[] data) private void NonMetaLoad(byte[] data)
{ {
//non meta not supported yet! but see what's in there... //non meta not supported yet! but see what's in there...
@ -103,22 +102,6 @@ namespace CodeWalker.GameFiles
Pso.Load(ms); Pso.Load(ms);
LoadPso(); LoadPso();
} }
else }
{
}
}
} }
} }

View File

@ -9,6 +9,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute; using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter; using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml.Linq;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
@ -34,7 +35,7 @@ namespace CodeWalker.GameFiles
//can be PSO .ymt or XML .meta //can be PSO .ymt or XML .meta
MemoryStream ms = new MemoryStream(data); using MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms)) if (PsoFile.IsPSO(ms))
{ {
Pso = new PsoFile(); Pso = new PsoFile();
@ -46,26 +47,28 @@ namespace CodeWalker.GameFiles
Xml = TextUtil.GetUTF8Text(data); Xml = TextUtil.GetUTF8Text(data);
} }
XmlDocument xdoc = new XmlDocument(); using var textReader = new StringReader(Xml);
if (!string.IsNullOrEmpty(Xml))
{ //XmlDocument xdoc = new XmlDocument();
try //if (!string.IsNullOrEmpty(Xml))
{ //{
xdoc.LoadXml(Xml); // try
} // {
catch (Exception ex) // xdoc.LoadXml(Xml);
{ // }
var msg = ex.Message; // catch (Exception ex)
} // {
} // var msg = ex.Message;
else // }
{ } //}
using var xmlReader = XmlReader.Create(textReader);
if (xdoc.DocumentElement != null) //if (xdoc.DocumentElement != null)
{ //{
InitDataList = new CPedModelInfo__InitDataList(xdoc.DocumentElement); InitDataList = new CPedModelInfo__InitDataList(xmlReader);
} //}
@ -84,6 +87,87 @@ namespace CodeWalker.GameFiles
public CTxdRelationship[] txdRelationships { get; set; } public CTxdRelationship[] txdRelationships { get; set; }
public CMultiTxdRelationship[] multiTxdRelationships { get; set; } public CMultiTxdRelationship[] multiTxdRelationships { get; set; }
public CPedModelInfo__InitDataList(XmlReader reader)
{
while (reader.Read())
{
reader.MoveToContent();
//var _ = xmlReader.Name switch
//{
// "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
// "InitDatas" => LoadInitDatas(xmlReader),
// "txdRelationships" => LoadTxdRelationships(xmlReader),
// _ => throw new Exception()
//};
switch (reader.Name)
{
case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
residentTxd = Xml.GetChildInnerText(reader, "residentTxd");
break;
case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
reader.ReadStartElement();
var initDatasList = new List<CPedModelInfo__InitData>();
while (reader.IsItemElement())
{
initDatasList.Add(new CPedModelInfo__InitData(reader));
}
if (initDatasList.Count > 0)
{
InitDatas = initDatasList.ToArray();
}
reader.ReadEndElement();
break;
case string Name when Name.Equals("txdRelationships", StringComparison.OrdinalIgnoreCase):
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
reader.ReadStartElement();
var txdRelationshipsList = new List<CTxdRelationship>();
while (reader.IsItemElement())
{
txdRelationshipsList.Add(new CTxdRelationship(reader));
}
reader.ReadEndElement();
if (txdRelationshipsList.Count > 0)
{
txdRelationships = txdRelationshipsList.ToArray();
}
break;
case string Name when Name.Equals("multiTxdRelationships", StringComparison.OrdinalIgnoreCase):
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
reader.ReadStartElement();
var multiTxdList = new List<CMultiTxdRelationship>();
while (reader.IsItemElement())
{
multiTxdList.Add(new CMultiTxdRelationship(reader));
}
reader.ReadEndElement();
if (multiTxdList.Count > 0)
{
multiTxdRelationships = multiTxdList.ToArray();
}
break;
default:
break;
}
}
}
public CPedModelInfo__InitDataList(XmlNode node) public CPedModelInfo__InitDataList(XmlNode node)
{ {
XmlNodeList items; XmlNodeList items;
@ -126,7 +210,6 @@ namespace CodeWalker.GameFiles
multiTxdRelationships[i] = new CMultiTxdRelationship(items[i]); multiTxdRelationships[i] = new CMultiTxdRelationship(items[i]);
} }
} }
} }
} }
@ -204,6 +287,249 @@ namespace CodeWalker.GameFiles
public float DefaultRemoveRangeMultiplier { get; set; } public float DefaultRemoveRangeMultiplier { get; set; }
public bool AllowCloseSpawning { get; set; } public bool AllowCloseSpawning { get; set; }
public CPedModelInfo__InitData(XmlReader reader)
{
reader.ReadStartElement("Item");
while (reader.Name != "Item" && reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Item")
{
reader.ReadEndElement();
return;
}
while (reader.MoveToContent() != XmlNodeType.Element && reader.Read())
{
}
switch (reader.Name)
{
case "Name":
Name = Xml.GetChildInnerText(reader, "Name");
break;
case "PropsName":
PropsName = Xml.GetChildInnerText(reader, "PropsName");
break;
case "ClipDictionaryName":
ClipDictionaryName = Xml.GetChildInnerText(reader, "ClipDictionaryName");
break;
case "BlendShapeFileName":
BlendShapeFileName = Xml.GetChildInnerText(reader, "BlendShapeFileName");
break;
case "ExpressionSetName":
ExpressionSetName = Xml.GetChildInnerText(reader, "ExpressionSetName");
break;
case "ExpressionDictionaryName":
ExpressionDictionaryName = Xml.GetChildInnerText(reader, "ExpressionDictionaryName");
break;
case "ExpressionName":
ExpressionName = Xml.GetChildInnerText(reader, "ExpressionName");
break;
case "Pedtype":
Pedtype = Xml.GetChildInnerText(reader, "Pedtype");
break;
case "MovementClipSet":
MovementClipSet = Xml.GetChildInnerText(reader, "MovementClipSet");
break;
case "MovementClipSets":
var clipSetsList = new List<string>();
foreach(var item in Xml.IterateItems(reader, "MovementClipSets"))
{
clipSetsList.Add(item.Value);
}
MovementClipSets = clipSetsList.ToArray();
break;
case "StrafeClipSet":
StrafeClipSet = Xml.GetChildInnerText(reader, "StrafeClipSet");
break;
case "MovementToStrafeClipSet":
MovementToStrafeClipSet = Xml.GetChildInnerText(reader, "MovementToStrafeClipSet");
break;
case "InjuredStrafeClipSet":
InjuredStrafeClipSet = Xml.GetChildInnerText(reader, "InjuredStrafeClipSet");
break;
case "FullBodyDamageClipSet":
FullBodyDamageClipSet = Xml.GetChildInnerText(reader, "FullBodyDamageClipSet");
break;
case "AdditiveDamageClipSet":
AdditiveDamageClipSet = Xml.GetChildInnerText(reader, "AdditiveDamageClipSet");
break;
case "DefaultGestureClipSet":
DefaultGestureClipSet = Xml.GetChildInnerText(reader, "DefaultGestureClipSet");
break;
case "FacialClipsetGroupName":
FacialClipsetGroupName = Xml.GetChildInnerText(reader, "FacialClipsetGroupName");
break;
case "DefaultVisemeClipSet":
DefaultVisemeClipSet = Xml.GetChildInnerText(reader, "DefaultVisemeClipSet");
break;
case "SidestepClipSet":
SidestepClipSet = Xml.GetChildInnerText(reader, "SidestepClipSet");
break;
case "PoseMatcherName":
PoseMatcherName = Xml.GetChildInnerText(reader, "PoseMatcherName");
break;
case "PoseMatcherProneName":
PoseMatcherProneName = Xml.GetChildInnerText(reader, "PoseMatcherProneName");
break;
case "GetupSetHash":
GetupSetHash = Xml.GetChildInnerText(reader, "GetupSetHash");
break;
case "CreatureMetadataName":
CreatureMetadataName = Xml.GetChildInnerText(reader, "CreatureMetadataName");
break;
case "DecisionMakerName":
DecisionMakerName = Xml.GetChildInnerText(reader, "DecisionMakerName");
break;
case "MotionTaskDataSetName":
MotionTaskDataSetName = Xml.GetChildInnerText(reader, "MotionTaskDataSetName");
break;
case "DefaultTaskDataSetName":
DefaultTaskDataSetName = Xml.GetChildInnerText(reader, "DefaultTaskDataSetName");
break;
case "PedCapsuleName":
PedCapsuleName = Xml.GetChildInnerText(reader, "PedCapsuleName");
break;
case "PedLayoutName":
PedLayoutName = Xml.GetChildInnerText(reader, "PedLayoutName");
break;
case "PedComponentSetName":
PedComponentSetName = Xml.GetChildInnerText(reader, "PedComponentSetName");
break;
case "PedComponentClothName":
PedComponentClothName = Xml.GetChildInnerText(reader, "PedComponentClothName");
break;
case "PedIKSettingsName":
PedIKSettingsName = Xml.GetChildInnerText(reader, "PedIKSettingsName");
break;
case "TaskDataName":
TaskDataName = Xml.GetChildInnerText(reader, "TaskDataName");
break;
case "IsStreamedGfx":
IsStreamedGfx = Xml.GetChildBoolAttribute(reader, "IsStreamedGfx", "value");
break;
case "AmbulanceShouldRespondTo":
AmbulanceShouldRespondTo = Xml.GetChildBoolAttribute(reader, "AmbulanceShouldRespondTo", "value");
break;
case "CanRideBikeWithNoHelmet":
CanRideBikeWithNoHelmet = Xml.GetChildBoolAttribute(reader, "CanRideBikeWithNoHelmet", "value");
break;
case "CanSpawnInCar":
CanSpawnInCar = Xml.GetChildBoolAttribute(reader, "CanSpawnInCar", "value");
break;
case "IsHeadBlendPed":
IsHeadBlendPed = Xml.GetChildBoolAttribute(reader, "IsHeadBlendPed", "value");
break;
case "bOnlyBulkyItemVariations":
bOnlyBulkyItemVariations = Xml.GetChildBoolAttribute(reader, "bOnlyBulkyItemVariations", "value");
break;
case "RelationshipGroup":
RelationshipGroup = Xml.GetChildInnerText(reader, "RelationshipGroup");
break;
case "NavCapabilitiesName":
NavCapabilitiesName = Xml.GetChildInnerText(reader, "NavCapabilitiesName");
break;
case "PerceptionInfo":
PerceptionInfo = Xml.GetChildInnerText(reader, "PerceptionInfo");
break;
case "DefaultBrawlingStyle":
DefaultBrawlingStyle = Xml.GetChildInnerText(reader, "DefaultBrawlingStyle");
break;
case "DefaultUnarmedWeapon":
DefaultUnarmedWeapon = Xml.GetChildInnerText(reader, "DefaultUnarmedWeapon");
break;
case "Personality":
Personality = Xml.GetChildInnerText(reader, "Personality");
break;
case "CombatInfo":
CombatInfo = Xml.GetChildInnerText(reader, "CombatInfo");
break;
case "VfxInfoName":
VfxInfoName = Xml.GetChildInnerText(reader, "VfxInfoName");
break;
case "AmbientClipsForFlee":
AmbientClipsForFlee = Xml.GetChildInnerText(reader, "AmbientClipsForFlee");
break;
case "Radio1":
Radio1 = Xml.GetChildInnerText(reader, "Radio1"); // MetaName.ePedRadioGenre
break;
case "Radio2":
Radio2 = Xml.GetChildInnerText(reader, "Radio2"); // MetaName.ePedRadioGenre
break;
case "FUpOffset":
FUpOffset = Xml.GetChildFloatAttribute(reader, "FUpOffset", "value");
break;
case "RUpOffset":
RUpOffset = Xml.GetChildFloatAttribute(reader, "RUpOffset", "value");
break;
case "FFrontOffset":
FFrontOffset = Xml.GetChildFloatAttribute(reader, "FFrontOffset", "value");
break;
case "RFrontOffset":
RFrontOffset = Xml.GetChildFloatAttribute(reader, "RFrontOffset", "value");
break;
case "MinActivationImpulse":
MinActivationImpulse = Xml.GetChildFloatAttribute(reader, "MinActivationImpulse", "value");
break;
case "Stubble":
Stubble = Xml.GetChildFloatAttribute(reader, "Stubble", "value");
break;
case "HDDist":
HDDist = Xml.GetChildFloatAttribute(reader, "HDDist", "value");
break;
case "TargetingThreatModifier":
TargetingThreatModifier = Xml.GetChildFloatAttribute(reader, "TargetingThreatModifier", "value");
break;
case "KilledPerceptionRangeModifer":
KilledPerceptionRangeModifer = Xml.GetChildFloatAttribute(reader, "KilledPerceptionRangeModifer", "value");
break;
case "Sexiness":
Sexiness = Xml.GetChildInnerText(reader, "Sexiness"); // MetaTypeName.ARRAYINFO MetaName.eSexinessFlags
break;
case "Age":
Age = (byte)Xml.GetChildUIntAttribute(reader, "Age", "value");
break;
case "MaxPassengersInCar":
MaxPassengersInCar = (byte)Xml.GetChildUIntAttribute(reader, "MaxPassengersInCar", "value");
break;
case "ExternallyDrivenDOFs":
ExternallyDrivenDOFs = Xml.GetChildInnerText(reader, "ExternallyDrivenDOFs"); // MetaTypeName.ARRAYINFO MetaName.eExternallyDrivenDOFs
break;
case "PedVoiceGroup":
PedVoiceGroup = Xml.GetChildInnerText(reader, "PedVoiceGroup");
break;
case "AnimalAudioObject":
AnimalAudioObject = Xml.GetChildInnerText(reader, "AnimalAudioObject");
break;
case "AbilityType":
AbilityType = Xml.GetChildInnerText(reader, "AbilityType"); // MetaName.SpecialAbilityType
break;
case "ThermalBehaviour":
ThermalBehaviour = Xml.GetChildInnerText(reader, "ThermalBehaviour"); // MetaName.ThermalBehaviour
break;
case "SuperlodType":
SuperlodType = Xml.GetChildInnerText(reader, "SuperlodType"); // MetaName.eSuperlodType
break;
case "ScenarioPopStreamingSlot":
ScenarioPopStreamingSlot = Xml.GetChildInnerText(reader, "ScenarioPopStreamingSlot"); // MetaName.eScenarioPopStreamingSlot
break;
case "DefaultSpawningPreference":
DefaultSpawningPreference = Xml.GetChildInnerText(reader, "DefaultSpawningPreference"); // MetaName.DefaultSpawnPreference
break;
case "DefaultRemoveRangeMultiplier":
DefaultRemoveRangeMultiplier = Xml.GetChildFloatAttribute(reader, "DefaultRemoveRangeMultiplier", "value");
break;
case "AllowCloseSpawning":
AllowCloseSpawning = Xml.GetChildBoolAttribute(reader, "AllowCloseSpawning", "value");
break;
default:
reader.Skip();
break;
}
}
}
public CPedModelInfo__InitData(XmlNode node) public CPedModelInfo__InitData(XmlNode node)
{ {
@ -300,6 +626,25 @@ namespace CodeWalker.GameFiles
public string parent { get; set; } public string parent { get; set; }
public string child { get; set; } public string child { get; set; }
public CTxdRelationship(XmlReader reader)
{
if (reader.Name == "parent")
{
parent = Xml.GetChildInnerText(reader, "parent");
} else if (reader.Name == "child")
{
child = Xml.GetChildInnerText(reader, "child");
}
if (reader.Name == "parent")
{
parent = Xml.GetChildInnerText(reader, "parent");
}
else if (reader.Name == "child")
{
child = Xml.GetChildInnerText(reader, "child");
}
}
public CTxdRelationship(XmlNode node) public CTxdRelationship(XmlNode node)
{ {
parent = Xml.GetChildInnerText(node, "parent"); parent = Xml.GetChildInnerText(node, "parent");
@ -331,6 +676,34 @@ namespace CodeWalker.GameFiles
} }
} }
public CMultiTxdRelationship(XmlReader reader)
{
reader.ReadStartElement("Item");
while (reader.MoveToContent() == XmlNodeType.Element && reader.Name != "Item")
{
switch (reader.Name)
{
case "children":
var childrenList = new List<string>();
foreach (var item in Xml.IterateItems(reader, "children"))
{
childrenList.Add(item.Value);
}
if (childrenList.Count > 0)
{
children = childrenList.ToArray();
}
break;
case "parent":
parent = Xml.GetChildInnerText(reader, "parent");
break;
default:
throw new InvalidOperationException($"Found invalid XML Element \"{reader.Name}\" of type \"{reader.NodeType}\"");
}
}
reader.ReadEndElement();
}
public override string ToString() public override string ToString()
{ {
return parent + ": " + (children?.Length ?? 0).ToString() + " children"; return parent + ": " + (children?.Length ?? 0).ToString() + " children";

View File

@ -105,7 +105,26 @@ namespace CodeWalker.GameFiles
RpfFileEntry = entry; RpfFileEntry = entry;
} }
public void Load(byte[] data, RpfFileEntry entry) public unsafe string ReadString(BinaryReader br, int length, bool ignoreNullTerminator = false)
{
var bytes = stackalloc char[length];
var currentLength = 0;
while (currentLength < length)
{
var c = (char)br.ReadByte();
if (c != 0)
{
bytes[currentLength] = c;
currentLength++;
}
else if (!ignoreNullTerminator)
break;
}
return new string(bytes, 0, currentLength);
}
public unsafe void Load(byte[] data, RpfFileEntry entry)
{ {
RawFileData = data; RawFileData = data;
if (entry != null) if (entry != null)
@ -114,8 +133,8 @@ namespace CodeWalker.GameFiles
Name = entry.Name; Name = entry.Name;
} }
MemoryStream ms = new MemoryStream(data); using MemoryStream ms = new MemoryStream(data);
BinaryReader br = new BinaryReader(ms); using BinaryReader br = new BinaryReader(ms);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
RelType = (RelDatFileType)br.ReadUInt32(); //type RelType = (RelDatFileType)br.ReadUInt32(); //type
@ -134,19 +153,25 @@ namespace CodeWalker.GameFiles
} }
NameTableOffsets = ntoffsets; NameTableOffsets = ntoffsets;
string[] names = new string[NameTableCount]; string[] names = new string[NameTableCount];
var remainingLength = (int)NameTableLength;
for (uint i = 0; i < NameTableCount; i++) for (uint i = 0; i < NameTableCount; i++)
{ {
sb.Clear(); int length = 0;
while (true) if (i < NameTableCount - 1)
{ {
char c = (char)br.ReadByte(); length = (int)(NameTableOffsets[i + 1] - NameTableOffsets[i]);
if (c != 0) sb.Append(c);
else break;
} }
names[i] = sb.ToString(); else
{
length = remainingLength;
}
names[i] = ReadString(br, length);
//JenkIndex.Ensure(names[i]); //really need both here..? //JenkIndex.Ensure(names[i]); //really need both here..?
JenkIndex.Ensure(names[i].ToLowerInvariant()); JenkIndex.EnsureLower(names[i]);
remainingLength -= length;
} }
NameTable = names; NameTable = names;
} }
@ -164,18 +189,13 @@ namespace CodeWalker.GameFiles
for (uint i = 0; i < IndexCount; i++) for (uint i = 0; i < IndexCount; i++)
{ {
byte sl = br.ReadByte(); byte sl = br.ReadByte();
sb.Clear(); var str = ReadString(br, (int)sl, true);
for (int j = 0; j < sl; j++)
{
char c = (char)br.ReadByte();
if (c != 0) sb.Append(c);
}
RelIndexString ristr = new RelIndexString(); RelIndexString ristr = new RelIndexString();
ristr.Name = sb.ToString(); ristr.Name = str;
ristr.Offset = br.ReadUInt32(); ristr.Offset = br.ReadUInt32();
ristr.Length = br.ReadUInt32(); ristr.Length = br.ReadUInt32();
indexstrs[i] = ristr; indexstrs[i] = ristr;
JenkIndex.Ensure(ristr.Name.ToLowerInvariant()); JenkIndex.EnsureLower(ristr.Name);
} }
IndexStrings = indexstrs; IndexStrings = indexstrs;
} }
@ -235,9 +255,6 @@ namespace CodeWalker.GameFiles
{ } { }
//EOF! //EOF!
br.Dispose();
ms.Dispose();
ParseDataBlock(); ParseDataBlock();
@ -253,8 +270,8 @@ namespace CodeWalker.GameFiles
MemoryStream ms = new MemoryStream(DataBlock); using MemoryStream ms = new MemoryStream(DataBlock);
BinaryReader br = new BinaryReader(ms); using BinaryReader br = new BinaryReader(ms);
DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version? flags? DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version? flags?
#region DataUnkVal unk values test #region DataUnkVal unk values test
@ -315,12 +332,6 @@ namespace CodeWalker.GameFiles
RelDatasSorted = reldatas.ToArray(); RelDatasSorted = reldatas.ToArray();
br.Dispose();
ms.Dispose();
RelDataDict.Clear(); RelDataDict.Clear();
foreach (var reldata in RelDatas) foreach (var reldata in RelDatas)
{ {
@ -328,7 +339,7 @@ namespace CodeWalker.GameFiles
{ {
reldata.NameHash = JenkHash.GenHash(reldata.Name); //should this be lower case? reldata.NameHash = JenkHash.GenHash(reldata.Name); //should this be lower case?
JenkIndex.Ensure(reldata.Name); JenkIndex.Ensure(reldata.Name);
JenkIndex.Ensure(reldata.Name.ToLowerInvariant()); //which one to use? JenkIndex.EnsureLower(reldata.Name); //which one to use?
} }
//if (reldata.NameHash == 0) //if (reldata.NameHash == 0)
@ -349,24 +360,10 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < snd.ChildSoundsCount; i++) for (int i = 0; i < snd.ChildSoundsCount; i++)
{ {
var audhash = snd.ChildSoundsHashes[i]; var audhash = snd.ChildSoundsHashes[i];
RelData auddata = null; if (RelDataDict.TryGetValue(audhash, out var auddata))
if (RelDataDict.TryGetValue(audhash, out auddata))
{ {
snd.ChildSounds[i] = auddata; snd.ChildSounds[i] = auddata;
} }
else
{ }
}
}
if (snd.AudioContainers != null)
{
foreach (var cnt in snd.AudioContainers)
{
string cname = JenkIndex.TryGetString(cnt.Hash);
if (!string.IsNullOrEmpty(cname))
{ }
else
{ }
} }
} }
} }
@ -385,8 +382,6 @@ namespace CodeWalker.GameFiles
{ {
speechDict[speechData.DataOffset] = speechData; speechDict[speechData.DataOffset] = speechData;
} }
else
{ }
speechData.Type = Dat4SpeechType.ByteArray; speechData.Type = Dat4SpeechType.ByteArray;
speechData.TypeID = 0; //will be set again after this speechData.TypeID = 0; //will be set again after this
@ -397,32 +392,24 @@ namespace CodeWalker.GameFiles
var hashOffset = HashTableOffsets[i]; var hashOffset = HashTableOffsets[i];
var hash = HashTable[i]; var hash = HashTable[i];
var itemOffset = hashOffset - 8; var itemOffset = hashOffset - 8;
Dat4SpeechData speechData = null; if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
speechDict.TryGetValue(itemOffset, out speechData);
if (speechData != null)
{ {
speechData.Type = Dat4SpeechType.Hash; speechData.Type = Dat4SpeechType.Hash;
speechData.TypeID = 4; speechData.TypeID = 4;
speechData.Hash = hash; speechData.Hash = hash;
} }
else
{ }
} }
for (uint i = 0; i < PackTableCount; i++) for (uint i = 0; i < PackTableCount; i++)
{ {
var packOffset = PackTableOffsets[i]; var packOffset = PackTableOffsets[i];
var pack = PackTable[i]; var pack = PackTable[i];
var itemOffset = packOffset - 12; var itemOffset = packOffset - 12;
Dat4SpeechData speechData = null; if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
speechDict.TryGetValue(itemOffset, out speechData);
if (speechData != null)
{ {
speechData.Type = Dat4SpeechType.Container; speechData.Type = Dat4SpeechType.Container;
speechData.TypeID = 8; speechData.TypeID = 8;
speechData.ContainerHash = pack; speechData.ContainerHash = pack;
} }
else
{ }//shouldn't happen!
} }
@ -456,8 +443,7 @@ namespace CodeWalker.GameFiles
d.Data = data; d.Data = data;
using (BinaryReader dbr = new BinaryReader(new MemoryStream(data))) using BinaryReader dbr = new BinaryReader(new MemoryStream(data));
{
d.ReadType(dbr); d.ReadType(dbr);
switch (RelType) switch (RelType)
@ -484,7 +470,6 @@ namespace CodeWalker.GameFiles
return d; //shouldn't get here... return d; //shouldn't get here...
} }
} }
}
@ -25334,7 +25319,7 @@ namespace CodeWalker.GameFiles
{ {
return 0; return 0;
} }
if (str.StartsWith("hash_")) if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{ {
return Convert.ToUInt32(str.Substring(5), 16); return Convert.ToUInt32(str.Substring(5), 16);
} }

View File

@ -26,16 +26,14 @@ namespace CodeWalker.GameFiles
public static bool Ensure(string str) public static bool Ensure(string str)
{ {
uint hash = JenkHash.GenHash(str); uint hash = JenkHash.GenHash(str);
if (hash == 0) return true; return Ensure(str, hash);
lock (syncRoot)
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
} }
}
return true; public static bool EnsureLower(string str)
{
uint hash = JenkHash.GenHashLower(str);
return Ensure(str, hash);
} }
public static bool Ensure(string str, uint hash) public static bool Ensure(string str, uint hash)

View File

@ -1,10 +1,15 @@
using SharpDX; using CodeWalker.World;
using SharpDX;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -28,14 +33,14 @@ namespace CodeWalker.GameFiles
public void Load(byte[] data, RpfFileEntry entry) public void LoadOld(byte[] data, RpfFileEntry entry)
{ {
RpfFileEntry = entry; RpfFileEntry = entry;
Name = entry.Name; Name = entry.Name;
FilePath = Name; FilePath = Name;
if (entry.NameLower.EndsWith(".meta")) if (entry.IsExtension(".meta"))
{ {
string xml = TextUtil.GetUTF8Text(data); string xml = TextUtil.GetUTF8Text(data);
@ -53,12 +58,64 @@ namespace CodeWalker.GameFiles
} }
} }
public void Load(byte[] data, RpfFileEntry entry)
{
RpfFileEntry = entry;
Name = entry.Name;
FilePath = Name;
if (entry.IsExtension(".meta"))
{
using var textReader = new StreamReader(new MemoryStream(data), Encoding.UTF8);
using var xmlReader = XmlReader.Create(textReader);
while (xmlReader.Read())
{
xmlReader.MoveToContent();
//var _ = xmlReader.Name switch
//{
// "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
// "InitDatas" => LoadInitDatas(xmlReader),
// "txdRelationships" => LoadTxdRelationships(xmlReader),
// _ => throw new Exception()
//};
switch (xmlReader.Name)
{
case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd");
break;
case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
LoadInitDatas(xmlReader);
break;
case string Name when Name.Equals("txdRelationships", StringComparison.OrdinalIgnoreCase):
LoadTxdRelationships(xmlReader);
break;
default:
break;
}
}
//ResidentTxd = Xml.GetChildInnerText(xmldoc.SelectSingleNode("CVehicleModelInfo__InitDataList"), "residentTxd");
//LoadInitDatas(xmldoc);
//LoadTxdRelationships(xmldoc);
Loaded = true;
}
}
private void LoadInitDatas(XmlDocument xmldoc) 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>(); InitDatas = new List<VehicleInitData>(items.Count);
for (int i = 0; i < items.Count; i++) for (int i = 0; i < items.Count; i++)
{ {
var node = items[i]; var node = items[i];
@ -68,6 +125,53 @@ namespace CodeWalker.GameFiles
} }
} }
private void LoadInitDatas(XmlReader reader)
{
if (!reader.IsStartElement() || reader.Name != "InitDatas")
{
throw new InvalidOperationException("XmlReader is not at start element of \"InitDatas\"");
}
InitDatas = new List<VehicleInitData>();
reader.ReadStartElement("InitDatas");
while (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "Item")
{
if (reader.IsStartElement())
{
VehicleInitData d = new VehicleInitData();
d.Load(reader);
InitDatas.Add(d);
}
}
}
private void LoadTxdRelationships(XmlReader reader)
{
if (reader.IsEmptyElement)
{
TxdRelationships = new Dictionary<string, string>();
reader.ReadStartElement();
return;
}
TxdRelationships = new Dictionary<string, string>();
foreach(var item in Xml.IterateItems(reader, "txdRelationships"))
{
var childstr = item.Element("child")?.Value;
var parentstr = item.Element("parent")?.Value;
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
{
if (!TxdRelationships.ContainsKey(childstr))
{
TxdRelationships.Add(childstr, parentstr);
}
}
}
}
private void LoadTxdRelationships(XmlDocument xmldoc) 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");
@ -89,8 +193,6 @@ namespace CodeWalker.GameFiles
} }
} }
} }
} }
@ -106,7 +208,7 @@ namespace CodeWalker.GameFiles
public string expressionName { get; set; } //<expressionName>null</expressionName> public string expressionName { get; set; } //<expressionName>null</expressionName>
public string animConvRoofDictName { get; set; } //<animConvRoofDictName>null</animConvRoofDictName> public string animConvRoofDictName { get; set; } //<animConvRoofDictName>null</animConvRoofDictName>
public string animConvRoofName { get; set; } //<animConvRoofName>null</animConvRoofName> public string animConvRoofName { get; set; } //<animConvRoofName>null</animConvRoofName>
public string animConvRoofWindowsAffected { get; set; } //<animConvRoofWindowsAffected /> public string[] animConvRoofWindowsAffected { get; set; } //<animConvRoofWindowsAffected />
public string ptfxAssetName { get; set; } //<ptfxAssetName>weap_xs_vehicle_weapons</ptfxAssetName> public string ptfxAssetName { get; set; } //<ptfxAssetName>weap_xs_vehicle_weapons</ptfxAssetName>
public string audioNameHash { get; set; } //<audioNameHash /> public string audioNameHash { get; set; } //<audioNameHash />
public string layout { get; set; } //<layout>LAYOUT_STD_ARENA_1HONLY</layout> public string layout { get; set; } //<layout>LAYOUT_STD_ARENA_1HONLY</layout>
@ -185,6 +287,354 @@ namespace CodeWalker.GameFiles
public VehicleOverrideRagdollThreshold pOverrideRagdollThreshold { get; set; } //<pOverrideRagdollThreshold type="NULL" /> public VehicleOverrideRagdollThreshold pOverrideRagdollThreshold { get; set; } //<pOverrideRagdollThreshold type="NULL" />
public string[] firstPersonDrivebyData { get; set; } //<firstPersonDrivebyData>// <Item>STD_IMPALER2_FRONT_LEFT</Item>// <Item>STD_IMPALER2_FRONT_RIGHT</Item>//</firstPersonDrivebyData> public string[] firstPersonDrivebyData { get; set; } //<firstPersonDrivebyData>// <Item>STD_IMPALER2_FRONT_LEFT</Item>// <Item>STD_IMPALER2_FRONT_RIGHT</Item>//</firstPersonDrivebyData>
public void Load(XmlReader reader)
{
reader.ReadStartElement("Item");
while (reader.Name != "Item" && reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Item")
{
reader.ReadEndElement();
return;
}
if (reader.IsStartElement())
{
while (reader.IsStartElement())
{
switch (reader.Name)
{
case "modelName":
modelName = Xml.GetChildInnerText(reader, "modelName");
break;
case "txdName":
txdName = Xml.GetChildInnerText(reader, "txdName");
break;
case "handlingId":
handlingId = Xml.GetChildInnerText(reader, "handlingId");
break;
case "gameName":
gameName = Xml.GetChildInnerText(reader, "gameName");
break;
case "vehicleMakeName":
vehicleMakeName = Xml.GetChildInnerText(reader, "vehicleMakeName");
break;
case "expressionDictName":
expressionDictName = Xml.GetChildInnerText(reader, "expressionDictName");
break;
case "expressionName":
expressionName = Xml.GetChildInnerText(reader, "expressionName");
break;
case "animConvRoofDictName":
animConvRoofDictName = Xml.GetChildInnerText(reader, "animConvRoofDictName");
break;
case "animConvRoofName":
animConvRoofName = Xml.GetChildInnerText(reader, "animConvRoofName");
break;
case "animConvRoofWindowsAffected":
animConvRoofWindowsAffected = GetStringItemArray(reader, "animConvRoofWindowsAffected");
break;
case "ptfxAssetName":
ptfxAssetName = Xml.GetChildInnerText(reader, "ptfxAssetName");
break;
case "audioNameHash":
audioNameHash = Xml.GetChildInnerText(reader, "audioNameHash");
break;
case "layout":
layout = Xml.GetChildInnerText(reader, "layout");
break;
case "coverBoundOffsets":
coverBoundOffsets = Xml.GetChildInnerText(reader, "coverBoundOffsets");
break;
case "explosionInfo":
explosionInfo = Xml.GetChildInnerText(reader, "explosionInfo");
break;
case "scenarioLayout":
scenarioLayout = Xml.GetChildInnerText(reader, "scenarioLayout");
break;
case "cameraName":
cameraName = Xml.GetChildInnerText(reader, "cameraName");
break;
case "aimCameraName":
aimCameraName = Xml.GetChildInnerText(reader, "aimCameraName");
break;
case "bonnetCameraName":
bonnetCameraName = Xml.GetChildInnerText(reader, "bonnetCameraName");
break;
case "povCameraName":
povCameraName = Xml.GetChildInnerText(reader, "povCameraName");
break;
case "FirstPersonDriveByIKOffset":
FirstPersonDriveByIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByIKOffset");
break;
case "FirstPersonDriveByUnarmedIKOffset":
FirstPersonDriveByUnarmedIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByUnarmedIKOffset");
break;
case "FirstPersonProjectileDriveByIKOffset":
FirstPersonProjectileDriveByIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonProjectileDriveByIKOffset");
break;
case "FirstPersonProjectileDriveByPassengerIKOffset":
FirstPersonProjectileDriveByPassengerIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonProjectileDriveByPassengerIKOffset");
break;
case "FirstPersonDriveByRightPassengerIKOffset":
FirstPersonDriveByRightPassengerIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByRightPassengerIKOffset");
break;
case "FirstPersonDriveByRightPassengerUnarmedIKOffset":
FirstPersonDriveByRightPassengerUnarmedIKOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonDriveByRightPassengerUnarmedIKOffset");
break;
case "FirstPersonMobilePhoneOffset":
FirstPersonMobilePhoneOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonMobilePhoneOffset");
break;
case "FirstPersonPassengerMobilePhoneOffset":
FirstPersonPassengerMobilePhoneOffset = Xml.GetChildVector3Attributes(reader, "FirstPersonPassengerMobilePhoneOffset");
break;
case "PovCameraOffset":
PovCameraOffset = Xml.GetChildVector3Attributes(reader, "PovCameraOffset");
break;
case "PovCameraVerticalAdjustmentForRollCage":
PovCameraVerticalAdjustmentForRollCage = Xml.GetChildVector3Attributes(reader, "PovCameraVerticalAdjustmentForRollCage");
break;
case "PovPassengerCameraOffset":
PovPassengerCameraOffset = Xml.GetChildVector3Attributes(reader, "PovPassengerCameraOffset");
break;
case "PovRearPassengerCameraOffset":
PovRearPassengerCameraOffset = Xml.GetChildVector3Attributes(reader, "PovRearPassengerCameraOffset");
break;
case "vfxInfoName":
vfxInfoName = Xml.GetChildInnerText(reader, "vfxInfoName");
break;
case "shouldUseCinematicViewMode":
shouldUseCinematicViewMode = Xml.GetChildBoolAttribute(reader, "shouldUseCinematicViewMode");
break;
case "shouldCameraTransitionOnClimbUpDown":
shouldCameraTransitionOnClimbUpDown = Xml.GetChildBoolAttribute(reader, "shouldCameraTransitionOnClimbUpDown");
break;
case "shouldCameraIgnoreExiting":
shouldCameraIgnoreExiting = Xml.GetChildBoolAttribute(reader, "shouldCameraIgnoreExiting");
break;
case "AllowPretendOccupants":
AllowPretendOccupants = Xml.GetChildBoolAttribute(reader, "AllowPretendOccupants");
break;
case "AllowJoyriding":
AllowJoyriding = Xml.GetChildBoolAttribute(reader, "AllowJoyriding");
break;
case "AllowSundayDriving":
AllowSundayDriving = Xml.GetChildBoolAttribute(reader, "AllowSundayDriving");
break;
case "AllowBodyColorMapping":
AllowBodyColorMapping = Xml.GetChildBoolAttribute(reader, "AllowBodyColorMapping");
break;
case "wheelScale":
wheelScale = Xml.GetChildFloatAttribute(reader, "wheelScale");
break;
case "wheelScaleRear":
wheelScaleRear = Xml.GetChildFloatAttribute(reader, "wheelScaleRear");
break;
case "dirtLevelMin":
dirtLevelMin = Xml.GetChildFloatAttribute(reader, "dirtLevelMin");
break;
case "dirtLevelMax":
dirtLevelMax = Xml.GetChildFloatAttribute(reader, "dirtLevelMax");
break;
case "envEffScaleMin":
envEffScaleMin = Xml.GetChildFloatAttribute(reader, "envEffScaleMin");
break;
case "envEffScaleMax":
envEffScaleMax = Xml.GetChildFloatAttribute(reader, "envEffScaleMax");
break;
case "envEffScaleMin2":
envEffScaleMin2 = Xml.GetChildFloatAttribute(reader, "envEffScaleMin2");
break;
case "envEffScaleMax2":
envEffScaleMax2 = Xml.GetChildFloatAttribute(reader, "envEffScaleMax2");
break;
case "damageMapScale":
damageMapScale = Xml.GetChildFloatAttribute(reader, "damageMapScale");
break;
case "damageOffsetScale":
damageOffsetScale = Xml.GetChildFloatAttribute(reader, "damageOffsetScale");
break;
case "diffuseTint":
diffuseTint = new Color4(Convert.ToUInt32(Xml.GetChildStringAttribute(reader, "diffuseTint", "value").Replace("0x", ""), 16)); ;
break;
case "steerWheelMult":
steerWheelMult = Xml.GetChildFloatAttribute(reader, "steerWheelMult");
break;
case "HDTextureDist":
HDTextureDist = Xml.GetChildFloatAttribute(reader, "HDTextureDist");
break;
case "lodDistances":
lodDistances = GetFloatArray(reader, "lodDistances", '\n');
break;
case "minSeatHeight":
minSeatHeight = Xml.GetChildFloatAttribute(reader, "minSeatHeight");
break;
case "identicalModelSpawnDistance":
identicalModelSpawnDistance = Xml.GetChildFloatAttribute(reader, "identicalModelSpawnDistance");
break;
case "maxNumOfSameColor":
maxNumOfSameColor = Xml.GetChildIntAttribute(reader, "maxNumOfSameColor");
break;
case "defaultBodyHealth":
defaultBodyHealth = Xml.GetChildFloatAttribute(reader, "defaultBodyHealth");
break;
case "pretendOccupantsScale":
pretendOccupantsScale = Xml.GetChildFloatAttribute(reader, "pretendOccupantsScale");
break;
case "visibleSpawnDistScale":
visibleSpawnDistScale = Xml.GetChildFloatAttribute(reader, "visibleSpawnDistScale");
break;
case "trackerPathWidth":
trackerPathWidth = Xml.GetChildFloatAttribute(reader, "trackerPathWidth");
break;
case "weaponForceMult":
weaponForceMult = Xml.GetChildFloatAttribute(reader, "weaponForceMult");
break;
case "frequency":
frequency = Xml.GetChildFloatAttribute(reader, "frequency");
break;
case "swankness":
swankness = Xml.GetChildInnerText(reader, "swankness");
break;
case "maxNum":
maxNum = Xml.GetChildIntAttribute(reader, "maxNum", "value");
break;
case "flags":
flags = GetStringArray(reader, "flags", ' ');
break;
case "type":
type = Xml.GetChildInnerText(reader, "type");
break;
case "plateType":
plateType = Xml.GetChildInnerText(reader, "plateType");
break;
case "dashboardType":
dashboardType = Xml.GetChildInnerText(reader, "dashboardType");
break;
case "vehicleClass":
vehicleClass = Xml.GetChildInnerText(reader, "vehicleClass");
break;
case "wheelType":
wheelType = Xml.GetChildInnerText(reader, "wheelType");
break;
case "trailers":
trailers = GetStringItemArray(reader, "trailers");
break;
case "additionalTrailers":
additionalTrailers = GetStringItemArray(reader, "additionalTrailers");
break;
case "drivers":
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
var _drivers = new List<VehicleDriver>();
foreach (var item in Xml.IterateItems(reader, "drivers"))
{
var driver = new VehicleDriver();
driver.driverName = item.Element("driverName")?.Value ?? string.Empty;
driver.npcName = item.Element("npcName")?.Value ?? string.Empty;
if (!string.IsNullOrEmpty(driver.npcName) || !string.IsNullOrEmpty(driver.driverName))
{
_drivers.Add(driver);
}
}
drivers = _drivers.ToArray();
break;
case "doorsWithCollisionWhenClosed":
doorsWithCollisionWhenClosed = GetStringItemArray(reader, "doorsWithCollisionWhenClosed");
break;
case "driveableDoors":
driveableDoors = GetStringItemArray(reader, "driveableDoors");
break;
case "bumpersNeedToCollideWithMap":
bumpersNeedToCollideWithMap = Xml.GetChildBoolAttribute(reader, "bumpersNeedToCollideWithMap", "value");
break;
case "needsRopeTexture":
needsRopeTexture = Xml.GetChildBoolAttribute(reader, "needsRopeTexture", "value");
break;
case "requiredExtras":
requiredExtras = GetStringArray(reader, "requiredExtras", ' ');
break;
case "rewards":
rewards = GetStringItemArray(reader, "rewards");
break;
case "cinematicPartCamera":
cinematicPartCamera = GetStringItemArray(reader, "cinematicPartCamera");
break;
case "NmBraceOverrideSet":
NmBraceOverrideSet = Xml.GetChildInnerText(reader, "NmBraceOverrideSet");
break;
case "buoyancySphereOffset":
buoyancySphereOffset = Xml.GetChildVector3Attributes(reader, "buoyancySphereOffset");
break;
case "buoyancySphereSizeScale":
buoyancySphereSizeScale = Xml.GetChildFloatAttribute(reader, "buoyancySphereSizeScale", "value");
break;
case "pOverrideRagdollThreshold":
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
switch (reader.GetAttribute("type"))
{
case "NULL":
break;
case "CVehicleModelInfo__CVehicleOverrideRagdollThreshold":
reader.ReadStartElement();
pOverrideRagdollThreshold = new VehicleOverrideRagdollThreshold();
while (reader.MoveToContent() == XmlNodeType.Element)
{
if (reader.Name == "MinComponent")
{
pOverrideRagdollThreshold.MinComponent = Xml.GetChildIntAttribute(reader, "MinComponent", "value");
}
else if (reader.Name == "MaxComponent")
{
pOverrideRagdollThreshold.MaxComponent = Xml.GetChildIntAttribute(reader, "MaxComponent", "value");
}
else if (reader.Name == "ThresholdMult")
{
pOverrideRagdollThreshold.ThresholdMult = Xml.GetChildFloatAttribute(reader, "ThresholdMult", "value");
} else
{
break;
}
}
reader.ReadEndElement();
break;
default:
break;
}
break;
case "firstPersonDrivebyData":
firstPersonDrivebyData = GetStringItemArray(reader, "firstPersonDrivebyData");
break;
case "extraIncludes":
extraIncludes = GetStringItemArray(reader, "extraIncludes");
break;
case "FirstPersonDriveByLeftPassengerIKOffset":
case "FirstPersonDriveByLeftPassengerUnarmedIKOffset":
default:
reader.Skip();
break;
}
}
}
else
{
reader.Read();
}
}
reader.ReadEndElement();
}
public void Load(XmlNode node) public void Load(XmlNode node)
{ {
@ -197,7 +647,7 @@ namespace CodeWalker.GameFiles
expressionName = Xml.GetChildInnerText(node, "expressionName"); expressionName = Xml.GetChildInnerText(node, "expressionName");
animConvRoofDictName = Xml.GetChildInnerText(node, "animConvRoofDictName"); animConvRoofDictName = Xml.GetChildInnerText(node, "animConvRoofDictName");
animConvRoofName = Xml.GetChildInnerText(node, "animConvRoofName"); animConvRoofName = Xml.GetChildInnerText(node, "animConvRoofName");
animConvRoofWindowsAffected = Xml.GetChildInnerText(node, "animConvRoofWindowsAffected");//? animConvRoofWindowsAffected = GetStringItemArray(node, "animConvRoofWindowsAffected");//?
ptfxAssetName = Xml.GetChildInnerText(node, "ptfxAssetName"); ptfxAssetName = Xml.GetChildInnerText(node, "ptfxAssetName");
audioNameHash = Xml.GetChildInnerText(node, "audioNameHash"); audioNameHash = Xml.GetChildInnerText(node, "audioNameHash");
layout = Xml.GetChildInnerText(node, "layout"); layout = Xml.GetChildInnerText(node, "layout");
@ -327,11 +777,41 @@ namespace CodeWalker.GameFiles
if (getStringArrayList.Count == 0) return null; if (getStringArrayList.Count == 0) return null;
return getStringArrayList.ToArray(); return getStringArrayList.ToArray();
} }
private string[] GetStringArray(XmlNode node, string childName, char delimiter)
private string[] GetStringItemArray(XmlReader node, string childName)
{
if (node.IsEmptyElement)
{
node.ReadStartElement();
return null;
}
lock(getStringArrayList)
{
getStringArrayList.Clear();
node.ReadStartElement();
while (node.MoveToContent() == XmlNodeType.Element && node.Name == "Item")
{
var istr = node.ReadElementContentAsString();
if (!string.IsNullOrEmpty(istr))
{
getStringArrayList.Add(istr);
}
}
node.ReadEndElement();
if (getStringArrayList.Count == 0)
return null;
return getStringArrayList.ToArray();
}
}
private string[] GetStringArray(string ldastr, char delimiter)
{ {
var ldastr = Xml.GetChildInnerText(node, childName);
var ldarr = ldastr?.Split(delimiter); var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null; if (ldarr == null) return null;
lock(getStringArrayList)
{
getStringArrayList.Clear(); getStringArrayList.Clear();
foreach (var ldstr in ldarr) foreach (var ldstr in ldarr)
{ {
@ -344,12 +824,26 @@ namespace CodeWalker.GameFiles
if (getStringArrayList.Count == 0) return null; if (getStringArrayList.Count == 0) return null;
return getStringArrayList.ToArray(); return getStringArrayList.ToArray();
} }
private float[] GetFloatArray(XmlNode node, string childName, char delimiter) }
private string[] GetStringArray(XmlNode node, string childName, char delimiter)
{ {
var ldastr = Xml.GetChildInnerText(node, childName); var ldastr = Xml.GetChildInnerText(node, childName);
return GetStringArray(ldastr, delimiter);
}
private string[] GetStringArray(XmlReader reader, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(reader, childName);
return GetStringArray(ldastr, delimiter);
}
private unsafe float[] GetFloatArray(string ldastr, char delimiter)
{
var ldarr = ldastr?.Split(delimiter); var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null; if (ldarr == null) return null;
getFloatArrayList.Clear(); var floats = stackalloc float[ldarr.Length];
var i = 0;
foreach (var ldstr in ldarr) foreach (var ldstr in ldarr)
{ {
var ldt = ldstr?.Trim(); var ldt = ldstr?.Trim();
@ -358,12 +852,30 @@ namespace CodeWalker.GameFiles
float f; float f;
if (FloatUtil.TryParse(ldt, out f)) if (FloatUtil.TryParse(ldt, out f))
{ {
getFloatArrayList.Add(f); floats[i] = f;
i++;
} }
} }
} }
if (getFloatArrayList.Count == 0) return null; if (i == 0) return null;
return getFloatArrayList.ToArray();
var result = new float[i];
Marshal.Copy((IntPtr)floats, result, 0, i);
return result;
}
private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(node, childName);
return GetFloatArray(ldastr, delimiter);
}
private float[] GetFloatArray(XmlReader reader, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(reader, childName);
return GetFloatArray(ldastr, delimiter);
} }
private static List<string> getStringArrayList = new List<string>(); //kinda hacky.. private static List<string> getStringArrayList = new List<string>(); //kinda hacky..
@ -374,28 +886,283 @@ namespace CodeWalker.GameFiles
{ {
return modelName; 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 class VehicleOverrideRagdollThreshold 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 int MinComponent { get; set; } public int MinComponent { get; set; }
public int MaxComponent { get; set; } public int MaxComponent { get; set; }
public float ThresholdMult { 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() public override string ToString()
{ {
return MinComponent.ToString() + ", " + MaxComponent.ToString() + ", " + ThresholdMult.ToString(); return MinComponent.ToString() + ", " + MaxComponent.ToString() + ", " + ThresholdMult.ToString();
} }
public static bool operator ==(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
{
return left.Equals(right);
} }
public class VehicleDriver
public static bool operator !=(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
{
return !(left == right);
}
}
public class VehicleDriver : IEquatable<VehicleDriver>
{ {
public string driverName { get; set; } public string driverName { get; set; }
public string npcName { 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 override string ToString() public override string ToString()
{ {
return driverName + ", " + npcName; 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);
}
} }
} }

View File

@ -75,20 +75,17 @@ namespace CodeWalker.GameFiles
var drawable = drawables[i]; var drawable = drawables[i];
var hash = hashes[i]; var hash = hashes[i];
drawable.Hash = hash; drawable.Hash = hash;
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd"))) if (drawable.Name == null || drawable.Name.EndsWith("#dd", StringComparison.OrdinalIgnoreCase))
{ {
string hstr = JenkIndex.TryGetString(hash); string hstr = JenkIndex.TryGetString(hash);
if (!string.IsNullOrEmpty(hstr)) if (!string.IsNullOrEmpty(hstr))
{ {
drawable.Name = hstr; drawable.Name = hstr;
} }
else
{ }
} }
} }
Drawables = Dict.Values.ToArray(); Drawables = Dict.Values.ToArray();
} }
Loaded = true; Loaded = true;

View File

@ -862,6 +862,7 @@ namespace CodeWalker.GameFiles
ChildYmaps[i] = gfc.GetYmap(chash); ChildYmaps[i] = gfc.GetYmap(chash);
if (ChildYmaps[i] == null) if (ChildYmaps[i] == null)
{ {
Console.WriteLine($"Couldn't find child ymap! {chash} for {Name}");
//couldn't find child ymap.. //couldn't find child ymap..
} }
} }
@ -895,7 +896,10 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < ChildYmaps.Length; i++) for (int i = 0; i < ChildYmaps.Length; i++)
{ {
var cmap = ChildYmaps[i]; var cmap = ChildYmaps[i];
if (cmap == null) continue; //nothing here.. if (cmap == null)
{
continue; //nothing here..
}
//cmap.EnsureChildYmaps(); //cmap.EnsureChildYmaps();
if ((cmap.Loaded) && (!cmap.MergedWithParent)) if ((cmap.Loaded) && (!cmap.MergedWithParent))
{ {
@ -956,7 +960,7 @@ namespace CodeWalker.GameFiles
YmapEntityDef p = null; YmapEntityDef p = null;
if ((pymap != null) && (pymap.AllEntities != null)) if ((pymap != null) && (pymap.AllEntities != null))
{ {
if ((pind < pymap.AllEntities.Length)) if (pind < pymap.AllEntities.Length)
{ {
p = pymap.AllEntities[pind]; p = pymap.AllEntities[pind];
ent.Parent = p; ent.Parent = p;
@ -964,7 +968,9 @@ namespace CodeWalker.GameFiles
} }
} }
else else
{ }//should only happen if parent ymap not loaded yet... {
Console.WriteLine($"Parent not loaded yet for {pymap.Name}");
}
} }
} }
} }
@ -974,8 +980,6 @@ namespace CodeWalker.GameFiles
{ {
LODLights.Init(Parent.DistantLODLights); LODLights.Init(Parent.DistantLODLights);
} }
else
{ }
} }
} }
@ -987,8 +991,11 @@ namespace CodeWalker.GameFiles
{ {
//used by the editor to add to the ymap. //used by the editor to add to the ymap.
List<YmapEntityDef> allents = new List<YmapEntityDef>(); List<YmapEntityDef> allents;
if (AllEntities != null) allents.AddRange(AllEntities); if (AllEntities != null)
allents = new List<YmapEntityDef>(AllEntities);
else
allents = new List<YmapEntityDef>();
ent.Index = allents.Count; ent.Index = allents.Count;
ent.Ymap = this; ent.Ymap = this;
allents.Add(ent); allents.Add(ent);
@ -999,8 +1006,11 @@ namespace CodeWalker.GameFiles
{ {
//root entity, add to roots. //root entity, add to roots.
List<YmapEntityDef> rootents = new List<YmapEntityDef>(); List<YmapEntityDef> rootents;
if (RootEntities != null) rootents.AddRange(RootEntities); if (RootEntities != null)
rootents = new List<YmapEntityDef>(RootEntities);
else
rootents = new List<YmapEntityDef>();
rootents.Add(ent); rootents.Add(ent);
RootEntities = rootents.ToArray(); RootEntities = rootents.ToArray();
} }
@ -1012,7 +1022,8 @@ namespace CodeWalker.GameFiles
public bool RemoveEntity(YmapEntityDef ent) public bool RemoveEntity(YmapEntityDef ent)
{ {
//used by the editor to remove from the ymap. //used by the editor to remove from the ymap.
if (ent == null) return false; if (ent == null)
return false;
var res = true; var res = true;
@ -1020,23 +1031,32 @@ namespace CodeWalker.GameFiles
List<YmapEntityDef> newAllEntities = new List<YmapEntityDef>(); List<YmapEntityDef> newAllEntities = new List<YmapEntityDef>();
List<YmapEntityDef> newRootEntities = new List<YmapEntityDef>(); List<YmapEntityDef> newRootEntities = new List<YmapEntityDef>();
if (AllEntities != null)
{
for (int i = 0; i < AllEntities.Length; i++) for (int i = 0; i < AllEntities.Length; i++)
{ {
var oent = AllEntities[i]; var oent = AllEntities[i];
oent.Index = newAllEntities.Count; oent.Index = newAllEntities.Count;
if (oent != ent) newAllEntities.Add(oent); if (oent != ent)
{
newAllEntities.Add(oent);
}
else if (i != idx) else if (i != idx)
{ {
res = false; //indexes didn't match.. this shouldn't happen! res = false; //indexes didn't match.. this shouldn't happen!
} }
} }
}
if (RootEntities != null)
{
for (int i = 0; i < RootEntities.Length; i++) for (int i = 0; i < RootEntities.Length; i++)
{ {
var oent = RootEntities[i]; var oent = RootEntities[i];
if (oent != ent) newRootEntities.Add(oent); if (oent != ent) newRootEntities.Add(oent);
} }
}
if ((AllEntities.Length == newAllEntities.Count) || (RootEntities.Length == newRootEntities.Count)) if (AllEntities == null || AllEntities.Length == newAllEntities.Count || RootEntities == null || RootEntities.Length == newRootEntities.Count)
{ {
res = false; res = false;
} }
@ -1055,7 +1075,8 @@ namespace CodeWalker.GameFiles
public void AddCarGen(YmapCarGen cargen) public void AddCarGen(YmapCarGen cargen)
{ {
List<YmapCarGen> cargens = new List<YmapCarGen>(); List<YmapCarGen> cargens = new List<YmapCarGen>();
if (CarGenerators != null) cargens.AddRange(CarGenerators); if (CarGenerators != null)
cargens.AddRange(CarGenerators);
cargen.Ymap = this; cargen.Ymap = this;
cargens.Add(cargen); cargens.Add(cargen);
CarGenerators = cargens.ToArray(); CarGenerators = cargens.ToArray();
@ -1334,10 +1355,9 @@ namespace CodeWalker.GameFiles
public void SetName(string newname) public void SetName(string newname)
{ {
var newnamel = newname.ToLowerInvariant();
var newnamex = newname + ".ymap"; var newnamex = newname + ".ymap";
var newhash = JenkHash.GenHash(newnamel); var newhash = JenkHash.GenHashLower(newname);
JenkIndex.Ensure(newnamel); JenkIndex.EnsureLower(newname);
if (RpfFileEntry != null) if (RpfFileEntry != null)
{ {
RpfFileEntry.Name = newnamex; RpfFileEntry.Name = newnamex;
@ -1692,7 +1712,7 @@ namespace CodeWalker.GameFiles
public int Index { get; set; } public int Index { get; set; }
public float Distance { get; set; } //used for rendering public float Distance { get; set; } //used for rendering
public bool IsVisible; //used for rendering public bool IsWithinLodDist; //used for rendering
public bool ChildrenVisible; //used for rendering public bool ChildrenVisible; //used for rendering
public bool ChildrenRendered; //used when rendering ymap mode to reduce LOD flashing... public bool ChildrenRendered; //used when rendering ymap mode to reduce LOD flashing...
public YmapEntityDef Parent { get; set; } //for browsing convenience, also used/updated for rendering public YmapEntityDef Parent { get; set; } //for browsing convenience, also used/updated for rendering

View File

@ -1890,7 +1890,7 @@ namespace CodeWalker.GameFiles
{ {
return 0; return 0;
} }
if (str.StartsWith("hash_")) if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{ {
return Convert.ToUInt32(str.Substring(5), 16); return Convert.ToUInt32(str.Substring(5), 16);
} }

View File

@ -288,16 +288,16 @@ namespace CodeWalker.GameFiles
{ } { }
NameHash = _CMapTypes.name; NameHash = _CMapTypes.name;
if ((NameHash == 0) && (entry.NameLower != null)) if ((NameHash == 0) && (entry.Name != null))
{ {
int ind = entry.NameLower.LastIndexOf('.'); int ind = entry.Name.LastIndexOf('.');
if (ind > 0) if (ind > 0)
{ {
NameHash = JenkHash.GenHash(entry.NameLower.Substring(0, ind)); NameHash = JenkHash.GenHashLower(entry.Name.AsSpan(0, ind));
} }
else else
{ {
NameHash = JenkHash.GenHash(entry.NameLower); NameHash = JenkHash.GenHashLower(entry.Name);
} }
} }

View File

@ -10,6 +10,7 @@ namespace CodeWalker.GameFiles
{ {
public volatile bool Loaded = false; public volatile bool Loaded = false;
public volatile bool LoadQueued = false; public volatile bool LoadQueued = false;
public DateTime LastLoadTime = DateTime.MinValue;
public RpfFileEntry RpfFileEntry { get; set; } public RpfFileEntry RpfFileEntry { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string FilePath { get; set; } //used by the project form. public string FilePath { get; set; } //used by the project form.
@ -91,7 +92,7 @@ namespace CodeWalker.GameFiles
public struct GameFileCacheKey public struct GameFileCacheKey : IEquatable<GameFileCacheKey>
{ {
public uint Hash { get; set; } public uint Hash { get; set; }
public GameFileType Type { get; set; } public GameFileType Type { get; set; }
@ -102,13 +103,22 @@ namespace CodeWalker.GameFiles
Type = type; Type = type;
} }
public override bool Equals(object obj) public override readonly bool Equals(object obj)
{ {
if (obj == null) return false; if (obj == null)
if (obj is not GameFileCacheKey gameFileCacheKey) return false; return false;
if (obj is not GameFileCacheKey gameFileCacheKey)
return false;
return gameFileCacheKey.Hash == Hash && gameFileCacheKey.Type == Type; 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) public static bool operator ==(GameFileCacheKey first, GameFileCacheKey second)
{ {
return first.Equals(second); return first.Equals(second);
@ -119,12 +129,9 @@ namespace CodeWalker.GameFiles
return !first.Equals(second); return !first.Equals(second);
} }
public override int GetHashCode() public override readonly int GetHashCode()
{ {
return (int)Hash; return (int)Hash;
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -393,10 +393,10 @@ namespace CodeWalker.GameFiles
{ {
foreach (var portal in portals) foreach (var portal in portals)
{ {
List<uint> newAttachedObjects = new List<uint>(); if (portal.AttachedObjects == null || portal.AttachedObjects.Length == 0)
if (portal.AttachedObjects == null)
continue; continue;
List<uint> newAttachedObjects = new List<uint>();
foreach (var objIndex in portal.AttachedObjects) foreach (var objIndex in portal.AttachedObjects)
{ {
if (objIndex == deletedIndex) continue; if (objIndex == deletedIndex) continue;
@ -411,9 +411,10 @@ namespace CodeWalker.GameFiles
{ {
foreach (var room in rooms) foreach (var room in rooms)
{ {
List<uint> newAttachedObjects = new List<uint>(); if (room.AttachedObjects == null || room.AttachedObjects.Length == 0)
if (room.AttachedObjects == null)
continue; continue;
List<uint> newAttachedObjects = new List<uint>();
foreach (var objIndex in room.AttachedObjects) foreach (var objIndex in room.AttachedObjects)
{ {
if (objIndex == deletedIndex) continue; if (objIndex == deletedIndex) continue;
@ -532,10 +533,12 @@ namespace CodeWalker.GameFiles
} }
public MCMloRoomDef GetEntityRoom(MCEntityDef ent) public MCMloRoomDef GetEntityRoom(MCEntityDef ent)
{ {
if (rooms == null) return null; if (rooms == null)
return null;
int objectIndex = GetEntityObjectIndex(ent); int objectIndex = GetEntityObjectIndex(ent);
if (objectIndex < 0) return null; if (objectIndex < 0)
return null;
for (int i = 0; i < rooms.Length; i++) for (int i = 0; i < rooms.Length; i++)
{ {
@ -684,10 +687,15 @@ namespace CodeWalker.GameFiles
{ {
var ient = Entities[j]; var ient = Entities[j];
var iarch = gfc.GetArchetype(ient._CEntityDef.archetypeName); var iarch = gfc.GetArchetype(ient._CEntityDef.archetypeName);
ient.SetArchetype(iarch);
if (iarch == null) if (iarch == null)
{ } //can't find archetype - des stuff eg {des_prologue_door} {
Console.WriteLine($"Can't find archetype for {ient._CEntityDef.archetypeName}!");
}
else
{
ient.SetArchetype(iarch);
}
} }
UpdateBBs(arch); UpdateBBs(arch);
@ -708,7 +716,13 @@ namespace CodeWalker.GameFiles
ient.SetArchetype(iarch); ient.SetArchetype(iarch);
if (iarch == null) if (iarch == null)
{ } //can't find archetype - des stuff eg {des_prologue_door} {
Console.WriteLine($"Couldn't find archetype {ient._CEntityDef.archetypeName}");
}
else
{
ient.SetArchetype(iarch);
}
} }
} }
} }
@ -729,7 +743,8 @@ namespace CodeWalker.GameFiles
for (int j = 0; j < rooms.Length; j++) for (int j = 0; j < rooms.Length; j++)
{ {
var room = rooms[j]; var room = rooms[j];
if ((room.AttachedObjects == null) || (room.AttachedObjects.Length == 0)) continue; if (room.AttachedObjects == null || room.AttachedObjects.Length == 0)
continue;
Vector3 min = new Vector3(float.MaxValue); Vector3 min = new Vector3(float.MaxValue);
Vector3 max = new Vector3(float.MinValue); Vector3 max = new Vector3(float.MinValue);
for (int k = 0; k < room.AttachedObjects.Length; k++) for (int k = 0; k < room.AttachedObjects.Length; k++)

View File

@ -1,5 +1,4 @@
using System; using System;using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -675,6 +674,35 @@ namespace CodeWalker.GameFiles
{ {
return "Array_StructurePointer: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")"; return "Array_StructurePointer: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")";
} }
public override bool Equals(object obj)
{
return obj is Array_StructurePointer pointer &&
Pointer == pointer.Pointer &&
Count1 == pointer.Count1 &&
Count2 == pointer.Count2 &&
Unk1 == pointer.Unk1;
}
public override int GetHashCode()
{
int hashCode = -1900453823;
hashCode = hashCode * -1521134295 + Pointer.GetHashCode();
hashCode = hashCode * -1521134295 + Count1.GetHashCode();
hashCode = hashCode * -1521134295 + Count2.GetHashCode();
hashCode = hashCode * -1521134295 + Unk1.GetHashCode();
return hashCode;
}
public static bool operator ==(Array_StructurePointer left, Array_StructurePointer right)
{
return left.Equals(right);
}
public static bool operator !=(Array_StructurePointer left, Array_StructurePointer right)
{
return !(left == right);
}
} }
[TC(typeof(EXP))] public struct Array_Structure //16 bytes - pointer for a structure array [TC(typeof(EXP))] public struct Array_Structure //16 bytes - pointer for a structure array
{ {
@ -717,6 +745,37 @@ namespace CodeWalker.GameFiles
{ {
return "Array_Structure: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")"; return "Array_Structure: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")";
} }
public static bool operator ==(Array_Structure x, Array_Structure y)
{
return x.Equals(y);
}
public static bool operator !=(Array_Structure x, Array_Structure y)
{
return !x.Equals(y);
}
public override bool Equals(object obj)
{
if (obj is null || obj is not Array_Structure arrObj)
return false;
return arrObj.Pointer == this.Pointer
&& arrObj.Count1 == this.Count1
&& arrObj.Count2 == this.Count2
&& arrObj.Unk1 == this.Unk1;
}
public override int GetHashCode()
{
int hashCode = -1900453823;
hashCode = hashCode * -1521134295 + Pointer.GetHashCode();
hashCode = hashCode * -1521134295 + Count1.GetHashCode();
hashCode = hashCode * -1521134295 + Count2.GetHashCode();
hashCode = hashCode * -1521134295 + Unk1.GetHashCode();
return hashCode;
}
} }
[TC(typeof(EXP))] public struct Array_uint //16 bytes - pointer for a uint array [TC(typeof(EXP))] public struct Array_uint //16 bytes - pointer for a uint array
{ {
@ -757,6 +816,37 @@ namespace CodeWalker.GameFiles
{ {
return "Array_uint: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")"; return "Array_uint: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")";
} }
public static bool operator ==(Array_uint x, Array_uint y)
{
return x.Equals(y);
}
public static bool operator !=(Array_uint x, Array_uint y)
{
return !x.Equals(y);
}
public override bool Equals(object obj)
{
if (obj is null || obj is not Array_uint arrObj)
return false;
return arrObj.Pointer == this.Pointer
&& arrObj.Count1 == this.Count1
&& arrObj.Count2 == this.Count2
&& arrObj.Unk1 == this.Unk1;
}
public override int GetHashCode()
{
int hashCode = -1900453823;
hashCode = hashCode * -1521134295 + Pointer.GetHashCode();
hashCode = hashCode * -1521134295 + Count1.GetHashCode();
hashCode = hashCode * -1521134295 + Count2.GetHashCode();
hashCode = hashCode * -1521134295 + Unk1.GetHashCode();
return hashCode;
}
} }
[TC(typeof(EXP))] public struct Array_ushort //16 bytes - pointer for a ushort array [TC(typeof(EXP))] public struct Array_ushort //16 bytes - pointer for a ushort array
{ {
@ -797,6 +887,37 @@ namespace CodeWalker.GameFiles
{ {
return "Array_ushort: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")"; return "Array_ushort: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")";
} }
public static bool operator ==(Array_ushort x, Array_ushort y)
{
return x.Equals(y);
}
public static bool operator !=(Array_ushort x, Array_ushort y)
{
return !x.Equals(y);
}
public override bool Equals(object obj)
{
if (obj is null || obj is not Array_ushort arrObj)
return false;
return arrObj.Pointer == this.Pointer
&& arrObj.Count1 == this.Count1
&& arrObj.Count2 == this.Count2
&& arrObj.Unk1 == this.Unk1;
}
public override int GetHashCode()
{
int hashCode = -1900453823;
hashCode = hashCode * -1521134295 + Pointer.GetHashCode();
hashCode = hashCode * -1521134295 + Count1.GetHashCode();
hashCode = hashCode * -1521134295 + Count2.GetHashCode();
hashCode = hashCode * -1521134295 + Unk1.GetHashCode();
return hashCode;
}
} }
[TC(typeof(EXP))] public struct Array_byte //16 bytes - pointer for a byte array [TC(typeof(EXP))] public struct Array_byte //16 bytes - pointer for a byte array
{ {
@ -836,6 +957,35 @@ namespace CodeWalker.GameFiles
{ {
return "Array_byte: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")"; return "Array_byte: " + PointerDataIndex.ToString() + " (" + Count1.ToString() + "/" + Count2.ToString() + ")";
} }
public override bool Equals(object obj)
{
return obj is Array_byte @byte &&
Pointer == @byte.Pointer &&
Count1 == @byte.Count1 &&
Count2 == @byte.Count2 &&
Unk1 == @byte.Unk1;
}
public override int GetHashCode()
{
int hashCode = -1900453823;
hashCode = hashCode * -1521134295 + Pointer.GetHashCode();
hashCode = hashCode * -1521134295 + Count1.GetHashCode();
hashCode = hashCode * -1521134295 + Count2.GetHashCode();
hashCode = hashCode * -1521134295 + Unk1.GetHashCode();
return hashCode;
}
public static bool operator ==(Array_byte left, Array_byte right)
{
return left.Equals(right);
}
public static bool operator !=(Array_byte left, Array_byte right)
{
return !(left == right);
}
} }
[TC(typeof(EXP))] public struct Array_float //16 bytes - pointer for a float array [TC(typeof(EXP))] public struct Array_float //16 bytes - pointer for a float array
{ {
@ -1118,7 +1268,7 @@ namespace CodeWalker.GameFiles
[TC(typeof(EXP))] public struct MetaHash [TC(typeof(EXP))] public struct MetaHash : IEquatable<MetaHash>
{ {
public uint Hash { get; set; } public uint Hash { get; set; }
@ -1180,7 +1330,7 @@ namespace CodeWalker.GameFiles
return new MetaHash(v); return new MetaHash(v);
} }
public override bool Equals(object obj) public override readonly bool Equals(object obj)
{ {
if (obj == null) return false; if (obj == null) return false;
if (obj is not MetaHash metaHash) return false; if (obj is not MetaHash metaHash) return false;
@ -1188,7 +1338,14 @@ namespace CodeWalker.GameFiles
return metaHash.Hash == Hash; return metaHash.Hash == Hash;
} }
public override int GetHashCode() public readonly bool Equals(MetaHash obj)
{
if (obj == null) return false;
return obj.Hash == Hash;
}
public override readonly int GetHashCode()
{ {
return (int)Hash; return (int)Hash;
} }

View File

@ -153,19 +153,22 @@ namespace CodeWalker.GameFiles
public Array_Vector3 AddPaddedVector3ArrayPtr(Vector4[] items) public Array_Vector3 AddPaddedVector3ArrayPtr(Vector4[] items)
{ {
if ((items == null) || (items.Length == 0)) return new Array_Vector3(); if (items == null || items.Length == 0)
return new Array_Vector3();
var ptr = AddItemArray((MetaName)MetaTypeName.VECTOR4, items); //padded to vec4... var ptr = AddItemArray((MetaName)MetaTypeName.VECTOR4, items); //padded to vec4...
return new Array_Vector3(ptr); return new Array_Vector3(ptr);
} }
public Array_uint AddHashArrayPtr(MetaHash[] items) public Array_uint AddHashArrayPtr(MetaHash[] items)
{ {
if ((items == null) || (items.Length == 0)) return new Array_uint(); if (items == null || items.Length == 0)
return new Array_uint();
var ptr = AddItemArray((MetaName)MetaTypeName.HASH, items); var ptr = AddItemArray((MetaName)MetaTypeName.HASH, items);
return new Array_uint(ptr); return new Array_uint(ptr);
} }
public Array_uint AddUintArrayPtr(uint[] items) public Array_uint AddUintArrayPtr(uint[] items)
{ {
if ((items == null) || (items.Length == 0)) return new Array_uint(); if (items == null || items.Length == 0)
return new Array_uint();
var ptr = AddItemArray((MetaName)MetaTypeName.UINT, items); var ptr = AddItemArray((MetaName)MetaTypeName.UINT, items);
return new Array_uint(ptr); return new Array_uint(ptr);
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -10,7 +11,7 @@ namespace CodeWalker.GameFiles
public static class MetaNames public static class MetaNames
{ {
public static Dictionary<uint, string> stringCache = new Dictionary<uint, string>(); public static ConcurrentDictionary<uint, string> stringCache = new ConcurrentDictionary<uint, string>();
public static bool TryGetString(uint h, out string str) public static bool TryGetString(uint h, out string str)
{ {
if (stringCache.TryGetValue(h, out str)) if (stringCache.TryGetValue(h, out str))
@ -21,10 +22,10 @@ namespace CodeWalker.GameFiles
{ {
str = ((MetaName)h).ToString(); str = ((MetaName)h).ToString();
if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry
stringCache.Add(h, str); stringCache.TryAdd(h, str);
return true; return true;
} }
stringCache.Add(h, str); stringCache.TryAdd(h, str);
str = null; str = null;
return false; return false;
} }

View File

@ -1438,26 +1438,39 @@ namespace CodeWalker.GameFiles
public static byte[] ConvertToBytes<T>(T item) where T : struct public static byte[] ConvertToBytes<T>(T item) where T : struct
{ {
int size = Marshal.SizeOf(typeof(T)); int size = Marshal.SizeOf(typeof(T));
int offset = 0; //int offset = 0;
byte[] arr = new byte[size]; byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); MemoryMarshal.TryWrite(arr.AsSpan(), ref item);
Marshal.StructureToPtr(item, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
offset += size;
return arr; return arr;
//IntPtr ptr = Marshal.AllocHGlobal(size);
//Marshal.StructureToPtr(item, ptr, true);
//Marshal.Copy(ptr, arr, 0, size);
//Marshal.FreeHGlobal(ptr);
//offset += size;
//return arr;
} }
public static byte[] ConvertArrayToBytes<T>(params T[] items) where T : struct public static byte[] ConvertArrayToBytes<T>(params T[] items) where T : struct
{ {
if (items == null) return null; if (items == null) return null;
var size = Marshal.SizeOf(typeof(T)) * items.Length; return MemoryMarshal.AsBytes(items.AsSpan()).ToArray();
var b = new byte[size];
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned); //var size = Marshal.SizeOf(typeof(T)) * items.Length;
var h = handle.AddrOfPinnedObject(); //var b = new byte[size];
Marshal.Copy(h, b, 0, size); //GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
handle.Free(); //var h = handle.AddrOfPinnedObject();
return b; //Marshal.Copy(h, b, 0, size);
//handle.Free();
//return b;
//var size = Marshal.SizeOf(typeof(T)) * items.Length;
//var b = new byte[size];
//return MemoryMarshal.AsBytes<T>(items).ToArray();
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(h, b, 0, size);
//handle.Free();
//return b;
//int size = Marshal.SizeOf(typeof(T)); //int size = Marshal.SizeOf(typeof(T));
@ -1502,20 +1515,33 @@ namespace CodeWalker.GameFiles
} }
public static Span<T> ConvertDataArray<T>(byte[] data, int offset, int count) where T : struct public static Span<T> ConvertDataArray<T>(byte[] data, int offset, int count) where T : struct
{ {
//T[] items = new T[count];
//int itemsize = Marshal.SizeOf(typeof(T));
////for (int i = 0; i < count; i++)
////{
//// int off = offset + i * itemsize;
//// items[i] = ConvertData<T>(data, off);
////}
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(data, offset, h, itemsize * count);
//handle.Free();
//return items;
return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T)))); return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T))));
T[] items = new T[count]; //T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T)); //int itemsize = Marshal.SizeOf(typeof(T));
//for (int i = 0; i < count; i++) //for (int i = 0; i < count; i++)
//{ //{
// int off = offset + i * itemsize; // int off = offset + i * itemsize;
// items[i] = ConvertData<T>(data, off); // items[i] = ConvertData<T>(data, off);
//} //}
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned); //GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject(); //var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, offset, h, itemsize * count); //Marshal.Copy(data, offset, h, itemsize * count);
handle.Free(); //handle.Free();
return items; //return items;
} }
public static T[] ConvertDataArray<T>(Meta meta, MetaName name, Array_StructurePointer array) where T : struct public static T[] ConvertDataArray<T>(Meta meta, MetaName name, Array_StructurePointer array) where T : struct
{ {
@ -1583,13 +1609,13 @@ namespace CodeWalker.GameFiles
} //don't try to read too many items.. } //don't try to read too many items..
ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi)); //ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi));
//for (int i = 0; i < itemcount; i++) for (int i = 0; i < itemcount; i++)
//{ {
// int offset = (itemoffset + i) * itemsize; int offset = (itemoffset + i) * itemsize;
// int index = curi + i; int index = curi + i;
// items[index] = ConvertData<T>(ptrblock.Data, offset); items[index] = ConvertData<T>(ptrblock.Data, offset);
//} }
itemoffset = 0; //start at beginning of next block.. itemoffset = 0; //start at beginning of next block..
curi += itemcount; curi += itemcount;
itemsleft -= itemcount; itemsleft -= itemcount;
@ -2500,7 +2526,7 @@ namespace CodeWalker.GameFiles
{ {
_Data = data; _Data = data;
RoomName = MetaTypes.GetString(meta, _Data.name); RoomName = MetaTypes.GetString(meta, _Data.name);
AttachedObjects = MetaTypes.GetUintArray(meta, _Data.attachedObjects); AttachedObjects = MetaTypes.GetUintArray(meta, _Data.attachedObjects) ?? Array.Empty<uint>();
} }
public override void Load(Meta meta, MetaPOINTER ptr) public override void Load(Meta meta, MetaPOINTER ptr)
@ -2521,14 +2547,7 @@ namespace CodeWalker.GameFiles
_Data.name = new CharPointer(); _Data.name = new CharPointer();
} }
if (AttachedObjects != null)
{
_Data.attachedObjects = mb.AddUintArrayPtr(AttachedObjects); _Data.attachedObjects = mb.AddUintArrayPtr(AttachedObjects);
}
else
{
_Data.attachedObjects = new Array_uint();
}
mb.AddStructureInfo(MetaName.CMloRoomDef); mb.AddStructureInfo(MetaName.CMloRoomDef);
return mb.AddItemPtr(MetaName.CMloRoomDef, _Data); return mb.AddItemPtr(MetaName.CMloRoomDef, _Data);
@ -4101,6 +4120,61 @@ namespace CodeWalker.GameFiles
MaxCellY = (int)Math.Floor(gv.Y); MaxCellY = (int)Math.Floor(gv.Y);
} }
} }
public static bool operator ==(rage__spdGrid2D x, rage__spdGrid2D y)
{
return x.Equals(y);
}
public static bool operator !=(rage__spdGrid2D x, rage__spdGrid2D y)
{
return x.Equals(y);
}
public override bool Equals(object obj)
{
if (obj is null || obj is not rage__spdGrid2D arrObj)
return false;
return arrObj.MaxCellX == this.MaxCellX
&& arrObj.MaxCellY == this.MaxCellY
&& arrObj.MinCellX == this.MinCellX
&& arrObj.MinCellY == this.MinCellY
&& arrObj.CellDimX == this.CellDimX
&& arrObj.CellDimY == this.CellDimY
&& arrObj.Unused0 == this.Unused0
&& arrObj.Unused1 == this.Unused1
&& arrObj.Unused2 == this.Unused2
&& arrObj.Unused3 == this.Unused3
&& arrObj.Unused4 == this.Unused4
&& arrObj.Unused5 == this.Unused5
&& arrObj.Unused6 == this.Unused6
&& arrObj.Unused7 == this.Unused7
&& arrObj.Unused8 == this.Unused8
&& arrObj.Unused9 == this.Unused9;
}
public override int GetHashCode()
{
int hashCode = 418850833;
hashCode = hashCode * -1521134295 + Unused0.GetHashCode();
hashCode = hashCode * -1521134295 + Unused1.GetHashCode();
hashCode = hashCode * -1521134295 + Unused2.GetHashCode();
hashCode = hashCode * -1521134295 + MinCellX.GetHashCode();
hashCode = hashCode * -1521134295 + MaxCellX.GetHashCode();
hashCode = hashCode * -1521134295 + MinCellY.GetHashCode();
hashCode = hashCode * -1521134295 + MaxCellY.GetHashCode();
hashCode = hashCode * -1521134295 + Unused3.GetHashCode();
hashCode = hashCode * -1521134295 + Unused4.GetHashCode();
hashCode = hashCode * -1521134295 + Unused5.GetHashCode();
hashCode = hashCode * -1521134295 + Unused6.GetHashCode();
hashCode = hashCode * -1521134295 + CellDimX.GetHashCode();
hashCode = hashCode * -1521134295 + CellDimY.GetHashCode();
hashCode = hashCode * -1521134295 + Unused7.GetHashCode();
hashCode = hashCode * -1521134295 + Unused8.GetHashCode();
hashCode = hashCode * -1521134295 + Unused9.GetHashCode();
return hashCode;
}
} }
[TC(typeof(EXP))] public struct rage__spdAABB //32 bytes, Key:1158138379 [TC(typeof(EXP))] public struct rage__spdAABB //32 bytes, Key:1158138379
@ -4150,6 +4224,57 @@ namespace CodeWalker.GameFiles
public Array_ushort Unk_3844724227 { get; set; } //248 248: Array: 0: 3844724227 {0: UnsignedShort: 0: 256} public Array_ushort Unk_3844724227 { get; set; } //248 248: Array: 0: 3844724227 {0: UnsignedShort: 0: 256}
public Array_Structure Clusters { get; set; } //264 264: Array: 0: Clusters//3587988394 {0: Structure: CScenarioPointCluster//750308016: 256} public Array_Structure Clusters { get; set; } //264 264: Array: 0: Clusters//3587988394 {0: Structure: CScenarioPointCluster//750308016: 256}
public CScenarioPointLookUps LookUps { get; set; } //280 280: Structure: CScenarioPointLookUps//3019621867: LookUps//1097626284 public CScenarioPointLookUps LookUps { get; set; } //280 280: Structure: CScenarioPointLookUps//3019621867: LookUps//1097626284
public override bool Equals(object obj)
{
return obj is CScenarioPointRegion region &&
VersionNumber == region.VersionNumber &&
Unused0 == region.Unused0 &&
EqualityComparer<CScenarioPointContainer>.Default.Equals(Points, region.Points) &&
Unused1 == region.Unused1 &&
Unused2 == region.Unused2 &&
Unused3 == region.Unused3 &&
Unused4 == region.Unused4 &&
EqualityComparer<Array_Structure>.Default.Equals(EntityOverrides, region.EntityOverrides) &&
Unused5 == region.Unused5 &&
Unused6 == region.Unused6 &&
EqualityComparer<CScenarioChainingGraph>.Default.Equals(ChainingGraph, region.ChainingGraph) &&
EqualityComparer<rage__spdGrid2D>.Default.Equals(AccelGrid, region.AccelGrid) &&
EqualityComparer<Array_ushort>.Default.Equals(Unk_3844724227, region.Unk_3844724227) &&
EqualityComparer<Array_Structure>.Default.Equals(Clusters, region.Clusters) &&
EqualityComparer<CScenarioPointLookUps>.Default.Equals(LookUps, region.LookUps);
}
public override int GetHashCode()
{
int hashCode = 1436693857;
hashCode = hashCode * -1521134295 + VersionNumber.GetHashCode();
hashCode = hashCode * -1521134295 + Unused0.GetHashCode();
hashCode = hashCode * -1521134295 + Points.GetHashCode();
hashCode = hashCode * -1521134295 + Unused1.GetHashCode();
hashCode = hashCode * -1521134295 + Unused2.GetHashCode();
hashCode = hashCode * -1521134295 + Unused3.GetHashCode();
hashCode = hashCode * -1521134295 + Unused4.GetHashCode();
hashCode = hashCode * -1521134295 + EntityOverrides.GetHashCode();
hashCode = hashCode * -1521134295 + Unused5.GetHashCode();
hashCode = hashCode * -1521134295 + Unused6.GetHashCode();
hashCode = hashCode * -1521134295 + ChainingGraph.GetHashCode();
hashCode = hashCode * -1521134295 + AccelGrid.GetHashCode();
hashCode = hashCode * -1521134295 + Unk_3844724227.GetHashCode();
hashCode = hashCode * -1521134295 + Clusters.GetHashCode();
hashCode = hashCode * -1521134295 + LookUps.GetHashCode();
return hashCode;
}
public static bool operator ==(CScenarioPointRegion left, CScenarioPointRegion right)
{
return left.Equals(right);
}
public static bool operator !=(CScenarioPointRegion left, CScenarioPointRegion right)
{
return !(left == right);
}
} }
[TC(typeof(EXP))] public class MCScenarioPointRegion : MetaWrapper [TC(typeof(EXP))] public class MCScenarioPointRegion : MetaWrapper
{ {
@ -4531,10 +4656,43 @@ namespace CodeWalker.GameFiles
public uint Unused2 { get; set; }//40 public uint Unused2 { get; set; }//40
public uint Unused3 { get; set; }//44 public uint Unused3 { get; set; }//44
public override bool Equals(object obj)
{
return obj is CScenarioPointContainer container &&
EqualityComparer<Array_Structure>.Default.Equals(LoadSavePoints, container.LoadSavePoints) &&
EqualityComparer<Array_Structure>.Default.Equals(MyPoints, container.MyPoints) &&
Unused0 == container.Unused0 &&
Unused1 == container.Unused1 &&
Unused2 == container.Unused2 &&
Unused3 == container.Unused3;
}
public override int GetHashCode()
{
int hashCode = 746587205;
hashCode = hashCode * -1521134295 + LoadSavePoints.GetHashCode();
hashCode = hashCode * -1521134295 + MyPoints.GetHashCode();
hashCode = hashCode * -1521134295 + Unused0.GetHashCode();
hashCode = hashCode * -1521134295 + Unused1.GetHashCode();
hashCode = hashCode * -1521134295 + Unused2.GetHashCode();
hashCode = hashCode * -1521134295 + Unused3.GetHashCode();
return hashCode;
}
public override string ToString() public override string ToString()
{ {
return LoadSavePoints.Count1.ToString() + " LoadSavePoints, " + MyPoints.Count1.ToString() + " MyPoints"; return LoadSavePoints.Count1.ToString() + " LoadSavePoints, " + MyPoints.Count1.ToString() + " MyPoints";
} }
public static bool operator ==(CScenarioPointContainer left, CScenarioPointContainer right)
{
return left.Equals(right);
}
public static bool operator !=(CScenarioPointContainer left, CScenarioPointContainer right)
{
return !(left == right);
}
} }
[TC(typeof(EXP))] public class MCScenarioPointContainer : MetaWrapper [TC(typeof(EXP))] public class MCScenarioPointContainer : MetaWrapper
{ {
@ -5032,6 +5190,45 @@ namespace CodeWalker.GameFiles
{ {
return Nodes.Count1.ToString() + " Nodes, " + Edges.Count1.ToString() + " Edges, " + Chains.Count1.ToString() + " Chains"; return Nodes.Count1.ToString() + " Nodes, " + Edges.Count1.ToString() + " Edges, " + Chains.Count1.ToString() + " Chains";
} }
public static bool operator !=(CScenarioChainingGraph x, CScenarioChainingGraph y)
{
return !x.Equals(y);
}
public static bool operator ==(CScenarioChainingGraph x, CScenarioChainingGraph y)
{
return x.Equals(y);
}
public override bool Equals(object obj)
{
if (obj is null || obj is not CScenarioChainingGraph arrObj)
return false;
return arrObj.Nodes == this.Nodes
&& arrObj.Edges == this.Edges
&& arrObj.Chains == this.Chains;
}
public override int GetHashCode()
{
int hashCode = -1851298727;
hashCode = hashCode * -1521134295 + Nodes.GetHashCode();
hashCode = hashCode * -1521134295 + Edges.GetHashCode();
hashCode = hashCode * -1521134295 + Chains.GetHashCode();
hashCode = hashCode * -1521134295 + Unused0.GetHashCode();
hashCode = hashCode * -1521134295 + Unused1.GetHashCode();
hashCode = hashCode * -1521134295 + Unused2.GetHashCode();
hashCode = hashCode * -1521134295 + Unused3.GetHashCode();
hashCode = hashCode * -1521134295 + Unused4.GetHashCode();
hashCode = hashCode * -1521134295 + Unused5.GetHashCode();
hashCode = hashCode * -1521134295 + Unused6.GetHashCode();
hashCode = hashCode * -1521134295 + Unused7.GetHashCode();
hashCode = hashCode * -1521134295 + Unused8.GetHashCode();
hashCode = hashCode * -1521134295 + Unused9.GetHashCode();
return hashCode;
}
} }
[TC(typeof(EXP))] public class MCScenarioChainingGraph : MetaWrapper [TC(typeof(EXP))] public class MCScenarioChainingGraph : MetaWrapper
{ {
@ -5731,6 +5928,39 @@ namespace CodeWalker.GameFiles
{ {
return "CScenarioPointLookUps"; return "CScenarioPointLookUps";
} }
public override bool Equals(object obj)
{
return obj is CScenarioPointLookUps ups &&
EqualityComparer<Array_uint>.Default.Equals(TypeNames, ups.TypeNames) &&
EqualityComparer<Array_uint>.Default.Equals(PedModelSetNames, ups.PedModelSetNames) &&
EqualityComparer<Array_uint>.Default.Equals(VehicleModelSetNames, ups.VehicleModelSetNames) &&
EqualityComparer<Array_uint>.Default.Equals(GroupNames, ups.GroupNames) &&
EqualityComparer<Array_uint>.Default.Equals(InteriorNames, ups.InteriorNames) &&
EqualityComparer<Array_uint>.Default.Equals(RequiredIMapNames, ups.RequiredIMapNames);
}
public override int GetHashCode()
{
int hashCode = -153113894;
hashCode = hashCode * -1521134295 + TypeNames.GetHashCode();
hashCode = hashCode * -1521134295 + PedModelSetNames.GetHashCode();
hashCode = hashCode * -1521134295 + VehicleModelSetNames.GetHashCode();
hashCode = hashCode * -1521134295 + GroupNames.GetHashCode();
hashCode = hashCode * -1521134295 + InteriorNames.GetHashCode();
hashCode = hashCode * -1521134295 + RequiredIMapNames.GetHashCode();
return hashCode;
}
public static bool operator ==(CScenarioPointLookUps left, CScenarioPointLookUps right)
{
return left.Equals(right);
}
public static bool operator !=(CScenarioPointLookUps left, CScenarioPointLookUps right)
{
return !(left == right);
}
} }
[TC(typeof(EXP))] public class MCScenarioPointLookUps : MetaWrapper [TC(typeof(EXP))] public class MCScenarioPointLookUps : MetaWrapper
{ {

View File

@ -1,4 +1,6 @@
using SharpDX; 
using SharpDX;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -15,144 +17,143 @@ namespace CodeWalker.GameFiles
public static string GetXml(RpfFileEntry e, byte[] data, out string filename, string outputfolder = "") public static string GetXml(RpfFileEntry e, byte[] data, out string filename, string outputfolder = "")
{ {
var fn = e.Name; var fn = e.Name;
var fnl = fn.ToLowerInvariant();
if (!string.IsNullOrEmpty(outputfolder)) if (!string.IsNullOrEmpty(outputfolder))
{ {
outputfolder = Path.Combine(outputfolder, e.GetShortName()); outputfolder = Path.Combine(outputfolder, e.ShortName);
} }
if (fnl.EndsWith(".ymt")) if (fn.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
{ {
YmtFile ymt = RpfFile.GetFile<YmtFile>(e, data); YmtFile ymt = RpfFile.GetFile<YmtFile>(e, data);
return GetXml(ymt, out filename); return GetXml(ymt, out filename);
} }
else if (fnl.EndsWith(".ymf")) else if (fn.EndsWith(".ymf", StringComparison.OrdinalIgnoreCase))
{ {
YmfFile ymf = RpfFile.GetFile<YmfFile>(e, data); YmfFile ymf = RpfFile.GetFile<YmfFile>(e, data);
return GetXml(ymf, out filename); return GetXml(ymf, out filename);
} }
else if (fnl.EndsWith(".ymap")) else if (fn.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase))
{ {
YmapFile ymap = RpfFile.GetFile<YmapFile>(e, data); YmapFile ymap = RpfFile.GetFile<YmapFile>(e, data);
return GetXml(ymap, out filename); return GetXml(ymap, out filename);
} }
else if (fnl.EndsWith(".ytyp")) else if (fn.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase))
{ {
YtypFile ytyp = RpfFile.GetFile<YtypFile>(e, data); YtypFile ytyp = RpfFile.GetFile<YtypFile>(e, data);
return GetXml(ytyp, out filename); return GetXml(ytyp, out filename);
} }
else if (fnl.EndsWith(".pso")) else if (fn.EndsWith(".pso", StringComparison.OrdinalIgnoreCase))
{ {
JPsoFile pso = RpfFile.GetFile<JPsoFile>(e, data); JPsoFile pso = RpfFile.GetFile<JPsoFile>(e, data);
return GetXml(pso, out filename); return GetXml(pso, out filename);
} }
else if (fnl.EndsWith(".cut")) else if (fn.EndsWith(".cut", StringComparison.OrdinalIgnoreCase))
{ {
CutFile cut = RpfFile.GetFile<CutFile>(e, data); CutFile cut = RpfFile.GetFile<CutFile>(e, data);
return GetXml(cut, out filename); return GetXml(cut, out filename);
} }
else if (fnl.EndsWith(".rel")) else if (fn.EndsWith(".rel", StringComparison.OrdinalIgnoreCase))
{ {
RelFile rel = RpfFile.GetFile<RelFile>(e, data); RelFile rel = RpfFile.GetFile<RelFile>(e, data);
return GetXml(rel, out filename); return GetXml(rel, out filename);
} }
else if (fnl.EndsWith(".ynd")) else if (fn.EndsWith(".ynd", StringComparison.OrdinalIgnoreCase))
{ {
YndFile ynd = RpfFile.GetFile<YndFile>(e, data); YndFile ynd = RpfFile.GetFile<YndFile>(e, data);
return GetXml(ynd, out filename); return GetXml(ynd, out filename);
} }
else if (fnl.EndsWith(".ynv")) else if (fn.EndsWith(".ynv", StringComparison.OrdinalIgnoreCase))
{ {
YnvFile ynv = RpfFile.GetFile<YnvFile>(e, data); YnvFile ynv = RpfFile.GetFile<YnvFile>(e, data);
return GetXml(ynv, out filename); return GetXml(ynv, out filename);
} }
else if (fnl.EndsWith(".ycd")) else if (fn.EndsWith(".ycd", StringComparison.OrdinalIgnoreCase))
{ {
YcdFile ycd = RpfFile.GetFile<YcdFile>(e, data); YcdFile ycd = RpfFile.GetFile<YcdFile>(e, data);
return GetXml(ycd, out filename); return GetXml(ycd, out filename);
} }
else if (fnl.EndsWith(".ybn")) else if (fn.EndsWith(".ybn", StringComparison.OrdinalIgnoreCase))
{ {
YbnFile ybn = RpfFile.GetFile<YbnFile>(e, data); YbnFile ybn = RpfFile.GetFile<YbnFile>(e, data);
return GetXml(ybn, out filename); return GetXml(ybn, out filename);
} }
else if (fnl.EndsWith(".ytd")) else if (fn.EndsWith(".ytd", StringComparison.OrdinalIgnoreCase))
{ {
YtdFile ytd = RpfFile.GetFile<YtdFile>(e, data); YtdFile ytd = RpfFile.GetFile<YtdFile>(e, data);
return GetXml(ytd, out filename, outputfolder); return GetXml(ytd, out filename, outputfolder);
} }
else if (fnl.EndsWith(".ydr")) else if (fn.EndsWith(".ydr", StringComparison.OrdinalIgnoreCase))
{ {
YdrFile ydr = RpfFile.GetFile<YdrFile>(e, data); YdrFile ydr = RpfFile.GetFile<YdrFile>(e, data);
return GetXml(ydr, out filename, outputfolder); return GetXml(ydr, out filename, outputfolder);
} }
else if (fnl.EndsWith(".ydd")) else if (fn.EndsWith(".ydd", StringComparison.OrdinalIgnoreCase))
{ {
YddFile ydd = RpfFile.GetFile<YddFile>(e, data); YddFile ydd = RpfFile.GetFile<YddFile>(e, data);
return GetXml(ydd, out filename, outputfolder); return GetXml(ydd, out filename, outputfolder);
} }
else if (fnl.EndsWith(".yft")) else if (fn.EndsWith(".yft", StringComparison.OrdinalIgnoreCase))
{ {
YftFile yft = RpfFile.GetFile<YftFile>(e, data); YftFile yft = RpfFile.GetFile<YftFile>(e, data);
return GetXml(yft, out filename, outputfolder); return GetXml(yft, out filename, outputfolder);
} }
else if (fnl.EndsWith(".ypt")) else if (fn.EndsWith(".ypt", StringComparison.OrdinalIgnoreCase))
{ {
YptFile ypt = RpfFile.GetFile<YptFile>(e, data); YptFile ypt = RpfFile.GetFile<YptFile>(e, data);
return GetXml(ypt, out filename, outputfolder); return GetXml(ypt, out filename, outputfolder);
} }
else if (fnl.EndsWith(".yld")) else if (fn.EndsWith(".yld", StringComparison.OrdinalIgnoreCase))
{ {
YldFile yld = RpfFile.GetFile<YldFile>(e, data); YldFile yld = RpfFile.GetFile<YldFile>(e, data);
return GetXml(yld, out filename); return GetXml(yld, out filename);
} }
else if (fnl.EndsWith(".yed")) else if (fn.EndsWith(".yed", StringComparison.OrdinalIgnoreCase))
{ {
YedFile yed = RpfFile.GetFile<YedFile>(e, data); YedFile yed = RpfFile.GetFile<YedFile>(e, data);
return GetXml(yed, out filename); return GetXml(yed, out filename);
} }
else if (fnl.EndsWith(".ywr")) else if (fn.EndsWith(".ywr", StringComparison.OrdinalIgnoreCase))
{ {
YwrFile ywr = RpfFile.GetFile<YwrFile>(e, data); YwrFile ywr = RpfFile.GetFile<YwrFile>(e, data);
return GetXml(ywr, out filename); return GetXml(ywr, out filename);
} }
else if (fnl.EndsWith(".yvr")) else if (fn.EndsWith(".yvr", StringComparison.OrdinalIgnoreCase))
{ {
YvrFile yvr = RpfFile.GetFile<YvrFile>(e, data); YvrFile yvr = RpfFile.GetFile<YvrFile>(e, data);
return GetXml(yvr, out filename); return GetXml(yvr, out filename);
} }
else if (fnl.EndsWith(".ypdb")) else if (fn.EndsWith(".ypdb", StringComparison.OrdinalIgnoreCase))
{ {
YpdbFile ypdb = RpfFile.GetFile<YpdbFile>(e, data); YpdbFile ypdb = RpfFile.GetFile<YpdbFile>(e, data);
return GetXml(ypdb, out filename); return GetXml(ypdb, out filename);
} }
else if (fnl.EndsWith(".yfd")) else if (fn.EndsWith(".yfd", StringComparison.OrdinalIgnoreCase))
{ {
YfdFile yfd = RpfFile.GetFile<YfdFile>(e, data); YfdFile yfd = RpfFile.GetFile<YfdFile>(e, data);
return GetXml(yfd, out filename); return GetXml(yfd, out filename);
} }
else if (fnl.EndsWith(".awc")) else if (fn.EndsWith(".awc", StringComparison.OrdinalIgnoreCase))
{ {
AwcFile awc = RpfFile.GetFile<AwcFile>(e, data); AwcFile awc = RpfFile.GetFile<AwcFile>(e, data);
return GetXml(awc, out filename, outputfolder); return GetXml(awc, out filename, outputfolder);
} }
else if (fnl.EndsWith(".fxc")) else if (fn.EndsWith(".fxc", StringComparison.OrdinalIgnoreCase))
{ {
FxcFile fxc = RpfFile.GetFile<FxcFile>(e, data); FxcFile fxc = RpfFile.GetFile<FxcFile>(e, data);
return GetXml(fxc, out filename, outputfolder); return GetXml(fxc, out filename, outputfolder);
} }
else if (fnl.EndsWith("cache_y.dat")) else if (fn.EndsWith("cache_y.dat", StringComparison.OrdinalIgnoreCase))
{ {
CacheDatFile cdf = RpfFile.GetFile<CacheDatFile>(e, data); CacheDatFile cdf = RpfFile.GetFile<CacheDatFile>(e, data);
return GetXml(cdf, out filename, outputfolder); return GetXml(cdf, out filename, outputfolder);
} }
else if (fnl.EndsWith(".dat") && fnl.StartsWith("heightmap")) else if (fn.EndsWith(".dat", StringComparison.OrdinalIgnoreCase) && fn.StartsWith("heightmap", StringComparison.OrdinalIgnoreCase))
{ {
HeightmapFile hmf = RpfFile.GetFile<HeightmapFile>(e, data); HeightmapFile hmf = RpfFile.GetFile<HeightmapFile>(e, data);
return GetXml(hmf, out filename, outputfolder); return GetXml(hmf, out filename, outputfolder);
} }
else if (fnl.EndsWith(".mrf")) else if (fn.EndsWith(".mrf", StringComparison.OrdinalIgnoreCase))
{ {
MrfFile mrf = RpfFile.GetFile<MrfFile>(e, data); MrfFile mrf = RpfFile.GetFile<MrfFile>(e, data);
return GetXml(mrf, out filename, outputfolder); return GetXml(mrf, out filename, outputfolder);
@ -1653,10 +1654,8 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < pso.SchemaSection.Entries.Length; i++) for (int i = 0; i < pso.SchemaSection.Entries.Length; i++)
{ {
var entry = pso.SchemaSection.Entries[i]; var entry = pso.SchemaSection.Entries[i];
var enuminfo = entry as PsoEnumInfo;
var structinfo = entry as PsoStructureInfo;
if (enuminfo != null) if (entry is PsoEnumInfo enuminfo)
{ {
if (!EnumDict.ContainsKey(enuminfo.IndexInfo.NameHash)) if (!EnumDict.ContainsKey(enuminfo.IndexInfo.NameHash))
{ {
@ -1670,7 +1669,7 @@ namespace CodeWalker.GameFiles
//} //}
} }
} }
else if (structinfo != null) else if (entry is PsoStructureInfo structinfo)
{ {
if (!StructDict.ContainsKey(structinfo.IndexInfo.NameHash)) if (!StructDict.ContainsKey(structinfo.IndexInfo.NameHash))
{ {
@ -1691,14 +1690,12 @@ namespace CodeWalker.GameFiles
public PsoStructureInfo GetStructureInfo(MetaName name) public PsoStructureInfo GetStructureInfo(MetaName name)
{ {
PsoStructureInfo i = null; StructDict.TryGetValue(name, out PsoStructureInfo i);
StructDict.TryGetValue(name, out i);
return i; return i;
} }
public PsoEnumInfo GetEnumInfo(MetaName name) public PsoEnumInfo GetEnumInfo(MetaName name)
{ {
PsoEnumInfo i = null; EnumDict.TryGetValue(name, out PsoEnumInfo i);
EnumDict.TryGetValue(name, out i);
return i; return i;
} }
@ -2004,7 +2001,7 @@ namespace CodeWalker.GameFiles
var aind = ind + 1; var aind = ind + 1;
if (itemCount > 0) if (itemCount > 0)
{ {
OpenTag(sb, ind, arrTag); OpenTag(sb, ind, arrTag, metaName: typeName);
for (int n = 0; n < itemCount; n++) for (int n = 0; n < itemCount; n++)
{ {
Indent(sb, aind); Indent(sb, aind);

View File

@ -337,16 +337,26 @@ namespace CodeWalker.GameFiles
var reader = new DataReader(stream, Endianess.BigEndian); var reader = new DataReader(stream, Endianess.BigEndian);
var identInt = reader.ReadUInt32(); var identInt = reader.ReadUInt32();
stream.Position = 0; stream.Position = 0;
return ((identInt ) == 1347635534); //"PSIN" return IsPSO(identInt); //"PSIN"
} }
public static bool IsPSO(uint identInt)
{
return identInt == 1347635534;
}
public static bool IsRBF(Stream stream) public static bool IsRBF(Stream stream)
{ {
var reader = new DataReader(stream, Endianess.BigEndian); var reader = new DataReader(stream, Endianess.BigEndian);
var identInt = reader.ReadUInt32(); var identInt = reader.ReadUInt32();
stream.Position = 0; stream.Position = 0;
return ((identInt & 0xFFFFFF00) == 0x52424600); return IsRBF(identInt);
}
public static bool IsRBF(uint identInt)
{
return (identInt & 0xFFFFFF00) == 0x52424600;
} }
} }
@ -871,15 +881,15 @@ namespace CodeWalker.GameFiles
foreach (var str in strs) foreach (var str in strs)
{ {
JenkIndex.Ensure(str); JenkIndex.Ensure(str);
JenkIndex.Ensure(str.ToLowerInvariant()); JenkIndex.EnsureLower(str);
} }
Strings = strs.ToArray(); Strings = strs.ToArray();
} }
public void Write(DataWriter writer) public void Write(DataWriter writer)
{ {
var strStream = new MemoryStream(); using var strStream = new MemoryStream();
var strWriter = new DataWriter(strStream, Endianess.BigEndian); using var strWriter = new DataWriter(strStream, Endianess.BigEndian);
foreach (var str in Strings) foreach (var str in Strings)
{ {
strWriter.Write(str); strWriter.Write(str);
@ -926,7 +936,7 @@ namespace CodeWalker.GameFiles
foreach (var str in strs) foreach (var str in strs)
{ {
JenkIndex.Ensure(str); JenkIndex.Ensure(str);
JenkIndex.Ensure(str.ToLowerInvariant()); JenkIndex.EnsureLower(str);
} }
Strings = strs.ToArray(); Strings = strs.ToArray();
} }

View File

@ -15819,46 +15819,52 @@ namespace CodeWalker.GameFiles
public static T ConvertDataRaw<T>(byte[] data) where T : struct public static T ConvertDataRaw<T>(byte[] data) where T : struct
{ {
MemoryMarshal.TryRead<T>(data.AsSpan(), out T value); GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
return value; var r = Marshal.PtrToStructure<T>(h);
handle.Free();
return r;
} }
public static T ConvertDataRawOld<T>(byte[] data, int offset) where T : struct
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset);
handle.Free();
return r;
}
public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct
{ {
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value); MemoryMarshal.TryRead<T>(data.AsSpan(offset), out var value);
return value; return value;
//return MemoryMarshal.GetReference<T>(data.AsSpan(offset));
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//return r;
} }
public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd
{ {
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value); GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
value.SwapEnd(); var r = Marshal.PtrToStructure<T>(h + offset);
handle.Free();
return value; r.SwapEnd();
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); return r;
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//r.SwapEnd();
//return r;
} }
public static Span<T> ConvertDataArrayRaw<T>(byte[] data, int offset, int count) where T : struct public static Span<T> ConvertDataArrayRaw<T>(byte[] data, int offset, int count) where T : struct
{ {
return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T)))); T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T));
//for (int i = 0; i < count; i++)
//{
// int off = offset + i * itemsize;
// items[i] = ConvertDataRaw<T>(data, off);
//}
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned); GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject(); var h = handle.AddrOfPinnedObject();
//Marshal.Copy(data, offset, h, itemsize * count); Marshal.Copy(data, offset, h, itemsize * count);
//handle.Free(); handle.Free();
//return items; return items;
} }

View File

@ -108,8 +108,8 @@ namespace CodeWalker.GameFiles
if (descriptorIndex == descriptors.Count) // new descriptor + data if (descriptorIndex == descriptors.Count) // new descriptor + data
{ {
var nameLength = reader.ReadInt16(); var nameLength = reader.ReadInt16();
var nameBytes = reader.ReadBytes(nameLength); var name = reader.ReadStringLength(nameLength);
var name = Encoding.ASCII.GetString(nameBytes); //var name = Encoding.ASCII.GetString(nameBytes);
var descriptor = new RbfEntryDescription(); var descriptor = new RbfEntryDescription();
descriptor.Name = name; descriptor.Name = name;
@ -203,8 +203,7 @@ namespace CodeWalker.GameFiles
case 0x60: case 0x60:
{ {
var valueLength = reader.ReadInt16(); var valueLength = reader.ReadInt16();
var valueBytes = reader.ReadBytes(valueLength); var value = reader.ReadStringLength(valueLength);
var value = Encoding.ASCII.GetString(valueBytes);
var stringValue = new RbfString(); var stringValue = new RbfString();
stringValue.Name = descriptor.Name; stringValue.Name = descriptor.Name;
stringValue.Value = value; stringValue.Value = value;

View File

@ -5044,8 +5044,8 @@ namespace CodeWalker.GameFiles
var line = lines[i]; var line = lines[i];
if (line[0] == '#') continue; if (line[0] == '#') continue;
if (line.StartsWith(startLine)) continue; if (line.StartsWith(startLine, StringComparison.OrdinalIgnoreCase)) continue;
if (line.StartsWith(endLine)) break; if (line.StartsWith(endLine, StringComparison.OrdinalIgnoreCase)) break;
string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);

View File

@ -4719,6 +4719,8 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableBase : ResourceFileBase [TypeConverter(typeof(ExpandableObjectConverter))] public class DrawableBase : ResourceFileBase
{ {
private DrawableModel[] allModels;
public override long BlockLength public override long BlockLength
{ {
get { return 168; } get { return 168; }
@ -4800,7 +4802,17 @@ namespace CodeWalker.GameFiles
public DrawableModelsBlock DrawableModels { get; set; } public DrawableModelsBlock DrawableModels { get; set; }
public DrawableModel[] AllModels { get; set; } public DrawableModel[] AllModels {
get
{
if (allModels is null)
{
BuildAllModels();
}
return allModels;
}
set => allModels = value;
}
public Dictionary<ulong, VertexDeclaration> VertexDecls { get; set; } public Dictionary<ulong, VertexDeclaration> VertexDecls { get; set; }
public object Owner { get; set; } public object Owner { get; set; }
@ -4867,7 +4879,7 @@ namespace CodeWalker.GameFiles
this.DrawableModels = reader.ReadBlockAt<DrawableModelsBlock>((DrawableModelsPointer == 0) ? DrawableModelsHighPointer : DrawableModelsPointer, this); this.DrawableModels = reader.ReadBlockAt<DrawableModelsBlock>((DrawableModelsPointer == 0) ? DrawableModelsHighPointer : DrawableModelsPointer, this);
BuildAllModels(); //BuildAllModels();
BuildVertexDecls(); BuildVertexDecls();
AssignGeometryShaders(ShaderGroup); AssignGeometryShaders(ShaderGroup);
@ -5185,7 +5197,7 @@ namespace CodeWalker.GameFiles
} }
BuildRenderMasks(); BuildRenderMasks();
BuildAllModels(); //BuildAllModels();
BuildVertexDecls(); BuildVertexDecls();
FileVFT = 1079456120; FileVFT = 1079456120;
@ -5236,13 +5248,54 @@ namespace CodeWalker.GameFiles
public void BuildAllModels() public void BuildAllModels()
{ {
var allModels = new List<DrawableModel>(); if (DrawableModels is null)
if (DrawableModels?.High != null) allModels.AddRange(DrawableModels.High); {
if (DrawableModels?.Med != null) allModels.AddRange(DrawableModels.Med); allModels = Array.Empty<DrawableModel>();
if (DrawableModels?.Low != null) allModels.AddRange(DrawableModels.Low); return;
if (DrawableModels?.VLow != null) allModels.AddRange(DrawableModels.VLow); }
if (DrawableModels?.Extra != null) allModels.AddRange(DrawableModels.Extra); var length = (DrawableModels.High?.Length ?? 0) + (DrawableModels.Med?.Length ?? 0) + (DrawableModels.Low?.Length ?? 0) + (DrawableModels.VLow?.Length ?? 0);
AllModels = allModels.ToArray(); allModels = new DrawableModel[length];
var currIndex = 0;
if (DrawableModels.High != null)
{
for (int i = 0; i < DrawableModels.High.Length; i++)
{
allModels[currIndex] = DrawableModels.High[i];
currIndex++;
}
}
if (DrawableModels.Med != null)
{
for (int i = 0; i < DrawableModels.Med.Length; i++)
{
allModels[currIndex] = DrawableModels.Med[i];
currIndex++;
}
}
if (DrawableModels.Low != null)
{
for (int i = 0; i < DrawableModels.Low.Length; i++)
{
allModels[currIndex] = DrawableModels.Low[i];
currIndex++;
}
}
if (DrawableModels.VLow != null)
{
for (int i = 0; i < DrawableModels.VLow.Length; i++)
{
allModels[currIndex] = DrawableModels.VLow[i];
currIndex++;
}
}
if (DrawableModels.Extra != null)
{
for (int i = 0; i < DrawableModels.Extra.Length; i++)
{
allModels[currIndex] = DrawableModels.Extra[i];
currIndex++;
}
}
} }
public void BuildVertexDecls() public void BuildVertexDecls()

View File

@ -1346,7 +1346,7 @@ namespace CodeWalker.GameFiles
if (ItemDataByteLength != 0)//sometimes this is 0 and UnkUshort3>0, which is weird if (ItemDataByteLength != 0)//sometimes this is 0 and UnkUshort3>0, which is weird
{ {
ShatterMapRowOffsets = reader.ReadStructs<ushort>(ItemDataCount);//byte offsets for following array ShatterMapRowOffsets = reader.ReadStructs<ushort>(ItemDataCount).ToArray();//byte offsets for following array
ShatterMap = new WindowShatterMapRow[ItemDataCount]; ShatterMap = new WindowShatterMapRow[ItemDataCount];
for (int i = 0; i < ItemDataCount; i++) for (int i = 0; i < ItemDataCount; i++)
{ {
@ -1942,7 +1942,13 @@ namespace CodeWalker.GameFiles
public void BuildOffsets() public void BuildOffsets()
{ {
var offs = new List<WindowOffset>(); if (Windows == null)
{
TotalLength = 16u;
WindowOffsets = Array.Empty<WindowOffset>();
return;
}
var offs = new List<WindowOffset>(Windows.Length);
var bc = 16u; var bc = 16u;
if (Windows != null) if (Windows != null)
{ {

View File

@ -3711,14 +3711,17 @@ namespace CodeWalker.GameFiles
public ParticleKeyframePropName(uint h) { Hash = h; } public ParticleKeyframePropName(uint h) { Hash = h; }
public ParticleKeyframePropName(string str) public ParticleKeyframePropName(string str)
{ {
var strl = str?.ToLowerInvariant() ?? ""; if (string.IsNullOrEmpty(str))
if (strl.StartsWith("hash_"))
{ {
Hash = Convert.ToUInt32(strl.Substring(5), 16); Hash = 0;
}
else if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{
Hash = Convert.ToUInt32(str.Substring(5), 16);
} }
else else
{ {
Hash = JenkHash.GenHash(strl); Hash = JenkHash.GenHashLower(str);
} }
} }

View File

@ -761,11 +761,15 @@ namespace CodeWalker.GameFiles
//TODO: NEEDS TO BE TESTED!!! //TODO: NEEDS TO BE TESTED!!!
data_items = new T[EntriesCount]; data_items = new T[EntriesCount];
var posbckp = reader.Position; var posbckp = reader.Position;
if (EntriesCount > 0)
{
reader.Position = (long)EntriesPointer; reader.Position = (long)EntriesPointer;
for (int i = 0; i < EntriesCount; i++) for (int i = 0; i < EntriesCount; i++)
{ {
data_items[i] = reader.ReadBlock<T>(); data_items[i] = reader.ReadBlock<T>();
} }
}
reader.Position = posbckp; reader.Position = posbckp;
} }

View File

@ -1,4 +1,5 @@
using System; using CodeWalker.Core.Utils;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
@ -470,7 +471,7 @@ namespace CodeWalker.GameFiles
{ {
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length); MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length);
ds.CopyTo(outstr); ds.CopyToFast(outstr);
return outstr.ToArray(); return outstr.ToArray();
} }
} }

View File

@ -23,7 +23,9 @@
//shamelessly stolen and mangled //shamelessly stolen and mangled
using CodeWalker.Utils;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
@ -34,7 +36,6 @@ using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
/// <summary> /// <summary>
/// Represents a resource data reader. /// Represents a resource data reader.
/// </summary> /// </summary>
@ -53,8 +54,23 @@ namespace CodeWalker.GameFiles
// this is a dictionary that contains all the resource blocks // this is a dictionary that contains all the resource blocks
// which were read from this resource reader // which were read from this resource reader
public Dictionary<long, IResourceBlock> blockPool = new Dictionary<long, IResourceBlock>(); public Dictionary<long, IResourceBlock> blockPool
public Dictionary<long, object> arrayPool = new Dictionary<long, object>(); {
get
{
return _blockPool ??= new Dictionary<long, IResourceBlock>();
}
}
public Dictionary<long, object> arrayPool {
get
{
return _arrayPool ??= new Dictionary<long, object>();
}
}
private Dictionary<long, object> _arrayPool;
private Dictionary<long, IResourceBlock> _blockPool;
private long position = SYSTEM_BASE;
/// <summary> /// <summary>
/// Gets the length of the underlying stream. /// Gets the length of the underlying stream.
@ -72,10 +88,22 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public override long Position public override long Position
{ {
get; get => position;
set; set {
} = SYSTEM_BASE; if ((value & SYSTEM_BASE) == SYSTEM_BASE)
{
systemStream.Position = value & ~SYSTEM_BASE;
}
else if ((value & GRAPHICS_BASE) == GRAPHICS_BASE)
{
graphicsStream.Position = value & ~GRAPHICS_BASE;
} else
{
throw new InvalidOperationException($"Invalid position {position}");
}
position = value;
}
}
/// <summary> /// <summary>
/// Initializes a new resource data reader for the specified system- and graphics-stream. /// Initializes a new resource data reader for the specified system- and graphics-stream.
/// </summary> /// </summary>
@ -108,93 +136,55 @@ namespace CodeWalker.GameFiles
// } // }
//} //}
this.systemStream = new MemoryStream(data, 0, (int)systemSize); if ((int)systemSize > data.Length)
this.graphicsStream = new MemoryStream(data, (int)systemSize, (int)graphicsSize); {
throw new ArgumentException($"systemSize {systemSize} is larger than data length ({data.Length})", nameof(systemSize));
}
if ((int)graphicsSize > data.Length)
{
throw new ArgumentException($"graphicsSize {graphicsSize} is larger than data length ({data.Length})", nameof(graphicsSize));
}
this.systemStream = Stream.Synchronized(new MemoryStream(data, 0, (int)systemSize));
this.graphicsStream = Stream.Synchronized(new MemoryStream(data, (int)systemSize, (int)graphicsSize));
} }
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base((Stream)null, endianess)
{ {
this.systemStream = new MemoryStream(data, 0, systemSize); this.systemStream = Stream.Synchronized(new MemoryStream(data, 0, systemSize));
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize); this.graphicsStream = Stream.Synchronized(new MemoryStream(data, systemSize, graphicsSize));
} }
/// <summary> public override Stream GetStream()
/// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
/// </summary>
protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[] buffer = null)
{ {
if ((Position & SYSTEM_BASE) == SYSTEM_BASE) if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{ {
// read from system stream... return systemStream;
systemStream.Position = Position & ~SYSTEM_BASE;
buffer ??= new byte[count];
systemStream.Read(buffer, 0, count);
// handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{
Array.Reverse(buffer);
}
Position = systemStream.Position | SYSTEM_BASE;
return buffer;
} }
else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE) else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{ {
// read from graphic stream... return graphicsStream;
graphicsStream.Position = Position & ~GRAPHICS_BASE;
buffer ??= new byte[count];
graphicsStream.Read(buffer, 0, count);
// handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{
Array.Reverse(buffer);
} }
Position = graphicsStream.Position | GRAPHICS_BASE; throw new InvalidOperationException("illegal position!");
return buffer;
}
throw new Exception("illegal position!");
} }
public override byte ReadByte() internal override void SetPositionAfterRead(Stream stream)
{
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{ {
if (stream == systemStream)
// read from system stream...
systemStream.Position = Position & ~SYSTEM_BASE;
var readByte = (byte)systemStream.ReadByte();
Position = systemStream.Position | SYSTEM_BASE;
return readByte;
}
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{ {
// read from graphic stream... position = systemStream.Position | SYSTEM_BASE;
}
graphicsStream.Position = Position & ~GRAPHICS_BASE; else if (stream == graphicsStream)
{
var readByte = (byte)graphicsStream.ReadByte(); position = graphicsStream.Position | GRAPHICS_BASE;
}
Position = graphicsStream.Position | GRAPHICS_BASE;
return readByte; if ((position & SYSTEM_BASE) != SYSTEM_BASE && (position & GRAPHICS_BASE) != GRAPHICS_BASE)
{
throw new InvalidOperationException($"Invalid position {position}");
} }
throw new Exception("illegal position!");
} }
/// <summary> /// <summary>
@ -233,7 +223,7 @@ namespace CodeWalker.GameFiles
if (result == null) if (result == null)
{ {
return default(T); return default;
} }
if (usepool) if (usepool)
@ -279,14 +269,14 @@ namespace CodeWalker.GameFiles
return items; return items;
} }
internal const int StackallocThreshold = 512;
public byte[] ReadBytesAt(ulong position, uint count, bool cache = true) public unsafe byte[] ReadBytesAt(ulong position, uint count, bool cache = true, byte[] buffer = null)
{ {
long pos = (long)position; long pos = (long)position;
if ((pos <= 0) || (count == 0)) return null; if ((pos <= 0) || (count == 0)) return null;
var posbackup = Position; var posbackup = Position;
Position = pos; Position = pos;
var result = ReadBytes((int)count); var result = ReadBytes((int)count, buffer);
Position = posbackup; Position = posbackup;
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
return result; return result;
@ -296,9 +286,18 @@ namespace CodeWalker.GameFiles
if ((position <= 0) || (count == 0)) return null; if ((position <= 0) || (count == 0)) return null;
var result = new ushort[count]; var result = new ushort[count];
var length = count * 2; var length = count * sizeof(ushort);
byte[] data = ReadBytesAt(position, length, false); var data = ArrayPool<byte>.Shared.Rent((int)length);
try
{
ReadBytesAt(position, length, false, data);
Buffer.BlockCopy(data, 0, result, 0, (int)length); Buffer.BlockCopy(data, 0, result, 0, (int)length);
}
finally
{
ArrayPool<byte>.Shared.Return(data);
}
//var posbackup = Position; //var posbackup = Position;
//Position = position; //Position = position;
@ -317,9 +316,18 @@ namespace CodeWalker.GameFiles
{ {
if ((position <= 0) || (count == 0)) return null; if ((position <= 0) || (count == 0)) return null;
var result = new short[count]; var result = new short[count];
var length = count * 2; var length = count * sizeof(short);
byte[] data = ReadBytesAt(position, length, false); var buffer = ArrayPool<byte>.Shared.Rent((int)length);
Buffer.BlockCopy(data, 0, result, 0, (int)length); try
{
ReadBytesAt(position, length, false, buffer);
Buffer.BlockCopy(buffer, 0, result, 0, (int)length);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
@ -330,18 +338,17 @@ namespace CodeWalker.GameFiles
if ((position <= 0) || (count == 0)) return null; if ((position <= 0) || (count == 0)) return null;
var result = new uint[count]; var result = new uint[count];
var length = count * 4; var length = count * sizeof(uint);
byte[] data = ReadBytesAt(position, length, false); var buffer = ArrayPool<byte>.Shared.Rent((int)length);
Buffer.BlockCopy(data, 0, result, 0, (int)length); try
{
ReadBytesAt(position, length, false, buffer);
Buffer.BlockCopy(buffer, 0, result, 0, (int)length);
} finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
//var posbackup = Position;
//Position = position;
//var result = new uint[count];
//for (uint i = 0; i < count; i++)
//{
// result[i] = ReadUInt32();
//}
//Position = posbackup;
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
@ -352,18 +359,17 @@ namespace CodeWalker.GameFiles
if ((position <= 0) || (count == 0)) return null; if ((position <= 0) || (count == 0)) return null;
var result = new ulong[count]; var result = new ulong[count];
var length = count * 8; var length = count * sizeof(ulong);
byte[] data = ReadBytesAt(position, length, false); var data = ArrayPool<byte>.Shared.Rent((int)length);
try
{
ReadBytesAt(position, length, false, data);
Buffer.BlockCopy(data, 0, result, 0, (int)length); Buffer.BlockCopy(data, 0, result, 0, (int)length);
}
//var posbackup = Position; finally
//Position = position; {
//var result = new ulong[count]; ArrayPool<byte>.Shared.Return(data);
//for (uint i = 0; i < count; i++) }
//{
// result[i] = ReadUInt64();
//}
//Position = posbackup;
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
@ -374,88 +380,85 @@ namespace CodeWalker.GameFiles
if ((position <= 0) || (count == 0)) return null; if ((position <= 0) || (count == 0)) return null;
var result = new float[count]; var result = new float[count];
var length = count * 4; var length = count * sizeof(float);
byte[] data = ReadBytesAt(position, length, false); var data = ArrayPool<byte>.Shared.Rent((int)length);
try
{
ReadBytesAt(position, length, false, data);
Buffer.BlockCopy(data, 0, result, 0, (int)length); Buffer.BlockCopy(data, 0, result, 0, (int)length);
}
//var posbackup = Position; finally
//Position = position; {
//var result = new float[count]; ArrayPool<byte>.Shared.Return(data);
//for (uint i = 0; i < count; i++) }
//{
// result[i] = ReadSingle();
//}
//Position = posbackup;
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
return result; return result;
} }
public T[] ReadStructsAt<T>(ulong position, uint count, bool cache = true) 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)); uint structsize = (uint)Marshal.SizeOf(typeof(T));
var length = count * structsize; var length = count * structsize;
byte[] data = ReadBytesAt(position, length, false); var data = ArrayPool<byte>.Shared.Rent((int)length);
try
//var result2 = new T[count]; {
//Buffer.BlockCopy(data, 0, result2, 0, (int)length); //error: "object must be an array of primitives" :( ReadBytesAt(position, length, false, data);
//var result = new T[count];
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//for (uint i = 0; i < count; i++)
//{
// result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
//}
//handle.Free();
var result = new T[count]; var result = new T[count];
GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, 0, h, (int)length);
handle.Free();
var resultSpan = MemoryMarshal.Cast<byte, T>(data.AsSpan(0, (int)length));
resultSpan.CopyTo(result);
if (cache) arrayPool[(long)position] = result; if (cache) arrayPool[(long)position] = result;
return result; return result;
} }
public T[] ReadStructs<T>(uint count) finally
{
ArrayPool<byte>.Shared.Return(data);
}
}
public T[] ReadStructs<T>(uint count) where T : struct
{ {
uint structsize = (uint)Marshal.SizeOf(typeof(T)); uint structsize = (uint)Marshal.SizeOf(typeof(T));
var result = new T[count]; var result = new T[count];
var length = count * structsize; var length = count * structsize;
byte[] data = ReadBytes((int)length); var data = ArrayPool<byte>.Shared.Rent((int)length);
try
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); {
//var h = handle.AddrOfPinnedObject(); ReadBytes((int)length, data);
//for (uint i = 0; i < count; i++)
//{
// result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
//}
//handle.Free();
GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, 0, h, (int)length);
handle.Free();
var resultSpan = MemoryMarshal.Cast<byte, T>(data.AsSpan(0, (int)length));
resultSpan.CopyTo(result);
return result; return result;
} }
finally
{
ArrayPool<byte>.Shared.Return(data);
}
}
public T ReadStruct<T>() where T : struct public T ReadStruct<T>() where T : struct
{ {
uint structsize = (uint)Marshal.SizeOf(typeof(T)); uint structsize = (uint)Marshal.SizeOf(typeof(T));
var length = structsize; var length = structsize;
byte[] data = ReadBytes((int)length); var data = ArrayPool<byte>.Shared.Rent((int)length);
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); try
var h = handle.AddrOfPinnedObject(); {
var result = Marshal.PtrToStructure<T>(h); ReadBytes((int)length, data);
handle.Free(); MemoryMarshal.TryRead<T>(data, out var value);
return result;
return value;
} finally
{
ArrayPool<byte>.Shared.Return(data);
}
} }
public T ReadStructAt<T>(long position) where T : struct public T ReadStructAt<T>(long position) where T : struct
@ -513,13 +516,22 @@ namespace CodeWalker.GameFiles
} }
} }
/// <summary> private long position = SYSTEM_BASE;
/// Gets or sets the position within the underlying stream.
/// </summary>
public override long Position public override long Position
{ {
get; get => position;
set; set
{
if ((value & SYSTEM_BASE) == SYSTEM_BASE)
{
systemStream.Position = value & ~SYSTEM_BASE;
}
else if ((value & GRAPHICS_BASE) == GRAPHICS_BASE)
{
graphicsStream.Position = value & ~GRAPHICS_BASE;
}
position = value;
}
} }
/// <summary> /// <summary>
@ -528,61 +540,33 @@ namespace CodeWalker.GameFiles
public ResourceDataWriter(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian) public ResourceDataWriter(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base((Stream)null, endianess)
{ {
this.systemStream = systemStream; this.systemStream = Stream.Synchronized(systemStream);
this.graphicsStream = graphicsStream; this.graphicsStream = Stream.Synchronized(graphicsStream);
} }
/// <summary> internal override Stream GetStream()
/// Writes data to the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
/// </summary>
protected override void WriteToStream(byte[] value, bool ignoreEndianess = true)
{ {
if ((Position & SYSTEM_BASE) == SYSTEM_BASE) if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{ {
// write to system stream... return systemStream;
}
systemStream.Position = Position & ~SYSTEM_BASE; else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
// handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{ {
var buf = (byte[])value.Clone(); return graphicsStream;
Array.Reverse(buf);
systemStream.Write(buf, 0, buf.Length);
} }
else throw new InvalidOperationException("illegal position!");
}
internal override void SetPositionAfterWrite(Stream stream)
{ {
systemStream.Write(value, 0, value.Length); if (stream == systemStream)
}
Position = systemStream.Position | 0x50000000;
return;
}
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{ {
// write to graphic stream... position = systemStream.Position | SYSTEM_BASE;
}
graphicsStream.Position = Position & ~GRAPHICS_BASE; else if (stream == graphicsStream)
// handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{ {
var buf = (byte[])value.Clone(); position = graphicsStream.Position | GRAPHICS_BASE;
Array.Reverse(buf);
graphicsStream.Write(buf, 0, buf.Length);
} }
else
{
graphicsStream.Write(value, 0, value.Length);
}
Position = graphicsStream.Position | 0x60000000;
return;
}
throw new Exception("illegal position!");
} }
/// <summary> /// <summary>
@ -599,20 +583,41 @@ namespace CodeWalker.GameFiles
public void WriteStruct<T>(T val) where T : struct public void WriteStruct<T>(T val) where T : struct
{ {
int size = Marshal.SizeOf(typeof(T)); int size = Marshal.SizeOf(typeof(T));
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size); var arr = new byte[size];
Marshal.StructureToPtr(val, ptr, true);
Marshal.Copy(ptr, arr, 0, size); MemoryMarshal.TryWrite(arr, ref val);
Marshal.FreeHGlobal(ptr);
Write(arr); Write(arr);
//byte[] arr = new byte[size];
//IntPtr ptr = Marshal.AllocHGlobal(size);
//Marshal.StructureToPtr(val, ptr, true);
//Marshal.Copy(ptr, arr, 0, size);
//Marshal.FreeHGlobal(ptr);
//Write(arr);
} }
public void WriteStructs<T>(Span<T> val) where T : struct
{
if (val == null) return;
var bytes = MemoryMarshal.AsBytes(val);
Write(bytes);
//foreach (var v in val)
//{
// WriteStruct(v);
//}
}
public void WriteStructs<T>(T[] val) where T : struct public void WriteStructs<T>(T[] val) where T : struct
{ {
if (val == null) return; if (val == null) return;
foreach (var v in val) var bytes = MemoryMarshal.AsBytes<T>(val);
{
WriteStruct(v); Write(bytes);
}
} }

View File

@ -636,8 +636,8 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public override void Read(ResourceDataReader reader, params object[] parameters) public override void Read(ResourceDataReader reader, params object[] parameters)
{ {
uint format = Convert.ToUInt32(parameters[0]); //uint format = Convert.ToUInt32(parameters[0]);
int Width = Convert.ToInt32(parameters[1]); //int Width = Convert.ToInt32(parameters[1]);
int Height = Convert.ToInt32(parameters[2]); int Height = Convert.ToInt32(parameters[2]);
int Levels = Convert.ToInt32(parameters[3]); int Levels = Convert.ToInt32(parameters[3]);
int Stride = Convert.ToInt32(parameters[4]); int Stride = Convert.ToInt32(parameters[4]);
@ -646,7 +646,7 @@ namespace CodeWalker.GameFiles
int length = Stride * Height; int length = Stride * Height;
for (int i = 0; i < Levels; i++) for (int i = 0; i < Levels; i++)
{ {
fullLength += length; fullLength += Math.Max(length, 4 * 4);
length /= 4; length /= 4;
} }

View File

@ -1,4 +1,5 @@
using Microsoft.IO; using CodeWalker.Core.Utils;
using Microsoft.IO;
using System; using System;
using System.Buffers; using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
@ -20,24 +21,6 @@ namespace CodeWalker.GameFiles
public class RpfFile public class RpfFile
{ {
public string Name { get; set; } //name of this RPF file/package public string Name { get; set; } //name of this RPF file/package
private string _nameLower;
public string NameLower
{
get
{
if (_nameLower == null)
{
_nameLower = Name.ToLowerInvariant();
}
return _nameLower;
}
set
{
_nameLower = value;
}
}
public string Path { get; set; } //path within the RPF structure public string Path { get; set; } //path within the RPF structure
public string FilePath { get; set; } //full file path of the RPF public string FilePath { get; set; } //full file path of the RPF
public long FileSize { get; set; } public long FileSize { get; set; }
@ -80,19 +63,48 @@ namespace CodeWalker.GameFiles
public uint GrandTotalBinaryFileCount { get; set; } public uint GrandTotalBinaryFileCount { get; set; }
public long ExtractedByteCount { get; set; } public long ExtractedByteCount { get; set; }
static RpfFile()
{
recyclableMemoryStreamManager.BufferDiscarded += (sender, args) =>
{
Console.WriteLine($"Buffer Discarded: BufferType: {args.BufferType}; Reason: {args.Reason};");
};
recyclableMemoryStreamManager.StreamDoubleDisposed += (sender, args) =>
{
Console.WriteLine($"StreamDoubleDisposed: Stack1: {args.DisposeStack1}; Stack2: {args.DisposeStack2};");
};
recyclableMemoryStreamManager.StreamOverCapacity += (sender, args) =>
{
Console.WriteLine($"StreamOverCapacity: MaximumCapacity is {args.MaximumCapacity / 1024f / 1024f} MB, requisted: {args.RequestedCapacity / 1024f / 1024f:0.##} MB Stack: {args.AllocationStack}");
};
recyclableMemoryStreamManager.StreamFinalized += (sender, args) =>
{
Console.WriteLine($"StreamFinalized: {args.AllocationStack}");
};
}
public RpfFile(FileInfo fileInfo)
{
Name = fileInfo.Name;
FilePath = fileInfo.FullName;
FileSize = fileInfo.Length;
}
public RpfFile(string fpath, string relpath) //for a ROOT filesystem RPF public RpfFile(string fpath, string relpath) //for a ROOT filesystem RPF
{ {
FileInfo fi = new FileInfo(fpath); FileInfo fi = new FileInfo(fpath);
Name = fi.Name; Name = fi.Name;
Path = relpath.ToLowerInvariant(); Path = relpath;
FilePath = fpath; FilePath = fpath;
FileSize = fi.Length; FileSize = fi.Length;
} }
public RpfFile(string name, string path, long filesize) //for a child RPF public RpfFile(string name, string path, long filesize) //for a child RPF
{ {
Name = name; Name = name;
Path = path.ToLowerInvariant(); Path = path;
FilePath = path; FilePath = path;
FileSize = filesize; FileSize = filesize;
} }
@ -104,7 +116,7 @@ namespace CodeWalker.GameFiles
string rel_parent_path = parentFile.Path; string rel_parent_path = parentFile.Path;
string full_parent_path = parentFile.FilePath; string full_parent_path = parentFile.FilePath;
if(rel_parent_path.StartsWith(@"mods\")) if(rel_parent_path.StartsWith(@"mods\", StringComparison.OrdinalIgnoreCase))
{ {
status = "already in mods folder"; status = "already in mods folder";
return null; return null;
@ -132,7 +144,7 @@ namespace CodeWalker.GameFiles
public bool IsInModsFolder() public bool IsInModsFolder()
{ {
return GetTopParent().Path.StartsWith(@"mods\"); return GetTopParent().Path.StartsWith(@"mods\", StringComparison.OrdinalIgnoreCase);
} }
public RpfFile GetTopParent() public RpfFile GetTopParent()
@ -169,11 +181,13 @@ namespace CodeWalker.GameFiles
throw new Exception("Invalid Resource - not GTAV!"); throw new Exception("Invalid Resource - not GTAV!");
} }
byte[] entriesdata = ArrayPool<byte>.Shared.Rent((int)EntryCount * 16); var entriesLength = (int)EntryCount * 16;
byte[] namesdata = ArrayPool<byte>.Shared.Rent((int)NamesLength); var namesLength = (int)NamesLength;
byte[] entriesdata = ArrayPool<byte>.Shared.Rent(entriesLength);
byte[] namesdata = ArrayPool<byte>.Shared.Rent(namesLength);
br.BaseStream.Read(entriesdata, 0, (int)EntryCount * 16); br.BaseStream.Read(entriesdata, 0, entriesLength);
br.BaseStream.Read(namesdata, 0, (int)NamesLength); br.BaseStream.Read(namesdata, 0, namesLength);
switch (Encryption) switch (Encryption)
{ {
@ -181,22 +195,22 @@ namespace CodeWalker.GameFiles
case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC
break; break;
case RpfEncryption.AES: case RpfEncryption.AES:
GTACrypto.DecryptAES(entriesdata, (int)EntryCount * 16); GTACrypto.DecryptAES(entriesdata, entriesLength);
GTACrypto.DecryptAES(namesdata, (int)NamesLength); GTACrypto.DecryptAES(namesdata, namesLength);
IsAESEncrypted = true; IsAESEncrypted = true;
break; break;
case RpfEncryption.NG: case RpfEncryption.NG:
default: default:
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, (int)EntryCount * 16); GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, entriesLength);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, (int)NamesLength); GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, namesLength);
IsNGEncrypted = true; IsNGEncrypted = true;
break; break;
} }
using var entriesrdr = new DataReader(new MemoryStream(entriesdata, 0, (int)EntryCount * 16)); using var entriesrdr = new DataReader(new MemoryStream(entriesdata, 0, entriesLength));
using var namesrdr = new DataReader(new MemoryStream(namesdata, 0, (int)NamesLength)); using var namesrdr = new DataReader(new MemoryStream(namesdata, 0, namesLength));
AllEntries = new List<RpfEntry>((int)EntryCount); AllEntries = new List<RpfEntry>((int)EntryCount);
@ -245,26 +259,19 @@ namespace CodeWalker.GameFiles
{ {
namesrdr.Position = entry.NameOffset; namesrdr.Position = entry.NameOffset;
entry.Name = namesrdr.ReadString(256); entry.Name = namesrdr.ReadString(256);
if (entry.Name.Length > 256) JenkIndex.EnsureLower(entry.Name);
{
// long names can freeze the RPFExplorer
entry.Name = entry.Name.Substring(0, 256);
}
if (entry is RpfResourceFileEntry rfe)// && string.IsNullOrEmpty(e.Name)) if (entry is RpfResourceFileEntry rfe)// && string.IsNullOrEmpty(e.Name))
{ {
rfe.IsEncrypted = rfe.Name.EndsWith(".ysc", StringComparison.OrdinalIgnoreCase);//any other way to know..? rfe.IsEncrypted = rfe.IsExtension(".ysc");//any other way to know..?
} }
} }
Root = (RpfDirectoryEntry)AllEntries[0]; Root = (RpfDirectoryEntry)AllEntries[0];
Root.Path = Path.ToLowerInvariant();// + "\\" + Root.Name; Root.Path = Path;// + "\\" + Root.Name;
var stack = new Stack<RpfDirectoryEntry>(); //var stack = new Stack<RpfDirectoryEntry>();
stack.Push(Root);
while (stack.Count > 0)
{
var item = stack.Pop();
void addSubDirectory(RpfDirectoryEntry item)
{
int starti = (int)item.EntriesIndex; int starti = (int)item.EntriesIndex;
int endi = (int)(item.EntriesIndex + item.EntriesCount); int endi = (int)(item.EntriesIndex + item.EntriesCount);
@ -274,19 +281,46 @@ namespace CodeWalker.GameFiles
e.Parent = item; e.Parent = item;
if (e is RpfDirectoryEntry rde) if (e is RpfDirectoryEntry rde)
{ {
rde.Path = item.Path + "\\" + rde.Name; rde.Path = item.Path + '\\' + rde.Name;
item.Directories.Add(rde); item.Directories.Add(rde);
stack.Push(rde); addSubDirectory(rde);
} }
else if (e is RpfFileEntry) else if (e is RpfFileEntry rfe)
{ {
RpfFileEntry rfe = e as RpfFileEntry; rfe.Path = item.Path + '\\' + rfe.Name;
rfe.Path = item.Path + "\\" + rfe.Name;
item.Files.Add(rfe); item.Files.Add(rfe);
} }
} }
} }
addSubDirectory(Root);
//stack.Push(Root);
//while (stack.Count > 0)
//{
// var item = stack.Pop();
// int starti = (int)item.EntriesIndex;
// int endi = (int)(item.EntriesIndex + item.EntriesCount);
// for (int i = starti; i < endi; i++)
// {
// RpfEntry e = AllEntries[i];
// e.Parent = item;
// if (e is RpfDirectoryEntry rde)
// {
// rde.Path = item.Path + "\\" + rde.Name;
// item.Directories.Add(rde);
// stack.Push(rde);
// }
// else if (e is RpfFileEntry)
// {
// RpfFileEntry rfe = e as RpfFileEntry;
// rfe.Path = item.Path + "\\" + rfe.Name;
// item.Files.Add(rfe);
// }
// }
//}
br.BaseStream.Position = StartPos; br.BaseStream.Position = StartPos;
CurrentFileReader = null; CurrentFileReader = null;
@ -294,10 +328,6 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(namesdata); ArrayPool<byte>.Shared.Return(namesdata);
} }
public bool ScanStructure(Action<string> updateStatus, Action<string> errorLog) public bool ScanStructure(Action<string> updateStatus, Action<string> errorLog)
{ {
@ -343,7 +373,7 @@ namespace CodeWalker.GameFiles
if (entry is RpfBinaryFileEntry binentry) if (entry is RpfBinaryFileEntry binentry)
{ {
//search all the sub resources for YSC files. (recurse!) //search all the sub resources for YSC files. (recurse!)
if (binentry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase) && binentry.Path.Length < 5000) // a long path is most likely an attempt to crash CW, so skip it if (binentry.IsExtension(".rpf") && binentry.Path.Length < 5000) // a long path is most likely an attempt to crash CW, so skip it
{ {
br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512); br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512);
@ -424,8 +454,7 @@ namespace CodeWalker.GameFiles
long l = binentry.GetFileSize(); long l = binentry.GetFileSize();
//search all the sub resources for YSC files. (recurse!) //search all the sub resources for YSC files. (recurse!)
string lname = binentry.NameLower; if (binentry.IsExtension(".rpf"))
if (lname.EndsWith(".rpf"))
{ {
br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512); br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512);
@ -442,9 +471,7 @@ namespace CodeWalker.GameFiles
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry; RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
string lname = resentry.NameLower; if (resentry.IsExtension(".ysc"))
if (lname.EndsWith(".ysc"))
{ {
updateStatus?.Invoke("Extracting " + resentry.Name + "..."); updateStatus?.Invoke("Extracting " + resentry.Name + "...");
@ -483,7 +510,7 @@ namespace CodeWalker.GameFiles
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = recyclableMemoryStreamManager.GetStream(); MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyTo(outstr); ds.CopyToFast(outstr);
byte[] deflated = outstr.GetBuffer(); byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes(). byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
Array.Copy(deflated, outbuf, outbuf.Length); Array.Copy(deflated, outbuf, outbuf.Length);
@ -556,7 +583,8 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath()))) using var fileStream = new FileStream(GetPhysicalFilePath(), FileMode.Open, FileAccess.Read, FileShare.Read, 4096);
using (BinaryReader br = new BinaryReader(fileStream))
{ {
if (entry is RpfBinaryFileEntry binaryFileEntry) if (entry is RpfBinaryFileEntry binaryFileEntry)
{ {
@ -579,18 +607,99 @@ namespace CodeWalker.GameFiles
return null; return null;
} }
} }
public ValueTask<byte[]> ExtractFileAsync(RpfFileEntry entry)
{
try
{
using var fileStream = new FileStream(GetPhysicalFilePath(), FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous);
using (BinaryReader br = new BinaryReader(fileStream))
{
if (entry is RpfBinaryFileEntry binaryFileEntry)
{
return ExtractFileBinaryAsync(binaryFileEntry, br);
}
else if (entry is RpfResourceFileEntry resourceFileEntry)
{
return ExtractFileResourceAsync(resourceFileEntry, br);
}
else
{
Console.WriteLine($"{entry} is not a BinaryFileEntry of ResourceFileEntry");
return new ValueTask<byte[]>();
}
}
}
catch (Exception ex)
{
LastError = ex.ToString();
LastException = ex;
return new ValueTask<byte[]>();
}
}
public async ValueTask<byte[]> ExtractFileBinaryAsync(RpfBinaryFileEntry entry, BinaryReader br)
{
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
long l = entry.GetFileSize();
if (l <= 0)
{
return null;
}
uint offset = 0;// 0x10;
uint totlen = (uint)l - offset;
byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
br.BaseStream.Position += offset;
await br.ReadAsync(tbytes, 0, (int)totlen).ConfigureAwait(false);
if (entry.IsEncrypted)
{
if (IsAESEncrypted)
{
GTACrypto.DecryptAES(tbytes, (int)totlen);
}
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize, 0, (int)totlen);
}
}
byte[] defl;
if (entry.FileSize > 0) //apparently this means it's compressed
{
defl = await DecompressBytesAsync(tbytes).ConfigureAwait(false);
}
else
{
defl = new byte[(int)totlen];
Array.Copy(tbytes, defl, (int)totlen);
}
ArrayPool<byte>.Shared.Return(tbytes);
return defl;
}
public byte[] ExtractFileBinary(RpfBinaryFileEntry entry, BinaryReader br) public byte[] ExtractFileBinary(RpfBinaryFileEntry entry, BinaryReader br)
{ {
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512); br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
long l = entry.GetFileSize(); long l = entry.GetFileSize();
if (l > 0) if (l <= 0)
{ {
return null;
}
uint offset = 0;// 0x10; uint offset = 0;// 0x10;
uint totlen = (uint)l - offset; uint totlen = (uint)l - offset;
byte[] tbytes = new byte[totlen]; byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
br.BaseStream.Position += offset; br.BaseStream.Position += offset;
br.Read(tbytes, 0, (int)totlen); br.Read(tbytes, 0, (int)totlen);
@ -599,71 +708,63 @@ namespace CodeWalker.GameFiles
{ {
if (IsAESEncrypted) if (IsAESEncrypted)
{ {
GTACrypto.DecryptAES(tbytes); GTACrypto.DecryptAES(tbytes, (int)totlen);
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize); GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize, 0, (int)totlen);
} }
//else
//{ }
} }
byte[] defl = tbytes; byte[] defl;
if (entry.FileSize > 0) //apparently this means it's compressed if (entry.FileSize > 0) //apparently this means it's compressed
{ {
defl = DecompressBytes(tbytes); defl = DecompressBytes(tbytes);
} }
else else
{ {
defl = new byte[(int)totlen];
Buffer.BlockCopy(tbytes, 0, defl, 0, (int)totlen);
} }
ArrayPool<byte>.Shared.Return(tbytes);
return defl; return defl;
} }
return null; public async ValueTask<byte[]> ExtractFileResourceAsync(RpfResourceFileEntry entry, BinaryReader br)
}
public byte[] ExtractFileResource(RpfResourceFileEntry entry, BinaryReader br)
{ {
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512); br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
if (entry.FileSize <= 0)
if (entry.FileSize > 0)
{ {
return null;
}
uint offset = 0x10; uint offset = 0x10;
uint totlen = entry.FileSize - offset; uint totlen = entry.FileSize - offset;
byte[] tbytes = new byte[totlen]; byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
br.BaseStream.Position += offset; br.BaseStream.Position += offset;
//byte[] hbytes = br.ReadBytes(16); //what are these 16 bytes actually used for?
//if (entry.FileSize > 0xFFFFFF)
//{ //(for huge files, the full file size is packed in 4 of these bytes... seriously wtf)
// var filesize = (hbytes[7] << 0) | (hbytes[14] << 8) | (hbytes[5] << 16) | (hbytes[2] << 24);
//}
await br.ReadAsync(tbytes, 0, (int)totlen);
br.Read(tbytes, 0, (int)totlen);
if (entry.IsEncrypted) if (entry.IsEncrypted)
{ {
if (IsAESEncrypted) if (IsAESEncrypted)
{ {
GTACrypto.DecryptAES(tbytes); GTACrypto.DecryptAES(tbytes, (int)totlen);
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize); GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen);
} }
//else
//{ }
} }
byte[] deflated = DecompressBytes(tbytes); byte[] deflated = await DecompressBytesAsync(tbytes);
byte[] data = null;
byte[] data;
if (deflated != null) if (deflated != null)
{ {
data = deflated; data = deflated;
@ -671,16 +772,67 @@ namespace CodeWalker.GameFiles
else else
{ {
entry.FileSize -= offset; entry.FileSize -= offset;
data = tbytes;
data = new byte[(int)totlen];
Buffer.BlockCopy(tbytes, 0, data, 0, (int)totlen);
} }
ArrayPool<byte>.Shared.Return(tbytes);
return data; return data;
} }
public byte[] ExtractFileResource(RpfResourceFileEntry entry, BinaryReader br)
{
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
if (entry.FileSize <= 0)
{
return null; return null;
} }
uint offset = 0x10;
uint totlen = entry.FileSize - offset;
byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
br.BaseStream.Position += offset;
br.Read(tbytes, 0, (int)totlen);
if (entry.IsEncrypted)
{
if (IsAESEncrypted)
{
GTACrypto.DecryptAES(tbytes, (int)totlen);
}
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen);
}
}
byte[] deflated = DecompressBytes(tbytes);
byte[] data;
if (deflated != null)
{
data = deflated;
}
else
{
entry.FileSize -= offset;
data = new byte[(int)totlen];
Buffer.BlockCopy(tbytes, 0, data, 0, (int)totlen);
}
ArrayPool<byte>.Shared.Return(tbytes);
return data;
}
public static T GetFile<T>(RpfEntry e) where T : class, PackedFile, new() public static T GetFile<T>(RpfEntry e) where T : class, PackedFile, new()
{ {
T file = null; T file = null;
@ -892,7 +1044,7 @@ namespace CodeWalker.GameFiles
{ {
LastError = string.Empty; LastError = string.Empty;
LastException = null; LastException = null;
if (!entry.NameLower.EndsWith(".rpf")) //don't try to extract rpf's, they will be done separately.. if (!entry.IsExtension(".rpf")) //don't try to extract rpf's, they will be done separately..
{ {
if (entry is RpfBinaryFileEntry) if (entry is RpfBinaryFileEntry)
{ {
@ -1025,19 +1177,17 @@ namespace CodeWalker.GameFiles
public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(); public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(256 * 1024, 1024 * 1024, 128 * 1024 * 1024, false, 256 * 1024 * 100, 1024 * 1024 * 128 * 4);
public byte[] DecompressBytes(byte[] bytes) public byte[] DecompressBytes(byte[] bytes)
{ {
try try
{ {
using (DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress)) using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
{ using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length);
using (var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length))
{ ds.CopyToFast(outstr);
ds.CopyTo(outstr); byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output.
byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for output.
Buffer.BlockCopy(deflated, 0, outbuf, 0, outbuf.Length);
if (outbuf.Length <= bytes.Length) if (outbuf.Length <= bytes.Length)
{ {
@ -1047,8 +1197,38 @@ namespace CodeWalker.GameFiles
return outbuf; return outbuf;
} }
catch (Exception ex)
{
LastError = "Could not decompress.";// ex.ToString();
LastException = ex;
return null;
} }
} }
public async Task<byte[]> DecompressBytesAsync(byte[] bytes)
{
try
{
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length);
await ds.CopyToFastAsync(outstr).ConfigureAwait(false);
byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output.
//byte[] deflated = outstr.GetBuffer();
//Console.WriteLine($"{outstr.Read(outbuf, 0, outbuf.Length)}; {outbuf.Length}");
//Buffer.BlockCopy(deflated, 0, outbuf, 0, outbuf.Length);
//Buffer.BlockCopy(deflated, 0, outbuf, 0, outbuf.Length);
if (outbuf.Length <= bytes.Length)
{
LastError = "Warning: Decompressed data was smaller than compressed data...";
//return null; //could still be OK for tiny things!
}
return outbuf;
}
catch (Exception ex) catch (Exception ex)
{ {
LastError = "Could not decompress.";// ex.ToString(); LastError = "Could not decompress.";// ex.ToString();
@ -1056,6 +1236,7 @@ namespace CodeWalker.GameFiles
return null; return null;
} }
} }
public static byte[] CompressBytes(byte[] data) //TODO: refactor this with ResourceBuilder.Compress/Decompress public static byte[] CompressBytes(byte[] data) //TODO: refactor this with ResourceBuilder.Compress/Decompress
{ {
using (MemoryStream ms = recyclableMemoryStreamManager.GetStream("CompressBytes", data.Length)) using (MemoryStream ms = recyclableMemoryStreamManager.GetStream("CompressBytes", data.Length))
@ -1152,7 +1333,7 @@ namespace CodeWalker.GameFiles
Root = new RpfDirectoryEntry(); Root = new RpfDirectoryEntry();
Root.File = this; Root.File = this;
Root.Name = string.Empty; Root.Name = string.Empty;
Root.Path = Path.ToLowerInvariant(); Root.Path = Path;
} }
if (Children == null) if (Children == null)
{ {
@ -1517,15 +1698,15 @@ namespace CodeWalker.GameFiles
//recursively update paths, including in child RPFs. //recursively update paths, including in child RPFs.
if (dir == null) if (dir == null)
{ {
Root.Path = Path.ToLowerInvariant(); Root.Path = Path;
dir = Root; dir = Root;
} }
foreach (var file in dir.Files) foreach (var file in dir.Files)
{ {
file.Path = dir.Path + "\\" + file.NameLower; file.Path = dir.Path + "\\" + file.Name;
RpfBinaryFileEntry binf = file as RpfBinaryFileEntry; RpfBinaryFileEntry binf = file as RpfBinaryFileEntry;
if ((binf != null) && file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)) if ((binf != null) && file.IsExtension(".rpf"))
{ {
RpfFile childrpf = FindChildArchive(binf); RpfFile childrpf = FindChildArchive(binf);
if (childrpf != null) if (childrpf != null)
@ -1685,9 +1866,8 @@ namespace CodeWalker.GameFiles
//create a new directory inside the given parent dir //create a new directory inside the given parent dir
RpfFile parent = dir.File; RpfFile parent = dir.File;
string namel = name.ToLowerInvariant();
string fpath = parent.GetPhysicalFilePath(); string fpath = parent.GetPhysicalFilePath();
string rpath = dir.Path + "\\" + namel; string rpath = dir.Path + "\\" + name;
if (!File.Exists(fpath)) if (!File.Exists(fpath))
{ {
@ -1862,7 +2042,6 @@ namespace CodeWalker.GameFiles
entry.File = parent; entry.File = parent;
entry.Path = rpath; entry.Path = rpath;
entry.Name = name; entry.Name = name;
entry.NameLower = namel;
@ -2194,6 +2373,9 @@ namespace CodeWalker.GameFiles
public RpfFile File { get; set; } public RpfFile File { get; set; }
public RpfDirectoryEntry Parent { get; set; } public RpfDirectoryEntry Parent { get; set; }
public static int ExtensionHits = 0;
public static int ExtensionMisses = 0;
public uint NameHash { get public uint NameHash { get
{ {
if (nameHash == 0 && !string.IsNullOrEmpty(Name)) if (nameHash == 0 && !string.IsNullOrEmpty(Name))
@ -2229,19 +2411,38 @@ namespace CodeWalker.GameFiles
return; return;
} }
name = value; name = value;
nameLower = null;
nameHash = 0; nameHash = 0;
shortNameHash = 0; shortNameHash = 0;
shortName = null; shortName = null;
extension = null;
} }
} }
public string NameLower
public string Extension
{ {
get get
{ {
return nameLower ??= Name?.ToLowerInvariant(); if (extension == null)
{
int length = Name.Length;
for (int i = length; --i >= 0;)
{
char ch = Name[i];
if (ch == '.')
{
extension = Name.Substring(i, length - i).ToLowerInvariant();
break;
}
if (ch == System.IO.Path.DirectorySeparatorChar || ch == System.IO.Path.AltDirectorySeparatorChar)
{
break;
}
}
extension ??= string.Empty;
}
return extension;
} }
set { nameLower = value; }
} }
public string ShortName { public string ShortName {
@ -2249,14 +2450,22 @@ namespace CodeWalker.GameFiles
{ {
if (string.IsNullOrEmpty(shortName) && !string.IsNullOrEmpty(Name)) if (string.IsNullOrEmpty(shortName) && !string.IsNullOrEmpty(Name))
{ {
int ind = Name.LastIndexOf('.'); int length = Name.Length;
if (ind > 0) for (int i = length; --i >= 0;)
{ {
shortName = Name.Substring(0, ind); char ch = Name[i];
if (ch == '.')
{
Interlocked.Increment(ref ExtensionHits);
shortName = Name.Substring(0, i);
break;
} }
else if (ch == System.IO.Path.DirectorySeparatorChar || ch == System.IO.Path.AltDirectorySeparatorChar)
{ {
Interlocked.Increment(ref ExtensionMisses);
shortName = Name; shortName = Name;
break;
}
} }
} }
@ -2273,10 +2482,10 @@ namespace CodeWalker.GameFiles
public uint H1; //first 2 header values from RPF table... public uint H1; //first 2 header values from RPF table...
public uint H2; public uint H2;
private string name; private string name;
private string nameLower;
private uint shortNameHash; private uint shortNameHash;
private uint nameHash; private uint nameHash;
private string shortName; private string shortName;
private string extension;
public abstract void Read(DataReader reader); public abstract void Read(DataReader reader);
public abstract void Write(DataWriter writer); public abstract void Write(DataWriter writer);
@ -2286,9 +2495,9 @@ namespace CodeWalker.GameFiles
return Path; return Path;
} }
public string GetShortName() public bool IsExtension(string ext)
{ {
return ShortName; return Extension.Equals(ext, StringComparison.Ordinal);
} }
} }

View File

@ -1,4 +1,6 @@
using CodeWalker.Core.Utils; 
using CodeWalker.Core.Utils;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -56,10 +58,10 @@ namespace CodeWalker.GameFiles
AllRpfs = new List<RpfFile>(); AllRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>(); DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>(); AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>(); RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(); EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(); ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(); ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
var rpfs = new ConcurrentBag<RpfFile>(); var rpfs = new ConcurrentBag<RpfFile>();
Parallel.ForEach(allfiles, (rpfpath) => Parallel.ForEach(allfiles, (rpfpath) =>
@ -73,7 +75,7 @@ namespace CodeWalker.GameFiles
bool excl = false; bool excl = false;
for (int i = 0; i < ExcludePaths.Length; i++) for (int i = 0; i < ExcludePaths.Length; i++)
{ {
if (rf.Path.StartsWith(ExcludePaths[i])) if (rf.Path.StartsWith(ExcludePaths[i], StringComparison.OrdinalIgnoreCase))
{ {
excl = true; excl = true;
break; break;
@ -147,10 +149,10 @@ namespace CodeWalker.GameFiles
DlcRpfs = new List<RpfFile>(); DlcRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>(); DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>(); AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>(); RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(); EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(); ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(); ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var rpf in allRpfs) foreach (var rpf in allRpfs)
{ {
RpfDict[rpf.Path] = rpf; RpfDict[rpf.Path] = rpf;
@ -171,8 +173,8 @@ namespace CodeWalker.GameFiles
private void AddRpfFile(RpfFile file, bool isdlc, bool ismod) private void AddRpfFile(RpfFile file, bool isdlc, bool ismod)
{ {
isdlc = isdlc || (file.NameLower == "update.rpf") || (file.NameLower.StartsWith("dlc") && file.NameLower.EndsWith(".rpf")); isdlc = isdlc || file.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase) || (file.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase) && file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase));
ismod = ismod || (file.Path.StartsWith("mods\\")); ismod = ismod || (file.Path.StartsWith("mods\\", StringComparison.OrdinalIgnoreCase));
if (file.AllEntries != null) if (file.AllEntries != null)
{ {
@ -249,8 +251,7 @@ namespace CodeWalker.GameFiles
public RpfFile FindRpfFile(string path, bool exactPathOnly) public RpfFile FindRpfFile(string path, bool exactPathOnly)
{ {
RpfFile file = null; //check the dictionary RpfFile file;
if (EnableMods && ModRpfDict.TryGetValue(path, out file)) if (EnableMods && ModRpfDict.TryGetValue(path, out file))
{ {
return file; return file;
@ -261,14 +262,13 @@ namespace CodeWalker.GameFiles
return file; return file;
} }
string lpath = path.ToLowerInvariant(); //try look at names etc
foreach (RpfFile tfile in AllRpfs) foreach (RpfFile tfile in AllRpfs)
{ {
if (!exactPathOnly && tfile.NameLower == lpath) if (!exactPathOnly && tfile.Name.Equals(path, StringComparison.OrdinalIgnoreCase))
{ {
return tfile; return tfile;
} }
if (tfile.Path == lpath) if (tfile.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
{ {
return tfile; return tfile;
} }
@ -281,21 +281,20 @@ namespace CodeWalker.GameFiles
public RpfEntry GetEntry(string path) public RpfEntry GetEntry(string path)
{ {
RpfEntry entry; RpfEntry entry;
string pathl = path.ToLowerInvariant(); if (EnableMods && ModEntryDict.TryGetValue(path, out entry))
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry))
{ {
return entry; return entry;
} }
EntryDict.TryGetValue(pathl, out entry); EntryDict.TryGetValue(path, out entry);
if (entry == null) if (entry == null)
{ {
pathl = pathl.Replace("/", "\\"); path = path.Replace("/", "\\");
pathl = pathl.Replace("common:", "common.rpf"); path = path.Replace("common:", "common.rpf");
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry)) if (EnableMods && ModEntryDict.TryGetValue(path, out entry))
{ {
return entry; return entry;
} }
EntryDict.TryGetValue(pathl, out entry); EntryDict.TryGetValue(path, out entry);
} }
return entry; return entry;
} }
@ -357,6 +356,30 @@ namespace CodeWalker.GameFiles
} }
return file; return file;
} }
public async Task<T> GetFileAsync<T>(RpfEntry e) where T : class, PackedFile, new()
{
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)
{
file = new T();
file.Load(data, entry);
}
return file;
} catch(Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile
{ {
byte[] data = null; byte[] data = null;
@ -366,10 +389,42 @@ namespace CodeWalker.GameFiles
data = entry.File.ExtractFile(entry); data = entry.File.ExtractFile(entry);
} }
if (data != null) if (data != null)
{
try
{ {
file.Load(data, entry); file.Load(data, entry);
return true; return true;
} }
catch(Exception ex)
{
Console.WriteLine($"Error occured while loading {entry.Name} at {entry.Path}:\n{ex}");
throw;
}
}
return false;
}
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)
{
data = await entry.File.ExtractFileAsync(entry).ConfigureAwait(false);
}
if (data != null && data.Length > 0)
{
try
{
file.Load(data, entry);
return true;
}
catch(Exception ex)
{
Console.WriteLine($"Error occured while loading {entry.Name} at {entry.Path}:\n{ex}");
throw;
}
}
return false; return false;
} }
@ -386,71 +441,64 @@ namespace CodeWalker.GameFiles
JenkIndex.Ensure(file.Name); JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
var nlow = entry.NameLower; var name = entry.Name;
if (string.IsNullOrEmpty(nlow)) continue; if (string.IsNullOrEmpty(name))
continue;
//JenkIndex.Ensure(entry.Name); //JenkIndex.Ensure(entry.Name);
//JenkIndex.Ensure(nlow); //JenkIndex.Ensure(nlow);
int ind = nlow.LastIndexOf('.'); var nameWithoutExtension = entry.ShortName;
if (ind > 0) JenkIndex.EnsureBoth(nameWithoutExtension);
{
JenkIndex.Ensure(entry.Name.Substring(0, ind));
JenkIndex.Ensure(nlow.Substring(0, ind));
//if (ind < entry.Name.Length - 2) //if (ind < entry.Name.Length - 2)
//{ //{
// JenkIndex.Ensure(entry.Name.Substring(0, ind) + ".#" + entry.Name.Substring(ind + 2)); // JenkIndex.Ensure(entry.Name.Substring(0, ind) + ".#" + entry.Name.Substring(ind + 2));
// JenkIndex.Ensure(entry.NameLower.Substring(0, ind) + ".#" + entry.NameLower.Substring(ind + 2)); // JenkIndex.Ensure(entry.NameLower.Substring(0, ind) + ".#" + entry.NameLower.Substring(ind + 2));
//} //}
}
else
{
JenkIndex.Ensure(entry.Name);
JenkIndex.Ensure(nlow);
}
if (BuildExtendedJenkIndex) if (BuildExtendedJenkIndex)
{ {
if (nlow.EndsWith(".ydr"))// || nlow.EndsWith(".yft")) //do yft's get lods? if (name.EndsWith(".ydr", StringComparison.OrdinalIgnoreCase))// || nlow.EndsWith(".yft")) //do yft's get lods?
{ {
var sname = nlow.Substring(0, nlow.Length - 4); var sname = entry.ShortName;
JenkIndex.Ensure(sname + "_lod"); var nameLod = sname + "_lod";
JenkIndex.Ensure(sname + "_loda"); JenkIndex.EnsureLower(nameLod);
JenkIndex.Ensure(sname + "_lodb"); JenkIndex.EnsureLower(nameLod + 'a');
JenkIndex.EnsureLower(nameLod + 'b');
} }
if (nlow.EndsWith(".ydd")) else if (name.EndsWith(".ydd", StringComparison.OrdinalIgnoreCase))
{ {
if (nlow.EndsWith("_children.ydd")) if (name.EndsWith("_children.ydd", StringComparison.OrdinalIgnoreCase))
{ {
var strn = nlow.Substring(0, nlow.Length - 13); var strn = entry.Name.Substring(0, name.Length - 13);
JenkIndex.Ensure(strn); JenkIndex.EnsureLower(strn);
JenkIndex.Ensure(strn + "_lod"); var nameChildrenLod = strn + "_lod";
JenkIndex.Ensure(strn + "_loda"); JenkIndex.EnsureLower(nameChildrenLod);
JenkIndex.Ensure(strn + "_lodb"); JenkIndex.EnsureLower(nameChildrenLod + 'a');
JenkIndex.EnsureLower(nameChildrenLod + 'b');
} }
var idx = nlow.LastIndexOf('_'); var idx = name.LastIndexOf('_');
if (idx > 0) if (idx > 0)
{ {
var str1 = nlow.Substring(0, idx); var str1 = name.Substring(0, idx);
var idx2 = str1.LastIndexOf('_'); var idx2 = str1.LastIndexOf('_');
if (idx2 > 0) if (idx2 > 0)
{ {
var str2 = str1.Substring(0, idx2); var str2 = str1.Substring(0, idx2);
JenkIndex.Ensure(str2 + "_lod"); JenkIndex.EnsureLower(str2 + "_lod");
var maxi = 100; var maxi = 100;
for (int i = 1; i <= maxi; i++) for (int i = 1; i <= maxi; i++)
{ {
var str3 = str2 + "_" + i.ToString().PadLeft(2, '0'); var str3 = str2 + '_' + i.ToString().PadLeft(2, '0') + "_lod";
//JenkIndex.Ensure(str3); //JenkIndex.Ensure(str3);
JenkIndex.Ensure(str3 + "_lod"); JenkIndex.EnsureLower(str3);
} }
} }
} }
} }
if (nlow.EndsWith(".sps")) else if(name.EndsWith(".sps", StringComparison.OrdinalIgnoreCase))
{ {
JenkIndex.Ensure(nlow);//for shader preset filename hashes! JenkIndex.EnsureLower(entry.Name);//for shader preset filename hashes!
} }
if (nlow.EndsWith(".awc")) //create audio container path hashes... else if(name.EndsWith(".awc", StringComparison.OrdinalIgnoreCase)) //create audio container path hashes...
{ {
string[] parts = entry.Path.Split('\\'); string[] parts = entry.Path.Split('\\');
int pl = parts.Length; int pl = parts.Length;
@ -459,21 +507,18 @@ namespace CodeWalker.GameFiles
string fn = parts[pl - 1]; string fn = parts[pl - 1];
string fd = parts[pl - 2]; string fd = parts[pl - 2];
string hpath = fn.Substring(0, fn.Length - 4); string hpath = fn.Substring(0, fn.Length - 4);
if (fd.EndsWith(".rpf")) if (fd.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
{ {
fd = fd.Substring(0, fd.Length - 4); fd = fd.Substring(0, fd.Length - 4);
} }
hpath = fd + "/" + hpath; hpath = fd + '/' + hpath;
if (parts[pl - 3] != "sfx")
{ }//no hit
JenkIndex.Ensure(hpath); JenkIndex.EnsureLower(hpath);
} }
} }
if (nlow.EndsWith(".nametable")) else if(name.EndsWith(".nametable", StringComparison.OrdinalIgnoreCase))
{ {
RpfBinaryFileEntry binfe = entry as RpfBinaryFileEntry; if (entry is RpfBinaryFileEntry binfe)
if (binfe != null)
{ {
byte[] data = file.ExtractFile(binfe); byte[] data = file.ExtractFile(binfe);
if (data != null) if (data != null)
@ -487,9 +532,8 @@ namespace CodeWalker.GameFiles
string str = sb.ToString(); string str = sb.ToString();
if (!string.IsNullOrEmpty(str)) if (!string.IsNullOrEmpty(str))
{ {
string strl = str.ToLowerInvariant();
//JenkIndex.Ensure(str); //JenkIndex.Ensure(str);
JenkIndex.Ensure(strl); JenkIndex.EnsureLower(str);
////DirMod_Sounds_ entries apparently can be used to infer SP audio strings ////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
////no luck here yet though ////no luck here yet though
@ -508,8 +552,6 @@ namespace CodeWalker.GameFiles
} }
} }
} }
else
{ }
} }
} }
} }

View File

@ -96,10 +96,12 @@
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using DirectXTexNet;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -122,8 +124,6 @@ namespace CodeWalker.Utils
public static class DDSIO public static class DDSIO
{ {
public static byte[] GetPixels(Texture texture, int mip) public static byte[] GetPixels(Texture texture, int mip)
{ {
//dexyfex version //dexyfex version
@ -159,29 +159,13 @@ namespace CodeWalker.Utils
bool swaprb = true; bool swaprb = true;
if (DirectXTexNet.TexHelper.Instance.IsCompressed((DirectXTexNet.DXGI_FORMAT)format))
{
px = Decompress(imgdata, w, h, format);
} else
{
switch (format) switch (format)
{ {
// compressed
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: // TextureFormat.D3DFMT_DXT1
px = DecompressDxt1(imgdata, w, h);
break;
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: // TextureFormat.D3DFMT_DXT3
px = DecompressDxt3(imgdata, w, h);
break;
case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: // TextureFormat.D3DFMT_DXT5
px = DecompressDxt5(imgdata, w, h);
break;
case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: // TextureFormat.D3DFMT_ATI1
px = DecompressBC4(imgdata, w, h);
break;
case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: // TextureFormat.D3DFMT_ATI2
px = DecompressBC5(imgdata, w, h);
break;
case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: // TextureFormat.D3DFMT_BC7
//BC7 TODO!!
break;
// uncompressed
case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: // TextureFormat.D3DFMT_A1R5G5B5 case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: // TextureFormat.D3DFMT_A1R5G5B5
px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing
break; break;
@ -205,8 +189,63 @@ namespace CodeWalker.Utils
swaprb = false; swaprb = false;
break; break;
default: default:
break; //shouldn't get here... px = imgdata;
break;
} }
}
//switch (format)
//{
// // compressed
// case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM: // TextureFormat.D3DFMT_DXT1
// px = DecompressDxt1(imgdata, w, h);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM: // TextureFormat.D3DFMT_DXT3
// px = DecompressDxt3(imgdata, w, h);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_BC3_UNORM: // TextureFormat.D3DFMT_DXT5
// px = DecompressDxt5(imgdata, w, h);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM: // TextureFormat.D3DFMT_ATI1
// px = Decompress(imgdata, w, h, DXGI_FORMAT.DXGI_FORMAT_BC4_UNORM);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM: // TextureFormat.D3DFMT_ATI2
// px = Decompress(imgdata, w, h, DXGI_FORMAT.DXGI_FORMAT_BC5_UNORM);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM: // TextureFormat.D3DFMT_BC7
// px = Decompress(imgdata, w, h, DXGI_FORMAT.DXGI_FORMAT_BC7_UNORM);
// //BC7 TODO!!
// break;
// // uncompressed
// case DXGI_FORMAT.DXGI_FORMAT_B5G5R5A1_UNORM: // TextureFormat.D3DFMT_A1R5G5B5
// px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing
// break;
// case DXGI_FORMAT.DXGI_FORMAT_A8_UNORM: // TextureFormat.D3DFMT_A8
// px = ConvertA8ToRGBA8(imgdata, w, h);
// swaprb = false;
// break;
// case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: // TextureFormat.D3DFMT_A8B8G8R8
// case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_TYPELESS:
// px = imgdata;
// break;
// case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: // TextureFormat.D3DFMT_L8
// px = ConvertR8ToRGBA8(imgdata, w, h);
// break;
// case DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM: // TextureFormat.D3DFMT_A8R8G8B8
// px = imgdata;
// swaprb = false;
// break;
// case DXGI_FORMAT.DXGI_FORMAT_B8G8R8X8_UNORM: // TextureFormat.D3DFMT_X8R8G8B8
// px = imgdata;
// swaprb = false;
// break;
// default:
// break; //shouldn't get here...
//}
if (swaprb && (px != null)) if (swaprb && (px != null))
{ {
@ -564,7 +603,10 @@ namespace CodeWalker.Utils
images[i].format = format; //(DXGI_FORMAT)img.Format; images[i].format = format; //(DXGI_FORMAT)img.Format;
images[i].pixels = buf + add; images[i].pixels = buf + add;
DXTex.ComputePitch(images[i].format, images[i].width, images[i].height, out images[i].rowPitch, out images[i].slicePitch, 0); DXTex.ComputePitch(images[i].format, images[i].width, images[i].height, out var rowPitch, out var slicePitch, 0);
images[i].rowPitch = (int)rowPitch;
images[i].slicePitch = (int)slicePitch;
//DXTex.ComputePitch(images[i].format, images[i].width, images[i].height, out images[i].rowPitch, out images[i].slicePitch, 0);
add += images[i].slicePitch; add += images[i].slicePitch;
div *= 2; div *= 2;
@ -2641,6 +2683,48 @@ namespace CodeWalker.Utils
} }
} }
public static unsafe byte[] Decompress(Byte[] data, int width, int height, DXGI_FORMAT format)
{
Console.WriteLine(format);
Console.WriteLine(width);
Console.WriteLine(height);
long inputRowPitch;
long inputSlicePitch;
TexHelper.Instance.ComputePitch((DirectXTexNet.DXGI_FORMAT)format, width, height, out inputRowPitch, out inputSlicePitch, DirectXTexNet.CP_FLAGS.NONE);
DirectXTexNet.DXGI_FORMAT FormatDecompressed;
if (format.ToString().Contains("SRGB"))
FormatDecompressed = DirectXTexNet.DXGI_FORMAT.R8G8B8A8_UNORM_SRGB;
else
FormatDecompressed = DirectXTexNet.DXGI_FORMAT.R8G8B8A8_UNORM;
byte* buf;
buf = (byte*)Marshal.AllocHGlobal((int)inputSlicePitch);
Marshal.Copy(data, 0, (IntPtr)buf, (int)inputSlicePitch);
DirectXTexNet.Image inputImage = new DirectXTexNet.Image(
width, height, (DirectXTexNet.DXGI_FORMAT)format, inputRowPitch,
inputSlicePitch, (IntPtr)buf, null);
DirectXTexNet.TexMetadata texMetadata = new DirectXTexNet.TexMetadata(width, height, 1, 1, 1, 0, 0,
(DirectXTexNet.DXGI_FORMAT)format, DirectXTexNet.TEX_DIMENSION.TEXTURE2D);
ScratchImage scratchImage = TexHelper.Instance.InitializeTemporary(
new DirectXTexNet.Image[] { inputImage }, texMetadata, null);
using (var decomp = scratchImage.Decompress(0, FormatDecompressed))
{
byte[] result = new byte[4 * width * height];
Marshal.Copy(decomp.GetImage(0).Pixels, result, 0, result.Length);
inputImage = null;
scratchImage.Dispose();
return result;
}
}
internal static byte[] DecompressBC5(byte[] imageData, int width, int height) internal static byte[] DecompressBC5(byte[] imageData, int width, int height)
{ {

View File

@ -1,4 +1,6 @@
/* 
/*
Copyright(c) 2015 Neodymium Copyright(c) 2015 Neodymium
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
@ -29,6 +31,12 @@ using System;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using CodeWalker.Core.Utils;
using System.Buffers.Binary;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Threading;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -98,18 +106,40 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public DataReader(Stream stream, Endianess endianess = Endianess.LittleEndian) public DataReader(Stream stream, Endianess endianess = Endianess.LittleEndian)
{ {
this.baseStream = stream; if (stream is not null)
{
this.baseStream = Stream.Synchronized(stream);
}
this.Endianess = endianess; this.Endianess = endianess;
} }
public virtual Stream GetStream()
{
return baseStream;
}
internal virtual void SetPositionAfterRead(Stream stream)
{
return;
}
/// <summary> /// <summary>
/// Reads data from the underlying stream. This is the only method that directly accesses /// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream. /// the data in the underlying stream.
/// </summary> /// </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 ??= new byte[count];
baseStream.Read(buffer, 0, count);
try
{
stream.Read(buffer, 0, count);
}
finally
{
SetPositionAfterRead(stream);
}
// handle endianess // handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian)) if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
@ -125,11 +155,22 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public virtual byte ReadByte() public virtual byte ReadByte()
{ {
var result = baseStream.ReadByte(); var stream = GetStream();
int result;
try
{
result = stream.ReadByte();
}
finally
{
SetPositionAfterRead(stream);
}
if (result == -1) if (result == -1)
{ {
throw new InvalidOperationException("Tried to read from stream beyond end!"); throw new InvalidOperationException("Tried to read from stream beyond end!");
} }
return (byte) result; return (byte) result;
} }
@ -205,21 +246,37 @@ namespace CodeWalker.GameFiles
return BitConverter.ToDouble(ReadFromStream(8, buffer: _buffer), 0); return BitConverter.ToDouble(ReadFromStream(8, buffer: _buffer), 0);
} }
/// <summary>
/// Reads a string.
/// </summary>
unsafe public string ReadStringLength(int length)
{
if (length == 0)
{
return string.Empty;
}
var 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, Math.Min(charsRead, maxLength));
}
/// <summary> /// <summary>
/// Reads a string. /// Reads a string.
/// </summary> /// </summary>
unsafe public string ReadString(int maxLength = 1024) unsafe public string ReadString(int maxLength = 1024)
{ {
var bytes = stackalloc byte[Math.Min(maxLength, 1024)]; var bytes = stackalloc byte[Math.Min(maxLength, 1024)];
var chars = stackalloc char[Math.Min(maxLength, 1024)];
var temp = ReadByte(); var temp = ReadByte();
var charsRead = 0; var charsRead = 0;
while (temp != 0 && (Length == -1 || Position <= Length)) while (temp != 0 && (Length == -1 || Position <= Length))
{ {
if (charsRead > 1023) if (charsRead < maxLength && charsRead < 1024)
{
throw new Exception("String too long!");
}
if (charsRead < maxLength)
{ {
bytes[charsRead] = temp; bytes[charsRead] = temp;
} }
@ -227,7 +284,10 @@ namespace CodeWalker.GameFiles
charsRead++; charsRead++;
} }
return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength)); var charsCount = Encoding.UTF8.GetChars(bytes, charsRead, chars, Math.Min(maxLength, 1024));
return new string(chars, 0, charsCount);
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
} }
unsafe public string ReadStringLower() unsafe public string ReadStringLower()
@ -357,12 +417,23 @@ namespace CodeWalker.GameFiles
} }
} }
internal virtual Stream GetStream()
{
return baseStream;
}
internal virtual void SetPositionAfterWrite(Stream stream)
{ }
/// <summary> /// <summary>
/// Initializes a new data writer for the specified stream. /// Initializes a new data writer for the specified stream.
/// </summary> /// </summary>
public DataWriter(Stream stream, Endianess endianess = Endianess.LittleEndian) public DataWriter(Stream stream, Endianess endianess = Endianess.LittleEndian)
{ {
this.baseStream = stream; if (stream is not null)
{
this.baseStream = Stream.Synchronized(stream);
}
this.Endianess = endianess; this.Endianess = endianess;
} }
@ -370,17 +441,65 @@ namespace CodeWalker.GameFiles
/// Writes data to the underlying stream. This is the only method that directly accesses /// Writes data to the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream. /// the data in the underlying stream.
/// </summary> /// </summary>
protected virtual void WriteToStream(byte[] value, bool ignoreEndianess = false) protected virtual void WriteToStream(byte[] value, bool ignoreEndianess = false, int count = -1, int offset = 0)
{ {
var stream = GetStream();
if (count == -1)
{
count = value.Length;
}
if (!ignoreEndianess && (Endianess == Endianess.BigEndian)) if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{ {
var buffer = (byte[])value.Clone(); var buffer = ArrayPool<byte>.Shared.Rent(count);
Array.Reverse(buffer); try
baseStream.Write(buffer, 0, buffer.Length); {
Array.Copy(value, offset, buffer, 0, count);
Array.Reverse(buffer, 0, count);
stream.Write(buffer, 0, count);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
} }
else else
{ {
baseStream.Write(value, 0, value.Length); stream.Write(value, offset, count);
}
SetPositionAfterWrite(stream);
}
protected virtual void WriteToStream(Span<byte> value, bool ignoreEndianess = false)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(value.Length);
try
{
value.CopyTo(sharedBuffer);
WriteToStream(sharedBuffer, count: value.Length);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
protected virtual void WriteToStream(Memory<byte> buffer, bool ignoreEndianess = false)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
WriteToStream(array.Array!, offset: array.Offset, count: array.Count);
return;
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.Span.CopyTo(sharedBuffer);
WriteToStream(sharedBuffer, count: buffer.Length);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
} }
} }
@ -400,6 +519,16 @@ namespace CodeWalker.GameFiles
WriteToStream(value, true); WriteToStream(value, true);
} }
public void Write(Span<byte> value)
{
WriteToStream(value, true);
}
public void Write(Memory<byte> value)
{
WriteToStream(value, true);
}
/// <summary> /// <summary>
/// Writes a signed 16-bit value. /// Writes a signed 16-bit value.
/// </summary> /// </summary>
@ -512,12 +641,12 @@ namespace CodeWalker.GameFiles
public virtual void Dispose() public virtual void Dispose()
{ {
baseStream.Dispose(); baseStream?.Dispose();
} }
public virtual void Close() public virtual void Close()
{ {
baseStream.Close(); baseStream?.Close();
} }
} }

View File

@ -1,4 +1,5 @@
/* 
/*
Copyright(c) 2015 Neodymium Copyright(c) 2015 Neodymium
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy

View File

@ -28,6 +28,7 @@
//shamelessly stolen //shamelessly stolen
using CodeWalker.Core.Properties; using CodeWalker.Core.Properties;
using CodeWalker.Core.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -297,7 +298,7 @@ namespace CodeWalker.GameFiles
{ {
using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream()) using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream())
{ {
ds.CopyTo(outstr); ds.CopyToFast(outstr);
b = outstr.GetBuffer(); b = outstr.GetBuffer();
} }
} }
@ -1220,7 +1221,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyTo(fs); ms.CopyToFast(fs);
} }
} }
@ -1307,7 +1308,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyTo(fs); ms.CopyToFast(fs);
} }
} }
@ -1445,7 +1446,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyTo(fs); ms.CopyToFast(fs);
} }
} }
} }

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -28,6 +29,11 @@ namespace CodeWalker.GameFiles
HashHex = "0x" + HashUint.ToString("X"); HashHex = "0x" + HashUint.ToString("X");
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToLower(char c)
{
return (c >= 'A' && c <= 'Z') ? (char)(c - 'A' + 'a') : c;
}
public static uint GenHash(string text, JenkHashInputEncoding encoding) public static uint GenHash(string text, JenkHashInputEncoding encoding)
{ {
@ -64,8 +70,47 @@ namespace CodeWalker.GameFiles
uint h = 0; uint h = 0;
for (int i = 0; i < text.Length; i++) 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);
h += (byte)char.ToLowerInvariant(text[i]); 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;
}
public static uint GenHash(ReadOnlySpan<char> text)
{
if (text == null) return 0;
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)text[i];
h += (h << 10); h += (h << 10);
h ^= (h >> 6); h ^= (h >> 6);
} }
@ -231,41 +276,42 @@ namespace CodeWalker.GameFiles
public static class JenkIndex public static class JenkIndex
{ {
public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount, 1500000); public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(32, 1500000);
public static void Clear() public static void Ensure(string str)
{
Index.Clear();
}
public static bool Ensure(string str)
{ {
uint hash = JenkHash.GenHash(str); uint hash = JenkHash.GenHash(str);
if (hash == 0) return true; Ensure(str, hash);
if (Index.ContainsKey(hash))
{
return true;
}
lock (Index)
{
Index[hash] = str;
return false;
}
} }
public static bool EnsureLower(string str) public static void Ensure(string str, uint hash)
{ {
uint hash = JenkHash.GenHashLower(str); if (hash == 0) return;
if (hash == 0) return true;
if (Index.ContainsKey(hash)) if (Index.ContainsKey(hash))
{ {
return true; return;
} }
Index.TryAdd(hash, str); Index.TryAdd(hash, str);
return false;
} }
public static void EnsureLower(string str)
{
uint hash = JenkHash.GenHashLower(str);
Ensure(str, hash);
}
public static void EnsureBoth(string str)
{
uint hash = JenkHash.GenHash(str);
uint hashLower = JenkHash.GenHashLower(str);
Ensure(str, hash);
if (hash != hashLower)
{
Ensure(str, hashLower);
}
}
public static void AddRange(params string[] strings) public static void AddRange(params string[] strings)
{ {
foreach(var s in strings) foreach(var s in strings)
@ -307,10 +353,9 @@ namespace CodeWalker.GameFiles
return res; return res;
} }
public static string[] GetAllStrings() public static ICollection<string> GetAllStrings()
{ {
string[] res = null; var res = Index.Values;
res = Index.Values.ToArray();
return res; return res;
} }

View File

@ -19,7 +19,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (entry.NameLower.EndsWith("cache_y.dat"))// || entry.NameLower.EndsWith("cache_y_bank.dat")) if (entry.Name.EndsWith("cache_y.dat", StringComparison.OrdinalIgnoreCase))// || entry.NameLower.EndsWith("cache_y_bank.dat"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
var cdfile = RpfMan.GetFile<CacheDatFile>(entry); var cdfile = RpfMan.GetFile<CacheDatFile>(entry);
@ -63,7 +63,7 @@ namespace CodeWalker.GameFiles
{ {
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
if (entry.NameLower.EndsWith(".dat") && entry.NameLower.StartsWith("heightmap")) if (entry.IsExtension(".dat") && entry.Name.StartsWith("heightmap", StringComparison.OrdinalIgnoreCase))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
HeightmapFile hmf = null; HeightmapFile hmf = null;
@ -100,7 +100,7 @@ namespace CodeWalker.GameFiles
{ {
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
if (entry.NameLower.EndsWith(".dat") && entry.NameLower.StartsWith("waterheight")) if (entry.IsExtension(".dat") && entry.Name.StartsWith("waterheight", StringComparison.OrdinalIgnoreCase))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
WatermapFile wmf = null; WatermapFile wmf = null;
@ -147,7 +147,7 @@ namespace CodeWalker.GameFiles
var rbfe = rfe as RpfBinaryFileEntry; var rbfe = rfe as RpfBinaryFileEntry;
if ((rfe == null) || (rbfe == null)) continue; if ((rfe == null) || (rbfe == null)) continue;
if (rfe.Name.EndsWith(".rel", StringComparison.OrdinalIgnoreCase)) if (rfe.IsExtension(".rel"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -315,8 +315,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
var n = entry.NameLower; if (entry.IsExtension(".ymt"))
if (n.EndsWith(".ymt"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
//YmtFile ymtfile = RpfMan.GetFile<YmtFile>(entry); //YmtFile ymtfile = RpfMan.GetFile<YmtFile>(entry);
@ -324,7 +323,7 @@ namespace CodeWalker.GameFiles
//{ //{
//} //}
var sn = entry.GetShortName(); var sn = entry.ShortName;
uint un; uint un;
if (uint.TryParse(sn, out un)) if (uint.TryParse(sn, out un))
{ {
@ -388,8 +387,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
//{ //{
var n = entry.NameLower; if (entry.IsExtension(".awc"))
if (n.EndsWith(".awc"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
var awcfile = RpfMan.GetFile<AwcFile>(entry); var awcfile = RpfMan.GetFile<AwcFile>(entry);
@ -417,8 +415,8 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
var n = entry.NameLower; var n = entry.Name;
//if (n.EndsWith(".ymap")) //if (n.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase))
//{ //{
// UpdateStatus?.Invoke(string.Format(entry.Path)); // UpdateStatus?.Invoke(string.Format(entry.Path));
// YmapFile ymapfile = RpfMan.GetFile<YmapFile>(entry); // YmapFile ymapfile = RpfMan.GetFile<YmapFile>(entry);
@ -427,7 +425,7 @@ namespace CodeWalker.GameFiles
// MetaTypes.EnsureMetaTypes(ymapfile.Meta); // MetaTypes.EnsureMetaTypes(ymapfile.Meta);
// } // }
//} //}
//else if (n.EndsWith(".ytyp")) //else if (n.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase))
//{ //{
// UpdateStatus?.Invoke(string.Format(entry.Path)); // UpdateStatus?.Invoke(string.Format(entry.Path));
// YtypFile ytypfile = RpfMan.GetFile<YtypFile>(entry); // YtypFile ytypfile = RpfMan.GetFile<YtypFile>(entry);
@ -436,7 +434,7 @@ namespace CodeWalker.GameFiles
// MetaTypes.EnsureMetaTypes(ytypfile.Meta); // MetaTypes.EnsureMetaTypes(ytypfile.Meta);
// } // }
//} //}
//else if (n.EndsWith(".ymt")) //else if (n.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
//{ //{
// UpdateStatus?.Invoke(string.Format(entry.Path)); // UpdateStatus?.Invoke(string.Format(entry.Path));
// YmtFile ymtfile = RpfMan.GetFile<YmtFile>(entry); // YmtFile ymtfile = RpfMan.GetFile<YmtFile>(entry);
@ -447,7 +445,7 @@ namespace CodeWalker.GameFiles
//} //}
if (n.EndsWith(".ymap") || n.EndsWith(".ytyp") || n.EndsWith(".ymt")) if (n.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase) || n.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase) || n.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase))
{ {
var rfe = entry as RpfResourceFileEntry; var rfe = entry as RpfResourceFileEntry;
if (rfe == null) continue; if (rfe == null) continue;
@ -465,7 +463,7 @@ namespace CodeWalker.GameFiles
if (xml.Length != xml2.Length) if (xml.Length != xml2.Length)
{ } { }
if ((xml != xml2) && (!n.EndsWith("srl.ymt") && !n.StartsWith("des_"))) if ((xml != xml2) && (!n.EndsWith("srl.ymt", StringComparison.OrdinalIgnoreCase) && !n.StartsWith("des_", StringComparison.OrdinalIgnoreCase)))
{ } { }
} }
@ -501,13 +499,13 @@ namespace CodeWalker.GameFiles
try try
#endif #endif
{ {
var n = entry.NameLower; var n = entry.Name;
if (!(n.EndsWith(".pso") || if (!(n.EndsWith(".pso", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ymt") || n.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ymf") || n.EndsWith(".ymf", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ymap") || n.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ytyp") || n.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".cut"))) n.EndsWith(".cut", StringComparison.OrdinalIgnoreCase)))
continue; //PSO files seem to only have these extensions continue; //PSO files seem to only have these extensions
var fentry = entry as RpfFileEntry; var fentry = entry as RpfFileEntry;
@ -597,12 +595,12 @@ namespace CodeWalker.GameFiles
{ {
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
var n = entry.NameLower; var n = entry.Name;
if (!(n.EndsWith(".ymt") || if (!(n.EndsWith(".ymt", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ymf") || n.EndsWith(".ymf", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ymap") || n.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".ytyp") || n.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase) ||
n.EndsWith(".cut"))) n.EndsWith(".cut", StringComparison.OrdinalIgnoreCase)))
continue; //PSO files seem to only have these extensions continue; //PSO files seem to only have these extensions
var fentry = entry as RpfFileEntry; var fentry = entry as RpfFileEntry;
@ -686,7 +684,7 @@ namespace CodeWalker.GameFiles
var rfe = entry as RpfFileEntry; var rfe = entry as RpfFileEntry;
if (rfe == null) continue; if (rfe == null) continue;
if (rfe.NameLower.EndsWith(".cut")) if (rfe.IsExtension(".cut"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -729,7 +727,7 @@ namespace CodeWalker.GameFiles
var rfe = entry as RpfFileEntry; var rfe = entry as RpfFileEntry;
if (rfe == null) continue; if (rfe == null) continue;
if (rfe.NameLower.EndsWith(".yld")) if (rfe.IsExtension(".yld"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -769,7 +767,7 @@ namespace CodeWalker.GameFiles
var rfe = entry as RpfFileEntry; var rfe = entry as RpfFileEntry;
if (rfe == null) continue; if (rfe == null) continue;
if (rfe.NameLower.EndsWith(".yed")) if (rfe.IsExtension(".yed"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -813,7 +811,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
//{ //{
if (entry.NameLower.EndsWith(".ycd")) if (entry.IsExtension(".ycd"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YcdFile ycd1 = RpfMan.GetFile<YcdFile>(entry); YcdFile ycd1 = RpfMan.GetFile<YcdFile>(entry);
@ -1084,7 +1082,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ytd")) if (entry.IsExtension(".ytd"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YtdFile ytdfile = null; YtdFile ytdfile = null;
@ -1103,7 +1101,7 @@ namespace CodeWalker.GameFiles
{ {
var dds = Utils.DDSIO.GetDDSFile(tex); var dds = Utils.DDSIO.GetDDSFile(tex);
var tex2 = Utils.DDSIO.GetTexture(dds); var tex2 = Utils.DDSIO.GetTexture(dds);
if (!tex.Name.StartsWith("script_rt")) if (!tex.Name.StartsWith("script_rt", StringComparison.OrdinalIgnoreCase))
{ {
if (tex.Data?.FullData?.Length != tex2.Data?.FullData?.Length) if (tex.Data?.FullData?.Length != tex2.Data?.FullData?.Length)
{ } { }
@ -1181,7 +1179,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ybn")) if (entry.IsExtension(".ybn"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YbnFile ybn = null; YbnFile ybn = null;
@ -1353,7 +1351,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ydr")) if (entry.IsExtension(".ydr"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YdrFile ydr = null; YdrFile ydr = null;
@ -1412,7 +1410,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ydd")) if (entry.IsExtension(".ydd"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YddFile ydd = null; YddFile ydd = null;
@ -1481,7 +1479,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".yft")) if (entry.IsExtension(".yft"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YftFile yft = null; YftFile yft = null;
@ -1558,7 +1556,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ypt")) if (entry.IsExtension(".ypt"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YptFile ypt = null; YptFile ypt = null;
@ -1616,7 +1614,7 @@ namespace CodeWalker.GameFiles
{ {
//try //try
{ {
if (entry.NameLower.EndsWith(".ynv")) if (entry.IsExtension(".ynv"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YnvFile ynv = null; YnvFile ynv = null;
@ -1697,9 +1695,9 @@ namespace CodeWalker.GameFiles
var rfe = entry as RpfFileEntry; var rfe = entry as RpfFileEntry;
if (rfe == null) continue; if (rfe == null) continue;
if (rfe.NameLower.EndsWith(".yvr")) if (rfe.IsExtension(".yvr"))
{ {
if (rfe.NameLower == "agencyprep001.yvr") continue; //this file seems corrupted if (rfe.Name.Equals("agencyprep001.yvr", StringComparison.OrdinalIgnoreCase)) continue; //this file seems corrupted
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -1745,10 +1743,9 @@ namespace CodeWalker.GameFiles
try try
#endif #endif
{ {
var rfe = entry as RpfFileEntry; if (entry is not RpfFileEntry rfe || rfe == null) continue;
if (rfe == null) continue;
if (rfe.NameLower.EndsWith(".ywr")) if (rfe.IsExtension(".ywr"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
@ -1789,7 +1786,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (entry.NameLower.EndsWith(".ymap")) if (entry.IsExtension(".ymap"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YmapFile ymapfile = RpfMan.GetFile<YmapFile>(entry); YmapFile ymapfile = RpfMan.GetFile<YmapFile>(entry);
@ -1817,7 +1814,7 @@ namespace CodeWalker.GameFiles
try try
{ {
if (rfe.NameLower.EndsWith(".ypdb")) if (rfe.IsExtension(".ypdb"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YpdbFile ypdb = RpfMan.GetFile<YpdbFile>(entry); YpdbFile ypdb = RpfMan.GetFile<YpdbFile>(entry);
@ -1866,7 +1863,7 @@ namespace CodeWalker.GameFiles
try try
{ {
if (rfe.NameLower.EndsWith(".yfd")) if (rfe.IsExtension(".yfd"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
YfdFile yfd = RpfMan.GetFile<YfdFile>(entry); YfdFile yfd = RpfMan.GetFile<YfdFile>(entry);
@ -1913,7 +1910,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (entry.NameLower.EndsWith(".mrf")) if (entry.IsExtension(".mrf"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
MrfFile mrffile = RpfMan.GetFile<MrfFile>(entry); MrfFile mrffile = RpfMan.GetFile<MrfFile>(entry);
@ -2083,7 +2080,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (entry.NameLower.EndsWith(".fxc")) if (entry.IsExtension(".fxc"))
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(string.Format(entry.Path));
var fxcfile = RpfMan.GetFile<FxcFile>(entry); var fxcfile = RpfMan.GetFile<FxcFile>(entry);
@ -2298,7 +2295,7 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (doydr && entry.NameLower.EndsWith(".ydr")) if (doydr && entry.IsExtension(".ydr"))
{ {
UpdateStatus?.Invoke(entry.Path); UpdateStatus?.Invoke(entry.Path);
YdrFile ydr = RpfMan.GetFile<YdrFile>(entry); YdrFile ydr = RpfMan.GetFile<YdrFile>(entry);
@ -2327,7 +2324,7 @@ namespace CodeWalker.GameFiles
} }
} }
} }
else if (doydd & entry.NameLower.EndsWith(".ydd")) else if (doydd & entry.IsExtension(".ydd"))
{ {
UpdateStatus?.Invoke(entry.Path); UpdateStatus?.Invoke(entry.Path);
YddFile ydd = RpfMan.GetFile<YddFile>(entry); YddFile ydd = RpfMan.GetFile<YddFile>(entry);
@ -2359,7 +2356,7 @@ namespace CodeWalker.GameFiles
} }
} }
} }
else if (doyft && entry.NameLower.EndsWith(".yft")) else if (doyft && entry.IsExtension(".yft"))
{ {
UpdateStatus?.Invoke(entry.Path); UpdateStatus?.Invoke(entry.Path);
YftFile yft = RpfMan.GetFile<YftFile>(entry); YftFile yft = RpfMan.GetFile<YftFile>(entry);

View File

@ -14,11 +14,12 @@ namespace CodeWalker
public long MaxMemoryUsage = 536870912; //512mb public long MaxMemoryUsage = 536870912; //512mb
public long CurrentMemoryUsage = 0; public long CurrentMemoryUsage = 0;
public double CacheTime = 10.0; //seconds to keep something that's not used public double CacheTime = 10.0; //seconds to keep something that's not used
public double LoadingCacheTime = 1.0;
public DateTime CurrentTime = DateTime.Now; public DateTime CurrentTime = DateTime.Now;
private LinkedList<TVal> loadedList = new LinkedList<TVal>(); private LinkedList<TVal> loadedList = new LinkedList<TVal>();
private object loadedListLock = new object(); private object loadedListLock = new object();
private Dictionary<TKey, LinkedListNode<TVal>> loadedListDict = new Dictionary<TKey, LinkedListNode<TVal>>(); private ConcurrentDictionary<TKey, LinkedListNode<TVal>> loadedListDict = new ConcurrentDictionary<TKey, LinkedListNode<TVal>>();
public int Count public int Count
{ {
@ -45,50 +46,52 @@ namespace CodeWalker
public TVal TryGet(TKey key) public TVal TryGet(TKey key)
{ {
LinkedListNode<TVal> lln = null;
lock (loadedListLock) lock (loadedListLock)
{ {
if (loadedListDict.TryGetValue(key, out lln)) if (loadedListDict.TryGetValue(key, out var lln))
{ {
loadedList.Remove(lln); loadedList.Remove(lln);
loadedList.AddLast(lln); loadedList.AddLast(lln);
lln.Value.LastUseTime = CurrentTime; lln.Value.LastUseTime = CurrentTime;
} }
}
return (lln != null) ? lln.Value : null; return (lln != null) ? lln.Value : null;
} }
}
public bool TryAdd(TKey key, TVal item) public bool TryAdd(TKey key, TVal item)
{ {
item.Key = key; item.Key = key;
if (CanAdd()) if (CanAdd())
{ {
LinkedListNode<TVal> lln;
lock(loadedListLock) lock(loadedListLock)
{ {
var lln = loadedList.AddLast(item); lln = loadedList.AddLast(item);
loadedListDict.Add(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
} }
lln.Value.LastUseTime = CurrentTime;
loadedListDict.TryAdd(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
return true; return true;
} }
else else
{ {
//cache full, check the front of the list for oldest.. //cache full, check the front of the list for oldest..
var oldlln = loadedList.First; var oldlln = loadedList.First;
var cachetime = CacheTime; var cachetime = LoadingCacheTime;
int iter = 0, maxiter = 2; 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 != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime))
{ {
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
lock (loadedListLock) lock (loadedListLock)
{ {
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later.. loadedList.Remove(oldlln); //gc should free up memory later..
} }
loadedListDict.TryRemove(oldlln.Value.Key, out _);
oldlln.Value = null; oldlln.Value = null;
oldlln = null; oldlln = null;
//GC.Collect(); //GC.Collect();
@ -99,12 +102,13 @@ namespace CodeWalker
} }
if (CanAdd()) //see if there's enough memory now... if (CanAdd()) //see if there's enough memory now...
{ {
LinkedListNode<TVal> newlln;
lock(loadedListLock) lock(loadedListLock)
{ {
var newlln = loadedList.AddLast(item); newlln = loadedList.AddLast(item);
loadedListDict.Add(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
} }
loadedListDict.TryAdd(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
return true; return true;
} }
else else
@ -127,8 +131,8 @@ namespace CodeWalker
{ {
loadedList.Clear(); loadedList.Clear();
loadedListDict.Clear(); loadedListDict.Clear();
CurrentMemoryUsage = 0;
} }
Interlocked.Exchange(ref CurrentMemoryUsage, 0);
} }
public void Remove(TKey key) public void Remove(TKey key)
@ -137,22 +141,21 @@ namespace CodeWalker
{ {
return; return;
} }
if (loadedListDict.TryRemove(key, out var n))
{
lock (loadedListLock) lock (loadedListLock)
{ {
LinkedListNode<TVal> n;
if (loadedListDict.TryGetValue(key, out n))
{
loadedListDict.Remove(key);
loadedList.Remove(n); loadedList.Remove(n);
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
} }
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
} }
} }
public void Compact() public void Compact()
{ {
lock(loadedList) lock(loadedListLock)
{ {
var oldlln = loadedList.First; var oldlln = loadedList.First;
while (oldlln != null) while (oldlln != null)
@ -160,7 +163,7 @@ namespace CodeWalker
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break; if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break;
var nextln = oldlln.Next; var nextln = oldlln.Next;
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key); loadedListDict.TryRemove(oldlln.Value.Key, out _);
loadedList.Remove(oldlln); //gc should free up memory later.. loadedList.Remove(oldlln); //gc should free up memory later..
oldlln.Value = null; oldlln.Value = null;

View File

@ -0,0 +1,167 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils
{
public static class StreamingExtensions
{
public static Task<int> ReadAsync(this BinaryReader br, byte[] buffer, int index, int count)
{
return br.BaseStream.ReadAsync(buffer, index, count);
}
public static void CopyToFast(this Stream stream, Stream destination)
{
var buffer = ArrayPool<byte>.Shared.Rent(81920);
try
{
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) != 0)
destination.Write(buffer, 0, read);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
public static async Task CopyToFastAsync(this Stream stream, Stream destination, int bufferSize = 131072, CancellationToken cancellationToken = default)
{
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
private static async Task FinishWriteAsync(Task writeTask, byte[] localBuffer)
{
try
{
await writeTask.ConfigureAwait(false);
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
}
public static ValueTask WriteAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return new ValueTask(stream.WriteAsync(array.Array!, array.Offset, array.Count, cancellationToken));
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.Span.CopyTo(sharedBuffer);
return new ValueTask(FinishWriteAsync(stream.WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer));
}
public static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.CopyTo(sharedBuffer);
stream.Write(sharedBuffer, 0, buffer.Length);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Span<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Memory<byte> buffer)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return stream.Read(array.Array!, array.Offset, array.Count);
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer.Span);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static ValueTask<int> ReadAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return new ValueTask<int>(stream.ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken));
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer);
static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
{
try
{
int result = await readTask.ConfigureAwait(false);
new ReadOnlySpan<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
return result;
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
}
}
}
}

View File

@ -90,22 +90,33 @@ namespace CodeWalker
{ {
if (bytes == null) if (bytes == null)
{ return string.Empty; } //file not found.. { return string.Empty; } //file not found..
var start = 0;
var length = bytes.Length;
if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF)) if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF))
{ {
byte[] newb = new byte[bytes.Length - 3]; start = 3;
for (int i = 3; i < bytes.Length; i++) length = bytes.Length - 3;
{
newb[i - 3] = bytes[i];
} }
bytes = newb; //trim starting byte order mark return Encoding.UTF8.GetString(bytes, start, length);
}
return Encoding.UTF8.GetString(bytes);
} }
public static bool Contains(this string source, string toCheck, StringComparison comp) public static bool Contains(this string source, string toCheck, StringComparison comp)
{ {
return source?.IndexOf(toCheck, comp) >= 0; return source?.IndexOf(toCheck, comp) >= 0;
} }
public static bool EndsWithAny(this string str, params string[] strings)
{
foreach(var searchString in strings)
{
if (str.EndsWith(searchString, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
} }
@ -264,6 +275,12 @@ namespace CodeWalker
public static uint UpdateBit(uint value, int bit, bool flag) public static uint UpdateBit(uint value, int bit, bool flag)
{ {
if (flag) return SetBit(value, bit); if (flag) return SetBit(value, bit);
else return ClearBit(value, bit); else return ClearBit(value, bit);
} }
public static uint RotateLeft(uint value, int count) public static uint RotateLeft(uint value, int count)
@ -275,24 +292,4 @@ namespace CodeWalker
return (value >> count) | (value << (32 - count)); return (value >> count) | (value << (32 - count));
} }
} }
public static class SpanExtensions
{
public static int Read(this Stream stream, Span<byte> buffer)
{
var n = Math.Min(stream.Length - stream.Position, buffer.Length);
if (n <= 0)
return 0;
for (int i = 0; i < buffer.Length; i++)
{
var result = stream.ReadByte();
buffer[i] = (byte)result;
}
return buffer.Length;
}
}
} }

View File

@ -117,6 +117,24 @@ namespace CodeWalker
return new Vector2I(a.X - b.X, a.Y - b.Y); return new Vector2I(a.X - b.X, a.Y - b.Y);
} }
public static bool operator ==(Vector2I a, Vector2I b)
{
return a.Equals(b);
}
public static bool operator !=(Vector2I a, Vector2I b)
{
return !a.Equals(b);
}
public override bool Equals(object obj)
{
if (obj is null) return false;
if (obj is not Vector2I vectorB) return false;
return vectorB.X == this.X && vectorB.Y == this.Y;
}
} }

View File

@ -1,4 +1,5 @@
using SharpDX; using CodeWalker.GameFiles;
using SharpDX;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -6,11 +7,27 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Linq;
namespace CodeWalker namespace CodeWalker
{ {
public static class Xml public static class Xml
{ {
public static void ValidateReaderState(XmlReader reader, string element)
{
if (reader == null)
{
throw new ArgumentNullException(nameof(reader));
}
if (!reader.IsStartElement())
{
throw new InvalidOperationException($"Expected reader to be at a start element but was at \"{reader.NodeType}\" with name \"{reader.Name}\"");
}
if (reader.Name != element)
{
throw new InvalidOperationException($"Expected reader to be at start element of \"{element}\" but was at \"{reader.NodeType}\" with name \"{reader.Name}\"");
}
}
public static string GetStringAttribute(XmlNode node, string attribute) public static string GetStringAttribute(XmlNode node, string attribute)
{ {
@ -63,6 +80,55 @@ namespace CodeWalker
if (node == null) return null; if (node == null) return null;
return node.SelectSingleNode(name)?.InnerText; return node.SelectSingleNode(name)?.InnerText;
} }
public static string GetChildInnerText(XElement node, string name) {
if (node == null) return null;
return node.Element(name).Value;
}
public static string GetChildInnerText(XmlReader reader, string name)
{
ValidateReaderState(reader, name);
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
return "";
}
return reader.ReadElementContentAsString();
}
else
{
return reader.ReadContentAsString();
}
}
public static bool GetChildBoolInnerText(XElement node, string name)
{
if (node == null) return false;
string val = node.Element(name).Value;
bool b;
bool.TryParse(val, out b);
return b;
}
public static bool GetChildBoolInnerText(XmlReader reader, string name)
{
ValidateReaderState(reader, name);
if (reader.NodeType == XmlNodeType.Element)
{
return reader.ReadElementContentAsBoolean();
}
else
{
return reader.ReadContentAsBoolean();
}
}
public static bool GetChildBoolInnerText(XmlNode node, string name) public static bool GetChildBoolInnerText(XmlNode node, string name)
{ {
if (node == null) return false; if (node == null) return false;
@ -87,6 +153,14 @@ namespace CodeWalker
FloatUtil.TryParse(val, out f); FloatUtil.TryParse(val, out f);
return f; return f;
} }
public static float GetChildFloatInnerText(XmlReader reader, string name)
{
ValidateReaderState(reader, name);
return reader.ReadElementContentAsFloat();
}
public static T GetChildEnumInnerText<T>(XmlNode node, string name) where T : struct public static T GetChildEnumInnerText<T>(XmlNode node, string name) where T : struct
{ {
if (node == null) return new T(); if (node == null) return new T();
@ -99,7 +173,7 @@ namespace CodeWalker
{ {
return default(T); return default(T);
} }
if (val.StartsWith("hash_")) if (val.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{ {
//convert hash_12ABC to Unk_12345 //convert hash_12ABC to Unk_12345
var substr = val.Substring(5); var substr = val.Substring(5);
@ -111,7 +185,18 @@ namespace CodeWalker
return enumval; return enumval;
} }
public static bool GetChildBoolAttribute(XmlReader reader, string name, string attribute = "value")
{
ValidateReaderState(reader, name);
string val = reader.GetAttribute(attribute);
bool.TryParse(val, out bool boolval);
reader.ReadStartElement();
return boolval;
}
public static bool GetChildBoolAttribute(XmlNode node, string name, string attribute = "value") public static bool GetChildBoolAttribute(XmlNode node, string name, string attribute = "value")
{ {
if (node == null) return false; if (node == null) return false;
@ -120,6 +205,20 @@ namespace CodeWalker
bool.TryParse(val, out b); bool.TryParse(val, out b);
return b; return b;
} }
public static int GetChildIntAttribute(XmlReader reader, string name, string attribute = "value")
{
ValidateReaderState(reader, name);
string val = reader.GetAttribute(attribute);
int.TryParse(val, out var i);
reader.ReadStartElement();
return i;
}
public static int GetChildIntAttribute(XmlNode node, string name, string attribute = "value") public static int GetChildIntAttribute(XmlNode node, string name, string attribute = "value")
{ {
if (node == null) return 0; if (node == null) return 0;
@ -128,12 +227,31 @@ namespace CodeWalker
int.TryParse(val, out i); int.TryParse(val, out i);
return i; return i;
} }
public static uint GetChildUIntAttribute(XmlReader reader, string name, string attribute = "value")
{
if (reader == null) return 0;
uint i;
string val = reader.GetAttribute(attribute);
if (val?.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ?? false)
{
var subs = val.Substring(2);
i = Convert.ToUInt32(subs, 16);
}
else
{
uint.TryParse(val, out i);
}
return i;
}
public static uint GetChildUIntAttribute(XmlNode node, string name, string attribute = "value") public static uint GetChildUIntAttribute(XmlNode node, string name, string attribute = "value")
{ {
if (node == null) return 0; if (node == null) return 0;
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText; string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
uint i; uint i;
if (val?.StartsWith("0x") ?? false) if (val?.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ?? false)
{ {
var subs = val.Substring(2); var subs = val.Substring(2);
i = Convert.ToUInt32(subs, 16); i = Convert.ToUInt32(subs, 16);
@ -149,7 +267,7 @@ namespace CodeWalker
if (node == null) return 0; if (node == null) return 0;
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText; string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
ulong i; ulong i;
if (val?.StartsWith("0x") ?? false) if (val?.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ?? false)
{ {
var subs = val.Substring(2); var subs = val.Substring(2);
i = Convert.ToUInt64(subs, 16); i = Convert.ToUInt64(subs, 16);
@ -160,6 +278,26 @@ namespace CodeWalker
} }
return i; return i;
} }
public static float GetChildFloatAttribute(XmlReader reader, string name, string attribute = "value")
{
if (!string.IsNullOrEmpty(name))
{
ValidateReaderState(reader, name);
}
string val = reader.GetAttribute(attribute);
FloatUtil.TryParse(val, out float f);
if (!string.IsNullOrEmpty(name))
{
reader.ReadStartElement();
}
return f;
}
public static float GetChildFloatAttribute(XmlNode node, string name, string attribute = "value") public static float GetChildFloatAttribute(XmlNode node, string name, string attribute = "value")
{ {
if (node == null) return 0; if (node == null) return 0;
@ -174,12 +312,37 @@ namespace CodeWalker
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText; string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
return val; return val;
} }
public static string GetChildStringAttribute(XmlReader reader, string name, string attribute = "value")
{
ValidateReaderState(reader, name);
var val = reader.GetAttribute(attribute);
reader.ReadStartElement();
return val;
}
public static Vector2 GetChildVector2Attributes(XmlNode node, string name, string x = "x", string y = "y") public static Vector2 GetChildVector2Attributes(XmlNode node, string name, string x = "x", string y = "y")
{ {
float fx = GetChildFloatAttribute(node, name, x); float fx = GetChildFloatAttribute(node, name, x);
float fy = GetChildFloatAttribute(node, name, y); float fy = GetChildFloatAttribute(node, name, y);
return new Vector2(fx, fy); return new Vector2(fx, fy);
} }
public static Vector3 GetChildVector3Attributes(XmlReader reader, string name, string x = "x", string y = "y", string z = "z")
{
ValidateReaderState(reader, name);
float fx = GetChildFloatAttribute(reader, null, x);
float fy = GetChildFloatAttribute(reader, null, y);
float fz = GetChildFloatAttribute(reader, null, z);
reader.ReadStartElement();
return new Vector3(fx, fy, fz);
}
public static Vector3 GetChildVector3Attributes(XmlNode node, string name, string x = "x", string y = "y", string z = "z") public static Vector3 GetChildVector3Attributes(XmlNode node, string name, string x = "x", string y = "y", string z = "z")
{ {
float fx = GetChildFloatAttribute(node, name, x); float fx = GetChildFloatAttribute(node, name, x);
@ -207,6 +370,37 @@ namespace CodeWalker
node.AppendChild(child); node.AppendChild(child);
return child; return child;
} }
public static bool IsItemElement(this XmlReader reader)
{
if (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "Item")
{
return true;
}
return false;
}
public static IEnumerable<XElement> IterateItems(XmlReader reader, string parentElementName)
{
ValidateReaderState(reader, parentElementName);
reader.MoveToContent();
if (reader.IsEmptyElement)
{
// Move past empty element
reader.ReadStartElement(parentElementName);
yield break;
}
reader.ReadStartElement(parentElementName);
while(reader.IsItemElement())
{
if (XNode.ReadFrom(reader) is XElement el)
{
yield return el;
}
}
reader.ReadEndElement();
}
public static XmlElement AddChildWithInnerText(XmlDocument doc, XmlNode node, string name, string innerText) public static XmlElement AddChildWithInnerText(XmlDocument doc, XmlNode node, string name, string innerText)
{ {
XmlElement child = AddChild(doc, node, name); XmlElement child = AddChild(doc, node, name);

View File

@ -33,6 +33,9 @@ namespace CodeWalker.World
List<AudioPlacement> placements = new List<AudioPlacement>(); List<AudioPlacement> placements = new List<AudioPlacement>();
GameFileCache.AudioDatRelFilesLock.EnterReadLock();
try
{
foreach (var relfile in GameFileCache.AudioDatRelFiles) foreach (var relfile in GameFileCache.AudioDatRelFiles)
{ {
if (relfile == null) continue; if (relfile == null) continue;
@ -43,6 +46,11 @@ namespace CodeWalker.World
PlacementsDict[relfile] = placements.ToArray(); PlacementsDict[relfile] = placements.ToArray();
} }
}
finally
{
GameFileCache.AudioDatRelFilesLock.ExitReadLock();
}
AllItems.AddRange(Zones); AllItems.AddRange(Zones);
AllItems.AddRange(Emitters); AllItems.AddRange(Emitters);

View File

@ -1799,7 +1799,6 @@ namespace CodeWalker.World
var noneset = new AmbientModelSet(); var noneset = new AmbientModelSet();
noneset.Name = "NONE"; noneset.Name = "NONE";
noneset.NameLower = "none";
noneset.NameHash = JenkHash.GenHash("none"); noneset.NameHash = JenkHash.GenHash("none");
sets[noneset.NameHash] = noneset; sets[noneset.NameHash] = noneset;
@ -1808,10 +1807,10 @@ namespace CodeWalker.World
{ {
AmbientModelSet set = new AmbientModelSet(); AmbientModelSet set = new AmbientModelSet();
set.Load(item); set.Load(item);
if (!string.IsNullOrEmpty(set.NameLower)) if (!string.IsNullOrEmpty(set.Name))
{ {
JenkIndex.Ensure(set.NameLower); uint hash = JenkHash.GenHashLower(set.Name);
uint hash = JenkHash.GenHash(set.NameLower); JenkIndex.Ensure(set.Name, hash);
sets[hash] = set; sets[hash] = set;
} }
} }
@ -1837,11 +1836,12 @@ namespace CodeWalker.World
{ {
ConditionalAnimsGroup group = new ConditionalAnimsGroup(); ConditionalAnimsGroup group = new ConditionalAnimsGroup();
group.Load(item); group.Load(item);
if (!string.IsNullOrEmpty(group.NameLower)) if (!string.IsNullOrEmpty(group.Name))
{ {
uint hash = JenkHash.GenHashLower(group.Name);
JenkIndex.Ensure(group.Name); JenkIndex.Ensure(group.Name);
JenkIndex.Ensure(group.NameLower); JenkIndex.Ensure(group.Name, hash);
uint hash = JenkHash.GenHash(group.NameLower);
groups[hash] = group; groups[hash] = group;
} }
} }
@ -1903,7 +1903,6 @@ namespace CodeWalker.World
string s_hash = hash.ToString("X"); string s_hash = hash.ToString("X");
ms = new AmbientModelSet(); ms = new AmbientModelSet();
ms.Name = $"UNKNOWN PED MODELSET ({s_hash})"; ms.Name = $"UNKNOWN PED MODELSET ({s_hash})";
ms.NameLower = ms.Name.ToLowerInvariant();
ms.NameHash = new MetaHash(hash); ms.NameHash = new MetaHash(hash);
ms.Models = new AmbientModel[] { }; ms.Models = new AmbientModel[] { };
PedModelSets.Add(hash, ms); PedModelSets.Add(hash, ms);
@ -1922,7 +1921,6 @@ namespace CodeWalker.World
string s_hash = hash.ToString("X"); string s_hash = hash.ToString("X");
ms = new AmbientModelSet(); ms = new AmbientModelSet();
ms.Name = $"UNKNOWN VEHICLE MODELSET ({s_hash})"; ms.Name = $"UNKNOWN VEHICLE MODELSET ({s_hash})";
ms.NameLower = ms.Name.ToLowerInvariant();
ms.NameHash = new MetaHash(hash); ms.NameHash = new MetaHash(hash);
ms.Models = new AmbientModel[] {}; ms.Models = new AmbientModel[] {};
VehicleModelSets.Add(hash, ms); VehicleModelSets.Add(hash, ms);
@ -2110,7 +2108,6 @@ namespace CodeWalker.World
[TypeConverter(typeof(ExpandableObjectConverter))] public class AmbientModelSet [TypeConverter(typeof(ExpandableObjectConverter))] public class AmbientModelSet
{ {
public string Name { get; set; } public string Name { get; set; }
public string NameLower { get; set; }
public MetaHash NameHash { get; set; } public MetaHash NameHash { get; set; }
public AmbientModel[] Models { get; set; } public AmbientModel[] Models { get; set; }
@ -2118,8 +2115,7 @@ namespace CodeWalker.World
public void Load(XmlNode node) public void Load(XmlNode node)
{ {
Name = Xml.GetChildInnerText(node, "Name"); Name = Xml.GetChildInnerText(node, "Name");
NameLower = Name.ToLowerInvariant(); NameHash = JenkHash.GenHashLower(Name);
NameHash = JenkHash.GenHash(NameLower);
var models = node.SelectNodes("Models/Item"); var models = node.SelectNodes("Models/Item");
var modellist = new List<AmbientModel>(); var modellist = new List<AmbientModel>();
@ -2190,14 +2186,12 @@ namespace CodeWalker.World
{ {
public string OuterXml { get; set; } public string OuterXml { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string NameLower { get; set; }
public void Load(XmlNode node) public void Load(XmlNode node)
{ {
OuterXml = node.OuterXml; OuterXml = node.OuterXml;
Name = Xml.GetChildInnerText(node, "Name"); Name = Xml.GetChildInnerText(node, "Name");
NameLower = Name.ToLowerInvariant();
} }
public override string ToString() public override string ToString()

View File

@ -277,7 +277,7 @@ namespace CodeWalker.World
{ {
foreach (var entry in maprpf.AllEntries) foreach (var entry in maprpf.AllEntries)
{ {
if (entry.NameLower.EndsWith(".ymap")) if (entry.IsExtension(".ymap"))
{ {
if (!nodedict.ContainsKey(new MetaHash(entry.ShortNameHash))) if (!nodedict.ContainsKey(new MetaHash(entry.ShortNameHash)))
{ {
@ -297,7 +297,7 @@ namespace CodeWalker.World
{ } { }
} }
} }
if (entry.NameLower.EndsWith(".ybn")) if (entry.IsExtension(".ybn"))
{ {
MetaHash ehash = new MetaHash(entry.ShortNameHash); MetaHash ehash = new MetaHash(entry.ShortNameHash);
if (!usedboundsdict.ContainsKey(ehash)) if (!usedboundsdict.ContainsKey(ehash))
@ -367,7 +367,7 @@ namespace CodeWalker.World
} }
foreach (var dlcrpf in GameFileCache.DlcActiveRpfs) //load nodes from current dlc rpfs foreach (var dlcrpf in GameFileCache.DlcActiveRpfs) //load nodes from current dlc rpfs
{ {
if (dlcrpf.Path.StartsWith("x64")) continue; //don't override update.rpf YNDs with x64 ones! *hack if (dlcrpf.Path.StartsWith("x64", StringComparison.OrdinalIgnoreCase)) continue; //don't override update.rpf YNDs with x64 ones! *hack
foreach (var rpffile in dlcrpf.Children) foreach (var rpffile in dlcrpf.Children)
{ {
AddRpfYnds(rpffile, yndentries); AddRpfYnds(rpffile, yndentries);
@ -509,7 +509,7 @@ namespace CodeWalker.World
if (entry is RpfFileEntry) if (entry is RpfFileEntry)
{ {
RpfFileEntry fentry = entry as RpfFileEntry; RpfFileEntry fentry = entry as RpfFileEntry;
if (entry.NameLower.EndsWith(".ynd")) if (entry.IsExtension(".ynd"))
{ {
if (yndentries.ContainsKey(entry.NameHash)) if (yndentries.ContainsKey(entry.NameHash))
{ } { }
@ -809,7 +809,7 @@ namespace CodeWalker.World
if (entry is RpfFileEntry) if (entry is RpfFileEntry)
{ {
RpfFileEntry fentry = entry as RpfFileEntry; RpfFileEntry fentry = entry as RpfFileEntry;
if (entry.NameLower.EndsWith(".ynv")) if (entry.IsExtension(".ynv"))
{ {
if (ynventries.ContainsKey(entry.NameHash)) if (ynventries.ContainsKey(entry.NameHash))
{ } { }

View File

@ -38,7 +38,7 @@ namespace CodeWalker.World
{ {
foreach (var file in dlcrpf.AllEntries) foreach (var file in dlcrpf.AllEntries)
{ {
if (file.NameLower.EndsWith(".xml") && file.NameLower.StartsWith("timecycle_mods_")) if (file.IsExtension(".xml") && file.Name.StartsWith("timecycle_mods_", StringComparison.OrdinalIgnoreCase))
{ {
LoadXml(rpfman.GetFileXml(file.Path)); LoadXml(rpfman.GetFileXml(file.Path));
} }

View File

@ -16,6 +16,9 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using CodeWalker.Utils; using CodeWalker.Utils;
using CodeWalker.Core.Utils;
namespace CodeWalker.RPFExplorer namespace CodeWalker.RPFExplorer
{ {
@ -18,13 +19,31 @@ namespace CodeWalker.RPFExplorer
[STAThread] [STAThread]
static void Main() static void Main()
{ {
//Process.Start("CodeWalker.exe", "explorer"); try
{
if (!NamedPipe.TrySendMessageToOtherProcess("explorer"))
{
ConsoleWindow.Hide(); ConsoleWindow.Hide();
//Process.Start("CodeWalker.exe", "explorer");
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ExploreForm());
var form = new ExploreForm();
var namedPipe = new NamedPipe(form);
namedPipe.Init();
Application.Run(form);
GTAFolder.UpdateSettings(); GTAFolder.UpdateSettings();
} }
} }
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
}
}
} }

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Test
{
internal class BinaryPrimitesFloats
{
}
}

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,96 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace CodeWalker.Test
{
public class JenkGenTests
{
[Fact]
public void GenHashMustReturnHash()
{
Assert.Equal((uint)1044535224, JenkHash.GenHash("ASDF"));
Assert.Equal((uint)1485398060, JenkHash.GenHashLower("asdf"));
}
[Fact]
public void EnsureMustAddStringToDictionary()
{
JenkIndex.Index.Clear();
Assert.Empty(JenkIndex.Index);
JenkIndex.Ensure("asdf");
Assert.Single(JenkIndex.Index);
}
[Fact]
public void TryGetStringWithMissingHashMustReturnEmptyString()
{
JenkIndex.Index.Clear();
Assert.Empty(JenkIndex.Index);
var str = JenkIndex.TryGetString((uint)1485398060);
Assert.Equal(string.Empty, str);
}
[Fact]
public void GetStringWithMissingHashMustReturnHashAsString()
{
JenkIndex.Index.Clear();
Assert.Empty(JenkIndex.Index);
var str = JenkIndex.GetString((uint)1485398060);
Assert.Equal(1485398060.ToString(), str);
}
[Fact]
public void TryGetStringMustReturnAddedString()
{
JenkIndex.Ensure("asdf");
var str = JenkIndex.TryGetString((uint)1485398060);
Assert.Equal("asdf", str);
}
[Fact]
public void EnsureLowerMustAddLoweredStringToDictionary()
{
JenkIndex.Index.Clear();
Assert.Empty(JenkIndex.Index);
JenkIndex.EnsureLower("ASDF");
Assert.Single(JenkIndex.Index);
var str = JenkIndex.TryGetString((uint)1485398060);
Assert.Equal("ASDF", str);
var missingStr = JenkIndex.TryGetString((uint)1044535224);
Assert.Equal(string.Empty, missingStr);
}
[Fact]
public void GetStringMustReturnAddedString()
{
JenkIndex.Index.Clear();
Assert.Empty(JenkIndex.Index);
JenkIndex.EnsureLower("ASDF");
Assert.Single(JenkIndex.Index);
var str = JenkIndex.TryGetString((uint)1485398060);
Assert.Equal("ASDF", str);
var missingStr = JenkIndex.TryGetString((uint)1044535224);
Assert.Equal(string.Empty, missingStr);
}
}
}

File diff suppressed because one or more lines are too long

1081
CodeWalker.Test/XmlTests.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,16 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net48</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" /> <PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.29806.167 VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeWalker.Shaders", "CodeWalker.Shaders\CodeWalker.Shaders.vcxproj", "{0D14B076-0ABF-434E-AB9F-36E7800D8887}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeWalker.Shaders", "CodeWalker.Shaders\CodeWalker.Shaders.vcxproj", "{0D14B076-0ABF-434E-AB9F-36E7800D8887}"
EndProject EndProject
@ -23,6 +23,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.RPFExplorer", "C
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Vehicles", "CodeWalker.Vehicles\CodeWalker.Vehicles.csproj", "{F5A776B0-2F1A-4C36-87B3-86206AC4B439}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Vehicles", "CodeWalker.Vehicles\CodeWalker.Vehicles.csproj", "{F5A776B0-2F1A-4C36-87B3-86206AC4B439}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Test", "CodeWalker.Test\CodeWalker.Test.csproj", "{067FF87E-CA79-4B29-BCF6-11622999EDC6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeWalker.Benchmarks", "CodeWalker.Benchmarks\CodeWalker.Benchmarks.csproj", "{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -128,6 +132,30 @@ Global
{F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x64.Build.0 = Release|Any CPU {F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x64.Build.0 = Release|Any CPU
{F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x86.ActiveCfg = Release|Any CPU {F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x86.ActiveCfg = Release|Any CPU
{F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x86.Build.0 = Release|Any CPU {F5A776B0-2F1A-4C36-87B3-86206AC4B439}.Release|x86.Build.0 = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|x64.ActiveCfg = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|x64.Build.0 = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|x86.ActiveCfg = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Debug|x86.Build.0 = Debug|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|Any CPU.Build.0 = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|x64.ActiveCfg = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|x64.Build.0 = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|x86.ActiveCfg = Release|Any CPU
{067FF87E-CA79-4B29-BCF6-11622999EDC6}.Release|x86.Build.0 = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|x64.ActiveCfg = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|x64.Build.0 = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|x86.ActiveCfg = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Debug|x86.Build.0 = Debug|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|Any CPU.Build.0 = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|x64.ActiveCfg = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|x64.Build.0 = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|x86.ActiveCfg = Release|Any CPU
{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -15,11 +15,17 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" /> <PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" />
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.1.0" />
<PackageReference Include="FCTB" Version="2.16.24" /> <PackageReference Include="FCTB" Version="2.16.24" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" /> <PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" /> <PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />

View File

@ -1,4 +1,7 @@
using CodeWalker.Core.Utils; using CodeWalker.Core.Utils;
using System;
using CodeWalker.WinForms;
namespace CodeWalker namespace CodeWalker
{ {
@ -15,11 +18,13 @@ namespace CodeWalker
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
using var _ = new DisposableTimer("ExploreForm Dipose");
base.Dispose(disposing);
MainListView.Dispose();
if (disposing && (components != null)) if (disposing && (components != null))
{ {
components.Dispose(); components.Dispose();
} }
base.Dispose(disposing);
} }
#region Windows Form Designer generated code #region Windows Form Designer generated code
@ -121,6 +126,7 @@ namespace CodeWalker
this.ListContextViewHexMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ListContextViewHexMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.ListContextExportXmlMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ListContextExportXmlMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractShadersMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractRawMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ListContextExtractRawMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractUncompressedMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ListContextExtractUncompressedMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractAllMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ListContextExtractAllMenu = new System.Windows.Forms.ToolStripMenuItem();
@ -990,6 +996,7 @@ namespace CodeWalker
this.ListContextViewMenu, this.ListContextViewMenu,
this.ListContextViewHexMenu, this.ListContextViewHexMenu,
this.toolStripSeparator2, this.toolStripSeparator2,
this.ListContextExtractShadersMenu,
this.ListContextExportXmlMenu, this.ListContextExportXmlMenu,
this.ListContextExtractRawMenu, this.ListContextExtractRawMenu,
this.ListContextExtractUncompressedMenu, this.ListContextExtractUncompressedMenu,
@ -1047,6 +1054,12 @@ namespace CodeWalker
this.ListContextExportXmlMenu.Size = new System.Drawing.Size(208, 22); this.ListContextExportXmlMenu.Size = new System.Drawing.Size(208, 22);
this.ListContextExportXmlMenu.Text = "Export XML..."; this.ListContextExportXmlMenu.Text = "Export XML...";
this.ListContextExportXmlMenu.Click += new System.EventHandler(this.ListContextExportXmlMenu_Click); this.ListContextExportXmlMenu.Click += new System.EventHandler(this.ListContextExportXmlMenu_Click);
this.ListContextExtractShadersMenu.Image = ((System.Drawing.Image)(resources.GetObject("ListContextExportXmlMenu.Image")));
this.ListContextExtractShadersMenu.Name = "ListContextExportShadersMenu";
this.ListContextExtractShadersMenu.ShortcutKeyDisplayString = "Ctrl+S";
this.ListContextExtractShadersMenu.Size = new System.Drawing.Size(208, 22);
this.ListContextExtractShadersMenu.Text = "Export Shaders...";
this.ListContextExtractShadersMenu.Click += new System.EventHandler(this.ListContextExportShaders_Click);
// //
// ListContextExtractRawMenu // ListContextExtractRawMenu
// //
@ -1394,6 +1407,7 @@ namespace CodeWalker
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem ListContextExportXmlMenu; private System.Windows.Forms.ToolStripMenuItem ListContextExportXmlMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextExtractAllMenu; private System.Windows.Forms.ToolStripMenuItem ListContextExtractAllMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextExtractShadersMenu;
private System.Windows.Forms.ToolStripSeparator ListContextImportSeparator; private System.Windows.Forms.ToolStripSeparator ListContextImportSeparator;
private System.Windows.Forms.ToolStripMenuItem ListContextCopyPathMenu; private System.Windows.Forms.ToolStripMenuItem ListContextCopyPathMenu;
private System.Windows.Forms.ToolStripSeparator ListContextEditSeparator; private System.Windows.Forms.ToolStripSeparator ListContextEditSeparator;

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ using System.Windows.Forms;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using Range = FastColoredTextBoxNS.Range; using Range = FastColoredTextBoxNS.Range;
using CodeWalker.Core.Utils;
namespace CodeWalker.Forms namespace CodeWalker.Forms
{ {
@ -400,7 +401,7 @@ namespace CodeWalker.Forms
{ {
Stream wavStream = audio.GetWavStream(); Stream wavStream = audio.GetWavStream();
FileStream stream = File.Create(saveFileDialog.FileName); FileStream stream = File.Create(saveFileDialog.FileName);
wavStream.CopyTo(stream); wavStream.CopyToFast(stream);
stream.Close(); stream.Close();
wavStream.Close(); wavStream.Close();
} }

View File

@ -25,14 +25,11 @@ namespace CodeWalker.Forms
} }
} }
public string FilePath { get; set; } public string FilePath { get; set; }
ExploreForm ExploreForm;
object CurrentFile; object CurrentFile;
public GenericForm(ExploreForm exploreForm) public GenericForm()
{ {
ExploreForm = exploreForm;
InitializeComponent(); InitializeComponent();
} }

View File

@ -45,16 +45,12 @@ namespace CodeWalker.Forms
private bool LoadingXml = false; private bool LoadingXml = false;
private bool DelayHighlight = false; private bool DelayHighlight = false;
private ExploreForm exploreForm = null;
public RpfFileEntry rpfFileEntry { get; private set; } = null; public RpfFileEntry rpfFileEntry { get; private set; } = null;
private MetaFormat metaFormat = MetaFormat.XML; private MetaFormat metaFormat = MetaFormat.XML;
public MetaForm(ExploreForm owner) public MetaForm()
{ {
exploreForm = owner;
InitializeComponent(); InitializeComponent();
} }
@ -417,6 +413,68 @@ namespace CodeWalker.Forms
} }
} }
public void LoadMeta(PackedFile gameFile)
{
gameFile = gameFile ?? throw new ArgumentNullException(nameof(gameFile));
if (gameFile is YmfFile ymfFile)
{
LoadMeta(ymfFile);
}
else if (gameFile is MrfFile mrfFile)
{
LoadMeta(mrfFile);
}
else if (gameFile is YfdFile yfdFile)
{
LoadMeta(yfdFile);
}
else if (gameFile is YpdbFile ypdbFile)
{
LoadMeta(ypdbFile);
}
else if (gameFile is HeightmapFile heightmap)
{
LoadMeta(heightmap);
}
else if (gameFile is CacheDatFile cacheDatFile)
{
LoadMeta(cacheDatFile);
}
else if (gameFile is YedFile yedFile)
{
LoadMeta(yedFile);
}
else if (gameFile is YldFile yldFile)
{
LoadMeta(yldFile);
}
else if (gameFile is YndFile yndFile)
{
LoadMeta(yndFile);
}
else if (gameFile is CutFile cutFile)
{
LoadMeta(cutFile);
}
else if (gameFile is JPsoFile jpsoFile)
{
LoadMeta(jpsoFile);
}
else if (gameFile is YtypFile ytypFile)
{
LoadMeta(ytypFile);
}
else if (gameFile is YmapFile ymapFile)
{
LoadMeta(ymapFile);
}
else if (gameFile is YmtFile ymtFile)
{
LoadMeta(ymtFile);
}
}
public bool SaveMeta(XmlDocument doc) public bool SaveMeta(XmlDocument doc)
@ -426,7 +484,7 @@ namespace CodeWalker.Forms
//otherwise, save the generated file to disk? //otherwise, save the generated file to disk?
//(currently just return false and revert to XML file save) //(currently just return false and revert to XML file save)
if (!(exploreForm?.EditMode ?? false)) return false; if (!(ExploreForm.Instance?.EditMode ?? false)) return false;
if(metaFormat == MetaFormat.XML) return false;//what are we even doing here? if(metaFormat == MetaFormat.XML) return false;//what are we even doing here?
@ -466,14 +524,14 @@ namespace CodeWalker.Forms
try try
{ {
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return false; if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return false;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data); var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
if (newentry != rpfFileEntry) if (newentry != rpfFileEntry)
{ } { }
rpfFileEntry = newentry; rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;
@ -492,7 +550,7 @@ namespace CodeWalker.Forms
{ {
File.WriteAllBytes(rpfFileEntry.Path, data); File.WriteAllBytes(rpfFileEntry.Path, data);
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;

View File

@ -24,7 +24,7 @@ namespace CodeWalker.Forms
{ {
public Form Form { get { return this; } } //for DXForm/DXManager use public Form Form { get { return this; } } //for DXForm/DXManager use
private Renderer Renderer = null; public Renderer Renderer { get; set; }
volatile bool formopen = false; volatile bool formopen = false;
@ -37,6 +37,8 @@ namespace CodeWalker.Forms
Weather weather; Weather weather;
Clouds clouds; Clouds clouds;
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
bool MouseLButtonDown = false; bool MouseLButtonDown = false;
bool MouseRButtonDown = false; bool MouseRButtonDown = false;
int MouseX; int MouseX;
@ -114,7 +116,6 @@ namespace CodeWalker.Forms
public bool showLightGizmos = true; public bool showLightGizmos = true;
public Skeleton Skeleton = null; public Skeleton Skeleton = null;
ExploreForm exploreForm = null;
RpfFileEntry rpfFileEntry = null; RpfFileEntry rpfFileEntry = null;
@ -128,16 +129,14 @@ namespace CodeWalker.Forms
public ModelForm(ExploreForm ExpForm = null) public ModelForm()
{ {
if (this.DesignMode) return; if (this.DesignMode) return;
InitializeComponent(); InitializeComponent();
exploreForm = ExpForm; gameFileCache = GameFileCacheFactory.GetInstance();
gameFileCache = ExpForm?.GetFileCache() ?? GameFileCacheFactory.Create(); if (ExploreForm.Instance == null)
if (ExpForm == null)
{ {
gameFileCache.EnableDlc = false; gameFileCache.EnableDlc = false;
gameFileCache.EnableMods = false; gameFileCache.EnableMods = false;
@ -163,12 +162,14 @@ namespace CodeWalker.Forms
catch(Exception ex) catch(Exception ex)
{ {
Console.WriteLine(ex); Console.WriteLine(ex);
throw ex; throw;
} }
}); });
Task.Run(() => Task.Run(async () =>
{
try
{ {
while (!IsDisposed) //run the file cache content thread until the form exits. while (!IsDisposed) //run the file cache content thread until the form exits.
{ {
@ -180,14 +181,21 @@ namespace CodeWalker.Forms
if (!fcItemsPending) if (!fcItemsPending)
{ {
Thread.Sleep(10); await Task.Delay(10);
} }
} }
else else
{ {
Thread.Sleep(20); await Task.Delay(20);
} }
} }
}
catch(Exception ex)
{
Console.WriteLine($"Exception occurred in gameFileCache ContentThread.\n{ex}");
throw;
}
}); });
} }
@ -299,7 +307,8 @@ namespace CodeWalker.Forms
formopen = true; formopen = true;
new Thread(new ThreadStart(ContentThread)).Start();
Task.Run(ContentThread);
frametimer.Start(); frametimer.Start();
} }
@ -324,19 +333,26 @@ namespace CodeWalker.Forms
Console.WriteLine("Clearing cache"); Console.WriteLine("Clearing cache");
gameFileCache.Clear(); gameFileCache.Clear();
gameFileCache.IsInited = true; gameFileCache.IsInited = true;
GC.Collect(); //GC.Collect();
} }
} }
public void RenderScene(DeviceContext context) public async ValueTask RenderScene(DeviceContext context)
{ {
float elapsed = (float)frametimer.Elapsed.TotalSeconds; float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart(); frametimer.Restart();
if (elapsed < 0.016666)
{
await Task.Delay((int)(0.016666 * elapsed) * 1000);
}
if (Pauserendering) return; if (Pauserendering) return;
if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50)) if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50))
{ return; } //couldn't get a lock, try again next time {
return;
} //couldn't get a lock, try again next time
UpdateControlInputs(elapsed); UpdateControlInputs(elapsed);
@ -376,7 +392,7 @@ namespace CodeWalker.Forms
} }
private void ContentThread() private async void ContentThread()
{ {
//main content loading thread. //main content loading thread.
//running = true; //running = true;
@ -453,7 +469,7 @@ namespace CodeWalker.Forms
if (!(rcItemsPending)) //gameFileCache.ItemsStillPending || if (!(rcItemsPending)) //gameFileCache.ItemsStillPending ||
{ {
Thread.Sleep(1); //sleep if there's nothing to do await Task.Delay(ActiveForm == null ? 50 : 1).ConfigureAwait(false);
} }
} }
@ -482,7 +498,7 @@ namespace CodeWalker.Forms
List<string> ycdlist = new List<string>(); List<string> ycdlist = new List<string>();
foreach (var ycde in ycds) foreach (var ycde in ycds)
{ {
ycdlist.Add(ycde.GetShortName()); ycdlist.Add(ycde.ShortName);
} }
ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray()); ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray());
ClipDictComboBox.Text = ""; ClipDictComboBox.Text = "";
@ -703,10 +719,10 @@ namespace CodeWalker.Forms
public void ViewModel(byte[] data, RpfFileEntry e) public void ViewModel(byte[] data, RpfFileEntry e)
{ {
var nl = e?.NameLower ?? ""; var nl = e?.Name ?? "";
var fe = Path.GetExtension(nl); var fe = Path.GetExtension(nl);
Show(); Show();
switch (fe) switch (fe.ToLowerInvariant())
{ {
case ".ydr": case ".ydr":
var ydr = RpfFile.GetFile<YdrFile>(e, data); var ydr = RpfFile.GetFile<YdrFile>(e, data);
@ -902,10 +918,10 @@ namespace CodeWalker.Forms
Yft = yft; Yft = yft;
rpfFileEntry = Yft.RpfFileEntry; rpfFileEntry = Yft.RpfFileEntry;
ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0; ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0;
var namelower = Yft.RpfFileEntry?.ShortName; var name = Yft.RpfFileEntry?.ShortName;
if (namelower?.EndsWith("_hi", StringComparison.OrdinalIgnoreCase) ?? false) if (name?.EndsWith("_hi", StringComparison.OrdinalIgnoreCase) ?? false)
{ {
ModelHash = JenkHash.GenHashLower(namelower.Substring(0, namelower.Length - 3)); ModelHash = JenkHash.GenHashLower(name.AsSpan(0, name.Length - 3));
} }
if (ModelHash != 0) if (ModelHash != 0)
{ {
@ -1295,7 +1311,7 @@ namespace CodeWalker.Forms
{ {
items.Add(kvp); items.Add(kvp);
} }
items.Sort((a, b) => { return a.Value?.Name?.CompareTo(b.Value?.Name ?? "") ?? 0; }); items.Sort((a, b) => StringComparer.OrdinalIgnoreCase.Compare(a.Value?.Name, b.Value?.Name));
foreach (var kvp in items) foreach (var kvp in items)
{ {
AddDrawableTreeNode(kvp.Value, kvp.Key, check); AddDrawableTreeNode(kvp.Value, kvp.Key, check);
@ -1668,7 +1684,7 @@ namespace CodeWalker.Forms
if (td != null) if (td != null)
{ {
YtdForm f = new YtdForm(null, this); YtdForm f = new YtdForm(this);
f.Show(this); f.Show(this);
f.LoadTexDict(td, fileName); f.LoadTexDict(td, fileName);
} }
@ -1760,7 +1776,7 @@ namespace CodeWalker.Forms
private void Save(bool saveAs = false) private void Save(bool saveAs = false)
{ {
var editMode = exploreForm?.EditMode ?? false; var editMode = ExploreForm.Instance?.EditMode ?? false;
if (string.IsNullOrEmpty(FilePath)) if (string.IsNullOrEmpty(FilePath))
{ {
@ -1857,14 +1873,14 @@ namespace CodeWalker.Forms
try try
{ {
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return; if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, fileBytes); var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, fileBytes);
if (newentry != rpfFileEntry) if (newentry != rpfFileEntry)
{ } { }
rpfFileEntry = newentry; rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
StatusLabel.Text = rpfFileEntry.Name + " saved successfully at " + DateTime.Now.ToString(); StatusLabel.Text = rpfFileEntry.Name + " saved successfully at " + DateTime.Now.ToString();
@ -1889,7 +1905,7 @@ namespace CodeWalker.Forms
fileName = Path.GetFileName(fn); fileName = Path.GetFileName(fn);
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
StatusLabel.Text = fileName + " saved successfully at " + DateTime.Now.ToString(); StatusLabel.Text = fileName + " saved successfully at " + DateTime.Now.ToString();
} }

View File

@ -51,8 +51,6 @@ namespace CodeWalker.Forms
private bool modified = false; private bool modified = false;
private bool LoadingXml = false; private bool LoadingXml = false;
private bool DelayHighlight = false; private bool DelayHighlight = false;
private ExploreForm exploreForm = null;
public RpfFileEntry rpfFileEntry { get; private set; } = null; public RpfFileEntry rpfFileEntry { get; private set; } = null;
private MetaFormat metaFormat = MetaFormat.XML; private MetaFormat metaFormat = MetaFormat.XML;
@ -60,10 +58,8 @@ namespace CodeWalker.Forms
private Dat10Synth currentSynth = null; private Dat10Synth currentSynth = null;
public RelForm(ExploreForm owner) public RelForm()
{ {
exploreForm = owner;
InitializeComponent(); InitializeComponent();
} }
@ -207,7 +203,7 @@ namespace CodeWalker.Forms
private bool SaveRel(XmlDocument doc) private bool SaveRel(XmlDocument doc)
{ {
if (!(exploreForm?.EditMode ?? false)) return false; if (!(ExploreForm.Instance?.EditMode ?? false)) return false;
byte[] data = null; byte[] data = null;
@ -256,14 +252,14 @@ namespace CodeWalker.Forms
try try
{ {
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return false; if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return false;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data); var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
if (newentry != rpfFileEntry) if (newentry != rpfFileEntry)
{ } { }
rpfFileEntry = newentry; rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;
@ -282,7 +278,7 @@ namespace CodeWalker.Forms
{ {
File.WriteAllBytes(rpfFileEntry.Path, data); File.WriteAllBytes(rpfFileEntry.Path, data);
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;
@ -513,7 +509,6 @@ namespace CodeWalker.Forms
bool textsearch = SearchTextRadio.Checked; bool textsearch = SearchTextRadio.Checked;
var text = SearchTextBox.Text; var text = SearchTextBox.Text;
var textl = text.ToLowerInvariant();
uint hash = 0; uint hash = 0;
uint hashl = 0; uint hashl = 0;
@ -521,8 +516,8 @@ namespace CodeWalker.Forms
{ {
hash = JenkHash.GenHash(text); hash = JenkHash.GenHash(text);
JenkIndex.Ensure(text); JenkIndex.Ensure(text);
hashl = JenkHash.GenHash(textl); hashl = JenkHash.GenHashLower(text);
JenkIndex.Ensure(textl); JenkIndex.EnsureLower(text);
} }
else else
{ {
@ -536,8 +531,8 @@ namespace CodeWalker.Forms
{ {
if (textsearch) if (textsearch)
{ {
if (((rd.Name?.Contains(textl, StringComparison.OrdinalIgnoreCase)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) || if (((rd.Name?.Contains(text, StringComparison.OrdinalIgnoreCase)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) ||
rd.NameHash.ToString().Contains(textl, StringComparison.OrdinalIgnoreCase)) rd.NameHash.ToString().Contains(text, StringComparison.OrdinalIgnoreCase))
{ {
results.Add(rd); results.Add(rd);
} }

View File

@ -41,7 +41,6 @@ namespace CodeWalker.Forms
private bool modified = false; private bool modified = false;
private ExploreForm exploreForm = null;
public RpfFileEntry rpfFileEntry { get; private set; } = null; public RpfFileEntry rpfFileEntry { get; private set; } = null;
@ -55,10 +54,8 @@ namespace CodeWalker.Forms
public TextForm(ExploreForm owner) public TextForm()
{ {
exploreForm = owner;
InitializeComponent(); InitializeComponent();
} }
@ -160,15 +157,14 @@ namespace CodeWalker.Forms
if (!File.Exists(fn)) return; //couldn't find file? if (!File.Exists(fn)) return; //couldn't find file?
var fnl = fn.ToLowerInvariant(); if (fn.EndsWith(".gxt2", StringComparison.OrdinalIgnoreCase))
if (fnl.EndsWith(".gxt2"))
{ {
var gxt = new Gxt2File(); var gxt = new Gxt2File();
gxt.Load(File.ReadAllBytes(fn), null); gxt.Load(File.ReadAllBytes(fn), null);
fileType = TextFileType.GXT2; fileType = TextFileType.GXT2;
TextValue = gxt.ToText(); TextValue = gxt.ToText();
} }
else if (fnl.EndsWith(".nametable")) else if (fn.EndsWith(".nametable", StringComparison.OrdinalIgnoreCase))
{ {
fileType = TextFileType.Nametable; fileType = TextFileType.Nametable;
TextValue = File.ReadAllText(fn).Replace('\0', '\n'); TextValue = File.ReadAllText(fn).Replace('\0', '\n');
@ -242,7 +238,7 @@ namespace CodeWalker.Forms
private bool SaveToRPF(string txt) private bool SaveToRPF(string txt)
{ {
if (!(exploreForm?.EditMode ?? false)) return false; if (!(ExploreForm.Instance?.EditMode ?? false)) return false;
if (rpfFileEntry?.Parent == null) return false; if (rpfFileEntry?.Parent == null) return false;
byte[] data = null; byte[] data = null;
@ -287,14 +283,14 @@ namespace CodeWalker.Forms
try try
{ {
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return false; if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return false;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data); var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
if (newentry != rpfFileEntry) if (newentry != rpfFileEntry)
{ } { }
rpfFileEntry = newentry; rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;

View File

@ -44,15 +44,11 @@ namespace CodeWalker.Forms
private bool modified = false; private bool modified = false;
private bool LoadingXml = false; private bool LoadingXml = false;
private bool DelayHighlight = false; private bool DelayHighlight = false;
private ExploreForm exploreForm = null;
public RpfFileEntry rpfFileEntry { get; private set; } = null; public RpfFileEntry rpfFileEntry { get; private set; } = null;
public XmlForm(ExploreForm owner) public XmlForm()
{ {
exploreForm = owner;
InitializeComponent(); InitializeComponent();
} }
@ -215,7 +211,7 @@ namespace CodeWalker.Forms
private bool SaveToRPF(string txt) private bool SaveToRPF(string txt)
{ {
if (!(exploreForm?.EditMode ?? false)) return false; if (!(ExploreForm.Instance?.EditMode ?? false)) return false;
if (rpfFileEntry?.Parent == null) return false; if (rpfFileEntry?.Parent == null) return false;
byte[] data = null; byte[] data = null;
@ -238,14 +234,14 @@ namespace CodeWalker.Forms
try try
{ {
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return false; if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return false;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data); var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
if (newentry != rpfFileEntry) if (newentry != rpfFileEntry)
{ } { }
rpfFileEntry = newentry; rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false; modified = false;

View File

@ -61,7 +61,7 @@
this.label1 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label();
this.SelTextureZoomCombo = new System.Windows.Forms.ComboBox(); this.SelTextureZoomCombo = new System.Windows.Forms.ComboBox();
this.SelTexturePanel = new System.Windows.Forms.Panel(); this.SelTexturePanel = new System.Windows.Forms.Panel();
this.SelTexturePictureBox = new System.Windows.Forms.PictureBox(); this.SelTexturePictureBox = new PixelBox();
this.SelTextureMipLabel = new System.Windows.Forms.Label(); this.SelTextureMipLabel = new System.Windows.Forms.Label();
this.SelTextureDimensionsLabel = new System.Windows.Forms.Label(); this.SelTextureDimensionsLabel = new System.Windows.Forms.Label();
this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar(); this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar();
@ -436,6 +436,7 @@
this.SelTexturePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; this.SelTexturePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.SelTexturePictureBox.TabIndex = 45; this.SelTexturePictureBox.TabIndex = 45;
this.SelTexturePictureBox.TabStop = false; this.SelTexturePictureBox.TabStop = false;
// //
// SelTextureMipLabel // SelTextureMipLabel
// //
@ -573,7 +574,7 @@
private System.Windows.Forms.Label SelTextureDimensionsLabel; private System.Windows.Forms.Label SelTextureDimensionsLabel;
private System.Windows.Forms.TrackBar SelTextureMipTrackBar; private System.Windows.Forms.TrackBar SelTextureMipTrackBar;
private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label4;
private System.Windows.Forms.PictureBox SelTexturePictureBox; private PixelBox SelTexturePictureBox;
private System.Windows.Forms.TabPage DetailsTabPage; private System.Windows.Forms.TabPage DetailsTabPage;
private WinForms.PropertyGridFix DetailsPropertyGrid; private WinForms.PropertyGridFix DetailsPropertyGrid;
private System.Windows.Forms.ListView TexturesListView; private System.Windows.Forms.ListView TexturesListView;

View File

@ -5,10 +5,12 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Remoting.Channels;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@ -23,13 +25,11 @@ namespace CodeWalker.Forms
private Texture CurrentTexture = null; private Texture CurrentTexture = null;
private float CurrentZoom = 0.0f; //1.0 = 100%, 0.0 = stretch private float CurrentZoom = 0.0f; //1.0 = 100%, 0.0 = stretch
private bool Modified = false; private bool Modified = false;
private ExploreForm ExploreForm = null;
private ModelForm ModelForm = null; private ModelForm ModelForm = null;
public YtdForm(ExploreForm exploreForm = null, ModelForm modelForm = null) public YtdForm(ModelForm modelForm = null)
{ {
ExploreForm = exploreForm;
ModelForm = modelForm; ModelForm = modelForm;
InitializeComponent(); InitializeComponent();
} }
@ -155,8 +155,10 @@ namespace CodeWalker.Forms
byte[] pixels = DDSIO.GetPixels(tex, cmip); byte[] pixels = DDSIO.GetPixels(tex, cmip);
int w = tex.Width >> cmip; int w = tex.Width >> cmip;
int h = tex.Height >> cmip; int h = tex.Height >> cmip;
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
//Image bmp = Image.FromStream(new MemoryStream(pixels));
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format32bppArgb);
if (pixels != null) if (pixels != null)
{ {
var BoundsRect = new System.Drawing.Rectangle(0, 0, w, h); var BoundsRect = new System.Drawing.Rectangle(0, 0, w, h);
@ -396,7 +398,7 @@ namespace CodeWalker.Forms
} }
else else
{ {
var cansave = (ExploreForm?.EditMode ?? false); var cansave = (ExploreForm.Instance?.EditMode ?? false);
var s = "Save " + FileName; var s = "Save " + FileName;
var sas = "Save " + FileName + " As..."; var sas = "Save " + FileName + " As...";
FileSaveMenu.Text = s; FileSaveMenu.Text = s;
@ -435,7 +437,7 @@ namespace CodeWalker.Forms
private void SaveYTD(bool saveas = false) private void SaveYTD(bool saveas = false)
{ {
if (Ytd == null) return; if (Ytd == null) return;
if (!(ExploreForm?.EditMode ?? false)) if (!(ExploreForm.Instance?.EditMode ?? false))
{ {
saveas = true; saveas = true;
} }
@ -485,18 +487,18 @@ namespace CodeWalker.Forms
else if (!isinrpf) //save direct to filesystem in RPF explorer else if (!isinrpf) //save direct to filesystem in RPF explorer
{ {
File.WriteAllBytes(rpfFileEntry.Path, data); File.WriteAllBytes(rpfFileEntry.Path, data);
ExploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
} }
else //save to RPF... else //save to RPF...
{ {
if (!(ExploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) if (!ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))
{ {
MessageBox.Show("Unable to save file, RPF encryption needs to be OPEN for this operation!"); MessageBox.Show("Unable to save file, RPF encryption needs to be OPEN for this operation!");
return; return;
} }
Ytd.RpfFileEntry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data); Ytd.RpfFileEntry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, data);
ExploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer... ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
} }
Modified = false; Modified = false;
@ -647,4 +649,14 @@ namespace CodeWalker.Forms
RenameTexture(SelTextureNameTextBox.Text); RenameTexture(SelTextureNameTextBox.Text);
} }
} }
public class PixelBox : PictureBox
{
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
pe.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
base.OnPaint(pe);
}
}
} }

View File

@ -1,6 +1,7 @@
 
using CodeWalker.Properties; using CodeWalker.Properties;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@ -11,7 +12,7 @@ namespace CodeWalker.GameFiles
public static class GameFileCacheFactory public static class GameFileCacheFactory
{ {
public static GameFileCache _instance = null; public static GameFileCache _instance = null;
public static GameFileCache Create() public static GameFileCache GetInstance()
{ {
if (_instance == null) if (_instance == null)
{ {

View File

@ -36,13 +36,13 @@ namespace CodeWalker
private void RPFExplorerButton_Click(object sender, EventArgs e) private void RPFExplorerButton_Click(object sender, EventArgs e)
{ {
ExploreForm f = new ExploreForm(); ExploreForm f = new ExploreForm();
f.Show(this); f.Show();
} }
private void RPFBrowserButton_Click(object sender, EventArgs e) private void RPFBrowserButton_Click(object sender, EventArgs e)
{ {
BrowseForm f = new BrowseForm(); BrowseForm f = new BrowseForm();
f.Show(this); f.Show();
} }
private void ExtractScriptsButton_Click(object sender, EventArgs e) private void ExtractScriptsButton_Click(object sender, EventArgs e)

View File

@ -24,7 +24,7 @@ namespace CodeWalker
{ {
public Form Form { get { return this; } } //for DXForm/DXManager use public Form Form { get { return this; } } //for DXForm/DXManager use
public Renderer Renderer = null; public Renderer Renderer { get; set; }
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } } public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
public bool Pauserendering { get; set; } public bool Pauserendering { get; set; }
@ -41,6 +41,8 @@ namespace CodeWalker
Entity camEntity = new Entity(); Entity camEntity = new Entity();
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
bool MouseLButtonDown = false; bool MouseLButtonDown = false;
bool MouseRButtonDown = false; bool MouseRButtonDown = false;
@ -50,7 +52,7 @@ namespace CodeWalker
System.Drawing.Point MouseLastPoint; System.Drawing.Point MouseLastPoint;
public GameFileCache GameFileCache { get; } = GameFileCacheFactory.Create(); public GameFileCache GameFileCache { get; } = GameFileCacheFactory.GetInstance();
InputManager Input = new InputManager(); InputManager Input = new InputManager();
@ -189,7 +191,8 @@ namespace CodeWalker
formopen = true; formopen = true;
new Thread(new ThreadStart(ContentThread)).Start();
Task.Run(ContentThread);
frametimer.Start(); frametimer.Start();
@ -207,18 +210,27 @@ namespace CodeWalker
count++; count++;
} }
} }
public void RenderScene(DeviceContext context) public async ValueTask RenderScene(DeviceContext context)
{ {
float elapsed = (float)frametimer.Elapsed.TotalSeconds; float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart(); frametimer.Restart();
if (elapsed < 0.016666)
{
await Task.Delay((int)(0.016666 * elapsed) * 1000);
}
if (Pauserendering) return; if (Pauserendering) return;
GameFileCache.BeginFrame(); GameFileCache.BeginFrame();
if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50)) if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50))
{ return; } //couldn't get a lock, try again next time {
return;
} //couldn't get a lock, try again next time
try
{
UpdateControlInputs(elapsed); UpdateControlInputs(elapsed);
//space.Update(elapsed); //space.Update(elapsed);
@ -262,8 +274,11 @@ namespace CodeWalker
//RenderWidgets(); //RenderWidgets();
Renderer.EndRender(); Renderer.EndRender();
}
finally
{
Monitor.Exit(Renderer.RenderSyncRoot); Monitor.Exit(Renderer.RenderSyncRoot);
}
//UpdateMarkerSelectionPanelInvoke(); //UpdateMarkerSelectionPanelInvoke();
} }
@ -323,6 +338,8 @@ namespace CodeWalker
private void ContentThread() private void ContentThread()
{
try
{ {
//main content loading thread. //main content loading thread.
running = true; running = true;
@ -340,6 +357,8 @@ namespace CodeWalker
return; return;
} }
if (!GameFileCache.IsInited)
{
GameFileCache.EnableDlc = true; GameFileCache.EnableDlc = true;
GameFileCache.EnableMods = true; GameFileCache.EnableMods = true;
GameFileCache.LoadPeds = true; GameFileCache.LoadPeds = true;
@ -347,7 +366,8 @@ namespace CodeWalker
GameFileCache.LoadArchetypes = false;//to speed things up a little GameFileCache.LoadArchetypes = false;//to speed things up a little
GameFileCache.BuildExtendedJenkIndex = false;//to speed things up a little GameFileCache.BuildExtendedJenkIndex = false;//to speed things up a little
GameFileCache.DoFullStringIndex = true;//to get all global text from DLC... GameFileCache.DoFullStringIndex = true;//to get all global text from DLC...
GameFileCache.Init(UpdateStatus, LogError); GameFileCache.Init(UpdateStatus, LogError, force: false);
}
//UpdateDlcListComboBox(gameFileCache.DlcNameList); //UpdateDlcListComboBox(gameFileCache.DlcNameList);
@ -367,31 +387,41 @@ namespace CodeWalker
//UpdateStatus("Ready"); //UpdateStatus("Ready");
Task.Run(() => { Task.Run(async () => {
while (formopen && !IsDisposed) //renderer content loop while (formopen && !IsDisposed) //renderer content loop
{ {
bool rcItemsPending = Renderer.ContentThreadProc(); bool rcItemsPending = Renderer.ContentThreadProc();
if (!rcItemsPending) if (!rcItemsPending)
{ {
Thread.Sleep(1); //sleep if there's nothing to do await Task.Delay(ActiveForm == null ? 50 : 1).ConfigureAwait(false);
} }
} }
}); });
Task.Run(async () =>
{
while (formopen && !IsDisposed) //main asset loop while (formopen && !IsDisposed) //main asset loop
{ {
bool fcItemsPending = GameFileCache.ContentThreadProc(); bool fcItemsPending = GameFileCache.ContentThreadProc();
if (!fcItemsPending) if (!fcItemsPending)
{ {
Thread.Sleep(1); //sleep if there's nothing to do await Task.Delay(ActiveForm == null ? 50 : 1).ConfigureAwait(false);
} }
} }
GameFileCache.Clear();
running = false; running = false;
});
}
catch(Exception ex)
{
Console.WriteLine($"Exception occured in PedsForm::ContentThread.\n{ex}");
}
finally
{
running = false;
}
} }
@ -662,7 +692,7 @@ namespace CodeWalker
List<string> ycdlist = new List<string>(); List<string> ycdlist = new List<string>();
foreach (var ycde in ycds) foreach (var ycde in ycds)
{ {
ycdlist.Add(ycde.GetShortName()); ycdlist.Add(ycde.ShortName);
} }
ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray()); ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray());
ClipDictComboBox.Text = ""; ClipDictComboBox.Text = "";

View File

@ -1,4 +1,5 @@
using CodeWalker.Forms; using CodeWalker.Core.Utils;
using CodeWalker.Forms;
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using CodeWalker.Properties; using CodeWalker.Properties;
using CodeWalker.Utils; using CodeWalker.Utils;
@ -10,6 +11,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Shell; using System.Windows.Shell;
@ -29,6 +31,13 @@ namespace CodeWalker
{ {
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ConsoleWindow.Hide(); ConsoleWindow.Hide();
Application.ThreadException += (object sender, ThreadExceptionEventArgs e) =>
{
Console.WriteLine($"Unhandeled exception occured: {e.Exception}");
};
bool menumode = false; bool menumode = false;
bool explorermode = false; bool explorermode = false;
bool projectmode = false; bool projectmode = false;
@ -98,7 +107,13 @@ namespace CodeWalker
} }
else if (explorermode) else if (explorermode)
{ {
Application.Run(new ExploreForm()); if (!NamedPipe.TrySendMessageToOtherProcess("explorer"))
{
var form = new ExploreForm();
var namedPipe = new NamedPipe(form);
namedPipe.Init();
Application.Run(form);
}
} }
else if (projectmode) else if (projectmode)
{ {
@ -114,15 +129,29 @@ namespace CodeWalker
} }
else if (path != null) else if (path != null)
{ {
var modelForm = new ModelForm(); if (!NamedPipe.TrySendMessageToOtherProcess($"open-file {path}"))
modelForm.Load += new EventHandler(async (sender, eventArgs) => { {
modelForm.ViewModel(path); Form form = null;
}); try
Application.Run(modelForm); {
form = OpenAnyFile.OpenFilePath(path);
}
catch (NotImplementedException ex)
{
MessageBox.Show("Dit type bestand is op het moment nog niet ondersteund!", ex.ToString());
}
if (form != null)
{
Application.Run(form);
}
}
} }
else else
{ {
Application.Run(new WorldForm()); var form = new WorldForm();
var namedPipe = new NamedPipe(form);
namedPipe.Init();
Application.Run(form);
} }
#if !DEBUG #if !DEBUG
} }

View File

@ -73,20 +73,19 @@ namespace CodeWalker.Project.Panels
var getYtypName = new Func<YtypFile, string>((ytyp) => var getYtypName = new Func<YtypFile, string>((ytyp) =>
{ {
var ytypname = ytyp?.RpfFileEntry?.NameLower; var ytypname = ytyp?.RpfFileEntry?.Name;
if (ytyp != null) if (ytyp != null)
{ {
if (string.IsNullOrEmpty(ytypname)) if (string.IsNullOrEmpty(ytypname))
{ {
ytypname = ytyp.RpfFileEntry?.Name?.ToLowerInvariant(); ytypname = ytyp.RpfFileEntry?.Name ?? string.Empty;
if (ytypname == null) ytypname = "";
} }
if (ytypname.EndsWith(".ytyp")) if (ytypname.EndsWith(".ytyp", StringComparison.OrdinalIgnoreCase))
{ {
ytypname = ytypname.Substring(0, ytypname.Length - 5); ytypname = ytypname.Substring(0, ytypname.Length - 5);
} }
} }
return ytypname; return ytypname?.ToLowerInvariant();
}); });
@ -97,7 +96,7 @@ namespace CodeWalker.Project.Panels
sb.AppendLine(" <imapDependencies_2>"); sb.AppendLine(" <imapDependencies_2>");
foreach (var ymap in CurrentProjectFile.YmapFiles) foreach (var ymap in CurrentProjectFile.YmapFiles)
{ {
var ymapname = ymap.RpfFileEntry?.NameLower; var ymapname = ymap.RpfFileEntry?.Name.ToLowerInvariant();
if (string.IsNullOrEmpty(ymapname)) if (string.IsNullOrEmpty(ymapname))
{ {
ymapname = ymap.Name.ToLowerInvariant(); ymapname = ymap.Name.ToLowerInvariant();

View File

@ -60,8 +60,8 @@ namespace CodeWalker.Project.Panels
LoadYmapTreeNodes(ymapfile, ymapnode); LoadYmapTreeNodes(ymapfile, ymapnode);
JenkIndex.Ensure(name); JenkIndex.EnsureBoth(name);
JenkIndex.Ensure(Path.GetFileNameWithoutExtension(name)); JenkIndex.EnsureBoth(Path.GetFileNameWithoutExtension(name));
} }
ymapsnode.Expand(); ymapsnode.Expand();
} }
@ -84,8 +84,8 @@ namespace CodeWalker.Project.Panels
LoadYtypTreeNodes(ytypfile, ytypnode); LoadYtypTreeNodes(ytypfile, ytypnode);
JenkIndex.Ensure(name); JenkIndex.EnsureBoth(name);
JenkIndex.Ensure(Path.GetFileNameWithoutExtension(name)); JenkIndex.EnsureBoth(Path.GetFileNameWithoutExtension(name));
} }
ytypsnode.Expand(); ytypsnode.Expand();
} }

View File

@ -530,9 +530,9 @@ namespace CodeWalker.Project
ytyp.RpfFileEntry.Name = Path.GetFileName(filename); ytyp.RpfFileEntry.Name = Path.GetFileName(filename);
ytyp.FilePath = GetFullFilePath(filename); ytyp.FilePath = GetFullFilePath(filename);
ytyp.Name = ytyp.RpfFileEntry.Name; ytyp.Name = ytyp.RpfFileEntry.Name;
JenkIndex.Ensure(ytyp.Name); JenkIndex.EnsureBoth(ytyp.Name);
JenkIndex.Ensure(Path.GetFileNameWithoutExtension(ytyp.Name)); JenkIndex.EnsureBoth(Path.GetFileNameWithoutExtension(ytyp.Name));
JenkIndex.Ensure(filename); JenkIndex.EnsureBoth(filename);
if (!AddYtypFile(ytyp)) return null; if (!AddYtypFile(ytyp)) return null;
return ytyp; return ytyp;
} }

View File

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -124,26 +125,22 @@ namespace CodeWalker.Project
SetTheme(Settings.Default.ProjectWindowTheme, false); SetTheme(Settings.Default.ProjectWindowTheme, false);
ShowDefaultPanels(); ShowDefaultPanels();
GameFileCache = GameFileCacheFactory.GetInstance();
if ((WorldForm != null) && (WorldForm.GameFileCache != null)) if (!GameFileCache.IsInited)
{ {
GameFileCache = WorldForm.GameFileCache; Task.Run(() =>
RpfMan = GameFileCache.RpfMan;
}
else
{
GameFileCache = GameFileCacheFactory.Create();
new Thread(new ThreadStart(() =>
{ {
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
GameFileCache.Init(UpdateStatus, UpdateError); GameFileCache.Init(UpdateStatus, UpdateError);
RpfMan = GameFileCache.RpfMan; });
})).Start();
}
} }
RpfMan = GameFileCache.RpfMan;
}
private void UpdateStatus(string text) private void UpdateStatus(string text)
{ {
return;
try try
{ {
if (InvokeRequired) if (InvokeRequired)
@ -168,6 +165,7 @@ namespace CodeWalker.Project
} }
else else
{ {
Console.WriteLine(text);
//TODO: error text //TODO: error text
//ErrorLabel.Text = text; //ErrorLabel.Text = text;
} }
@ -1640,14 +1638,14 @@ namespace CodeWalker.Project
} }
break; break;
case ".dat": case ".dat":
if (fn.StartsWith("trains")) if (fn.StartsWith("trains", StringComparison.OrdinalIgnoreCase))
{ {
var track = CurrentProjectFile.AddTrainsFile(file); var track = CurrentProjectFile.AddTrainsFile(file);
if (track != null) LoadTrainTrackFromFile(track, file); if (track != null) LoadTrainTrackFromFile(track, file);
} }
break; break;
case ".rel": case ".rel":
if (fn.EndsWith(".dat151.rel")) if (fn.EndsWith(".dat151.rel", StringComparison.OrdinalIgnoreCase))
{ {
var dat151 = CurrentProjectFile.AddAudioRelFile(file); var dat151 = CurrentProjectFile.AddAudioRelFile(file);
if (dat151 != null) LoadAudioRelFromFile(dat151, file); if (dat151 != null) LoadAudioRelFromFile(dat151, file);
@ -6129,7 +6127,7 @@ namespace CodeWalker.Project
var delim = line.Contains(",") ? "," : " "; var delim = line.Contains(",") ? "," : " ";
var vals = line.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries); var vals = line.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries);
if (vals.Length < 3) continue; if (vals.Length < 3) continue;
if (vals[0].StartsWith("X")) continue; if (vals[0].StartsWith("X", StringComparison.OrdinalIgnoreCase)) continue;
Vector3 pos = Vector3.Zero; Vector3 pos = Vector3.Zero;
float dir = 0; float dir = 0;
var action = CScenarioChainingEdge__eAction.Move; var action = CScenarioChainingEdge__eAction.Move;
@ -7083,7 +7081,7 @@ namespace CodeWalker.Project
private DateTime LastProjectCheck = DateTime.MinValue;
public void GetVisibleYmaps(Camera camera, Dictionary<MetaHash, YmapFile> ymaps) public void GetVisibleYmaps(Camera camera, Dictionary<MetaHash, YmapFile> ymaps)
{ {
if (hidegtavmap) if (hidegtavmap)
@ -7106,10 +7104,16 @@ namespace CodeWalker.Project
} }
} }
visiblemloentities.Clear(); if (DateTime.Now - LastProjectCheck < TimeSpan.FromSeconds(1))
foreach (var kvp in ymaps)//TODO: improve performance {
return;
}
LastProjectCheck = DateTime.Now;
visiblemloentities.Clear();
foreach (var ymap in ymaps.Values)//TODO: improve performance
{ {
var ymap = kvp.Value;
if (ymap.AllEntities != null)//THIS IS TERRIBLE! EATING ALL FPS if (ymap.AllEntities != null)//THIS IS TERRIBLE! EATING ALL FPS
{ {
foreach (var ent in ymap.AllEntities)//WHYYYY - maybe only do this after loading/editing ytyp! foreach (var ent in ymap.AllEntities)//WHYYYY - maybe only do this after loading/editing ytyp!
@ -8664,7 +8668,6 @@ namespace CodeWalker.Project
track.Name = fname; track.Name = fname;
track.FilePath = filename; track.FilePath = filename;
track.RpfFileEntry.Name = fname; track.RpfFileEntry.Name = fname;
track.RpfFileEntry.NameLower = fname.ToLowerInvariant();
if (WorldForm != null) if (WorldForm != null)
{ {

View File

@ -8,6 +8,9 @@
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using System;
using System.Windows.Forms;
namespace CodeWalker.Properties { namespace CodeWalker.Properties {

View File

@ -2,7 +2,8 @@
"profiles": { "profiles": {
"CodeWalker": { "CodeWalker": {
"commandName": "Project", "commandName": "Project",
"workingDirectory": ".." "workingDirectory": "..",
"nativeDebugging": true
} }
} }
} }

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
@ -17,12 +18,14 @@ namespace CodeWalker.Rendering
//So, i've used an interface instead, since really just some of the form properties //So, i've used an interface instead, since really just some of the form properties
//and a couple of extra methods (these callbacks) are needed by DXManager. //and a couple of extra methods (these callbacks) are needed by DXManager.
public Renderer Renderer { get; set; }
Form Form { get; } Form Form { get; }
public CancellationTokenSource CancellationTokenSource { get; }
public bool Pauserendering { get; set; } public bool Pauserendering { get; set; }
void InitScene(Device device); void InitScene(Device device);
void CleanupScene(); void CleanupScene();
void RenderScene(DeviceContext context); ValueTask RenderScene(DeviceContext context);
void BuffersResized(int w, int h); void BuffersResized(int w, int h);
bool ConfirmQuit(); bool ConfirmQuit();
} }

View File

@ -15,6 +15,7 @@ using SharpDX.Direct3D;
using System.Runtime; using System.Runtime;
using CodeWalker.Core.Utils; using CodeWalker.Core.Utils;
using CodeWalker.Properties; using CodeWalker.Properties;
using CodeWalker.GameFiles;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -30,10 +31,14 @@ namespace CodeWalker.Rendering
public RenderTargetView targetview { get; private set; } public RenderTargetView targetview { get; private set; }
public DepthStencilView depthview { get; private set; } public DepthStencilView depthview { get; private set; }
public CancellationToken cancellationToken;
private volatile bool Running = false; private volatile bool Running = false;
private volatile bool Rendering = false; private volatile bool Rendering = false;
private volatile bool Resizing = false; private volatile bool Resizing = false;
private object syncroot = new object(); //for thread safety private SemaphoreSlim semaphore = new SemaphoreSlim(1);
public int multisamplecount { get; private set; } = Settings.Default.AntiAliasing; public int multisamplecount { get; private set; } = Settings.Default.AntiAliasing;
public int multisamplequality { get; private set; } = 0; //should be a setting... public int multisamplequality { get; private set; } = 0; //should be a setting...
public Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross public Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross
@ -46,6 +51,8 @@ namespace CodeWalker.Rendering
dxform = form; dxform = form;
autoStartLoop = autostart; autoStartLoop = autostart;
cancellationToken = form.CancellationTokenSource.Token;
try try
{ {
//SharpDX.Configuration.EnableObjectTracking = true; //SharpDX.Configuration.EnableObjectTracking = true;
@ -147,6 +154,9 @@ namespace CodeWalker.Rendering
private void Cleanup() private void Cleanup()
{ {
try
{
using var _ = new DisposableTimer("DXManager Cleanup");
Running = false; Running = false;
int count = 0; int count = 0;
while (Rendering && (count < 1000)) while (Rendering && (count < 1000))
@ -170,8 +180,12 @@ namespace CodeWalker.Rendering
//var objs = SharpDX.Diagnostics.ObjectTracker.FindActiveObjects(); //var objs = SharpDX.Diagnostics.ObjectTracker.FindActiveObjects();
if (device != null) device.Dispose(); if (device != null) device.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
GC.Collect();
} }
private void CreateRenderBuffers() private void CreateRenderBuffers()
{ {
@ -209,19 +223,22 @@ namespace CodeWalker.Rendering
} }
private void Resize() private void Resize()
{ {
Console.WriteLine($"Resizing {Resizing}");
if (Resizing) return; if (Resizing) return;
Monitor.Enter(syncroot); semaphore.Wait();
int width = dxform.Form.ClientSize.Width; int width = dxform.Form.ClientSize.Width;
int height = dxform.Form.ClientSize.Height; int height = dxform.Form.ClientSize.Height;
if (targetview != null) targetview.Dispose(); if (targetview != null) targetview.Dispose();
if (backbuffer != null) backbuffer.Dispose(); if (backbuffer != null) backbuffer.Dispose();
swapchain.ResizeBuffers(1, width, height, Format.Unknown, SwapChainFlags.AllowModeSwitch); swapchain.ResizeBuffers(1, width, height, Format.Unknown, SwapChainFlags.AllowModeSwitch);
CreateRenderBuffers(); CreateRenderBuffers();
Monitor.Exit(syncroot); semaphore.Release();
dxform.BuffersResized(width, height); dxform.BuffersResized(width, height);
} }
@ -244,6 +261,10 @@ namespace CodeWalker.Rendering
} }
if (!e.Cancel) if (!e.Cancel)
{ {
if (!dxform.CancellationTokenSource.IsCancellationRequested)
{
dxform.CancellationTokenSource.Cancel();
}
Cleanup(); Cleanup();
} }
} }
@ -274,11 +295,38 @@ namespace CodeWalker.Rendering
} }
private void StartRenderLoop() private void StartRenderLoop()
{ {
if (Running)
{
return;
}
Running = true; Running = true;
new Task(RenderLoop, TaskCreationOptions.LongRunning).Start(TaskScheduler.Default);
Task.Run(RenderLoop);
//new Thread(new ThreadStart(RenderLoop)).Start(); //new Thread(new ThreadStart(RenderLoop)).Start();
} }
private void RenderLoop()
bool isPointVisibleOnAScreen(Point p)
{
foreach (Screen s in Screen.AllScreens)
{
if (p.X < s.Bounds.Right && p.X > s.Bounds.Left && p.Y > s.Bounds.Top && p.Y < s.Bounds.Bottom)
return true;
}
return false;
}
bool isFormFullyVisible(Form f)
{
return isPointVisibleOnAScreen(new Point(f.Left, f.Top))
&& isPointVisibleOnAScreen(new Point(f.Right, f.Top))
&& isPointVisibleOnAScreen(new Point(f.Left, f.Bottom))
&& isPointVisibleOnAScreen(new Point(f.Right, f.Bottom));
}
private int inactiveCount = 0;
private async Task RenderLoop()
{ {
//SharpDX.Configuration.EnableObjectTracking = true; //SharpDX.Configuration.EnableObjectTracking = true;
//Task.Run(async () => //Task.Run(async () =>
@ -289,7 +337,9 @@ namespace CodeWalker.Rendering
// await Task.Delay(5000); // await Task.Delay(5000);
// } // }
//}); //});
Thread.CurrentThread.Name = "RenderLoop";
try
{
while (Running) while (Running)
{ {
while (Resizing) while (Resizing)
@ -301,13 +351,14 @@ namespace CodeWalker.Rendering
dxform.Pauserendering = true; dxform.Pauserendering = true;
Console.WriteLine("Window is minimized"); Console.WriteLine("Window is minimized");
dxform.RenderScene(context); await dxform.RenderScene(context);
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
while (dxform.Form.WindowState == FormWindowState.Minimized) while (dxform.Form.WindowState == FormWindowState.Minimized)
{ {
Thread.Sleep(100); //don't hog CPU when minimised await Task.Delay(100, cancellationToken).ConfigureAwait(false); //don't hog CPU when minimised
if (dxform.Form.IsDisposed) return; //if closed while minimised if (dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested) return; //if closed while minimised
} }
dxform.Pauserendering = false; dxform.Pauserendering = false;
Console.WriteLine("Window is maximized"); Console.WriteLine("Window is maximized");
@ -316,20 +367,50 @@ namespace CodeWalker.Rendering
if (Form.ActiveForm == null) if (Form.ActiveForm == null)
{ {
Thread.Sleep(100); //reduce the FPS when the app isn't active (maybe this should be configurable?) inactiveCount++;
if (context.IsDisposed) return; //if form closed while sleeping (eg from rightclick on taskbar) if (inactiveCount > 100)
{
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
while (Form.ActiveForm == null)
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false); //reduce the FPS when the app isn't active (maybe this should be configurable?)
if (context.IsDisposed || dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested) return; //if form closed while sleeping (eg from rightclick on taskbar)
}
}
else
{
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
}
}
else
{
inactiveCount = 0;
if (Form.ActiveForm != dxform.Form)
{
await Task.Delay(25, cancellationToken).ConfigureAwait(false);
}
} }
if (context.IsDisposed || dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested) return;
Rendering = true; Rendering = true;
try
{ if (!await semaphore.WaitAsync(50, cancellationToken).ConfigureAwait(false))
if (!Monitor.TryEnter(syncroot, 50))
{ {
Console.WriteLine("Failed to get lock for syncroot"); Console.WriteLine("Failed to get lock for syncroot");
Thread.Sleep(10); //don't hog CPU when not able to render... await Task.Delay(10, cancellationToken).ConfigureAwait(false); //don't hog CPU when not able to render...
continue; continue;
} }
if (dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested)
{
Rendering = false;
return;
}
try
{
bool ok = true; bool ok = true;
try try
@ -345,14 +426,14 @@ namespace CodeWalker.Rendering
if (ok) if (ok)
{ {
if (dxform.Form.IsDisposed) if (dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested)
{ {
Monitor.Exit(syncroot);
Rendering = false; Rendering = false;
return; //the form was closed... stop!! return; //the form was closed... stop!!
} }
dxform.RenderScene(context); await dxform.RenderScene(context);
try try
{ {
@ -366,11 +447,22 @@ namespace CodeWalker.Rendering
} }
finally finally
{ {
Monitor.Exit(syncroot); semaphore.Release();
Rendering = false; Rendering = false;
} }
} }
} }
catch (TaskCanceledException) { }
catch (Exception ex)
{
Console.WriteLine(ex);
throw;
}
finally
{
Running = false;
}
}
public void ClearRenderTarget(DeviceContext ctx) public void ClearRenderTarget(DeviceContext ctx)

View File

@ -10,6 +10,7 @@ using Buffer = SharpDX.Direct3D11.Buffer;
using CodeWalker.World; using CodeWalker.World;
using SharpDX.Direct3D; using SharpDX.Direct3D;
using SharpDX; using SharpDX;
using System.Threading;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -72,6 +73,12 @@ namespace CodeWalker.Rendering
public RenderableModel[] LowModels; public RenderableModel[] LowModels;
public RenderableModel[] VlowModels; public RenderableModel[] VlowModels;
public RenderableModel[] AllModels; public RenderableModel[] AllModels;
public float LodDistanceHigh;
public float LodDistanceMed;
public float LodDistanceLow;
public float LodDistanceVLow;
//public Dictionary<uint, Texture> TextureDict { get; private set; } //public Dictionary<uint, Texture> TextureDict { get; private set; }
//public long EmbeddedTextureSize { get; private set; } //public long EmbeddedTextureSize { get; private set; }
@ -148,6 +155,10 @@ namespace CodeWalker.Rendering
curmodel += vlow.Length; curmodel += vlow.Length;
} }
LodDistanceHigh = drawable.LodDistHigh;
LodDistanceMed = drawable.LodDistMed;
LodDistanceLow = drawable.LodDistLow;
LodDistanceVLow = drawable.LodDistVlow;
//var sg = Drawable.ShaderGroup; //var sg = Drawable.ShaderGroup;
//if ((sg != null) && (sg.TextureDictionary != null)) //if ((sg != null) && (sg.TextureDictionary != null))
@ -346,6 +357,30 @@ namespace CodeWalker.Rendering
Lights = rlights; Lights = rlights;
} }
public RenderableModel[] GetModels(float distance)
{
if (distance > LodDistanceVLow)
{
return Array.Empty<RenderableModel>();
}
else if (distance > LodDistanceLow)
{
return VlowModels ?? Array.Empty<RenderableModel>();
}
else if (distance > LodDistanceMed)
{
return LowModels ?? Array.Empty<RenderableModel>();
}
else if (distance > LodDistanceHigh)
{
return MedModels ?? Array.Empty<RenderableModel>();
}
else
{
return HDModels;
}
}
private RenderableModel InitModel(DrawableModel dm) private RenderableModel InitModel(DrawableModel dm)
{ {
var rmodel = new RenderableModel(); var rmodel = new RenderableModel();
@ -723,7 +758,7 @@ namespace CodeWalker.Rendering
} }
else else
{ {
foreach (var model in HDModels) //TODO: figure out which models/geometries this should be applying to! foreach (var model in AllModels) //TODO: figure out which models/geometries this should be applying to!
{ {
if (model == null) continue; if (model == null) continue;
foreach (var geom in model.Geometries) foreach (var geom in model.Geometries)

View File

@ -11,6 +11,7 @@ using System.Collections.Concurrent;
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using System.Threading; using System.Threading;
using CodeWalker.Properties; using CodeWalker.Properties;
using System.Diagnostics;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -20,7 +21,7 @@ namespace CodeWalker.Rendering
public DateTime LastUnload = DateTime.UtcNow; public DateTime LastUnload = DateTime.UtcNow;
public double CacheTime = Settings.Default.GPUCacheTime;// 10.0; //seconds to keep something that's not used public double CacheTime = Settings.Default.GPUCacheTime;// 10.0; //seconds to keep something that's not used
public double UnloadTime = Settings.Default.GPUCacheFlushTime;// 0.1; //seconds between running unload cycles public double UnloadTime = Settings.Default.GPUCacheFlushTime;// 0.1; //seconds between running unload cycles
public int MaxItemsPerLoop = 1; //to keep things flowing public int MaxItemsPerLoop = 3; //to keep things flowing
public long TotalGraphicsMemoryUse public long TotalGraphicsMemoryUse
{ {
@ -117,6 +118,11 @@ namespace CodeWalker.Rendering
{ {
currentDevice = null; currentDevice = null;
Clear();
}
public void Clear()
{
renderables.Clear(); renderables.Clear();
textures.Clear(); textures.Clear();
boundcomps.Clear(); boundcomps.Clear();
@ -132,8 +138,8 @@ namespace CodeWalker.Rendering
if (currentDevice == null) return false; //can't do anything with no device if (currentDevice == null) return false; //can't do anything with no device
Monitor.Enter(updateSyncRoot); Monitor.Enter(updateSyncRoot);
try
{
//load the queued items if possible //load the queued items if possible
int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop); int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop);
int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop); int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop);
@ -160,7 +166,8 @@ namespace CodeWalker.Rendering
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
var deltat = (now - LastUpdate).TotalSeconds; var deltat = (now - LastUpdate).TotalSeconds;
var unloadt = (now - LastUnload).TotalSeconds; var unloadt = (now - LastUnload).TotalSeconds;
if ((unloadt > UnloadTime) && (deltat < 0.25)) //don't try the unload on every loop... or when really busy //Console.WriteLine($"deltat: {deltat}; unloadt: {unloadt}; UnloadTime: {UnloadTime};");
if ((unloadt > UnloadTime) && (deltat < 1.0)) //don't try the unload on every loop... or when really busy
{ {
//unload items that haven't been used in longer than the cache period. //unload items that haven't been used in longer than the cache period.
@ -179,10 +186,18 @@ namespace CodeWalker.Rendering
LastUpdate = DateTime.UtcNow; LastUpdate = DateTime.UtcNow;
Monitor.Exit(updateSyncRoot);
return itemsStillPending; return itemsStillPending;
} }
catch(Exception ex)
{
Console.WriteLine(ex);
return true;
}
finally
{
Monitor.Exit(updateSyncRoot);
}
}
public void RenderThreadSync() public void RenderThreadSync()
{ {
@ -255,13 +270,20 @@ namespace CodeWalker.Rendering
} }
[Flags]
public enum LoadFlags
{
None = 0,
IsLoaded = 1,
LoadQueued = 2,
}
public abstract class RenderableCacheItem<TKey> public abstract class RenderableCacheItem<TKey>
{ {
public TKey Key; public TKey Key;
public volatile bool IsLoaded = false; public volatile bool IsLoaded = false;
public volatile bool LoadQueued = false; public volatile bool LoadQueued = false;
public long LastUseTime = 0; public Stopwatch LastUseTime = Stopwatch.StartNew();
//public DateTime LastUseTime { get; set; } //public DateTime LastUseTime { get; set; }
public long DataSize { get; set; } public long DataSize { get; set; }
@ -333,7 +355,14 @@ namespace CodeWalker.Rendering
LoadedCount = 0; LoadedCount = 0;
while (itemsToLoad.TryDequeue(out item)) while (itemsToLoad.TryDequeue(out item))
{ {
if (item.IsLoaded) continue; //don't load it again... if (item.IsLoaded)
{
item.LoadQueued = false;
continue;
}
try
{
LoadedCount++; LoadedCount++;
long gcachefree = CacheLimit - Interlocked.Read(ref CacheUse);// CacheUse; long gcachefree = CacheLimit - Interlocked.Read(ref CacheUse);// CacheUse;
if (gcachefree > item.DataSize) if (gcachefree > item.DataSize)
@ -344,15 +373,21 @@ namespace CodeWalker.Rendering
loadeditems.AddLast(item); loadeditems.AddLast(item);
Interlocked.Add(ref CacheUse, item.DataSize); Interlocked.Add(ref CacheUse, item.DataSize);
} }
catch //(Exception ex) catch (Exception ex)
{ {
//todo: error handling... Console.WriteLine(ex);
} }
} }
else }
catch(Exception ex)
{ {
item.LoadQueued = false; //can try load it again later.. Console.WriteLine(ex);
} }
finally
{
item.LoadQueued = false;
}
if (LoadedCount >= maxitemsperloop) break; if (LoadedCount >= maxitemsperloop) break;
} }
return LoadedCount; return LoadedCount;
@ -361,12 +396,12 @@ namespace CodeWalker.Rendering
public void UnloadProc() public void UnloadProc()
{ {
//unload items that haven't been used in longer than the cache period. //unload items that haven't been used in longer than the cache period.
var now = DateTime.UtcNow; //var now = DateTime.UtcNow;
var rnode = loadeditems.First; var rnode = loadeditems.First;
while (rnode != null) while (rnode != null)
{ {
var lu = DateTime.FromBinary(Interlocked.Read(ref rnode.Value.LastUseTime)); var lastUsed = rnode.Value.LastUseTime.Elapsed;
if ((now - lu).TotalSeconds > CacheTime) if (lastUsed.TotalSeconds > CacheTime)
{ {
var nextnode = rnode.Next; var nextnode = rnode.Next;
itemsToUnload.Enqueue(rnode.Value); itemsToUnload.Enqueue(rnode.Value);
@ -399,7 +434,7 @@ namespace CodeWalker.Rendering
} }
while (itemsToUnload.TryDequeue(out item)) while (itemsToUnload.TryDequeue(out item))
{ {
if ((item.Key != null) && (cacheitems.ContainsKey(item.Key))) if (item.Key != null && cacheitems.ContainsKey(item.Key))
{ {
cacheitems.Remove(item.Key); cacheitems.Remove(item.Key);
} }
@ -420,7 +455,7 @@ namespace CodeWalker.Rendering
item.Init(key); item.Init(key);
cacheitems.Add(key, item); cacheitems.Add(key, item);
} }
Interlocked.Exchange(ref item.LastUseTime, LastFrameTime); item.LastUseTime.Restart();
if ((!item.IsLoaded) && (!item.LoadQueued))// || if ((!item.IsLoaded) && (!item.LoadQueued))// ||
{ {
item.LoadQueued = true; item.LoadQueued = true;

View File

@ -173,7 +173,7 @@ namespace CodeWalker.Rendering
public Renderer(DXForm form, GameFileCache cache) public Renderer(DXForm form, GameFileCache cache)
{ {
Form = form; Form = form;
gameFileCache = cache ?? GameFileCacheFactory.Create(); gameFileCache = cache ?? GameFileCacheFactory.GetInstance();
renderableCache = new RenderableCache(); renderableCache = new RenderableCache();
var s = Settings.Default; var s = Settings.Default;
@ -386,8 +386,11 @@ namespace CodeWalker.Rendering
var ctc = renderableCache.LoadedTextureCount; var ctc = renderableCache.LoadedTextureCount;
var vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0); var vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
var vram = TextUtil.GetBytesReadable(vr); var vram = TextUtil.GetBytesReadable(vr);
var cacheUsage = TextUtil.GetBytesReadable(gameFileCache.MemoryUsage);
var cacheMax = TextUtil.GetBytesReadable(gameFileCache.MaxMemoryUsage);
return $"Drawn: {rgc} geom, Loaded: {crc} dr, {ctc} tx, Vram: {vram}, Fps: {fps}";
return $"Drawn: {rgc} geom, Loaded: {crc} dr, {ctc} tx, Vram: {vram}, Fps: {fps}, Cache: {cacheUsage}/{cacheMax}";
} }
@ -1690,7 +1693,7 @@ namespace CodeWalker.Rendering
for (int i = 0; i < frag.Layers.Length; i++) for (int i = 0; i < frag.Layers.Length; i++)
{ {
CloudHatFragLayer layer = frag.Layers[i]; CloudHatFragLayer layer = frag.Layers[i];
uint dhash = JenkHash.GenHash(layer.Filename.ToLowerInvariant()); uint dhash = JenkHash.GenHashLower(layer.Filename);
Archetype arch = gameFileCache.GetArchetype(dhash); Archetype arch = gameFileCache.GetArchetype(dhash);
if (arch == null) if (arch == null)
{ continue; } { continue; }
@ -1700,8 +1703,10 @@ namespace CodeWalker.Rendering
var drw = gameFileCache.TryGetDrawable(arch); var drw = gameFileCache.TryGetDrawable(arch);
var rnd = TryGetRenderable(arch, drw); var rnd = TryGetRenderable(arch, drw);
if ((rnd == null) || (rnd.IsLoaded == false) || (rnd.AllTexturesLoaded == false)) if (rnd == null || !rnd.IsLoaded || !rnd.AllTexturesLoaded)
{ continue; } {
continue;
}
RenderableInst rinst = new RenderableInst(); RenderableInst rinst = new RenderableInst();
@ -2051,7 +2056,7 @@ namespace CodeWalker.Rendering
ent.Distance = dist; ent.Distance = dist;
ent.IsVisible = (dist <= loddist); ent.IsWithinLodDist = (dist <= loddist);
ent.ChildrenVisible = (dist <= cloddist) && (ent._CEntityDef.numChildren > 0); ent.ChildrenVisible = (dist <= cloddist) && (ent._CEntityDef.numChildren > 0);
@ -2061,7 +2066,7 @@ namespace CodeWalker.Rendering
if ((ent._CEntityDef.lodLevel == rage__eLodType.LODTYPES_DEPTH_ORPHANHD) || if ((ent._CEntityDef.lodLevel == rage__eLodType.LODTYPES_DEPTH_ORPHANHD) ||
(ent._CEntityDef.lodLevel < renderworldMaxLOD)) (ent._CEntityDef.lodLevel < renderworldMaxLOD))
{ {
ent.IsVisible = false; ent.IsWithinLodDist = false;
ent.ChildrenVisible = false; ent.ChildrenVisible = false;
} }
if (ent._CEntityDef.lodLevel == renderworldMaxLOD) if (ent._CEntityDef.lodLevel == renderworldMaxLOD)
@ -2090,9 +2095,10 @@ namespace CodeWalker.Rendering
} }
private void RenderWorldRecurseAddEntities(YmapEntityDef ent) private void RenderWorldRecurseAddEntities(YmapEntityDef ent)
{ {
bool hide = ent.ChildrenVisible; bool childrenVisible = ent.ChildrenVisible;
bool force = (ent.Parent != null) && ent.Parent.ChildrenVisible && !hide; var parentChildrenVisible = ent.Parent?.ChildrenVisible ?? true;
if (force || (ent.IsVisible && !hide)) bool force = parentChildrenVisible && !childrenVisible;
if (force || (ent.IsWithinLodDist && !childrenVisible))
{ {
if (ent.Archetype != null) if (ent.Archetype != null)
{ {
@ -2115,7 +2121,7 @@ namespace CodeWalker.Rendering
} }
} }
if (ent.IsVisible && ent.ChildrenVisible && (ent.Children != null)) if (ent.IsWithinLodDist && ent.ChildrenVisible && (ent.Children != null))
{ {
for (int i = 0; i < ent.Children.Length; i++) for (int i = 0; i < ent.Children.Length; i++)
{ {
@ -2167,7 +2173,7 @@ namespace CodeWalker.Rendering
if (intent?.Archetype == null) continue; //missing archetype... if (intent?.Archetype == null) continue; //missing archetype...
if (!RenderIsEntityFinalRender(intent)) continue; //proxy or something.. if (!RenderIsEntityFinalRender(intent)) continue; //proxy or something..
intent.IsVisible = true; intent.IsWithinLodDist = true;
if (!camera.ViewFrustum.ContainsAABBNoClip(ref intent.BBCenter, ref intent.BBExtent)) if (!camera.ViewFrustum.ContainsAABBNoClip(ref intent.BBCenter, ref intent.BBExtent))
{ {
@ -2192,7 +2198,7 @@ namespace CodeWalker.Rendering
if (intent?.Archetype == null) continue; //missing archetype... if (intent?.Archetype == null) continue; //missing archetype...
if (!RenderIsEntityFinalRender(intent)) continue; //proxy or something.. if (!RenderIsEntityFinalRender(intent)) continue; //proxy or something..
intent.IsVisible = true; intent.IsWithinLodDist = true;
if (!camera.ViewFrustum.ContainsAABBNoClip(ref intent.BBCenter, ref intent.BBExtent)) if (!camera.ViewFrustum.ContainsAABBNoClip(ref intent.BBCenter, ref intent.BBExtent))
{ {
@ -2390,8 +2396,11 @@ namespace CodeWalker.Rendering
{ {
var drawable = gameFileCache.TryGetDrawable(arch); var drawable = gameFileCache.TryGetDrawable(arch);
rndbl = TryGetRenderable(arch, drawable); rndbl = TryGetRenderable(arch, drawable);
if (rndbl != null && rndbl.IsLoaded)
{
ArchetypeRenderables[arch] = rndbl; ArchetypeRenderables[arch] = rndbl;
} }
}
if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload)) if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload))
{ {
return rndbl; return rndbl;
@ -2457,7 +2466,10 @@ namespace CodeWalker.Rendering
} }
private bool RenderYmapLOD(YmapFile ymap, YmapEntityDef entity) private bool RenderYmapLOD(YmapFile ymap, YmapEntityDef entity)
{ {
if (!ymap.Loaded) return false; if (!ymap.Loaded)
{
return false;
}
ymap.EnsureChildYmaps(gameFileCache); ymap.EnsureChildYmaps(gameFileCache);
@ -3020,8 +3032,11 @@ namespace CodeWalker.Rendering
rndbl = TryGetRenderable(arche, drawable); rndbl = TryGetRenderable(arche, drawable);
} }
if (rndbl != null) if (rndbl == null || !rndbl.IsLoaded)
{ {
return false;
}
if (animClip != null) if (animClip != null)
{ {
rndbl.ClipMapEntry = animClip; rndbl.ClipMapEntry = animClip;
@ -3041,14 +3056,13 @@ namespace CodeWalker.Rendering
if ((frag != null) && (frag.DrawableCloth != null)) //cloth... if ((frag != null) && (frag.DrawableCloth != null)) //cloth...
{ {
rndbl = TryGetRenderable(arche, frag.DrawableCloth); rndbl = TryGetRenderable(arche, frag.DrawableCloth);
if (rndbl != null) if (rndbl != null && rndbl.IsLoaded)
{ {
bool res2 = RenderRenderable(rndbl, arche, entity); bool res2 = RenderRenderable(rndbl, arche, entity);
res = res || res2; res = res || res2;
} }
} }
} }
}
return res; return res;
@ -3062,7 +3076,7 @@ namespace CodeWalker.Rendering
return false; return false;
Renderable rndbl = TryGetRenderable(arche, drawable, txdHash, txdExtra, diffOverride); Renderable rndbl = TryGetRenderable(arche, drawable, txdHash, txdExtra, diffOverride);
if (rndbl == null) if (rndbl == null || !rndbl.IsLoaded)
return false; return false;
if (animClip != null) if (animClip != null)
@ -3243,7 +3257,7 @@ namespace CodeWalker.Rendering
rginst.Inst.CastShadow = castshadow; rginst.Inst.CastShadow = castshadow;
RenderableModel[] models = isselected ? rndbl.AllModels : rndbl.HDModels; RenderableModel[] models = isselected ? rndbl.HDModels : rndbl.GetModels(distance);
for (int mi = 0; mi < models.Length; mi++) for (int mi = 0; mi < models.Length; mi++)
{ {
@ -3568,17 +3582,23 @@ namespace CodeWalker.Rendering
MetaHash ahash = arche.Hash; MetaHash ahash = arche.Hash;
if (ycd.ClipMap.TryGetValue(ahash, out rndbl.ClipMapEntry)) rndbl.HasAnims = true; if (ycd.ClipMap.TryGetValue(ahash, out rndbl.ClipMapEntry)) rndbl.HasAnims = true;
foreach (var model in rndbl.HDModels) var models = rndbl.HDModels;
for (int i = 0; i < models.Length; i++)
{ {
if (model == null) continue; var model = models[i];
foreach (var geom in model.Geometries) if (models[i] == null)
continue;
for (int j = 0; j < model.Geometries.Length; j++)
{ {
if (geom == null) continue; var geom = model.Geometries[j];
if (geom == null)
continue;
if (geom.globalAnimUVEnable) if (geom.globalAnimUVEnable)
{ {
uint cmeindex = geom.DrawableGeom.ShaderID + 1u; uint cmeindex = geom.DrawableGeom.ShaderID + 1u;
MetaHash cmehash = ahash + cmeindex; //this goes to at least uv5! (from uv0) - see hw1_09.ycd MetaHash cmehash = ahash + cmeindex; //this goes to at least uv5! (from uv0) - see hw1_09.ycd
if (ycd.ClipMap.TryGetValue(cmehash, out geom.ClipMapEntryUV)) rndbl.HasAnims = true; if (ycd.ClipMap.TryGetValue(cmehash, out geom.ClipMapEntryUV))
rndbl.HasAnims = true;
} }
} }
} }
@ -3587,7 +3607,8 @@ namespace CodeWalker.Rendering
var extraTexDict = (drawable.Owner as YptFile)?.PtfxList?.TextureDictionary; var extraTexDict = (drawable.Owner as YptFile)?.PtfxList?.TextureDictionary;
if (extraTexDict == null) extraTexDict = txdExtra; if (extraTexDict == null)
extraTexDict = txdExtra;
bool cacheSD = (rndbl.SDtxds == null); bool cacheSD = (rndbl.SDtxds == null);
bool cacheHD = (renderhdtextures && (rndbl.HDtxds == null)); bool cacheHD = (renderhdtextures && (rndbl.HDtxds == null));

View File

@ -851,10 +851,11 @@ namespace CodeWalker.Rendering
if (batch.Renderable.HDModels.Length > 1) if (batch.Renderable.HDModels.Length > 1)
{ } { }
foreach (var model in batch.Renderable.HDModels) for(int i = 0; i < batch.Renderable.HDModels.Length; i++)
{ {
if (model.Geometries.Length > 1) var model = batch.Renderable.HDModels[i];
{ } if (model.Geometries.Length == 0)
continue;
Basic.SetModelVars(context, model); Basic.SetModelVars(context, model);
foreach (var geom in model.Geometries) foreach (var geom in model.Geometries)
@ -866,6 +867,7 @@ namespace CodeWalker.Rendering
} }
} }
} }
} }

View File

@ -440,7 +440,7 @@ namespace CodeWalker.Tools
curfile++; curfile++;
if (fentry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)) if (fentry.IsExtension(".rpf"))
{ continue; } { continue; }
if (onlyexts != null) if (onlyexts != null)

View File

@ -1,4 +1,6 @@
namespace CodeWalker.Tools using CodeWalker.Forms;
namespace CodeWalker.Tools
{ {
partial class BrowseForm partial class BrowseForm
{ {
@ -78,7 +80,7 @@
this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar(); this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar();
this.label4 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label();
this.SelTextureNameTextBox = new System.Windows.Forms.TextBox(); this.SelTextureNameTextBox = new System.Windows.Forms.TextBox();
this.SelTexturePictureBox = new System.Windows.Forms.PictureBox(); this.SelTexturePictureBox = new PixelBox();
this.MainStatusStrip = new System.Windows.Forms.StatusStrip(); this.MainStatusStrip = new System.Windows.Forms.StatusStrip();
this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.TestAllButton = new System.Windows.Forms.Button(); this.TestAllButton = new System.Windows.Forms.Button();

View File

@ -218,7 +218,7 @@ namespace CodeWalker.Tools
{ {
if (entry is RpfFileEntry) if (entry is RpfFileEntry)
{ {
bool show = !entry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase); //rpf entries get their own root node.. bool show = !entry.IsExtension(".rpf"); //rpf entries get their own root node..
if (show) if (show)
{ {
//string text = entry.Path.Substring(file.Path.Length + 1); //includes \ on the end //string text = entry.Path.Substring(file.Path.Length + 1); //includes \ on the end
@ -234,14 +234,14 @@ namespace CodeWalker.Tools
JenkIndex.Ensure(file.Name); JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
if (string.IsNullOrEmpty(entry.Name)) continue; if (string.IsNullOrEmpty(entry.Name))
JenkIndex.Ensure(entry.Name); continue;
JenkIndex.Ensure(entry.NameLower);
int ind = entry.Name.LastIndexOf('.'); JenkIndex.EnsureBoth(entry.Name);
if (ind > 0) var shortName = entry.ShortName;
if (shortName != entry.Name)
{ {
JenkIndex.Ensure(entry.Name.Substring(0, ind)); JenkIndex.EnsureBoth(shortName);
JenkIndex.Ensure(entry.NameLower.Substring(0, ind));
} }
} }
@ -368,43 +368,43 @@ namespace CodeWalker.Tools
bool istexdict = false; bool istexdict = false;
if (rfe.NameLower.EndsWith(".ymap")) if (rfe.IsExtension(".ymap"))
{ {
YmapFile ymap = new YmapFile(rfe); YmapFile ymap = new YmapFile(rfe);
ymap.Load(data, rfe); ymap.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ymap; DetailsPropertyGrid.SelectedObject = ymap;
} }
else if (rfe.NameLower.EndsWith(".ytyp")) else if (rfe.IsExtension(".ytyp"))
{ {
YtypFile ytyp = new YtypFile(); YtypFile ytyp = new YtypFile();
ytyp.Load(data, rfe); ytyp.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ytyp; DetailsPropertyGrid.SelectedObject = ytyp;
} }
else if (rfe.NameLower.EndsWith(".ymf")) else if (rfe.IsExtension(".ymf"))
{ {
YmfFile ymf = new YmfFile(); YmfFile ymf = new YmfFile();
ymf.Load(data, rfe); ymf.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ymf; DetailsPropertyGrid.SelectedObject = ymf;
} }
else if (rfe.NameLower.EndsWith(".ymt")) else if (rfe.IsExtension(".ymt"))
{ {
YmtFile ymt = new YmtFile(); YmtFile ymt = new YmtFile();
ymt.Load(data, rfe); ymt.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ymt; DetailsPropertyGrid.SelectedObject = ymt;
} }
else if (rfe.NameLower.EndsWith(".ybn")) else if (rfe.IsExtension(".ybn"))
{ {
YbnFile ybn = new YbnFile(); YbnFile ybn = new YbnFile();
ybn.Load(data, rfe); ybn.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ybn; DetailsPropertyGrid.SelectedObject = ybn;
} }
else if (rfe.NameLower.EndsWith(".fxc")) else if (rfe.IsExtension(".fxc"))
{ {
FxcFile fxc = new FxcFile(); FxcFile fxc = new FxcFile();
fxc.Load(data, rfe); fxc.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = fxc; DetailsPropertyGrid.SelectedObject = fxc;
} }
else if (rfe.NameLower.EndsWith(".yft")) else if (rfe.IsExtension(".yft"))
{ {
YftFile yft = new YftFile(); YftFile yft = new YftFile();
yft.Load(data, rfe); yft.Load(data, rfe);
@ -416,7 +416,7 @@ namespace CodeWalker.Tools
istexdict = true; istexdict = true;
} }
} }
else if (rfe.NameLower.EndsWith(".ydr")) else if (rfe.IsExtension(".ydr"))
{ {
YdrFile ydr = new YdrFile(); YdrFile ydr = new YdrFile();
ydr.Load(data, rfe); ydr.Load(data, rfe);
@ -428,14 +428,14 @@ namespace CodeWalker.Tools
istexdict = true; istexdict = true;
} }
} }
else if (rfe.NameLower.EndsWith(".ydd")) else if (rfe.IsExtension(".ydd"))
{ {
YddFile ydd = new YddFile(); YddFile ydd = new YddFile();
ydd.Load(data, rfe); ydd.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ydd; DetailsPropertyGrid.SelectedObject = ydd;
//todo: show embedded texdicts in ydd's? is this possible? //todo: show embedded texdicts in ydd's? is this possible?
} }
else if (rfe.NameLower.EndsWith(".ytd")) else if (rfe.IsExtension(".ytd"))
{ {
YtdFile ytd = new YtdFile(); YtdFile ytd = new YtdFile();
ytd.Load(data, rfe); ytd.Load(data, rfe);
@ -443,43 +443,43 @@ namespace CodeWalker.Tools
ShowTextures(ytd.TextureDict); ShowTextures(ytd.TextureDict);
istexdict = true; istexdict = true;
} }
else if (rfe.NameLower.EndsWith(".ycd")) else if (rfe.IsExtension(".ycd"))
{ {
YcdFile ycd = new YcdFile(); YcdFile ycd = new YcdFile();
ycd.Load(data, rfe); ycd.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ycd; DetailsPropertyGrid.SelectedObject = ycd;
} }
else if (rfe.NameLower.EndsWith(".ynd")) else if (rfe.IsExtension(".ynd"))
{ {
YndFile ynd = new YndFile(); YndFile ynd = new YndFile();
ynd.Load(data, rfe); ynd.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ynd; DetailsPropertyGrid.SelectedObject = ynd;
} }
else if (rfe.NameLower.EndsWith(".ynv")) else if (rfe.IsExtension(".ynv"))
{ {
YnvFile ynv = new YnvFile(); YnvFile ynv = new YnvFile();
ynv.Load(data, rfe); ynv.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = ynv; DetailsPropertyGrid.SelectedObject = ynv;
} }
else if (rfe.NameLower.EndsWith("_cache_y.dat")) else if (rfe.Name.EndsWith("_cache_y.dat", StringComparison.OrdinalIgnoreCase))
{ {
CacheDatFile cdf = new CacheDatFile(); CacheDatFile cdf = new CacheDatFile();
cdf.Load(data, rfe); cdf.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = cdf; DetailsPropertyGrid.SelectedObject = cdf;
} }
else if (rfe.NameLower.EndsWith(".rel")) else if (rfe.IsExtension(".rel"))
{ {
RelFile rel = new RelFile(rfe); RelFile rel = new RelFile(rfe);
rel.Load(data, rfe); rel.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = rel; DetailsPropertyGrid.SelectedObject = rel;
} }
else if (rfe.NameLower.EndsWith(".gxt2")) else if (rfe.IsExtension(".gxt2"))
{ {
Gxt2File gxt2 = new Gxt2File(); Gxt2File gxt2 = new Gxt2File();
gxt2.Load(data, rfe); gxt2.Load(data, rfe);
DetailsPropertyGrid.SelectedObject = gxt2; DetailsPropertyGrid.SelectedObject = gxt2;
} }
else if (rfe.NameLower.EndsWith(".pso")) else if (rfe.IsExtension(".pso"))
{ {
JPsoFile pso = new JPsoFile(); JPsoFile pso = new JPsoFile();
pso.Load(data, rfe); pso.Load(data, rfe);
@ -797,14 +797,14 @@ namespace CodeWalker.Tools
int max = 500; int max = 500;
foreach (RpfFile file in ScannedFiles) foreach (RpfFile file in ScannedFiles)
{ {
if (file.NameLower.Contains(find)) if (file.Name.Contains(find, StringComparison.OrdinalIgnoreCase))
{ {
AddFileNode(file, null); AddFileNode(file, null);
count++; count++;
} }
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
if (entry.NameLower.Contains(find)) if (entry.Name.Contains(find, StringComparison.OrdinalIgnoreCase))
{ {
if (entry is RpfDirectoryEntry direntry) if (entry is RpfDirectoryEntry direntry)
{ {
@ -819,7 +819,8 @@ namespace CodeWalker.Tools
} }
else if (entry is RpfBinaryFileEntry) else if (entry is RpfBinaryFileEntry)
{ {
if (entry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)) continue; if (entry.IsExtension(".rpf"))
continue;
AddEntryNode(entry, null); AddEntryNode(entry, null);
count++; count++;
} }
@ -1079,7 +1080,7 @@ namespace CodeWalker.Tools
curfile++; curfile++;
if (fentry.NameLower.EndsWith(".rpf")) if (fentry.IsExtension(".rpf"))
{ continue; } { continue; }
if (ignoreexts != null) if (ignoreexts != null)
@ -1087,7 +1088,7 @@ namespace CodeWalker.Tools
bool ignore = false; bool ignore = false;
for (int i = 0; i < ignoreexts.Length; i++) for (int i = 0; i < ignoreexts.Length; i++)
{ {
if (fentry.NameLower.EndsWith(ignoreexts[i])) if (fentry.Name.EndsWith(ignoreexts[i], StringComparison.OrdinalIgnoreCase))
{ {
ignore = true; ignore = true;
break; break;

View File

@ -142,11 +142,11 @@ namespace CodeWalker.Tools
bool extract = false; bool extract = false;
if (endswith) if (endswith)
{ {
extract = entry.NameLower.EndsWith(matchstr); extract = entry.Name.EndsWith(matchstr, StringComparison.OrdinalIgnoreCase);
} }
else else
{ {
extract = entry.NameLower.Contains(matchstr); extract = entry.Name.Contains(matchstr, StringComparison.OrdinalIgnoreCase);
} }
var fentry = entry as RpfFileEntry; var fentry = entry as RpfFileEntry;
if (fentry == null) if (fentry == null)

View File

@ -130,7 +130,7 @@ namespace CodeWalker.Tools
} }
try try
{ {
if (entry.NameLower.EndsWith(".fxc")) if (entry.IsExtension(".fxc"))
{ {
UpdateExtractStatus(entry.Path); UpdateExtractStatus(entry.Path);
FxcFile fxc = rpfman.GetFile<FxcFile>(entry); FxcFile fxc = rpfman.GetFile<FxcFile>(entry);

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