Update to .NET 6

Added Span support in some places already, and changed Nullable to annotations to declare intent to enable nullable at some point in the future
This commit is contained in:
Niek Schoemaker 2023-11-14 16:16:59 +01:00
parent ba72dadd16
commit 8c2e444049
No known key found for this signature in database
GPG Key ID: BDF9404CFECB0006
70 changed files with 2197 additions and 2825 deletions

View File

@ -2,27 +2,25 @@
using BenchmarkDotNet.Configs; using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments; using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Jobs; using BenchmarkDotNet.Jobs;
using CodeWalker.Core.Utils;
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using System; using System;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.Benchmarks namespace CodeWalker.Benchmarks
{ {
[MemoryDiagnoser] [MemoryDiagnoser]
[Config(typeof(JitsConfig))]
public class Benchmarks public class Benchmarks
{ {
private class JitsConfig : ManualConfig public const string markup = @"<CVehicleModelInfo__InitDataList>
{
public JitsConfig()
{
AddJob(Job.Default.WithJit(Jit.RyuJit).WithPlatform(Platform.X64));
}
}
public static string markup = @"<CVehicleModelInfo__InitDataList>
<residentTxd>vehshare</residentTxd> <residentTxd>vehshare</residentTxd>
<residentAnims /> <residentAnims />
<InitDatas> <InitDatas>
@ -179,22 +177,39 @@ namespace CodeWalker.Benchmarks
[GlobalSetup] [GlobalSetup]
public void Setup() public void Setup()
{ {
data = Encoding.UTF8.GetBytes(markup); data = new byte[2048];
fileEntry = RpfFile.CreateFileEntry("kaas.meta", "saak.meta", ref data); var random = new Random(42);
for (int i = 0; i < data.Length; i++)
{
data[i] = (byte)random.Next(byte.MinValue, byte.MaxValue);
}
GTA5Keys.LoadFromPath("C:\\Program Files\\Rockstar Games\\Grand Theft Auto V", "");
} }
[Benchmark(Baseline = true)] //[Benchmark(Baseline = true)]
public void RunLoad() //public void RunLoad()
//{
// var vehiclesFileExpected = new VehiclesFile();
// vehiclesFileExpected.LoadOld(data, fileEntry);
//}
//[Benchmark]
//public void RunLoadNew()
//{
// var vehiclesFile = new VehiclesFile();
// vehiclesFile.Load(data, fileEntry);
//}
[Benchmark]
public void DecryptNGSpan()
{ {
var vehiclesFileExpected = new VehiclesFile(); GTACrypto.DecryptNG(data.AsSpan(), "kaas", 2048);
vehiclesFileExpected.LoadOld(data, fileEntry);
} }
[Benchmark] [Benchmark]
public void RunLoadNew() public void DecryptNG()
{ {
var vehiclesFile = new VehiclesFile(); GTACrypto.DecryptNG(data, "kaas", 2048);
vehiclesFile.Load(data, fileEntry);
} }
} }
} }

View File

@ -1,201 +1,55 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<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> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>CodeWalker.Benchmarks</RootNamespace> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<AssemblyName>CodeWalker.Benchmarks</AssemblyName> <AssemblyTitle>CodeWalker.Benchmarks</AssemblyTitle>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <Product>CodeWalker.Benchmarks</Product>
<FileAlignment>512</FileAlignment> <Copyright>Copyright © 2023</Copyright>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AssemblyVersion>1.0.0.0</AssemblyVersion>
<Deterministic>true</Deterministic> <FileVersion>1.0.0.0</FileVersion>
<NuGetPackageImportStamp> <LangVersion>latest</LangVersion>
</NuGetPackageImportStamp> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </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> <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.Analyzers.dll" />
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" /> <Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.3.3.3\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj"> <ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
<Project>{FF6B9F41-14BE-474E-9ED0-549C3BEB7E00}</Project> </ItemGroup>
<Name>CodeWalker.Core</Name> <ItemGroup>
</ProjectReference> <PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.10" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Gee.External.Capstone" Version="2.3.0" />
<PackageReference Include="Iced" Version="1.17.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.1.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.1.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.251802" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.332302" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.0.2" />
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageReference Include="Perfolizer" Version="0.2.1" />
<PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="6.0.0" />
<PackageReference Include="System.Collections.Immutable" Version="6.0.0" />
<PackageReference Include="System.Management" Version="6.0.2" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Reflection.Metadata" Version="6.0.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="System.Security.AccessControl" Version="6.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
</ItemGroup> </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> </Project>

View File

@ -2,15 +2,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using static System.Reflection.Metadata.BlobBuilder;
namespace CodeWalker.Benchmarks namespace CodeWalker.Benchmarks
{ {
internal class Program internal class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
//var benchmarks = new Benchmarks();
//benchmarks.Setup();
//benchmarks.ToUInt64();
//benchmarks.ReadUInt64LittleEndian();
//benchmarks.ReadUInt64BigEndian();
//benchmarks.ToUIntBigEndian();
//benchmarks.HtmlEncode();
//benchmarks.GlobalCleanup();
//ParseBuffer();
BenchmarkRunner.Run<Benchmarks>(); BenchmarkRunner.Run<Benchmarks>();
} }
} }

View File

@ -1,16 +1,6 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; 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: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@ -21,16 +11,3 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM // The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("edda8a8e-5333-4e28-8221-a31e3b70eb7a")] [assembly: Guid("edda8a8e-5333-4e28-8221-a31e3b70eb7a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

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

View File

@ -1,30 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" /> <PackageReference Include="DirectXTexNet" Version="1.0.3" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="directxtex_desktop_win10" Version="2023.3.30.1" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="RequiredMemberAttribute" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" /> <PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Properties\" /> <Folder Include="Properties\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="Resources\magic.dat" /> <Resource Include="Resources\magic.dat" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using TC = System.ComponentModel.TypeConverterAttribute; using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter; using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Xml; using System.Xml;
using System.Runtime.InteropServices;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -39,12 +40,12 @@ namespace CodeWalker.GameFiles
public Dictionary<uint, AwcStream> StreamDict { get; set; } public Dictionary<uint, AwcStream> StreamDict { get; set; }
static public void Decrypt_RSXXTEA(Span<byte> data, uint[] key)
static public void Decrypt_RSXXTEA(byte[] data, uint[] key)
{ {
// Rockstar's modified version of XXTEA // Rockstar's modified version of XXTEA
uint[] blocks = new uint[data.Length / 4]; var blocks = MemoryMarshal.Cast<byte, uint>(data);
Buffer.BlockCopy(data, 0, blocks, 0, data.Length); //[] blocks = new uint[data.Length / 4];
//Buffer.BlockCopy(data, 0, blocks, 0, data.Length);
int block_count = blocks.Length; int block_count = blocks.Length;
uint a, b = blocks[0], i; uint a, b = blocks[0], i;
@ -60,7 +61,7 @@ namespace CodeWalker.GameFiles
i -= 0x9E3779B9; i -= 0x9E3779B9;
} while (i != 0); } while (i != 0);
Buffer.BlockCopy(blocks, 0, data, 0, data.Length); //Buffer.BlockCopy(blocks, 0, data, 0, data.Length);
} }
public void Load(byte[] data, RpfFileEntry entry) public void Load(byte[] data, RpfFileEntry entry)
@ -1171,15 +1172,13 @@ namespace CodeWalker.GameFiles
for (int b = 0; b < bcount; b++) for (int b = 0; b < bcount; b++)
{ {
int srcoff = b * bsize; int srcoff = b * bsize;
int mcsoff = (b < ocount) ? (int)SeekTableChunk.SeekTable[b] : 0; int sampleOffset = (b < ocount) ? (int)SeekTableChunk.SeekTable[b] : 0;
int blen = Math.Max(Math.Min(bsize, DataChunk.Data.Length - srcoff), 0); int dataBlockLength = Math.Max(Math.Min(bsize, DataChunk.Data.Length - srcoff), 0);
var bdat = new byte[blen];
Buffer.BlockCopy(DataChunk.Data, srcoff, bdat, 0, blen);
if (Awc.MultiChannelEncryptFlag && !Awc.WholeFileEncrypted) if (Awc.MultiChannelEncryptFlag && !Awc.WholeFileEncrypted)
{ {
AwcFile.Decrypt_RSXXTEA(bdat, GTA5Keys.PC_AWC_KEY); AwcFile.Decrypt_RSXXTEA(DataChunk.Data.AsSpan(srcoff, dataBlockLength), GTA5Keys.PC_AWC_KEY);
} }
var blk = new AwcStreamDataBlock(bdat, StreamFormatChunk, endianess, mcsoff); var blk = new AwcStreamDataBlock(DataChunk.Data, StreamFormatChunk, endianess, sampleOffset, srcoff, dataBlockLength);
blist.Add(blk); blist.Add(blk);
} }
StreamBlocks = blist.ToArray(); StreamBlocks = blist.ToArray();
@ -2712,21 +2711,22 @@ namespace CodeWalker.GameFiles
public AwcStreamDataBlock() public AwcStreamDataBlock()
{ } { }
public AwcStreamDataBlock(byte[] data, AwcStreamFormatChunk channelInfo, Endianess endianess, int sampleOffset) public AwcStreamDataBlock(byte[] data, AwcStreamFormatChunk channelInfo, Endianess endianess, int sampleOffset) : this(data, channelInfo, endianess, sampleOffset, 0, data?.Length ?? 0)
{ }
public AwcStreamDataBlock(byte[] data, AwcStreamFormatChunk channelInfo, Endianess endianess, int sampleOffset, int offset, int dataLength)
{ {
DataLength = data?.Length ?? 0; DataLength = dataLength;
SampleOffset = sampleOffset; SampleOffset = sampleOffset;
ChannelCount = channelInfo?.ChannelCount ?? 0; ChannelCount = channelInfo?.ChannelCount ?? 0;
ChannelInfo = channelInfo; ChannelInfo = channelInfo;
using (var ms = new MemoryStream(data)) using var ms = new MemoryStream(data, offset, dataLength);
{
var r = new DataReader(ms, endianess); var r = new DataReader(ms, endianess);
Read(r); Read(r);
} }
}
public void Read(DataReader r) public void Read(DataReader r)
{ {
var ilist = new List<AwcStreamDataChannel>(); var ilist = new List<AwcStreamDataChannel>();
@ -2744,7 +2744,7 @@ namespace CodeWalker.GameFiles
} }
var padc = (0x800 - (r.Position % 0x800)) % 0x800; var padc = (0x800 - (r.Position % 0x800)) % 0x800;
var padb = r.ReadBytes((int)padc); r.Position += padc;
foreach (var channel in Channels) foreach (var channel in Channels)
{ {

View File

@ -5,6 +5,7 @@ using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@ -31,25 +32,30 @@ namespace CodeWalker.GameFiles
{ {
FileEntry = entry; FileEntry = entry;
MemoryStream ms = new MemoryStream(data); using MemoryStream ms = new MemoryStream(data);
BinaryReader br = new BinaryReader(ms); using BinaryReader br = new BinaryReader(ms);
StringBuilder sb = new StringBuilder(); Span<char> charArr = stackalloc char[100];
var length = 0;
for (int i = 0; (i < 100) && (i < data.Length); i++) for (int i = 0; (i < 100) && (i < data.Length); i++)
{ {
//read version string. //read version string.
byte b = data[i]; byte b = data[i];
if (b == 0) break; if (b == 0)
sb.Append((char)b); {
break;
} }
Version = sb.ToString().Replace("[VERSION]", "").Replace("\r", "").Replace("\n", "");
sb.Clear(); charArr[i] = (char)b;
length++;
}
Version = new string(charArr.Slice(0, length)).Replace("[VERSION]", "").Replace("\r", "").Replace("\n", "");
length = 0;
int lastn = 0; int lastn = 0;
int lspos = 0; int lspos = 0;
uint structcount = 0; uint structcount = 0;
uint modlen; uint modlen;
bool indates = false; bool indates = false;
List<string> lines = new List<string>();
var dates = new List<CacheFileDate>(); var dates = new List<CacheFileDate>();
var allMapNodes = new List<MapDataStoreNode>(); var allMapNodes = new List<MapDataStoreNode>();
var allCInteriorProxies = new List<CInteriorProxy>(); var allCInteriorProxies = new List<CInteriorProxy>();
@ -62,9 +68,8 @@ namespace CodeWalker.GameFiles
if (b == 0xA) if (b == 0xA)
{ {
lastn = i; lastn = i;
string line = sb.ToString(); string line = new string(charArr.Slice(0, length));
lines.Add(line); switch (charArr.Slice(0, length))
switch (line)
{ {
case "<fileDates>": case "<fileDates>":
indates = true; indates = true;
@ -125,11 +130,12 @@ namespace CodeWalker.GameFiles
break; break;
} }
sb.Clear(); length = 0;
} }
else else
{ {
sb.Append((char)b); charArr[length] = (char)b;
length++;
} }
} }
FileDates = dates.ToArray(); FileDates = dates.ToArray();

View File

@ -55,7 +55,7 @@ namespace CodeWalker.GameFiles
using (MemoryStream ms = new MemoryStream(data)) using (MemoryStream ms = new MemoryStream(data))
{ {
DataReader r = new DataReader(ms, Endianess.LittleEndian); DataReader r = new DataReader(ms);
Read(r); Read(r);
}; };

View File

@ -16,6 +16,7 @@ namespace CodeWalker.GameFiles
{ {
[TC(typeof(EXP))] public class PedsFile : GameFile, PackedFile [TC(typeof(EXP))] public class PedsFile : GameFile, PackedFile
{ {
private static XmlNameTable cachedNameTable = new System.Xml.NameTable();
public PsoFile Pso { get; set; } public PsoFile Pso { get; set; }
public string Xml { get; set; } public string Xml { get; set; }
@ -62,7 +63,7 @@ namespace CodeWalker.GameFiles
// } // }
//} //}
using var xmlReader = XmlReader.Create(textReader); using var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { NameTable = cachedNameTable, });
//if (xdoc.DocumentElement != null) //if (xdoc.DocumentElement != null)

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,7 +16,7 @@ namespace CodeWalker.GameFiles
{ {
public class VehiclesFile : GameFile, PackedFile public class VehiclesFile : GameFile, PackedFile
{ {
private static XmlNameTable cachedNameTable = new NameTable();
public string ResidentTxd { get; set; } public string ResidentTxd { get; set; }
public List<VehicleInitData> InitDatas { get; set; } public List<VehicleInitData> InitDatas { get; set; }
@ -69,7 +70,7 @@ namespace CodeWalker.GameFiles
{ {
using var textReader = new StreamReader(new MemoryStream(data), Encoding.UTF8); using var textReader = new StreamReader(new MemoryStream(data), Encoding.UTF8);
using var xmlReader = XmlReader.Create(textReader); using var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { NameTable = cachedNameTable });
while (xmlReader.Read()) while (xmlReader.Read())
{ {
@ -838,30 +839,27 @@ namespace CodeWalker.GameFiles
return GetStringArray(ldastr, delimiter); return GetStringArray(ldastr, delimiter);
} }
private unsafe float[] GetFloatArray(string ldastr, char delimiter) [SkipLocalsInit]
private float[] GetFloatArray(string ldastr, char delimiter)
{ {
var ldarr = ldastr?.Split(delimiter); var ldarr = ldastr?.Split(delimiter);
if (ldarr == null) return null; if (ldarr == null) return null;
var floats = stackalloc float[ldarr.Length]; Span<float> floats = stackalloc float[ldarr.Length];
var i = 0; var i = 0;
foreach (var ldstr in ldarr) foreach (var ldstr in ldarr)
{ {
var ldt = ldstr?.Trim(); var ldt = ldstr?.Trim();
if (!string.IsNullOrEmpty(ldt)) if (!string.IsNullOrEmpty(ldt) && FloatUtil.TryParse(ldt, out var f))
{
float f;
if (FloatUtil.TryParse(ldt, out f))
{ {
floats[i] = f; floats[i] = f;
i++; i++;
} }
} }
}
if (i == 0) return null; if (i == 0) return null;
var result = new float[i]; var result = new float[i];
Marshal.Copy((IntPtr)floats, result, 0, i); floats.Slice(0, i).CopyTo(result);
return result; return result;
} }

View File

@ -3285,13 +3285,16 @@ namespace CodeWalker.GameFiles
public class YmapOccludeModel : BasePathData public class YmapOccludeModel : BasePathData
{ {
public OccludeModel _OccludeModel; public OccludeModel _OccludeModel;
public OccludeModel OccludeModel { get { return _OccludeModel; } set { _OccludeModel = value; } } public OccludeModel OccludeModel { get { return _OccludeModel; } set { _OccludeModel = value; } }
public YmapFile Ymap { get; set; } public YmapFile Ymap { get; set; }
public byte[] Data { get; set; } public byte[] Data { get; set; }
public Vector3[] Vertices { get; set; } private int vertexCount;
public byte[] Indices { get; set; } private int indicesOffset { get => vertexCount * 12; }
public Span<Vector3> Vertices { get => MetaTypes.ConvertDataArray<Vector3>(Data, 0, vertexCount); }
public Span<byte> Indices { get => Data.AsSpan(indicesOffset); }
public int Index { get; set; } public int Index { get; set; }
public YmapOccludeModelTriangle[] Triangles { get; set; } public YmapOccludeModelTriangle[] Triangles { get; set; }
@ -3321,12 +3324,12 @@ namespace CodeWalker.GameFiles
var vptr = _OccludeModel.verts; var vptr = _OccludeModel.verts;
var dataSize = _OccludeModel.dataSize; var dataSize = _OccludeModel.dataSize;
var indicesOffset = _OccludeModel.numVertsInBytes; var indicesOffset = _OccludeModel.numVertsInBytes;
var vertexCount = indicesOffset / 12; vertexCount = indicesOffset / 12;
var indexCount = (int)(dataSize - indicesOffset);// / 4; var indexCount = (int)(dataSize - indicesOffset);// / 4;
Data = MetaTypes.GetByteArray(meta, vptr, dataSize); Data = MetaTypes.GetByteArray(meta, vptr, dataSize);
Vertices = MetaTypes.ConvertDataArray<Vector3>(Data, 0, vertexCount).ToArray(); //Vertices = MetaTypes.ConvertDataArray<Vector3>(Data, 0, vertexCount).ToArray();
Indices = new byte[indexCount]; //Indices = new byte[indexCount];
Buffer.BlockCopy(Data, indicesOffset, Indices, 0, indexCount); //Buffer.BlockCopy(Data, indicesOffset, Indices, 0, indexCount);
BuildTriangles(); BuildTriangles();
} }
@ -3362,10 +3365,11 @@ namespace CodeWalker.GameFiles
//create vertices and indices arrays from Triangles //create vertices and indices arrays from Triangles
if (Triangles == null) if (Triangles == null)
{ {
Vertices = null;
Indices = null;
return; return;
} }
var vdict = new Dictionary<Vector3, byte>(); var vdict = new Dictionary<Vector3, byte>();
var verts = new List<Vector3>(); var verts = new List<Vector3>();
var inds = new List<byte>(); var inds = new List<byte>();
@ -3391,21 +3395,40 @@ namespace CodeWalker.GameFiles
inds.Add(ensureVert(tri.Corner2)); inds.Add(ensureVert(tri.Corner2));
inds.Add(ensureVert(tri.Corner3)); inds.Add(ensureVert(tri.Corner3));
} }
Vertices = verts.ToArray();
Indices = inds.ToArray(); var newVertsSize = Vector3.SizeInBytes * verts.Count;
var newIndicesSize = Marshal.SizeOf<byte>() * inds.Count;
if (newVertsSize + newIndicesSize != Data.Length)
{
Data = new byte[newVertsSize + newIndicesSize];
}
vertexCount = verts.Count;
for (int i = 0; i < verts.Count; i++)
{
Vertices[i] = verts[i];
}
for (int i = 0; i < inds.Count; i++)
{
Indices[i] = inds[i];
}
} }
public void BuildData() public void BuildData()
{ {
//create Data from vertices and indices arrays //create Data from vertices and indices arrays
if (Vertices == null) return; if (Vertices == null) return;
if (Indices == null) return; if (Indices == null) return;
var dlen = (Vertices.Length * 12) + (Indices.Length * 1); var dlen = (Vertices.Length * Vector3.SizeInBytes) + (Indices.Length * 1);
var d = new byte[dlen]; if (dlen != Data.Length)
var vbytes = MetaTypes.ConvertArrayToBytes(Vertices); {
var ibytes = Indices; throw new InvalidOperationException("Size mismatch in YmapOccludeModel BuildData");
Buffer.BlockCopy(vbytes, 0, d, 0, vbytes.Length); }
Buffer.BlockCopy(ibytes, 0, d, vbytes.Length, ibytes.Length); //var d = new byte[dlen];
Data = d; //var vbytes = MetaTypes.ConvertArrayToBytes(Vertices);
//var ibytes = Indices;
//Buffer.BlockCopy(vbytes, 0, d, 0, vbytes.Length);
//Buffer.BlockCopy(ibytes, 0, d, vbytes.Length, ibytes.Length);
//Data = d;
var min = new Vector3(float.MaxValue); var min = new Vector3(float.MaxValue);
var max = new Vector3(float.MinValue); var max = new Vector3(float.MinValue);
for (int i = 0; i < Vertices.Length; i++) for (int i = 0; i < Vertices.Length; i++)
@ -3418,8 +3441,8 @@ namespace CodeWalker.GameFiles
_OccludeModel.Unused0 = min.X; _OccludeModel.Unused0 = min.X;
_OccludeModel.Unused1 = max.X; _OccludeModel.Unused1 = max.X;
_OccludeModel.dataSize = (uint)dlen; _OccludeModel.dataSize = (uint)dlen;
_OccludeModel.numVertsInBytes = (ushort)vbytes.Length; _OccludeModel.numVertsInBytes = (ushort)(vertexCount * Vector3.SizeInBytes);
_OccludeModel.numTris = (ushort)((ibytes.Length / 3) + 32768);//is this actually a flag lurking..? _OccludeModel.numTris = (ushort)((Indices.Length / 3) + 32768);//is this actually a flag lurking..?
//_OccludeModel.flags = ... //_OccludeModel.flags = ...
} }
@ -3462,7 +3485,7 @@ namespace CodeWalker.GameFiles
public override string ToString() public override string ToString()
{ {
return Index.ToString() + ": " + (Vertices?.Length ?? 0).ToString() + " vertices, " + (Triangles?.Length ?? 0).ToString() + " triangles"; return Index.ToString() + ": " + Vertices.Length.ToString() + " vertices, " + (Triangles?.Length ?? 0).ToString() + " triangles";
} }
} }

View File

@ -37,7 +37,7 @@ namespace CodeWalker.GameFiles
using (MemoryStream ms = new MemoryStream(data)) using (MemoryStream ms = new MemoryStream(data))
{ {
DataReader r = new DataReader(ms, Endianess.LittleEndian); DataReader r = new DataReader(ms);
Read(r); Read(r);
} }

View File

@ -1,6 +1,4 @@
using CodeWalker.Core.Utils; using CodeWalker.Core.Utils;
using CodeWalker.World;
using Dasync.Collections;
using SharpDX; using SharpDX;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -92,15 +90,15 @@ namespace CodeWalker.GameFiles
public Dictionary<MetaHash, Dictionary<MetaHash, RpfFileEntry>> PedClothDicts { get; set; } public Dictionary<MetaHash, Dictionary<MetaHash, RpfFileEntry>> PedClothDicts { get; set; }
public List<RelFile> AudioDatRelFiles = new List<RelFile>(); public List<RelFile> AudioDatRelFiles;
public Dictionary<MetaHash, RelData> AudioConfigDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioConfigDict;
public Dictionary<MetaHash, RelData> AudioSpeechDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioSpeechDict;
public Dictionary<MetaHash, RelData> AudioSynthsDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioSynthsDict;
public Dictionary<MetaHash, RelData> AudioMixersDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioMixersDict;
public Dictionary<MetaHash, RelData> AudioCurvesDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioCurvesDict;
public Dictionary<MetaHash, RelData> AudioCategsDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioCategsDict;
public Dictionary<MetaHash, RelData> AudioSoundsDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioSoundsDict;
public Dictionary<MetaHash, RelData> AudioGameDict = new Dictionary<MetaHash, RelData>(); public Dictionary<MetaHash, RelData> AudioGameDict;
@ -122,6 +120,7 @@ namespace CodeWalker.GameFiles
private string GTAFolder; private string GTAFolder;
private string ExcludeFolders; private string ExcludeFolders;
private string Key;
@ -157,7 +156,7 @@ namespace CodeWalker.GameFiles
public GameFileCache(long size, double cacheTime, string folder, string dlc, bool mods, string excludeFolders) public GameFileCache(long size, double cacheTime, string folder, string dlc, bool mods, string excludeFolders, string key)
{ {
mainCache = new Cache<GameFileCacheKey, GameFile>(size, cacheTime);//2GB is good as default mainCache = new Cache<GameFileCacheKey, GameFile>(size, cacheTime);//2GB is good as default
SelectedDlc = dlc; SelectedDlc = dlc;
@ -165,19 +164,19 @@ namespace CodeWalker.GameFiles
EnableMods = mods; EnableMods = mods;
GTAFolder = folder; GTAFolder = folder;
ExcludeFolders = excludeFolders; ExcludeFolders = excludeFolders;
Key = key;
} }
public void Clear() public void Clear()
{ {
IsInited = false; //IsInited = false;
mainCache.Clear(); mainCache.Clear();
textureLookup.Clear(); //textureLookup.Clear();
while (requestQueue.TryDequeue(out _)) requestQueue.Clear();
{ } //empty the old queue out...
} }
public void SetGtaFolder(string folder) public void SetGtaFolder(string folder)
@ -204,6 +203,7 @@ namespace CodeWalker.GameFiles
if (RpfMan == null) if (RpfMan == null)
{ {
GTA5Keys.LoadFromPath(GTAFolder, Key);
//EnableDlc = !string.IsNullOrEmpty(SelectedDlc); //EnableDlc = !string.IsNullOrEmpty(SelectedDlc);
RpfMan = RpfManager.GetInstance(); RpfMan = RpfManager.GetInstance();
@ -280,6 +280,8 @@ namespace CodeWalker.GameFiles
{ {
return; return;
} }
ArgumentNullException.ThrowIfNull(allRpfs, nameof(allRpfs));
IsIniting = true; IsIniting = true;
try try
{ {
@ -378,7 +380,7 @@ namespace CodeWalker.GameFiles
DlcPaths.Clear(); DlcPaths.Clear();
if ((dlclistxml == null) || (dlclistxml.DocumentElement == null)) if ((dlclistxml == null) || (dlclistxml.DocumentElement == null))
{ {
ErrorLog("InitDlcList: Couldn't load " + dlclistpath + "."); ErrorLog?.Invoke("InitDlcList: Couldn't load " + dlclistpath + ".");
} }
else else
{ {
@ -442,7 +444,9 @@ namespace CodeWalker.GameFiles
RpfFile dlcfile; RpfFile dlcfile;
dlcDict2.TryGetValue(dlcname, out dlcfile); dlcDict2.TryGetValue(dlcname, out dlcfile);
if (dlcfile == null) if (dlcfile == null)
{ continue; } {
continue;
}
var dlcpath = dlcfile.Path + "\\"; var dlcpath = dlcfile.Path + "\\";
var files = updrpffile.GetFiles(relpath, true); var files = updrpffile.GetFiles(relpath, true);
foreach (var file in files) foreach (var file in files)
@ -1052,7 +1056,11 @@ namespace CodeWalker.GameFiles
YmapDict ??= new Dictionary<uint, RpfFileEntry>(4600); YmapDict ??= new Dictionary<uint, RpfFileEntry>(4600);
YbnDict ??= new Dictionary<uint, RpfFileEntry>(8800); YbnDict ??= new Dictionary<uint, RpfFileEntry>(8800);
YnvDict ??= new Dictionary<uint, RpfFileEntry>(4500); YnvDict ??= new Dictionary<uint, RpfFileEntry>(4500);
AllYmapsDict = new Dictionary<uint, RpfFileEntry>(11000); AllYmapsDict ??= new Dictionary<uint, RpfFileEntry>(11000);
YmapDict.Clear();
YbnDict.Clear();
YnvDict.Clear();
AllYmapsDict.Clear();
foreach (var rpffile in ActiveMapRpfFiles.Values) //RpfMan.BaseRpfs) foreach (var rpffile in ActiveMapRpfFiles.Values) //RpfMan.BaseRpfs)
{ {
if (rpffile.AllEntries == null) continue; if (rpffile.AllEntries == null) continue;
@ -1082,16 +1090,20 @@ namespace CodeWalker.GameFiles
Console.WriteLine($"YmapDict: {YmapDict.Count}; YbnDict: {YbnDict.Count}; YnvDict: {YnvDict.Count}; AllYmapsDict: {AllYmapsDict.Count};"); Console.WriteLine($"YmapDict: {YmapDict.Count}; YbnDict: {YbnDict.Count}; YnvDict: {YnvDict.Count}; AllYmapsDict: {AllYmapsDict.Count};");
} }
private void InitManifestDicts(bool force = true) private async ValueTask InitManifestDicts(bool force = true)
{ {
UpdateStatus?.Invoke("Loading manifests..."); UpdateStatus?.Invoke("Loading manifests...");
using var _ = new DisposableTimer("InitManifestDicts"); using var _ = new DisposableTimer("InitManifestDicts");
AllManifests = new List<YmfFile>(2000); AllManifests ??= new List<YmfFile>(2000);
hdtexturelookup = new Dictionary<MetaHash, MetaHash>(24000); hdtexturelookup ??= new Dictionary<MetaHash, MetaHash>(24000);
AllManifests.Clear();
hdtexturelookup.Clear();
IEnumerable<RpfFile> rpfs = PreloadedMode ? AllRpfs : ActiveMapRpfFiles.Values; IEnumerable<RpfFile> rpfs = PreloadedMode ? AllRpfs : ActiveMapRpfFiles.Values;
foreach (RpfFile file in rpfs) await Parallel.ForEachAsync(rpfs, async (file, cancellationToken) =>
{ {
if (file.AllEntries == null) continue; if (file.AllEntries == null)
return;
//manifest and meta parsing.. //manifest and meta parsing..
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
@ -1101,11 +1113,14 @@ namespace CodeWalker.GameFiles
} }
try try
{ {
UpdateStatus?.Invoke(string.Format(entry.Path)); UpdateStatus?.Invoke(entry.Path);
YmfFile ymffile = RpfMan.GetFile<YmfFile>(entry); YmfFile ymffile = await RpfMan.GetFileAsync<YmfFile>(entry).ConfigureAwait(false);
if (ymffile != null) if (ymffile != null)
{
lock(AllManifests)
{ {
AllManifests.Add(ymffile); AllManifests.Add(ymffile);
}
if (ymffile.HDTxdAssetBindings != null) if (ymffile.HDTxdAssetBindings != null)
{ {
@ -1114,9 +1129,12 @@ namespace CodeWalker.GameFiles
var b = ymffile.HDTxdAssetBindings[i]; var b = ymffile.HDTxdAssetBindings[i];
var targetasset = JenkHash.GenHashLower(b.targetAsset.ToString()); var targetasset = JenkHash.GenHashLower(b.targetAsset.ToString());
var hdtxd = JenkHash.GenHashLower(b.HDTxd.ToString()); var hdtxd = JenkHash.GenHashLower(b.HDTxd.ToString());
lock(hdtexturelookup)
{
hdtexturelookup[targetasset] = hdtxd; hdtexturelookup[targetasset] = hdtxd;
} }
} }
}
} }
} }
@ -1127,7 +1145,7 @@ namespace CodeWalker.GameFiles
Console.WriteLine(errstr); Console.WriteLine(errstr);
} }
} }
} }).ConfigureAwait(false);
Console.WriteLine($"hdtexturelookup: {hdtexturelookup.Count}; AllManifests: {AllManifests.Count};"); Console.WriteLine($"hdtexturelookup: {hdtexturelookup.Count}; AllManifests: {AllManifests.Count};");
} }
@ -1159,7 +1177,7 @@ namespace CodeWalker.GameFiles
allRpfs = allRpfs.Concat(DlcActiveRpfs); allRpfs = allRpfs.Concat(DlcActiveRpfs);
} }
await allRpfs.Where(p => p.AllEntries != null).ParallelForEachAsync(async (file) => await Parallel.ForEachAsync(allRpfs, async (file, cancellationToken) =>
{ {
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
@ -1228,7 +1246,7 @@ namespace CodeWalker.GameFiles
} }
else else
{ {
Console.WriteLine($"Couldn't find {node.Name} in YmapDict"); //Console.WriteLine($"Couldn't find {node.Name} in YmapDict");
} //ymap not found... } //ymap not found...
} }
} }
@ -1249,18 +1267,12 @@ namespace CodeWalker.GameFiles
CacheDatFile maincache = null; CacheDatFile maincache = null;
maincache = loadCacheFile("common.rpf\\data\\gta5_cache_y.dat", true);
if (EnableDlc) if (EnableDlc)
{ {
maincache = loadCacheFile("update\\update.rpf\\common\\data\\gta5_cache_y.dat", false); maincache = loadCacheFile("update\\update.rpf\\common\\data\\gta5_cache_y.dat", false);
if (maincache == null)
{
maincache = loadCacheFile("update\\update2.rpf\\common\\data\\gta5_cache_y.dat", true); maincache = loadCacheFile("update\\update2.rpf\\common\\data\\gta5_cache_y.dat", true);
} }
}
else
{
maincache = loadCacheFile("common.rpf\\data\\gta5_cache_y.dat", true);
}
@ -1311,7 +1323,8 @@ namespace CodeWalker.GameFiles
// } // }
//}, maxDegreeOfParallelism: 8); //}, maxDegreeOfParallelism: 8);
await allYtypes.ParallelForEachAsync(async (file) =>
await Parallel.ForEachAsync(allYtypes, async (file, cancellationToken) =>
{ {
foreach (var entry in file.AllEntries) foreach (var entry in file.AllEntries)
{ {
@ -1323,6 +1336,7 @@ namespace CodeWalker.GameFiles
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.ToString());
ErrorLog(entry.Path + ": " + ex.ToString()); ErrorLog(entry.Path + ": " + ex.ToString());
} }
} }
@ -1707,10 +1721,13 @@ namespace CodeWalker.GameFiles
if (file.Name.Equals(name + ".yld", StringComparison.OrdinalIgnoreCase)) if (file.Name.Equals(name + ".yld", StringComparison.OrdinalIgnoreCase))
{ {
pedClotsDict ??= ensureDict(allPedClothDicts, hash); pedClotsDict ??= ensureDict(allPedClothDicts, hash);
lock(pedClotsDict)
{
pedClotsDict[file.ShortNameHash] = file; pedClotsDict[file.ShortNameHash] = file;
} }
} }
} }
}
if (dir?.Directories != null) if (dir?.Directories != null)
{ {
@ -1733,21 +1750,30 @@ namespace CodeWalker.GameFiles
if (file.IsExtension(".ydd")) if (file.IsExtension(".ydd"))
{ {
pedDrwDicts ??= ensureDict(allPedDrwDicts, hash); pedDrwDicts ??= ensureDict(allPedDrwDicts, hash);
lock(pedDrwDicts)
{
pedDrwDicts[file.ShortNameHash] = file; pedDrwDicts[file.ShortNameHash] = file;
} }
}
else if (file.IsExtension(".ytd")) else if (file.IsExtension(".ytd"))
{ {
pedTextDicts ??= ensureDict(allPedTexDicts, hash); pedTextDicts ??= ensureDict(allPedTexDicts, hash);
lock(pedTextDicts)
{
pedTextDicts[file.ShortNameHash] = file; pedTextDicts[file.ShortNameHash] = file;
} }
}
else if (file.IsExtension(".yld")) else if (file.IsExtension(".yld"))
{ {
pedClotsDict ??= ensureDict(allPedClothDicts, hash); pedClotsDict ??= ensureDict(allPedClothDicts, hash);
lock(pedClotsDict)
{
pedClotsDict[file.ShortNameHash] = file; pedClotsDict[file.ShortNameHash] = file;
} }
} }
} }
} }
}
}); });
var numDlcs = dlcrpfs?.Count ?? 0; var numDlcs = dlcrpfs?.Count ?? 0;
@ -1914,7 +1940,8 @@ namespace CodeWalker.GameFiles
var relFiles = new ConcurrentBag<RelFile>(); var relFiles = new ConcurrentBag<RelFile>();
await datrelentries.Values.ParallelForEachAsync(async (datrelentry) =>
await Parallel.ForEachAsync(datrelentries.Values, async (datrelentry, cancellationToken) =>
{ {
var relfile = await RpfMan.GetFileAsync<RelFile>(datrelentry).ConfigureAwait(false); var relfile = await RpfMan.GetFileAsync<RelFile>(datrelentry).ConfigureAwait(false);
if (relfile == null) if (relfile == null)
@ -2134,7 +2161,7 @@ namespace CodeWalker.GameFiles
catch(Exception ex) catch(Exception ex)
{ {
Console.WriteLine(ex); Console.WriteLine(ex);
throw ex; throw;
} }
finally finally
{ {
@ -2201,7 +2228,7 @@ namespace CodeWalker.GameFiles
if (!ydr.Loaded && !ydr.LoadQueued) if (!ydr.Loaded && !ydr.LoadQueued)
{ {
TryLoadEnqueue(ydr); _ = TryLoadEnqueue(ydr);
} }
return ydr; return ydr;
} }
@ -2233,7 +2260,7 @@ namespace CodeWalker.GameFiles
if (!ydd.Loaded && !ydd.LoadQueued) if (!ydd.Loaded && !ydd.LoadQueued)
{ {
TryLoadEnqueue(ydd); _ = TryLoadEnqueue(ydd);
} }
return ydd; return ydd;
} }
@ -2298,7 +2325,7 @@ namespace CodeWalker.GameFiles
} }
if (!ymap.Loaded && !ymap.LoadQueued) if (!ymap.Loaded && !ymap.LoadQueued)
{ {
TryLoadEnqueue(ymap); _ = TryLoadEnqueue(ymap);
} }
return ymap; return ymap;
} }
@ -2331,7 +2358,7 @@ namespace CodeWalker.GameFiles
} }
if (!yft.Loaded && !yft.LoadQueued) if (!yft.Loaded && !yft.LoadQueued)
{ {
TryLoadEnqueue(yft); _ = TryLoadEnqueue(yft);
} }
return yft; return yft;
} }
@ -2360,7 +2387,7 @@ namespace CodeWalker.GameFiles
} }
if (!ybn.Loaded && !ybn.LoadQueued) if (!ybn.Loaded && !ybn.LoadQueued)
{ {
TryLoadEnqueue(ybn); _ = TryLoadEnqueue(ybn);
} }
return ybn; return ybn;
} }
@ -2389,7 +2416,7 @@ namespace CodeWalker.GameFiles
} }
if (!ycd.Loaded && !ycd.LoadQueued) if (!ycd.Loaded && !ycd.LoadQueued)
{ {
TryLoadEnqueue(ycd); _ = TryLoadEnqueue(ycd);
} }
return ycd; return ycd;
} }
@ -2418,7 +2445,7 @@ namespace CodeWalker.GameFiles
} }
if (!yed.Loaded && !yed.LoadQueued) if (!yed.Loaded && !yed.LoadQueued)
{ {
TryLoadEnqueue(yed); _ = TryLoadEnqueue(yed);
} }
return yed; return yed;
} }
@ -2447,7 +2474,7 @@ namespace CodeWalker.GameFiles
} }
if (!ynv.Loaded && !ynv.LoadQueued) if (!ynv.Loaded && !ynv.LoadQueued)
{ {
TryLoadEnqueue(ynv); _ = TryLoadEnqueue(ynv);
} }
return ynv; return ynv;
} }
@ -2539,12 +2566,19 @@ namespace CodeWalker.GameFiles
return new ValueTask<bool>(false); return new ValueTask<bool>(false);
} }
public async ValueTask<T> GetFileUncachedAsync<T>(RpfFileEntry e) where T : GameFile, new()
{
var f = new T();
f.RpfFileEntry = e;
await TryLoadEnqueue(f);
return f;
}
public T GetFileUncached<T>(RpfFileEntry e) where T : GameFile, new() public T GetFileUncached<T>(RpfFileEntry e) where T : GameFile, new()
{ {
var f = new T(); var f = new T();
f.RpfFileEntry = e; f.RpfFileEntry = e;
TryLoadEnqueue(f); _ = TryLoadEnqueue(f);
return f; return f;
} }
@ -2623,7 +2657,7 @@ namespace CodeWalker.GameFiles
req.LoadQueued = false; req.LoadQueued = false;
req.Loaded = false; req.Loaded = false;
Console.WriteLine(e); Console.WriteLine(e);
TryLoadEnqueue(req); _ = TryLoadEnqueue(req);
} }
finally finally
{ {

View File

@ -114,9 +114,6 @@ namespace CodeWalker.GameFiles
(ulong)this.NamePointer // offset (ulong)this.NamePointer // offset
); );
if (!string.IsNullOrEmpty(Name))
{ }
//Strings = MetaTypes.GetStrings(this); //Strings = MetaTypes.GetStrings(this);
#if DEBUG #if DEBUG

