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>
<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="Microsoft.Bcl.HashCode" Version="1.1.1" />
<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.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<ItemGroup>

View File

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

View File

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

View File

@ -1186,7 +1186,7 @@ namespace CodeWalker.GameFiles
SlotGS = br.ReadUInt16(); //6, 5
SlotHS = br.ReadUInt16(); //6, 5
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)
{

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml.Linq;
namespace CodeWalker.GameFiles
@ -34,7 +35,7 @@ namespace CodeWalker.GameFiles
//can be PSO .ymt or XML .meta
MemoryStream ms = new MemoryStream(data);
using MemoryStream ms = new MemoryStream(data);
if (PsoFile.IsPSO(ms))
{
Pso = new PsoFile();
@ -46,26 +47,28 @@ namespace CodeWalker.GameFiles
Xml = TextUtil.GetUTF8Text(data);
}
XmlDocument xdoc = new XmlDocument();
if (!string.IsNullOrEmpty(Xml))
{
try
{
xdoc.LoadXml(Xml);
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
else
{ }
using var textReader = new StringReader(Xml);
//XmlDocument xdoc = new XmlDocument();
//if (!string.IsNullOrEmpty(Xml))
//{
// try
// {
// xdoc.LoadXml(Xml);
// }
// catch (Exception ex)
// {
// var msg = ex.Message;
// }
//}
using var xmlReader = XmlReader.Create(textReader);
if (xdoc.DocumentElement != null)
{
InitDataList = new CPedModelInfo__InitDataList(xdoc.DocumentElement);
}
//if (xdoc.DocumentElement != null)
//{
InitDataList = new CPedModelInfo__InitDataList(xmlReader);
//}
@ -84,6 +87,87 @@ namespace CodeWalker.GameFiles
public CTxdRelationship[] txdRelationships { get; set; }
public CMultiTxdRelationship[] multiTxdRelationships { get; set; }
public CPedModelInfo__InitDataList(XmlReader reader)
{
while (reader.Read())
{
reader.MoveToContent();
//var _ = xmlReader.Name switch
//{
// "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
// "InitDatas" => LoadInitDatas(xmlReader),
// "txdRelationships" => LoadTxdRelationships(xmlReader),
// _ => throw new Exception()
//};
switch (reader.Name)
{
case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
residentTxd = Xml.GetChildInnerText(reader, "residentTxd");
break;
case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
if (reader.IsEmptyElement)
{
reader.ReadStartElement();
break;
}
reader.ReadStartElement();
var initDatasList = new List<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)
{
XmlNodeList items;
@ -126,7 +210,6 @@ namespace CodeWalker.GameFiles
multiTxdRelationships[i] = new CMultiTxdRelationship(items[i]);
}
}
}
}
@ -204,6 +287,249 @@ namespace CodeWalker.GameFiles
public float DefaultRemoveRangeMultiplier { get; set; }
public bool AllowCloseSpawning { get; set; }
public CPedModelInfo__InitData(XmlReader reader)
{
reader.ReadStartElement("Item");
while (reader.Name != "Item" && reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Item")
{
reader.ReadEndElement();
return;
}
while (reader.MoveToContent() != XmlNodeType.Element && reader.Read())
{
}
switch (reader.Name)
{
case "Name":
Name = Xml.GetChildInnerText(reader, "Name");
break;
case "PropsName":
PropsName = Xml.GetChildInnerText(reader, "PropsName");
break;
case "ClipDictionaryName":
ClipDictionaryName = Xml.GetChildInnerText(reader, "ClipDictionaryName");
break;
case "BlendShapeFileName":
BlendShapeFileName = Xml.GetChildInnerText(reader, "BlendShapeFileName");
break;
case "ExpressionSetName":
ExpressionSetName = Xml.GetChildInnerText(reader, "ExpressionSetName");
break;
case "ExpressionDictionaryName":
ExpressionDictionaryName = Xml.GetChildInnerText(reader, "ExpressionDictionaryName");
break;
case "ExpressionName":
ExpressionName = Xml.GetChildInnerText(reader, "ExpressionName");
break;
case "Pedtype":
Pedtype = Xml.GetChildInnerText(reader, "Pedtype");
break;
case "MovementClipSet":
MovementClipSet = Xml.GetChildInnerText(reader, "MovementClipSet");
break;
case "MovementClipSets":
var clipSetsList = new List<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)
{
@ -300,6 +626,25 @@ namespace CodeWalker.GameFiles
public string parent { get; set; }
public string child { get; set; }
public CTxdRelationship(XmlReader reader)
{
if (reader.Name == "parent")
{
parent = Xml.GetChildInnerText(reader, "parent");
} else if (reader.Name == "child")
{
child = Xml.GetChildInnerText(reader, "child");
}
if (reader.Name == "parent")
{
parent = Xml.GetChildInnerText(reader, "parent");
}
else if (reader.Name == "child")
{
child = Xml.GetChildInnerText(reader, "child");
}
}
public CTxdRelationship(XmlNode node)
{
parent = Xml.GetChildInnerText(node, "parent");
@ -331,6 +676,34 @@ namespace CodeWalker.GameFiles
}
}
public CMultiTxdRelationship(XmlReader reader)
{
reader.ReadStartElement("Item");
while (reader.MoveToContent() == XmlNodeType.Element && reader.Name != "Item")
{
switch (reader.Name)
{
case "children":
var childrenList = new List<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()
{
return parent + ": " + (children?.Length ?? 0).ToString() + " children";

View File

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

View File

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

View File

@ -1,10 +1,15 @@
using SharpDX;
using CodeWalker.World;
using SharpDX;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles
{
@ -28,14 +33,14 @@ namespace CodeWalker.GameFiles
public void Load(byte[] data, RpfFileEntry entry)
public void LoadOld(byte[] data, RpfFileEntry entry)
{
RpfFileEntry = entry;
Name = entry.Name;
FilePath = Name;
if (entry.NameLower.EndsWith(".meta"))
if (entry.IsExtension(".meta"))
{
string xml = TextUtil.GetUTF8Text(data);
@ -53,12 +58,64 @@ namespace CodeWalker.GameFiles
}
}
public void Load(byte[] data, RpfFileEntry entry)
{
RpfFileEntry = entry;
Name = entry.Name;
FilePath = Name;
if (entry.IsExtension(".meta"))
{
using var textReader = new StreamReader(new MemoryStream(data), Encoding.UTF8);
using var xmlReader = XmlReader.Create(textReader);
while (xmlReader.Read())
{
xmlReader.MoveToContent();
//var _ = xmlReader.Name switch
//{
// "residentTxd" => ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd"),
// "InitDatas" => LoadInitDatas(xmlReader),
// "txdRelationships" => LoadTxdRelationships(xmlReader),
// _ => throw new Exception()
//};
switch (xmlReader.Name)
{
case string Name when Name.Equals("residentTxd", StringComparison.OrdinalIgnoreCase):
ResidentTxd = Xml.GetChildInnerText(xmlReader, "residentTxd");
break;
case string Name when Name.Equals("InitDatas", StringComparison.OrdinalIgnoreCase):
LoadInitDatas(xmlReader);
break;
case string Name when Name.Equals("txdRelationships", StringComparison.OrdinalIgnoreCase):
LoadTxdRelationships(xmlReader);
break;
default:
break;
}
}
//ResidentTxd = Xml.GetChildInnerText(xmldoc.SelectSingleNode("CVehicleModelInfo__InitDataList"), "residentTxd");
//LoadInitDatas(xmldoc);
//LoadTxdRelationships(xmldoc);
Loaded = true;
}
}
private void LoadInitDatas(XmlDocument xmldoc)
{
XmlNodeList items = xmldoc.SelectNodes("CVehicleModelInfo__InitDataList/InitDatas/Item | CVehicleModelInfo__InitDataList/InitDatas/item");
InitDatas = new List<VehicleInitData>();
InitDatas = new List<VehicleInitData>(items.Count);
for (int i = 0; i < items.Count; i++)
{
var node = items[i];
@ -68,6 +125,53 @@ namespace CodeWalker.GameFiles
}
}
private void LoadInitDatas(XmlReader reader)
{
if (!reader.IsStartElement() || reader.Name != "InitDatas")
{
throw new InvalidOperationException("XmlReader is not at start element of \"InitDatas\"");
}
InitDatas = new List<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)
{
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 animConvRoofDictName { get; set; } //<animConvRoofDictName>null</animConvRoofDictName>
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 audioNameHash { get; set; } //<audioNameHash />
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 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)
{
@ -197,7 +647,7 @@ namespace CodeWalker.GameFiles
expressionName = Xml.GetChildInnerText(node, "expressionName");
animConvRoofDictName = Xml.GetChildInnerText(node, "animConvRoofDictName");
animConvRoofName = Xml.GetChildInnerText(node, "animConvRoofName");
animConvRoofWindowsAffected = Xml.GetChildInnerText(node, "animConvRoofWindowsAffected");//?
animConvRoofWindowsAffected = GetStringItemArray(node, "animConvRoofWindowsAffected");//?
ptfxAssetName = Xml.GetChildInnerText(node, "ptfxAssetName");
audioNameHash = Xml.GetChildInnerText(node, "audioNameHash");
layout = Xml.GetChildInnerText(node, "layout");
@ -327,11 +777,41 @@ namespace CodeWalker.GameFiles
if (getStringArrayList.Count == 0) return null;
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);
if (ldarr == null) return null;
lock(getStringArrayList)
{
getStringArrayList.Clear();
foreach (var ldstr in ldarr)
{
@ -344,12 +824,26 @@ namespace CodeWalker.GameFiles
if (getStringArrayList.Count == 0) return null;
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);
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);
if (ldarr == null) return null;
getFloatArrayList.Clear();
var floats = stackalloc float[ldarr.Length];
var i = 0;
foreach (var ldstr in ldarr)
{
var ldt = ldstr?.Trim();
@ -358,12 +852,30 @@ namespace CodeWalker.GameFiles
float f;
if (FloatUtil.TryParse(ldt, out f))
{
getFloatArrayList.Add(f);
floats[i] = f;
i++;
}
}
}
if (getFloatArrayList.Count == 0) return null;
return getFloatArrayList.ToArray();
if (i == 0) return null;
var result = new float[i];
Marshal.Copy((IntPtr)floats, result, 0, i);
return result;
}
private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(node, childName);
return GetFloatArray(ldastr, delimiter);
}
private float[] GetFloatArray(XmlReader reader, string childName, char delimiter)
{
var ldastr = Xml.GetChildInnerText(reader, childName);
return GetFloatArray(ldastr, delimiter);
}
private static List<string> getStringArrayList = new List<string>(); //kinda hacky..
@ -374,28 +886,283 @@ namespace CodeWalker.GameFiles
{
return modelName;
}
public override bool Equals(object obj)
{
return obj is VehicleInitData data &&
modelName == data.modelName &&
txdName == data.txdName &&
handlingId == data.handlingId &&
gameName == data.gameName &&
vehicleMakeName == data.vehicleMakeName &&
expressionDictName == data.expressionDictName &&
expressionName == data.expressionName &&
animConvRoofDictName == data.animConvRoofDictName &&
animConvRoofName == data.animConvRoofName &&
animConvRoofWindowsAffected == data.animConvRoofWindowsAffected &&
ptfxAssetName == data.ptfxAssetName &&
audioNameHash == data.audioNameHash &&
layout == data.layout &&
coverBoundOffsets == data.coverBoundOffsets &&
explosionInfo == data.explosionInfo &&
scenarioLayout == data.scenarioLayout &&
cameraName == data.cameraName &&
aimCameraName == data.aimCameraName &&
bonnetCameraName == data.bonnetCameraName &&
povCameraName == data.povCameraName &&
FirstPersonDriveByIKOffset.Equals(data.FirstPersonDriveByIKOffset) &&
FirstPersonDriveByUnarmedIKOffset.Equals(data.FirstPersonDriveByUnarmedIKOffset) &&
FirstPersonProjectileDriveByIKOffset.Equals(data.FirstPersonProjectileDriveByIKOffset) &&
FirstPersonProjectileDriveByPassengerIKOffset.Equals(data.FirstPersonProjectileDriveByPassengerIKOffset) &&
FirstPersonDriveByRightPassengerIKOffset.Equals(data.FirstPersonDriveByRightPassengerIKOffset) &&
FirstPersonDriveByRightPassengerUnarmedIKOffset.Equals(data.FirstPersonDriveByRightPassengerUnarmedIKOffset) &&
FirstPersonMobilePhoneOffset.Equals(data.FirstPersonMobilePhoneOffset) &&
FirstPersonPassengerMobilePhoneOffset.Equals(data.FirstPersonPassengerMobilePhoneOffset) &&
PovCameraOffset.Equals(data.PovCameraOffset) &&
PovCameraVerticalAdjustmentForRollCage.Equals(data.PovCameraVerticalAdjustmentForRollCage) &&
PovPassengerCameraOffset.Equals(data.PovPassengerCameraOffset) &&
PovRearPassengerCameraOffset.Equals(data.PovRearPassengerCameraOffset) &&
vfxInfoName == data.vfxInfoName &&
shouldUseCinematicViewMode == data.shouldUseCinematicViewMode &&
shouldCameraTransitionOnClimbUpDown == data.shouldCameraTransitionOnClimbUpDown &&
shouldCameraIgnoreExiting == data.shouldCameraIgnoreExiting &&
AllowPretendOccupants == data.AllowPretendOccupants &&
AllowJoyriding == data.AllowJoyriding &&
AllowSundayDriving == data.AllowSundayDriving &&
AllowBodyColorMapping == data.AllowBodyColorMapping &&
wheelScale == data.wheelScale &&
wheelScaleRear == data.wheelScaleRear &&
dirtLevelMin == data.dirtLevelMin &&
dirtLevelMax == data.dirtLevelMax &&
envEffScaleMin == data.envEffScaleMin &&
envEffScaleMax == data.envEffScaleMax &&
envEffScaleMin2 == data.envEffScaleMin2 &&
envEffScaleMax2 == data.envEffScaleMax2 &&
damageMapScale == data.damageMapScale &&
damageOffsetScale == data.damageOffsetScale &&
diffuseTint.Equals(data.diffuseTint) &&
steerWheelMult == data.steerWheelMult &&
HDTextureDist == data.HDTextureDist &&
StructuralComparisons.StructuralEqualityComparer.Equals(lodDistances, data.lodDistances) &&
minSeatHeight == data.minSeatHeight &&
identicalModelSpawnDistance == data.identicalModelSpawnDistance &&
maxNumOfSameColor == data.maxNumOfSameColor &&
defaultBodyHealth == data.defaultBodyHealth &&
pretendOccupantsScale == data.pretendOccupantsScale &&
visibleSpawnDistScale == data.visibleSpawnDistScale &&
trackerPathWidth == data.trackerPathWidth &&
weaponForceMult == data.weaponForceMult &&
frequency == data.frequency &&
swankness == data.swankness &&
maxNum == data.maxNum &&
StructuralComparisons.StructuralEqualityComparer.Equals(flags, data.flags) &&
type == data.type &&
plateType == data.plateType &&
dashboardType == data.dashboardType &&
vehicleClass == data.vehicleClass &&
wheelType == data.wheelType &&
StructuralComparisons.StructuralEqualityComparer.Equals(trailers, data.trailers) &&
StructuralComparisons.StructuralEqualityComparer.Equals(additionalTrailers, data.additionalTrailers) &&
StructuralComparisons.StructuralEqualityComparer.Equals(drivers, data.drivers) &&
StructuralComparisons.StructuralEqualityComparer.Equals(extraIncludes, data.extraIncludes) &&
StructuralComparisons.StructuralEqualityComparer.Equals(doorsWithCollisionWhenClosed, data.doorsWithCollisionWhenClosed) &&
StructuralComparisons.StructuralEqualityComparer.Equals(driveableDoors, data.driveableDoors) &&
bumpersNeedToCollideWithMap == data.bumpersNeedToCollideWithMap &&
needsRopeTexture == data.needsRopeTexture &&
StructuralComparisons.StructuralEqualityComparer.Equals(requiredExtras, data.requiredExtras) &&
StructuralComparisons.StructuralEqualityComparer.Equals(rewards, data.rewards) &&
StructuralComparisons.StructuralEqualityComparer.Equals(cinematicPartCamera, data.cinematicPartCamera) &&
NmBraceOverrideSet == data.NmBraceOverrideSet &&
buoyancySphereOffset.Equals(data.buoyancySphereOffset) &&
buoyancySphereSizeScale == data.buoyancySphereSizeScale &&
EqualityComparer<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 MaxComponent { get; set; }
public float ThresholdMult { get; set; }
public override bool Equals(object obj)
{
return obj is VehicleOverrideRagdollThreshold threshold && Equals(threshold);
}
public bool Equals(VehicleOverrideRagdollThreshold other)
{
return MinComponent == other.MinComponent &&
MaxComponent == other.MaxComponent &&
ThresholdMult == other.ThresholdMult;
}
public override int GetHashCode()
{
int hashCode = 1172526364;
hashCode = hashCode * -1521134295 + MinComponent.GetHashCode();
hashCode = hashCode * -1521134295 + MaxComponent.GetHashCode();
hashCode = hashCode * -1521134295 + ThresholdMult.GetHashCode();
return hashCode;
}
public override string ToString()
{
return MinComponent.ToString() + ", " + MaxComponent.ToString() + ", " + ThresholdMult.ToString();
}
public static bool operator ==(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
{
return left.Equals(right);
}
public 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 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()
{
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 hash = hashes[i];
drawable.Hash = hash;
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
if (drawable.Name == null || drawable.Name.EndsWith("#dd", StringComparison.OrdinalIgnoreCase))
{
string hstr = JenkIndex.TryGetString(hash);
if (!string.IsNullOrEmpty(hstr))
{
drawable.Name = hstr;
}
else
{ }
}
}
Drawables = Dict.Values.ToArray();
}
Loaded = true;

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -393,10 +393,10 @@ namespace CodeWalker.GameFiles
{
foreach (var portal in portals)
{
List<uint> newAttachedObjects = new List<uint>();
if (portal.AttachedObjects == null)
if (portal.AttachedObjects == null || portal.AttachedObjects.Length == 0)
continue;
List<uint> newAttachedObjects = new List<uint>();
foreach (var objIndex in portal.AttachedObjects)
{
if (objIndex == deletedIndex) continue;
@ -411,9 +411,10 @@ namespace CodeWalker.GameFiles
{
foreach (var room in rooms)
{
List<uint> newAttachedObjects = new List<uint>();
if (room.AttachedObjects == null)
if (room.AttachedObjects == null || room.AttachedObjects.Length == 0)
continue;
List<uint> newAttachedObjects = new List<uint>();
foreach (var objIndex in room.AttachedObjects)
{
if (objIndex == deletedIndex) continue;
@ -532,10 +533,12 @@ namespace CodeWalker.GameFiles
}
public MCMloRoomDef GetEntityRoom(MCEntityDef ent)
{
if (rooms == null) return null;
if (rooms == null)
return null;
int objectIndex = GetEntityObjectIndex(ent);
if (objectIndex < 0) return null;
if (objectIndex < 0)
return null;
for (int i = 0; i < rooms.Length; i++)
{
@ -684,10 +687,15 @@ namespace CodeWalker.GameFiles
{
var ient = Entities[j];
var iarch = gfc.GetArchetype(ient._CEntityDef.archetypeName);
ient.SetArchetype(iarch);
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);
@ -708,7 +716,13 @@ namespace CodeWalker.GameFiles
ient.SetArchetype(iarch);
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++)
{
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 max = new Vector3(float.MinValue);
for (int k = 0; k < room.AttachedObjects.Length; k++)

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System;using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
@ -675,6 +674,35 @@ namespace CodeWalker.GameFiles
{
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
{
@ -717,6 +745,37 @@ namespace CodeWalker.GameFiles
{
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
{
@ -757,6 +816,37 @@ namespace CodeWalker.GameFiles
{
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
{
@ -797,6 +887,37 @@ namespace CodeWalker.GameFiles
{
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
{
@ -836,6 +957,35 @@ namespace CodeWalker.GameFiles
{
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
{
@ -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; }
@ -1180,7 +1330,7 @@ namespace CodeWalker.GameFiles
return new MetaHash(v);
}
public override bool Equals(object obj)
public override readonly bool Equals(object obj)
{
if (obj == null) return false;
if (obj is not MetaHash metaHash) return false;
@ -1188,7 +1338,14 @@ namespace CodeWalker.GameFiles
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;
}

View File

@ -153,19 +153,22 @@ namespace CodeWalker.GameFiles
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...
return new Array_Vector3(ptr);
}
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);
return new Array_uint(ptr);
}
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);
return new Array_uint(ptr);
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -10,7 +11,7 @@ namespace CodeWalker.GameFiles
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)
{
if (stringCache.TryGetValue(h, out str))
@ -21,10 +22,10 @@ namespace CodeWalker.GameFiles
{
str = ((MetaName)h).ToString();
if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry
stringCache.Add(h, str);
stringCache.TryAdd(h, str);
return true;
}
stringCache.Add(h, str);
stringCache.TryAdd(h, str);
str = null;
return false;
}

View File

@ -1438,26 +1438,39 @@ namespace CodeWalker.GameFiles
public static byte[] ConvertToBytes<T>(T item) where T : struct
{
int size = Marshal.SizeOf(typeof(T));
int offset = 0;
//int offset = 0;
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(item, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
offset += size;
MemoryMarshal.TryWrite(arr.AsSpan(), ref item);
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
{
if (items == null) return null;
var size = Marshal.SizeOf(typeof(T)) * items.Length;
var b = new byte[size];
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(h, b, 0, size);
handle.Free();
return b;
return MemoryMarshal.AsBytes(items.AsSpan()).ToArray();
//var size = Marshal.SizeOf(typeof(T)) * items.Length;
//var b = new byte[size];
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//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));
@ -1502,20 +1515,33 @@ namespace CodeWalker.GameFiles
}
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))));
T[] items = new T[count];
int itemsize = 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] = ConvertData<T>(data, off);
//}
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, offset, h, itemsize * count);
handle.Free();
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(data, offset, h, itemsize * count);
//handle.Free();
return items;
//return items;
}
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..
ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi));
//for (int i = 0; i < itemcount; i++)
//{
// int offset = (itemoffset + i) * itemsize;
// int index = curi + i;
// items[index] = ConvertData<T>(ptrblock.Data, offset);
//}
//ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi));
for (int i = 0; i < itemcount; i++)
{
int offset = (itemoffset + i) * itemsize;
int index = curi + i;
items[index] = ConvertData<T>(ptrblock.Data, offset);
}
itemoffset = 0; //start at beginning of next block..
curi += itemcount;
itemsleft -= itemcount;
@ -2500,7 +2526,7 @@ namespace CodeWalker.GameFiles
{
_Data = data;
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)
@ -2521,14 +2547,7 @@ namespace CodeWalker.GameFiles
_Data.name = new CharPointer();
}
if (AttachedObjects != null)
{
_Data.attachedObjects = mb.AddUintArrayPtr(AttachedObjects);
}
else
{
_Data.attachedObjects = new Array_uint();
}
mb.AddStructureInfo(MetaName.CMloRoomDef);
return mb.AddItemPtr(MetaName.CMloRoomDef, _Data);
@ -4101,6 +4120,61 @@ namespace CodeWalker.GameFiles
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
@ -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_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 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
{
@ -4531,10 +4656,43 @@ namespace CodeWalker.GameFiles
public uint Unused2 { get; set; }//40
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()
{
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
{
@ -5032,6 +5190,45 @@ namespace CodeWalker.GameFiles
{
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
{
@ -5731,6 +5928,39 @@ namespace CodeWalker.GameFiles
{
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
{

View File

@ -1,4 +1,6 @@
using SharpDX;

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

View File

@ -337,16 +337,26 @@ namespace CodeWalker.GameFiles
var reader = new DataReader(stream, Endianess.BigEndian);
var identInt = reader.ReadUInt32();
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)
{
var reader = new DataReader(stream, Endianess.BigEndian);
var identInt = reader.ReadUInt32();
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)
{
JenkIndex.Ensure(str);
JenkIndex.Ensure(str.ToLowerInvariant());
JenkIndex.EnsureLower(str);
}
Strings = strs.ToArray();
}
public void Write(DataWriter writer)
{
var strStream = new MemoryStream();
var strWriter = new DataWriter(strStream, Endianess.BigEndian);
using var strStream = new MemoryStream();
using var strWriter = new DataWriter(strStream, Endianess.BigEndian);
foreach (var str in Strings)
{
strWriter.Write(str);
@ -926,7 +936,7 @@ namespace CodeWalker.GameFiles
foreach (var str in strs)
{
JenkIndex.Ensure(str);
JenkIndex.Ensure(str.ToLowerInvariant());
JenkIndex.EnsureLower(str);
}
Strings = strs.ToArray();
}

View File

@ -15819,46 +15819,52 @@ namespace CodeWalker.GameFiles
public static T ConvertDataRaw<T>(byte[] data) where T : struct
{
MemoryMarshal.TryRead<T>(data.AsSpan(), out T value);
return value;
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
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
{
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out var 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
{
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
value.SwapEnd();
return value;
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//r.SwapEnd();
//return r;
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
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
{
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);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(data, offset, h, itemsize * count);
//handle.Free();
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, offset, h, itemsize * count);
handle.Free();
//return items;
return items;
}

View File

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

View File

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

View File

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

View File

@ -1346,7 +1346,7 @@ namespace CodeWalker.GameFiles
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];
for (int i = 0; i < ItemDataCount; i++)
{
@ -1942,7 +1942,13 @@ namespace CodeWalker.GameFiles
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;
if (Windows != null)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
using Microsoft.IO;
using CodeWalker.Core.Utils;
using Microsoft.IO;
using System;
using System.Buffers;
using System.Buffers.Binary;
@ -20,24 +21,6 @@ namespace CodeWalker.GameFiles
public class RpfFile
{
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 FilePath { get; set; } //full file path of the RPF
public long FileSize { get; set; }
@ -80,19 +63,48 @@ namespace CodeWalker.GameFiles
public uint GrandTotalBinaryFileCount { 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
{
FileInfo fi = new FileInfo(fpath);
Name = fi.Name;
Path = relpath.ToLowerInvariant();
Path = relpath;
FilePath = fpath;
FileSize = fi.Length;
}
public RpfFile(string name, string path, long filesize) //for a child RPF
{
Name = name;
Path = path.ToLowerInvariant();
Path = path;
FilePath = path;
FileSize = filesize;
}
@ -104,7 +116,7 @@ namespace CodeWalker.GameFiles
string rel_parent_path = parentFile.Path;
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";
return null;
@ -132,7 +144,7 @@ namespace CodeWalker.GameFiles
public bool IsInModsFolder()
{
return GetTopParent().Path.StartsWith(@"mods\");
return GetTopParent().Path.StartsWith(@"mods\", StringComparison.OrdinalIgnoreCase);
}
public RpfFile GetTopParent()
@ -169,11 +181,13 @@ namespace CodeWalker.GameFiles
throw new Exception("Invalid Resource - not GTAV!");
}
byte[] entriesdata = ArrayPool<byte>.Shared.Rent((int)EntryCount * 16);
byte[] namesdata = ArrayPool<byte>.Shared.Rent((int)NamesLength);
var entriesLength = (int)EntryCount * 16;
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(namesdata, 0, (int)NamesLength);
br.BaseStream.Read(entriesdata, 0, entriesLength);
br.BaseStream.Read(namesdata, 0, namesLength);
switch (Encryption)
{
@ -181,22 +195,22 @@ namespace CodeWalker.GameFiles
case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC
break;
case RpfEncryption.AES:
GTACrypto.DecryptAES(entriesdata, (int)EntryCount * 16);
GTACrypto.DecryptAES(namesdata, (int)NamesLength);
GTACrypto.DecryptAES(entriesdata, entriesLength);
GTACrypto.DecryptAES(namesdata, namesLength);
IsAESEncrypted = true;
break;
case RpfEncryption.NG:
default:
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, (int)EntryCount * 16);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, (int)NamesLength);
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, entriesLength);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, namesLength);
IsNGEncrypted = true;
break;
}
using var entriesrdr = new DataReader(new MemoryStream(entriesdata, 0, (int)EntryCount * 16));
using var namesrdr = new DataReader(new MemoryStream(namesdata, 0, (int)NamesLength));
using var entriesrdr = new DataReader(new MemoryStream(entriesdata, 0, entriesLength));
using var namesrdr = new DataReader(new MemoryStream(namesdata, 0, namesLength));
AllEntries = new List<RpfEntry>((int)EntryCount);
@ -245,26 +259,19 @@ namespace CodeWalker.GameFiles
{
namesrdr.Position = entry.NameOffset;
entry.Name = namesrdr.ReadString(256);
if (entry.Name.Length > 256)
{
// long names can freeze the RPFExplorer
entry.Name = entry.Name.Substring(0, 256);
}
JenkIndex.EnsureLower(entry.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.Path = Path.ToLowerInvariant();// + "\\" + Root.Name;
var stack = new Stack<RpfDirectoryEntry>();
stack.Push(Root);
while (stack.Count > 0)
{
var item = stack.Pop();
Root.Path = Path;// + "\\" + Root.Name;
//var stack = new Stack<RpfDirectoryEntry>();
void addSubDirectory(RpfDirectoryEntry item)
{
int starti = (int)item.EntriesIndex;
int endi = (int)(item.EntriesIndex + item.EntriesCount);
@ -274,19 +281,46 @@ namespace CodeWalker.GameFiles
e.Parent = item;
if (e is RpfDirectoryEntry rde)
{
rde.Path = item.Path + "\\" + rde.Name;
rde.Path = item.Path + '\\' + rde.Name;
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);
}
}
}
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;
CurrentFileReader = null;
@ -294,10 +328,6 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(namesdata);
}
public bool ScanStructure(Action<string> updateStatus, Action<string> errorLog)
{
@ -343,7 +373,7 @@ namespace CodeWalker.GameFiles
if (entry is RpfBinaryFileEntry binentry)
{
//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);
@ -424,8 +454,7 @@ namespace CodeWalker.GameFiles
long l = binentry.GetFileSize();
//search all the sub resources for YSC files. (recurse!)
string lname = binentry.NameLower;
if (lname.EndsWith(".rpf"))
if (binentry.IsExtension(".rpf"))
{
br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512);
@ -442,9 +471,7 @@ namespace CodeWalker.GameFiles
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
string lname = resentry.NameLower;
if (lname.EndsWith(".ysc"))
if (resentry.IsExtension(".ysc"))
{
updateStatus?.Invoke("Extracting " + resentry.Name + "...");
@ -483,7 +510,7 @@ namespace CodeWalker.GameFiles
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyTo(outstr);
ds.CopyToFast(outstr);
byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
Array.Copy(deflated, outbuf, outbuf.Length);
@ -556,7 +583,8 @@ namespace CodeWalker.GameFiles
{
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)
{
@ -579,18 +607,99 @@ namespace CodeWalker.GameFiles
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)
{
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
long l = entry.GetFileSize();
if (l > 0)
if (l <= 0)
{
return null;
}
uint offset = 0;// 0x10;
uint totlen = (uint)l - offset;
byte[] tbytes = new byte[totlen];
byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
br.BaseStream.Position += offset;
br.Read(tbytes, 0, (int)totlen);
@ -599,71 +708,63 @@ namespace CodeWalker.GameFiles
{
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)
{
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
{
defl = DecompressBytes(tbytes);
}
else
{
defl = new byte[(int)totlen];
Buffer.BlockCopy(tbytes, 0, defl, 0, (int)totlen);
}
ArrayPool<byte>.Shared.Return(tbytes);
return defl;
}
return null;
}
public byte[] ExtractFileResource(RpfResourceFileEntry entry, BinaryReader br)
public async ValueTask<byte[]> ExtractFileResourceAsync(RpfResourceFileEntry entry, BinaryReader br)
{
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
if (entry.FileSize > 0)
if (entry.FileSize <= 0)
{
return null;
}
uint offset = 0x10;
uint totlen = entry.FileSize - offset;
byte[] tbytes = new byte[totlen];
byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
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);
//}
br.Read(tbytes, 0, (int)totlen);
await br.ReadAsync(tbytes, 0, (int)totlen);
if (entry.IsEncrypted)
{
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)
{
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize);
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen);
}
//else
//{ }
}
byte[] deflated = DecompressBytes(tbytes);
byte[] data = null;
byte[] deflated = await DecompressBytesAsync(tbytes);
byte[] data;
if (deflated != null)
{
data = deflated;
@ -671,16 +772,67 @@ namespace CodeWalker.GameFiles
else
{
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;
}
public byte[] ExtractFileResource(RpfResourceFileEntry entry, BinaryReader br)
{
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
if (entry.FileSize <= 0)
{
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()
{
T file = null;
@ -892,7 +1044,7 @@ namespace CodeWalker.GameFiles
{
LastError = string.Empty;
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)
{
@ -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)
{
try
{
using (DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress))
{
using (var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length))
{
ds.CopyTo(outstr);
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);
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length);
ds.CopyToFast(outstr);
byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output.
if (outbuf.Length <= bytes.Length)
{
@ -1047,8 +1197,38 @@ namespace CodeWalker.GameFiles
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)
{
LastError = "Could not decompress.";// ex.ToString();
@ -1056,6 +1236,7 @@ namespace CodeWalker.GameFiles
return null;
}
}
public static byte[] CompressBytes(byte[] data) //TODO: refactor this with ResourceBuilder.Compress/Decompress
{
using (MemoryStream ms = recyclableMemoryStreamManager.GetStream("CompressBytes", data.Length))
@ -1152,7 +1333,7 @@ namespace CodeWalker.GameFiles
Root = new RpfDirectoryEntry();
Root.File = this;
Root.Name = string.Empty;
Root.Path = Path.ToLowerInvariant();
Root.Path = Path;
}
if (Children == null)
{
@ -1517,15 +1698,15 @@ namespace CodeWalker.GameFiles
//recursively update paths, including in child RPFs.
if (dir == null)
{
Root.Path = Path.ToLowerInvariant();
Root.Path = Path;
dir = Root;
}
foreach (var file in dir.Files)
{
file.Path = dir.Path + "\\" + file.NameLower;
file.Path = dir.Path + "\\" + file.Name;
RpfBinaryFileEntry binf = file as RpfBinaryFileEntry;
if ((binf != null) && file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
if ((binf != null) && file.IsExtension(".rpf"))
{
RpfFile childrpf = FindChildArchive(binf);
if (childrpf != null)
@ -1685,9 +1866,8 @@ namespace CodeWalker.GameFiles
//create a new directory inside the given parent dir
RpfFile parent = dir.File;
string namel = name.ToLowerInvariant();
string fpath = parent.GetPhysicalFilePath();
string rpath = dir.Path + "\\" + namel;
string rpath = dir.Path + "\\" + name;
if (!File.Exists(fpath))
{
@ -1862,7 +2042,6 @@ namespace CodeWalker.GameFiles
entry.File = parent;
entry.Path = rpath;
entry.Name = name;
entry.NameLower = namel;
@ -2194,6 +2373,9 @@ namespace CodeWalker.GameFiles
public RpfFile File { get; set; }
public RpfDirectoryEntry Parent { get; set; }
public static int ExtensionHits = 0;
public static int ExtensionMisses = 0;
public uint NameHash { get
{
if (nameHash == 0 && !string.IsNullOrEmpty(Name))
@ -2229,19 +2411,38 @@ namespace CodeWalker.GameFiles
return;
}
name = value;
nameLower = null;
nameHash = 0;
shortNameHash = 0;
shortName = null;
extension = null;
}
}
public string NameLower
public string Extension
{
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 {
@ -2249,14 +2450,22 @@ namespace CodeWalker.GameFiles
{
if (string.IsNullOrEmpty(shortName) && !string.IsNullOrEmpty(Name))
{
int ind = Name.LastIndexOf('.');
if (ind > 0)
int length = Name.Length;
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;
break;
}
}
}
@ -2273,10 +2482,10 @@ namespace CodeWalker.GameFiles
public uint H1; //first 2 header values from RPF table...
public uint H2;
private string name;
private string nameLower;
private uint shortNameHash;
private uint nameHash;
private string shortName;
private string extension;
public abstract void Read(DataReader reader);
public abstract void Write(DataWriter writer);
@ -2286,9 +2495,9 @@ namespace CodeWalker.GameFiles
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.Collections.Concurrent;
using System.Collections.Generic;
@ -56,10 +58,10 @@ namespace CodeWalker.GameFiles
AllRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>();
EntryDict = new Dictionary<string, RpfEntry>();
ModRpfDict = new Dictionary<string, RpfFile>();
ModEntryDict = new Dictionary<string, RpfEntry>();
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
var rpfs = new ConcurrentBag<RpfFile>();
Parallel.ForEach(allfiles, (rpfpath) =>
@ -73,7 +75,7 @@ namespace CodeWalker.GameFiles
bool excl = false;
for (int i = 0; i < ExcludePaths.Length; i++)
{
if (rf.Path.StartsWith(ExcludePaths[i]))
if (rf.Path.StartsWith(ExcludePaths[i], StringComparison.OrdinalIgnoreCase))
{
excl = true;
break;
@ -147,10 +149,10 @@ namespace CodeWalker.GameFiles
DlcRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>();
EntryDict = new Dictionary<string, RpfEntry>();
ModRpfDict = new Dictionary<string, RpfFile>();
ModEntryDict = new Dictionary<string, RpfEntry>();
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var rpf in allRpfs)
{
RpfDict[rpf.Path] = rpf;
@ -171,8 +173,8 @@ namespace CodeWalker.GameFiles
private void AddRpfFile(RpfFile file, bool isdlc, bool ismod)
{
isdlc = isdlc || (file.NameLower == "update.rpf") || (file.NameLower.StartsWith("dlc") && file.NameLower.EndsWith(".rpf"));
ismod = ismod || (file.Path.StartsWith("mods\\"));
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\\", StringComparison.OrdinalIgnoreCase));
if (file.AllEntries != null)
{
@ -249,8 +251,7 @@ namespace CodeWalker.GameFiles
public RpfFile FindRpfFile(string path, bool exactPathOnly)
{
RpfFile file = null; //check the dictionary
RpfFile file;
if (EnableMods && ModRpfDict.TryGetValue(path, out file))
{
return file;
@ -261,14 +262,13 @@ namespace CodeWalker.GameFiles
return file;
}
string lpath = path.ToLowerInvariant(); //try look at names etc
foreach (RpfFile tfile in AllRpfs)
{
if (!exactPathOnly && tfile.NameLower == lpath)
if (!exactPathOnly && tfile.Name.Equals(path, StringComparison.OrdinalIgnoreCase))
{
return tfile;
}
if (tfile.Path == lpath)
if (tfile.Path.Equals(path, StringComparison.OrdinalIgnoreCase))
{
return tfile;
}
@ -281,21 +281,20 @@ namespace CodeWalker.GameFiles
public RpfEntry GetEntry(string path)
{
RpfEntry entry;
string pathl = path.ToLowerInvariant();
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry))
if (EnableMods && ModEntryDict.TryGetValue(path, out entry))
{
return entry;
}
EntryDict.TryGetValue(pathl, out entry);
EntryDict.TryGetValue(path, out entry);
if (entry == null)
{
pathl = pathl.Replace("/", "\\");
pathl = pathl.Replace("common:", "common.rpf");
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry))
path = path.Replace("/", "\\");
path = path.Replace("common:", "common.rpf");
if (EnableMods && ModEntryDict.TryGetValue(path, out entry))
{
return entry;
}
EntryDict.TryGetValue(pathl, out entry);
EntryDict.TryGetValue(path, out entry);
}
return entry;
}
@ -357,6 +356,30 @@ namespace CodeWalker.GameFiles
}
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
{
byte[] data = null;
@ -366,10 +389,42 @@ namespace CodeWalker.GameFiles
data = entry.File.ExtractFile(entry);
}
if (data != null)
{
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;
}
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;
}
@ -386,71 +441,64 @@ namespace CodeWalker.GameFiles
JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries)
{
var nlow = entry.NameLower;
if (string.IsNullOrEmpty(nlow)) continue;
var name = entry.Name;
if (string.IsNullOrEmpty(name))
continue;
//JenkIndex.Ensure(entry.Name);
//JenkIndex.Ensure(nlow);
int ind = nlow.LastIndexOf('.');
if (ind > 0)
{
JenkIndex.Ensure(entry.Name.Substring(0, ind));
JenkIndex.Ensure(nlow.Substring(0, ind));
var nameWithoutExtension = entry.ShortName;
JenkIndex.EnsureBoth(nameWithoutExtension);
//if (ind < entry.Name.Length - 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));
//}
}
else
{
JenkIndex.Ensure(entry.Name);
JenkIndex.Ensure(nlow);
}
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);
JenkIndex.Ensure(sname + "_lod");
JenkIndex.Ensure(sname + "_loda");
JenkIndex.Ensure(sname + "_lodb");
var sname = entry.ShortName;
var nameLod = sname + "_lod";
JenkIndex.EnsureLower(nameLod);
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);
JenkIndex.Ensure(strn);
JenkIndex.Ensure(strn + "_lod");
JenkIndex.Ensure(strn + "_loda");
JenkIndex.Ensure(strn + "_lodb");
var strn = entry.Name.Substring(0, name.Length - 13);
JenkIndex.EnsureLower(strn);
var nameChildrenLod = strn + "_lod";
JenkIndex.EnsureLower(nameChildrenLod);
JenkIndex.EnsureLower(nameChildrenLod + 'a');
JenkIndex.EnsureLower(nameChildrenLod + 'b');
}
var idx = nlow.LastIndexOf('_');
var idx = name.LastIndexOf('_');
if (idx > 0)
{
var str1 = nlow.Substring(0, idx);
var str1 = name.Substring(0, idx);
var idx2 = str1.LastIndexOf('_');
if (idx2 > 0)
{
var str2 = str1.Substring(0, idx2);
JenkIndex.Ensure(str2 + "_lod");
JenkIndex.EnsureLower(str2 + "_lod");
var maxi = 100;
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 + "_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('\\');
int pl = parts.Length;
@ -459,21 +507,18 @@ namespace CodeWalker.GameFiles
string fn = parts[pl - 1];
string fd = parts[pl - 2];
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);
}
hpath = fd + "/" + hpath;
if (parts[pl - 3] != "sfx")
{ }//no hit
hpath = fd + '/' + hpath;
JenkIndex.Ensure(hpath);
JenkIndex.EnsureLower(hpath);
}
}
if (nlow.EndsWith(".nametable"))
else if(name.EndsWith(".nametable", StringComparison.OrdinalIgnoreCase))
{
RpfBinaryFileEntry binfe = entry as RpfBinaryFileEntry;
if (binfe != null)
if (entry is RpfBinaryFileEntry binfe)
{
byte[] data = file.ExtractFile(binfe);
if (data != null)
@ -487,9 +532,8 @@ namespace CodeWalker.GameFiles
string str = sb.ToString();
if (!string.IsNullOrEmpty(str))
{
string strl = str.ToLowerInvariant();
//JenkIndex.Ensure(str);
JenkIndex.Ensure(strl);
JenkIndex.EnsureLower(str);
////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
////no luck here yet though
@ -508,8 +552,6 @@ namespace CodeWalker.GameFiles
}
}
}
else
{ }
}
}
}

View File

@ -96,10 +96,12 @@
using CodeWalker.GameFiles;
using DirectXTexNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -122,8 +124,6 @@ namespace CodeWalker.Utils
public static class DDSIO
{
public static byte[] GetPixels(Texture texture, int mip)
{
//dexyfex version
@ -159,29 +159,13 @@ namespace CodeWalker.Utils
bool swaprb = true;
if (DirectXTexNet.TexHelper.Instance.IsCompressed((DirectXTexNet.DXGI_FORMAT)format))
{
px = Decompress(imgdata, w, h, format);
} else
{
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
px = ConvertBGR5A1ToRGBA8(imgdata, w, h); //needs testing
break;
@ -205,8 +189,63 @@ namespace CodeWalker.Utils
swaprb = false;
break;
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))
{
@ -564,7 +603,10 @@ namespace CodeWalker.Utils
images[i].format = format; //(DXGI_FORMAT)img.Format;
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;
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)
{

View File

@ -1,4 +1,6 @@
/*

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

View File

@ -1,4 +1,5 @@
/*

/*
Copyright(c) 2015 Neodymium
Permission is hereby granted, free of charge, to any person obtaining a copy

View File

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

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -28,6 +29,11 @@ namespace CodeWalker.GameFiles
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)
{
@ -64,8 +70,47 @@ namespace CodeWalker.GameFiles
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)ToLower(text[i]);
h += (h << 10);
h ^= (h >> 6);
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
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 >> 6);
}
@ -231,41 +276,42 @@ namespace CodeWalker.GameFiles
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()
{
Index.Clear();
}
public static bool Ensure(string str)
public static void Ensure(string str)
{
uint hash = JenkHash.GenHash(str);
if (hash == 0) return true;
if (Index.ContainsKey(hash))
{
return true;
}
lock (Index)
{
Index[hash] = str;
return false;
}
Ensure(str, hash);
}
public static bool EnsureLower(string str)
public static void Ensure(string str, uint hash)
{
uint hash = JenkHash.GenHashLower(str);
if (hash == 0) return true;
if (hash == 0) return;
if (Index.ContainsKey(hash))
{
return true;
return;
}
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)
{
foreach(var s in strings)
@ -307,10 +353,9 @@ namespace CodeWalker.GameFiles
return res;
}
public static string[] GetAllStrings()
public static ICollection<string> GetAllStrings()
{
string[] res = null;
res = Index.Values.ToArray();
var res = Index.Values;
return res;
}

View File

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

View File

@ -14,11 +14,12 @@ namespace CodeWalker
public long MaxMemoryUsage = 536870912; //512mb
public long CurrentMemoryUsage = 0;
public double CacheTime = 10.0; //seconds to keep something that's not used
public double LoadingCacheTime = 1.0;
public DateTime CurrentTime = DateTime.Now;
private LinkedList<TVal> loadedList = new LinkedList<TVal>();
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
{
@ -45,50 +46,52 @@ namespace CodeWalker
public TVal TryGet(TKey key)
{
LinkedListNode<TVal> lln = null;
lock (loadedListLock)
{
if (loadedListDict.TryGetValue(key, out lln))
if (loadedListDict.TryGetValue(key, out var lln))
{
loadedList.Remove(lln);
loadedList.AddLast(lln);
lln.Value.LastUseTime = CurrentTime;
}
}
return (lln != null) ? lln.Value : null;
}
}
public bool TryAdd(TKey key, TVal item)
{
item.Key = key;
if (CanAdd())
{
LinkedListNode<TVal> lln;
lock(loadedListLock)
{
var lln = loadedList.AddLast(item);
loadedListDict.Add(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
lln = loadedList.AddLast(item);
}
lln.Value.LastUseTime = CurrentTime;
loadedListDict.TryAdd(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
return true;
}
else
{
//cache full, check the front of the list for oldest..
var oldlln = loadedList.First;
var cachetime = CacheTime;
var cachetime = LoadingCacheTime;
int iter = 0, maxiter = 2;
while (!CanAdd() && (iter<maxiter))
{
while ((!CanAdd()) && (oldlln != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime))
{
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
lock (loadedListLock)
{
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later..
}
loadedListDict.TryRemove(oldlln.Value.Key, out _);
oldlln.Value = null;
oldlln = null;
//GC.Collect();
@ -99,12 +102,13 @@ namespace CodeWalker
}
if (CanAdd()) //see if there's enough memory now...
{
LinkedListNode<TVal> newlln;
lock(loadedListLock)
{
var newlln = loadedList.AddLast(item);
loadedListDict.Add(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
newlln = loadedList.AddLast(item);
}
loadedListDict.TryAdd(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
return true;
}
else
@ -127,8 +131,8 @@ namespace CodeWalker
{
loadedList.Clear();
loadedListDict.Clear();
CurrentMemoryUsage = 0;
}
Interlocked.Exchange(ref CurrentMemoryUsage, 0);
}
public void Remove(TKey key)
@ -137,22 +141,21 @@ namespace CodeWalker
{
return;
}
if (loadedListDict.TryRemove(key, out var n))
{
lock (loadedListLock)
{
LinkedListNode<TVal> n;
if (loadedListDict.TryGetValue(key, out n))
{
loadedListDict.Remove(key);
loadedList.Remove(n);
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
}
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
}
}
public void Compact()
{
lock(loadedList)
lock(loadedListLock)
{
var oldlln = loadedList.First;
while (oldlln != null)
@ -160,7 +163,7 @@ namespace CodeWalker
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break;
var nextln = oldlln.Next;
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..
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)
{ 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))
{
byte[] newb = new byte[bytes.Length - 3];
for (int i = 3; i < bytes.Length; i++)
{
newb[i - 3] = bytes[i];
start = 3;
length = bytes.Length - 3;
}
bytes = newb; //trim starting byte order mark
}
return Encoding.UTF8.GetString(bytes);
return Encoding.UTF8.GetString(bytes, start, length);
}
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
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)
{
if (flag) return SetBit(value, bit);
else return ClearBit(value, bit);
}
public static uint RotateLeft(uint value, int count)
@ -275,24 +292,4 @@ namespace CodeWalker
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);
}
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.Collections.Generic;
using System.Linq;
@ -6,11 +7,27 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker
{
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)
{
@ -63,6 +80,55 @@ namespace CodeWalker
if (node == null) return null;
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)
{
if (node == null) return false;
@ -87,6 +153,14 @@ namespace CodeWalker
FloatUtil.TryParse(val, out 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
{
if (node == null) return new T();
@ -99,7 +173,7 @@ namespace CodeWalker
{
return default(T);
}
if (val.StartsWith("hash_"))
if (val.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
{
//convert hash_12ABC to Unk_12345
var substr = val.Substring(5);
@ -111,7 +185,18 @@ namespace CodeWalker
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")
{
if (node == null) return false;
@ -120,6 +205,20 @@ namespace CodeWalker
bool.TryParse(val, out 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")
{
if (node == null) return 0;
@ -128,12 +227,31 @@ namespace CodeWalker
int.TryParse(val, out 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")
{
if (node == null) return 0;
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
uint i;
if (val?.StartsWith("0x") ?? false)
if (val?.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ?? false)
{
var subs = val.Substring(2);
i = Convert.ToUInt32(subs, 16);
@ -149,7 +267,7 @@ namespace CodeWalker
if (node == null) return 0;
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
ulong i;
if (val?.StartsWith("0x") ?? false)
if (val?.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ?? false)
{
var subs = val.Substring(2);
i = Convert.ToUInt64(subs, 16);
@ -160,6 +278,26 @@ namespace CodeWalker
}
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")
{
if (node == null) return 0;
@ -174,12 +312,37 @@ namespace CodeWalker
string val = node.SelectSingleNode(name)?.Attributes[attribute]?.InnerText;
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")
{
float fx = GetChildFloatAttribute(node, name, x);
float fy = GetChildFloatAttribute(node, name, y);
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")
{
float fx = GetChildFloatAttribute(node, name, x);
@ -207,6 +370,37 @@ namespace CodeWalker
node.AppendChild(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)
{
XmlElement child = AddChild(doc, node, name);

View File

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

View File

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

View File

@ -277,7 +277,7 @@ namespace CodeWalker.World
{
foreach (var entry in maprpf.AllEntries)
{
if (entry.NameLower.EndsWith(".ymap"))
if (entry.IsExtension(".ymap"))
{
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);
if (!usedboundsdict.ContainsKey(ehash))
@ -367,7 +367,7 @@ namespace CodeWalker.World
}
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)
{
AddRpfYnds(rpffile, yndentries);
@ -509,7 +509,7 @@ namespace CodeWalker.World
if (entry is RpfFileEntry)
{
RpfFileEntry fentry = entry as RpfFileEntry;
if (entry.NameLower.EndsWith(".ynd"))
if (entry.IsExtension(".ynd"))
{
if (yndentries.ContainsKey(entry.NameHash))
{ }
@ -809,7 +809,7 @@ namespace CodeWalker.World
if (entry is RpfFileEntry)
{
RpfFileEntry fentry = entry as RpfFileEntry;
if (entry.NameLower.EndsWith(".ynv"))
if (entry.IsExtension(".ynv"))
{
if (ynventries.ContainsKey(entry.NameHash))
{ }

View File

@ -38,7 +38,7 @@ namespace CodeWalker.World
{
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));
}

View File

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

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using CodeWalker.Utils;
using CodeWalker.Core.Utils;
namespace CodeWalker.RPFExplorer
{
@ -18,13 +19,31 @@ namespace CodeWalker.RPFExplorer
[STAThread]
static void Main()
{
//Process.Start("CodeWalker.exe", "explorer");
try
{
if (!NamedPipe.TrySendMessageToOtherProcess("explorer"))
{
ConsoleWindow.Hide();
//Process.Start("CodeWalker.exe", "explorer");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ExploreForm());
var form = new ExploreForm();
var namedPipe = new NamedPipe(form);
namedPipe.Init();
Application.Run(form);
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">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net48</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" />
<PackageReference Include="SharpDX" Version="4.2.0" />

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
# Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeWalker.Shaders", "CodeWalker.Shaders\CodeWalker.Shaders.vcxproj", "{0D14B076-0ABF-434E-AB9F-36E7800D8887}"
EndProject
@ -23,6 +23,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.RPFExplorer", "C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Vehicles", "CodeWalker.Vehicles\CodeWalker.Vehicles.csproj", "{F5A776B0-2F1A-4C36-87B3-86206AC4B439}"
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x86.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -15,11 +15,17 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<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="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.D3DCompiler" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />

View File

@ -1,4 +1,7 @@
using CodeWalker.Core.Utils;
using System;
using CodeWalker.WinForms;
namespace CodeWalker
{
@ -15,11 +18,13 @@ namespace CodeWalker
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
using var _ = new DisposableTimer("ExploreForm Dipose");
base.Dispose(disposing);
MainListView.Dispose();
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
@ -121,6 +126,7 @@ namespace CodeWalker
this.ListContextViewHexMenu = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.ListContextExportXmlMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractShadersMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractRawMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractUncompressedMenu = new System.Windows.Forms.ToolStripMenuItem();
this.ListContextExtractAllMenu = new System.Windows.Forms.ToolStripMenuItem();
@ -990,6 +996,7 @@ namespace CodeWalker
this.ListContextViewMenu,
this.ListContextViewHexMenu,
this.toolStripSeparator2,
this.ListContextExtractShadersMenu,
this.ListContextExportXmlMenu,
this.ListContextExtractRawMenu,
this.ListContextExtractUncompressedMenu,
@ -1047,6 +1054,12 @@ namespace CodeWalker
this.ListContextExportXmlMenu.Size = new System.Drawing.Size(208, 22);
this.ListContextExportXmlMenu.Text = "Export XML...";
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
//
@ -1394,6 +1407,7 @@ namespace CodeWalker
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem ListContextExportXmlMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextExtractAllMenu;
private System.Windows.Forms.ToolStripMenuItem ListContextExtractShadersMenu;
private System.Windows.Forms.ToolStripSeparator ListContextImportSeparator;
private System.Windows.Forms.ToolStripMenuItem ListContextCopyPathMenu;
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.Linq;
using Range = FastColoredTextBoxNS.Range;
using CodeWalker.Core.Utils;
namespace CodeWalker.Forms
{
@ -400,7 +401,7 @@ namespace CodeWalker.Forms
{
Stream wavStream = audio.GetWavStream();
FileStream stream = File.Create(saveFileDialog.FileName);
wavStream.CopyTo(stream);
wavStream.CopyToFast(stream);
stream.Close();
wavStream.Close();
}

View File

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

View File

@ -45,16 +45,12 @@ namespace CodeWalker.Forms
private bool LoadingXml = false;
private bool DelayHighlight = false;
private ExploreForm exploreForm = null;
public RpfFileEntry rpfFileEntry { get; private set; } = null;
private MetaFormat metaFormat = MetaFormat.XML;
public MetaForm(ExploreForm owner)
public MetaForm()
{
exploreForm = owner;
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)
@ -426,7 +484,7 @@ namespace CodeWalker.Forms
//otherwise, save the generated file to disk?
//(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?
@ -466,14 +524,14 @@ namespace CodeWalker.Forms
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);
if (newentry != rpfFileEntry)
{ }
rpfFileEntry = newentry;
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer...
ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false;
@ -492,7 +550,7 @@ namespace CodeWalker.Forms
{
File.WriteAllBytes(rpfFileEntry.Path, data);
exploreForm?.RefreshMainListViewInvoke(); //update the file details in explorer...
ExploreForm.RefreshMainListViewInvoke(); //update the file details in explorer...
modified = false;

View File

@ -24,7 +24,7 @@ namespace CodeWalker.Forms
{
public Form Form { get { return this; } } //for DXForm/DXManager use
private Renderer Renderer = null;
public Renderer Renderer { get; set; }
volatile bool formopen = false;
@ -37,6 +37,8 @@ namespace CodeWalker.Forms
Weather weather;
Clouds clouds;
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
bool MouseLButtonDown = false;
bool MouseRButtonDown = false;
int MouseX;
@ -114,7 +116,6 @@ namespace CodeWalker.Forms
public bool showLightGizmos = true;
public Skeleton Skeleton = null;
ExploreForm exploreForm = null;
RpfFileEntry rpfFileEntry = null;
@ -128,16 +129,14 @@ namespace CodeWalker.Forms
public ModelForm(ExploreForm ExpForm = null)
public ModelForm()
{
if (this.DesignMode) return;
InitializeComponent();
exploreForm = ExpForm;
gameFileCache = GameFileCacheFactory.GetInstance();
gameFileCache = ExpForm?.GetFileCache() ?? GameFileCacheFactory.Create();
if (ExpForm == null)
if (ExploreForm.Instance == null)
{
gameFileCache.EnableDlc = false;
gameFileCache.EnableMods = false;
@ -163,12 +162,14 @@ namespace CodeWalker.Forms
catch(Exception 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.
{
@ -180,14 +181,21 @@ namespace CodeWalker.Forms
if (!fcItemsPending)
{
Thread.Sleep(10);
await Task.Delay(10);
}
}
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;
new Thread(new ThreadStart(ContentThread)).Start();
Task.Run(ContentThread);
frametimer.Start();
}
@ -324,19 +333,26 @@ namespace CodeWalker.Forms
Console.WriteLine("Clearing cache");
gameFileCache.Clear();
gameFileCache.IsInited = true;
GC.Collect();
//GC.Collect();
}
}
public void RenderScene(DeviceContext context)
public async ValueTask RenderScene(DeviceContext context)
{
float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart();
if (elapsed < 0.016666)
{
await Task.Delay((int)(0.016666 * elapsed) * 1000);
}
if (Pauserendering) return;
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);
@ -376,7 +392,7 @@ namespace CodeWalker.Forms
}
private void ContentThread()
private async void ContentThread()
{
//main content loading thread.
//running = true;
@ -453,7 +469,7 @@ namespace CodeWalker.Forms
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>();
foreach (var ycde in ycds)
{
ycdlist.Add(ycde.GetShortName());
ycdlist.Add(ycde.ShortName);
}
ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray());
ClipDictComboBox.Text = "";
@ -703,10 +719,10 @@ namespace CodeWalker.Forms
public void ViewModel(byte[] data, RpfFileEntry e)
{
var nl = e?.NameLower ?? "";
var nl = e?.Name ?? "";
var fe = Path.GetExtension(nl);
Show();
switch (fe)
switch (fe.ToLowerInvariant())
{
case ".ydr":
var ydr = RpfFile.GetFile<YdrFile>(e, data);
@ -902,10 +918,10 @@ namespace CodeWalker.Forms
Yft = yft;
rpfFileEntry = Yft.RpfFileEntry;
ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0;
var namelower = Yft.RpfFileEntry?.ShortName;
if (namelower?.EndsWith("_hi", StringComparison.OrdinalIgnoreCase) ?? false)
var name = Yft.RpfFileEntry?.ShortName;
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)
{
@ -1295,7 +1311,7 @@ namespace CodeWalker.Forms
{
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)
{
AddDrawableTreeNode(kvp.Value, kvp.Key, check);
@ -1668,7 +1684,7 @@ namespace CodeWalker.Forms
if (td != null)
{
YtdForm f = new YtdForm(null, this);
YtdForm f = new YtdForm(this);
f.Show(this);
f.LoadTexDict(td, fileName);
}
@ -1760,7 +1776,7 @@ namespace CodeWalker.Forms
private void Save(bool saveAs = false)
{
var editMode = exploreForm?.EditMode ?? false;
var editMode = ExploreForm.Instance?.EditMode ?? false;
if (string.IsNullOrEmpty(FilePath))
{
@ -1857,14 +1873,14 @@ namespace CodeWalker.Forms
try
{
if (!(exploreForm?.EnsureRpfValidEncryption(rpfFileEntry.File) ?? false)) return;
if (!(ExploreForm.EnsureRpfValidEncryption(rpfFileEntry.File))) return;
var newentry = RpfFile.CreateFile(rpfFileEntry.Parent, rpfFileEntry.Name, fileBytes);
if (newentry != rpfFileEntry)
{ }
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();
@ -1889,7 +1905,7 @@ namespace CodeWalker.Forms
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();
}

View File

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

View File

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

View File

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

View File

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

View File

@ -5,10 +5,12 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Channels;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -23,13 +25,11 @@ namespace CodeWalker.Forms
private Texture CurrentTexture = null;
private float CurrentZoom = 0.0f; //1.0 = 100%, 0.0 = stretch
private bool Modified = false;
private ExploreForm ExploreForm = null;
private ModelForm ModelForm = null;
public YtdForm(ExploreForm exploreForm = null, ModelForm modelForm = null)
public YtdForm(ModelForm modelForm = null)
{
ExploreForm = exploreForm;
ModelForm = modelForm;
InitializeComponent();
}
@ -155,8 +155,10 @@ namespace CodeWalker.Forms
byte[] pixels = DDSIO.GetPixels(tex, cmip);
int w = tex.Width >> 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)
{
var BoundsRect = new System.Drawing.Rectangle(0, 0, w, h);
@ -396,7 +398,7 @@ namespace CodeWalker.Forms
}
else
{
var cansave = (ExploreForm?.EditMode ?? false);
var cansave = (ExploreForm.Instance?.EditMode ?? false);
var s = "Save " + FileName;
var sas = "Save " + FileName + " As...";
FileSaveMenu.Text = s;
@ -435,7 +437,7 @@ namespace CodeWalker.Forms
private void SaveYTD(bool saveas = false)
{
if (Ytd == null) return;
if (!(ExploreForm?.EditMode ?? false))
if (!(ExploreForm.Instance?.EditMode ?? false))
{
saveas = true;
}
@ -485,18 +487,18 @@ namespace CodeWalker.Forms
else if (!isinrpf) //save direct to filesystem in RPF explorer
{
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...
{
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!");
return;
}
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;
@ -647,4 +649,14 @@ namespace CodeWalker.Forms
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 System;
using System.Diagnostics;
@ -11,7 +12,7 @@ namespace CodeWalker.GameFiles
public static class GameFileCacheFactory
{
public static GameFileCache _instance = null;
public static GameFileCache Create()
public static GameFileCache GetInstance()
{
if (_instance == null)
{

View File

@ -36,13 +36,13 @@ namespace CodeWalker
private void RPFExplorerButton_Click(object sender, EventArgs e)
{
ExploreForm f = new ExploreForm();
f.Show(this);
f.Show();
}
private void RPFBrowserButton_Click(object sender, EventArgs e)
{
BrowseForm f = new BrowseForm();
f.Show(this);
f.Show();
}
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 Renderer Renderer = null;
public Renderer Renderer { get; set; }
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
public bool Pauserendering { get; set; }
@ -41,6 +41,8 @@ namespace CodeWalker
Entity camEntity = new Entity();
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
bool MouseLButtonDown = false;
bool MouseRButtonDown = false;
@ -50,7 +52,7 @@ namespace CodeWalker
System.Drawing.Point MouseLastPoint;
public GameFileCache GameFileCache { get; } = GameFileCacheFactory.Create();
public GameFileCache GameFileCache { get; } = GameFileCacheFactory.GetInstance();
InputManager Input = new InputManager();
@ -189,7 +191,8 @@ namespace CodeWalker
formopen = true;
new Thread(new ThreadStart(ContentThread)).Start();
Task.Run(ContentThread);
frametimer.Start();
@ -207,18 +210,27 @@ namespace CodeWalker
count++;
}
}
public void RenderScene(DeviceContext context)
public async ValueTask RenderScene(DeviceContext context)
{
float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart();
if (elapsed < 0.016666)
{
await Task.Delay((int)(0.016666 * elapsed) * 1000);
}
if (Pauserendering) return;
GameFileCache.BeginFrame();
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);
//space.Update(elapsed);
@ -262,8 +274,11 @@ namespace CodeWalker
//RenderWidgets();
Renderer.EndRender();
}
finally
{
Monitor.Exit(Renderer.RenderSyncRoot);
}
//UpdateMarkerSelectionPanelInvoke();
}
@ -323,6 +338,8 @@ namespace CodeWalker
private void ContentThread()
{
try
{
//main content loading thread.
running = true;
@ -340,6 +357,8 @@ namespace CodeWalker
return;
}
if (!GameFileCache.IsInited)
{
GameFileCache.EnableDlc = true;
GameFileCache.EnableMods = true;
GameFileCache.LoadPeds = true;
@ -347,7 +366,8 @@ namespace CodeWalker
GameFileCache.LoadArchetypes = 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.Init(UpdateStatus, LogError);
GameFileCache.Init(UpdateStatus, LogError, force: false);
}
//UpdateDlcListComboBox(gameFileCache.DlcNameList);
@ -367,31 +387,41 @@ namespace CodeWalker
//UpdateStatus("Ready");
Task.Run(() => {
Task.Run(async () => {
while (formopen && !IsDisposed) //renderer content loop
{
bool rcItemsPending = Renderer.ContentThreadProc();
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
{
bool fcItemsPending = GameFileCache.ContentThreadProc();
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;
});
}
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>();
foreach (var ycde in ycds)
{
ycdlist.Add(ycde.GetShortName());
ycdlist.Add(ycde.ShortName);
}
ClipDictComboBox.AutoCompleteCustomSource.AddRange(ycdlist.ToArray());
ClipDictComboBox.Text = "";

View File

@ -1,4 +1,5 @@
using CodeWalker.Forms;
using CodeWalker.Core.Utils;
using CodeWalker.Forms;
using CodeWalker.GameFiles;
using CodeWalker.Properties;
using CodeWalker.Utils;
@ -10,6 +11,7 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Shell;
@ -29,6 +31,13 @@ namespace CodeWalker
{
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ConsoleWindow.Hide();
Application.ThreadException += (object sender, ThreadExceptionEventArgs e) =>
{
Console.WriteLine($"Unhandeled exception occured: {e.Exception}");
};
bool menumode = false;
bool explorermode = false;
bool projectmode = false;
@ -98,7 +107,13 @@ namespace CodeWalker
}
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)
{
@ -114,15 +129,29 @@ namespace CodeWalker
}
else if (path != null)
{
var modelForm = new ModelForm();
modelForm.Load += new EventHandler(async (sender, eventArgs) => {
modelForm.ViewModel(path);
});
Application.Run(modelForm);
if (!NamedPipe.TrySendMessageToOtherProcess($"open-file {path}"))
{
Form form = null;
try
{
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
{
Application.Run(new WorldForm());
var form = new WorldForm();
var namedPipe = new NamedPipe(form);
namedPipe.Init();
Application.Run(form);
}
#if !DEBUG
}

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
@ -124,26 +125,22 @@ namespace CodeWalker.Project
SetTheme(Settings.Default.ProjectWindowTheme, false);
ShowDefaultPanels();
GameFileCache = GameFileCacheFactory.GetInstance();
if ((WorldForm != null) && (WorldForm.GameFileCache != null))
if (!GameFileCache.IsInited)
{
GameFileCache = WorldForm.GameFileCache;
RpfMan = GameFileCache.RpfMan;
}
else
{
GameFileCache = GameFileCacheFactory.Create();
new Thread(new ThreadStart(() =>
Task.Run(() =>
{
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
GameFileCache.Init(UpdateStatus, UpdateError);
RpfMan = GameFileCache.RpfMan;
})).Start();
}
});
}
RpfMan = GameFileCache.RpfMan;
}
private void UpdateStatus(string text)
{
return;
try
{
if (InvokeRequired)
@ -168,6 +165,7 @@ namespace CodeWalker.Project
}
else
{
Console.WriteLine(text);
//TODO: error text
//ErrorLabel.Text = text;
}
@ -1640,14 +1638,14 @@ namespace CodeWalker.Project
}
break;
case ".dat":
if (fn.StartsWith("trains"))
if (fn.StartsWith("trains", StringComparison.OrdinalIgnoreCase))
{
var track = CurrentProjectFile.AddTrainsFile(file);
if (track != null) LoadTrainTrackFromFile(track, file);
}
break;
case ".rel":
if (fn.EndsWith(".dat151.rel"))
if (fn.EndsWith(".dat151.rel", StringComparison.OrdinalIgnoreCase))
{
var dat151 = CurrentProjectFile.AddAudioRelFile(file);
if (dat151 != null) LoadAudioRelFromFile(dat151, file);
@ -6129,7 +6127,7 @@ namespace CodeWalker.Project
var delim = line.Contains(",") ? "," : " ";
var vals = line.Split(new[] { delim }, StringSplitOptions.RemoveEmptyEntries);
if (vals.Length < 3) continue;
if (vals[0].StartsWith("X")) continue;
if (vals[0].StartsWith("X", StringComparison.OrdinalIgnoreCase)) continue;
Vector3 pos = Vector3.Zero;
float dir = 0;
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)
{
if (hidegtavmap)
@ -7106,10 +7104,16 @@ namespace CodeWalker.Project
}
}
visiblemloentities.Clear();
foreach (var kvp in ymaps)//TODO: improve performance
if (DateTime.Now - LastProjectCheck < TimeSpan.FromSeconds(1))
{
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
{
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.FilePath = filename;
track.RpfFileEntry.Name = fname;
track.RpfFileEntry.NameLower = fname.ToLowerInvariant();
if (WorldForm != null)
{

View File

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

View File

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

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
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
//and a couple of extra methods (these callbacks) are needed by DXManager.
public Renderer Renderer { get; set; }
Form Form { get; }
public CancellationTokenSource CancellationTokenSource { get; }
public bool Pauserendering { get; set; }
void InitScene(Device device);
void CleanupScene();
void RenderScene(DeviceContext context);
ValueTask RenderScene(DeviceContext context);
void BuffersResized(int w, int h);
bool ConfirmQuit();
}

View File

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

View File

@ -10,6 +10,7 @@ using Buffer = SharpDX.Direct3D11.Buffer;
using CodeWalker.World;
using SharpDX.Direct3D;
using SharpDX;
using System.Threading;
namespace CodeWalker.Rendering
{
@ -72,6 +73,12 @@ namespace CodeWalker.Rendering
public RenderableModel[] LowModels;
public RenderableModel[] VlowModels;
public RenderableModel[] AllModels;
public float LodDistanceHigh;
public float LodDistanceMed;
public float LodDistanceLow;
public float LodDistanceVLow;
//public Dictionary<uint, Texture> TextureDict { get; private set; }
//public long EmbeddedTextureSize { get; private set; }
@ -148,6 +155,10 @@ namespace CodeWalker.Rendering
curmodel += vlow.Length;
}
LodDistanceHigh = drawable.LodDistHigh;
LodDistanceMed = drawable.LodDistMed;
LodDistanceLow = drawable.LodDistLow;
LodDistanceVLow = drawable.LodDistVlow;
//var sg = Drawable.ShaderGroup;
//if ((sg != null) && (sg.TextureDictionary != null))
@ -346,6 +357,30 @@ namespace CodeWalker.Rendering
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)
{
var rmodel = new RenderableModel();
@ -723,7 +758,7 @@ namespace CodeWalker.Rendering
}
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;
foreach (var geom in model.Geometries)

View File

@ -11,6 +11,7 @@ using System.Collections.Concurrent;
using CodeWalker.GameFiles;
using System.Threading;
using CodeWalker.Properties;
using System.Diagnostics;
namespace CodeWalker.Rendering
{
@ -20,7 +21,7 @@ namespace CodeWalker.Rendering
public DateTime LastUnload = DateTime.UtcNow;
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 int MaxItemsPerLoop = 1; //to keep things flowing
public int MaxItemsPerLoop = 3; //to keep things flowing
public long TotalGraphicsMemoryUse
{
@ -117,6 +118,11 @@ namespace CodeWalker.Rendering
{
currentDevice = null;
Clear();
}
public void Clear()
{
renderables.Clear();
textures.Clear();
boundcomps.Clear();
@ -132,8 +138,8 @@ namespace CodeWalker.Rendering
if (currentDevice == null) return false; //can't do anything with no device
Monitor.Enter(updateSyncRoot);
try
{
//load the queued items if possible
int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop);
int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop);
@ -160,7 +166,8 @@ namespace CodeWalker.Rendering
var now = DateTime.UtcNow;
var deltat = (now - LastUpdate).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.
@ -179,10 +186,18 @@ namespace CodeWalker.Rendering
LastUpdate = DateTime.UtcNow;
Monitor.Exit(updateSyncRoot);
return itemsStillPending;
}
catch(Exception ex)
{
Console.WriteLine(ex);
return true;
}
finally
{
Monitor.Exit(updateSyncRoot);
}
}
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 TKey Key;
public volatile bool IsLoaded = false;
public volatile bool LoadQueued = false;
public long LastUseTime = 0;
public Stopwatch LastUseTime = Stopwatch.StartNew();
//public DateTime LastUseTime { get; set; }
public long DataSize { get; set; }
@ -333,7 +355,14 @@ namespace CodeWalker.Rendering
LoadedCount = 0;
while (itemsToLoad.TryDequeue(out item))
{
if (item.IsLoaded) continue; //don't load it again...
if (item.IsLoaded)
{
item.LoadQueued = false;
continue;
}
try
{
LoadedCount++;
long gcachefree = CacheLimit - Interlocked.Read(ref CacheUse);// CacheUse;
if (gcachefree > item.DataSize)
@ -344,15 +373,21 @@ namespace CodeWalker.Rendering
loadeditems.AddLast(item);
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;
}
return LoadedCount;
@ -361,12 +396,12 @@ namespace CodeWalker.Rendering
public void UnloadProc()
{
//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;
while (rnode != null)
{
var lu = DateTime.FromBinary(Interlocked.Read(ref rnode.Value.LastUseTime));
if ((now - lu).TotalSeconds > CacheTime)
var lastUsed = rnode.Value.LastUseTime.Elapsed;
if (lastUsed.TotalSeconds > CacheTime)
{
var nextnode = rnode.Next;
itemsToUnload.Enqueue(rnode.Value);
@ -399,7 +434,7 @@ namespace CodeWalker.Rendering
}
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);
}
@ -420,7 +455,7 @@ namespace CodeWalker.Rendering
item.Init(key);
cacheitems.Add(key, item);
}
Interlocked.Exchange(ref item.LastUseTime, LastFrameTime);
item.LastUseTime.Restart();
if ((!item.IsLoaded) && (!item.LoadQueued))// ||
{
item.LoadQueued = true;

View File

@ -173,7 +173,7 @@ namespace CodeWalker.Rendering
public Renderer(DXForm form, GameFileCache cache)
{
Form = form;
gameFileCache = cache ?? GameFileCacheFactory.Create();
gameFileCache = cache ?? GameFileCacheFactory.GetInstance();
renderableCache = new RenderableCache();
var s = Settings.Default;
@ -386,8 +386,11 @@ namespace CodeWalker.Rendering
var ctc = renderableCache.LoadedTextureCount;
var vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
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++)
{
CloudHatFragLayer layer = frag.Layers[i];
uint dhash = JenkHash.GenHash(layer.Filename.ToLowerInvariant());
uint dhash = JenkHash.GenHashLower(layer.Filename);
Archetype arch = gameFileCache.GetArchetype(dhash);
if (arch == null)
{ continue; }
@ -1700,8 +1703,10 @@ namespace CodeWalker.Rendering
var drw = gameFileCache.TryGetDrawable(arch);
var rnd = TryGetRenderable(arch, drw);
if ((rnd == null) || (rnd.IsLoaded == false) || (rnd.AllTexturesLoaded == false))
{ continue; }
if (rnd == null || !rnd.IsLoaded || !rnd.AllTexturesLoaded)
{
continue;
}
RenderableInst rinst = new RenderableInst();
@ -2051,7 +2056,7 @@ namespace CodeWalker.Rendering
ent.Distance = dist;
ent.IsVisible = (dist <= loddist);
ent.IsWithinLodDist = (dist <= loddist);
ent.ChildrenVisible = (dist <= cloddist) && (ent._CEntityDef.numChildren > 0);
@ -2061,7 +2066,7 @@ namespace CodeWalker.Rendering
if ((ent._CEntityDef.lodLevel == rage__eLodType.LODTYPES_DEPTH_ORPHANHD) ||
(ent._CEntityDef.lodLevel < renderworldMaxLOD))
{
ent.IsVisible = false;
ent.IsWithinLodDist = false;
ent.ChildrenVisible = false;
}
if (ent._CEntityDef.lodLevel == renderworldMaxLOD)
@ -2090,9 +2095,10 @@ namespace CodeWalker.Rendering
}
private void RenderWorldRecurseAddEntities(YmapEntityDef ent)
{
bool hide = ent.ChildrenVisible;
bool force = (ent.Parent != null) && ent.Parent.ChildrenVisible && !hide;
if (force || (ent.IsVisible && !hide))
bool childrenVisible = ent.ChildrenVisible;
var parentChildrenVisible = ent.Parent?.ChildrenVisible ?? true;
bool force = parentChildrenVisible && !childrenVisible;
if (force || (ent.IsWithinLodDist && !childrenVisible))
{
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++)
{
@ -2167,7 +2173,7 @@ namespace CodeWalker.Rendering
if (intent?.Archetype == null) continue; //missing archetype...
if (!RenderIsEntityFinalRender(intent)) continue; //proxy or something..
intent.IsVisible = true;
intent.IsWithinLodDist = true;
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 (!RenderIsEntityFinalRender(intent)) continue; //proxy or something..
intent.IsVisible = true;
intent.IsWithinLodDist = true;
if (!camera.ViewFrustum.ContainsAABBNoClip(ref intent.BBCenter, ref intent.BBExtent))
{
@ -2390,8 +2396,11 @@ namespace CodeWalker.Rendering
{
var drawable = gameFileCache.TryGetDrawable(arch);
rndbl = TryGetRenderable(arch, drawable);
if (rndbl != null && rndbl.IsLoaded)
{
ArchetypeRenderables[arch] = rndbl;
}
}
if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload))
{
return rndbl;
@ -2457,7 +2466,10 @@ namespace CodeWalker.Rendering
}
private bool RenderYmapLOD(YmapFile ymap, YmapEntityDef entity)
{
if (!ymap.Loaded) return false;
if (!ymap.Loaded)
{
return false;
}
ymap.EnsureChildYmaps(gameFileCache);
@ -3020,8 +3032,11 @@ namespace CodeWalker.Rendering
rndbl = TryGetRenderable(arche, drawable);
}
if (rndbl != null)
if (rndbl == null || !rndbl.IsLoaded)
{
return false;
}
if (animClip != null)
{
rndbl.ClipMapEntry = animClip;
@ -3041,14 +3056,13 @@ namespace CodeWalker.Rendering
if ((frag != null) && (frag.DrawableCloth != null)) //cloth...
{
rndbl = TryGetRenderable(arche, frag.DrawableCloth);
if (rndbl != null)
if (rndbl != null && rndbl.IsLoaded)
{
bool res2 = RenderRenderable(rndbl, arche, entity);
res = res || res2;
}
}
}
}
return res;
@ -3062,7 +3076,7 @@ namespace CodeWalker.Rendering
return false;
Renderable rndbl = TryGetRenderable(arche, drawable, txdHash, txdExtra, diffOverride);
if (rndbl == null)
if (rndbl == null || !rndbl.IsLoaded)
return false;
if (animClip != null)
@ -3243,7 +3257,7 @@ namespace CodeWalker.Rendering
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++)
{
@ -3568,17 +3582,23 @@ namespace CodeWalker.Rendering
MetaHash ahash = arche.Hash;
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;
foreach (var geom in model.Geometries)
var model = models[i];
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)
{
uint cmeindex = geom.DrawableGeom.ShaderID + 1u;
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;
if (extraTexDict == null) extraTexDict = txdExtra;
if (extraTexDict == null)
extraTexDict = txdExtra;
bool cacheSD = (rndbl.SDtxds == null);
bool cacheHD = (renderhdtextures && (rndbl.HDtxds == null));

View File

@ -851,10 +851,11 @@ namespace CodeWalker.Rendering
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);
foreach (var geom in model.Geometries)
@ -866,6 +867,7 @@ namespace CodeWalker.Rendering
}
}
}
}

View File

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

View File

@ -1,4 +1,6 @@
namespace CodeWalker.Tools
using CodeWalker.Forms;
namespace CodeWalker.Tools
{
partial class BrowseForm
{
@ -78,7 +80,7 @@
this.SelTextureMipTrackBar = new System.Windows.Forms.TrackBar();
this.label4 = new System.Windows.Forms.Label();
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.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.TestAllButton = new System.Windows.Forms.Button();

View File

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

View File

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

View File

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

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