mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-16 20:17:30 +08:00
Many optimizations and bug fixes
This commit is contained in:
parent
aed7a1e051
commit
ba72dadd16
38
CodeWalker.Benchmarks/App.config
Normal file
38
CodeWalker.Benchmarks/App.config
Normal 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>
|
200
CodeWalker.Benchmarks/Benchmarks.cs
Normal file
200
CodeWalker.Benchmarks/Benchmarks.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
201
CodeWalker.Benchmarks/CodeWalker.Benchmarks.csproj
Normal file
201
CodeWalker.Benchmarks/CodeWalker.Benchmarks.csproj
Normal 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>
|
17
CodeWalker.Benchmarks/Program.cs
Normal file
17
CodeWalker.Benchmarks/Program.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
}
|
36
CodeWalker.Benchmarks/Properties/AssemblyInfo.cs
Normal file
36
CodeWalker.Benchmarks/Properties/AssemblyInfo.cs
Normal 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")]
|
42
CodeWalker.Benchmarks/packages.config
Normal file
42
CodeWalker.Benchmarks/packages.config
Normal 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>
|
@ -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>
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -105,7 +105,26 @@ namespace CodeWalker.GameFiles
|
||||
RpfFileEntry = entry;
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
public unsafe string ReadString(BinaryReader br, int length, bool ignoreNullTerminator = false)
|
||||
{
|
||||
var bytes = stackalloc char[length];
|
||||
var currentLength = 0;
|
||||
while (currentLength < length)
|
||||
{
|
||||
var c = (char)br.ReadByte();
|
||||
if (c != 0)
|
||||
{
|
||||
bytes[currentLength] = c;
|
||||
currentLength++;
|
||||
}
|
||||
else if (!ignoreNullTerminator)
|
||||
break;
|
||||
}
|
||||
|
||||
return new string(bytes, 0, currentLength);
|
||||
}
|
||||
|
||||
public unsafe void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
RawFileData = data;
|
||||
if (entry != null)
|
||||
@ -114,8 +133,8 @@ namespace CodeWalker.GameFiles
|
||||
Name = entry.Name;
|
||||
}
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
using MemoryStream ms = new MemoryStream(data);
|
||||
using BinaryReader br = new BinaryReader(ms);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
RelType = (RelDatFileType)br.ReadUInt32(); //type
|
||||
@ -134,19 +153,25 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
NameTableOffsets = ntoffsets;
|
||||
string[] names = new string[NameTableCount];
|
||||
var remainingLength = (int)NameTableLength;
|
||||
for (uint i = 0; i < NameTableCount; i++)
|
||||
{
|
||||
sb.Clear();
|
||||
while (true)
|
||||
int length = 0;
|
||||
if (i < NameTableCount - 1)
|
||||
{
|
||||
char c = (char)br.ReadByte();
|
||||
if (c != 0) sb.Append(c);
|
||||
else break;
|
||||
length = (int)(NameTableOffsets[i + 1] - NameTableOffsets[i]);
|
||||
|
||||
}
|
||||
names[i] = sb.ToString();
|
||||
else
|
||||
{
|
||||
length = remainingLength;
|
||||
}
|
||||
|
||||
names[i] = ReadString(br, length);
|
||||
|
||||
//JenkIndex.Ensure(names[i]); //really need both here..?
|
||||
JenkIndex.Ensure(names[i].ToLowerInvariant());
|
||||
JenkIndex.EnsureLower(names[i]);
|
||||
remainingLength -= length;
|
||||
}
|
||||
NameTable = names;
|
||||
}
|
||||
@ -164,18 +189,13 @@ namespace CodeWalker.GameFiles
|
||||
for (uint i = 0; i < IndexCount; i++)
|
||||
{
|
||||
byte sl = br.ReadByte();
|
||||
sb.Clear();
|
||||
for (int j = 0; j < sl; j++)
|
||||
{
|
||||
char c = (char)br.ReadByte();
|
||||
if (c != 0) sb.Append(c);
|
||||
}
|
||||
var str = ReadString(br, (int)sl, true);
|
||||
RelIndexString ristr = new RelIndexString();
|
||||
ristr.Name = sb.ToString();
|
||||
ristr.Name = str;
|
||||
ristr.Offset = br.ReadUInt32();
|
||||
ristr.Length = br.ReadUInt32();
|
||||
indexstrs[i] = ristr;
|
||||
JenkIndex.Ensure(ristr.Name.ToLowerInvariant());
|
||||
JenkIndex.EnsureLower(ristr.Name);
|
||||
}
|
||||
IndexStrings = indexstrs;
|
||||
}
|
||||
@ -235,9 +255,6 @@ namespace CodeWalker.GameFiles
|
||||
{ }
|
||||
//EOF!
|
||||
|
||||
br.Dispose();
|
||||
ms.Dispose();
|
||||
|
||||
|
||||
ParseDataBlock();
|
||||
|
||||
@ -253,8 +270,8 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
|
||||
|
||||
MemoryStream ms = new MemoryStream(DataBlock);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
using MemoryStream ms = new MemoryStream(DataBlock);
|
||||
using BinaryReader br = new BinaryReader(ms);
|
||||
|
||||
DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version? flags?
|
||||
#region DataUnkVal unk values test
|
||||
@ -315,12 +332,6 @@ namespace CodeWalker.GameFiles
|
||||
RelDatasSorted = reldatas.ToArray();
|
||||
|
||||
|
||||
br.Dispose();
|
||||
ms.Dispose();
|
||||
|
||||
|
||||
|
||||
|
||||
RelDataDict.Clear();
|
||||
foreach (var reldata in RelDatas)
|
||||
{
|
||||
@ -328,7 +339,7 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
reldata.NameHash = JenkHash.GenHash(reldata.Name); //should this be lower case?
|
||||
JenkIndex.Ensure(reldata.Name);
|
||||
JenkIndex.Ensure(reldata.Name.ToLowerInvariant()); //which one to use?
|
||||
JenkIndex.EnsureLower(reldata.Name); //which one to use?
|
||||
}
|
||||
|
||||
//if (reldata.NameHash == 0)
|
||||
@ -349,24 +360,10 @@ namespace CodeWalker.GameFiles
|
||||
for (int i = 0; i < snd.ChildSoundsCount; i++)
|
||||
{
|
||||
var audhash = snd.ChildSoundsHashes[i];
|
||||
RelData auddata = null;
|
||||
if (RelDataDict.TryGetValue(audhash, out auddata))
|
||||
if (RelDataDict.TryGetValue(audhash, out var auddata))
|
||||
{
|
||||
snd.ChildSounds[i] = auddata;
|
||||
}
|
||||
else
|
||||
{ }
|
||||
}
|
||||
}
|
||||
if (snd.AudioContainers != null)
|
||||
{
|
||||
foreach (var cnt in snd.AudioContainers)
|
||||
{
|
||||
string cname = JenkIndex.TryGetString(cnt.Hash);
|
||||
if (!string.IsNullOrEmpty(cname))
|
||||
{ }
|
||||
else
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -385,8 +382,6 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
speechDict[speechData.DataOffset] = speechData;
|
||||
}
|
||||
else
|
||||
{ }
|
||||
|
||||
speechData.Type = Dat4SpeechType.ByteArray;
|
||||
speechData.TypeID = 0; //will be set again after this
|
||||
@ -397,32 +392,24 @@ namespace CodeWalker.GameFiles
|
||||
var hashOffset = HashTableOffsets[i];
|
||||
var hash = HashTable[i];
|
||||
var itemOffset = hashOffset - 8;
|
||||
Dat4SpeechData speechData = null;
|
||||
speechDict.TryGetValue(itemOffset, out speechData);
|
||||
if (speechData != null)
|
||||
if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
|
||||
{
|
||||
speechData.Type = Dat4SpeechType.Hash;
|
||||
speechData.TypeID = 4;
|
||||
speechData.Hash = hash;
|
||||
}
|
||||
else
|
||||
{ }
|
||||
}
|
||||
for (uint i = 0; i < PackTableCount; i++)
|
||||
{
|
||||
var packOffset = PackTableOffsets[i];
|
||||
var pack = PackTable[i];
|
||||
var itemOffset = packOffset - 12;
|
||||
Dat4SpeechData speechData = null;
|
||||
speechDict.TryGetValue(itemOffset, out speechData);
|
||||
if (speechData != null)
|
||||
if (speechDict.TryGetValue(itemOffset, out var speechData) && speechData != null)
|
||||
{
|
||||
speechData.Type = Dat4SpeechType.Container;
|
||||
speechData.TypeID = 8;
|
||||
speechData.ContainerHash = pack;
|
||||
}
|
||||
else
|
||||
{ }//shouldn't happen!
|
||||
}
|
||||
|
||||
|
||||
@ -456,33 +443,31 @@ namespace CodeWalker.GameFiles
|
||||
d.Data = data;
|
||||
|
||||
|
||||
using (BinaryReader dbr = new BinaryReader(new MemoryStream(data)))
|
||||
{
|
||||
d.ReadType(dbr);
|
||||
using BinaryReader dbr = new BinaryReader(new MemoryStream(data));
|
||||
d.ReadType(dbr);
|
||||
|
||||
switch (RelType)
|
||||
{
|
||||
case RelDatFileType.Dat4: //speech.dat4.rel, audioconfig.dat4.rel
|
||||
return ReadData4(d, dbr);
|
||||
case RelDatFileType.Dat10ModularSynth: //amp.dat10.rel
|
||||
return ReadData10(d, dbr);
|
||||
case RelDatFileType.Dat15DynamicMixer: //mix.dat15.rel
|
||||
return ReadData15(d, dbr);
|
||||
case RelDatFileType.Dat16Curves: //curves.dat16.rel
|
||||
return ReadData16(d, dbr);
|
||||
case RelDatFileType.Dat22Categories: //categories.dat22.rel
|
||||
return ReadData22(d, dbr);
|
||||
case RelDatFileType.Dat54DataEntries: //sounds.dat54.rel
|
||||
return ReadData54(d, dbr);
|
||||
case RelDatFileType.Dat149: //game.dat149.rel
|
||||
return ReadData149(d, dbr);
|
||||
case RelDatFileType.Dat150: //game.dat150.rel
|
||||
return ReadData150(d, dbr);
|
||||
case RelDatFileType.Dat151: //game.dat151.rel
|
||||
return ReadData151(d, dbr);
|
||||
default:
|
||||
return d; //shouldn't get here...
|
||||
}
|
||||
switch (RelType)
|
||||
{
|
||||
case RelDatFileType.Dat4: //speech.dat4.rel, audioconfig.dat4.rel
|
||||
return ReadData4(d, dbr);
|
||||
case RelDatFileType.Dat10ModularSynth: //amp.dat10.rel
|
||||
return ReadData10(d, dbr);
|
||||
case RelDatFileType.Dat15DynamicMixer: //mix.dat15.rel
|
||||
return ReadData15(d, dbr);
|
||||
case RelDatFileType.Dat16Curves: //curves.dat16.rel
|
||||
return ReadData16(d, dbr);
|
||||
case RelDatFileType.Dat22Categories: //categories.dat22.rel
|
||||
return ReadData22(d, dbr);
|
||||
case RelDatFileType.Dat54DataEntries: //sounds.dat54.rel
|
||||
return ReadData54(d, dbr);
|
||||
case RelDatFileType.Dat149: //game.dat149.rel
|
||||
return ReadData149(d, dbr);
|
||||
case RelDatFileType.Dat150: //game.dat150.rel
|
||||
return ReadData150(d, dbr);
|
||||
case RelDatFileType.Dat151: //game.dat151.rel
|
||||
return ReadData151(d, dbr);
|
||||
default:
|
||||
return d; //shouldn't get here...
|
||||
}
|
||||
}
|
||||
|
||||
@ -25334,7 +25319,7 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (str.StartsWith("hash_"))
|
||||
if (str.StartsWith("hash_", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Convert.ToUInt32(str.Substring(5), 16);
|
||||
}
|
||||
|
@ -26,16 +26,14 @@ namespace CodeWalker.GameFiles
|
||||
public static bool Ensure(string str)
|
||||
{
|
||||
uint hash = JenkHash.GenHash(str);
|
||||
if (hash == 0) return true;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.ContainsKey(hash))
|
||||
{
|
||||
Index.Add(hash, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return Ensure(str, hash);
|
||||
}
|
||||
|
||||
public static bool EnsureLower(string str)
|
||||
{
|
||||
uint hash = JenkHash.GenHashLower(str);
|
||||
|
||||
return Ensure(str, hash);
|
||||
}
|
||||
|
||||
public static bool Ensure(string str, uint hash)
|
||||
|
@ -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,29 +777,73 @@ namespace CodeWalker.GameFiles
|
||||
if (getStringArrayList.Count == 0) return null;
|
||||
return getStringArrayList.ToArray();
|
||||
}
|
||||
|
||||
private string[] GetStringItemArray(XmlReader node, string childName)
|
||||
{
|
||||
if (node.IsEmptyElement)
|
||||
{
|
||||
node.ReadStartElement();
|
||||
return null;
|
||||
}
|
||||
|
||||
lock(getStringArrayList)
|
||||
{
|
||||
getStringArrayList.Clear();
|
||||
node.ReadStartElement();
|
||||
while (node.MoveToContent() == XmlNodeType.Element && node.Name == "Item")
|
||||
{
|
||||
var istr = node.ReadElementContentAsString();
|
||||
if (!string.IsNullOrEmpty(istr))
|
||||
{
|
||||
getStringArrayList.Add(istr);
|
||||
}
|
||||
}
|
||||
node.ReadEndElement();
|
||||
|
||||
if (getStringArrayList.Count == 0)
|
||||
return null;
|
||||
return getStringArrayList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private string[] GetStringArray(string ldastr, char delimiter)
|
||||
{
|
||||
var ldarr = ldastr?.Split(delimiter);
|
||||
if (ldarr == null) return null;
|
||||
lock(getStringArrayList)
|
||||
{
|
||||
getStringArrayList.Clear();
|
||||
foreach (var ldstr in ldarr)
|
||||
{
|
||||
var ldt = ldstr?.Trim();
|
||||
if (!string.IsNullOrEmpty(ldt))
|
||||
{
|
||||
getStringArrayList.Add(ldt);
|
||||
}
|
||||
}
|
||||
if (getStringArrayList.Count == 0) return null;
|
||||
return getStringArrayList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
private string[] GetStringArray(XmlNode node, string childName, char delimiter)
|
||||
{
|
||||
var ldastr = Xml.GetChildInnerText(node, childName);
|
||||
var ldarr = ldastr?.Split(delimiter);
|
||||
if (ldarr == null) return null;
|
||||
getStringArrayList.Clear();
|
||||
foreach (var ldstr in ldarr)
|
||||
{
|
||||
var ldt = ldstr?.Trim();
|
||||
if (!string.IsNullOrEmpty(ldt))
|
||||
{
|
||||
getStringArrayList.Add(ldt);
|
||||
}
|
||||
}
|
||||
if (getStringArrayList.Count == 0) return null;
|
||||
return getStringArrayList.ToArray();
|
||||
return GetStringArray(ldastr, delimiter);
|
||||
}
|
||||
private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
|
||||
|
||||
private string[] GetStringArray(XmlReader reader, string childName, char delimiter)
|
||||
{
|
||||
var ldastr = Xml.GetChildInnerText(reader, childName);
|
||||
return GetStringArray(ldastr, delimiter);
|
||||
}
|
||||
|
||||
private unsafe float[] GetFloatArray(string ldastr, char delimiter)
|
||||
{
|
||||
var ldastr = Xml.GetChildInnerText(node, childName);
|
||||
var ldarr = ldastr?.Split(delimiter);
|
||||
if (ldarr == null) return null;
|
||||
getFloatArrayList.Clear();
|
||||
var floats = stackalloc float[ldarr.Length];
|
||||
var i = 0;
|
||||
foreach (var ldstr in ldarr)
|
||||
{
|
||||
var ldt = ldstr?.Trim();
|
||||
@ -358,12 +852,30 @@ namespace CodeWalker.GameFiles
|
||||
float f;
|
||||
if (FloatUtil.TryParse(ldt, out f))
|
||||
{
|
||||
getFloatArrayList.Add(f);
|
||||
floats[i] = f;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getFloatArrayList.Count == 0) return null;
|
||||
return getFloatArrayList.ToArray();
|
||||
if (i == 0) return null;
|
||||
|
||||
var result = new float[i];
|
||||
|
||||
Marshal.Copy((IntPtr)floats, result, 0, i);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private float[] GetFloatArray(XmlNode node, string childName, char delimiter)
|
||||
{
|
||||
var ldastr = Xml.GetChildInnerText(node, childName);
|
||||
return GetFloatArray(ldastr, delimiter);
|
||||
}
|
||||
|
||||
private float[] GetFloatArray(XmlReader reader, string childName, char delimiter)
|
||||
{
|
||||
var ldastr = Xml.GetChildInnerText(reader, childName);
|
||||
return GetFloatArray(ldastr, delimiter);
|
||||
}
|
||||
|
||||
private static List<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 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
|
||||
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 static bool operator !=(VehicleOverrideRagdollThreshold left, VehicleOverrideRagdollThreshold right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
public class VehicleDriver
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>();
|
||||
|
||||
for (int i = 0; i < AllEntities.Length; i++)
|
||||
if (AllEntities != null)
|
||||
{
|
||||
var oent = AllEntities[i];
|
||||
oent.Index = newAllEntities.Count;
|
||||
if (oent != ent) newAllEntities.Add(oent);
|
||||
else if (i != idx)
|
||||
for (int i = 0; i < AllEntities.Length; i++)
|
||||
{
|
||||
res = false; //indexes didn't match.. this shouldn't happen!
|
||||
var oent = AllEntities[i];
|
||||
oent.Index = newAllEntities.Count;
|
||||
if (oent != ent)
|
||||
{
|
||||
newAllEntities.Add(oent);
|
||||
}
|
||||
else if (i != idx)
|
||||
{
|
||||
res = false; //indexes didn't match.. this shouldn't happen!
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < RootEntities.Length; i++)
|
||||
if (RootEntities != null)
|
||||
{
|
||||
var oent = RootEntities[i];
|
||||
if (oent != ent) newRootEntities.Add(oent);
|
||||
for (int i = 0; i < RootEntities.Length; i++)
|
||||
{
|
||||
var oent = RootEntities[i];
|
||||
if (oent != ent) newRootEntities.Add(oent);
|
||||
}
|
||||
}
|
||||
|
||||
if ((AllEntities.Length == newAllEntities.Count) || (RootEntities.Length == newRootEntities.Count))
|
||||
if (AllEntities == null || AllEntities.Length == newAllEntities.Count || RootEntities == null || RootEntities.Length == newRootEntities.Count)
|
||||
{
|
||||
res = false;
|
||||
}
|
||||
@ -1055,7 +1075,8 @@ namespace CodeWalker.GameFiles
|
||||
public void AddCarGen(YmapCarGen cargen)
|
||||
{
|
||||
List<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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -10,6 +10,7 @@ namespace CodeWalker.GameFiles
|
||||
{
|
||||
public volatile bool Loaded = false;
|
||||
public volatile bool LoadQueued = false;
|
||||
public DateTime LastLoadTime = DateTime.MinValue;
|
||||
public RpfFileEntry RpfFileEntry { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FilePath { get; set; } //used by the project form.
|
||||
@ -91,7 +92,7 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
|
||||
|
||||
public struct GameFileCacheKey
|
||||
public struct GameFileCacheKey : IEquatable<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
@ -393,11 +393,11 @@ 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;
|
||||
|
||||
foreach(var objIndex in portal.AttachedObjects)
|
||||
List<uint> newAttachedObjects = new List<uint>();
|
||||
foreach (var objIndex in portal.AttachedObjects)
|
||||
{
|
||||
if (objIndex == deletedIndex) continue;
|
||||
if (objIndex > deletedIndex)
|
||||
@ -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++)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
_Data.attachedObjects = mb.AddUintArrayPtr(AttachedObjects);
|
||||
|
||||
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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -761,11 +761,15 @@ namespace CodeWalker.GameFiles
|
||||
//TODO: NEEDS TO BE TESTED!!!
|
||||
data_items = new T[EntriesCount];
|
||||
var posbckp = reader.Position;
|
||||
reader.Position = (long)EntriesPointer;
|
||||
for (int i = 0; i < EntriesCount; i++)
|
||||
if (EntriesCount > 0)
|
||||
{
|
||||
data_items[i] = reader.ReadBlock<T>();
|
||||
reader.Position = (long)EntriesPointer;
|
||||
for (int i = 0; i < EntriesCount; i++)
|
||||
{
|
||||
data_items[i] = reader.ReadBlock<T>();
|
||||
}
|
||||
}
|
||||
|
||||
reader.Position = posbckp;
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | GRAPHICS_BASE;
|
||||
return buffer;
|
||||
return graphicsStream;
|
||||
}
|
||||
throw new Exception("illegal position!");
|
||||
|
||||
throw new InvalidOperationException("illegal position!");
|
||||
}
|
||||
|
||||
public override byte ReadByte()
|
||||
internal override void SetPositionAfterRead(Stream stream)
|
||||
{
|
||||
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
|
||||
|
||||
if (stream == systemStream)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
// read from system stream...
|
||||
|
||||
systemStream.Position = Position & ~SYSTEM_BASE;
|
||||
|
||||
var readByte = (byte)systemStream.ReadByte();
|
||||
|
||||
Position = systemStream.Position | SYSTEM_BASE;
|
||||
return readByte;
|
||||
|
||||
position = systemStream.Position | SYSTEM_BASE;
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
else if (stream == graphicsStream)
|
||||
{
|
||||
// read from graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~GRAPHICS_BASE;
|
||||
|
||||
var readByte = (byte)graphicsStream.ReadByte();
|
||||
|
||||
Position = graphicsStream.Position | GRAPHICS_BASE;
|
||||
return readByte;
|
||||
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);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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 data = ArrayPool<byte>.Shared.Rent((int)length);
|
||||
try
|
||||
{
|
||||
ReadBytesAt(position, length, false, data);
|
||||
|
||||
//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];
|
||||
|
||||
//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 resultSpan = MemoryMarshal.Cast<byte, T>(data.AsSpan(0, (int)length));
|
||||
resultSpan.CopyTo(result);
|
||||
|
||||
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();
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(data);
|
||||
}
|
||||
}
|
||||
public T[] ReadStructs<T>(uint count)
|
||||
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);
|
||||
var data = ArrayPool<byte>.Shared.Rent((int)length);
|
||||
try
|
||||
{
|
||||
ReadBytes((int)length, data);
|
||||
|
||||
//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 resultSpan = MemoryMarshal.Cast<byte, T>(data.AsSpan(0, (int)length));
|
||||
resultSpan.CopyTo(result);
|
||||
|
||||
GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
Marshal.Copy(data, 0, h, (int)length);
|
||||
handle.Free();
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(data);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
systemStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
systemStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = systemStream.Position | 0x50000000;
|
||||
return;
|
||||
|
||||
return systemStream;
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
{
|
||||
// write to graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~GRAPHICS_BASE;
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
graphicsStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicsStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | 0x60000000;
|
||||
return;
|
||||
return graphicsStream;
|
||||
}
|
||||
throw new InvalidOperationException("illegal position!");
|
||||
}
|
||||
|
||||
throw new Exception("illegal position!");
|
||||
internal override void SetPositionAfterWrite(Stream stream)
|
||||
{
|
||||
if (stream == systemStream)
|
||||
{
|
||||
position = systemStream.Position | SYSTEM_BASE;
|
||||
}
|
||||
else if (stream == graphicsStream)
|
||||
{
|
||||
position = graphicsStream.Position | GRAPHICS_BASE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,106 +607,230 @@ 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)
|
||||
{
|
||||
uint offset = 0;// 0x10;
|
||||
uint totlen = (uint)l - offset;
|
||||
|
||||
byte[] tbytes = new byte[totlen];
|
||||
|
||||
br.BaseStream.Position += offset;
|
||||
br.Read(tbytes, 0, (int)totlen);
|
||||
|
||||
if (entry.IsEncrypted)
|
||||
{
|
||||
if (IsAESEncrypted)
|
||||
{
|
||||
GTACrypto.DecryptAES(tbytes);
|
||||
}
|
||||
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);
|
||||
}
|
||||
//else
|
||||
//{ }
|
||||
}
|
||||
|
||||
byte[] defl = tbytes;
|
||||
|
||||
if (entry.FileSize > 0) //apparently this means it's compressed
|
||||
{
|
||||
defl = DecompressBytes(tbytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
return defl;
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
uint offset = 0;// 0x10;
|
||||
uint totlen = (uint)l - 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.FileUncompressedSize, 0, (int)totlen);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public async ValueTask<byte[]> ExtractFileResourceAsync(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;
|
||||
|
||||
await br.ReadAsync(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 = await DecompressBytesAsync(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 byte[] ExtractFileResource(RpfResourceFileEntry entry, BinaryReader br)
|
||||
{
|
||||
br.BaseStream.Position = StartPos + ((long)entry.FileOffset * 512);
|
||||
|
||||
|
||||
if (entry.FileSize > 0)
|
||||
if (entry.FileSize <= 0)
|
||||
{
|
||||
uint offset = 0x10;
|
||||
uint totlen = entry.FileSize - offset;
|
||||
|
||||
byte[] tbytes = new byte[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);
|
||||
if (entry.IsEncrypted)
|
||||
{
|
||||
if (IsAESEncrypted)
|
||||
{
|
||||
GTACrypto.DecryptAES(tbytes);
|
||||
}
|
||||
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);
|
||||
}
|
||||
//else
|
||||
//{ }
|
||||
}
|
||||
|
||||
byte[] deflated = DecompressBytes(tbytes);
|
||||
|
||||
byte[] data = null;
|
||||
|
||||
if (deflated != null)
|
||||
{
|
||||
data = deflated;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.FileSize -= offset;
|
||||
data = tbytes;
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
uint offset = 0x10;
|
||||
uint totlen = entry.FileSize - offset;
|
||||
|
||||
byte[] tbytes = ArrayPool<byte>.Shared.Rent((int)totlen);
|
||||
|
||||
|
||||
br.BaseStream.Position += offset;
|
||||
|
||||
|
||||
br.Read(tbytes, 0, (int)totlen);
|
||||
if (entry.IsEncrypted)
|
||||
{
|
||||
if (IsAESEncrypted)
|
||||
{
|
||||
GTACrypto.DecryptAES(tbytes, (int)totlen);
|
||||
}
|
||||
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
|
||||
{
|
||||
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] deflated = DecompressBytes(tbytes);
|
||||
|
||||
byte[] data;
|
||||
if (deflated != null)
|
||||
{
|
||||
data = deflated;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.FileSize -= offset;
|
||||
|
||||
data = new byte[(int)totlen];
|
||||
Buffer.BlockCopy(tbytes, 0, data, 0, (int)totlen);
|
||||
}
|
||||
|
||||
ArrayPool<byte>.Shared.Return(tbytes);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static T GetFile<T>(RpfEntry e) where T : class, PackedFile, new()
|
||||
@ -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,29 +1177,25 @@ 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 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)
|
||||
{
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
LastError = "Warning: Decompressed data was smaller than compressed data...";
|
||||
//return null; //could still be OK for tiny things!
|
||||
}
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1056,6 +1204,39 @@ namespace CodeWalker.GameFiles
|
||||
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();
|
||||
LastException = ex;
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
shortName = Name;
|
||||
char ch = Name[i];
|
||||
if (ch == '.')
|
||||
{
|
||||
Interlocked.Increment(ref ExtensionHits);
|
||||
shortName = Name.Substring(0, i);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
@ -367,8 +390,40 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
if (data != null)
|
||||
{
|
||||
file.Load(data, entry);
|
||||
return true;
|
||||
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 (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));
|
||||
//}
|
||||
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
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,55 +159,94 @@ namespace CodeWalker.Utils
|
||||
|
||||
bool swaprb = true;
|
||||
|
||||
switch (format)
|
||||
if (DirectXTexNet.TexHelper.Instance.IsCompressed((DirectXTexNet.DXGI_FORMAT)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;
|
||||
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...
|
||||
px = Decompress(imgdata, w, h, format);
|
||||
} else
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
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:
|
||||
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))
|
||||
{
|
||||
int pxc = px.Length / 4;// w * h;
|
||||
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)char.ToLowerInvariant(text[i]);
|
||||
h += (byte)ToLower(text[i]);
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
public static uint GenHashLower(ReadOnlySpan<char> text, ReadOnlySpan<char> str2 = default)
|
||||
{
|
||||
if (text == null) return 0;
|
||||
uint h = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
h += (byte)ToLower(text[i]);
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
for (int i = 0; i < str2.Length; i++)
|
||||
{
|
||||
h += (byte)ToLower(str2[i]);
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
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))
|
||||
{
|
||||
lock(loadedListLock)
|
||||
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;
|
||||
}
|
||||
lock(loadedListLock)
|
||||
|
||||
if (loadedListDict.TryRemove(key, out var n))
|
||||
{
|
||||
LinkedListNode<TVal> n;
|
||||
if (loadedListDict.TryGetValue(key, out n))
|
||||
lock (loadedListLock)
|
||||
{
|
||||
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;
|
||||
|
167
CodeWalker.Core/Utils/StreamingExtensions.cs
Normal file
167
CodeWalker.Core/Utils/StreamingExtensions.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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];
|
||||
}
|
||||
bytes = newb; //trim starting byte order mark
|
||||
start = 3;
|
||||
length = bytes.Length - 3;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -33,15 +33,23 @@ namespace CodeWalker.World
|
||||
|
||||
List<AudioPlacement> placements = new List<AudioPlacement>();
|
||||
|
||||
foreach (var relfile in GameFileCache.AudioDatRelFiles)
|
||||
GameFileCache.AudioDatRelFilesLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
if (relfile == null) continue;
|
||||
foreach (var relfile in GameFileCache.AudioDatRelFiles)
|
||||
{
|
||||
if (relfile == null) continue;
|
||||
|
||||
placements.Clear();
|
||||
placements.Clear();
|
||||
|
||||
CreatePlacements(relfile, placements, true);
|
||||
CreatePlacements(relfile, placements, true);
|
||||
|
||||
PlacementsDict[relfile] = placements.ToArray();
|
||||
PlacementsDict[relfile] = placements.ToArray();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
GameFileCache.AudioDatRelFilesLock.ExitReadLock();
|
||||
}
|
||||
|
||||
AllItems.AddRange(Zones);
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
{ }
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -16,7 +16,10 @@
|
||||
<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" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
|
@ -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");
|
||||
ConsoleWindow.Hide();
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new ExploreForm());
|
||||
try
|
||||
{
|
||||
if (!NamedPipe.TrySendMessageToOtherProcess("explorer"))
|
||||
{
|
||||
ConsoleWindow.Hide();
|
||||
//Process.Start("CodeWalker.exe", "explorer");
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
|
||||
var form = new ExploreForm();
|
||||
var namedPipe = new NamedPipe(form);
|
||||
namedPipe.Init();
|
||||
|
||||
Application.Run(form);
|
||||
|
||||
GTAFolder.UpdateSettings();
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
GTAFolder.UpdateSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
CodeWalker.Test/BinaryPrimitesFloats.cs
Normal file
12
CodeWalker.Test/BinaryPrimitesFloats.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
30
CodeWalker.Test/CodeWalker.Test.csproj
Normal file
30
CodeWalker.Test/CodeWalker.Test.csproj
Normal 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>
|
96
CodeWalker.Test/JenkGenTests.cs
Normal file
96
CodeWalker.Test/JenkGenTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
445
CodeWalker.Test/TestBinaryConversions.cs
Normal file
445
CodeWalker.Test/TestBinaryConversions.cs
Normal file
File diff suppressed because one or more lines are too long
1081
CodeWalker.Test/XmlTests.cs
Normal file
1081
CodeWalker.Test/XmlTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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" />
|
||||
|
@ -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
|
||||
|
@ -9,17 +9,23 @@
|
||||
<Copyright>dexyfex</Copyright>
|
||||
<Company>dexyfex software</Company>
|
||||
<Authors>dexyfex</Authors>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<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="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" />
|
||||
|
16
CodeWalker/ExploreForm.Designer.cs
generated
16
CodeWalker/ExploreForm.Designer.cs
generated
@ -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
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,31 +162,40 @@ namespace CodeWalker.Forms
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
throw ex;
|
||||
throw;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Task.Run(() =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (!IsDisposed) //run the file cache content thread until the form exits.
|
||||
try
|
||||
{
|
||||
if (gameFileCache.IsInited)
|
||||
while (!IsDisposed) //run the file cache content thread until the form exits.
|
||||
{
|
||||
gameFileCache.BeginFrame();
|
||||
|
||||
bool fcItemsPending = gameFileCache.ContentThreadProc();
|
||||
|
||||
if (!fcItemsPending)
|
||||
if (gameFileCache.IsInited)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
gameFileCache.BeginFrame();
|
||||
|
||||
bool fcItemsPending = gameFileCache.ContentThreadProc();
|
||||
|
||||
if (!fcItemsPending)
|
||||
{
|
||||
await Task.Delay(10);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(20);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread.Sleep(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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
5
CodeWalker/Forms/YtdForm.Designer.cs
generated
5
CodeWalker/Forms/YtdForm.Designer.cs
generated
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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,63 +210,75 @@ 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
|
||||
|
||||
UpdateControlInputs(elapsed);
|
||||
//space.Update(elapsed);
|
||||
try
|
||||
{
|
||||
UpdateControlInputs(elapsed);
|
||||
//space.Update(elapsed);
|
||||
|
||||
Renderer.Update(elapsed, MouseLastPoint.X, MouseLastPoint.Y);
|
||||
Renderer.Update(elapsed, MouseLastPoint.X, MouseLastPoint.Y);
|
||||
|
||||
|
||||
|
||||
//UpdateWidgets();
|
||||
//BeginMouseHitTest();
|
||||
//UpdateWidgets();
|
||||
//BeginMouseHitTest();
|
||||
|
||||
|
||||
|
||||
|
||||
Renderer.BeginRender(context);
|
||||
Renderer.BeginRender(context);
|
||||
|
||||
Renderer.RenderSkyAndClouds();
|
||||
Renderer.RenderSkyAndClouds();
|
||||
|
||||
Renderer.SelectedDrawable = null;// SelectedItem.Drawable;
|
||||
Renderer.SelectedDrawable = null;// SelectedItem.Drawable;
|
||||
|
||||
|
||||
Renderer.RenderPed(SelectedPed);
|
||||
Renderer.RenderPed(SelectedPed);
|
||||
|
||||
//UpdateMouseHitsFromRenderer();
|
||||
//RenderSelection();
|
||||
//UpdateMouseHitsFromRenderer();
|
||||
//RenderSelection();
|
||||
|
||||
|
||||
RenderGrid(context);
|
||||
RenderGrid(context);
|
||||
|
||||
|
||||
Renderer.RenderQueued();
|
||||
Renderer.RenderQueued();
|
||||
|
||||
//Renderer.RenderBounds(MapSelectionMode.Entity);
|
||||
//Renderer.RenderBounds(MapSelectionMode.Entity);
|
||||
|
||||
//Renderer.RenderSelectionGeometry(MapSelectionMode.Entity);
|
||||
//Renderer.RenderSelectionGeometry(MapSelectionMode.Entity);
|
||||
|
||||
//RenderMoused();
|
||||
//RenderMoused();
|
||||
|
||||
Renderer.RenderFinalPass();
|
||||
Renderer.RenderFinalPass();
|
||||
|
||||
//RenderMarkers();
|
||||
//RenderWidgets();
|
||||
//RenderMarkers();
|
||||
//RenderWidgets();
|
||||
|
||||
Renderer.EndRender();
|
||||
|
||||
Monitor.Exit(Renderer.RenderSyncRoot);
|
||||
Renderer.EndRender();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(Renderer.RenderSyncRoot);
|
||||
}
|
||||
|
||||
//UpdateMarkerSelectionPanelInvoke();
|
||||
}
|
||||
@ -324,74 +339,89 @@ namespace CodeWalker
|
||||
|
||||
private void ContentThread()
|
||||
{
|
||||
//main content loading thread.
|
||||
running = true;
|
||||
|
||||
UpdateStatus("Scanning...");
|
||||
|
||||
try
|
||||
{
|
||||
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("Keys not found! This shouldn't happen.");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
//main content loading thread.
|
||||
running = true;
|
||||
|
||||
GameFileCache.EnableDlc = true;
|
||||
GameFileCache.EnableMods = true;
|
||||
GameFileCache.LoadPeds = true;
|
||||
GameFileCache.LoadVehicles = false;
|
||||
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);
|
||||
UpdateStatus("Scanning...");
|
||||
|
||||
//UpdateDlcListComboBox(gameFileCache.DlcNameList);
|
||||
|
||||
//EnableCacheDependentUI();
|
||||
|
||||
UpdateGlobalPedsUI();
|
||||
|
||||
|
||||
LoadWorld();
|
||||
|
||||
|
||||
|
||||
//initialised = true;
|
||||
|
||||
//EnableDLCModsUI();
|
||||
|
||||
//UpdateStatus("Ready");
|
||||
|
||||
|
||||
Task.Run(() => {
|
||||
while (formopen && !IsDisposed) //renderer content loop
|
||||
try
|
||||
{
|
||||
bool rcItemsPending = Renderer.ContentThreadProc();
|
||||
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
MessageBox.Show("Keys not found! This shouldn't happen.");
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rcItemsPending)
|
||||
if (!GameFileCache.IsInited)
|
||||
{
|
||||
GameFileCache.EnableDlc = true;
|
||||
GameFileCache.EnableMods = true;
|
||||
GameFileCache.LoadPeds = true;
|
||||
GameFileCache.LoadVehicles = false;
|
||||
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, force: false);
|
||||
}
|
||||
|
||||
//UpdateDlcListComboBox(gameFileCache.DlcNameList);
|
||||
|
||||
//EnableCacheDependentUI();
|
||||
|
||||
UpdateGlobalPedsUI();
|
||||
|
||||
|
||||
LoadWorld();
|
||||
|
||||
|
||||
|
||||
//initialised = true;
|
||||
|
||||
//EnableDLCModsUI();
|
||||
|
||||
//UpdateStatus("Ready");
|
||||
|
||||
|
||||
Task.Run(async () => {
|
||||
while (formopen && !IsDisposed) //renderer content loop
|
||||
{
|
||||
Thread.Sleep(1); //sleep if there's nothing to do
|
||||
bool rcItemsPending = Renderer.ContentThreadProc();
|
||||
|
||||
if (!rcItemsPending)
|
||||
{
|
||||
await Task.Delay(ActiveForm == null ? 50 : 1).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
while (formopen && !IsDisposed) //main asset loop
|
||||
{
|
||||
bool fcItemsPending = GameFileCache.ContentThreadProc();
|
||||
|
||||
if (!fcItemsPending)
|
||||
Task.Run(async () =>
|
||||
{
|
||||
Thread.Sleep(1); //sleep if there's nothing to do
|
||||
}
|
||||
while (formopen && !IsDisposed) //main asset loop
|
||||
{
|
||||
bool fcItemsPending = GameFileCache.ContentThreadProc();
|
||||
|
||||
if (!fcItemsPending)
|
||||
{
|
||||
await Task.Delay(ActiveForm == null ? 50 : 1).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
running = false;
|
||||
});
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Exception occured in PedsForm::ContentThread.\n{ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
GameFileCache.Clear();
|
||||
|
||||
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 = "";
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
3
CodeWalker/Properties/Settings.Designer.cs
generated
3
CodeWalker/Properties/Settings.Designer.cs
generated
@ -8,6 +8,9 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace CodeWalker.Properties {
|
||||
|
||||
|
||||
|
@ -2,7 +2,8 @@
|
||||
"profiles": {
|
||||
"CodeWalker": {
|
||||
"commandName": "Project",
|
||||
"workingDirectory": ".."
|
||||
"workingDirectory": "..",
|
||||
"nativeDebugging": true
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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,31 +154,38 @@ namespace CodeWalker.Rendering
|
||||
|
||||
private void Cleanup()
|
||||
{
|
||||
Running = false;
|
||||
int count = 0;
|
||||
while (Rendering && (count < 1000))
|
||||
try
|
||||
{
|
||||
Thread.Sleep(1); //try to gracefully exit...
|
||||
count++;
|
||||
using var _ = new DisposableTimer("DXManager Cleanup");
|
||||
Running = false;
|
||||
int count = 0;
|
||||
while (Rendering && (count < 1000))
|
||||
{
|
||||
Thread.Sleep(1); //try to gracefully exit...
|
||||
count++;
|
||||
}
|
||||
|
||||
dxform.CleanupScene();
|
||||
|
||||
if (context != null) context.ClearState();
|
||||
|
||||
//dipose of all objects
|
||||
if (depthview != null) depthview.Dispose();
|
||||
if (depthbuffer != null) depthbuffer.Dispose();
|
||||
if (targetview != null) targetview.Dispose();
|
||||
if (backbuffer != null) backbuffer.Dispose();
|
||||
if (swapchain != null) swapchain.Dispose();
|
||||
if (context != null) context.Dispose();
|
||||
|
||||
//var objs = SharpDX.Diagnostics.ObjectTracker.FindActiveObjects();
|
||||
|
||||
if (device != null) device.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
}
|
||||
|
||||
dxform.CleanupScene();
|
||||
|
||||
if (context != null) context.ClearState();
|
||||
|
||||
//dipose of all objects
|
||||
if (depthview != null) depthview.Dispose();
|
||||
if (depthbuffer != null) depthbuffer.Dispose();
|
||||
if (targetview != null) targetview.Dispose();
|
||||
if (backbuffer != null) backbuffer.Dispose();
|
||||
if (swapchain != null) swapchain.Dispose();
|
||||
if (context != null) context.Dispose();
|
||||
|
||||
//var objs = SharpDX.Diagnostics.ObjectTracker.FindActiveObjects();
|
||||
|
||||
if (device != null) device.Dispose();
|
||||
|
||||
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,86 +337,130 @@ namespace CodeWalker.Rendering
|
||||
// await Task.Delay(5000);
|
||||
// }
|
||||
//});
|
||||
Thread.CurrentThread.Name = "RenderLoop";
|
||||
while (Running)
|
||||
|
||||
try
|
||||
{
|
||||
while (Resizing)
|
||||
while (Running)
|
||||
{
|
||||
swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw
|
||||
}
|
||||
if (dxform.Form.WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
dxform.Pauserendering = true;
|
||||
|
||||
Console.WriteLine("Window is minimized");
|
||||
dxform.RenderScene(context);
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
while (dxform.Form.WindowState == FormWindowState.Minimized)
|
||||
while (Resizing)
|
||||
{
|
||||
Thread.Sleep(100); //don't hog CPU when minimised
|
||||
if (dxform.Form.IsDisposed) return; //if closed while minimised
|
||||
swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw
|
||||
}
|
||||
dxform.Pauserendering = false;
|
||||
Console.WriteLine("Window is maximized");
|
||||
}
|
||||
if (dxform.Form.WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
dxform.Pauserendering = true;
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
Console.WriteLine("Window is minimized");
|
||||
await dxform.RenderScene(context);
|
||||
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
|
||||
while (dxform.Form.WindowState == FormWindowState.Minimized)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
Rendering = true;
|
||||
try
|
||||
{
|
||||
if (!Monitor.TryEnter(syncroot, 50))
|
||||
|
||||
if (Form.ActiveForm == null)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
if (dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
Rendering = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
context.OutputMerger.SetRenderTargets(depthview, targetview);
|
||||
context.Rasterizer.SetViewport(0, 0, dxform.Form.ClientSize.Width, dxform.Form.ClientSize.Height);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error setting main render target!\n" + ex.ToString());
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (dxform.Form.IsDisposed)
|
||||
{
|
||||
Monitor.Exit(syncroot);
|
||||
Rendering = false;
|
||||
return; //the form was closed... stop!!
|
||||
}
|
||||
|
||||
dxform.RenderScene(context);
|
||||
bool ok = true;
|
||||
|
||||
try
|
||||
{
|
||||
swapchain.Present(1, PresentFlags.None);
|
||||
context.OutputMerger.SetRenderTargets(depthview, targetview);
|
||||
context.Rasterizer.SetViewport(0, 0, dxform.Form.ClientSize.Width, dxform.Form.ClientSize.Height);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
|
||||
MessageBox.Show("Error setting main render target!\n" + ex.ToString());
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
if (dxform.Form.IsDisposed || cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
||||
Rendering = false;
|
||||
return; //the form was closed... stop!!
|
||||
}
|
||||
|
||||
await dxform.RenderScene(context);
|
||||
|
||||
try
|
||||
{
|
||||
swapchain.Present(1, PresentFlags.None);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
Rendering = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(syncroot);
|
||||
Rendering = false;
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException) { }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,56 +138,65 @@ namespace CodeWalker.Rendering
|
||||
if (currentDevice == null) return false; //can't do anything with no device
|
||||
|
||||
Monitor.Enter(updateSyncRoot);
|
||||
|
||||
|
||||
//load the queued items if possible
|
||||
int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int boundcompcount = boundcomps.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int instbatchcount = instbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int lodlightcount = lodlights.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int distlodlightcount = distlodlights.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int pathbatchcount = pathbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int waterquadcount = waterquads.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
|
||||
|
||||
bool itemsStillPending =
|
||||
(renderablecount >= MaxItemsPerLoop) ||
|
||||
(texturecount >= MaxItemsPerLoop) ||
|
||||
(boundcompcount >= MaxItemsPerLoop) ||
|
||||
(instbatchcount >= MaxItemsPerLoop) ||
|
||||
(lodlightcount >= MaxItemsPerLoop) ||
|
||||
(distlodlightcount >= MaxItemsPerLoop) ||
|
||||
(pathbatchcount >= MaxItemsPerLoop) ||
|
||||
(waterquadcount >= MaxItemsPerLoop);
|
||||
|
||||
|
||||
//todo: change this to unload only when necessary (ie when something is loaded)
|
||||
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
|
||||
try
|
||||
{
|
||||
//load the queued items if possible
|
||||
int renderablecount = renderables.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int texturecount = textures.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int boundcompcount = boundcomps.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int instbatchcount = instbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int lodlightcount = lodlights.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int distlodlightcount = distlodlights.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int pathbatchcount = pathbatches.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
int waterquadcount = waterquads.LoadProc(currentDevice, MaxItemsPerLoop);
|
||||
|
||||
//unload items that haven't been used in longer than the cache period.
|
||||
renderables.UnloadProc();
|
||||
textures.UnloadProc();
|
||||
boundcomps.UnloadProc();
|
||||
instbatches.UnloadProc();
|
||||
lodlights.UnloadProc();
|
||||
distlodlights.UnloadProc();
|
||||
pathbatches.UnloadProc();
|
||||
waterquads.UnloadProc();
|
||||
|
||||
LastUnload = DateTime.UtcNow;
|
||||
bool itemsStillPending =
|
||||
(renderablecount >= MaxItemsPerLoop) ||
|
||||
(texturecount >= MaxItemsPerLoop) ||
|
||||
(boundcompcount >= MaxItemsPerLoop) ||
|
||||
(instbatchcount >= MaxItemsPerLoop) ||
|
||||
(lodlightcount >= MaxItemsPerLoop) ||
|
||||
(distlodlightcount >= MaxItemsPerLoop) ||
|
||||
(pathbatchcount >= MaxItemsPerLoop) ||
|
||||
(waterquadcount >= MaxItemsPerLoop);
|
||||
|
||||
|
||||
//todo: change this to unload only when necessary (ie when something is loaded)
|
||||
var now = DateTime.UtcNow;
|
||||
var deltat = (now - LastUpdate).TotalSeconds;
|
||||
var unloadt = (now - LastUnload).TotalSeconds;
|
||||
//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.
|
||||
renderables.UnloadProc();
|
||||
textures.UnloadProc();
|
||||
boundcomps.UnloadProc();
|
||||
instbatches.UnloadProc();
|
||||
lodlights.UnloadProc();
|
||||
distlodlights.UnloadProc();
|
||||
pathbatches.UnloadProc();
|
||||
waterquads.UnloadProc();
|
||||
|
||||
LastUnload = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
|
||||
LastUpdate = DateTime.UtcNow;
|
||||
|
||||
return itemsStillPending;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(updateSyncRoot);
|
||||
}
|
||||
|
||||
|
||||
LastUpdate = DateTime.UtcNow;
|
||||
|
||||
Monitor.Exit(updateSyncRoot);
|
||||
|
||||
return itemsStillPending;
|
||||
}
|
||||
|
||||
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,26 +355,39 @@ namespace CodeWalker.Rendering
|
||||
LoadedCount = 0;
|
||||
while (itemsToLoad.TryDequeue(out item))
|
||||
{
|
||||
if (item.IsLoaded) continue; //don't load it again...
|
||||
LoadedCount++;
|
||||
long gcachefree = CacheLimit - Interlocked.Read(ref CacheUse);// CacheUse;
|
||||
if (gcachefree > item.DataSize)
|
||||
if (item.IsLoaded)
|
||||
{
|
||||
try
|
||||
item.LoadQueued = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LoadedCount++;
|
||||
long gcachefree = CacheLimit - Interlocked.Read(ref CacheUse);// CacheUse;
|
||||
if (gcachefree > item.DataSize)
|
||||
{
|
||||
item.Load(device);
|
||||
loadeditems.AddLast(item);
|
||||
Interlocked.Add(ref CacheUse, item.DataSize);
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//todo: error handling...
|
||||
try
|
||||
{
|
||||
item.Load(device);
|
||||
loadeditems.AddLast(item);
|
||||
Interlocked.Add(ref CacheUse, item.DataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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;
|
||||
|
@ -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);
|
||||
|
||||
return $"Drawn: {rgc} geom, Loaded: {crc} dr, {ctc} tx, Vram: {vram}, Fps: {fps}";
|
||||
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}, 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,7 +2396,10 @@ namespace CodeWalker.Rendering
|
||||
{
|
||||
var drawable = gameFileCache.TryGetDrawable(arch);
|
||||
rndbl = TryGetRenderable(arch, drawable);
|
||||
ArchetypeRenderables[arch] = rndbl;
|
||||
if (rndbl != null && rndbl.IsLoaded)
|
||||
{
|
||||
ArchetypeRenderables[arch] = rndbl;
|
||||
}
|
||||
}
|
||||
if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload))
|
||||
{
|
||||
@ -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,32 +3032,34 @@ namespace CodeWalker.Rendering
|
||||
rndbl = TryGetRenderable(arche, drawable);
|
||||
}
|
||||
|
||||
if (rndbl != null)
|
||||
if (rndbl == null || !rndbl.IsLoaded)
|
||||
{
|
||||
if (animClip != null)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animClip != null)
|
||||
{
|
||||
rndbl.ClipMapEntry = animClip;
|
||||
rndbl.ClipDict = animClip.Clip?.Ycd;
|
||||
rndbl.HasAnims = true;
|
||||
}
|
||||
|
||||
|
||||
res = RenderRenderable(rndbl, arche, entity);
|
||||
|
||||
|
||||
//fragments have extra drawables! need to render those too... TODO: handle fragments properly...
|
||||
FragDrawable fd = rndbl.Key as FragDrawable;
|
||||
if (fd != null)
|
||||
{
|
||||
var frag = fd.OwnerFragment;
|
||||
if ((frag != null) && (frag.DrawableCloth != null)) //cloth...
|
||||
{
|
||||
rndbl.ClipMapEntry = animClip;
|
||||
rndbl.ClipDict = animClip.Clip?.Ycd;
|
||||
rndbl.HasAnims = true;
|
||||
}
|
||||
|
||||
|
||||
res = RenderRenderable(rndbl, arche, entity);
|
||||
|
||||
|
||||
//fragments have extra drawables! need to render those too... TODO: handle fragments properly...
|
||||
FragDrawable fd = rndbl.Key as FragDrawable;
|
||||
if (fd != null)
|
||||
{
|
||||
var frag = fd.OwnerFragment;
|
||||
if ((frag != null) && (frag.DrawableCloth != null)) //cloth...
|
||||
rndbl = TryGetRenderable(arche, frag.DrawableCloth);
|
||||
if (rndbl != null && rndbl.IsLoaded)
|
||||
{
|
||||
rndbl = TryGetRenderable(arche, frag.DrawableCloth);
|
||||
if (rndbl != null)
|
||||
{
|
||||
bool res2 = RenderRenderable(rndbl, arche, entity);
|
||||
res = res || res2;
|
||||
}
|
||||
bool res2 = RenderRenderable(rndbl, arche, entity);
|
||||
res = res || res2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -440,7 +440,7 @@ namespace CodeWalker.Tools
|
||||
|
||||
curfile++;
|
||||
|
||||
if (fentry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
|
||||
if (fentry.IsExtension(".rpf"))
|
||||
{ continue; }
|
||||
|
||||
if (onlyexts != null)
|
||||
|
6
CodeWalker/Tools/BrowseForm.Designer.cs
generated
6
CodeWalker/Tools/BrowseForm.Designer.cs
generated
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user