View File

@ -11,6 +11,7 @@ using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter; using EXP = System.ComponentModel.ExpandableObjectConverter;
using CodeWalker.World; using CodeWalker.World;
using System.Reflection; using System.Reflection;
using System.Threading;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -1454,40 +1455,13 @@ namespace CodeWalker.GameFiles
if (items == null) return null; if (items == null) return null;
return MemoryMarshal.AsBytes(items.AsSpan()).ToArray(); return MemoryMarshal.AsBytes(items.AsSpan()).ToArray();
}
//var size = Marshal.SizeOf(typeof(T)) * items.Length; public static Span<byte> ConvertArrayToBytes<T>(Span<T> items) where T : struct
//var b = new byte[size]; {
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned); if (items == null) return null;
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(h, b, 0, size);
//handle.Free();
//return b;
//var size = Marshal.SizeOf(typeof(T)) * items.Length; return MemoryMarshal.AsBytes(items);
//var b = new byte[size];
//return MemoryMarshal.AsBytes<T>(items).ToArray();
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(h, b, 0, size);
//handle.Free();
//return b;
//int size = Marshal.SizeOf(typeof(T));
//int sizetot = size * items.Length;
//byte[] arrout = new byte[sizetot];
//int offset = 0;
//for (int i = 0; i < items.Length; i++)
//{
// byte[] arr = new byte[size];
// IntPtr ptr = Marshal.AllocHGlobal(size);
// Marshal.StructureToPtr(items[i], ptr, true);
// Marshal.Copy(ptr, arr, 0, size);
// Marshal.FreeHGlobal(ptr);
// Buffer.BlockCopy(arr, 0, arrout, offset, size);
// offset += size;
//}
//return arrout;
} }
@ -1561,7 +1535,10 @@ namespace CodeWalker.GameFiles
var ptr = ptrs[i]; var ptr = ptrs[i];
var offset = ptr.Offset; var offset = ptr.Offset;
var block = meta.GetBlock(ptr.BlockID); var block = meta.GetBlock(ptr.BlockID);
if (block == null) continue; if (block == null)
{
continue;
}
//if (blocktype == 0) //if (blocktype == 0)
//{ blocktype = block.StructureNameHash; } //{ blocktype = block.StructureNameHash; }
@ -1569,9 +1546,13 @@ namespace CodeWalker.GameFiles
//{ } //not all the same type..! //{ } //not all the same type..!
if (block.StructureNameHash != name) if (block.StructureNameHash != name)
{ return null; } //type mismatch - don't return anything... {
return null;
} //type mismatch - don't return anything...
if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length)) if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
{ continue; } {
continue;
}
items[i] = ConvertData<T>(block.Data, offset); items[i] = ConvertData<T>(block.Data, offset);
} }
@ -1583,7 +1564,8 @@ namespace CodeWalker.GameFiles
} }
public static T[] ConvertDataArray<T>(Meta meta, MetaName name, ulong pointer, uint count) where T : struct public static T[] ConvertDataArray<T>(Meta meta, MetaName name, ulong pointer, uint count) where T : struct
{ {
if (count == 0) return null; if (count == 0)
return null;
T[] items = new T[count]; T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T)); int itemsize = Marshal.SizeOf(typeof(T));
@ -1593,7 +1575,9 @@ namespace CodeWalker.GameFiles
uint ptroffset = (uint)((pointer >> 12) & 0xFFFFF); uint ptroffset = (uint)((pointer >> 12) & 0xFFFFF);
var ptrblock = (ptrindex < meta.DataBlocks.Count) ? meta.DataBlocks[(int)ptrindex] : null; var ptrblock = (ptrindex < meta.DataBlocks.Count) ? meta.DataBlocks[(int)ptrindex] : null;
if ((ptrblock == null) || (ptrblock.Data == null) || (ptrblock.StructureNameHash != name)) if ((ptrblock == null) || (ptrblock.Data == null) || (ptrblock.StructureNameHash != name))
{ return null; } //no block or wrong block? shouldn't happen! {
return null;
} //no block or wrong block? shouldn't happen!
int byteoffset = (int)ptroffset;// (ptroffset * 16 + ptrunkval); int byteoffset = (int)ptroffset;// (ptroffset * 16 + ptrunkval);
int itemoffset = byteoffset / itemsize; int itemoffset = byteoffset / itemsize;
@ -1608,25 +1592,30 @@ namespace CodeWalker.GameFiles
itemcount = itemsleft; itemcount = itemsleft;
} //don't try to read too many items.. } //don't try to read too many items..
ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi));
//ConvertDataArray<T>(ptrblock.Data, itemoffset * Marshal.SizeOf(typeof(T)), itemcount).CopyTo(items.AsSpan(curi)); //for (int i = 0; i < itemcount; i++)
for (int i = 0; i < itemcount; i++) //{
{ // int offset = (itemoffset + i) * itemsize;
int offset = (itemoffset + i) * itemsize; // int index = curi + i;
int index = curi + i; // items[index] = ConvertData<T>(ptrblock.Data, offset);
items[index] = ConvertData<T>(ptrblock.Data, offset); //}
}
itemoffset = 0; //start at beginning of next block.. itemoffset = 0; //start at beginning of next block..
curi += itemcount; curi += itemcount;
itemsleft -= itemcount; itemsleft -= itemcount;
if (itemsleft <= 0) if (itemsleft <= 0)
{ return items; }//all done! {
return items;
}//all done!
ptrindex++; ptrindex++;
ptrblock = (ptrindex < meta.DataBlocks.Count) ? meta.DataBlocks[(int)ptrindex] : null; ptrblock = (ptrindex < meta.DataBlocks.Count) ? meta.DataBlocks[(int)ptrindex] : null;
if ((ptrblock == null) || (ptrblock.Data == null)) if ((ptrblock == null) || (ptrblock.Data == null))
{ break; } //not enough items..? {
break;
} //not enough items..?
if (ptrblock.StructureNameHash != name) if (ptrblock.StructureNameHash != name)
{ break; } //type mismatch.. {
break;
} //type mismatch..
} }
return null; return null;

View File

@ -2207,12 +2207,7 @@ namespace CodeWalker.GameFiles
public static string XmlEscape(string unescaped) public static string XmlEscape(string unescaped)
{ {
if (unescaped == null) return null; if (unescaped == null) return null;
XmlDocument doc = new XmlDocument(); var escaped = System.Web.HttpUtility.HtmlEncode(unescaped).Replace("\"", "&quot;");
XmlNode node = doc.CreateElement("root");
node.InnerText = unescaped;
var escaped = node.InnerXml.Replace("\"", "&quot;");
if (escaped != unescaped)
{ }
return escaped; return escaped;
} }

View File

@ -117,6 +117,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -880,8 +881,7 @@ namespace CodeWalker.GameFiles
} }
foreach (var str in strs) foreach (var str in strs)
{ {
JenkIndex.Ensure(str); JenkIndex.EnsureBoth(str);
JenkIndex.EnsureLower(str);
} }
Strings = strs.ToArray(); Strings = strs.ToArray();
} }

View File

@ -471,7 +471,7 @@ namespace CodeWalker.GameFiles
{ {
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length); MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length);
ds.CopyToFast(outstr); ds.CopyTo(outstr, 524288);
return outstr.ToArray(); return outstr.ToArray();
} }
} }

View File

@ -104,11 +104,12 @@ namespace CodeWalker.GameFiles
position = value; position = value;
} }
} }
/// <summary> /// <summary>
/// Initializes a new resource data reader for the specified system- and graphics-stream. /// Initializes a new resource data reader for the specified system- and graphics-stream.
/// </summary> /// </summary>
public ResourceDataReader(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base(null, endianess)
{ {
this.systemStream = systemStream; this.systemStream = systemStream;
this.graphicsStream = graphicsStream; this.graphicsStream = graphicsStream;
@ -117,7 +118,7 @@ namespace CodeWalker.GameFiles
} }
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base(null, endianess)
{ {
FileEntry = resentry; FileEntry = resentry;
this.systemSize = resentry.SystemSize; this.systemSize = resentry.SystemSize;
@ -138,21 +139,21 @@ namespace CodeWalker.GameFiles
if ((int)systemSize > data.Length) if ((int)systemSize > data.Length)
{ {
throw new ArgumentException($"systemSize {systemSize} is larger than data length ({data.Length})", nameof(systemSize)); throw new ArgumentException($"systemSize {systemSize} is larger than data length ({data.Length})", nameof(resentry));
} }
if ((int)graphicsSize > data.Length) if ((int)graphicsSize > data.Length)
{ {
throw new ArgumentException($"graphicsSize {graphicsSize} is larger than data length ({data.Length})", nameof(graphicsSize)); throw new ArgumentException($"graphicsSize {graphicsSize} is larger than data length ({data.Length})", nameof(resentry));
} }
this.systemStream = Stream.Synchronized(new MemoryStream(data, 0, (int)systemSize)); this.systemStream = new MemoryStream(data, 0, (int)systemSize);
this.graphicsStream = Stream.Synchronized(new MemoryStream(data, (int)systemSize, (int)graphicsSize)); this.graphicsStream = new MemoryStream(data, (int)systemSize, (int)graphicsSize);
} }
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base(null, endianess)
{ {
this.systemStream = Stream.Synchronized(new MemoryStream(data, 0, systemSize)); this.systemStream = new MemoryStream(data, 0, systemSize);
this.graphicsStream = Stream.Synchronized(new MemoryStream(data, systemSize, graphicsSize)); this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
} }
public override Stream GetStream() public override Stream GetStream()
@ -269,7 +270,6 @@ namespace CodeWalker.GameFiles
return items; return items;
} }
internal const int StackallocThreshold = 512;
public unsafe byte[] ReadBytesAt(ulong position, uint count, bool cache = true, byte[] buffer = null) public unsafe byte[] ReadBytesAt(ulong position, uint count, bool cache = true, byte[] buffer = null)
{ {
long pos = (long)position; long pos = (long)position;
@ -489,6 +489,7 @@ namespace CodeWalker.GameFiles
systemStream?.Dispose(); systemStream?.Dispose();
graphicsStream?.Dispose(); graphicsStream?.Dispose();
GC.SuppressFinalize(this);
} }
} }
@ -647,6 +648,7 @@ namespace CodeWalker.GameFiles
systemStream?.Dispose(); systemStream?.Dispose();
graphicsStream?.Dispose(); graphicsStream?.Dispose();
GC.SuppressFinalize(this);
} }
} }

View File

@ -17,6 +17,26 @@ using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
public struct FileCounts
{
public uint Rpfs;
public uint Files;
public uint Folders;
public uint Resources;
public uint BinaryFiles;
public static FileCounts operator +(FileCounts a, FileCounts b)
{
return new FileCounts
{
Rpfs = a.Rpfs + b.Rpfs,
Files = a.Files + b.Files,
Folders = a.Folders + b.Folders,
Resources = a.Resources + b.Resources,
BinaryFiles = a.BinaryFiles + b.BinaryFiles
};
}
}
public class RpfFile public class RpfFile
{ {
@ -53,15 +73,6 @@ namespace CodeWalker.GameFiles
public uint TotalFileCount { get; set; } public uint TotalFileCount { get; set; }
public uint TotalFolderCount { get; set; }
public uint TotalResourceCount { get; set; }
public uint TotalBinaryFileCount { get; set; }
public uint GrandTotalRpfCount { get; set; }
public uint GrandTotalFileCount { get; set; }
public uint GrandTotalFolderCount { get; set; }
public uint GrandTotalResourceCount { get; set; }
public uint GrandTotalBinaryFileCount { get; set; }
public long ExtractedByteCount { get; set; }
static RpfFile() static RpfFile()
{ {
@ -163,7 +174,10 @@ namespace CodeWalker.GameFiles
} }
private void ThrowInvalidResource()
{
throw new Exception("Invalid Resource - not GTAV!");
}
private void ReadHeader(BinaryReader br) private void ReadHeader(BinaryReader br)
{ {
@ -178,7 +192,7 @@ namespace CodeWalker.GameFiles
if (Version != 0x52504637) if (Version != 0x52504637)
{ {
throw new Exception("Invalid Resource - not GTAV!"); ThrowInvalidResource();
} }
var entriesLength = (int)EntryCount * 16; var entriesLength = (int)EntryCount * 16;
@ -202,8 +216,8 @@ namespace CodeWalker.GameFiles
break; break;
case RpfEncryption.NG: case RpfEncryption.NG:
default: default:
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, entriesLength); GTACrypto.DecryptNG(entriesdata.AsSpan(0, entriesLength), Name, (uint)FileSize);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, namesLength); GTACrypto.DecryptNG(namesdata.AsSpan(0, namesLength), Name, (uint)FileSize);
IsNGEncrypted = true; IsNGEncrypted = true;
break; break;
@ -215,9 +229,6 @@ namespace CodeWalker.GameFiles
AllEntries = new List<RpfEntry>((int)EntryCount); AllEntries = new List<RpfEntry>((int)EntryCount);
TotalFileCount = 0; TotalFileCount = 0;
TotalFolderCount = 0;
TotalResourceCount = 0;
TotalBinaryFileCount = 0;
for (uint i = 0; i < EntryCount; i++) for (uint i = 0; i < EntryCount; i++)
{ {
@ -231,18 +242,15 @@ namespace CodeWalker.GameFiles
if (x == 0x7fffff00) //directory entry if (x == 0x7fffff00) //directory entry
{ {
e = new RpfDirectoryEntry(); e = new RpfDirectoryEntry();
TotalFolderCount++;
} }
else if ((x & 0x80000000) == 0) //binary file entry else if ((x & 0x80000000) == 0) //binary file entry
{ {
e = new RpfBinaryFileEntry(); e = new RpfBinaryFileEntry();
TotalBinaryFileCount++;
TotalFileCount++; TotalFileCount++;
} }
else //assume resource file entry else //assume resource file entry
{ {
e = new RpfResourceFileEntry(); e = new RpfResourceFileEntry();
TotalResourceCount++;
TotalFileCount++; TotalFileCount++;
} }
@ -328,39 +336,39 @@ namespace CodeWalker.GameFiles
ArrayPool<byte>.Shared.Return(namesdata); ArrayPool<byte>.Shared.Return(namesdata);
} }
public bool ScanStructure(Action<string> updateStatus, Action<string> errorLog) public FileCounts ScanStructure(Action<string> updateStatus, Action<string> errorLog)
{ {
using var fileStream = File.OpenRead(FilePath); using var fileStream = File.OpenRead(FilePath);
using var br = new BinaryReader(fileStream); using var br = new BinaryReader(fileStream);
try try
{ {
return ScanStructure(br, updateStatus, errorLog); return ScanStructure(br, updateStatus, errorLog) ?? default;
} }
catch (Exception ex) catch (Exception ex)
{ {
LastError = ex.ToString(); LastError = ex.ToString();
LastException = ex; LastException = ex;
errorLog?.Invoke(FilePath + ": " + LastError); errorLog?.Invoke(FilePath + ": " + LastError);
return false; return default;
} }
} }
private bool ScanStructure(BinaryReader br, Action<string> updateStatus, Action<string> errorLog) private FileCounts? ScanStructure(BinaryReader br, Action<string> updateStatus, Action<string> errorLog)
{ {
if (FilePath == "update\\update.rpf\\dlc_patch\\patchday1ng\\x64\\patch\\data\\lang\\chinesesimp.rpf") return false; if (FilePath == "update\\update.rpf\\dlc_patch\\patchday1ng\\x64\\patch\\data\\lang\\chinesesimp.rpf") return null;
try try
{ {
ReadHeader(br); ReadHeader(br);
} catch } catch
{ {
return false; return null;
} }
GrandTotalRpfCount = 1; //count this file.. var fileCounts = new FileCounts
GrandTotalFileCount = 1; //start with this one. {
GrandTotalFolderCount = 0; Rpfs = 1,
GrandTotalResourceCount = 0; Files = 1
GrandTotalBinaryFileCount = 0; };
Children = new List<RpfFile>(); Children = new List<RpfFile>();
@ -383,32 +391,29 @@ namespace CodeWalker.GameFiles
subfile.Parent = this; subfile.Parent = this;
subfile.ParentFileEntry = binentry; subfile.ParentFileEntry = binentry;
if (subfile.ScanStructure(br, updateStatus, errorLog)) var result = subfile.ScanStructure(br, updateStatus, errorLog);
{
GrandTotalRpfCount += subfile.GrandTotalRpfCount;
GrandTotalFileCount += subfile.GrandTotalFileCount;
GrandTotalFolderCount += subfile.GrandTotalFolderCount;
GrandTotalResourceCount += subfile.GrandTotalResourceCount;
GrandTotalBinaryFileCount += subfile.GrandTotalBinaryFileCount;
if (result is not null)
{
fileCounts += result.Value;
Children.Add(subfile); Children.Add(subfile);
} }
} }
else else
{ {
//binary file that's not an rpf... //binary file that's not an rpf...
GrandTotalBinaryFileCount++; fileCounts.BinaryFiles++;
GrandTotalFileCount++; fileCounts.Files++;
} }
} }
else if (entry is RpfResourceFileEntry) else if (entry is RpfResourceFileEntry)
{ {
GrandTotalResourceCount++; fileCounts.Resources++;
GrandTotalFileCount++; fileCounts.Files++;
} }
else if (entry is RpfDirectoryEntry) else if (entry is RpfDirectoryEntry)
{ {
GrandTotalFolderCount++; fileCounts.Folders++;
} }
} }
catch (Exception ex) catch (Exception ex)
@ -416,7 +421,7 @@ namespace CodeWalker.GameFiles
errorLog?.Invoke(entry.Path + ": " + ex.ToString()); errorLog?.Invoke(entry.Path + ": " + ex.ToString());
} }
} }
return true; return fileCounts;
} }
@ -500,7 +505,7 @@ namespace CodeWalker.GameFiles
} }
else else
{ {
GTACrypto.DecryptNG(tbytes, resentry.Name, resentry.FileSize); GTACrypto.DecryptNG(tbytes.AsSpan(0, (int)totlen), resentry.Name, resentry.FileSize);
} }
@ -510,10 +515,8 @@ namespace CodeWalker.GameFiles
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = recyclableMemoryStreamManager.GetStream(); MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyToFast(outstr); ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer(); byte[] outbuf = outstr.ToArray();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
Array.Copy(deflated, outbuf, outbuf.Length);
bool pathok = true; bool pathok = true;
if (File.Exists(ofpath)) if (File.Exists(ofpath))
@ -604,11 +607,12 @@ namespace CodeWalker.GameFiles
{ {
LastError = ex.ToString(); LastError = ex.ToString();
LastException = ex; LastException = ex;
Console.WriteLine(ex);
return null; return null;
} }
} }
public ValueTask<byte[]> ExtractFileAsync(RpfFileEntry entry) public async ValueTask<byte[]> ExtractFileAsync(RpfFileEntry entry)
{ {
try try
{ {
@ -617,16 +621,16 @@ namespace CodeWalker.GameFiles
{ {
if (entry is RpfBinaryFileEntry binaryFileEntry) if (entry is RpfBinaryFileEntry binaryFileEntry)
{ {
return ExtractFileBinaryAsync(binaryFileEntry, br); return await ExtractFileBinaryAsync(binaryFileEntry, br).ConfigureAwait(false);
} }
else if (entry is RpfResourceFileEntry resourceFileEntry) else if (entry is RpfResourceFileEntry resourceFileEntry)
{ {
return ExtractFileResourceAsync(resourceFileEntry, br); return await ExtractFileResourceAsync(resourceFileEntry, br).ConfigureAwait(false);
} }
else else
{ {
Console.WriteLine($"{entry} is not a BinaryFileEntry of ResourceFileEntry"); Console.WriteLine($"{entry} is not a BinaryFileEntry of ResourceFileEntry");
return new ValueTask<byte[]>(); return null;
} }
} }
} }
@ -634,7 +638,7 @@ namespace CodeWalker.GameFiles
{ {
LastError = ex.ToString(); LastError = ex.ToString();
LastException = ex; LastException = ex;
return new ValueTask<byte[]>(); return null;
} }
} }
@ -665,7 +669,7 @@ namespace CodeWalker.GameFiles
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize, 0, (int)totlen); GTACrypto.DecryptNG(tbytes.AsSpan(0, (int)totlen), entry.Name, entry.FileUncompressedSize);
} }
} }
@ -712,7 +716,7 @@ namespace CodeWalker.GameFiles
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize, 0, (int)totlen); GTACrypto.DecryptNG(tbytes.AsSpan(0, (int)totlen), entry.Name, entry.FileUncompressedSize);
} }
} }
@ -758,7 +762,7 @@ namespace CodeWalker.GameFiles
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen); GTACrypto.DecryptNG(tbytes.AsSpan(0, (int)totlen), entry.Name, entry.FileSize);
} }
} }
@ -809,7 +813,7 @@ namespace CodeWalker.GameFiles
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize, 0, (int)totlen); GTACrypto.DecryptNG(tbytes.AsSpan(0, (int)totlen), entry.Name, entry.FileSize);
} }
} }
@ -1033,7 +1037,6 @@ namespace CodeWalker.GameFiles
public string TestExtractAllFiles() public string TestExtractAllFiles()
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
ExtractedByteCount = 0;
try try
{ {
using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath()))) using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath())))
@ -1068,10 +1071,6 @@ namespace CodeWalker.GameFiles
sb.AppendFormat("{0} : Decompressed output was empty.", entry.Path); sb.AppendFormat("{0} : Decompressed output was empty.", entry.Path);
sb.AppendLine(); sb.AppendLine();
} }
else
{
ExtractedByteCount += data.Length;
}
} }
else if (entry is RpfResourceFileEntry) else if (entry is RpfResourceFileEntry)
{ {
@ -1095,10 +1094,6 @@ namespace CodeWalker.GameFiles
sb.AppendFormat("{0} : Decompressed output was empty.", entry.Path); sb.AppendFormat("{0} : Decompressed output was empty.", entry.Path);
sb.AppendLine(); sb.AppendLine();
} }
else
{
ExtractedByteCount += data.Length;
}
} }
} }
} }
@ -1128,18 +1123,22 @@ namespace CodeWalker.GameFiles
public List<RpfFileEntry> GetFiles(string folder, bool recurse) public List<RpfFileEntry> GetFiles(string folder, bool recurse)
{ {
if (Root == null)
{
return new List<RpfFileEntry>();
}
List<RpfFileEntry> result = new List<RpfFileEntry>(); List<RpfFileEntry> result = new List<RpfFileEntry>();
string[] parts = folder.ToLowerInvariant().Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); //folder.AsSpan().Split
RpfDirectoryEntry dir = Root; RpfDirectoryEntry dir = Root;
for (int i = 0; i < parts.Length; i++) foreach(var part in folder.EnumerateSplit('\\'))
{ {
if (dir == null) break; if (part.Length == 0)
dir = FindSubDirectory(dir, parts[i]); continue;
dir = FindSubDirectory(dir, part);
} }
if (dir != null)
{
GetFiles(dir, result, recurse); GetFiles(dir, result, recurse);
}
return result; return result;
} }
public void GetFiles(RpfDirectoryEntry dir, List<RpfFileEntry> result, bool recurse) public void GetFiles(RpfDirectoryEntry dir, List<RpfFileEntry> result, bool recurse)
@ -1167,7 +1166,7 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < dir.Directories.Count; i++) for (int i = 0; i < dir.Directories.Count; i++)
{ {
var cdir = dir.Directories[i]; var cdir = dir.Directories[i];
if (cdir.Name.ToLowerInvariant() == name) if (cdir.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{ {
return cdir; return cdir;
} }
@ -1175,8 +1174,25 @@ namespace CodeWalker.GameFiles
return null; return null;
} }
private RpfDirectoryEntry FindSubDirectory(RpfDirectoryEntry dir, ReadOnlySpan<char> name)
{
if (dir == null) return null;
if (dir.Directories == null) return null;
for (int i = 0; i < dir.Directories.Count; i++)
{
var cdir = dir.Directories[i];
if (cdir.Name.AsSpan() == name || cdir.Name.AsSpan().Equals(name, StringComparison.OrdinalIgnoreCase))
{
return cdir;
}
}
return null;
}
private static readonly Stopwatch compression = new Stopwatch();
public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(256 * 1024, 1024 * 1024, 128 * 1024 * 1024, false, 256 * 1024 * 100, 1024 * 1024 * 128 * 4); public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(256 * 1024, 1024 * 1024, 128 * 1024 * 1024, false, 256 * 1024 * 100, 1024 * 1024 * 128 * 4);
public byte[] DecompressBytes(byte[] bytes) public byte[] DecompressBytes(byte[] bytes)
@ -1186,7 +1202,7 @@ namespace CodeWalker.GameFiles
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress); using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length); using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length);
ds.CopyToFast(outstr); ds.CopyTo(outstr, 524288);
byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output. byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output.
if (outbuf.Length <= bytes.Length) if (outbuf.Length <= bytes.Length)
@ -1212,14 +1228,8 @@ namespace CodeWalker.GameFiles
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress); using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length); using var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length);
await ds.CopyToFastAsync(outstr).ConfigureAwait(false); await ds.CopyToAsync(outstr, 524288).ConfigureAwait(false);
byte[] outbuf = outstr.ToArray(); //need to copy to the right size buffer for output. 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) if (outbuf.Length <= bytes.Length)
{ {
@ -1245,9 +1255,7 @@ namespace CodeWalker.GameFiles
{ {
ds.Write(data, 0, data.Length); ds.Write(data, 0, data.Length);
ds.Close(); ds.Close();
byte[] deflated = ms.GetBuffer(); byte[] outbuf = ms.ToArray(); //need to copy to the right size buffer...
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer...
Buffer.BlockCopy(deflated, 0, outbuf, 0, outbuf.Length);
return outbuf; return outbuf;
} }
} }
@ -1377,8 +1385,8 @@ namespace CodeWalker.GameFiles
} }
private byte[] GetHeaderNamesData() private byte[] GetHeaderNamesData()
{ {
MemoryStream namesstream = new MemoryStream(); using MemoryStream namesstream = new MemoryStream();
DataWriter nameswriter = new DataWriter(namesstream); using DataWriter nameswriter = new DataWriter(namesstream);
var namedict = new Dictionary<string, uint>(); var namedict = new Dictionary<string, uint>();
foreach (var entry in AllEntries) foreach (var entry in AllEntries)
{ {
@ -1395,10 +1403,10 @@ namespace CodeWalker.GameFiles
nameswriter.Write(name); nameswriter.Write(name);
} }
} }
var buf = new byte[namesstream.Length]; var buf = new byte[Math.Max(namesstream.Length, 16)];
namesstream.Position = 0; namesstream.Position = 0;
namesstream.Read(buf, 0, buf.Length); namesstream.Read(buf, 0, (int)namesstream.Length);
return PadBuffer(buf, 16); return buf;
} }
private byte[] GetHeaderEntriesData() private byte[] GetHeaderEntriesData()
{ {
@ -1419,18 +1427,6 @@ namespace CodeWalker.GameFiles
uint headerblockcount = GetBlockCount(headerusedbytes); uint headerblockcount = GetBlockCount(headerusedbytes);
return headerblockcount; return headerblockcount;
} }
private static byte[] PadBuffer(byte[] buf, uint n)//add extra bytes as necessary to nearest n
{
uint buflen = (uint)buf.Length;
uint newlen = PadLength(buflen, n);
if (newlen != buflen)
{
byte[] buf2 = new byte[newlen];
Buffer.BlockCopy(buf, 0, buf2, 0, buf.Length);
return buf2;
}
return buf;
}
private static uint PadLength(uint l, uint n)//round up to nearest n bytes private static uint PadLength(uint l, uint n)//round up to nearest n bytes
{ {
uint rem = l % n; uint rem = l % n;
@ -2430,7 +2426,10 @@ namespace CodeWalker.GameFiles
char ch = Name[i]; char ch = Name[i];
if (ch == '.') if (ch == '.')
{ {
extension = Name.Substring(i, length - i).ToLowerInvariant(); var result = Name.AsSpan(i, length - i);
Span<char> lowered = stackalloc char[result.Length];
result.ToLowerInvariant(lowered);
extension = lowered.ToString();
break; break;
} }
if (ch == System.IO.Path.DirectorySeparatorChar || ch == System.IO.Path.AltDirectorySeparatorChar) if (ch == System.IO.Path.DirectorySeparatorChar || ch == System.IO.Path.AltDirectorySeparatorChar)
@ -3006,7 +3005,7 @@ namespace CodeWalker.GameFiles
{ {
public uint Value { get; set; } public uint Value { get; set; }
public RpfResourcePage[] Pages public readonly RpfResourcePage[] Pages
{ {
get get
{ {
@ -3032,10 +3031,10 @@ namespace CodeWalker.GameFiles
} }
} }
public uint TypeVal { get { return (Value >> 28) & 0xF; } } public readonly uint TypeVal { get { return (Value >> 28) & 0xF; } }
public uint BaseShift { get { return (Value & 0xF); } } public readonly uint BaseShift { get { return (Value & 0xF); } }
public uint BaseSize { get { return (0x200u << (int)BaseShift); } } public readonly uint BaseSize { get { return (0x200u << (int)BaseShift); } }
public uint[] BaseSizes public readonly uint[] BaseSizes
{ {
get get
{ {
@ -3054,7 +3053,7 @@ namespace CodeWalker.GameFiles
}; };
} }
} }
public uint[] PageCounts public readonly uint[] PageCounts
{ {
get get
{ {
@ -3072,7 +3071,7 @@ namespace CodeWalker.GameFiles
}; };
} }
} }
public uint[] PageSizes public readonly uint[] PageSizes
{ {
get get
{ {
@ -3092,7 +3091,7 @@ namespace CodeWalker.GameFiles
}; };
} }
} }
public uint Count public readonly uint Count
{ {
get get
{ {
@ -3100,7 +3099,7 @@ namespace CodeWalker.GameFiles
return c[0] + c[1] + c[2] + c[3] + c[4] + c[5] + c[6] + c[7] + c[8]; return c[0] + c[1] + c[2] + c[3] + c[4] + c[5] + c[6] + c[7] + c[8];
} }
} }
public uint Size public readonly uint Size
{ {
get get
{ {
@ -3152,7 +3151,7 @@ namespace CodeWalker.GameFiles
return new RpfResourcePageFlags(v); return new RpfResourcePageFlags(v);
} }
public override string ToString() public override readonly string ToString()
{ {
return "Size: " + Size.ToString() + ", Pages: " + Count.ToString(); return "Size: " + Size.ToString() + ", Pages: " + Count.ToString();
} }

View File

@ -8,8 +8,10 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -42,6 +44,9 @@ namespace CodeWalker.GameFiles
public volatile bool IsInited = false; public volatile bool IsInited = false;
private const int DefaultEntryDictCapacity = 354878;
private const int DefaultRpfDictCapacity = 4650;
public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true) public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true)
{ {
using var _ = new DisposableTimer("RpfManager.Init"); using var _ = new DisposableTimer("RpfManager.Init");
@ -52,14 +57,14 @@ namespace CodeWalker.GameFiles
var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories; var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
string[] allfiles = Directory.GetFiles(folder, "*.rpf", sopt); string[] allfiles = Directory.GetFiles(folder, "*.rpf", sopt);
BaseRpfs = new List<RpfFile>(); BaseRpfs = new List<RpfFile>(1300);
ModRpfs = new List<RpfFile>(); ModRpfs = new List<RpfFile>(0);
DlcRpfs = new List<RpfFile>(); DlcRpfs = new List<RpfFile>(3500);
AllRpfs = new List<RpfFile>(); AllRpfs = new List<RpfFile>(5000);
DlcNoModRpfs = new List<RpfFile>(); DlcNoModRpfs = new List<RpfFile>(3500);
AllNoModRpfs = new List<RpfFile>(); AllNoModRpfs = new List<RpfFile>(5000);
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase); RpfDict = new Dictionary<string, RpfFile>(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase); EntryDict = new Dictionary<string, RpfEntry>(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase); ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase); ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
@ -88,6 +93,7 @@ namespace CodeWalker.GameFiles
if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF) if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF)
{ {
Console.WriteLine(rf.LastException);
return; return;
} }
@ -99,12 +105,10 @@ namespace CodeWalker.GameFiles
} }
}); });
var calculateSum = (RpfFile rpf) => { return 0; }; static int calculateSum(RpfFile rpf)
calculateSum = (RpfFile rpf) =>
{ {
return rpf.AllEntries?.Count ?? 0 + rpf.Children?.Sum(calculateSum) ?? 0; return rpf.AllEntries?.Count ?? 0 + rpf.Children?.Sum(calculateSum) ?? 0;
}; }
var minCapacity = rpfs.Sum(calculateSum); var minCapacity = rpfs.Sum(calculateSum);
if (minCapacity > AllRpfs.Capacity) if (minCapacity > AllRpfs.Capacity)
@ -119,13 +123,13 @@ namespace CodeWalker.GameFiles
if (buildIndex) if (buildIndex)
{ {
updateStatus?.Invoke("Building jenkindex...");
Task.Run(() => Task.Run(() =>
{ {
updateStatus?.Invoke("Building jenkindex...");
BuildBaseJenkIndex(); BuildBaseJenkIndex();
IsInited = true; IsInited = true;
});
updateStatus?.Invoke("Scan complete"); updateStatus?.Invoke("Scan complete");
});
} }
else else
{ {
@ -135,7 +139,7 @@ namespace CodeWalker.GameFiles
Console.WriteLine($"AllRpfs: {AllRpfs.Count}; RpfDict: {RpfDict.Count}; EntryDict: {EntryDict.Count}; BaseRpfs: {BaseRpfs.Count}; ModRpfs: {ModRpfs.Count}; DlcRpfs: {DlcRpfs.Count}; DlcNoModRpfs: {DlcNoModRpfs.Count}; AllNoModRpfs: {AllNoModRpfs.Count}; ModRpfDict: {ModRpfDict.Count}; ModEntryDict: {ModEntryDict.Count}");
} }
public void Init(List<RpfFile> allRpfs) public void Init(List<RpfFile> allRpfs)
@ -149,20 +153,23 @@ namespace CodeWalker.GameFiles
DlcRpfs = new List<RpfFile>(); DlcRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>(); DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>(); AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase); RpfDict = new Dictionary<string, RpfFile>(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase); EntryDict = new Dictionary<string, RpfEntry>(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase); ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase); ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var rpf in allRpfs) foreach (var rpf in allRpfs)
{ {
RpfDict[rpf.Path] = rpf; RpfDict[rpf.Path] = rpf;
if (rpf.AllEntries == null) continue; if (rpf.AllEntries == null)
continue;
foreach (var entry in rpf.AllEntries) foreach (var entry in rpf.AllEntries)
{ {
EntryDict[entry.Path] = entry; EntryDict[entry.Path] = entry;
} }
} }
Console.WriteLine($"RpfDict: {RpfDict.Count}; EntryDict: {EntryDict.Count}");
Task.Run(() => Task.Run(() =>
{ {
BuildBaseJenkIndex(); BuildBaseJenkIndex();
@ -173,7 +180,10 @@ namespace CodeWalker.GameFiles
private void AddRpfFile(RpfFile file, bool isdlc, bool ismod) private void AddRpfFile(RpfFile file, bool isdlc, bool ismod)
{ {
isdlc = isdlc || file.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase) || (file.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase) && file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)); if (file.AllEntries == null && file.Children == null)
return;
isdlc = isdlc || (file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase) && (file.Name.StartsWith("dlc", StringComparison.OrdinalIgnoreCase) || file.Name.Equals("update.rpf", StringComparison.OrdinalIgnoreCase)));
ismod = ismod || (file.Path.StartsWith("mods\\", StringComparison.OrdinalIgnoreCase)); ismod = ismod || (file.Path.StartsWith("mods\\", StringComparison.OrdinalIgnoreCase));
if (file.AllEntries != null) if (file.AllEntries != null)
@ -313,6 +323,7 @@ namespace CodeWalker.GameFiles
byte[] bytes = GetFileData(path); byte[] bytes = GetFileData(path);
return TextUtil.GetUTF8Text(bytes); return TextUtil.GetUTF8Text(bytes);
} }
public XmlDocument GetFileXml(string path) public XmlDocument GetFileXml(string path)
{ {
XmlDocument doc = new XmlDocument(); XmlDocument doc = new XmlDocument();
@ -321,6 +332,7 @@ namespace CodeWalker.GameFiles
{ {
doc.LoadXml(text); doc.LoadXml(text);
} }
return doc; return doc;
} }
@ -373,12 +385,12 @@ namespace CodeWalker.GameFiles
file.Load(data, entry); file.Load(data, entry);
} }
return file; return file;
} catch(Exception ex) }
catch(Exception ex)
{ {
Console.WriteLine(ex); Console.WriteLine(ex);
throw; throw;
} }
} }
public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile
{ {
@ -428,12 +440,96 @@ namespace CodeWalker.GameFiles
return false; return false;
} }
private ConcurrentDictionary<string, int> counts = new ConcurrentDictionary<string, int>();
public void AddAllLods(string name)
{
var idx = name.LastIndexOf('_');
if (idx > 0)
{
var str1 = name.AsSpan(0, idx);
var idx2 = str1.LastIndexOf('_');
if (idx2 > 0)
{
// Filter some peds and clothing models (ydd's) which don't have LOD hashes we're interested in.
// This saves about 50% of the time it takes to do initial hashing
var str2 = str1.Slice(0, idx2 + 1);
if (str2.Length <= 2)
{
return;
}
switch(str2)
{
case "uppr_":
case "p_":
case "accs_":
case "decl_":
case "berd_":
case "hair_":
case "teef_":
case "lowr_":
case "jbib_":
case "hand_":
case "feet_":
case "task_":
case "head_":
case "s_m_y_":
case "s_m_":
case "s_m_m_":
case "s_f_y_":
case "a_m_y_":
case "ig_":
case "u_m_y_":
case "u_m_m_":
case "u_m_":
case "minimap_":
case "a_":
case "u_f_":
case "csb_":
case "g_m_y_":
case "a_f_m_":
case "a_m_m_":
case "g_m_m_":
case "mp_m_":
case "mp_f_":
case "hand_000_":
case "hand_001_":
case "hair_000_":
case "a_f_y_":
return;
default:
break;
}
Span<char> buff = stackalloc char[str2.Length + 2 + 4];
str2.CopyTo(buff.Slice(0, str2.Length));
"lod".AsSpan().CopyTo(buff.Slice(str2.Length, 3));
//Console.WriteLine(buff.Slice(0, str2.Length + 3).ToString());
JenkIndex.EnsureLower(buff.Slice(0, str2.Length + 3));
var maxi = 99;
"00_lod".AsSpan().CopyTo(buff.Slice(str2.Length, 6));
for (int i = 1; i <= maxi; i++)
{
if (i < 10)
{
i.ToString().AsSpan().CopyTo(buff.Slice(str2.Length + 1, 1));
}
else
{
i.ToString().AsSpan().CopyTo(buff.Slice(str2.Length, 2));
}
//Console.WriteLine(buff.ToString());
//JenkIndex.Ensure(buff);
JenkIndex.EnsureLower(buff);
}
}
}
}
public void BuildBaseJenkIndex() public void BuildBaseJenkIndex()
{ {
using var _ = new DisposableTimer("BuildBaseJenkIndex"); using var _ = new DisposableTimer("BuildBaseJenkIndex");
Parallel.ForEach(AllRpfs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (file) => Parallel.ForEach(AllRpfs, (file) =>
{ {
try try
{ {
@ -475,24 +571,26 @@ namespace CodeWalker.GameFiles
JenkIndex.EnsureLower(nameChildrenLod + 'a'); JenkIndex.EnsureLower(nameChildrenLod + 'a');
JenkIndex.EnsureLower(nameChildrenLod + 'b'); JenkIndex.EnsureLower(nameChildrenLod + 'b');
} }
var idx = name.LastIndexOf('_'); //var idx = name.LastIndexOf('_');
if (idx > 0) //if (idx > 0)
{ //{
var str1 = name.Substring(0, idx); // var str1 = name.Substring(0, idx);
var idx2 = str1.LastIndexOf('_'); // var idx2 = str1.LastIndexOf('_');
if (idx2 > 0) // if (idx2 > 0)
{ // {
var str2 = str1.Substring(0, idx2); // var str2 = str1.Substring(0, idx2);
JenkIndex.EnsureLower(str2 + "_lod"); // JenkIndex.EnsureLower(str2 + "_lod");
var maxi = 100; // var maxi = 100;
for (int i = 1; i <= maxi; i++)
{ // for (int i = 1; i <= maxi; i++)
var str3 = str2 + '_' + i.ToString().PadLeft(2, '0') + "_lod"; // {
//JenkIndex.Ensure(str3); // var str3 = str2 + '_' + i.ToString().PadLeft(2, '0') + "_lod";
JenkIndex.EnsureLower(str3); // //JenkIndex.Ensure(str3);
} // JenkIndex.EnsureLower(str3);
} // }
} // }
//}
AddAllLods(name);
} }
else if(name.EndsWith(".sps", StringComparison.OrdinalIgnoreCase)) else if(name.EndsWith(".sps", StringComparison.OrdinalIgnoreCase))
{ {
@ -560,6 +658,7 @@ namespace CodeWalker.GameFiles
catch(Exception err) catch(Exception err)
{ {
ErrorLog?.Invoke(err.ToString()); ErrorLog?.Invoke(err.ToString());
Console.WriteLine(err.ToString());
//failing silently!! not so good really //failing silently!! not so good really
} }
}); });
@ -572,6 +671,14 @@ namespace CodeWalker.GameFiles
{ {
JenkIndex.Ensure(i.ToString("00")); JenkIndex.Ensure(i.ToString("00"));
} }
//Task.Run(() =>
//{
// foreach (var count in counts.OrderBy(p => p.Value))
// {
// Console.WriteLine($"{count.Key,30}: {count.Value,3}");
// }
//});
} }
} }

View File

@ -42,8 +42,8 @@ namespace CodeWalker.GameFiles
{ {
public enum Endianess public enum Endianess
{ {
LittleEndian, LittleEndian = 0,
BigEndian BigEndian = 1,
} }
public enum DataType public enum DataType
@ -64,16 +64,10 @@ namespace CodeWalker.GameFiles
{ {
private Stream baseStream; private Stream baseStream;
private readonly byte[] _buffer = new byte[8];
/// <summary> /// <summary>
/// Gets or sets the endianess of the underlying stream. /// Gets or sets the endianess of the underlying stream.
/// </summary> /// </summary>
public Endianess Endianess public Endianess Endianess { get; set; } = Endianess.LittleEndian;
{
get;
set;
}
/// <summary> /// <summary>
/// Gets the length of the underlying stream. /// Gets the length of the underlying stream.
@ -101,15 +95,19 @@ namespace CodeWalker.GameFiles
} }
} }
/// <summary> public DataReader(Stream stream)
/// Initializes a new data reader for the specified stream.
/// </summary>
public DataReader(Stream stream, Endianess endianess = Endianess.LittleEndian)
{ {
if (stream is not null) if (stream is not null)
{ {
this.baseStream = Stream.Synchronized(stream); this.baseStream = Stream.Synchronized(stream);
} }
}
/// <summary>
/// Initializes a new data reader for the specified stream.
/// </summary>
public DataReader(Stream stream, Endianess endianess) : this(stream)
{
this.Endianess = endianess; this.Endianess = endianess;
} }
@ -123,6 +121,25 @@ namespace CodeWalker.GameFiles
return; return;
} }
protected virtual void ReadFromStream(Span<byte> buffer, bool ignoreEndianess = false)
{
var stream = GetStream();
try
{
stream.Read(buffer);
}
finally
{
SetPositionAfterRead(stream);
}
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{
buffer.Reverse();
}
}
/// <summary> /// <summary>
/// Reads data from the underlying stream. This is the only method that directly accesses /// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream. /// the data in the underlying stream.
@ -187,7 +204,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public short ReadInt16() public short ReadInt16()
{ {
return BitConverter.ToInt16(ReadFromStream(2, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(short)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadInt16LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadInt16BigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -195,7 +222,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public int ReadInt32() public int ReadInt32()
{ {
return BitConverter.ToInt32(ReadFromStream(4, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(int)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadInt32LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadInt32BigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -203,7 +240,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public long ReadInt64() public long ReadInt64()
{ {
return BitConverter.ToInt64(ReadFromStream(8, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(long)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadInt64LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadInt64BigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -211,7 +258,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public ushort ReadUInt16() public ushort ReadUInt16()
{ {
return BitConverter.ToUInt16(ReadFromStream(2, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(ushort)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadUInt16LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadUInt16BigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -219,7 +276,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public uint ReadUInt32() public uint ReadUInt32()
{ {
return BitConverter.ToUInt32(ReadFromStream(4, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(uint)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadUInt32LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadUInt32BigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -227,7 +294,18 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public ulong ReadUInt64() public ulong ReadUInt64()
{ {
return BitConverter.ToUInt64(ReadFromStream(8, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(ulong)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadUInt64BigEndian(_buffer);
}
//return BitConverter.ToUInt64(ReadFromStream(_buffer), 0);
} }
/// <summary> /// <summary>
@ -235,7 +313,17 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public float ReadSingle() public float ReadSingle()
{ {
return BitConverter.ToSingle(ReadFromStream(4, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(float)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadSingleLittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadSingleBigEndian(_buffer);
}
} }
/// <summary> /// <summary>
@ -243,12 +331,23 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public double ReadDouble() public double ReadDouble()
{ {
return BitConverter.ToDouble(ReadFromStream(8, buffer: _buffer), 0); Span<byte> _buffer = stackalloc byte[sizeof(double)];
ReadFromStream(_buffer, true);
if (Endianess == Endianess.LittleEndian)
{
return BinaryPrimitives.ReadDoubleLittleEndian(_buffer);
}
else
{
return BinaryPrimitives.ReadDoubleBigEndian(_buffer);
}
} }
/// <summary> /// <summary>
/// Reads a string. /// Reads a string.
/// </summary> /// </summary>
[SkipLocalsInit]
unsafe public string ReadStringLength(int length) unsafe public string ReadStringLength(int length)
{ {
if (length == 0) if (length == 0)
@ -268,47 +367,30 @@ namespace CodeWalker.GameFiles
/// <summary> /// <summary>
/// Reads a string. /// Reads a string.
/// </summary> /// </summary>
[SkipLocalsInit]
unsafe public string ReadString(int maxLength = 1024) unsafe public string ReadString(int maxLength = 1024)
{ {
var bytes = stackalloc byte[Math.Min(maxLength, 1024)]; Span<byte> bytes = stackalloc byte[Math.Min(maxLength, 1024)];
var chars = stackalloc char[Math.Min(maxLength, 1024)]; Span<char> chars = stackalloc char[Math.Min(maxLength, 1024)];
var temp = ReadByte(); var temp = ReadByte();
var charsRead = 0; var bytesRead = 0;
while (temp != 0 && (Length == -1 || Position <= Length)) var length = Length;
while (temp != 0 && (length == -1 || Position <= length))
{ {
if (charsRead < maxLength && charsRead < 1024) if (bytesRead < maxLength && bytesRead < 1024)
{ {
bytes[charsRead] = temp; bytes[bytesRead] = temp;
} }
temp = ReadByte(); temp = ReadByte();
charsRead++; bytesRead++;
} }
var charsCount = Encoding.UTF8.GetChars(bytes, charsRead, chars, Math.Min(maxLength, 1024)); var charsRead = Encoding.UTF8.GetChars(bytes.Slice(0, bytesRead), chars);
return new string(chars, 0, charsCount); return chars.Slice(0, charsRead).ToString();
//return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength)); //return Encoding.UTF8.GetString(bytes, Math.Min(charsRead, maxLength));
} }
unsafe public string ReadStringLower()
{
var bytes = stackalloc byte[1024];
var temp = ReadByte();
var charsRead = 0;
while (temp != 0 && (Length == -1 || Position <= Length))
{
if (charsRead > 1023)
{
throw new Exception("String too long!");
}
bytes[charsRead] = temp;
temp = ReadByte();
charsRead++;
}
return Encoding.UTF8.GetString(bytes, charsRead);
}
public Vector3 ReadVector3() public Vector3 ReadVector3()
{ {

View File

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
@ -59,21 +60,33 @@ namespace CodeWalker.GameFiles
public static byte[] DecryptAESData(byte[] data, byte[] key, int length, int rounds = 1) public static byte[] DecryptAESData(byte[] data, byte[] key, int length, int rounds = 1)
{ {
var rijndael = Rijndael.Create(); using var aes = Aes.Create();
rijndael.KeySize = 256; aes.KeySize = 256;
rijndael.Key = key; aes.Key = key;
rijndael.BlockSize = 128; aes.BlockSize = 128;
rijndael.Mode = CipherMode.ECB; aes.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.None; aes.Padding = PaddingMode.None;
//var rijndael = Rijndael.Create();
//rijndael.KeySize = 256;
//rijndael.Key = key;
//rijndael.BlockSize = 128;
//rijndael.Mode = CipherMode.ECB;
//rijndael.Padding = PaddingMode.None;
length = length - length % 16; length = length - length % 16;
// decrypt... // decrypt...
if (length > 0) if (length > 0)
{ {
var decryptor = rijndael.CreateDecryptor(); using var decryptorAes = aes.CreateDecryptor();
for (var roundIndex = 0; roundIndex < rounds; roundIndex++) for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
decryptor.TransformBlock(data, 0, length, data, 0); {
decryptorAes.TransformBlock(data, 0, length, data, 0);
}
//var decryptor = rijndael.CreateDecryptor();
//for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
// decryptor.TransformBlock(data, 0, length, data, 0);
} }
return data; return data;
@ -104,7 +117,7 @@ namespace CodeWalker.GameFiles
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint[][] GetNGKey(string name, uint length) public static uint[][] GetNGKey(string name, uint length)
{ {
uint hash = GTA5Hash.CalculateHash(name); uint hash = GTA5Hash.CalculateHash(name);
@ -112,36 +125,39 @@ namespace CodeWalker.GameFiles
return GTA5Keys.PC_NG_KEYS[keyidx]; return GTA5Keys.PC_NG_KEYS[keyidx];
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] DecryptNG(byte[] data, string name, uint fileSize, int offset = 0, int? length = null) public static void DecryptNG(byte[] data, string name, uint fileSize, int offset = 0, int? length = null)
{ {
var key = GetNGKey(name, fileSize); var key = GetNGKey(name, fileSize);
return DecryptNG(data, key, offset, length); length ??= data.Length;
DecryptNG(data.AsSpan(offset, length.Value), key);
} }
public unsafe static byte[] DecryptNG(byte[] data, uint[][] key, int offset = 0, int? length = null) [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNG(Span<byte> data, string name, uint fileSize)
{
var key = GetNGKey(name, fileSize);
DecryptNG(data, key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void DecryptNG(byte[] data, uint[][] key, int offset = 0, int? length = null)
{ {
length ??= data.Length; length ??= data.Length;
fixed (byte* bptr = data) DecryptNG(data.AsSpan(offset, length.Value), key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNG(Span<byte> data, uint[][] key)
{ {
for (int blockIndex = offset * 16; blockIndex < length / 16; blockIndex++) for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
{ {
DecryptNGBlock(bptr + 16 * blockIndex, key); DecryptNGBlock(data.Slice(16 * blockIndex, 16), key);
} }
} }
return data; [MethodImpl(MethodImplOptions.AggressiveInlining)]
} public static void DecryptNGBlock(Span<byte> data, uint[][] key)
public unsafe static void DecryptNGBlock(byte[] data, uint[][] key)
{
fixed(byte* bptr = data)
{
DecryptNGBlock(data, key);
}
}
public unsafe static void DecryptNGBlock(byte* data, uint[][] key)
{ {
DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]); DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
DecryptNGRoundA(data, key[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]); DecryptNGRoundA(data, key[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
@ -150,24 +166,8 @@ namespace CodeWalker.GameFiles
DecryptNGRoundA(data, key[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]); DecryptNGRoundA(data, key[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
} }
public unsafe static void DecryptNGRoundA(byte[] data, uint[] key, uint[][] table) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static void DecryptNGRoundA(Span<byte> data, uint[] key, uint[][] table)
fixed(byte* bptr = data)
{
DecryptNGRoundA(bptr, key, table);
}
}
public unsafe static void DecryptNGRoundB(byte[] data, uint[] key, uint[][] table)
{
fixed (byte* bptr = data)
{
DecryptNGRoundB(bptr, key, table);
}
}
// round 1,2,16
public unsafe static void DecryptNGRoundA(byte* data, uint[] key, uint[][] table)
{ {
var x1 = var x1 =
table[0][data[0]] ^ table[0][data[0]] ^
@ -194,14 +194,16 @@ namespace CodeWalker.GameFiles
table[15][data[15]] ^ table[15][data[15]] ^
key[3]; key[3];
*(uint*)data = x1; MemoryMarshal.Write(data.Slice(0, 4), ref x1);
*(uint*)(data + 4) = x2; MemoryMarshal.Write(data.Slice(4, 4), ref x2);
*(uint*)(data + 8) = x3; MemoryMarshal.Write(data.Slice(8, 4), ref x3);
*(uint*)(data + 12) = x4; MemoryMarshal.Write(data.Slice(12, 4), ref x4);
} }
// round 3-15 // round 3-15
public unsafe static void DecryptNGRoundB(byte* data, uint[] key, uint[][] table)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void DecryptNGRoundB(Span<byte> data, uint[] key, uint[][] table)
{ {
var x1 = var x1 =
table[0][data[0]] ^ table[0][data[0]] ^
@ -228,10 +230,10 @@ namespace CodeWalker.GameFiles
table[12][data[12]] ^ table[12][data[12]] ^
key[3]; key[3];
*(uint*)data = x1; MemoryMarshal.Write(data.Slice(0, 4), ref x1);
*(uint*)(data + 4) = x2; MemoryMarshal.Write(data.Slice(4, 4), ref x2);
*(uint*)(data + 8) = x3; MemoryMarshal.Write(data.Slice(8, 4), ref x3);
*(uint*)(data + 12) = x4; MemoryMarshal.Write(data.Slice(12, 4), ref x4);
} }
@ -248,8 +250,6 @@ namespace CodeWalker.GameFiles
public static byte[] EncryptNG(byte[] data, string name, uint length) public static byte[] EncryptNG(byte[] data, string name, uint length)
{ {
var key = GetNGKey(name, length); var key = GetNGKey(name, length);
@ -396,136 +396,5 @@ namespace CodeWalker.GameFiles
}
public class DecryptNGStream : Stream
{
public string Name { get; set; }
private long _length = 0;
private Stream _baseStream;
public Stream BaseStream { get => _baseStream; private set => _baseStream = value; }
public uint[][] Key { get; set; }
private long offset = 0;
public override long Length
{
get
{
if (_length != 0) return _length;
return _baseStream.Length - offset;
}
}
private DecryptNGStream(string name, uint length)
{
_length = length;
Name = name;
Key = GTACrypto.GetNGKey(name, length);
}
public DecryptNGStream(byte[] data, string name, uint fileSize) : this(name, fileSize)
{
BaseStream = new MemoryStream(data);
}
public DecryptNGStream(Stream data, string name, uint fileSize) : this(name, fileSize)
{
BaseStream = data;
}
public DecryptNGStream(Stream data, string name, uint fileSize, int start, int length = 0): this(data, name, fileSize)
{
this.offset = start;
this._length = length;
}
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Position
{
get
{
var position = _baseStream.Position - offset;
position = position - position % 16;
return position + positionInBuffer;
}
set
{
positionInBuffer = 0;
_baseStream.Position = (value + offset);
ReadBlock();
}
}
public override void Flush()
{
return;
}
public void ReadBlock()
{
_baseStream.Read(_buffer, 0, _buffer.Length);
}
private byte[] _buffer = new byte[16];
private byte positionInBuffer = 0;
public override int Read(byte[] buffer, int offset, int count)
{
if (positionInBuffer > 0)
{
var toCopy = (byte)Math.Min(16 - positionInBuffer, count);
Array.Copy(_buffer, positionInBuffer, buffer, offset, toCopy);
positionInBuffer += toCopy;
if (positionInBuffer >= 16)
{
positionInBuffer = 0;
}
offset += toCopy;
count -= toCopy;
}
var i = 0;
while (count >= 16)
{
_baseStream.Read(buffer, offset + i, 16);
i += 16;
}
if (count > 0)
{
_baseStream.Read(_buffer, 0, 16);
GTACrypto.DecryptNG(_buffer, Key, 0, 16);
Array.Copy(_buffer, positionInBuffer, buffer, offset + i, count - i);
positionInBuffer += (byte)(count - i);
}
var data = _baseStream.Read(buffer, offset, count);
GTACrypto.DecryptNG(buffer, Key, offset, count);
return data;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -34,6 +34,8 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -69,15 +71,15 @@ namespace CodeWalker.GameFiles
public static void Generate(byte[] exeData, Action<string> updateStatus) //Stream exeStr)// public static void Generate(byte[] exeData, Action<string>? updateStatus) //Stream exeStr)//
{ {
var exeStr = new MemoryStream(exeData); var exeStr = Stream.Synchronized(new MemoryStream(exeData));
updateStatus("Searching for AES key..."); updateStatus?.Invoke("Searching for AES key...");
PC_AES_KEY = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_AES_KEY_HASH, 32); PC_AES_KEY = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_AES_KEY_HASH, 32);
//updateStatus("aes key found"); //updateStatus("aes key found");
updateStatus("Searching for NG keys..."); updateStatus?.Invoke("Searching for NG keys...");
//PC_NG_KEYS = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 272); //PC_NG_KEYS = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 272);
var tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 272); var tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 272);
PC_NG_KEYS = new uint[tabs.Length][][]; PC_NG_KEYS = new uint[tabs.Length][][];
@ -92,31 +94,21 @@ namespace CodeWalker.GameFiles
} }
//updateStatus("ng keys found"); //updateStatus("ng keys found");
updateStatus("Searching for NG decrypt tables..."); updateStatus?.Invoke("Searching for NG decrypt tables...");
tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_DECRYPT_TABLE_HASHES, 1024); tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_DECRYPT_TABLE_HASHES, 1024);
//updateStatus("ng decrypt tables found"); //updateStatus("ng decrypt tables found");
updateStatus("Searching for NG hash lookup tables..."); updateStatus?.Invoke("Searching for NG hash lookup tables...");
// 17 rounds // 17 rounds
PC_NG_DECRYPT_TABLES = new uint[17][][]; LoadPCNGDecryptTable(tabs);
for (int i = 0; i < 17; i++)
{
//
PC_NG_DECRYPT_TABLES[i] = new uint[16][];
for (int j = 0; j < 16; j++)
{
var buf = tabs[j + 16 * i];
PC_NG_DECRYPT_TABLES[i][j] = new uint[256];
Buffer.BlockCopy(buf, 0, PC_NG_DECRYPT_TABLES[i][j], 0, 1024);
}
}
PC_LUT = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_LUT_HASH, 0x100); PC_LUT = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_LUT_HASH, 0x100);
//updateStatus("ng hash LUTs found"); //updateStatus("ng hash LUTs found");
updateStatus("Calculating NG encryption tables..."); updateStatus?.Invoke("Calculating NG encryption tables...");
PC_NG_ENCRYPT_TABLES = new uint[17][][]; PC_NG_ENCRYPT_TABLES = new uint[17][][];
for (int i = 0; i < 17; i++) for (int i = 0; i < 17; i++)
{ {
@ -142,28 +134,62 @@ namespace CodeWalker.GameFiles
updateStatus("Calculating NG encryption tables (1/17)..."); updateStatus?.Invoke("Calculating NG encryption tables (1/17)...");
PC_NG_ENCRYPT_TABLES[0] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[0]); PC_NG_ENCRYPT_TABLES[0] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[0]);
//updateStatus("ng encrypt table 1 of 17 calculated"); //updateStatus("ng encrypt table 1 of 17 calculated");
updateStatus("Calculating NG encryption tables (2/17)..."); updateStatus?.Invoke("Calculating NG encryption tables (2/17)...");
PC_NG_ENCRYPT_TABLES[1] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[1]); PC_NG_ENCRYPT_TABLES[1] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[1]);
//updateStatus("ng encrypt table 2 of 17 calculated"); //updateStatus("ng encrypt table 2 of 17 calculated");
for (int k = 2; k <= 15; k++) for (int k = 2; k <= 15; k++)
{ {
updateStatus("Calculating NG encryption tables (" + (k + 1).ToString() + "/17)..."); updateStatus?.Invoke("Calculating NG encryption tables (" + (k + 1).ToString() + "/17)...");
PC_NG_ENCRYPT_LUTs[k] = LookUpTableGenerator.BuildLUTs2(PC_NG_DECRYPT_TABLES[k]); PC_NG_ENCRYPT_LUTs[k] = LookUpTableGenerator.BuildLUTs2(PC_NG_DECRYPT_TABLES[k]);
//updateStatus("ng encrypt table " + (k + 1).ToString() + " of 17 calculated"); //updateStatus("ng encrypt table " + (k + 1).ToString() + " of 17 calculated");
} }
updateStatus("Calculating NG encryption tables (17/17)..."); updateStatus?.Invoke("Calculating NG encryption tables (17/17)...");
PC_NG_ENCRYPT_TABLES[16] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[16]); PC_NG_ENCRYPT_TABLES[16] = RandomGauss.Solve(PC_NG_DECRYPT_TABLES[16]);
//updateStatus("ng encrypt table 17 of 17 calculated"); //updateStatus("ng encrypt table 17 of 17 calculated");
updateStatus("Complete."); updateStatus?.Invoke("Complete.");
} }
public static void LoadPCNGDecryptTable(Span<byte> rawFlattenedKey)
{
PC_NG_DECRYPT_TABLES = new uint[17][][];
var flattenedKey = MemoryMarshal.Cast<byte, uint>(rawFlattenedKey);
for (int i = 0; i < 17; i++)
{
PC_NG_DECRYPT_TABLES[i] = new uint[16][];
for (int j = 0; j < 16; j++)
{
var buf = flattenedKey.Slice(0, 256);
flattenedKey = flattenedKey.Slice(256);
PC_NG_DECRYPT_TABLES[i][j] = new uint[256];
buf.CopyTo(PC_NG_DECRYPT_TABLES[i][j].AsSpan());
//Buffer.BlockCopy(buf, 0, PC_NG_DECRYPT_TABLES[i][j], 0, 1024);
}
}
}
public static void LoadPCNGDecryptTable(byte[][] rawKey)
{
PC_NG_DECRYPT_TABLES = new uint[17][][];
for (int i = 0; i < 17; i++)
{
//
PC_NG_DECRYPT_TABLES[i] = new uint[16][];
for (int j = 0; j < 16; j++)
{
var buf = rawKey[j + 16 * i];
PC_NG_DECRYPT_TABLES[i][j] = new uint[256];
Buffer.BlockCopy(buf, 0, PC_NG_DECRYPT_TABLES[i][j], 0, 1024);
}
}
}
public static void GenerateV2(byte[] exeData, Action<string> updateStatus) public static void GenerateV2(byte[] exeData, Action<string> updateStatus)
{ {
@ -175,10 +201,14 @@ namespace CodeWalker.GameFiles
updateStatus?.Invoke("Complete."); updateStatus?.Invoke("Complete.");
} }
private static bool keysLoaded = false;
public static void LoadFromPath(string path = ".\\Keys", string key = null) public static void LoadFromPath(string path = ".\\Keys", string key = null)
{ {
if (keysLoaded)
{
return;
}
//PC_AES_KEY = File.ReadAllBytes(path + "\\gtav_aes_key.dat"); //PC_AES_KEY = File.ReadAllBytes(path + "\\gtav_aes_key.dat");
//PC_NG_KEYS = CryptoIO.ReadNgKeys(path + "\\gtav_ng_key.dat"); //PC_NG_KEYS = CryptoIO.ReadNgKeys(path + "\\gtav_ng_key.dat");
//PC_NG_DECRYPT_TABLES = CryptoIO.ReadNgTables(path + "\\gtav_ng_decrypt_tables.dat"); //PC_NG_DECRYPT_TABLES = CryptoIO.ReadNgTables(path + "\\gtav_ng_decrypt_tables.dat");
@ -188,6 +218,7 @@ namespace CodeWalker.GameFiles
//GenerateMagicData(path); //GenerateMagicData(path);
UseMagicData(path, key); UseMagicData(path, key);
keysLoaded = true;
} }
public static void SaveToPath(string path = ".\\Keys") public static void SaveToPath(string path = ".\\Keys")
@ -298,7 +329,7 @@ namespace CodeWalker.GameFiles
{ {
using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream()) using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream())
{ {
ds.CopyToFast(outstr); ds.CopyTo(outstr);
b = outstr.GetBuffer(); b = outstr.GetBuffer();
} }
} }
@ -760,11 +791,12 @@ namespace CodeWalker.GameFiles
public static byte[][] SearchHashes(Stream stream, IList<byte[]> hashes, int length = 32) public static byte[][] SearchHashes(Stream stream, IList<byte[]> hashes, int length = 32)
{ {
stream = Stream.Synchronized(stream);
var result = new byte[hashes.Count][]; var result = new byte[hashes.Count][];
Parallel.For(0, (stream.Length / BLOCK_LENGTH), (long k) => { Parallel.For(0, (stream.Length / BLOCK_LENGTH), (long k) => {
var hashProvider = new SHA1CryptoServiceProvider(); var hashProvider = new SHA1CryptoServiceProvider();
// TODO: Convert to stack alloc when length appropriate (1024 or lower probs)
var buffer = new byte[length]; var buffer = new byte[length];
for (long i = 0; i < (BLOCK_LENGTH / ALIGN_LENGTH); i++) for (long i = 0; i < (BLOCK_LENGTH / ALIGN_LENGTH); i++)
{ {
@ -772,11 +804,9 @@ namespace CodeWalker.GameFiles
if (position >= stream.Length) if (position >= stream.Length)
continue; continue;
lock (stream)
{
stream.Position = position; stream.Position = position;
stream.Read(buffer, 0, length); stream.Read(buffer, 0, length);
}
var hash = hashProvider.ComputeHash(buffer); var hash = hashProvider.ComputeHash(buffer);
for (int j = 0; j < hashes.Count; j++) for (int j = 0; j < hashes.Count; j++)
@ -852,7 +882,7 @@ namespace CodeWalker.GameFiles
firstPivot.SetB(); firstPivot.SetB();
pivots.Add(firstPivot); pivots.Add(firstPivot);
var buf_encrypted = new byte[16]; Span<byte> buf_encrypted = stackalloc byte[16];
for (int pivotIdx = 1; pivotIdx < 1024; pivotIdx++) for (int pivotIdx = 1; pivotIdx < 1024; pivotIdx++)
{ {
while (true) while (true)
@ -1221,7 +1251,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyToFast(fs); ms.CopyTo(fs);
} }
} }
@ -1308,7 +1338,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyToFast(fs); ms.CopyTo(fs);
} }
} }
@ -1446,7 +1476,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create)) using (var fs = new FileStream(fileName, FileMode.Create))
{ {
ms.Position = 0; ms.Position = 0;
ms.CopyToFast(fs); ms.CopyTo(fs);
} }
} }
} }
@ -1463,6 +1493,7 @@ namespace CodeWalker.GameFiles
LUT = GTA5Keys.PC_LUT; LUT = GTA5Keys.PC_LUT;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint CalculateHash(string text) public static uint CalculateHash(string text)
{ {
/* /*

View File

@ -276,7 +276,8 @@ namespace CodeWalker.GameFiles
public static class JenkIndex public static class JenkIndex
{ {
public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(32, 1500000); //public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount * 2, 2000000);
public static Dictionary<uint, string> Index = new Dictionary<uint, string>(2000000);
public static void Ensure(string str) public static void Ensure(string str)
{ {
@ -293,7 +294,25 @@ namespace CodeWalker.GameFiles
return; return;
} }
Index.TryAdd(hash, str); lock(Index)
{
Index[hash] = str;
}
}
public static void Ensure(ReadOnlySpan<char> str, uint hash)
{
if (hash == 0) return;
if (Index.ContainsKey(hash))
{
return;
}
lock(Index)
{
Index[hash] = str.ToString();
}
} }
public static void EnsureLower(string str) public static void EnsureLower(string str)
@ -302,6 +321,12 @@ namespace CodeWalker.GameFiles
Ensure(str, hash); Ensure(str, hash);
} }
public static void EnsureLower(ReadOnlySpan<char> str)
{
uint hash = JenkHash.GenHashLower(str);
Ensure(str, hash);
}
public static void EnsureBoth(string str) public static void EnsureBoth(string str)
{ {
uint hash = JenkHash.GenHash(str); uint hash = JenkHash.GenHash(str);
@ -312,25 +337,15 @@ namespace CodeWalker.GameFiles
Ensure(str, hashLower); Ensure(str, hashLower);
} }
} }
public static void AddRange(params string[] strings)
{
foreach(var s in strings)
{
uint hash = JenkHash.GenHash(s);
if (hash == 0) continue;
Index[hash] = s; public static void EnsureBoth(ReadOnlySpan<char> str)
}
}
public static void AddRangeLower(params string[] strings)
{ {
foreach (var s in strings) uint hash = JenkHash.GenHash(str);
uint hashLower = JenkHash.GenHashLower(str);
Ensure(str, hash);
if (hash != hashLower)
{ {
uint hash = JenkHash.GenHashLower(s); Ensure(str, hashLower);
if (hash == 0) continue;
Index[hash] = s;
} }
} }

View File

@ -196,10 +196,6 @@ namespace CodeWalker.GameFiles
var rel3 = XmlRel.GetRel(relxml); var rel3 = XmlRel.GetRel(relxml);
if (rel3 != null) if (rel3 != null)
{ {
if (rel3.RelDatasSorted?.Length != rel.RelDatasSorted?.Length)
{ } //check nothing went missing...
data = rel3.Save(); //full roundtrip! data = rel3.Save(); //full roundtrip!
if (data != null) if (data != null)
{ {

View File

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils;
[EventSource(Name = "CodeWalker-Diagnostics")]
public class ETWEvents : EventSource
{
public static class Keywords
{
public const EventKeywords ComponentLifespan = (EventKeywords)1;
public const EventKeywords StateChanges = (EventKeywords)(1 << 1);
public const EventKeywords Performance = (EventKeywords)(1 << 2);
public const EventKeywords DumpState = (EventKeywords)(1 << 3);
public const EventKeywords StateTracking = (EventKeywords)(1 << 4);
}
internal static class DebugCounters
{
}
public ETWEvents(bool throwOnEventWriteErrors) : base(throwOnEventWriteErrors)
{ }
[Event(1, Message = "Starting up.", Keywords = Keywords.Performance, Level = EventLevel.Informational)]
public void Startup() {
WriteEvent(1);
}
[Event(2, Message = "Creating form {0}", Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void CreatingForm(string form) { WriteEvent(2, form); }
[Event(3, Message = "Loading form {0}", Keywords = Keywords.Performance | Keywords.StateChanges, Level = EventLevel.Verbose)]
public void LoadingForm(string form) { WriteEvent(3, form); }
public static readonly ETWEvents Log = new ETWEvents(true);
}

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils
{
/// <summary>
/// Enumerates the lines of a <see cref="ReadOnlySpan{Char}"/>.
/// </summary>
/// <remarks>
/// To get an instance of this type, use <see cref="MemoryExtensions.EnumerateLines(ReadOnlySpan{char})"/>.
/// </remarks>
public ref struct SpanSplitEnumerator
{
private ReadOnlySpan<char> _remaining;
private ReadOnlySpan<char> _current;
private bool _isEnumeratorActive;
private char _splitBy;
internal SpanSplitEnumerator(ReadOnlySpan<char> buffer, char splitBy)
{
_remaining = buffer;
_current = default;
_isEnumeratorActive = true;
_splitBy = splitBy;
}
/// <summary>
/// Gets the line at the current position of the enumerator.
/// </summary>
public ReadOnlySpan<char> Current => _current;
/// <summary>
/// Returns this instance as an enumerator.
/// </summary>
public SpanSplitEnumerator GetEnumerator() => this;
/// <summary>
/// Advances the enumerator to the next line of the span.
/// </summary>
/// <returns>
/// True if the enumerator successfully advanced to the next line; false if
/// the enumerator has advanced past the end of the span.
/// </returns>
public bool MoveNext()
{
if (!_isEnumeratorActive)
{
return false; // EOF previously reached or enumerator was never initialized
}
ReadOnlySpan<char> remaining = _remaining;
int idx = remaining.IndexOf(_splitBy);
if ((uint)idx < (uint)remaining.Length)
{
_current = remaining.Slice(0, idx);
_remaining = remaining.Slice(idx + 1);
}
else
{
// We've reached EOF, but we still need to return 'true' for this final
// iteration so that the caller can query the Current property once more.
_current = remaining;
_remaining = default;
_isEnumeratorActive = false;
}
return true;
}
}
public static class EnumerateSplitExtensions
{
public static SpanSplitEnumerator EnumerateSplit(this ReadOnlySpan<char> span, char splitBy)
{
return new SpanSplitEnumerator(span, splitBy);
}
public static SpanSplitEnumerator EnumerateSplit(this Span<char> span, char splitBy)
{
return new SpanSplitEnumerator(span, splitBy);
}
public static SpanSplitEnumerator EnumerateSplit(this string str, char splitBy)
{
return new SpanSplitEnumerator(str.AsSpan(), splitBy);
}
}
}

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.IO; using System.IO;
@ -16,152 +17,45 @@ namespace CodeWalker.Core.Utils
{ {
return br.BaseStream.ReadAsync(buffer, index, 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) public ref struct SpanStream
{ {
var buffer = ArrayPool<byte>.Shared.Rent(bufferSize); public Span<byte> Buffer { get; private set; }
try private int _position;
public SpanStream(Span<byte> buffer)
{ {
int bytesRead; Buffer = buffer;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) _position = 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 ReadOnlySpan<byte> InternalRead(int count)
{
int origPos = _position;
int newPos = origPos + count;
if ((uint)newPos > (uint)Buffer.Length)
{
_position = Buffer.Length;
ThrowHelper.ThrowEndOfFileException();
} }
private static async Task FinishWriteAsync(Task writeTask, byte[] localBuffer) var span = Buffer.Slice(origPos, count);
{ _position = newPos;
try return span;
{
await writeTask.ConfigureAwait(false);
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
} }
public static ValueTask WriteAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default) public short ReadInt16() => BinaryPrimitives.ReadInt16LittleEndian(InternalRead(sizeof(short)));
{
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); public ushort ReadUInt16() => BinaryPrimitives.ReadUInt16LittleEndian(InternalRead(sizeof(ushort)));
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) public int ReadInt32() => BinaryPrimitives.ReadInt32LittleEndian(InternalRead(sizeof(int)));
{ public uint ReadUInt32() => BinaryPrimitives.ReadUInt32LittleEndian(InternalRead(sizeof(uint)));
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length); public long ReadInt64() => BinaryPrimitives.ReadInt64LittleEndian(InternalRead(sizeof(long)));
try public ulong ReadUInt64() => BinaryPrimitives.ReadUInt64LittleEndian(InternalRead(sizeof(ulong)));
{ public unsafe Half ReadHalf() => BinaryPrimitives.ReadHalfLittleEndian(InternalRead(sizeof(Half)));
buffer.CopyTo(sharedBuffer); public unsafe float ReadSingle() => BinaryPrimitives.ReadSingleLittleEndian(InternalRead(sizeof(float)));
stream.Write(sharedBuffer, 0, buffer.Length); public unsafe double ReadDouble() => BinaryPrimitives.ReadDoubleLittleEndian(InternalRead(sizeof(double)));
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Span<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Memory<byte> buffer)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return stream.Read(array.Array!, array.Offset, array.Count);
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer.Span);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static ValueTask<int> ReadAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return new ValueTask<int>(stream.ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken));
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer);
static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
{
try
{
int result = await readTask.ConfigureAwait(false);
new ReadOnlySpan<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
return result;
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
}
}
} }
} }

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Core.Utils
{
internal class ThrowHelper
{
internal static Exception CreateEndOfFileException() =>
new EndOfStreamException("Tried to read stream beyond end");
[DoesNotReturn]
internal static void ThrowEndOfFileException()
{
throw CreateEndOfFileException();
}
}
}

View File

@ -10,6 +10,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using SharpDX; using SharpDX;
using Color = SharpDX.Color; using Color = SharpDX.Color;
using Half = SharpDX.Half;
namespace CodeWalker namespace CodeWalker
{ {
@ -86,24 +87,39 @@ namespace CodeWalker
public static string GetUTF8Text(byte[] bytes) public static string GetUTF8Text(Span<byte> bytes)
{ {
if (bytes == null) if (bytes == null || bytes.Length == 0)
{ return string.Empty; } //file not found.. {
var start = 0; return string.Empty;
var length = bytes.Length; } //file not found..
if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF)) if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF))
{ {
start = 3; bytes = bytes.Slice(3);
length = bytes.Length - 3;
} }
return Encoding.UTF8.GetString(bytes, start, length);
if (bytes.Length == 0)
{
return string.Empty;
}
return Encoding.UTF8.GetString(bytes);
} }
public static bool Contains(this string source, string toCheck, StringComparison comp) public static bool Contains(this string source, string toCheck, StringComparison comp)
{ {
return source?.IndexOf(toCheck, comp) >= 0; return source?.IndexOf(toCheck, comp) >= 0;
} }
public static bool EndsWithAny(this string str, string searchString)
{
return str.EndsWith(searchString, StringComparison.OrdinalIgnoreCase);
}
public static bool EndsWithAny(this string str, string searchString, string searchString2)
{
return str.EndsWith(searchString, StringComparison.OrdinalIgnoreCase) || str.EndsWith(searchString2, StringComparison.OrdinalIgnoreCase);
}
public static bool EndsWithAny(this string str, params string[] strings) public static bool EndsWithAny(this string str, params string[] strings)
{ {
foreach(var searchString in strings) foreach(var searchString in strings)

View File

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

View File

@ -44,13 +44,13 @@ namespace CodeWalker.World
public YmapEntityDef RenderEntity = new YmapEntityDef(); //placeholder entity object for rendering public YmapEntityDef RenderEntity = new YmapEntityDef(); //placeholder entity object for rendering
public void Init(string name, GameFileCache gfc) public async ValueTask InitAsync(string name, GameFileCache gfc)
{ {
var hash = JenkHash.GenHash(name.ToLowerInvariant()); var hash = JenkHash.GenHash(name.ToLowerInvariant());
Init(hash, gfc); await InitAsync(hash, gfc);
Name = name; Name = name;
} }
public void Init(MetaHash pedhash, GameFileCache gfc) public async ValueTask InitAsync(MetaHash pedhash, GameFileCache gfc)
{ {
Name = string.Empty; Name = string.Empty;
@ -106,11 +106,11 @@ namespace CodeWalker.World
RpfFileEntry clothFile = null; RpfFileEntry clothFile = null;
if (ClothFilesDict?.TryGetValue(pedhash, out clothFile) ?? false) if (ClothFilesDict?.TryGetValue(pedhash, out clothFile) ?? false)
{ {
Yld = gfc.GetFileUncached<YldFile>(clothFile); Yld = await gfc.GetFileUncachedAsync<YldFile>(clothFile);
while ((Yld != null) && (!Yld.Loaded)) while ((Yld != null) && (!Yld.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
gfc.TryLoadEnqueue(Yld); await gfc.TryLoadEnqueue(Yld);
} }
} }
@ -118,27 +118,27 @@ namespace CodeWalker.World
while ((Ydd != null) && (!Ydd.Loaded)) while ((Ydd != null) && (!Ydd.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
Ydd = gfc.GetYdd(pedhash); Ydd = gfc.GetYdd(pedhash);
} }
while ((Ytd != null) && (!Ytd.Loaded)) while ((Ytd != null) && (!Ytd.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
Ytd = gfc.GetYtd(pedhash); Ytd = gfc.GetYtd(pedhash);
} }
while ((Ycd != null) && (!Ycd.Loaded)) while ((Ycd != null) && (!Ycd.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
Ycd = gfc.GetYcd(ycdhash); Ycd = gfc.GetYcd(ycdhash);
} }
while ((Yed != null) && (!Yed.Loaded)) while ((Yed != null) && (!Yed.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
Yed = gfc.GetYed(yedhash); Yed = gfc.GetYed(yedhash);
} }
while ((Yft != null) && (!Yft.Loaded)) while ((Yft != null) && (!Yft.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
Yft = gfc.GetYft(pedhash); Yft = gfc.GetYft(pedhash);
} }
@ -150,7 +150,7 @@ namespace CodeWalker.World
Ycd?.ClipMap?.TryGetValue(cliphash, out cme); Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
AnimClip = cme; AnimClip = cme;
var exprhash = JenkHash.GenHash(initdata.ExpressionName.ToLowerInvariant()); var exprhash = JenkHash.GenHashLower(initdata.ExpressionName);
Expression expr = null; Expression expr = null;
Yed?.ExprMap?.TryGetValue(exprhash, out expr); Yed?.ExprMap?.TryGetValue(exprhash, out expr);
Expression = expr; Expression = expr;
@ -163,7 +163,7 @@ namespace CodeWalker.World
public void SetComponentDrawable(int index, string name, string tex, GameFileCache gfc) public async ValueTask SetComponentDrawableAsync(int index, string name, string tex, GameFileCache gfc)
{ {
if (string.IsNullOrEmpty(name)) if (string.IsNullOrEmpty(name))
{ {
@ -174,7 +174,7 @@ namespace CodeWalker.World
return; return;
} }
MetaHash namehash = JenkHash.GenHash(name.ToLowerInvariant()); MetaHash namehash = JenkHash.GenHashLower(name);
Drawable d = null; Drawable d = null;
if (Ydd?.Dict != null) if (Ydd?.Dict != null)
{ {
@ -185,11 +185,11 @@ namespace CodeWalker.World
RpfFileEntry file = null; RpfFileEntry file = null;
if (DrawableFilesDict.TryGetValue(namehash, out file)) if (DrawableFilesDict.TryGetValue(namehash, out file))
{ {
var ydd = gfc.GetFileUncached<YddFile>(file); var ydd = await gfc.GetFileUncachedAsync<YddFile>(file);
while ((ydd != null) && (!ydd.Loaded)) while ((ydd != null) && (!ydd.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
gfc.TryLoadEnqueue(ydd); await gfc.TryLoadEnqueue(ydd);
} }
if (ydd?.Drawables?.Length > 0) if (ydd?.Drawables?.Length > 0)
{ {
@ -209,11 +209,11 @@ namespace CodeWalker.World
RpfFileEntry file = null; RpfFileEntry file = null;
if (TextureFilesDict.TryGetValue(texhash, out file)) if (TextureFilesDict.TryGetValue(texhash, out file))
{ {
var ytd = gfc.GetFileUncached<YtdFile>(file); var ytd = await gfc.GetFileUncachedAsync<YtdFile>(file);
while ((ytd != null) && (!ytd.Loaded)) while ((ytd != null) && (!ytd.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
gfc.TryLoadEnqueue(ytd); await gfc.TryLoadEnqueue(ytd);
} }
if (ytd?.TextureDict?.Textures?.data_items.Length > 0) if (ytd?.TextureDict?.Textures?.data_items.Length > 0)
{ {
@ -232,11 +232,11 @@ namespace CodeWalker.World
RpfFileEntry file = null; RpfFileEntry file = null;
if (ClothFilesDict.TryGetValue(namehash, out file)) if (ClothFilesDict.TryGetValue(namehash, out file))
{ {
var yld = gfc.GetFileUncached<YldFile>(file); var yld = await gfc.GetFileUncachedAsync<YldFile>(file);
while ((yld != null) && (!yld.Loaded)) while ((yld != null) && (!yld.Loaded))
{ {
Thread.Sleep(1);//kinda hacky await Task.Delay(1);//kinda hacky
gfc.TryLoadEnqueue(yld); await gfc.TryLoadEnqueue(yld);
} }
if (yld?.ClothDictionary?.Clothes?.data_items?.Length > 0) if (yld?.ClothDictionary?.Clothes?.data_items?.Length > 0)
{ {
@ -266,7 +266,7 @@ namespace CodeWalker.World
DrawableNames[index] = name; DrawableNames[index] = name;
} }
public void SetComponentDrawable(int index, int drawbl, int alt, int tex, GameFileCache gfc) public async ValueTask SetComponentDrawableAsync(int index, int drawbl, int alt, int tex, GameFileCache gfc)
{ {
var vi = Ymt?.VariationInfo; var vi = Ymt?.VariationInfo;
if (vi != null) if (vi != null)
@ -279,17 +279,17 @@ namespace CodeWalker.World
{ {
var name = item?.GetDrawableName(alt); var name = item?.GetDrawableName(alt);
var texn = item?.GetTextureName(tex); var texn = item?.GetTextureName(tex);
SetComponentDrawable(index, name, texn, gfc); await SetComponentDrawableAsync(index, name, texn, gfc);
} }
} }
} }
} }
public void LoadDefaultComponents(GameFileCache gfc) public async ValueTask LoadDefaultComponentsAsync(GameFileCache gfc)
{ {
for (int i = 0; i < 12; i++) for (int i = 0; i < 12; i++)
{ {
SetComponentDrawable(i, 0, 0, 0, gfc); await SetComponentDrawableAsync(i, 0, 0, 0, gfc);
} }
} }

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>CWPeds.ico</ApplicationIcon> <ApplicationIcon>CWPeds.ico</ApplicationIcon>
<Copyright>dexyfex</Copyright> <Copyright>dexyfex</Copyright>
@ -10,18 +9,13 @@
<Authors>dexyfex</Authors> <Authors>dexyfex</Authors>
<AssemblyName>CodeWalker Ped Viewer</AssemblyName> <AssemblyName>CodeWalker Ped Viewer</AssemblyName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>CWRPFExplorer.ico</ApplicationIcon> <ApplicationIcon>CWRPFExplorer.ico</ApplicationIcon>
@ -12,21 +11,16 @@
<AssemblyName>CodeWalker RPF Explorer</AssemblyName> <AssemblyName>CodeWalker RPF Explorer</AssemblyName>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,17 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <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" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -22,9 +17,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" /> <ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,104 @@
using CodeWalker.GameFiles;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace CodeWalker.Test
{
public class GTACryptTests
{
private const string RawKey = "sVGBe8yn7a4jqG0Di35DL9kFdd8IxUrIRuAMhRuEHOybRfi3sBMmEzz/ijT4EX4P8BwMOjmghVJi/AGUqqhOQsZdUcvcyOo4phKlPkl+xGX1wafkzNxpRJFdvhImFz+4+glX8Yavo3o76wBWlFfkUXRqGtiLAdTsKtdqgFMalzmnkDZsL7CeFjQONS/3W5ZzS/aikwsOYLYCo/f8QaIemm385HvFDEeLgHvsigxYHd/dyICvpK0bUvhZfz1BUmmHLOlYFKll/HPa7q8KhL/bRT0FblZ0s26INHaIf4tZ3nnWq51YGR1SnddxqDP/L0314KxuI4ou4xmAiu67x+sv+cLxScJc83U1iRhngfn3s4w=";
private uint[][] Key;
private readonly ITestOutputHelper _output;
[MemberNotNull(nameof(Key))]
private void LoadKeys()
{
var bytes = Convert.FromBase64String(RawKey);
Key = new uint[17][];
var uints = MemoryMarshal.Cast<byte, uint>(bytes);
for (int i = 0; i < Key.Length; i++)
{
Key[i] = uints.Slice(i * 4, 4).ToArray();
}
// GTA5Keys.LoadFromPath("C:\\Program Files\\Rockstar Games\\Grand Theft Auto V");
var rawKey = File.ReadAllBytes(TestFiles.GetFilePath("raw_key.dat"));
GTA5Keys.LoadPCNGDecryptTable(rawKey.AsSpan());
}
public GTACryptTests(ITestOutputHelper testOutputHelper)
{
_output = testOutputHelper;
LoadKeys();
//File.WriteAllBytes(TestFiles.GetFilePath("raw_key.dat"), rawKeyBytes.ToArray());
//var base64Key = Convert.ToBase64String(rawKeyBytes);
}
// Sanity check, before running tests for decryption, ensure we actually have the correct key loaded in
[Fact(DisplayName = "Key should match key which was used to create decryption tests")]
public void KeyShouldBeExpectedKey()
{
// The full key is pretty long, so just check a small part of it
var expectedKey = "l15p1+ANbFRRf+S3O0BHuSrwiWt9O4HBcLc7foJtA0v+GB5u0HV9T5V3b4CyFRJQMk9ZSIe+deW172Kp+pu6IAXNOKpWhZROt8Zk/rpK3kHIyq1sUgYwAP1hytlc814Iko0feUc1WpxDtv7SK6Bbi72wrrg/w+P3gz3Rq0vpMsM6EJVZXtpYX9ypFRD0btQoN5wv5k46RG2hjNrVnSijkUq54COuKWY9hMehUhnxTMd1ZE3Q7YHW6+p7phKr+hCTIq9Fej3q5aAIQYIVwcWznSP/l5rU9tkBoNwINbNFwLBoCOtd6FKgRcPstcrvqNC8c87vyUwTQjoCN0hTjJhtQw+78uz3FwCfWln8EfOUpNHEFsUzfGtTIUDPKmVv8pukScA0lELmLDL1PgbIcefpnhZU8C/8MRg5GoiYcItiHbrMSQkiJVU1g1hw+kbNGdvCGKGeJ1SskhkO6yAM4V2+tCCGQy2R9MvO8O1wZi5zLSWKMs9amPvVP6UPfpu0v7BJ8b2ihsmaf4wtCvmSJ3wz1B1y6IluoklEZNSDAt/QwaeARAUcTUOQ2pQnvWD54m6XZ61XtcrjqztqIe0KYFcnTKqqwnO84HxY43S44/9IzI5Bn/iFeOj3b4lLG+1ynj0pbItPE3YdmWemdqossWzG59oDtwnW399WPpMxF5rS02hm/YVVzmAPdYWXc7KvebTdCRFQ9Xm4JY+tULKKE4eGgdgqsV5rcT/q5d4a+vvLaMBbCS7xaVg5vVAvNleeUXcmm4IBiHdNS4difiEbOWlB7tl6Y76BFNf8YQf1rAFOnOST3c2ZH1vu3rhj2BYeCzw+ovUOYsCVYX2+yXoPj+G59NePDbaceHFx0SWvryxaK3J7kSPY6QJypSjZjzxETI4rFH32eJ8BpcaIG8kNU1bi4FkgKKZ6wfE4ZYRR4qRfrHsVLSSYV9VGrmMu8/sxNo3/T2qWjeSOyBrSXHsYCzhWoqOl3IJFHFzLDZL0u9tTZel0NJ8wKYld3PZH0n8RroDW0wyp+BD+UjZIkOZ0IdaRzawAYGoGtOwdA2eas0ZliHwcIjppG9hKkDy6N0CQpBkuLyP/xQpohELyxHYx6yt08pYOuzfPMN2VmasH312jjOhfioq/fxKHlgAeTgTGP8Nktpa2HrkzCvYzH4uo4iRqAzTl+1E2zP0GwrxnKsdvEYSNyL+j5qfOTb+ZqO8kBedjhu6nBcuzedtt253zqIPEJO74Alyp0xbEODmTDuzRBAtV/ED55/ccrbA8FAc1tSmx1aYL4RcEIs8MwiZbuxoMoRLXVGEwZl8f3fnH8MVGF9OOsWsUpyZ4zCYs4TQH5D79fkJVdt6AE0cEnepK+LK8dw==";
var loadedKey = MemoryMarshal.AsBytes(GTA5Keys.PC_NG_DECRYPT_TABLES[5][5].AsSpan());
var loadedKeyBase64 = Convert.ToBase64String(loadedKey);
Assert.Equal(expectedKey, loadedKeyBase64);
// This is the key which was just loaded, so these should be equivalent
var fullKeyBytes = MemoryMarshal.AsBytes(GTA5Keys.PC_NG_DECRYPT_TABLES.SelectMany(p => p).SelectMany(p => p).ToArray().AsSpan());
var expectedBytes = File.ReadAllBytes(TestFiles.GetFilePath("raw_key.dat"));
var loadedBytes = fullKeyBytes.ToArray();
Assert.Equal(expectedBytes.LongLength, loadedBytes.LongLength);
// Use manual for loop, equivalent is quite slow compared to this (7 seconds vs 1 second)
for (int i = 0; i < loadedBytes.Length; i++)
{
Assert.Equal(expectedBytes[i], loadedBytes[i]);
}
}
private const string InputData = "6Zemmftaofsb2sEYb3AC9A==";
private const string OutputData = "AAAAAAD//38BAAAAGAAAAA==";
[Fact]
public void DecryptWithArrayShouldReturnExpectedData()
{
var data = Convert.FromBase64String(InputData);
GTACrypto.DecryptNG(data, Key);
Assert.Equal(OutputData, Convert.ToBase64String(data));
}
[Fact]
public void DecryptWithSpanShouldReturnExpectedData()
{
var data = Convert.FromBase64String(InputData);
GTACrypto.DecryptNG(data.AsSpan(), Key);
Assert.Equal(OutputData, Convert.ToBase64String(data));
}
}
}

View File

@ -4,11 +4,13 @@ using System.Buffers.Binary;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Xml.Linq; using System.Xml.Linq;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace CodeWalker.Test namespace CodeWalker.Test
{ {
@ -122,6 +124,11 @@ namespace CodeWalker.Test
public class TestBinaryConversions public class TestBinaryConversions
{ {
private readonly ITestOutputHelper _output;
public TestBinaryConversions(ITestOutputHelper testOutputHelper)
{
_output = testOutputHelper;
}
[Fact] [Fact]
public void TestConvertData() public void TestConvertData()
{ {
@ -442,4 +449,86 @@ namespace CodeWalker.Test
} }
} }
public class TestConvertDataArray
{
private readonly ITestOutputHelper _output;
public TestConvertDataArray(ITestOutputHelper output)
{
_output = output;
}
private string GetFilePath(string filename)
{
// Directory we're looking for.
var dirToFind = Path.Combine(@"CodeWalker.Test", "Files");
// Search up directory tree starting at assembly path looking for 'Images' dir.
var searchPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
while (true)
{
var testPath = Path.Combine(searchPath, dirToFind);
if (Directory.Exists(testPath))
{
// Found it!
return Path.Combine(testPath, filename);
}
// Move up one directory.
var newSearchPath = Path.GetFullPath(Path.Combine(searchPath, ".."));
if (newSearchPath == searchPath)
{
// Didn't move up, so we're at the root.
throw new FileNotFoundException($"Could not find '{dirToFind}' directory.");
}
searchPath = newSearchPath;
}
}
public string TestData = "AAAAAAAAAAAFAAAAAAAAAAUABQAAAAAAAAAAAAAAAAAAAKDAAACgwAAAAMAAAAAAAACgQAAAoEAAAABAAAAAAAAAgD9kViJCAAAAAGAAAAABAAAAAAAAAP////8AAAAABgAAAAAAAAABAAEAAAAAAAAAAAAAAAAABWAAAAAAAAAKAAoAAAAAAAAAAAAAAAAAgPEnwiB/QcIAcFzBAAAAAADgDsL9ke/BHKgswQAAAAAAAIA/yyTUkwAAAABgAAAAAQAAAAEAAAD/////AAAAAAYAAQAAAAAALgAuAAAAAAA=";
[Fact]
public void ConvertDataArrayShouldReturnSameAsConvertData()
{
var data = Convert.FromBase64String(TestData);
var referenceData = MetaTypes.ConvertData<CMloRoomDef>(data);
var arrayData = MetaTypes.ConvertDataArray<CMloRoomDef>(data, 0, 2);
var path = GetFilePath("anwblokaal.ytyp");
var bytes = File.ReadAllBytes(path);
var entry = RpfFile.CreateFileEntry("anwblokaal.ytyp", path, ref bytes);
var ytypFile = RpfFile.GetFile<YtypFile>(entry, bytes);
var rooms = ytypFile.AllArchetypes
.Where(p => p is MloArchetype)
.Select(p => p as MloArchetype)
.SelectMany(p => p.rooms)
.Select(p => p.Data);
var secondRoom = rooms.Last();
var arr = new byte[Marshal.SizeOf<CMloRoomDef>() * rooms.Count()];
//MemoryMarshal.Write(arr.AsSpan(), ref rooms);
_output.WriteLine(Convert.ToBase64String(MemoryMarshal.AsBytes(rooms.ToArray().AsSpan())));
// I know the bbMax values don't really make sense here, seems to be a weird ytyp, but it's mostly about testing the serialization, not about using a 100% correct ytyp
// And didn't want to include a ytyp from Rockstar Games since that could cause some legal issues down the road
// Another possible solution would be to do abest attempt to find the GTA V installed on the user's computer running the tests and use files from there, but this would add complexity and links which could break tests
// However this would be more in the scope of implementation testing as it tests the whole chain including encryption which is not wanted here
Assert.Equivalent(new SharpDX.Vector3(-41.98584f, -48.3741455f, -13.7773438f), secondRoom.bbMin);
Assert.Equivalent(new SharpDX.Vector3(-35.71875f, -29.9462833f, -10.7910423f), secondRoom.bbMax);
Assert.Equivalent(new Array_uint(65542, 46), secondRoom.attachedObjects);
Assert.Equal(96u, secondRoom.flags);
Assert.Equal(1, secondRoom.floorId);
Assert.Equivalent(new CharPointer() { Count1 = 10, Count2 = 10, Pointer = 24581 }, secondRoom.name);
Assert.Equal(1u, secondRoom.portalCount);
Assert.Equal(2480153803, secondRoom.timecycleName.Hash);
// First room is limbo, so this check isn't that useful, but just make sure, and the following check will compare the entire array anyways
Assert.Equivalent(referenceData, rooms.First());
Assert.Equivalent(arrayData.ToArray(), rooms);
}
}
} }

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.Test
{
internal class TestFiles
{
public static string GetFilePath(string filename)
{
// Directory we're looking for.
var dirToFind = Path.Combine(@"CodeWalker.Test", "Files");
// Search up directory tree starting at assembly path looking for 'Images' dir.
var searchPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
while (true)
{
var testPath = Path.Combine(searchPath, dirToFind);
if (Directory.Exists(testPath))
{
// Found it!
return Path.Combine(testPath, filename);
}
// Move up one directory.
var newSearchPath = Path.GetFullPath(Path.Combine(searchPath, ".."));
if (newSearchPath == searchPath)
{
// Didn't move up, so we're at the root.
throw new FileNotFoundException($"Could not find '{dirToFind}' directory.");
}
searchPath = newSearchPath;
}
}
}
}

View File

@ -0,0 +1,31 @@
using CodeWalker.Core.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace CodeWalker.Test
{
public class TestSpanLineEnumerator
{
private readonly ITestOutputHelper output;
public TestSpanLineEnumerator(ITestOutputHelper output)
{
this.output = output;
}
[Fact]
public void IteratorShouldSplitLinesCorrectly()
{
var lines = "kaas\nsaak\nnog\neen\nline".AsSpan();
foreach(var line in lines.EnumerateSplit('\n'))
{
output.WriteLine(line.ToString());
}
}
}
}

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>CWVehicles.ico</ApplicationIcon> <ApplicationIcon>CWVehicles.ico</ApplicationIcon>
<Copyright>dexyfex</Copyright> <Copyright>dexyfex</Copyright>
@ -11,18 +10,13 @@
<AssemblyName>CodeWalker Vehicle Viewer</AssemblyName> <AssemblyName>CodeWalker Vehicle Viewer</AssemblyName>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,24 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" /> <PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.1.0" />
<PackageReference Include="HIC.System.Windows.Forms.DataVisualization" Version="1.0.1" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -249,22 +249,34 @@ namespace CodeWalker
ofd.InitialDirectory = fbd.SelectedPath; ofd.InitialDirectory = fbd.SelectedPath;
int result = 0; int result = 0;
var ns = "System.Windows.Forms"; var ns = "MS.Internal.AppModel";
var asmb = Assembly.GetAssembly(typeof(OpenFileDialog)); var asmb = Assembly.Load("System.Windows.Forms.Primitives");
var dialogint = GetType(asmb, ns, "FileDialogNative.IFileDialog"); //var asmb = Assembly.LoadWithPartialName("PresentationFramework");
var dialogint = asmb.GetType("Interop+Shell32+IFileDialog");
//var dialogint = GetType(asmb, ns, "IFileDialog");
var dialog = Call(typeof(OpenFileDialog), ofd, "CreateVistaDialog"); var dialog = Call(typeof(OpenFileDialog), ofd, "CreateVistaDialog");
Call(typeof(OpenFileDialog), ofd, "OnBeforeVistaDialog", dialog); Call(typeof(OpenFileDialog), ofd, "OnBeforeVistaDialog", dialog);
var options = Convert.ToUInt32(Call(typeof(FileDialog), ofd, "GetOptions")); var options = Convert.ToUInt32(Call(typeof(FileDialog), ofd, "GetOptions"));
options |= Convert.ToUInt32(GetEnumValue(asmb, ns, "FileDialogNative.FOS", "FOS_PICKFOLDERS"));
var enumValue = GetEnumValue("System.Windows.Forms.Primitives", "Interop+Shell32+FOS", "PICKFOLDERS");
options |= Convert.ToUInt32(enumValue);
Call(dialogint, dialog, "SetOptions", options); Call(dialogint, dialog, "SetOptions", options);
var pfde = New(asmb, ns, "FileDialog.VistaDialogEvents", ofd); dynamic pfde = New("System.Windows.Forms", "System.Windows.Forms.FileDialog+VistaDialogEvents", ofd);
var parameters = new object[] { pfde, (uint)0 }; var parameters = new object[] { pfde, (uint)0 };
Call(dialogint, dialog, "Advise", parameters); Call(dialogint, dialog, "Advise", parameters);
var adviseres = Convert.ToUInt32(parameters[1]); var adviseres = Convert.ToUInt32(parameters[1]);
try { result = Convert.ToInt32(Call(dialogint, dialog, "Show", hWndOwner)); } try {
finally { Call(dialogint, dialog, "Unadvise", adviseres); } result = Convert.ToInt32(Call(dialogint, dialog, "Show", hWndOwner));
}
finally {
//pfde.Dispose();
Call(dialogint, dialog, "Unadvise", adviseres);
}
GC.KeepAlive(pfde); GC.KeepAlive(pfde);
fbd.SelectedPath = ofd.FileName; fbd.SelectedPath = ofd.FileName;
return (result == 0) ? DialogResult.OK : DialogResult.Cancel; return (result == 0) ? DialogResult.OK : DialogResult.Cancel;
@ -296,12 +308,33 @@ namespace CodeWalker
if (mi == null) return null; if (mi == null) return null;
return mi.Invoke(obj, parameters); return mi.Invoke(obj, parameters);
} }
private static object GetEnumValue(string assemblyName, string typeName, string name)
{
var type = Type.GetType($"{typeName}, {assemblyName}");
var fieldInfo = type.GetField(name);
return fieldInfo.GetValue(null);
}
private static object GetEnumValue(Assembly asmb, string ns, string typeName, string name) private static object GetEnumValue(Assembly asmb, string ns, string typeName, string name)
{ {
var type = GetType(asmb, ns, typeName); var type = GetType(asmb, ns, typeName);
var fieldInfo = type.GetField(name); var fieldInfo = type.GetField(name);
return fieldInfo.GetValue(null); return fieldInfo.GetValue(null);
} }
private static object New(string assemblyName, string typeName, params object[] parameters)
{
var type = Type.GetType($"{typeName}, {assemblyName}");
var ctorInfos = type.GetConstructors();
foreach (ConstructorInfo ci in ctorInfos)
{
try { return ci.Invoke(parameters); }
catch { }
}
return null;
}
private static object New(Assembly asmb, string ns, string name, params object[] parameters) private static object New(Assembly asmb, string ns, string name, params object[] parameters)
{ {
var type = GetType(asmb, ns, name); var type = GetType(asmb, ns, name);

View File

@ -1,4 +1,4 @@

Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.7.34031.279 VisualStudioVersion = 17.7.34031.279
@ -25,7 +25,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Vehicles", "Code
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Test", "CodeWalker.Test\CodeWalker.Test.csproj", "{067FF87E-CA79-4B29-BCF6-11622999EDC6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Test", "CodeWalker.Test\CodeWalker.Test.csproj", "{067FF87E-CA79-4B29-BCF6-11622999EDC6}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeWalker.Benchmarks", "CodeWalker.Benchmarks\CodeWalker.Benchmarks.csproj", "{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeWalker.Benchmarks", "CodeWalker.Benchmarks\CodeWalker.Benchmarks.csproj", "{EDDA8A8E-5333-4E28-8221-A31E3B70EB7A}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@ -151,27 +151,6 @@
<setting name="DLC" serializeAs="String"> <setting name="DLC" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="KeyBindings" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<string>Move Forwards: W</string>
<string>Move Backwards: S</string>
<string>Move Left: A</string>
<string>Move Right: D</string>
<string>Move Up: R</string>
<string>Move Down: F</string>
<string>Move Slower / Zoom In: Z</string>
<string>Move Faster / Zoom Out: X</string>
<string>Toggle Mouse Select: C</string>
<string>Toggle Toolbar: T</string>
<string>Exit Edit Mode: Q</string>
<string>Edit Position: W</string>
<string>Edit Rotation: E</string>
<string>Edit Scale: R</string>
</ArrayOfString>
</value>
</setting>
<setting name="XInputLThumbInvert" serializeAs="String"> <setting name="XInputLThumbInvert" serializeAs="String">
<value>True</value> <value>True</value>
</setting> </setting>
@ -250,6 +229,26 @@
<setting name="AntiAliasing" serializeAs="String"> <setting name="AntiAliasing" serializeAs="String">
<value>2</value> <value>2</value>
</setting> </setting>
<setting name="KeyBindings" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<string>Move Forwards: W</string>
<string>Move Backwards: S</string>
<string>Move Left: A</string>
<string>Move Right: D</string>
<string>Move Up: R</string>
<string>Move Down: F</string>
<string>Move Slower / Zoom In: Z</string>
<string>Move Faster / Zoom Out: X</string>
<string>Toggle Mouse Select: C</string>
<string>Toggle Toolbar: T</string>
<string>Exit Edit Mode: Q</string>
<string>Edit Position: W</string>
<string>Edit Rotation: E</string>
<string>Edit Scale: R</string>
</ArrayOfString>
</value>
</setting>
</CodeWalker.Properties.Settings> </CodeWalker.Properties.Settings>
</userSettings> </userSettings>
<runtime> <runtime>

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>CW.ico</ApplicationIcon> <ApplicationIcon>CW.ico</ApplicationIcon>
@ -10,22 +9,25 @@
<Company>dexyfex software</Company> <Company>dexyfex software</Company>
<Authors>dexyfex</Authors> <Authors>dexyfex</Authors>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<Nullable>annotations</Nullable>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" /> <PackageReference Include="DirectXTexNet" Version="1.0.3" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" />
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.1.0" /> <PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.1.0" />
<PackageReference Include="FCTB" Version="2.16.24" /> <PackageReference Include="FCTB" Version="2.16.24" />
<PackageReference Include="HIC.System.Windows.Forms.DataVisualization" Version="1.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" /> <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="RequiredMemberAttribute" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" /> <PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" /> <PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
@ -33,18 +35,13 @@
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="SharpDX.XAudio2" Version="4.2.0" /> <PackageReference Include="SharpDX.XAudio2" Version="4.2.0" />
<PackageReference Include="SharpDX.XInput" Version="4.2.0" /> <PackageReference Include="SharpDX.XInput" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" /> <PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" /> <ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
<ProjectReference Include="..\CodeWalker.WinForms\CodeWalker.WinForms.csproj" /> <ProjectReference Include="..\CodeWalker.WinForms\CodeWalker.WinForms.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms.DataVisualization" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Properties\Settings.Designer.cs"> <Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput> <DesignTimeSharedInput>True</DesignTimeSharedInput>
@ -52,12 +49,10 @@
<DependentUpon>Settings.settings</DependentUpon> <DependentUpon>Settings.settings</DependentUpon>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Properties\Settings.settings"> <None Update="Properties\Settings.settings">
<Generator>PublicSettingsSingleFileGenerator</Generator> <Generator>PublicSettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -8,7 +8,6 @@ using CodeWalker.Core.Utils;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Data; using System.Data;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
@ -19,13 +18,10 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.Xml; using System.Xml;
using System.Xml.Linq;
using WeifenLuo.WinFormsUI.Docking; using WeifenLuo.WinFormsUI.Docking;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using static CodeWalker.GameFiles.GameFileCache; using static CodeWalker.GameFiles.GameFileCache;
using System.Diagnostics.CodeAnalysis;
using CodeWalker.WinForms;
using Newtonsoft.Json;
namespace CodeWalker namespace CodeWalker
{ {
@ -34,15 +30,15 @@ namespace CodeWalker
private volatile bool Ready = false; private volatile bool Ready = false;
private bool IsInited = false; private bool IsInited = false;
public static ExploreForm Instance; public static ExploreForm? Instance;
private static Dictionary<string, FileTypeInfo> FileTypes; private static Dictionary<string, FileTypeInfo>? FileTypes;
private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars();
public MainTreeFolder RootFolder; public MainTreeFolder? RootFolder;
public List<MainTreeFolder> ExtraRootFolders = new List<MainTreeFolder>(); public List<MainTreeFolder> ExtraRootFolders = new List<MainTreeFolder>();
public MainTreeFolder CurrentFolder; public MainTreeFolder? CurrentFolder;
public List<MainListItem> CurrentFiles; public List<MainListItem>? CurrentFiles;
private bool FirstRefreshed = false; private bool FirstRefreshed = false;
private List<MainListItem> CopiedFiles = new List<MainListItem>(); private List<MainListItem> CopiedFiles = new List<MainListItem>();
@ -57,17 +53,17 @@ namespace CodeWalker
public volatile bool Searching = false; public volatile bool Searching = false;
private MainTreeFolder SearchResults; private MainTreeFolder SearchResults;
private List<RpfFile> AllRpfs { get; set; } private List<RpfFile>? AllRpfs { get; set; }
private GameFileCache FileCache { get; set; } private static GameFileCache FileCache => GameFileCacheFactory.Instance;
private object FileCacheSyncRoot = new object(); private readonly object FileCacheSyncRoot = new object();
public bool EditMode { get; private set; } = false; public bool EditMode { get; private set; } = false;
public ThemeBase Theme { get; private set; } public ThemeBase? Theme { get; private set; }
public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); public CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
public static event Action ForceTreeRefresh; public static event Action? ForceTreeRefresh;
static ExploreForm() static ExploreForm()
{ {
@ -76,8 +72,11 @@ namespace CodeWalker
public ExploreForm() public ExploreForm()
{ {
ETWEvents.Log.CreatingForm(nameof(ExploreForm));
Instance = this; Instance = this;
SuspendLayout();
InitializeComponent(); InitializeComponent();
ResumeLayout();
SetTheme(Settings.Default.ExplorerWindowTheme, false); SetTheme(Settings.Default.ExplorerWindowTheme, false);
@ -170,11 +169,13 @@ namespace CodeWalker
var folderPath = folder?.Trim(); var folderPath = folder?.Trim();
if (!string.IsNullOrEmpty(folderPath)) if (!string.IsNullOrEmpty(folderPath))
{ {
var root = new MainTreeFolder(); var root = new MainTreeFolder
root.FullPath = folderPath; {
root.Path = folderPath; FullPath = folderPath,
root.Name = Path.GetFileName(Path.GetDirectoryName(folderPath)); Path = folderPath,
root.IsExtraFolder = true; Name = Path.GetFileName(Path.GetDirectoryName(folderPath)) ?? folderPath,
IsExtraFolder = true
};
ExtraRootFolders.Add(root); ExtraRootFolders.Add(root);
} }
} }
@ -185,16 +186,17 @@ namespace CodeWalker
var extrafolders = new StringBuilder(); var extrafolders = new StringBuilder();
foreach (var folder in ExtraRootFolders) foreach (var folder in ExtraRootFolders)
{ {
if (extrafolders.Length > 0) extrafolders.Append("\n"); extrafolders.AppendLine(folder.FullPath);
extrafolders.Append(folder.FullPath);
} }
Settings.Default.RPFExplorerExtraFolders = extrafolders.ToString(); Settings.Default.RPFExplorerExtraFolders = extrafolders.ToString();
Settings.Default.Save(); Settings.Default.Save();
} }
[MemberNotNull(nameof(FileCache))]
private void Init() private void Init()
{ {
ETWEvents.Log.LoadingForm(nameof(ExploreForm));
//called from ExploreForm_Load //called from ExploreForm_Load
// This is probably not necessary now that the GTA folder is checked // This is probably not necessary now that the GTA folder is checked
@ -203,11 +205,9 @@ namespace CodeWalker
if (!GTAFolder.UpdateGTAFolder(true)) if (!GTAFolder.UpdateGTAFolder(true))
{ {
Close(); Close();
return; throw new Exception("No GTA Folder found!");
} }
FileCache = GameFileCacheFactory.GetInstance();
new Task(async () => new Task(async () =>
{ {
try try
@ -271,6 +271,7 @@ namespace CodeWalker
{ {
UpdateStatus?.Invoke("Loading file cache..."); UpdateStatus?.Invoke("Loading file cache...");
var allRpfs = AllRpfs; var allRpfs = AllRpfs;
FileCache.Init(updateStatus: null, ErrorLog, allRpfs); //inits main dicts and archetypes only... FileCache.Init(updateStatus: null, ErrorLog, allRpfs); //inits main dicts and archetypes only...
UpdateStatus?.Invoke("Loading materials..."); UpdateStatus?.Invoke("Loading materials...");
@ -299,9 +300,10 @@ namespace CodeWalker
return FileCache; //return it even though it's probably not inited yet.. return FileCache; //return it even though it's probably not inited yet..
} }
[MemberNotNull(nameof(FileTypes))]
public static void InitFileTypes() public static void InitFileTypes()
{ {
FileTypes = new Dictionary<string, FileTypeInfo>(StringComparer.OrdinalIgnoreCase); FileTypes ??= new Dictionary<string, FileTypeInfo>(StringComparer.OrdinalIgnoreCase);
InitFileType(".rpf", "Rage Package File", 3); InitFileType(".rpf", "Rage Package File", 3);
InitFileType("", "File", 4); InitFileType("", "File", 4);
InitFileType(".dat", "Data File", 4); InitFileType(".dat", "Data File", 4);
@ -361,15 +363,28 @@ namespace CodeWalker
InitSubFileType(".dat", "distantlights.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights); InitSubFileType(".dat", "distantlights.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights);
InitSubFileType(".dat", "distantlights_hd.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights); InitSubFileType(".dat", "distantlights_hd.dat", "Distant Lights", 6, FileTypeAction.ViewDistantLights);
} }
[MemberNotNull(nameof(FileTypes))]
private static void EnsureFileTypesInitialized()
{
if (FileTypes is null)
{
throw new InvalidOperationException($"FileTypes are not initialized yet, please ensure {nameof(InitFileTypes)} is called before using FileTypes");
}
}
private static void InitFileType(string ext, string name, int imgidx, FileTypeAction defaultAction = FileTypeAction.ViewHex, bool xmlConvertible = false) private static void InitFileType(string ext, string name, int imgidx, FileTypeAction defaultAction = FileTypeAction.ViewHex, bool xmlConvertible = false)
{ {
EnsureFileTypesInitialized();
FileTypes ??= new Dictionary<string, FileTypeInfo>(StringComparer.OrdinalIgnoreCase);
var ft = new FileTypeInfo(ext, name, imgidx, defaultAction, xmlConvertible); var ft = new FileTypeInfo(ext, name, imgidx, defaultAction, xmlConvertible);
FileTypes[ext] = ft; FileTypes[ext] = ft;
} }
private static void InitSubFileType(string ext, string subext, string name, int imgidx, FileTypeAction defaultAction = FileTypeAction.ViewHex, bool xmlConvertible = false) private static void InitSubFileType(string ext, string subext, string name, int imgidx, FileTypeAction defaultAction = FileTypeAction.ViewHex, bool xmlConvertible = false)
{ {
FileTypeInfo pti = null; EnsureFileTypesInitialized();
if (FileTypes.TryGetValue(ext, out pti)) if (FileTypes.TryGetValue(ext, out var pti))
{ {
var ft = new FileTypeInfo(subext, name, imgidx, defaultAction, xmlConvertible); var ft = new FileTypeInfo(subext, name, imgidx, defaultAction, xmlConvertible);
pti.AddSubType(ft); pti.AddSubType(ft);
@ -377,6 +392,7 @@ namespace CodeWalker
} }
public static FileTypeInfo GetFileType(string fn) public static FileTypeInfo GetFileType(string fn)
{ {
EnsureFileTypesInitialized();
if (fn.IndexOfAny(InvalidFileNameChars) != -1) if (fn.IndexOfAny(InvalidFileNameChars) != -1)
{ {
return FileTypes[""]; return FileTypes[""];
@ -402,7 +418,7 @@ namespace CodeWalker
} }
else else
{ {
ft = new FileTypeInfo(ext, ext.Substring(1).ToUpperInvariant() + " File", 4, FileTypeAction.ViewHex, false); ft = new FileTypeInfo(ext, ext[1..].ToUpperInvariant() + " File", 4, FileTypeAction.ViewHex, false);
FileTypes[ft.Extension] = ft; //save it for later! FileTypes[ft.Extension] = ft; //save it for later!
return ft; return ft;
} }
@ -461,10 +477,11 @@ namespace CodeWalker
} }
public void Navigate(MainTreeFolder f) public void Navigate(MainTreeFolder? f)
{ {
if (!Ready) return; if (!Ready) return;
if (f == CurrentFolder) return; //already there! if (f == CurrentFolder) return; //already there!
ArgumentNullException.ThrowIfNull(f);
if (f.IsSearchResults) if (f.IsSearchResults)
{ {
AddMainTreeViewRoot(f); //add the current search result node AddMainTreeViewRoot(f); //add the current search result node
@ -477,8 +494,7 @@ namespace CodeWalker
foreach (TreeNode tn in MainTreeView.Nodes) foreach (TreeNode tn in MainTreeView.Nodes)
{ {
var tnf = tn.Tag as MainTreeFolder; if ((tn != sr) && (tn.Tag is MainTreeFolder tnf) && (tnf.IsSearchResults))
if ((tn != sr) && (tnf != null) && (tnf.IsSearchResults))
{ {
MainTreeView.Nodes.Remove(tn); //remove existing search result node MainTreeView.Nodes.Remove(tn); //remove existing search result node
break; break;
@ -494,7 +510,7 @@ namespace CodeWalker
hierarchy.Add(pf); hierarchy.Add(pf);
pf = pf.Parent; pf = pf.Parent;
} }
TreeNode n = null; TreeNode? n = null;
for (int i = hierarchy.Count - 1; i >= 0; i--) for (int i = hierarchy.Count - 1; i >= 0; i--)
{ {
n = FindTreeNode(hierarchy[i], n); n = FindTreeNode(hierarchy[i], n);
@ -516,9 +532,10 @@ namespace CodeWalker
{ {
if (!Ready) return; if (!Ready) return;
var pathl = path.Replace('/', '\\'); var pathl = path.Replace('/', '\\');
if ((CurrentFolder != null) && (CurrentFolder.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase))) return; //already there if ((CurrentFolder != null) && (CurrentFolder.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase)))
return; //already there
var hierarchy = pathl.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); var hierarchy = pathl.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
TreeNode n = MainTreeView.Nodes[0];// FindTreeNode("gta v", null); TreeNode? n = MainTreeView.Nodes[0];// FindTreeNode("gta v", null);
if (!string.IsNullOrEmpty(path)) if (!string.IsNullOrEmpty(path))
{ {
for (int i = 0; i < hierarchy.Length; i++) for (int i = 0; i < hierarchy.Length; i++)
@ -565,13 +582,13 @@ namespace CodeWalker
EditModeButton.Enabled = true; EditModeButton.Enabled = true;
} }
public void GoUp(MainTreeFolder toFolder = null) public void GoUp(MainTreeFolder? toFolder = null)
{ {
var fld = (toFolder == null) ? CurrentFolder?.Parent : toFolder; var fld = (toFolder == null) ? CurrentFolder?.Parent : toFolder;
if (fld == null) return; if (fld == null) return;
Navigate(fld); Navigate(fld);
} }
public void GoBack(MainTreeFolder toFolder = null) public void GoBack(MainTreeFolder? toFolder = null)
{ {
if (BackSteps.Count == 0) return; if (BackSteps.Count == 0) return;
var s = BackSteps.Pop(); var s = BackSteps.Pop();
@ -586,7 +603,7 @@ namespace CodeWalker
HistoryNavigating = false; HistoryNavigating = false;
UpdateHistoryUI(); UpdateHistoryUI();
} }
public void GoForward(MainTreeFolder toFolder = null) public void GoForward(MainTreeFolder? toFolder = null)
{ {
if (ForwardSteps.Count == 0) return; if (ForwardSteps.Count == 0) return;
var s = ForwardSteps.Pop(); var s = ForwardSteps.Pop();
@ -611,6 +628,7 @@ namespace CodeWalker
{ {
var button = UpButton.DropDownItems.Add(pf.GetToolText()); var button = UpButton.DropDownItems.Add(pf.GetToolText());
button.Tag = pf; button.Tag = pf;
button.Click += UpListButton_Click; button.Click += UpListButton_Click;
pf = pf.Parent; pf = pf.Parent;
if (i >= 10) break; if (i >= 10) break;
@ -709,7 +727,7 @@ namespace CodeWalker
} }
private TreeNode FindTreeNode(MainTreeFolder f, TreeNode parent) private TreeNode? FindTreeNode(MainTreeFolder f, TreeNode? parent)
{ {
var tnc = (parent != null) ? parent.Nodes : MainTreeView.Nodes; var tnc = (parent != null) ? parent.Nodes : MainTreeView.Nodes;
foreach (TreeNode node in tnc) foreach (TreeNode node in tnc)
@ -721,7 +739,7 @@ namespace CodeWalker
} }
return null; return null;
} }
private TreeNode FindTreeNode(string text, TreeNode parent) private TreeNode? FindTreeNode(string text, TreeNode? parent)
{ {
var tnc = (parent != null) ? parent.Nodes : MainTreeView.Nodes; var tnc = (parent != null) ? parent.Nodes : MainTreeView.Nodes;
foreach (TreeNode node in tnc) foreach (TreeNode node in tnc)
@ -735,7 +753,8 @@ namespace CodeWalker
} }
[MemberNotNull(nameof(RootFolder))]
[MemberNotNull(nameof(AllRpfs))]
private async Task RefreshMainTreeView() private async Task RefreshMainTreeView()
{ {
using var _ = new DisposableTimer("RefreshMainTreeView"); using var _ = new DisposableTimer("RefreshMainTreeView");
@ -747,15 +766,18 @@ namespace CodeWalker
UpdateStatus?.Invoke("Scanning..."); UpdateStatus?.Invoke("Scanning...");
var root = new MainTreeFolder(); var root = new MainTreeFolder
root.FullPath = GTAFolder.GetCurrentGTAFolderWithTrailingSlash(); {
root.Path = ""; FullPath = GTAFolder.GetCurrentGTAFolderWithTrailingSlash(),
root.Name = "GTA V"; Path = "",
Name = "GTA V"
};
RootFolder = root; RootFolder = root;
var tasks = new List<Task<TreeNode>>(); var tasks = new List<Task<TreeNode>>
{
tasks.Add(Task.Run(() => RefreshMainTreeViewRoot(root, true))); Task.Run(() => RefreshMainTreeViewRoot(root, true))
};
var remFolders = new List<MainTreeFolder>(); var remFolders = new List<MainTreeFolder>();
@ -791,6 +813,7 @@ namespace CodeWalker
BeginInvoke(() => BeginInvoke(() =>
{ {
SuspendLayout();
MainTreeView.BeginUpdate(); MainTreeView.BeginUpdate();
ClearMainTreeView(); ClearMainTreeView();
@ -799,6 +822,7 @@ namespace CodeWalker
Ready = true; Ready = true;
MainTreeView.EndUpdate(); MainTreeView.EndUpdate();
ResumeLayout();
MainTreeViewRefreshComplete(); MainTreeViewRefreshComplete();
}); });
@ -889,6 +913,7 @@ namespace CodeWalker
if (rpf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF) if (rpf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF)
{ {
Console.WriteLine(rpf.LastException);
return; return;
} }
@ -1141,29 +1166,35 @@ namespace CodeWalker
} }
private MainTreeFolder CreateRpfTreeFolder(RpfFile rpf, string relpath, string fullpath) private MainTreeFolder CreateRpfTreeFolder(RpfFile rpf, string relpath, string fullpath)
{ {
var node = new MainTreeFolder(); var node = new MainTreeFolder
node.RpfFile = rpf; {
node.RpfFolder = rpf.Root; RpfFile = rpf,
node.Name = rpf.Name; RpfFolder = rpf.Root,
node.Path = relpath; Name = rpf.Name,
node.FullPath = fullpath; Path = relpath,
FullPath = fullpath,
};
return node; return node;
} }
private MainTreeFolder CreateRpfDirTreeFolder(RpfDirectoryEntry dir, string relpath, string fullpath) private MainTreeFolder CreateRpfDirTreeFolder(RpfDirectoryEntry dir, string relpath, string fullpath)
{ {
var node = new MainTreeFolder(); var node = new MainTreeFolder
node.RpfFolder = dir; {
node.Name = dir.Name; RpfFolder = dir,
node.Path = relpath; Name = dir.Name,
node.FullPath = fullpath; Path = relpath,
FullPath = fullpath,
};
return node; return node;
} }
private MainTreeFolder CreateRootDirTreeFolder(string name, string path, string fullpath) private MainTreeFolder CreateRootDirTreeFolder(string name, string path, string fullpath)
{ {
var node = new MainTreeFolder(); var node = new MainTreeFolder
node.Name = name; {
node.Path = path; Name = name,
node.FullPath = fullpath; Path = path,
FullPath = fullpath,
};
return node; return node;
} }
private void RenameTreeFolder(MainTreeFolder f, string newname) private void RenameTreeFolder(MainTreeFolder f, string newname)
@ -1299,6 +1330,8 @@ namespace CodeWalker
{ {
ForceTreeRefresh?.Invoke(); ForceTreeRefresh?.Invoke();
} }
private void RefreshMainListView() private void RefreshMainListView()
{ {
MainListView.VirtualListSize = 0; MainListView.VirtualListSize = 0;
@ -1377,11 +1410,13 @@ namespace CodeWalker
SearchGlobalButton.Checked = true; SearchGlobalButton.Checked = true;
SearchFilterButton.Checked = false; SearchFilterButton.Checked = false;
SearchResults = new MainTreeFolder(); SearchResults = new MainTreeFolder
SearchResults.Name = "Search Results: " + text; {
SearchResults.Path = SearchResults.Name; Name = "Search Results: " + text,
SearchResults.IsSearchResults = true; Path = "Search Results: " + text,
SearchResults.SearchTerm = text; IsSearchResults = true,
SearchTerm = text,
};
Navigate(SearchResults); Navigate(SearchResults);
@ -1389,7 +1424,7 @@ namespace CodeWalker
MainListView.SetSortIcon(SortColumnIndex, SortDirection); MainListView.SetSortIcon(SortColumnIndex, SortDirection);
MainListView.VirtualListSize = 0; MainListView.VirtualListSize = 0;
CurrentFiles.Clear(); CurrentFiles?.Clear();
UpdateStatus?.Invoke("Searching..."); UpdateStatus?.Invoke("Searching...");
@ -1401,7 +1436,7 @@ namespace CodeWalker
Cursor = Cursors.WaitCursor; Cursor = Cursors.WaitCursor;
var resultcount = RootFolder.Search(term, this); var resultcount = RootFolder?.Search(term, this) ?? 0;
foreach(var extraFolder in ExtraRootFolders) foreach(var extraFolder in ExtraRootFolders)
{ {
@ -1436,7 +1471,7 @@ namespace CodeWalker
} }
public void AddSearchResult(MainListItem item) public void AddSearchResult(MainListItem? item)
{ {
if (SearchResults == null) return; if (SearchResults == null) return;
if (SearchResults.ListItems != CurrentFiles) return; if (SearchResults.ListItems != CurrentFiles) return;
@ -2416,7 +2451,7 @@ namespace CodeWalker
File.WriteAllText(Path.Combine(CurrentFolder.FullPath, "texture_usage.json"), JsonConvert.SerializeObject(textures.Values.OrderBy(p => p.Count), new JsonSerializerSettings { Formatting = Newtonsoft.Json.Formatting.Indented })); File.WriteAllText(Path.Combine(CurrentFolder.FullPath, "texture_usage.json"), System.Text.Json.JsonSerializer.Serialize(textures.Values.OrderBy(p => p.Count), new System.Text.Json.JsonSerializerOptions { WriteIndented = true }));
var shaders = data.Values.ToList(); var shaders = data.Values.ToList();
shaders.Sort((a, b) => { return b.GeomCount.CompareTo(a.GeomCount); }); shaders.Sort((a, b) => { return b.GeomCount.CompareTo(a.GeomCount); });
@ -3691,11 +3726,13 @@ namespace CodeWalker
if (folder.FullPath == folderPath) return; if (folder.FullPath == folderPath) return;
} }
var root = new MainTreeFolder(); var root = new MainTreeFolder
root.FullPath = folderPath; {
root.Path = folderPath; FullPath = folderPath,
root.Name = Path.GetFileName(Path.GetDirectoryName(folderPath)); Path = folderPath,
root.IsExtraFolder = true; Name = Path.GetFileName(Path.GetDirectoryName(folderPath)),
IsExtraFolder = true,
};
ExtraRootFolders.Add(root); ExtraRootFolders.Add(root);
Task.Run(() => Task.Run(() =>
@ -4586,13 +4623,13 @@ namespace CodeWalker
private void ToolsBinSearchMenu_Click(object sender, EventArgs e) private void ToolsBinSearchMenu_Click(object sender, EventArgs e)
{ {
BinarySearchForm f = new BinarySearchForm(GetFileCache()); BinarySearchForm f = new BinarySearchForm();
f.Show(this); f.Show(this);
} }
private void ToolsAudioExplorerMenu_Click(object sender, EventArgs e) private void ToolsAudioExplorerMenu_Click(object sender, EventArgs e)
{ {
AudioExplorerForm f = new AudioExplorerForm(GetFileCache()); AudioExplorerForm f = new AudioExplorerForm();
f.Show(this); f.Show(this);
} }
@ -4604,7 +4641,7 @@ namespace CodeWalker
private void ToolsJenkIndMenu_Click(object sender, EventArgs e) private void ToolsJenkIndMenu_Click(object sender, EventArgs e)
{ {
JenkIndForm f = new JenkIndForm(GetFileCache()); JenkIndForm f = new JenkIndForm();
f.Show(this); f.Show(this);
} }
@ -4632,9 +4669,9 @@ namespace CodeWalker
public class MainTreeFolder public class MainTreeFolder
{ {
public string Name { get; set; } public required string Name { get; set; }
private string _nameLower; private string? _nameLower;
public string NameLower public string NameLower
{ {
get get
@ -4646,20 +4683,20 @@ namespace CodeWalker
_nameLower = value; _nameLower = value;
} }
} }
public string Path { get; set; } public required string Path { get; set; }
public string FullPath { get; set; } public string? FullPath { get; set; }
public RpfFile RpfFile { get; set; } public RpfFile? RpfFile { get; set; }
public RpfDirectoryEntry RpfFolder { get; set; } public RpfDirectoryEntry? RpfFolder { get; set; }
public HashSet<string> Files { get; set; } public HashSet<string>? Files { get; set; }
private object filesLock = new object(); private object filesLock = new object();
public MainTreeFolder Parent { get; set; } public MainTreeFolder? Parent { get; set; }
public List<MainTreeFolder> Children { get; set; } public List<MainTreeFolder>? Children { get; set; }
private object childrenLock = new object(); private readonly object childrenLock = new object();
public List<MainListItem> ListItems { get; set; } public List<MainListItem>? ListItems { get; set; }
public TreeNode TreeNode { get; set; } public TreeNode? TreeNode { get; set; }
public bool IsSearchResults { get; set; } public bool IsSearchResults { get; set; } = false;
public string SearchTerm { get; set; } public string? SearchTerm { get; set; }
public bool IsExtraFolder { get; set; } public bool IsExtraFolder { get; set; } = false;
public void AddFile(string file) public void AddFile(string file)
{ {

View File

@ -401,7 +401,7 @@ namespace CodeWalker.Forms
{ {
Stream wavStream = audio.GetWavStream(); Stream wavStream = audio.GetWavStream();
FileStream stream = File.Create(saveFileDialog.FileName); FileStream stream = File.Create(saveFileDialog.FileName);
wavStream.CopyToFast(stream); wavStream.CopyTo(stream);
stream.Close(); stream.Close();
wavStream.Close(); wavStream.Close();
} }

View File

@ -100,7 +100,7 @@ namespace CodeWalker.Forms
List<VertexTypePC> gridVerts = new List<VertexTypePC>(); List<VertexTypePC> gridVerts = new List<VertexTypePC>();
object gridSyncRoot = new object(); object gridSyncRoot = new object();
GameFileCache gameFileCache = null; GameFileCache gameFileCache => GameFileCacheFactory.Instance;
Archetype currentArchetype = null; Archetype currentArchetype = null;
bool updateArchetypeStatus = true; bool updateArchetypeStatus = true;
@ -134,8 +134,6 @@ namespace CodeWalker.Forms
if (this.DesignMode) return; if (this.DesignMode) return;
InitializeComponent(); InitializeComponent();
gameFileCache = GameFileCacheFactory.GetInstance();
if (ExploreForm.Instance == null) if (ExploreForm.Instance == null)
{ {
gameFileCache.EnableDlc = false; gameFileCache.EnableDlc = false;
@ -2310,7 +2308,7 @@ namespace CodeWalker.Forms
Input.KeyDown(e, enablemove); Input.KeyDown(e, enablemove);
var k = e.KeyCode; var k = e.KeyCode;
var kb = Input.keyBindings; var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed; bool ctrl = Input.CtrlPressed;
bool shift = Input.ShiftPressed; bool shift = Input.ShiftPressed;

View File

@ -218,7 +218,7 @@ namespace CodeWalker.Forms
return false;//what are we even doing here? return false;//what are we even doing here?
case MetaFormat.AudioRel: case MetaFormat.AudioRel:
var rel = XmlRel.GetRel(doc); var rel = XmlRel.GetRel(doc);
if ((rel?.RelDatasSorted == null) || (rel.RelDatasSorted.Length == 0)) if ((rel?.RelDatas == null) || (rel.RelDatas.Length == 0))
{ {
MessageBox.Show("Schema not supported.", "Cannot import REL XML"); MessageBox.Show("Schema not supported.", "Cannot import REL XML");
return false; return false;

View File

@ -10,7 +10,6 @@ using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Remoting.Channels;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;

View File

@ -11,18 +11,25 @@ namespace CodeWalker.GameFiles
public static class GameFileCacheFactory public static class GameFileCacheFactory
{ {
public static GameFileCache _instance = null; public static GameFileCache? _instance;
public static GameFileCache GetInstance()
public static GameFileCache Instance
{ {
if (_instance == null) get
{
if (_instance is null)
{ {
var s = Settings.Default; var s = Settings.Default;
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); _instance = new GameFileCache(s.CacheSize, s.CacheTime, GTAFolder.CurrentGTAFolder, s.DLC, s.EnableMods, s.ExcludeFolders, Settings.Default.Key);
_instance = new GameFileCache(s.CacheSize, s.CacheTime, GTAFolder.CurrentGTAFolder, s.DLC, s.EnableMods, s.ExcludeFolders);
GTAFolder.OnGTAFolderChanged += _instance.SetGtaFolder; GTAFolder.OnGTAFolderChanged += _instance.SetGtaFolder;
} }
return _instance; return _instance;
} }
}
public static GameFileCache GetInstance()
{
return Instance;
}
} }

View File

@ -752,7 +752,7 @@ namespace CodeWalker
public void LoadPed() public async ValueTask LoadPedAsync()
{ {
var pedname = PedNameComboBox.Text; var pedname = PedNameComboBox.Text;
var pedhash = JenkHash.GenHash(pedname.ToLowerInvariant()); var pedhash = JenkHash.GenHash(pedname.ToLowerInvariant());
@ -765,7 +765,7 @@ namespace CodeWalker
DetailsPropertyGrid.SelectedObject = null; DetailsPropertyGrid.SelectedObject = null;
SelectedPed.Init(pedname, GameFileCache); await SelectedPed.InitAsync(pedname, GameFileCache);
LoadModel(SelectedPed.Yft, pedchange); LoadModel(SelectedPed.Yft, pedchange);
@ -839,14 +839,14 @@ namespace CodeWalker
} }
} }
private void SetComponentDrawable(int index, object comboObj) private async ValueTask SetComponentDrawableAsync(int index, object comboObj)
{ {
var comboItem = comboObj as ComponentComboItem; var comboItem = comboObj as ComponentComboItem;
var name = comboItem?.DrawableName; var name = comboItem?.DrawableName;
var tex = comboItem?.TextureName; var tex = comboItem?.TextureName;
SelectedPed.SetComponentDrawable(index, name, tex, GameFileCache); await SelectedPed.SetComponentDrawableAsync(index, name, tex, GameFileCache);
UpdateModelsUI(); UpdateModelsUI();
} }
@ -916,10 +916,15 @@ namespace CodeWalker
private void SelectClip(string name) private void SelectClip(string name)
{ {
MetaHash cliphash = JenkHash.GenHash(name); MetaHash cliphash = JenkHash.GenHash(name);
ClipMapEntry cme = null; if (SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out var cme) ?? false)
SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out cme); {
SelectedPed.AnimClip = cme; SelectedPed.AnimClip = cme;
} }
else
{
SelectedPed.AnimClip = null;
}
}
@ -1209,7 +1214,7 @@ namespace CodeWalker
Input.KeyDown(e, enablemove); Input.KeyDown(e, enablemove);
var k = e.KeyCode; var k = e.KeyCode;
var kb = Input.keyBindings; var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed; bool ctrl = Input.CtrlPressed;
bool shift = Input.ShiftPressed; bool shift = Input.ShiftPressed;
@ -1576,71 +1581,71 @@ namespace CodeWalker
private void PedNameComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void PedNameComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
if (!GameFileCache.IsInited) return; if (!GameFileCache.IsInited) return;
LoadPed(); await LoadPedAsync();
} }
private void CompHeadComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompHeadComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(0, CompHeadComboBox.SelectedItem); await SetComponentDrawableAsync(0, CompHeadComboBox.SelectedItem);
} }
private void CompBerdComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompBerdComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(1, CompBerdComboBox.SelectedItem); await SetComponentDrawableAsync(1, CompBerdComboBox.SelectedItem);
} }
private void CompHairComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompHairComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(2, CompHairComboBox.SelectedItem); await SetComponentDrawableAsync(2, CompHairComboBox.SelectedItem);
} }
private void CompUpprComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompUpprComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(3, CompUpprComboBox.SelectedItem); await SetComponentDrawableAsync(3, CompUpprComboBox.SelectedItem);
} }
private void CompLowrComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompLowrComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(4, CompLowrComboBox.SelectedItem); await SetComponentDrawableAsync(4, CompLowrComboBox.SelectedItem);
} }
private void CompHandComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompHandComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(5, CompHandComboBox.SelectedItem); await SetComponentDrawableAsync(5, CompHandComboBox.SelectedItem);
} }
private void CompFeetComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompFeetComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(6, CompFeetComboBox.SelectedItem); await SetComponentDrawableAsync(6, CompFeetComboBox.SelectedItem);
} }
private void CompTeefComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompTeefComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(7, CompTeefComboBox.SelectedItem); await SetComponentDrawableAsync(7, CompTeefComboBox.SelectedItem);
} }
private void CompAccsComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompAccsComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(8, CompAccsComboBox.SelectedItem); await SetComponentDrawableAsync(8, CompAccsComboBox.SelectedItem);
} }
private void CompTaskComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompTaskComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(9, CompTaskComboBox.SelectedItem); await SetComponentDrawableAsync(9, CompTaskComboBox.SelectedItem);
} }
private void CompDeclComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompDeclComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(10, CompDeclComboBox.SelectedItem); await SetComponentDrawableAsync(10, CompDeclComboBox.SelectedItem);
} }
private void CompJbibComboBox_SelectedIndexChanged(object sender, EventArgs e) private async void CompJbibComboBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
SetComponentDrawable(11, CompJbibComboBox.SelectedItem); await SetComponentDrawableAsync(11, CompJbibComboBox.SelectedItem);
} }
private void ClipDictComboBox_TextChanged(object sender, EventArgs e) private void ClipDictComboBox_TextChanged(object sender, EventArgs e)

View File

@ -16,18 +16,16 @@ using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Shell; using System.Windows.Shell;
namespace CodeWalker namespace CodeWalker;
{
static class Program static class Program
{ {
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main(string[] args) static void Main(string[] args)
{
Task.Run(() =>
{ {
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ConsoleWindow.Hide(); ConsoleWindow.Hide();
@ -36,8 +34,7 @@ namespace CodeWalker
{ {
Console.WriteLine($"Unhandeled exception occured: {e.Exception}"); Console.WriteLine($"Unhandeled exception occured: {e.Exception}");
}; };
});
bool menumode = false; bool menumode = false;
bool explorermode = false; bool explorermode = false;
bool projectmode = false; bool projectmode = false;
@ -84,7 +81,7 @@ namespace CodeWalker
} }
} }
EnsureJumpList(); Task.Run(EnsureJumpList);
//Application.SetHighDpiMode(HighDpiMode.SystemAware); //Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles(); Application.EnableVisualStyles();
@ -221,8 +218,9 @@ namespace CodeWalker
Settings.Default.JumpListInitialised = true; Settings.Default.JumpListInitialised = true;
Settings.Default.Save(); Settings.Default.Save();
} }
catch catch (Exception ex)
{ } {
Console.WriteLine($"Initializing console failed {ex}");
} }
} }
} }

View File

@ -8,9 +8,6 @@
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
using System;
using System.Windows.Forms;
namespace CodeWalker.Properties { namespace CodeWalker.Properties {
@ -590,34 +587,6 @@ namespace CodeWalker.Properties {
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(@"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfString xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<string>Move Forwards: W</string>
<string>Move Backwards: S</string>
<string>Move Left: A</string>
<string>Move Right: D</string>
<string>Move Up: R</string>
<string>Move Down: F</string>
<string>Move Slower / Zoom In: Z</string>
<string>Move Faster / Zoom Out: X</string>
<string>Toggle Mouse Select: C</string>
<string>Toggle Toolbar: T</string>
<string>Exit Edit Mode: Q</string>
<string>Edit Position: W</string>
<string>Edit Rotation: E</string>
<string>Edit Scale: R</string>
</ArrayOfString>")]
public global::System.Collections.Specialized.StringCollection KeyBindings {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["KeyBindings"]));
}
set {
this["KeyBindings"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")] [global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -929,5 +898,33 @@ namespace CodeWalker.Properties {
this["AntiAliasing"] = value; this["AntiAliasing"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(@"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfString xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<string>Move Forwards: W</string>
<string>Move Backwards: S</string>
<string>Move Left: A</string>
<string>Move Right: D</string>
<string>Move Up: R</string>
<string>Move Down: F</string>
<string>Move Slower / Zoom In: Z</string>
<string>Move Faster / Zoom Out: X</string>
<string>Toggle Mouse Select: C</string>
<string>Toggle Toolbar: T</string>
<string>Exit Edit Mode: Q</string>
<string>Edit Position: W</string>
<string>Edit Rotation: E</string>
<string>Edit Scale: R</string>
</ArrayOfString>")]
public global::System.Collections.Specialized.StringCollection KeyBindings {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["KeyBindings"]));
}
set {
this["KeyBindings"] = value;
}
}
} }
} }

View File

@ -143,25 +143,6 @@
<Setting Name="DLC" Type="System.String" Scope="User"> <Setting Name="DLC" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="KeyBindings" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
&lt;string&gt;Move Forwards: W&lt;/string&gt;
&lt;string&gt;Move Backwards: S&lt;/string&gt;
&lt;string&gt;Move Left: A&lt;/string&gt;
&lt;string&gt;Move Right: D&lt;/string&gt;
&lt;string&gt;Move Up: R&lt;/string&gt;
&lt;string&gt;Move Down: F&lt;/string&gt;
&lt;string&gt;Move Slower / Zoom In: Z&lt;/string&gt;
&lt;string&gt;Move Faster / Zoom Out: X&lt;/string&gt;
&lt;string&gt;Toggle Mouse Select: C&lt;/string&gt;
&lt;string&gt;Toggle Toolbar: T&lt;/string&gt;
&lt;string&gt;Exit Edit Mode: Q&lt;/string&gt;
&lt;string&gt;Edit Position: W&lt;/string&gt;
&lt;string&gt;Edit Rotation: E&lt;/string&gt;
&lt;string&gt;Edit Scale: R&lt;/string&gt;
&lt;/ArrayOfString&gt;</Value>
</Setting>
<Setting Name="XInputLThumbInvert" Type="System.Boolean" Scope="User"> <Setting Name="XInputLThumbInvert" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value> <Value Profile="(Default)">True</Value>
</Setting> </Setting>
@ -240,5 +221,24 @@
<Setting Name="AntiAliasing" Type="System.Int32" Scope="User"> <Setting Name="AntiAliasing" Type="System.Int32" Scope="User">
<Value Profile="(Default)">2</Value> <Value Profile="(Default)">2</Value>
</Setting> </Setting>
<Setting Name="KeyBindings" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
&lt;string&gt;Move Forwards: W&lt;/string&gt;
&lt;string&gt;Move Backwards: S&lt;/string&gt;
&lt;string&gt;Move Left: A&lt;/string&gt;
&lt;string&gt;Move Right: D&lt;/string&gt;
&lt;string&gt;Move Up: R&lt;/string&gt;
&lt;string&gt;Move Down: F&lt;/string&gt;
&lt;string&gt;Move Slower / Zoom In: Z&lt;/string&gt;
&lt;string&gt;Move Faster / Zoom Out: X&lt;/string&gt;
&lt;string&gt;Toggle Mouse Select: C&lt;/string&gt;
&lt;string&gt;Toggle Toolbar: T&lt;/string&gt;
&lt;string&gt;Exit Edit Mode: Q&lt;/string&gt;
&lt;string&gt;Edit Position: W&lt;/string&gt;
&lt;string&gt;Edit Rotation: E&lt;/string&gt;
&lt;string&gt;Edit Scale: R&lt;/string&gt;
&lt;/ArrayOfString&gt;</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -57,7 +57,7 @@ namespace CodeWalker.Rendering
{ {
//SharpDX.Configuration.EnableObjectTracking = true; //SharpDX.Configuration.EnableObjectTracking = true;
SwapChainDescription scd = new SwapChainDescription() var scd = new SwapChainDescription()
{ {
BufferCount = 2, BufferCount = 2,
Flags = SwapChainFlags.None, Flags = SwapChainFlags.None,

View File

@ -14,15 +14,14 @@ namespace CodeWalker.Tools
{ {
public partial class AudioExplorerForm : Form public partial class AudioExplorerForm : Form
{ {
private GameFileCache GameFileCache { get; set; } private static GameFileCache GameFileCache { get => GameFileCacheFactory.Instance; }
private List<string> NameComboItems = new List<string>(); private List<string> NameComboItems = new List<string>();
private Dictionary<string, RelData> NameComboLookup = new Dictionary<string, RelData>(); private Dictionary<string, RelData> NameComboLookup = new Dictionary<string, RelData>();
public AudioExplorerForm(GameFileCache gfc) public AudioExplorerForm()
{ {
GameFileCache = gfc;
InitializeComponent(); InitializeComponent();
LoadDropDowns(); LoadDropDowns();
} }

View File

@ -18,14 +18,12 @@ namespace CodeWalker.Tools
private volatile bool InProgress = false; private volatile bool InProgress = false;
private volatile bool AbortOperation = false; private volatile bool AbortOperation = false;
private GameFileCache FileCache = null; private static GameFileCache FileCache => GameFileCacheFactory.Instance;
private RpfManager RpfMan = null; private static RpfManager RpfMan => FileCache.RpfMan;
public BinarySearchForm(GameFileCache cache = null) public BinarySearchForm()
{ {
FileCache = cache;
RpfMan = cache?.RpfMan;
InitializeComponent(); InitializeComponent();
} }
@ -37,12 +35,11 @@ namespace CodeWalker.Tools
DataTextBox.SetTabStopWidth(3); DataTextBox.SetTabStopWidth(3);
if (RpfMan == null || !RpfMan.IsInited) if (!RpfMan.IsInited)
{ {
Task.Run(() => Task.Run(() =>
{ {
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
RpfMan ??= RpfManager.GetInstance();
RpfMan.Init(GTAFolder.CurrentGTAFolder, UpdateStatus, UpdateStatus, false, false); RpfMan.Init(GTAFolder.CurrentGTAFolder, UpdateStatus, UpdateStatus, false, false);
RPFScanComplete(); RPFScanComplete();
}); });

View File

@ -43,7 +43,9 @@ namespace CodeWalker.Tools
private void BrowseForm_Load(object sender, EventArgs e) private void BrowseForm_Load(object sender, EventArgs e)
{ {
var info = DetailsPropertyGrid.GetType().GetProperty("Controls"); var info = DetailsPropertyGrid.GetType().GetProperty("Controls");
var collection = info.GetValue(DetailsPropertyGrid, null) as Control.ControlCollection; var collection = info?.GetValue(DetailsPropertyGrid, null) as Control.ControlCollection;
if (collection is not null)
{
foreach (var control in collection) foreach (var control in collection)
{ {
var ctyp = control.GetType(); var ctyp = control.GetType();
@ -54,6 +56,8 @@ namespace CodeWalker.Tools
prop.SetValue(control, 4.0); //somehow this sets the width of the property grid's label column... prop.SetValue(control, 4.0); //somehow this sets the width of the property grid's label column...
} }
} }
}
FolderTextBox.Text = GTAFolder.CurrentGTAFolder; FolderTextBox.Text = GTAFolder.CurrentGTAFolder;
DataHexLineCombo.Text = "16"; DataHexLineCombo.Text = "16";
@ -120,11 +124,7 @@ namespace CodeWalker.Tools
string[] allfiles = Directory.GetFiles(searchpath, "*.rpf", SearchOption.AllDirectories); string[] allfiles = Directory.GetFiles(searchpath, "*.rpf", SearchOption.AllDirectories);
uint totrpfs = 0; var counts = new FileCounts();
uint totfiles = 0;
uint totfolders = 0;
uint totresfiles = 0;
uint totbinfiles = 0;
foreach (string rpfpath in allfiles) foreach (string rpfpath in allfiles)
{ {
@ -139,22 +139,16 @@ namespace CodeWalker.Tools
UpdateStatus("Scanning " + rf.Name + "..."); UpdateStatus("Scanning " + rf.Name + "...");
rf.ScanStructure(UpdateStatus, UpdateStatus); counts += rf.ScanStructure(UpdateStatus, UpdateStatus);
totrpfs += rf.GrandTotalRpfCount;
totfiles += rf.GrandTotalFileCount;
totfolders += rf.GrandTotalFolderCount;
totresfiles += rf.GrandTotalResourceCount;
totbinfiles += rf.GrandTotalBinaryFileCount;
AddScannedFile(rf, null, true); AddScannedFile(rf, null, true);
RootFiles.Add(rf); RootFiles.Add(rf);
} }
UpdateStatus(string.Format("Scan complete. {0} RPF files, {1} total files, {2} total folders, {3} resources, {4} binary files.", totrpfs, totfiles, totfolders, totresfiles, totbinfiles)); UpdateStatus(string.Format("Scan complete. {0} RPF files, {1} total files, {2} total folders, {3} resources, {4} binary files.", counts.Rpfs, counts.Files, counts.Folders, counts.Resources, counts.BinaryFiles));
InProgress = false; InProgress = false;
TotalFileCount = (int)totfiles; TotalFileCount = (int)counts.Files;
}); });
} }
@ -739,12 +733,11 @@ namespace CodeWalker.Tools
errcount++; errcount++;
} }
totbytes += file.ExtractedByteCount;
curfile++; curfile++;
} }
UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered, " + totbytes.ToString() + " total bytes extracted."); UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered");
InProgress = false; InProgress = false;
}); });
} }

View File

@ -17,9 +17,9 @@ namespace CodeWalker.Tools
{ {
Dictionary<uint, string> extraStrings = new Dictionary<uint, string>(); Dictionary<uint, string> extraStrings = new Dictionary<uint, string>();
private static GameFileCache GameFileCache => GameFileCacheFactory.Instance;
public JenkIndForm()
public JenkIndForm(GameFileCache gameFileCache = null)
{ {
InitializeComponent(); InitializeComponent();
@ -32,16 +32,15 @@ namespace CodeWalker.Tools
MainPanel.Enabled = false; MainPanel.Enabled = false;
Cursor = Cursors.WaitCursor; Cursor = Cursors.WaitCursor;
if ((gameFileCache == null) || (gameFileCache.IsInited == false)) if (!GameFileCache.IsInited)
{ {
Task.Run(() => Task.Run(() =>
{ {
try try
{ {
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
GameFileCache gfc = GameFileCacheFactory.GetInstance(); GameFileCache.DoFullStringIndex = true;
gfc.DoFullStringIndex = true; GameFileCache.Init(UpdateStatus, UpdateStatus);
gfc.Init(UpdateStatus, UpdateStatus);
IndexBuildComplete(); IndexBuildComplete();
} }
catch (Exception ex) catch (Exception ex)
@ -58,8 +57,8 @@ namespace CodeWalker.Tools
try try
{ {
UpdateStatus("Loading strings..."); UpdateStatus("Loading strings...");
gameFileCache.DoFullStringIndex = true; GameFileCache.DoFullStringIndex = true;
await gameFileCache.InitStringDictsAsync(); await GameFileCache.InitStringDictsAsync();
IndexBuildComplete(); IndexBuildComplete();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -9,6 +9,7 @@ using CodeWalker.Properties;
using Microsoft.Win32; using Microsoft.Win32;
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using CodeWalker.Utils; using CodeWalker.Utils;
using System.Diagnostics.CodeAnalysis;
namespace CodeWalker namespace CodeWalker
{ {
@ -16,7 +17,7 @@ namespace CodeWalker
{ {
private static string currentGTAFolder = Settings.Default.GTAFolder; private static string currentGTAFolder = Settings.Default.GTAFolder;
public static event Action<string> OnGTAFolderChanged; public static event Action<string>? OnGTAFolderChanged;
public static string CurrentGTAFolder { get => currentGTAFolder; private set public static string CurrentGTAFolder { get => currentGTAFolder; private set
{ {
if (currentGTAFolder == value) return; if (currentGTAFolder == value) return;
@ -24,9 +25,9 @@ namespace CodeWalker
OnGTAFolderChanged?.Invoke(currentGTAFolder); OnGTAFolderChanged?.Invoke(currentGTAFolder);
} }
} }
public static bool ValidateGTAFolder(string folder, out string failReason) public static bool ValidateGTAFolder([NotNullWhen(true)] string? folder, out string failReason)
{ {
failReason = ""; failReason = string.Empty;
if(string.IsNullOrWhiteSpace(folder)) if(string.IsNullOrWhiteSpace(folder))
{ {
@ -49,7 +50,7 @@ namespace CodeWalker
return true; return true;
} }
public static bool ValidateGTAFolder(string folder) => ValidateGTAFolder(folder, out string reason); public static bool ValidateGTAFolder([NotNullWhen(true)] string? folder) => ValidateGTAFolder(folder, out string reason);
public static bool IsCurrentGTAFolderValid() => ValidateGTAFolder(CurrentGTAFolder); public static bool IsCurrentGTAFolderValid() => ValidateGTAFolder(CurrentGTAFolder);
@ -60,11 +61,11 @@ namespace CodeWalker
return true; return true;
} }
string origFolder = CurrentGTAFolder; var origFolder = CurrentGTAFolder;
string folder = CurrentGTAFolder; var folder = CurrentGTAFolder;
SelectFolderForm f = new SelectFolderForm(); SelectFolderForm f = new SelectFolderForm();
string autoFolder = AutoDetectFolder(out string source); var autoFolder = AutoDetectFolder(out string? source);
if (autoFolder != null && MessageBox.Show($"Auto-detected game folder \"{autoFolder}\" from {source}.\n\nContinue with auto-detected folder?", "Auto-detected game folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes) if (autoFolder != null && MessageBox.Show($"Auto-detected game folder \"{autoFolder}\" from {source}.\n\nContinue with auto-detected folder?", "Auto-detected game folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
{ {
f.SelectedFolder = autoFolder; f.SelectedFolder = autoFolder;
@ -76,8 +77,7 @@ namespace CodeWalker
folder = f.SelectedFolder; folder = f.SelectedFolder;
} }
string failReason; if (ValidateGTAFolder(folder, out string? failReason))
if(ValidateGTAFolder(folder, out failReason))
{ {
SetGTAFolder(folder); SetGTAFolder(folder);
if (folder != origFolder) if (folder != origFolder)
@ -85,13 +85,15 @@ namespace CodeWalker
MessageBox.Show($"Successfully changed GTA Folder to \"{folder}\"", "Set GTA Folder", MessageBoxButtons.OK, MessageBoxIcon.Information); MessageBox.Show($"Successfully changed GTA Folder to \"{folder}\"", "Set GTA Folder", MessageBoxButtons.OK, MessageBoxIcon.Information);
} }
return true; return true;
} else }
else
{ {
var tryAgain = MessageBox.Show($"Folder \"{folder}\" is not a valid GTA folder:\n\n{failReason}\n\nDo you want to try choosing a different folder?", "Unable to set GTA Folder", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); var tryAgain = MessageBox.Show($"Folder \"{folder}\" is not a valid GTA folder:\n\n{failReason}\n\nDo you want to try choosing a different folder?", "Unable to set GTA Folder", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
if (tryAgain == DialogResult.Retry) if (tryAgain == DialogResult.Retry)
{ {
return UpdateGTAFolder(false); return UpdateGTAFolder(false);
} else }
else
{ {
return false; return false;
} }
@ -123,9 +125,9 @@ namespace CodeWalker
} }
RegistryKey baseKey32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); RegistryKey baseKey32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
string steamPathValue = baseKey32.OpenSubKey(@"Software\Rockstar Games\GTAV")?.GetValue("InstallFolderSteam") as string; string? steamPathValue = baseKey32.OpenSubKey(@"Software\Rockstar Games\GTAV")?.GetValue("InstallFolderSteam") as string;
string retailPathValue = baseKey32.OpenSubKey(@"Software\Rockstar Games\Grand Theft Auto V")?.GetValue("InstallFolder") as string; string? retailPathValue = baseKey32.OpenSubKey(@"Software\Rockstar Games\Grand Theft Auto V")?.GetValue("InstallFolder") as string;
string oivPathValue = Registry.CurrentUser.OpenSubKey(@"Software\NewTechnologyStudio\OpenIV.exe\BrowseForFolder")?.GetValue("game_path_Five_pc") as string; string? oivPathValue = Registry.CurrentUser.OpenSubKey(@"Software\NewTechnologyStudio\OpenIV.exe\BrowseForFolder")?.GetValue("game_path_Five_pc") as string;
if(steamPathValue?.EndsWith("\\GTAV") == true) if(steamPathValue?.EndsWith("\\GTAV") == true)
{ {
@ -150,7 +152,7 @@ namespace CodeWalker
return matches.Count > 0; return matches.Count > 0;
} }
public static string AutoDetectFolder(out string source) public static string? AutoDetectFolder(out string? source)
{ {
source = null; source = null;
@ -164,7 +166,7 @@ namespace CodeWalker
return null; return null;
} }
public static string AutoDetectFolder() => AutoDetectFolder(out string _); public static string? AutoDetectFolder() => AutoDetectFolder(out string? _);
public static void UpdateSettings() public static void UpdateSettings()
{ {

View File

@ -42,7 +42,18 @@ namespace CodeWalker
public volatile bool kbjump = false; public volatile bool kbjump = false;
public volatile bool kbmoving = false; public volatile bool kbmoving = false;
public KeyBindings keyBindings = new KeyBindings(Settings.Default.KeyBindings); private KeyBindings keyBindings;
public KeyBindings KeyBindings
{
get
{
return keyBindings ??= new KeyBindings(Settings.Default.KeyBindings);
}
set
{
keyBindings = value;
}
}
public bool CtrlPressed = false; public bool CtrlPressed = false;
public bool ShiftPressed = false; public bool ShiftPressed = false;
@ -127,13 +138,13 @@ namespace CodeWalker
//WASD move... //WASD move...
if (enablemove) if (enablemove)
{ {
if (k == keyBindings.MoveForward) kbmovefwd = true; if (k == KeyBindings.MoveForward) kbmovefwd = true;
if (k == keyBindings.MoveBackward) kbmovebck = true; if (k == KeyBindings.MoveBackward) kbmovebck = true;
if (k == keyBindings.MoveLeft) kbmovelft = true; if (k == KeyBindings.MoveLeft) kbmovelft = true;
if (k == keyBindings.MoveRight) kbmovergt = true; if (k == KeyBindings.MoveRight) kbmovergt = true;
if (k == keyBindings.MoveUp) kbmoveup = true; if (k == KeyBindings.MoveUp) kbmoveup = true;
if (k == keyBindings.MoveDown) kbmovedn = true; if (k == KeyBindings.MoveDown) kbmovedn = true;
if (k == keyBindings.Jump) kbjump = true; if (k == KeyBindings.Jump) kbjump = true;
} }
kbmoving = kbmovefwd || kbmovebck || kbmovelft || kbmovergt || kbmoveup || kbmovedn || kbjump; kbmoving = kbmovefwd || kbmovebck || kbmovelft || kbmovergt || kbmoveup || kbmovedn || kbjump;
@ -146,13 +157,13 @@ namespace CodeWalker
ShiftPressed = (e.Modifiers & Keys.Shift) > 0; ShiftPressed = (e.Modifiers & Keys.Shift) > 0;
var k = e.KeyCode; var k = e.KeyCode;
if (k == keyBindings.MoveForward) kbmovefwd = false; if (k == KeyBindings.MoveForward) kbmovefwd = false;
if (k == keyBindings.MoveBackward) kbmovebck = false; if (k == KeyBindings.MoveBackward) kbmovebck = false;
if (k == keyBindings.MoveLeft) kbmovelft = false; if (k == KeyBindings.MoveLeft) kbmovelft = false;
if (k == keyBindings.MoveRight) kbmovergt = false; if (k == KeyBindings.MoveRight) kbmovergt = false;
if (k == keyBindings.MoveUp) kbmoveup = false; if (k == KeyBindings.MoveUp) kbmoveup = false;
if (k == keyBindings.MoveDown) kbmovedn = false; if (k == KeyBindings.MoveDown) kbmovedn = false;
if (k == keyBindings.Jump) kbjump = false; if (k == KeyBindings.Jump) kbjump = false;
kbmoving = kbmovefwd || kbmovebck || kbmovelft || kbmovergt || kbmoveup || kbmovedn || kbjump; kbmoving = kbmovefwd || kbmovebck || kbmovelft || kbmovergt || kbmoveup || kbmovedn || kbjump;
@ -263,6 +274,7 @@ namespace CodeWalker
public KeyBindings(StringCollection sc) public KeyBindings(StringCollection sc)
{ {
Console.WriteLine("Creating KeyBindings");
foreach (string s in sc) foreach (string s in sc)
{ {
string[] parts = s.Split(':'); string[] parts = s.Split(':');

View File

@ -1041,7 +1041,7 @@ namespace CodeWalker
Input.KeyDown(e, enablemove); Input.KeyDown(e, enablemove);
var k = e.KeyCode; var k = e.KeyCode;
var kb = Input.keyBindings; var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed; bool ctrl = Input.CtrlPressed;
bool shift = Input.ShiftPressed; bool shift = Input.ShiftPressed;

View File

@ -18,7 +18,7 @@ namespace CodeWalker.World
public partial class CutsceneForm : Form public partial class CutsceneForm : Form
{ {
private WorldForm WorldForm; private WorldForm WorldForm;
private GameFileCache GameFileCache; private GameFileCache GameFileCache => GameFileCacheFactory.Instance;
private AudioDatabase AudioDatabase; private AudioDatabase AudioDatabase;
private Cutscene Cutscene = null; private Cutscene Cutscene = null;
@ -43,7 +43,6 @@ namespace CodeWalker.World
public CutsceneForm(WorldForm worldForm) public CutsceneForm(WorldForm worldForm)
{ {
WorldForm = worldForm; WorldForm = worldForm;
GameFileCache = WorldForm.GameFileCache;
AudioDatabase = new AudioDatabase(); AudioDatabase = new AudioDatabase();
InitializeComponent(); InitializeComponent();
} }
@ -105,7 +104,7 @@ namespace CodeWalker.World
private void SelectCutscene(CutsceneDropdownItem dditem) private void SelectCutscene(CutsceneDropdownItem dditem)
{ {
Cursor = Cursors.WaitCursor; Cursor = Cursors.WaitCursor;
Task.Run(() => Task.Run(async () =>
{ {
CutFile cutFile = null; CutFile cutFile = null;
Cutscene cutscene = null; Cutscene cutscene = null;
@ -125,7 +124,7 @@ namespace CodeWalker.World
GameFileCache.RpfMan.LoadFile(cutFile, entry); GameFileCache.RpfMan.LoadFile(cutFile, entry);
cutscene = new Cutscene(); cutscene = new Cutscene();
cutscene.Init(cutFile, GameFileCache, WorldForm, AudioDatabase); await cutscene.InitAsync(cutFile, GameFileCache, WorldForm, AudioDatabase);
} }
} }
@ -473,7 +472,7 @@ namespace CodeWalker.World
[TypeConverter(typeof(ExpandableObjectConverter))] public class Cutscene [TypeConverter(typeof(ExpandableObjectConverter))] public class Cutscene
{ {
public CutFile CutFile { get; set; } = null; public CutFile CutFile { get; set; } = null;
private GameFileCache GameFileCache = null; private GameFileCache GameFileCache => GameFileCacheFactory.Instance;
private WorldForm WorldForm = null; private WorldForm WorldForm = null;
private AudioDatabase AudioDB = null; private AudioDatabase AudioDB = null;
@ -516,10 +515,9 @@ namespace CodeWalker.World
public void Init(CutFile cutFile, GameFileCache gfc, WorldForm wf, AudioDatabase adb) public async ValueTask InitAsync(CutFile cutFile, GameFileCache gfc, WorldForm wf, AudioDatabase adb)
{ {
CutFile = cutFile; CutFile = cutFile;
GameFileCache = gfc;
WorldForm = wf; WorldForm = wf;
AudioDB = adb; AudioDB = adb;
@ -539,7 +537,7 @@ namespace CodeWalker.World
LoadYcds(); LoadYcds();
CreateSceneObjects(); await CreateSceneObjectsAsync();
RaiseEvents(0.0f); RaiseEvents(0.0f);
} }
@ -1164,9 +1162,9 @@ namespace CodeWalker.World
int drbl = args.iDrawable; int drbl = args.iDrawable;
int texx = args.iTexture; int texx = args.iTexture;
Task.Run(() => Task.Run(async () =>
{ {
cso.Ped.SetComponentDrawable(comp, drbl, 0, texx, GameFileCache); await cso.Ped.SetComponentDrawableAsync(comp, drbl, 0, texx, GameFileCache);
}); });
} }
@ -1262,7 +1260,7 @@ namespace CodeWalker.World
} }
private void CreateSceneObjects() private async ValueTask CreateSceneObjectsAsync()
{ {
SceneObjects = new Dictionary<int, CutsceneObject>(); SceneObjects = new Dictionary<int, CutsceneObject>();
@ -1274,7 +1272,7 @@ namespace CodeWalker.World
foreach (var obj in Objects.Values) foreach (var obj in Objects.Values)
{ {
var sobj = new CutsceneObject(); var sobj = new CutsceneObject();
sobj.Init(obj, GameFileCache, AudioDB); await sobj.InitAsync(obj, GameFileCache, AudioDB);
SceneObjects[sobj.ObjectID] = sobj; SceneObjects[sobj.ObjectID] = sobj;
if (sobj.AnimHash != 0) if (sobj.AnimHash != 0)
@ -1320,7 +1318,7 @@ namespace CodeWalker.World
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
public void Init(CutObject obj, GameFileCache gfc, AudioDatabase adb) public async ValueTask InitAsync(CutObject obj, GameFileCache gfc, AudioDatabase adb)
{ {
CutObject = obj; CutObject = obj;
ObjectID = obj?.iObjectId ?? -1; ObjectID = obj?.iObjectId ?? -1;
@ -1345,7 +1343,7 @@ namespace CodeWalker.World
} }
else if (obj is CutPedModelObject ped) else if (obj is CutPedModelObject ped)
{ {
InitPed(ped, gfc); await InitPedAsync(ped, gfc);
} }
else if (obj is CutPropModelObject prop) else if (obj is CutPropModelObject prop)
{ {
@ -1506,12 +1504,12 @@ namespace CodeWalker.World
} }
private void InitPed(CutPedModelObject ped, GameFileCache gfc) private async ValueTask InitPedAsync(CutPedModelObject ped, GameFileCache gfc)
{ {
Ped = new Ped(); Ped = new Ped();
Ped.Init(ped.StreamingName, gfc); await Ped.InitAsync(ped.StreamingName, gfc);
Ped.LoadDefaultComponents(gfc); await Ped.LoadDefaultComponentsAsync(gfc);
//if (ped.StreamingName == JenkHash.GenHash("player_zero")) //if (ped.StreamingName == JenkHash.GenHash("player_zero"))
//{ //{

View File

@ -27,7 +27,13 @@ namespace CodeWalker
{ {
public Form Form { get { return this; } } //for DXForm/DXManager use public Form Form { get { return this; } } //for DXForm/DXManager use
public Renderer Renderer { get; set; } public Renderer Renderer {
get
{
return renderer ??= new Renderer(this, GameFileCache);
}
set => renderer = value;
}
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } } public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
volatile bool formopen = false; volatile bool formopen = false;
@ -36,23 +42,31 @@ namespace CodeWalker
volatile bool initialised = false; volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch(); Stopwatch frametimer = new Stopwatch();
Space space = new Space(); Space space;
Camera camera; Camera camera;
Timecycle timecycle; Timecycle timecycle;
Weather weather; Weather weather;
Clouds clouds; Clouds clouds;
Water water = new Water();
Trains trains = new Trains(); private Water water;
Scenarios scenarios = new Scenarios(); Water Water { get => water ??= new Water(); }
PopZones popzones = new PopZones(); private Trains trains;
Heightmaps heightmaps = new Heightmaps(); Trains Trains { get => trains ??= new Trains(); }
Watermaps watermaps = new Watermaps(); private Scenarios scenarios;
AudioZones audiozones = new AudioZones(); Scenarios Scenarios { get => scenarios ??= new Scenarios(); }
private PopZones popZones;
PopZones PopZones { get => popZones ??= new PopZones(); }
private Heightmaps heightmaps;
Heightmaps Heightmaps { get => heightmaps ??= new Heightmaps(); }
private Watermaps watermaps;
Watermaps Watermaps { get => watermaps ??= new Watermaps(); }
private AudioZones audioZones;
AudioZones AudioZones { get => audioZones ??= new AudioZones(); }
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
public CancellationToken CancellationToken; public CancellationToken CancellationToken;
public Space Space { get { return space; } } public Space Space { get => space ??= new Space(); }
bool MouseLButtonDown = false; bool MouseLButtonDown = false;
bool MouseRButtonDown = false; bool MouseRButtonDown = false;
@ -70,8 +84,9 @@ namespace CodeWalker
Vector3 prevworldpos = new Vector3(0, 0, 100); //also the start pos Vector3 prevworldpos = new Vector3(0, 0, 100); //also the start pos
public GameFileCache GameFileCache { get => gameFileCache; } private GameFileCache gameFileCache;
GameFileCache gameFileCache = GameFileCacheFactory.GetInstance(); public GameFileCache GameFileCache { get => gameFileCache ??= GameFileCacheFactory.GetInstance(); }
WorldControlMode ControlMode = WorldControlMode.Free; WorldControlMode ControlMode = WorldControlMode.Free;
@ -91,8 +106,11 @@ namespace CodeWalker
bool ControlBrushEnabled; bool ControlBrushEnabled;
//float ControlBrushRadius; //float ControlBrushRadius;
Entity camEntity = new Entity(); private Entity camEntity;
PedEntity pedEntity = new PedEntity(); private PedEntity pedEntity;
Entity CamEntity { get => camEntity ??= new Entity(); }
PedEntity PedEntity { get => pedEntity ??= new PedEntity(); }
bool iseditmode = false; bool iseditmode = false;
@ -216,10 +234,17 @@ namespace CodeWalker
public WorldForm() public WorldForm()
{ {
Task.Run(() => {
if (!GameFileCache.IsInited)
{
GameFileCache.Init();
}
});
CancellationToken = CancellationTokenSource.Token; CancellationToken = CancellationTokenSource.Token;
InitializeComponent(); InitializeComponent();
Renderer = new Renderer(this, gameFileCache); Renderer = new Renderer(this, GameFileCache);
camera = Renderer.camera; camera = Renderer.camera;
timecycle = Renderer.timecycle; timecycle = Renderer.timecycle;
weather = Renderer.weather; weather = Renderer.weather;
@ -228,20 +253,15 @@ namespace CodeWalker
CurMouseHit.WorldForm = this; CurMouseHit.WorldForm = this;
LastMouseHit.WorldForm = this; LastMouseHit.WorldForm = this;
PrevMouseHit.WorldForm = this; PrevMouseHit.WorldForm = this;
if (!gameFileCache.IsInited)
{
Task.Run(() => gameFileCache.Init());
}
initedOk = Renderer.Init();
} }
private void Init() private async ValueTask Init()
{ {
//called from WorldForm_Load //called from WorldForm_Load
initedOk = Renderer.Init();
if (!initedOk) if (!initedOk)
{ {
Close(); Close();
@ -365,11 +385,11 @@ namespace CodeWalker
} }
} }
camera.FollowEntity = camEntity; camera.FollowEntity = CamEntity;
camEntity.Position = (startupviewmode!=2) ? prevworldpos : Vector3.Zero; CamEntity.Position = (startupviewmode!=2) ? prevworldpos : Vector3.Zero;
camEntity.Orientation = Quaternion.LookAtLH(Vector3.Zero, Vector3.Up, Vector3.ForwardLH); CamEntity.Orientation = Quaternion.LookAtLH(Vector3.Zero, Vector3.Up, Vector3.ForwardLH);
space.AddPersistentEntity(pedEntity); Space.AddPersistentEntity(PedEntity);
LoadSettings(); LoadSettings();
@ -413,11 +433,10 @@ namespace CodeWalker
{ {
Renderer.BuffersResized(w, h); Renderer.BuffersResized(w, h);
if (WindowState == FormWindowState.Minimized && gameFileCache.IsInited) if (WindowState == FormWindowState.Minimized && GameFileCache.IsInited)
{ {
Console.WriteLine("Clearing cache"); Console.WriteLine("Clearing cache");
gameFileCache.Clear(); GameFileCache.Clear();
gameFileCache.IsInited = true;
//GC.Collect(); //GC.Collect();
} }
} }
@ -445,7 +464,7 @@ namespace CodeWalker
{ {
UpdateControlInputs(elapsed); UpdateControlInputs(elapsed);
space.Update(elapsed); Space.Update(elapsed);
if (CutsceneForm != null) if (CutsceneForm != null)
{ {
@ -597,7 +616,7 @@ namespace CodeWalker
Vector3 movewvec = camera.ViewInvQuaternion.Multiply(movevec); Vector3 movewvec = camera.ViewInvQuaternion.Multiply(movevec);
camEntity.Position += movewvec; CamEntity.Position += movewvec;
MapViewDragX = 0; MapViewDragX = 0;
MapViewDragY = 0; MapViewDragY = 0;
@ -682,9 +701,9 @@ namespace CodeWalker
movexy *= (1.0f + (Math.Min(Math.Max(Input.xblt, 0.0f), 1.0f) * 15.0f)); //boost with left trigger movexy *= (1.0f + (Math.Min(Math.Max(Input.xblt, 0.0f), 1.0f) * 15.0f)); //boost with left trigger
pedEntity.ControlMovement = movexy; PedEntity.ControlMovement = movexy;
pedEntity.ControlJump = Input.kbjump || Input.ControllerButtonPressed(GamepadButtonFlags.X); PedEntity.ControlJump = Input.kbjump || Input.ControllerButtonPressed(GamepadButtonFlags.X);
pedEntity.ControlBoost = Input.ShiftPressed || Input.ControllerButtonPressed(GamepadButtonFlags.A | GamepadButtonFlags.RightShoulder | GamepadButtonFlags.LeftShoulder); PedEntity.ControlBoost = Input.ShiftPressed || Input.ControllerButtonPressed(GamepadButtonFlags.A | GamepadButtonFlags.RightShoulder | GamepadButtonFlags.LeftShoulder);
//Vector3 pedfwd = pedEntity.Orientation.Multiply(Vector3.UnitZ); //Vector3 pedfwd = pedEntity.Orientation.Multiply(Vector3.UnitZ);
@ -725,9 +744,9 @@ namespace CodeWalker
if (renderworld) if (renderworld)
{ {
space.GetVisibleYmaps(camera, hour, weathertype, renderworldVisibleYmapDict); Space.GetVisibleYmaps(camera, hour, weathertype, renderworldVisibleYmapDict);
spaceEnts = space.TemporaryEntities; spaceEnts = Space.TemporaryEntities;
} }
if (ProjectForm != null) if (ProjectForm != null)
@ -808,12 +827,12 @@ namespace CodeWalker
//enqueue collision meshes for rendering - from the world grid //enqueue collision meshes for rendering - from the world grid
collisionitems.Clear(); collisionitems.Clear();
space.GetVisibleBounds(camera, collisionmeshrange, collisionmeshlayers, collisionitems); Space.GetVisibleBounds(camera, collisionmeshrange, collisionmeshlayers, collisionitems);
collisionybns.Clear(); collisionybns.Clear();
foreach (var item in collisionitems) foreach (var item in collisionitems)
{ {
YbnFile ybn = gameFileCache.GetYbn(item.Name); YbnFile ybn = GameFileCache.GetYbn(item.Name);
if ((ybn != null) && (ybn.Loaded)) if ((ybn != null) && (ybn.Loaded))
{ {
collisionybns.Add(ybn); collisionybns.Add(ybn);
@ -825,7 +844,7 @@ namespace CodeWalker
{ {
if (mlo.Archetype == null) return; if (mlo.Archetype == null) return;
var hash = mlo.Archetype.Hash; var hash = mlo.Archetype.Hash;
YbnFile ybn = gameFileCache.GetYbn(hash); YbnFile ybn = GameFileCache.GetYbn(hash);
if ((ybn != null) && (ybn.Loaded)) if ((ybn != null) && (ybn.Loaded))
{ {
collisioninteriors[mlo] = ybn; collisioninteriors[mlo] = ybn;
@ -858,17 +877,17 @@ namespace CodeWalker
private void RenderWorldWaterQuads() private void RenderWorldWaterQuads()
{ {
var quads = RenderWorldBaseWaterQuads(water.WaterQuads, MapSelectionMode.WaterQuad); var quads = RenderWorldBaseWaterQuads(Water.WaterQuads, MapSelectionMode.WaterQuad);
Renderer.RenderWaterQuads(quads); Renderer.RenderWaterQuads(quads);
} }
private void RenderWorldWaterCalmingQuads() => RenderWorldBaseWaterQuads(water.CalmingQuads, MapSelectionMode.CalmingQuad); private void RenderWorldWaterCalmingQuads() => RenderWorldBaseWaterQuads(Water.CalmingQuads, MapSelectionMode.CalmingQuad);
private void RenderWorldWaterWaveQuads() => RenderWorldBaseWaterQuads(water.WaveQuads, MapSelectionMode.WaveQuad); private void RenderWorldWaterWaveQuads() => RenderWorldBaseWaterQuads(Water.WaveQuads, MapSelectionMode.WaveQuad);
private List<T> RenderWorldBaseWaterQuads<T>(IEnumerable<T> quads, MapSelectionMode requiredMode) where T : BaseWaterQuad private List<T> RenderWorldBaseWaterQuads<T>(IEnumerable<T> quads, MapSelectionMode requiredMode) where T : BaseWaterQuad
{ {
List<T> renderwaterquadlist = water.GetVisibleQuads<T>(camera, quads); List<T> renderwaterquadlist = Water.GetVisibleQuads<T>(camera, quads);
ProjectForm?.GetVisibleWaterQuads<T>(camera, renderwaterquadlist); ProjectForm?.GetVisibleWaterQuads<T>(camera, renderwaterquadlist);
@ -881,7 +900,7 @@ namespace CodeWalker
{ {
renderpathynds.Clear(); renderpathynds.Clear();
space.GetVisibleYnds(camera, renderpathynds); Space.GetVisibleYnds(camera, renderpathynds);
if (ProjectForm != null) if (ProjectForm != null)
{ {
@ -895,10 +914,10 @@ namespace CodeWalker
private void RenderWorldTrainTracks() private void RenderWorldTrainTracks()
{ {
if (!trains.Inited) return; if (!Trains.Inited) return;
rendertraintracklist.Clear(); rendertraintracklist.Clear();
rendertraintracklist.AddRange(trains.TrainTracks); rendertraintracklist.AddRange(Trains.TrainTracks);
if (ProjectForm != null) if (ProjectForm != null)
{ {
@ -914,7 +933,7 @@ namespace CodeWalker
{ {
rendernavmeshynvs.Clear(); rendernavmeshynvs.Clear();
space.GetVisibleYnvs(camera, collisionmeshrange, rendernavmeshynvs); Space.GetVisibleYnvs(camera, collisionmeshrange, rendernavmeshynvs);
if (ProjectForm != null) if (ProjectForm != null)
{ {
@ -930,10 +949,10 @@ namespace CodeWalker
private void RenderWorldScenarios() private void RenderWorldScenarios()
{ {
if (!scenarios.Inited) return; if (!Scenarios.Inited) return;
renderscenariolist.Clear(); renderscenariolist.Clear();
renderscenariolist.AddRange(scenarios.ScenarioRegions); renderscenariolist.AddRange(Scenarios.ScenarioRegions);
if (ProjectForm != null) if (ProjectForm != null)
{ {
@ -947,7 +966,7 @@ namespace CodeWalker
private void RenderWorldPopZones() private void RenderWorldPopZones()
{ {
if (!popzones.Inited) return; if (!PopZones.Inited) return;
//renderpopzonelist.Clear(); //renderpopzonelist.Clear();
//renderpopzonelist.AddRange(popzones.Groups.Values); //renderpopzonelist.AddRange(popzones.Groups.Values);
@ -957,12 +976,12 @@ namespace CodeWalker
//ProjectForm.GetVisiblePopZones(camera, renderpopzonelist); //ProjectForm.GetVisiblePopZones(camera, renderpopzonelist);
} }
Renderer.RenderPopZones(popzones); Renderer.RenderPopZones(PopZones);
} }
private void RenderWorldHeightmaps() private void RenderWorldHeightmaps()
{ {
if (!heightmaps.Inited) return; if (!Heightmaps.Inited) return;
//renderheightmaplist.Clear(); //renderheightmaplist.Clear();
//renderheightmaplist.AddRange(heightmaps.Heightmaps); //renderheightmaplist.AddRange(heightmaps.Heightmaps);
@ -972,12 +991,12 @@ namespace CodeWalker
//ProjectForm.GetVisibleHeightmaps(camera, renderheightmaplist); //ProjectForm.GetVisibleHeightmaps(camera, renderheightmaplist);
} }
Renderer.RenderBasePath(heightmaps); Renderer.RenderBasePath(Heightmaps);
} }
private void RenderWorldWatermaps() private void RenderWorldWatermaps()
{ {
if (!watermaps.Inited) return; if (!Watermaps.Inited) return;
//renderwatermaplist.Clear(); //renderwatermaplist.Clear();
//renderwatermaplist.AddRange(watermaps.Watermaps); //renderwatermaplist.AddRange(watermaps.Watermaps);
@ -987,12 +1006,12 @@ namespace CodeWalker
//ProjectForm.GetVisibleWatermaps(camera, renderwatermaplist); //ProjectForm.GetVisibleWatermaps(camera, renderwatermaplist);
} }
Renderer.RenderBasePath(watermaps); Renderer.RenderBasePath(Watermaps);
} }
private void RenderWorldAudioZones() private void RenderWorldAudioZones()
{ {
if (!audiozones.Inited) return; if (!AudioZones.Inited) return;
renderaudfilelist.Clear(); renderaudfilelist.Clear();
renderaudfilelist.AddRange(GameFileCache.AudioDatRelFiles); renderaudfilelist.AddRange(GameFileCache.AudioDatRelFiles);
@ -1003,7 +1022,7 @@ namespace CodeWalker
} }
renderaudplacementslist.Clear(); renderaudplacementslist.Clear();
audiozones.GetPlacements(renderaudfilelist, renderaudplacementslist); AudioZones.GetPlacements(renderaudfilelist, renderaudplacementslist);
@ -1125,7 +1144,7 @@ namespace CodeWalker
{ {
hash = JenkHash.GenHash(modelname); hash = JenkHash.GenHash(modelname);
} }
Archetype arche = gameFileCache.GetArchetype(hash); Archetype arche = GameFileCache.GetArchetype(hash);
Archetype selarch = null; Archetype selarch = null;
DrawableBase seldrwbl = null; DrawableBase seldrwbl = null;
@ -1139,7 +1158,7 @@ namespace CodeWalker
} }
else else
{ {
YmapFile ymap = gameFileCache.GetYmap(hash); YmapFile ymap = GameFileCache.GetYmap(hash);
if (ymap != null) if (ymap != null)
{ {
Renderer.RenderYmap(ymap); Renderer.RenderYmap(ymap);
@ -1147,7 +1166,7 @@ namespace CodeWalker
else else
{ {
//not a ymap... see if it's a ydr or yft //not a ymap... see if it's a ydr or yft
YdrFile ydr = gameFileCache.GetYdr(hash); YdrFile ydr = GameFileCache.GetYdr(hash);
if (ydr != null) if (ydr != null)
{ {
if (ydr.Loaded) if (ydr.Loaded)
@ -1159,7 +1178,7 @@ namespace CodeWalker
} }
else else
{ {
YftFile yft = gameFileCache.GetYft(hash); YftFile yft = GameFileCache.GetYft(hash);
if (yft != null) if (yft != null)
{ {
if (yft.Loaded) if (yft.Loaded)
@ -1186,7 +1205,7 @@ namespace CodeWalker
if ((selarch != null) && (seldrwbl == null)) if ((selarch != null) && (seldrwbl == null))
{ {
seldrwbl = gameFileCache.TryGetDrawable(selarch); seldrwbl = GameFileCache.TryGetDrawable(selarch);
} }
//select this item for viewing by the UI... //select this item for viewing by the UI...
@ -1209,7 +1228,7 @@ namespace CodeWalker
foreach (string lod in ymaplist) foreach (string lod in ymaplist)
{ {
uint hash = JenkHash.GenHash(lod); uint hash = JenkHash.GenHash(lod);
YmapFile ymap = gameFileCache.GetYmap(hash); YmapFile ymap = GameFileCache.GetYmap(hash);
Renderer.RenderYmap(ymap); Renderer.RenderYmap(ymap);
UpdateMouseHits(ymap); UpdateMouseHits(ymap);
@ -1763,13 +1782,13 @@ namespace CodeWalker
float downlimit = 20.0f; float downlimit = 20.0f;
Ray ray = new Ray(p, new Vector3(0, 0, -1.0f)); Ray ray = new Ray(p, new Vector3(0, 0, -1.0f));
ray.Position.Z += 0.1f; ray.Position.Z += 0.1f;
SpaceRayIntersectResult hit = space.RayIntersect(ray, downlimit); SpaceRayIntersectResult hit = Space.RayIntersect(ray, downlimit);
if (hit.Hit) if (hit.Hit)
{ {
return hit.Position; return hit.Position;
} }
ray.Position.Z += uplimit; ray.Position.Z += uplimit;
hit = space.RayIntersect(ray, downlimit); hit = Space.RayIntersect(ray, downlimit);
if (hit.Hit) if (hit.Hit)
{ {
return hit.Position; return hit.Position;
@ -1928,12 +1947,12 @@ namespace CodeWalker
ynd.UpdateAllNodePositions(); ynd.UpdateAllNodePositions();
ynd.BuildBVH(); ynd.BuildBVH();
space.BuildYndData(ynd); Space.BuildYndData(ynd);
} }
else else
{ {
ynd.UpdateAllNodePositions(); ynd.UpdateAllNodePositions();
space.BuildYndVerts(ynd, selection); Space.BuildYndVerts(ynd, selection);
} }
//lock (Renderer.RenderSyncRoot) //lock (Renderer.RenderSyncRoot)
{ {
@ -1948,7 +1967,7 @@ namespace CodeWalker
} }
public YndNode GetPathNodeFromSpace(ushort areaid, ushort nodeid) public YndNode GetPathNodeFromSpace(ushort areaid, ushort nodeid)
{ {
return space.NodeGrid.GetYndNode(areaid, nodeid); return Space.NodeGrid.GetYndNode(areaid, nodeid);
} }
public void UpdateCollisionBoundsGraphics(Bounds b) public void UpdateCollisionBoundsGraphics(Bounds b)
@ -2080,7 +2099,7 @@ namespace CodeWalker
public void UpdateAudioPlacementGraphics(RelFile rel) public void UpdateAudioPlacementGraphics(RelFile rel)
{ {
audiozones.PlacementsDict.Remove(rel); //should cause a rebuild to add/remove items AudioZones.PlacementsDict.Remove(rel); //should cause a rebuild to add/remove items
} }
@ -2136,12 +2155,12 @@ namespace CodeWalker
public void SetKeyBindings(KeyBindings kb) public void SetKeyBindings(KeyBindings kb)
{ {
Input.keyBindings = kb.Copy(); Input.KeyBindings = kb.Copy();
UpdateToolbarShortcutsText(); UpdateToolbarShortcutsText();
} }
private void UpdateToolbarShortcutsText() private void UpdateToolbarShortcutsText()
{ {
var kb = Input.keyBindings; var kb = Input.KeyBindings;
ToolbarSelectButton.ToolTipText = string.Format("Select objects / Exit edit mode ({0}, {1})", kb.ToggleMouseSelect, kb.ExitEditMode); ToolbarSelectButton.ToolTipText = string.Format("Select objects / Exit edit mode ({0}, {1})", kb.ToggleMouseSelect, kb.ExitEditMode);
ToolbarMoveButton.ToolTipText = string.Format("Move ({0})", kb.EditPosition); ToolbarMoveButton.ToolTipText = string.Format("Move ({0})", kb.EditPosition);
ToolbarRotateButton.ToolTipText = string.Format("Rotate ({0})", kb.EditRotation); ToolbarRotateButton.ToolTipText = string.Format("Rotate ({0})", kb.EditRotation);
@ -2246,7 +2265,7 @@ namespace CodeWalker
private void SpawnTestEntity(bool cameraCenter = false) private void SpawnTestEntity(bool cameraCenter = false)
{ {
if (!space.Inited) return; if (!Space.Inited) return;
Vector3 dir = (cameraCenter ? camera.ViewDirection : camera.MouseRay.Direction); Vector3 dir = (cameraCenter ? camera.ViewDirection : camera.MouseRay.Direction);
Vector3 ofs = (cameraCenter ? Vector3.Zero : camera.MouseRay.Position); Vector3 ofs = (cameraCenter ? Vector3.Zero : camera.MouseRay.Position);
@ -2290,7 +2309,7 @@ namespace CodeWalker
lock (Renderer.RenderSyncRoot) lock (Renderer.RenderSyncRoot)
{ {
space.AddTemporaryEntity(e); Space.AddTemporaryEntity(e);
} }
} }
@ -2318,13 +2337,13 @@ namespace CodeWalker
if (isfree && !wasfree) if (isfree && !wasfree)
{ {
camEntity.Position = pedEntity.Position; CamEntity.Position = PedEntity.Position;
pedEntity.Enabled = false; PedEntity.Enabled = false;
Renderer.timerunning = false; Renderer.timerunning = false;
camera.SetFollowEntity(camEntity); camera.SetFollowEntity(CamEntity);
camera.TargetDistance = 1.0f; //default? camera.TargetDistance = 1.0f; //default?
camera.Smoothness = Settings.Default.CameraSmoothing; camera.Smoothness = Settings.Default.CameraSmoothing;
@ -2332,13 +2351,13 @@ namespace CodeWalker
} }
else if (!isfree && wasfree) else if (!isfree && wasfree)
{ {
pedEntity.Position = camEntity.Position; PedEntity.Position = CamEntity.Position;
pedEntity.Velocity = Vector3.Zero; PedEntity.Velocity = Vector3.Zero;
pedEntity.Enabled = true; PedEntity.Enabled = true;
Renderer.timerunning = true; Renderer.timerunning = true;
camera.SetFollowEntity(pedEntity.CameraEntity); camera.SetFollowEntity(PedEntity.CameraEntity);
camera.TargetDistance = 0.01f; //1cm camera.TargetDistance = 0.01f; //1cm
camera.Smoothness = 20.0f; camera.Smoothness = 20.0f;
@ -2403,19 +2422,19 @@ namespace CodeWalker
public SpaceRayIntersectResult GetSpaceMouseRay() public SpaceRayIntersectResult GetSpaceMouseRay()
{ {
SpaceRayIntersectResult ret = new SpaceRayIntersectResult(); SpaceRayIntersectResult ret = new SpaceRayIntersectResult();
if (space.Inited && space.BoundsStore != null) if (Space.Inited && Space.BoundsStore != null)
{ {
Ray mray = new Ray(); Ray mray = new Ray();
mray.Position = camera.MouseRay.Position + camera.Position; mray.Position = camera.MouseRay.Position + camera.Position;
mray.Direction = camera.MouseRay.Direction; mray.Direction = camera.MouseRay.Direction;
return space.RayIntersect(mray, float.MaxValue, collisionmeshlayers); return Space.RayIntersect(mray, float.MaxValue, collisionmeshlayers);
} }
return ret; return ret;
} }
public SpaceRayIntersectResult Raycast(Ray ray) public SpaceRayIntersectResult Raycast(Ray ray)
{ {
return space.RayIntersect(ray, float.MaxValue, collisionmeshlayers); return Space.RayIntersect(ray, float.MaxValue, collisionmeshlayers);
} }
private void UpdateMouseHits() private void UpdateMouseHits()
@ -3503,7 +3522,7 @@ namespace CodeWalker
} }
if ((mhitv.Archetype != null) && (mhitv.Drawable == null)) if ((mhitv.Archetype != null) && (mhitv.Drawable == null))
{ {
mhitv.Drawable = gameFileCache.TryGetDrawable(mhitv.Archetype); //no drawable given.. try to get it from the cache.. if it's not there, drawable info won't display... mhitv.Drawable = GameFileCache.TryGetDrawable(mhitv.Archetype); //no drawable given.. try to get it from the cache.. if it's not there, drawable info won't display...
} }
var oldnode = SelectedItem.PathNode; var oldnode = SelectedItem.PathNode;
@ -4212,43 +4231,43 @@ namespace CodeWalker
{ {
UpdateStatus("Loading timecycles..."); UpdateStatus("Loading timecycles...");
timecycle.Init(gameFileCache, UpdateStatus); timecycle.Init(GameFileCache, UpdateStatus);
timecycle.SetTime(Renderer.timeofday); timecycle.SetTime(Renderer.timeofday);
UpdateStatus("Loading materials..."); UpdateStatus("Loading materials...");
BoundsMaterialTypes.Init(gameFileCache); BoundsMaterialTypes.Init(GameFileCache);
UpdateStatus("Loading weather..."); UpdateStatus("Loading weather...");
weather.Init(gameFileCache, UpdateStatus, timecycle); weather.Init(GameFileCache, UpdateStatus, timecycle);
UpdateWeatherTypesComboBox(weather); UpdateWeatherTypesComboBox(weather);
UpdateStatus("Loading clouds..."); UpdateStatus("Loading clouds...");
clouds.Init(gameFileCache, UpdateStatus, weather); clouds.Init(GameFileCache, UpdateStatus, weather);
UpdateCloudTypesComboBox(clouds); UpdateCloudTypesComboBox(clouds);
UpdateStatus("Loading water..."); UpdateStatus("Loading water...");
water.Init(gameFileCache, UpdateStatus); Water.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading trains..."); UpdateStatus("Loading trains...");
trains.Init(gameFileCache, UpdateStatus); Trains.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading scenarios..."); UpdateStatus("Loading scenarios...");
scenarios.Init(gameFileCache, UpdateStatus, timecycle); Scenarios.Init(GameFileCache, UpdateStatus, timecycle);
UpdateStatus("Loading popzones..."); UpdateStatus("Loading popzones...");
popzones.Init(gameFileCache, UpdateStatus); PopZones.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading heightmaps..."); UpdateStatus("Loading heightmaps...");
heightmaps.Init(gameFileCache, UpdateStatus); Heightmaps.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading watermaps..."); UpdateStatus("Loading watermaps...");
watermaps.Init(gameFileCache, UpdateStatus); Watermaps.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading audio zones..."); UpdateStatus("Loading audio zones...");
audiozones.Init(gameFileCache, UpdateStatus); AudioZones.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading world..."); UpdateStatus("Loading world...");
space.Init(gameFileCache, UpdateStatus); Space.Init(GameFileCache, UpdateStatus);
UpdateStatus("World loaded"); UpdateStatus("World loaded");
@ -4264,7 +4283,7 @@ namespace CodeWalker
{ {
lock (Renderer.RenderSyncRoot) lock (Renderer.RenderSyncRoot)
{ {
if (gameFileCache.SetDlcLevel(dlc, enable)) if (GameFileCache.SetDlcLevel(dlc, enable))
{ {
LoadWorld(); LoadWorld();
} }
@ -4283,9 +4302,9 @@ namespace CodeWalker
{ {
lock (Renderer.RenderSyncRoot) lock (Renderer.RenderSyncRoot)
{ {
if (gameFileCache.SetModsEnabled(enable)) if (GameFileCache.SetModsEnabled(enable))
{ {
UpdateDlcListComboBox(gameFileCache.DlcNameList); UpdateDlcListComboBox(GameFileCache.DlcNameList);
LoadWorld(); LoadWorld();
} }
@ -4326,18 +4345,18 @@ namespace CodeWalker
return; return;
} }
gameFileCache.UpdateStatus += UpdateStatus; GameFileCache.UpdateStatus += UpdateStatus;
gameFileCache.ErrorLog += LogError; GameFileCache.ErrorLog += LogError;
while (gameFileCache.IsIniting) while (GameFileCache.IsIniting)
{ {
await Task.Delay(0); await Task.Delay(0);
} }
if (!gameFileCache.IsInited) if (!GameFileCache.IsInited)
{ {
gameFileCache.Init(); GameFileCache.Init();
} }
UpdateDlcListComboBox(gameFileCache.DlcNameList); UpdateDlcListComboBox(GameFileCache.DlcNameList);
EnableCacheDependentUI(); EnableCacheDependentUI();
@ -4351,7 +4370,7 @@ namespace CodeWalker
EnableDLCModsUI(); EnableDLCModsUI();
Task.Run(async () => _ = Task.Run(async () =>
{ {
try try
{ {
@ -4373,13 +4392,13 @@ namespace CodeWalker
Console.WriteLine("Renderer ContentThread stopped"); Console.WriteLine("Renderer ContentThread stopped");
}); });
Task.Run(async () => _ = Task.Run(async () =>
{ {
try try
{ {
while (formopen && !IsDisposed && !CancellationToken.IsCancellationRequested) //main asset loop while (formopen && !IsDisposed && !CancellationToken.IsCancellationRequested) //main asset loop
{ {
bool fcItemsPending = gameFileCache.ContentThreadProc(); bool fcItemsPending = GameFileCache.ContentThreadProc();
if (!fcItemsPending) if (!fcItemsPending)
{ {
@ -4413,6 +4432,8 @@ namespace CodeWalker
private Stopwatch lastStatusUpdate = Stopwatch.StartNew(); private Stopwatch lastStatusUpdate = Stopwatch.StartNew();
private TimeSpan updateInterval = TimeSpan.FromSeconds(0.05); private TimeSpan updateInterval = TimeSpan.FromSeconds(0.05);
private Renderer renderer;
private void UpdateStatus(string text) private void UpdateStatus(string text)
{ {
try try
@ -4527,13 +4548,13 @@ namespace CodeWalker
{ {
DlcLevelComboBox.Items.Add(dlcname); DlcLevelComboBox.Items.Add(dlcname);
} }
if (string.IsNullOrEmpty(gameFileCache.SelectedDlc)) if (string.IsNullOrEmpty(GameFileCache.SelectedDlc))
{ {
DlcLevelComboBox.SelectedIndex = dlcnames.Count - 1; DlcLevelComboBox.SelectedIndex = dlcnames.Count - 1;
} }
else else
{ {
int idx = DlcLevelComboBox.FindString(gameFileCache.SelectedDlc); int idx = DlcLevelComboBox.FindString(GameFileCache.SelectedDlc);
DlcLevelComboBox.SelectedIndex = (idx > 0) ? idx : (dlcnames.Count - 1); DlcLevelComboBox.SelectedIndex = (idx > 0) ? idx : (dlcnames.Count - 1);
} }
} }
@ -4806,7 +4827,7 @@ namespace CodeWalker
EnableModsCheckBox.Checked = s.EnableMods; EnableModsCheckBox.Checked = s.EnableMods;
DlcLevelComboBox.Text = s.DLC; DlcLevelComboBox.Text = s.DLC;
gameFileCache.SelectedDlc = s.DLC; GameFileCache.SelectedDlc = s.DLC;
EnableDlcCheckBox.Checked = !string.IsNullOrEmpty(s.DLC); EnableDlcCheckBox.Checked = !string.IsNullOrEmpty(s.DLC);
} }
private void SaveSettings() private void SaveSettings()
@ -4849,8 +4870,8 @@ namespace CodeWalker
s.Clouds = CloudsComboBox.Text; s.Clouds = CloudsComboBox.Text;
//additional settings from gamefilecache... //additional settings from gamefilecache...
s.EnableMods = gameFileCache.EnableMods; s.EnableMods = GameFileCache.EnableMods;
s.DLC = gameFileCache.EnableDlc ? gameFileCache.SelectedDlc : ""; s.DLC = GameFileCache.EnableDlc ? GameFileCache.SelectedDlc : "";
s.Save(); s.Save();
} }
@ -6045,9 +6066,10 @@ namespace CodeWalker
CameraPositionTextBox.Text = FloatUtil.GetVector3StringFormat(camera.Position, "0.##"); CameraPositionTextBox.Text = FloatUtil.GetVector3StringFormat(camera.Position, "0.##");
} }
private void WorldForm_Load(object sender, EventArgs e) private bool isRenderedLoaded = false;
private async void WorldForm_Load(object sender, EventArgs e)
{ {
Init(); await Init();
} }
private void WorldForm_FormClosing(object sender, FormClosingEventArgs e) private void WorldForm_FormClosing(object sender, FormClosingEventArgs e)
@ -6379,7 +6401,7 @@ namespace CodeWalker
Input.KeyDown(e, enablemove); Input.KeyDown(e, enablemove);
var k = e.KeyCode; var k = e.KeyCode;
var kb = Input.keyBindings; var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed; bool ctrl = Input.CtrlPressed;
bool shift = Input.ShiftPressed; bool shift = Input.ShiftPressed;
@ -6977,7 +6999,7 @@ namespace CodeWalker
private void ToolsMenuAudioExplorer_Click(object sender, EventArgs e) private void ToolsMenuAudioExplorer_Click(object sender, EventArgs e)
{ {
AudioExplorerForm f = new AudioExplorerForm(gameFileCache); AudioExplorerForm f = new AudioExplorerForm();
f.Show(this); f.Show(this);
} }
@ -6988,7 +7010,7 @@ namespace CodeWalker
private void ToolsMenuBinarySearch_Click(object sender, EventArgs e) private void ToolsMenuBinarySearch_Click(object sender, EventArgs e)
{ {
BinarySearchForm f = new BinarySearchForm(gameFileCache); BinarySearchForm f = new BinarySearchForm();
f.Show(this); f.Show(this);
} }
@ -7000,7 +7022,7 @@ namespace CodeWalker
private void ToolsMenuJenkInd_Click(object sender, EventArgs e) private void ToolsMenuJenkInd_Click(object sender, EventArgs e)
{ {
JenkIndForm f = new JenkIndForm(gameFileCache); JenkIndForm f = new JenkIndForm();
f.Show(); f.Show();
} }
@ -7934,6 +7956,10 @@ namespace CodeWalker
private void AntiAliasingTrackBar_ValueChanged(object sender, EventArgs e) private void AntiAliasingTrackBar_ValueChanged(object sender, EventArgs e)
{ {
if (AntiAliasingTrackBar.Value == Settings.Default.AntiAliasing)
{
return;
}
Settings.Default.AntiAliasing = AntiAliasingTrackBar.Value; Settings.Default.AntiAliasing = AntiAliasingTrackBar.Value;
Settings.Default.Save(); Settings.Default.Save();
AntiAliasingValue.Text = Settings.Default.AntiAliasing.ToString(); AntiAliasingValue.Text = Settings.Default.AntiAliasing.ToString();