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

View File

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

View File

@ -2,15 +2,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static System.Reflection.Metadata.BlobBuilder;
namespace CodeWalker.Benchmarks
{
internal class Program
{
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>();
}
}

View File

@ -1,16 +1,6 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CodeWalker.Benchmarks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CodeWalker.Benchmarks")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -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
[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">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>annotations</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" />
<PackageReference Include="directxtex_desktop_win10" Version="2023.3.30.1" />
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
<PackageReference Include="DirectXTexNet" Version="1.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<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.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageReference Include="System.Diagnostics.Tracing" Version="4.3.0" />
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\magic.dat" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

@ -16,6 +16,7 @@ namespace CodeWalker.GameFiles
{
[TC(typeof(EXP))] public class PedsFile : GameFile, PackedFile
{
private static XmlNameTable cachedNameTable = new System.Xml.NameTable();
public PsoFile Pso { 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)

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.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
@ -15,7 +16,7 @@ namespace CodeWalker.GameFiles
{
public class VehiclesFile : GameFile, PackedFile
{
private static XmlNameTable cachedNameTable = new NameTable();
public string ResidentTxd { 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 xmlReader = XmlReader.Create(textReader);
using var xmlReader = XmlReader.Create(textReader, new XmlReaderSettings { NameTable = cachedNameTable });
while (xmlReader.Read())
{
@ -838,30 +839,27 @@ namespace CodeWalker.GameFiles
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);
if (ldarr == null) return null;
var floats = stackalloc float[ldarr.Length];
Span<float> floats = stackalloc float[ldarr.Length];
var i = 0;
foreach (var ldstr in ldarr)
{
var ldt = ldstr?.Trim();
if (!string.IsNullOrEmpty(ldt))
{
float f;
if (FloatUtil.TryParse(ldt, out f))
if (!string.IsNullOrEmpty(ldt) && FloatUtil.TryParse(ldt, out var f))
{
floats[i] = f;
i++;
}
}
}
if (i == 0) return null;
var result = new float[i];
Marshal.Copy((IntPtr)floats, result, 0, i);
floats.Slice(0, i).CopyTo(result);
return result;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,26 @@ using System.Threading.Tasks;
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
{
@ -53,15 +73,6 @@ namespace CodeWalker.GameFiles
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()
{
@ -163,7 +174,10 @@ namespace CodeWalker.GameFiles
}
private void ThrowInvalidResource()
{
throw new Exception("Invalid Resource - not GTAV!");
}
private void ReadHeader(BinaryReader br)
{
@ -178,7 +192,7 @@ namespace CodeWalker.GameFiles
if (Version != 0x52504637)
{
throw new Exception("Invalid Resource - not GTAV!");
ThrowInvalidResource();
}
var entriesLength = (int)EntryCount * 16;
@ -202,8 +216,8 @@ namespace CodeWalker.GameFiles
break;
case RpfEncryption.NG:
default:
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, entriesLength);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, namesLength);
GTACrypto.DecryptNG(entriesdata.AsSpan(0, entriesLength), Name, (uint)FileSize);
GTACrypto.DecryptNG(namesdata.AsSpan(0, namesLength), Name, (uint)FileSize);
IsNGEncrypted = true;
break;
@ -215,9 +229,6 @@ namespace CodeWalker.GameFiles
AllEntries = new List<RpfEntry>((int)EntryCount);
TotalFileCount = 0;
TotalFolderCount = 0;
TotalResourceCount = 0;
TotalBinaryFileCount = 0;
for (uint i = 0; i < EntryCount; i++)
{
@ -231,18 +242,15 @@ namespace CodeWalker.GameFiles
if (x == 0x7fffff00) //directory entry
{
e = new RpfDirectoryEntry();
TotalFolderCount++;
}
else if ((x & 0x80000000) == 0) //binary file entry
{
e = new RpfBinaryFileEntry();
TotalBinaryFileCount++;
TotalFileCount++;
}
else //assume resource file entry
{
e = new RpfResourceFileEntry();
TotalResourceCount++;
TotalFileCount++;
}
@ -328,39 +336,39 @@ namespace CodeWalker.GameFiles
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 br = new BinaryReader(fileStream);
try
{
return ScanStructure(br, updateStatus, errorLog);
return ScanStructure(br, updateStatus, errorLog) ?? default;
}
catch (Exception ex)
{
LastError = ex.ToString();
LastException = ex;
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
{
ReadHeader(br);
} catch
{
return false;
return null;
}
GrandTotalRpfCount = 1; //count this file..
GrandTotalFileCount = 1; //start with this one.
GrandTotalFolderCount = 0;
GrandTotalResourceCount = 0;
GrandTotalBinaryFileCount = 0;
var fileCounts = new FileCounts
{
Rpfs = 1,
Files = 1
};
Children = new List<RpfFile>();
@ -383,32 +391,29 @@ namespace CodeWalker.GameFiles
subfile.Parent = this;
subfile.ParentFileEntry = binentry;
if (subfile.ScanStructure(br, updateStatus, errorLog))
{
GrandTotalRpfCount += subfile.GrandTotalRpfCount;
GrandTotalFileCount += subfile.GrandTotalFileCount;
GrandTotalFolderCount += subfile.GrandTotalFolderCount;
GrandTotalResourceCount += subfile.GrandTotalResourceCount;
GrandTotalBinaryFileCount += subfile.GrandTotalBinaryFileCount;
var result = subfile.ScanStructure(br, updateStatus, errorLog);
if (result is not null)
{
fileCounts += result.Value;
Children.Add(subfile);
}
}
else
{
//binary file that's not an rpf...
GrandTotalBinaryFileCount++;
GrandTotalFileCount++;
fileCounts.BinaryFiles++;
fileCounts.Files++;
}
}
else if (entry is RpfResourceFileEntry)
{
GrandTotalResourceCount++;
GrandTotalFileCount++;
fileCounts.Resources++;
fileCounts.Files++;
}
else if (entry is RpfDirectoryEntry)
{
GrandTotalFolderCount++;
fileCounts.Folders++;
}
}
catch (Exception ex)
@ -416,7 +421,7 @@ namespace CodeWalker.GameFiles
errorLog?.Invoke(entry.Path + ": " + ex.ToString());
}
}
return true;
return fileCounts;
}
@ -500,7 +505,7 @@ namespace CodeWalker.GameFiles
}
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);
MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyToFast(outstr);
byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
Array.Copy(deflated, outbuf, outbuf.Length);
ds.CopyTo(outstr);
byte[] outbuf = outstr.ToArray();
bool pathok = true;
if (File.Exists(ofpath))
@ -604,11 +607,12 @@ namespace CodeWalker.GameFiles
{
LastError = ex.ToString();
LastException = ex;
Console.WriteLine(ex);
return null;
}
}
public ValueTask<byte[]> ExtractFileAsync(RpfFileEntry entry)
public async ValueTask<byte[]> ExtractFileAsync(RpfFileEntry entry)
{
try
{
@ -617,16 +621,16 @@ namespace CodeWalker.GameFiles
{
if (entry is RpfBinaryFileEntry binaryFileEntry)
{
return ExtractFileBinaryAsync(binaryFileEntry, br);
return await ExtractFileBinaryAsync(binaryFileEntry, br).ConfigureAwait(false);
}
else if (entry is RpfResourceFileEntry resourceFileEntry)
{
return ExtractFileResourceAsync(resourceFileEntry, br);
return await ExtractFileResourceAsync(resourceFileEntry, br).ConfigureAwait(false);
}
else
{
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();
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)
{
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)
{
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)
{
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)
{
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()
{
StringBuilder sb = new StringBuilder();
ExtractedByteCount = 0;
try
{
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.AppendLine();
}
else
{
ExtractedByteCount += data.Length;
}
}
else if (entry is RpfResourceFileEntry)
{
@ -1095,10 +1094,6 @@ namespace CodeWalker.GameFiles
sb.AppendFormat("{0} : Decompressed output was empty.", entry.Path);
sb.AppendLine();
}
else
{
ExtractedByteCount += data.Length;
}
}
}
}
@ -1128,18 +1123,22 @@ namespace CodeWalker.GameFiles
public List<RpfFileEntry> GetFiles(string folder, bool recurse)
{
if (Root == null)
{
return new List<RpfFileEntry>();
}
List<RpfFileEntry> result = new List<RpfFileEntry>();
string[] parts = folder.ToLowerInvariant().Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
//folder.AsSpan().Split
RpfDirectoryEntry dir = Root;
for (int i = 0; i < parts.Length; i++)
foreach(var part in folder.EnumerateSplit('\\'))
{
if (dir == null) break;
dir = FindSubDirectory(dir, parts[i]);
if (part.Length == 0)
continue;
dir = FindSubDirectory(dir, part);
}
if (dir != null)
{
GetFiles(dir, result, recurse);
}
return result;
}
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++)
{
var cdir = dir.Directories[i];
if (cdir.Name.ToLowerInvariant() == name)
if (cdir.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return cdir;
}
@ -1175,8 +1174,25 @@ namespace CodeWalker.GameFiles
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 byte[] DecompressBytes(byte[] bytes)
@ -1186,7 +1202,7 @@ namespace CodeWalker.GameFiles
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
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.
if (outbuf.Length <= bytes.Length)
@ -1212,14 +1228,8 @@ namespace CodeWalker.GameFiles
using DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress);
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[] 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)
{
@ -1245,9 +1255,7 @@ namespace CodeWalker.GameFiles
{
ds.Write(data, 0, data.Length);
ds.Close();
byte[] deflated = ms.GetBuffer();
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer...
Buffer.BlockCopy(deflated, 0, outbuf, 0, outbuf.Length);
byte[] outbuf = ms.ToArray(); //need to copy to the right size buffer...
return outbuf;
}
}
@ -1377,8 +1385,8 @@ namespace CodeWalker.GameFiles
}
private byte[] GetHeaderNamesData()
{
MemoryStream namesstream = new MemoryStream();
DataWriter nameswriter = new DataWriter(namesstream);
using MemoryStream namesstream = new MemoryStream();
using DataWriter nameswriter = new DataWriter(namesstream);
var namedict = new Dictionary<string, uint>();
foreach (var entry in AllEntries)
{
@ -1395,10 +1403,10 @@ namespace CodeWalker.GameFiles
nameswriter.Write(name);
}
}
var buf = new byte[namesstream.Length];
var buf = new byte[Math.Max(namesstream.Length, 16)];
namesstream.Position = 0;
namesstream.Read(buf, 0, buf.Length);
return PadBuffer(buf, 16);
namesstream.Read(buf, 0, (int)namesstream.Length);
return buf;
}
private byte[] GetHeaderEntriesData()
{
@ -1419,18 +1427,6 @@ namespace CodeWalker.GameFiles
uint headerblockcount = GetBlockCount(headerusedbytes);
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
{
uint rem = l % n;
@ -2430,7 +2426,10 @@ namespace CodeWalker.GameFiles
char ch = Name[i];
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;
}
if (ch == System.IO.Path.DirectorySeparatorChar || ch == System.IO.Path.AltDirectorySeparatorChar)
@ -3006,7 +3005,7 @@ namespace CodeWalker.GameFiles
{
public uint Value { get; set; }
public RpfResourcePage[] Pages
public readonly RpfResourcePage[] Pages
{
get
{
@ -3032,10 +3031,10 @@ namespace CodeWalker.GameFiles
}
}
public uint TypeVal { get { return (Value >> 28) & 0xF; } }
public uint BaseShift { get { return (Value & 0xF); } }
public uint BaseSize { get { return (0x200u << (int)BaseShift); } }
public uint[] BaseSizes
public readonly uint TypeVal { get { return (Value >> 28) & 0xF; } }
public readonly uint BaseShift { get { return (Value & 0xF); } }
public readonly uint BaseSize { get { return (0x200u << (int)BaseShift); } }
public readonly uint[] BaseSizes
{
get
{
@ -3054,7 +3053,7 @@ namespace CodeWalker.GameFiles
};
}
}
public uint[] PageCounts
public readonly uint[] PageCounts
{
get
{
@ -3072,7 +3071,7 @@ namespace CodeWalker.GameFiles
};
}
}
public uint[] PageSizes
public readonly uint[] PageSizes
{
get
{
@ -3092,7 +3091,7 @@ namespace CodeWalker.GameFiles
};
}
}
public uint Count
public readonly uint Count
{
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];
}
}
public uint Size
public readonly uint Size
{
get
{
@ -3152,7 +3151,7 @@ namespace CodeWalker.GameFiles
return new RpfResourcePageFlags(v);
}
public override string ToString()
public override readonly string ToString()
{
return "Size: " + Size.ToString() + ", Pages: " + Count.ToString();
}

View File

@ -8,8 +8,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
namespace CodeWalker.GameFiles
{
@ -42,6 +44,9 @@ namespace CodeWalker.GameFiles
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)
{
using var _ = new DisposableTimer("RpfManager.Init");
@ -52,14 +57,14 @@ namespace CodeWalker.GameFiles
var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
string[] allfiles = Directory.GetFiles(folder, "*.rpf", sopt);
BaseRpfs = new List<RpfFile>();
ModRpfs = new List<RpfFile>();
DlcRpfs = new List<RpfFile>();
AllRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
BaseRpfs = new List<RpfFile>(1300);
ModRpfs = new List<RpfFile>(0);
DlcRpfs = new List<RpfFile>(3500);
AllRpfs = new List<RpfFile>(5000);
DlcNoModRpfs = new List<RpfFile>(3500);
AllNoModRpfs = new List<RpfFile>(5000);
RpfDict = new Dictionary<string, RpfFile>(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(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)
{
Console.WriteLine(rf.LastException);
return;
}
@ -99,12 +105,10 @@ namespace CodeWalker.GameFiles
}
});
var calculateSum = (RpfFile rpf) => { return 0; };
calculateSum = (RpfFile rpf) =>
static int calculateSum(RpfFile rpf)
{
return rpf.AllEntries?.Count ?? 0 + rpf.Children?.Sum(calculateSum) ?? 0;
};
}
var minCapacity = rpfs.Sum(calculateSum);
if (minCapacity > AllRpfs.Capacity)
@ -119,13 +123,13 @@ namespace CodeWalker.GameFiles
if (buildIndex)
{
updateStatus?.Invoke("Building jenkindex...");
Task.Run(() =>
{
updateStatus?.Invoke("Building jenkindex...");
BuildBaseJenkIndex();
IsInited = true;
});
updateStatus?.Invoke("Scan complete");
});
}
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)
@ -149,20 +153,23 @@ namespace CodeWalker.GameFiles
DlcRpfs = new List<RpfFile>();
DlcNoModRpfs = new List<RpfFile>();
AllNoModRpfs = new List<RpfFile>();
RpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
RpfDict = new Dictionary<string, RpfFile>(DefaultRpfDictCapacity, StringComparer.OrdinalIgnoreCase);
EntryDict = new Dictionary<string, RpfEntry>(DefaultEntryDictCapacity, StringComparer.OrdinalIgnoreCase);
ModRpfDict = new Dictionary<string, RpfFile>(StringComparer.OrdinalIgnoreCase);
ModEntryDict = new Dictionary<string, RpfEntry>(StringComparer.OrdinalIgnoreCase);
foreach (var rpf in allRpfs)
{
RpfDict[rpf.Path] = rpf;
if (rpf.AllEntries == null) continue;
if (rpf.AllEntries == null)
continue;
foreach (var entry in rpf.AllEntries)
{
EntryDict[entry.Path] = entry;
}
}
Console.WriteLine($"RpfDict: {RpfDict.Count}; EntryDict: {EntryDict.Count}");
Task.Run(() =>
{
BuildBaseJenkIndex();
@ -173,7 +180,10 @@ namespace CodeWalker.GameFiles
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));
if (file.AllEntries != null)
@ -313,6 +323,7 @@ namespace CodeWalker.GameFiles
byte[] bytes = GetFileData(path);
return TextUtil.GetUTF8Text(bytes);
}
public XmlDocument GetFileXml(string path)
{
XmlDocument doc = new XmlDocument();
@ -321,6 +332,7 @@ namespace CodeWalker.GameFiles
{
doc.LoadXml(text);
}
return doc;
}
@ -373,12 +385,12 @@ namespace CodeWalker.GameFiles
file.Load(data, entry);
}
return file;
} catch(Exception ex)
}
catch(Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile
{
@ -428,12 +440,96 @@ namespace CodeWalker.GameFiles
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()
{
using var _ = new DisposableTimer("BuildBaseJenkIndex");
Parallel.ForEach(AllRpfs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (file) =>
Parallel.ForEach(AllRpfs, (file) =>
{
try
{
@ -475,24 +571,26 @@ namespace CodeWalker.GameFiles
JenkIndex.EnsureLower(nameChildrenLod + 'a');
JenkIndex.EnsureLower(nameChildrenLod + 'b');
}
var idx = name.LastIndexOf('_');
if (idx > 0)
{
var str1 = name.Substring(0, idx);
var idx2 = str1.LastIndexOf('_');
if (idx2 > 0)
{
var str2 = str1.Substring(0, idx2);
JenkIndex.EnsureLower(str2 + "_lod");
var maxi = 100;
for (int i = 1; i <= maxi; i++)
{
var str3 = str2 + '_' + i.ToString().PadLeft(2, '0') + "_lod";
//JenkIndex.Ensure(str3);
JenkIndex.EnsureLower(str3);
}
}
}
//var idx = name.LastIndexOf('_');
//if (idx > 0)
//{
// var str1 = name.Substring(0, idx);
// var idx2 = str1.LastIndexOf('_');
// if (idx2 > 0)
// {
// var str2 = str1.Substring(0, idx2);
// JenkIndex.EnsureLower(str2 + "_lod");
// var maxi = 100;
// for (int i = 1; i <= maxi; i++)
// {
// var str3 = str2 + '_' + i.ToString().PadLeft(2, '0') + "_lod";
// //JenkIndex.Ensure(str3);
// JenkIndex.EnsureLower(str3);
// }
// }
//}
AddAllLods(name);
}
else if(name.EndsWith(".sps", StringComparison.OrdinalIgnoreCase))
{
@ -560,6 +658,7 @@ namespace CodeWalker.GameFiles
catch(Exception err)
{
ErrorLog?.Invoke(err.ToString());
Console.WriteLine(err.ToString());
//failing silently!! not so good really
}
});
@ -572,6 +671,14 @@ namespace CodeWalker.GameFiles
{
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
{
LittleEndian,
BigEndian
LittleEndian = 0,
BigEndian = 1,
}
public enum DataType
@ -64,16 +64,10 @@ namespace CodeWalker.GameFiles
{
private Stream baseStream;
private readonly byte[] _buffer = new byte[8];
/// <summary>
/// Gets or sets the endianess of the underlying stream.
/// </summary>
public Endianess Endianess
{
get;
set;
}
public Endianess Endianess { get; set; } = Endianess.LittleEndian;
/// <summary>
/// Gets the length of the underlying stream.
@ -101,15 +95,19 @@ namespace CodeWalker.GameFiles
}
}
/// <summary>
/// Initializes a new data reader for the specified stream.
/// </summary>
public DataReader(Stream stream, Endianess endianess = Endianess.LittleEndian)
public DataReader(Stream stream)
{
if (stream is not null)
{
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;
}
@ -123,6 +121,25 @@ namespace CodeWalker.GameFiles
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>
/// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
@ -187,7 +204,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -195,7 +222,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -203,7 +240,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -211,7 +258,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -219,7 +276,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -227,7 +294,18 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -235,7 +313,17 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
@ -243,12 +331,23 @@ namespace CodeWalker.GameFiles
/// </summary>
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>
/// Reads a string.
/// </summary>
[SkipLocalsInit]
unsafe public string ReadStringLength(int length)
{
if (length == 0)
@ -268,47 +367,30 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Reads a string.
/// </summary>
[SkipLocalsInit]
unsafe public string ReadString(int maxLength = 1024)
{
var bytes = stackalloc byte[Math.Min(maxLength, 1024)];
var chars = stackalloc char[Math.Min(maxLength, 1024)];
Span<byte> bytes = stackalloc byte[Math.Min(maxLength, 1024)];
Span<char> chars = stackalloc char[Math.Min(maxLength, 1024)];
var temp = ReadByte();
var charsRead = 0;
while (temp != 0 && (Length == -1 || Position <= Length))
var bytesRead = 0;
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();
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));
}
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()
{

View File

@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
@ -59,21 +60,33 @@ namespace CodeWalker.GameFiles
public static byte[] DecryptAESData(byte[] data, byte[] key, int length, int rounds = 1)
{
var rijndael = Rijndael.Create();
rijndael.KeySize = 256;
rijndael.Key = key;
rijndael.BlockSize = 128;
rijndael.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.None;
using var aes = Aes.Create();
aes.KeySize = 256;
aes.Key = key;
aes.BlockSize = 128;
aes.Mode = CipherMode.ECB;
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;
// decrypt...
if (length > 0)
{
var decryptor = rijndael.CreateDecryptor();
using var decryptorAes = aes.CreateDecryptor();
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;
@ -104,7 +117,7 @@ namespace CodeWalker.GameFiles
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint[][] GetNGKey(string name, uint length)
{
uint hash = GTA5Hash.CalculateHash(name);
@ -112,36 +125,39 @@ namespace CodeWalker.GameFiles
return GTA5Keys.PC_NG_KEYS[keyidx];
}
public static byte[] DecryptNG(byte[] data, string name, uint fileSize, int offset = 0, int? length = null)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNG(byte[] data, string name, uint fileSize, int offset = 0, int? length = null)
{
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;
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;
}
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)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNGBlock(Span<byte> data, uint[][] key)
{
DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
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]);
}
public unsafe static void DecryptNGRoundA(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)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void DecryptNGRoundA(Span<byte> data, uint[] key, uint[][] table)
{
var x1 =
table[0][data[0]] ^
@ -194,14 +194,16 @@ namespace CodeWalker.GameFiles
table[15][data[15]] ^
key[3];
*(uint*)data = x1;
*(uint*)(data + 4) = x2;
*(uint*)(data + 8) = x3;
*(uint*)(data + 12) = x4;
MemoryMarshal.Write(data.Slice(0, 4), ref x1);
MemoryMarshal.Write(data.Slice(4, 4), ref x2);
MemoryMarshal.Write(data.Slice(8, 4), ref x3);
MemoryMarshal.Write(data.Slice(12, 4), ref x4);
}
// 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 =
table[0][data[0]] ^
@ -228,10 +230,10 @@ namespace CodeWalker.GameFiles
table[12][data[12]] ^
key[3];
*(uint*)data = x1;
*(uint*)(data + 4) = x2;
*(uint*)(data + 8) = x3;
*(uint*)(data + 12) = x4;
MemoryMarshal.Write(data.Slice(0, 4), ref x1);
MemoryMarshal.Write(data.Slice(4, 4), ref x2);
MemoryMarshal.Write(data.Slice(8, 4), ref x3);
MemoryMarshal.Write(data.Slice(12, 4), ref x4);
}
@ -248,8 +250,6 @@ namespace CodeWalker.GameFiles
public static byte[] EncryptNG(byte[] data, string name, uint 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.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
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);
//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);
var tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 272);
PC_NG_KEYS = new uint[tabs.Length][][];
@ -92,31 +94,21 @@ namespace CodeWalker.GameFiles
}
//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);
//updateStatus("ng decrypt tables found");
updateStatus("Searching for NG hash lookup tables...");
updateStatus?.Invoke("Searching for NG hash lookup tables...");
// 17 rounds
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 = 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);
}
}
LoadPCNGDecryptTable(tabs);
PC_LUT = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_LUT_HASH, 0x100);
//updateStatus("ng hash LUTs found");
updateStatus("Calculating NG encryption tables...");
updateStatus?.Invoke("Calculating NG encryption tables...");
PC_NG_ENCRYPT_TABLES = new uint[17][][];
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]);
//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]);
//updateStatus("ng encrypt table 2 of 17 calculated");
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]);
//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]);
//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)
{
@ -175,10 +201,14 @@ namespace CodeWalker.GameFiles
updateStatus?.Invoke("Complete.");
}
private static bool keysLoaded = false;
public static void LoadFromPath(string path = ".\\Keys", string key = null)
{
if (keysLoaded)
{
return;
}
//PC_AES_KEY = File.ReadAllBytes(path + "\\gtav_aes_key.dat");
//PC_NG_KEYS = CryptoIO.ReadNgKeys(path + "\\gtav_ng_key.dat");
//PC_NG_DECRYPT_TABLES = CryptoIO.ReadNgTables(path + "\\gtav_ng_decrypt_tables.dat");
@ -188,6 +218,7 @@ namespace CodeWalker.GameFiles
//GenerateMagicData(path);
UseMagicData(path, key);
keysLoaded = true;
}
public static void SaveToPath(string path = ".\\Keys")
@ -298,7 +329,7 @@ namespace CodeWalker.GameFiles
{
using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream())
{
ds.CopyToFast(outstr);
ds.CopyTo(outstr);
b = outstr.GetBuffer();
}
}
@ -760,11 +791,12 @@ namespace CodeWalker.GameFiles
public static byte[][] SearchHashes(Stream stream, IList<byte[]> hashes, int length = 32)
{
stream = Stream.Synchronized(stream);
var result = new byte[hashes.Count][];
Parallel.For(0, (stream.Length / BLOCK_LENGTH), (long k) => {
var hashProvider = new SHA1CryptoServiceProvider();
// TODO: Convert to stack alloc when length appropriate (1024 or lower probs)
var buffer = new byte[length];
for (long i = 0; i < (BLOCK_LENGTH / ALIGN_LENGTH); i++)
{
@ -772,11 +804,9 @@ namespace CodeWalker.GameFiles
if (position >= stream.Length)
continue;
lock (stream)
{
stream.Position = position;
stream.Read(buffer, 0, length);
}
var hash = hashProvider.ComputeHash(buffer);
for (int j = 0; j < hashes.Count; j++)
@ -852,7 +882,7 @@ namespace CodeWalker.GameFiles
firstPivot.SetB();
pivots.Add(firstPivot);
var buf_encrypted = new byte[16];
Span<byte> buf_encrypted = stackalloc byte[16];
for (int pivotIdx = 1; pivotIdx < 1024; pivotIdx++)
{
while (true)
@ -1221,7 +1251,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create))
{
ms.Position = 0;
ms.CopyToFast(fs);
ms.CopyTo(fs);
}
}
@ -1308,7 +1338,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create))
{
ms.Position = 0;
ms.CopyToFast(fs);
ms.CopyTo(fs);
}
}
@ -1446,7 +1476,7 @@ namespace CodeWalker.GameFiles
using (var fs = new FileStream(fileName, FileMode.Create))
{
ms.Position = 0;
ms.CopyToFast(fs);
ms.CopyTo(fs);
}
}
}
@ -1463,6 +1493,7 @@ namespace CodeWalker.GameFiles
LUT = GTA5Keys.PC_LUT;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint CalculateHash(string text)
{
/*

View File

@ -276,7 +276,8 @@ namespace CodeWalker.GameFiles
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)
{
@ -293,7 +294,25 @@ namespace CodeWalker.GameFiles
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)
@ -302,6 +321,12 @@ namespace CodeWalker.GameFiles
Ensure(str, hash);
}
public static void EnsureLower(ReadOnlySpan<char> str)
{
uint hash = JenkHash.GenHashLower(str);
Ensure(str, hash);
}
public static void EnsureBoth(string str)
{
uint hash = JenkHash.GenHash(str);
@ -312,25 +337,15 @@ namespace CodeWalker.GameFiles
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 AddRangeLower(params string[] strings)
public static void EnsureBoth(ReadOnlySpan<char> str)
{
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);
if (hash == 0) continue;
Index[hash] = s;
Ensure(str, hashLower);
}
}

View File

@ -196,10 +196,6 @@ namespace CodeWalker.GameFiles
var rel3 = XmlRel.GetRel(relxml);
if (rel3 != null)
{
if (rel3.RelDatasSorted?.Length != rel.RelDatasSorted?.Length)
{ } //check nothing went missing...
data = rel3.Save(); //full roundtrip!
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.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
@ -16,152 +17,45 @@ namespace CodeWalker.Core.Utils
{
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);
try
public Span<byte> Buffer { get; private set; }
private int _position;
public SpanStream(Span<byte> buffer)
{
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
Buffer = buffer;
_position = 0;
}
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)
{
try
{
await writeTask.ConfigureAwait(false);
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
var span = Buffer.Slice(origPos, count);
_position = newPos;
return span;
}
public static ValueTask WriteAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return new ValueTask(stream.WriteAsync(array.Array!, array.Offset, array.Count, cancellationToken));
}
public short ReadInt16() => BinaryPrimitives.ReadInt16LittleEndian(InternalRead(sizeof(short)));
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.Span.CopyTo(sharedBuffer);
return new ValueTask(FinishWriteAsync(stream.WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer));
}
public ushort ReadUInt16() => BinaryPrimitives.ReadUInt16LittleEndian(InternalRead(sizeof(ushort)));
public static void Write(this Stream stream, ReadOnlySpan<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
buffer.CopyTo(sharedBuffer);
stream.Write(sharedBuffer, 0, buffer.Length);
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Span<byte> buffer)
{
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static int Read(this Stream stream, Memory<byte> buffer)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return stream.Read(array.Array!, array.Offset, array.Count);
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
try
{
int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
if ((uint)numRead > (uint)buffer.Length)
{
throw new IOException("Stream too long!");
}
new ReadOnlySpan<byte>(sharedBuffer, 0, numRead).CopyTo(buffer.Span);
return numRead;
}
finally
{
ArrayPool<byte>.Shared.Return(sharedBuffer);
}
}
public static ValueTask<int> ReadAsync(this Stream stream, Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> array))
{
return new ValueTask<int>(stream.ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken));
}
byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer);
static async ValueTask<int> FinishReadAsync(Task<int> readTask, byte[] localBuffer, Memory<byte> localDestination)
{
try
{
int result = await readTask.ConfigureAwait(false);
new ReadOnlySpan<byte>(localBuffer, 0, result).CopyTo(localDestination.Span);
return result;
}
finally
{
ArrayPool<byte>.Shared.Return(localBuffer);
}
}
}
public int ReadInt32() => BinaryPrimitives.ReadInt32LittleEndian(InternalRead(sizeof(int)));
public uint ReadUInt32() => BinaryPrimitives.ReadUInt32LittleEndian(InternalRead(sizeof(uint)));
public long ReadInt64() => BinaryPrimitives.ReadInt64LittleEndian(InternalRead(sizeof(long)));
public ulong ReadUInt64() => BinaryPrimitives.ReadUInt64LittleEndian(InternalRead(sizeof(ulong)));
public unsafe Half ReadHalf() => BinaryPrimitives.ReadHalfLittleEndian(InternalRead(sizeof(Half)));
public unsafe float ReadSingle() => BinaryPrimitives.ReadSingleLittleEndian(InternalRead(sizeof(float)));
public unsafe double ReadDouble() => BinaryPrimitives.ReadDoubleLittleEndian(InternalRead(sizeof(double)));
}
}

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 SharpDX;
using Color = SharpDX.Color;
using Half = SharpDX.Half;
namespace CodeWalker
{
@ -86,24 +87,39 @@ namespace CodeWalker
public static string GetUTF8Text(byte[] bytes)
public static string GetUTF8Text(Span<byte> bytes)
{
if (bytes == null)
{ return string.Empty; } //file not found..
var start = 0;
var length = bytes.Length;
if (bytes == null || bytes.Length == 0)
{
return string.Empty;
} //file not found..
if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF))
{
start = 3;
length = bytes.Length - 3;
bytes = bytes.Slice(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)
{
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)
{
foreach(var searchString in strings)

View File

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

View File

@ -44,13 +44,13 @@ namespace CodeWalker.World
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());
Init(hash, gfc);
await InitAsync(hash, gfc);
Name = name;
}
public void Init(MetaHash pedhash, GameFileCache gfc)
public async ValueTask InitAsync(MetaHash pedhash, GameFileCache gfc)
{
Name = string.Empty;
@ -106,11 +106,11 @@ namespace CodeWalker.World
RpfFileEntry clothFile = null;
if (ClothFilesDict?.TryGetValue(pedhash, out clothFile) ?? false)
{
Yld = gfc.GetFileUncached<YldFile>(clothFile);
Yld = await gfc.GetFileUncachedAsync<YldFile>(clothFile);
while ((Yld != null) && (!Yld.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(Yld);
await Task.Delay(1);//kinda hacky
await gfc.TryLoadEnqueue(Yld);
}
}
@ -118,27 +118,27 @@ namespace CodeWalker.World
while ((Ydd != null) && (!Ydd.Loaded))
{
Thread.Sleep(1);//kinda hacky
await Task.Delay(1);//kinda hacky
Ydd = gfc.GetYdd(pedhash);
}
while ((Ytd != null) && (!Ytd.Loaded))
{
Thread.Sleep(1);//kinda hacky
await Task.Delay(1);//kinda hacky
Ytd = gfc.GetYtd(pedhash);
}
while ((Ycd != null) && (!Ycd.Loaded))
{
Thread.Sleep(1);//kinda hacky
await Task.Delay(1);//kinda hacky
Ycd = gfc.GetYcd(ycdhash);
}
while ((Yed != null) && (!Yed.Loaded))
{
Thread.Sleep(1);//kinda hacky
await Task.Delay(1);//kinda hacky
Yed = gfc.GetYed(yedhash);
}
while ((Yft != null) && (!Yft.Loaded))
{
Thread.Sleep(1);//kinda hacky
await Task.Delay(1);//kinda hacky
Yft = gfc.GetYft(pedhash);
}
@ -150,7 +150,7 @@ namespace CodeWalker.World
Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
AnimClip = cme;
var exprhash = JenkHash.GenHash(initdata.ExpressionName.ToLowerInvariant());
var exprhash = JenkHash.GenHashLower(initdata.ExpressionName);
Expression expr = null;
Yed?.ExprMap?.TryGetValue(exprhash, out 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))
{
@ -174,7 +174,7 @@ namespace CodeWalker.World
return;
}
MetaHash namehash = JenkHash.GenHash(name.ToLowerInvariant());
MetaHash namehash = JenkHash.GenHashLower(name);
Drawable d = null;
if (Ydd?.Dict != null)
{
@ -185,11 +185,11 @@ namespace CodeWalker.World
RpfFileEntry file = null;
if (DrawableFilesDict.TryGetValue(namehash, out file))
{
var ydd = gfc.GetFileUncached<YddFile>(file);
var ydd = await gfc.GetFileUncachedAsync<YddFile>(file);
while ((ydd != null) && (!ydd.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(ydd);
await Task.Delay(1);//kinda hacky
await gfc.TryLoadEnqueue(ydd);
}
if (ydd?.Drawables?.Length > 0)
{
@ -209,11 +209,11 @@ namespace CodeWalker.World
RpfFileEntry file = null;
if (TextureFilesDict.TryGetValue(texhash, out file))
{
var ytd = gfc.GetFileUncached<YtdFile>(file);
var ytd = await gfc.GetFileUncachedAsync<YtdFile>(file);
while ((ytd != null) && (!ytd.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(ytd);
await Task.Delay(1);//kinda hacky
await gfc.TryLoadEnqueue(ytd);
}
if (ytd?.TextureDict?.Textures?.data_items.Length > 0)
{
@ -232,11 +232,11 @@ namespace CodeWalker.World
RpfFileEntry file = null;
if (ClothFilesDict.TryGetValue(namehash, out file))
{
var yld = gfc.GetFileUncached<YldFile>(file);
var yld = await gfc.GetFileUncachedAsync<YldFile>(file);
while ((yld != null) && (!yld.Loaded))
{
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(yld);
await Task.Delay(1);//kinda hacky
await gfc.TryLoadEnqueue(yld);
}
if (yld?.ClothDictionary?.Clothes?.data_items?.Length > 0)
{
@ -266,7 +266,7 @@ namespace CodeWalker.World
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;
if (vi != null)
@ -279,17 +279,17 @@ namespace CodeWalker.World
{
var name = item?.GetDrawableName(alt);
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++)
{
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">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>CWPeds.ico</ApplicationIcon>
<Copyright>dexyfex</Copyright>
@ -10,18 +9,13 @@
<Authors>dexyfex</Authors>
<AssemblyName>CodeWalker Ped Viewer</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@ -1,17 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="xunit" Version="2.6.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@ -22,9 +17,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
</ItemGroup>
</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.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml.Linq;
using Xunit;
using Xunit.Abstractions;
namespace CodeWalker.Test
{
@ -122,6 +124,11 @@ namespace CodeWalker.Test
public class TestBinaryConversions
{
private readonly ITestOutputHelper _output;
public TestBinaryConversions(ITestOutputHelper testOutputHelper)
{
_output = testOutputHelper;
}
[Fact]
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">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>CWVehicles.ico</ApplicationIcon>
<Copyright>dexyfex</Copyright>
@ -11,18 +10,13 @@
<AssemblyName>CodeWalker Vehicle Viewer</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@ -249,22 +249,34 @@ namespace CodeWalker
ofd.InitialDirectory = fbd.SelectedPath;
int result = 0;
var ns = "System.Windows.Forms";
var asmb = Assembly.GetAssembly(typeof(OpenFileDialog));
var dialogint = GetType(asmb, ns, "FileDialogNative.IFileDialog");
var ns = "MS.Internal.AppModel";
var asmb = Assembly.Load("System.Windows.Forms.Primitives");
//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");
Call(typeof(OpenFileDialog), ofd, "OnBeforeVistaDialog", dialog);
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);
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 };
Call(dialogint, dialog, "Advise", parameters);
var adviseres = Convert.ToUInt32(parameters[1]);
try { result = Convert.ToInt32(Call(dialogint, dialog, "Show", hWndOwner)); }
finally { Call(dialogint, dialog, "Unadvise", adviseres); }
try {
result = Convert.ToInt32(Call(dialogint, dialog, "Show", hWndOwner));
}
finally {
//pfde.Dispose();
Call(dialogint, dialog, "Unadvise", adviseres);
}
GC.KeepAlive(pfde);
fbd.SelectedPath = ofd.FileName;
return (result == 0) ? DialogResult.OK : DialogResult.Cancel;
@ -296,12 +308,33 @@ namespace CodeWalker
if (mi == null) return null;
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)
{
var type = GetType(asmb, ns, typeName);
var fieldInfo = type.GetField(name);
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)
{
var type = GetType(asmb, ns, name);

View File

@ -1,4 +1,4 @@

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

View File

@ -151,27 +151,6 @@
<setting name="DLC" serializeAs="String">
<value />
</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">
<value>True</value>
</setting>
@ -250,6 +229,26 @@
<setting name="AntiAliasing" serializeAs="String">
<value>2</value>
</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>
</userSettings>
<runtime>

View File

@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net48</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ApplicationIcon>CW.ico</ApplicationIcon>
@ -10,22 +9,25 @@
<Company>dexyfex software</Company>
<Authors>dexyfex</Authors>
<LangVersion>latest</LangVersion>
<Nullable>annotations</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="DirectXTexNet" Version="1.0.1" />
<PackageReference Include="DirectXTexNet" Version="1.0.3" />
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.1.0" />
<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="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.D3DCompiler" 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.XAudio2" 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>
<ProjectReference Include="..\CodeWalker.Core\CodeWalker.Core.csproj" />
<ProjectReference Include="..\CodeWalker.WinForms\CodeWalker.WinForms.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms.DataVisualization" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
@ -52,12 +49,10 @@
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>PublicSettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

@ -218,7 +218,7 @@ namespace CodeWalker.Forms
return false;//what are we even doing here?
case MetaFormat.AudioRel:
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");
return false;

View File

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

View File

@ -11,18 +11,25 @@ namespace CodeWalker.GameFiles
public static class GameFileCacheFactory
{
public static GameFileCache _instance = null;
public static GameFileCache GetInstance()
public static GameFileCache? _instance;
public static GameFileCache Instance
{
if (_instance == null)
get
{
if (_instance is null)
{
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);
_instance = new GameFileCache(s.CacheSize, s.CacheTime, GTAFolder.CurrentGTAFolder, s.DLC, s.EnableMods, s.ExcludeFolders, Settings.Default.Key);
GTAFolder.OnGTAFolderChanged += _instance.SetGtaFolder;
}
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 pedhash = JenkHash.GenHash(pedname.ToLowerInvariant());
@ -765,7 +765,7 @@ namespace CodeWalker
DetailsPropertyGrid.SelectedObject = null;
SelectedPed.Init(pedname, GameFileCache);
await SelectedPed.InitAsync(pedname, GameFileCache);
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 name = comboItem?.DrawableName;
var tex = comboItem?.TextureName;
SelectedPed.SetComponentDrawable(index, name, tex, GameFileCache);
await SelectedPed.SetComponentDrawableAsync(index, name, tex, GameFileCache);
UpdateModelsUI();
}
@ -916,10 +916,15 @@ namespace CodeWalker
private void SelectClip(string name)
{
MetaHash cliphash = JenkHash.GenHash(name);
ClipMapEntry cme = null;
SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out cme);
if (SelectedPed.Ycd?.ClipMap?.TryGetValue(cliphash, out var cme) ?? false)
{
SelectedPed.AnimClip = cme;
}
else
{
SelectedPed.AnimClip = null;
}
}
@ -1209,7 +1214,7 @@ namespace CodeWalker
Input.KeyDown(e, enablemove);
var k = e.KeyCode;
var kb = Input.keyBindings;
var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed;
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;
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)

View File

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

View File

@ -8,9 +8,6 @@
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Windows.Forms;
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.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -929,5 +898,33 @@ namespace CodeWalker.Properties {
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">
<Value Profile="(Default)" />
</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">
<Value Profile="(Default)">True</Value>
</Setting>
@ -240,5 +221,24 @@
<Setting Name="AntiAliasing" Type="System.Int32" Scope="User">
<Value Profile="(Default)">2</Value>
</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>
</SettingsFile>

View File

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

View File

@ -14,15 +14,14 @@ namespace CodeWalker.Tools
{
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 Dictionary<string, RelData> NameComboLookup = new Dictionary<string, RelData>();
public AudioExplorerForm(GameFileCache gfc)
public AudioExplorerForm()
{
GameFileCache = gfc;
InitializeComponent();
LoadDropDowns();
}

View File

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

View File

@ -43,7 +43,9 @@ namespace CodeWalker.Tools
private void BrowseForm_Load(object sender, EventArgs e)
{
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)
{
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...
}
}
}
FolderTextBox.Text = GTAFolder.CurrentGTAFolder;
DataHexLineCombo.Text = "16";
@ -120,11 +124,7 @@ namespace CodeWalker.Tools
string[] allfiles = Directory.GetFiles(searchpath, "*.rpf", SearchOption.AllDirectories);
uint totrpfs = 0;
uint totfiles = 0;
uint totfolders = 0;
uint totresfiles = 0;
uint totbinfiles = 0;
var counts = new FileCounts();
foreach (string rpfpath in allfiles)
{
@ -139,22 +139,16 @@ namespace CodeWalker.Tools
UpdateStatus("Scanning " + rf.Name + "...");
rf.ScanStructure(UpdateStatus, UpdateStatus);
totrpfs += rf.GrandTotalRpfCount;
totfiles += rf.GrandTotalFileCount;
totfolders += rf.GrandTotalFolderCount;
totresfiles += rf.GrandTotalResourceCount;
totbinfiles += rf.GrandTotalBinaryFileCount;
counts += rf.ScanStructure(UpdateStatus, UpdateStatus);
AddScannedFile(rf, null, true);
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;
TotalFileCount = (int)totfiles;
TotalFileCount = (int)counts.Files;
});
}
@ -739,12 +733,11 @@ namespace CodeWalker.Tools
errcount++;
}
totbytes += file.ExtractedByteCount;
curfile++;
}
UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered, " + totbytes.ToString() + " total bytes extracted.");
UpdateStatus("Test complete. " + errcount.ToString() + " problems encountered");
InProgress = false;
});
}

View File

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

View File

@ -9,6 +9,7 @@ using CodeWalker.Properties;
using Microsoft.Win32;
using CodeWalker.GameFiles;
using CodeWalker.Utils;
using System.Diagnostics.CodeAnalysis;
namespace CodeWalker
{
@ -16,7 +17,7 @@ namespace CodeWalker
{
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
{
if (currentGTAFolder == value) return;
@ -24,9 +25,9 @@ namespace CodeWalker
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))
{
@ -49,7 +50,7 @@ namespace CodeWalker
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);
@ -60,11 +61,11 @@ namespace CodeWalker
return true;
}
string origFolder = CurrentGTAFolder;
string folder = CurrentGTAFolder;
var origFolder = CurrentGTAFolder;
var folder = CurrentGTAFolder;
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)
{
f.SelectedFolder = autoFolder;
@ -76,8 +77,7 @@ namespace CodeWalker
folder = f.SelectedFolder;
}
string failReason;
if(ValidateGTAFolder(folder, out failReason))
if (ValidateGTAFolder(folder, out string? failReason))
{
SetGTAFolder(folder);
if (folder != origFolder)
@ -85,13 +85,15 @@ namespace CodeWalker
MessageBox.Show($"Successfully changed GTA Folder to \"{folder}\"", "Set GTA Folder", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
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);
if (tryAgain == DialogResult.Retry)
{
return UpdateGTAFolder(false);
} else
}
else
{
return false;
}
@ -123,9 +125,9 @@ namespace CodeWalker
}
RegistryKey baseKey32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
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 oivPathValue = Registry.CurrentUser.OpenSubKey(@"Software\NewTechnologyStudio\OpenIV.exe\BrowseForFolder")?.GetValue("game_path_Five_pc") 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? oivPathValue = Registry.CurrentUser.OpenSubKey(@"Software\NewTechnologyStudio\OpenIV.exe\BrowseForFolder")?.GetValue("game_path_Five_pc") as string;
if(steamPathValue?.EndsWith("\\GTAV") == true)
{
@ -150,7 +152,7 @@ namespace CodeWalker
return matches.Count > 0;
}
public static string AutoDetectFolder(out string source)
public static string? AutoDetectFolder(out string? source)
{
source = null;
@ -164,7 +166,7 @@ namespace CodeWalker
return null;
}
public static string AutoDetectFolder() => AutoDetectFolder(out string _);
public static string? AutoDetectFolder() => AutoDetectFolder(out string? _);
public static void UpdateSettings()
{

View File

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

View File

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

View File

@ -18,7 +18,7 @@ namespace CodeWalker.World
public partial class CutsceneForm : Form
{
private WorldForm WorldForm;
private GameFileCache GameFileCache;
private GameFileCache GameFileCache => GameFileCacheFactory.Instance;
private AudioDatabase AudioDatabase;
private Cutscene Cutscene = null;
@ -43,7 +43,6 @@ namespace CodeWalker.World
public CutsceneForm(WorldForm worldForm)
{
WorldForm = worldForm;
GameFileCache = WorldForm.GameFileCache;
AudioDatabase = new AudioDatabase();
InitializeComponent();
}
@ -105,7 +104,7 @@ namespace CodeWalker.World
private void SelectCutscene(CutsceneDropdownItem dditem)
{
Cursor = Cursors.WaitCursor;
Task.Run(() =>
Task.Run(async () =>
{
CutFile cutFile = null;
Cutscene cutscene = null;
@ -125,7 +124,7 @@ namespace CodeWalker.World
GameFileCache.RpfMan.LoadFile(cutFile, entry);
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
{
public CutFile CutFile { get; set; } = null;
private GameFileCache GameFileCache = null;
private GameFileCache GameFileCache => GameFileCacheFactory.Instance;
private WorldForm WorldForm = 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;
GameFileCache = gfc;
WorldForm = wf;
AudioDB = adb;
@ -539,7 +537,7 @@ namespace CodeWalker.World
LoadYcds();
CreateSceneObjects();
await CreateSceneObjectsAsync();
RaiseEvents(0.0f);
}
@ -1164,9 +1162,9 @@ namespace CodeWalker.World
int drbl = args.iDrawable;
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>();
@ -1274,7 +1272,7 @@ namespace CodeWalker.World
foreach (var obj in Objects.Values)
{
var sobj = new CutsceneObject();
sobj.Init(obj, GameFileCache, AudioDB);
await sobj.InitAsync(obj, GameFileCache, AudioDB);
SceneObjects[sobj.ObjectID] = sobj;
if (sobj.AnimHash != 0)
@ -1320,7 +1318,7 @@ namespace CodeWalker.World
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;
ObjectID = obj?.iObjectId ?? -1;
@ -1345,7 +1343,7 @@ namespace CodeWalker.World
}
else if (obj is CutPedModelObject ped)
{
InitPed(ped, gfc);
await InitPedAsync(ped, gfc);
}
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.Init(ped.StreamingName, gfc);
Ped.LoadDefaultComponents(gfc);
await Ped.InitAsync(ped.StreamingName, gfc);
await Ped.LoadDefaultComponentsAsync(gfc);
//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 Renderer Renderer { get; set; }
public Renderer Renderer {
get
{
return renderer ??= new Renderer(this, GameFileCache);
}
set => renderer = value;
}
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
volatile bool formopen = false;
@ -36,23 +42,31 @@ namespace CodeWalker
volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch();
Space space = new Space();
Space space;
Camera camera;
Timecycle timecycle;
Weather weather;
Clouds clouds;
Water water = new Water();
Trains trains = new Trains();
Scenarios scenarios = new Scenarios();
PopZones popzones = new PopZones();
Heightmaps heightmaps = new Heightmaps();
Watermaps watermaps = new Watermaps();
AudioZones audiozones = new AudioZones();
private Water water;
Water Water { get => water ??= new Water(); }
private Trains trains;
Trains Trains { get => trains ??= new Trains(); }
private Scenarios scenarios;
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 CancellationToken CancellationToken;
public Space Space { get { return space; } }
public Space Space { get => space ??= new Space(); }
bool MouseLButtonDown = false;
bool MouseRButtonDown = false;
@ -70,8 +84,9 @@ namespace CodeWalker
Vector3 prevworldpos = new Vector3(0, 0, 100); //also the start pos
public GameFileCache GameFileCache { get => gameFileCache; }
GameFileCache gameFileCache = GameFileCacheFactory.GetInstance();
private GameFileCache gameFileCache;
public GameFileCache GameFileCache { get => gameFileCache ??= GameFileCacheFactory.GetInstance(); }
WorldControlMode ControlMode = WorldControlMode.Free;
@ -91,8 +106,11 @@ namespace CodeWalker
bool ControlBrushEnabled;
//float ControlBrushRadius;
Entity camEntity = new Entity();
PedEntity pedEntity = new PedEntity();
private Entity camEntity;
private PedEntity pedEntity;
Entity CamEntity { get => camEntity ??= new Entity(); }
PedEntity PedEntity { get => pedEntity ??= new PedEntity(); }
bool iseditmode = false;
@ -216,10 +234,17 @@ namespace CodeWalker
public WorldForm()
{
Task.Run(() => {
if (!GameFileCache.IsInited)
{
GameFileCache.Init();
}
});
CancellationToken = CancellationTokenSource.Token;
InitializeComponent();
Renderer = new Renderer(this, gameFileCache);
Renderer = new Renderer(this, GameFileCache);
camera = Renderer.camera;
timecycle = Renderer.timecycle;
weather = Renderer.weather;
@ -228,20 +253,15 @@ namespace CodeWalker
CurMouseHit.WorldForm = this;
LastMouseHit.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
initedOk = Renderer.Init();
if (!initedOk)
{
Close();
@ -365,11 +385,11 @@ namespace CodeWalker
}
}
camera.FollowEntity = camEntity;
camEntity.Position = (startupviewmode!=2) ? prevworldpos : Vector3.Zero;
camEntity.Orientation = Quaternion.LookAtLH(Vector3.Zero, Vector3.Up, Vector3.ForwardLH);
camera.FollowEntity = CamEntity;
CamEntity.Position = (startupviewmode!=2) ? prevworldpos : Vector3.Zero;
CamEntity.Orientation = Quaternion.LookAtLH(Vector3.Zero, Vector3.Up, Vector3.ForwardLH);
space.AddPersistentEntity(pedEntity);
Space.AddPersistentEntity(PedEntity);
LoadSettings();
@ -413,11 +433,10 @@ namespace CodeWalker
{
Renderer.BuffersResized(w, h);
if (WindowState == FormWindowState.Minimized && gameFileCache.IsInited)
if (WindowState == FormWindowState.Minimized && GameFileCache.IsInited)
{
Console.WriteLine("Clearing cache");
gameFileCache.Clear();
gameFileCache.IsInited = true;
GameFileCache.Clear();
//GC.Collect();
}
}
@ -445,7 +464,7 @@ namespace CodeWalker
{
UpdateControlInputs(elapsed);
space.Update(elapsed);
Space.Update(elapsed);
if (CutsceneForm != null)
{
@ -597,7 +616,7 @@ namespace CodeWalker
Vector3 movewvec = camera.ViewInvQuaternion.Multiply(movevec);
camEntity.Position += movewvec;
CamEntity.Position += movewvec;
MapViewDragX = 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
pedEntity.ControlMovement = movexy;
pedEntity.ControlJump = Input.kbjump || Input.ControllerButtonPressed(GamepadButtonFlags.X);
pedEntity.ControlBoost = Input.ShiftPressed || Input.ControllerButtonPressed(GamepadButtonFlags.A | GamepadButtonFlags.RightShoulder | GamepadButtonFlags.LeftShoulder);
PedEntity.ControlMovement = movexy;
PedEntity.ControlJump = Input.kbjump || Input.ControllerButtonPressed(GamepadButtonFlags.X);
PedEntity.ControlBoost = Input.ShiftPressed || Input.ControllerButtonPressed(GamepadButtonFlags.A | GamepadButtonFlags.RightShoulder | GamepadButtonFlags.LeftShoulder);
//Vector3 pedfwd = pedEntity.Orientation.Multiply(Vector3.UnitZ);
@ -725,9 +744,9 @@ namespace CodeWalker
if (renderworld)
{
space.GetVisibleYmaps(camera, hour, weathertype, renderworldVisibleYmapDict);
Space.GetVisibleYmaps(camera, hour, weathertype, renderworldVisibleYmapDict);
spaceEnts = space.TemporaryEntities;
spaceEnts = Space.TemporaryEntities;
}
if (ProjectForm != null)
@ -808,12 +827,12 @@ namespace CodeWalker
//enqueue collision meshes for rendering - from the world grid
collisionitems.Clear();
space.GetVisibleBounds(camera, collisionmeshrange, collisionmeshlayers, collisionitems);
Space.GetVisibleBounds(camera, collisionmeshrange, collisionmeshlayers, collisionitems);
collisionybns.Clear();
foreach (var item in collisionitems)
{
YbnFile ybn = gameFileCache.GetYbn(item.Name);
YbnFile ybn = GameFileCache.GetYbn(item.Name);
if ((ybn != null) && (ybn.Loaded))
{
collisionybns.Add(ybn);
@ -825,7 +844,7 @@ namespace CodeWalker
{
if (mlo.Archetype == null) return;
var hash = mlo.Archetype.Hash;
YbnFile ybn = gameFileCache.GetYbn(hash);
YbnFile ybn = GameFileCache.GetYbn(hash);
if ((ybn != null) && (ybn.Loaded))
{
collisioninteriors[mlo] = ybn;
@ -858,17 +877,17 @@ namespace CodeWalker
private void RenderWorldWaterQuads()
{
var quads = RenderWorldBaseWaterQuads(water.WaterQuads, MapSelectionMode.WaterQuad);
var quads = RenderWorldBaseWaterQuads(Water.WaterQuads, MapSelectionMode.WaterQuad);
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
{
List<T> renderwaterquadlist = water.GetVisibleQuads<T>(camera, quads);
List<T> renderwaterquadlist = Water.GetVisibleQuads<T>(camera, quads);
ProjectForm?.GetVisibleWaterQuads<T>(camera, renderwaterquadlist);
@ -881,7 +900,7 @@ namespace CodeWalker
{
renderpathynds.Clear();
space.GetVisibleYnds(camera, renderpathynds);
Space.GetVisibleYnds(camera, renderpathynds);
if (ProjectForm != null)
{
@ -895,10 +914,10 @@ namespace CodeWalker
private void RenderWorldTrainTracks()
{
if (!trains.Inited) return;
if (!Trains.Inited) return;
rendertraintracklist.Clear();
rendertraintracklist.AddRange(trains.TrainTracks);
rendertraintracklist.AddRange(Trains.TrainTracks);
if (ProjectForm != null)
{
@ -914,7 +933,7 @@ namespace CodeWalker
{
rendernavmeshynvs.Clear();
space.GetVisibleYnvs(camera, collisionmeshrange, rendernavmeshynvs);
Space.GetVisibleYnvs(camera, collisionmeshrange, rendernavmeshynvs);
if (ProjectForm != null)
{
@ -930,10 +949,10 @@ namespace CodeWalker
private void RenderWorldScenarios()
{
if (!scenarios.Inited) return;
if (!Scenarios.Inited) return;
renderscenariolist.Clear();
renderscenariolist.AddRange(scenarios.ScenarioRegions);
renderscenariolist.AddRange(Scenarios.ScenarioRegions);
if (ProjectForm != null)
{
@ -947,7 +966,7 @@ namespace CodeWalker
private void RenderWorldPopZones()
{
if (!popzones.Inited) return;
if (!PopZones.Inited) return;
//renderpopzonelist.Clear();
//renderpopzonelist.AddRange(popzones.Groups.Values);
@ -957,12 +976,12 @@ namespace CodeWalker
//ProjectForm.GetVisiblePopZones(camera, renderpopzonelist);
}
Renderer.RenderPopZones(popzones);
Renderer.RenderPopZones(PopZones);
}
private void RenderWorldHeightmaps()
{
if (!heightmaps.Inited) return;
if (!Heightmaps.Inited) return;
//renderheightmaplist.Clear();
//renderheightmaplist.AddRange(heightmaps.Heightmaps);
@ -972,12 +991,12 @@ namespace CodeWalker
//ProjectForm.GetVisibleHeightmaps(camera, renderheightmaplist);
}
Renderer.RenderBasePath(heightmaps);
Renderer.RenderBasePath(Heightmaps);
}
private void RenderWorldWatermaps()
{
if (!watermaps.Inited) return;
if (!Watermaps.Inited) return;
//renderwatermaplist.Clear();
//renderwatermaplist.AddRange(watermaps.Watermaps);
@ -987,12 +1006,12 @@ namespace CodeWalker
//ProjectForm.GetVisibleWatermaps(camera, renderwatermaplist);
}
Renderer.RenderBasePath(watermaps);
Renderer.RenderBasePath(Watermaps);
}
private void RenderWorldAudioZones()
{
if (!audiozones.Inited) return;
if (!AudioZones.Inited) return;
renderaudfilelist.Clear();
renderaudfilelist.AddRange(GameFileCache.AudioDatRelFiles);
@ -1003,7 +1022,7 @@ namespace CodeWalker
}
renderaudplacementslist.Clear();
audiozones.GetPlacements(renderaudfilelist, renderaudplacementslist);
AudioZones.GetPlacements(renderaudfilelist, renderaudplacementslist);
@ -1125,7 +1144,7 @@ namespace CodeWalker
{
hash = JenkHash.GenHash(modelname);
}
Archetype arche = gameFileCache.GetArchetype(hash);
Archetype arche = GameFileCache.GetArchetype(hash);
Archetype selarch = null;
DrawableBase seldrwbl = null;
@ -1139,7 +1158,7 @@ namespace CodeWalker
}
else
{
YmapFile ymap = gameFileCache.GetYmap(hash);
YmapFile ymap = GameFileCache.GetYmap(hash);
if (ymap != null)
{
Renderer.RenderYmap(ymap);
@ -1147,7 +1166,7 @@ namespace CodeWalker
else
{
//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.Loaded)
@ -1159,7 +1178,7 @@ namespace CodeWalker
}
else
{
YftFile yft = gameFileCache.GetYft(hash);
YftFile yft = GameFileCache.GetYft(hash);
if (yft != null)
{
if (yft.Loaded)
@ -1186,7 +1205,7 @@ namespace CodeWalker
if ((selarch != null) && (seldrwbl == null))
{
seldrwbl = gameFileCache.TryGetDrawable(selarch);
seldrwbl = GameFileCache.TryGetDrawable(selarch);
}
//select this item for viewing by the UI...
@ -1209,7 +1228,7 @@ namespace CodeWalker
foreach (string lod in ymaplist)
{
uint hash = JenkHash.GenHash(lod);
YmapFile ymap = gameFileCache.GetYmap(hash);
YmapFile ymap = GameFileCache.GetYmap(hash);
Renderer.RenderYmap(ymap);
UpdateMouseHits(ymap);
@ -1763,13 +1782,13 @@ namespace CodeWalker
float downlimit = 20.0f;
Ray ray = new Ray(p, new Vector3(0, 0, -1.0f));
ray.Position.Z += 0.1f;
SpaceRayIntersectResult hit = space.RayIntersect(ray, downlimit);
SpaceRayIntersectResult hit = Space.RayIntersect(ray, downlimit);
if (hit.Hit)
{
return hit.Position;
}
ray.Position.Z += uplimit;
hit = space.RayIntersect(ray, downlimit);
hit = Space.RayIntersect(ray, downlimit);
if (hit.Hit)
{
return hit.Position;
@ -1928,12 +1947,12 @@ namespace CodeWalker
ynd.UpdateAllNodePositions();
ynd.BuildBVH();
space.BuildYndData(ynd);
Space.BuildYndData(ynd);
}
else
{
ynd.UpdateAllNodePositions();
space.BuildYndVerts(ynd, selection);
Space.BuildYndVerts(ynd, selection);
}
//lock (Renderer.RenderSyncRoot)
{
@ -1948,7 +1967,7 @@ namespace CodeWalker
}
public YndNode GetPathNodeFromSpace(ushort areaid, ushort nodeid)
{
return space.NodeGrid.GetYndNode(areaid, nodeid);
return Space.NodeGrid.GetYndNode(areaid, nodeid);
}
public void UpdateCollisionBoundsGraphics(Bounds b)
@ -2080,7 +2099,7 @@ namespace CodeWalker
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)
{
Input.keyBindings = kb.Copy();
Input.KeyBindings = kb.Copy();
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);
ToolbarMoveButton.ToolTipText = string.Format("Move ({0})", kb.EditPosition);
ToolbarRotateButton.ToolTipText = string.Format("Rotate ({0})", kb.EditRotation);
@ -2246,7 +2265,7 @@ namespace CodeWalker
private void SpawnTestEntity(bool cameraCenter = false)
{
if (!space.Inited) return;
if (!Space.Inited) return;
Vector3 dir = (cameraCenter ? camera.ViewDirection : camera.MouseRay.Direction);
Vector3 ofs = (cameraCenter ? Vector3.Zero : camera.MouseRay.Position);
@ -2290,7 +2309,7 @@ namespace CodeWalker
lock (Renderer.RenderSyncRoot)
{
space.AddTemporaryEntity(e);
Space.AddTemporaryEntity(e);
}
}
@ -2318,13 +2337,13 @@ namespace CodeWalker
if (isfree && !wasfree)
{
camEntity.Position = pedEntity.Position;
CamEntity.Position = PedEntity.Position;
pedEntity.Enabled = false;
PedEntity.Enabled = false;
Renderer.timerunning = false;
camera.SetFollowEntity(camEntity);
camera.SetFollowEntity(CamEntity);
camera.TargetDistance = 1.0f; //default?
camera.Smoothness = Settings.Default.CameraSmoothing;
@ -2332,13 +2351,13 @@ namespace CodeWalker
}
else if (!isfree && wasfree)
{
pedEntity.Position = camEntity.Position;
pedEntity.Velocity = Vector3.Zero;
pedEntity.Enabled = true;
PedEntity.Position = CamEntity.Position;
PedEntity.Velocity = Vector3.Zero;
PedEntity.Enabled = true;
Renderer.timerunning = true;
camera.SetFollowEntity(pedEntity.CameraEntity);
camera.SetFollowEntity(PedEntity.CameraEntity);
camera.TargetDistance = 0.01f; //1cm
camera.Smoothness = 20.0f;
@ -2403,19 +2422,19 @@ namespace CodeWalker
public SpaceRayIntersectResult GetSpaceMouseRay()
{
SpaceRayIntersectResult ret = new SpaceRayIntersectResult();
if (space.Inited && space.BoundsStore != null)
if (Space.Inited && Space.BoundsStore != null)
{
Ray mray = new Ray();
mray.Position = camera.MouseRay.Position + camera.Position;
mray.Direction = camera.MouseRay.Direction;
return space.RayIntersect(mray, float.MaxValue, collisionmeshlayers);
return Space.RayIntersect(mray, float.MaxValue, collisionmeshlayers);
}
return ret;
}
public SpaceRayIntersectResult Raycast(Ray ray)
{
return space.RayIntersect(ray, float.MaxValue, collisionmeshlayers);
return Space.RayIntersect(ray, float.MaxValue, collisionmeshlayers);
}
private void UpdateMouseHits()
@ -3503,7 +3522,7 @@ namespace CodeWalker
}
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;
@ -4212,43 +4231,43 @@ namespace CodeWalker
{
UpdateStatus("Loading timecycles...");
timecycle.Init(gameFileCache, UpdateStatus);
timecycle.Init(GameFileCache, UpdateStatus);
timecycle.SetTime(Renderer.timeofday);
UpdateStatus("Loading materials...");
BoundsMaterialTypes.Init(gameFileCache);
BoundsMaterialTypes.Init(GameFileCache);
UpdateStatus("Loading weather...");
weather.Init(gameFileCache, UpdateStatus, timecycle);
weather.Init(GameFileCache, UpdateStatus, timecycle);
UpdateWeatherTypesComboBox(weather);
UpdateStatus("Loading clouds...");
clouds.Init(gameFileCache, UpdateStatus, weather);
clouds.Init(GameFileCache, UpdateStatus, weather);
UpdateCloudTypesComboBox(clouds);
UpdateStatus("Loading water...");
water.Init(gameFileCache, UpdateStatus);
Water.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading trains...");
trains.Init(gameFileCache, UpdateStatus);
Trains.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading scenarios...");
scenarios.Init(gameFileCache, UpdateStatus, timecycle);
Scenarios.Init(GameFileCache, UpdateStatus, timecycle);
UpdateStatus("Loading popzones...");
popzones.Init(gameFileCache, UpdateStatus);
PopZones.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading heightmaps...");
heightmaps.Init(gameFileCache, UpdateStatus);
Heightmaps.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading watermaps...");
watermaps.Init(gameFileCache, UpdateStatus);
Watermaps.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading audio zones...");
audiozones.Init(gameFileCache, UpdateStatus);
AudioZones.Init(GameFileCache, UpdateStatus);
UpdateStatus("Loading world...");
space.Init(gameFileCache, UpdateStatus);
Space.Init(GameFileCache, UpdateStatus);
UpdateStatus("World loaded");
@ -4264,7 +4283,7 @@ namespace CodeWalker
{
lock (Renderer.RenderSyncRoot)
{
if (gameFileCache.SetDlcLevel(dlc, enable))
if (GameFileCache.SetDlcLevel(dlc, enable))
{
LoadWorld();
}
@ -4283,9 +4302,9 @@ namespace CodeWalker
{
lock (Renderer.RenderSyncRoot)
{
if (gameFileCache.SetModsEnabled(enable))
if (GameFileCache.SetModsEnabled(enable))
{
UpdateDlcListComboBox(gameFileCache.DlcNameList);
UpdateDlcListComboBox(GameFileCache.DlcNameList);
LoadWorld();
}
@ -4326,18 +4345,18 @@ namespace CodeWalker
return;
}
gameFileCache.UpdateStatus += UpdateStatus;
gameFileCache.ErrorLog += LogError;
while (gameFileCache.IsIniting)
GameFileCache.UpdateStatus += UpdateStatus;
GameFileCache.ErrorLog += LogError;
while (GameFileCache.IsIniting)
{
await Task.Delay(0);
}
if (!gameFileCache.IsInited)
if (!GameFileCache.IsInited)
{
gameFileCache.Init();
GameFileCache.Init();
}
UpdateDlcListComboBox(gameFileCache.DlcNameList);
UpdateDlcListComboBox(GameFileCache.DlcNameList);
EnableCacheDependentUI();
@ -4351,7 +4370,7 @@ namespace CodeWalker
EnableDLCModsUI();
Task.Run(async () =>
_ = Task.Run(async () =>
{
try
{
@ -4373,13 +4392,13 @@ namespace CodeWalker
Console.WriteLine("Renderer ContentThread stopped");
});
Task.Run(async () =>
_ = Task.Run(async () =>
{
try
{
while (formopen && !IsDisposed && !CancellationToken.IsCancellationRequested) //main asset loop
{
bool fcItemsPending = gameFileCache.ContentThreadProc();
bool fcItemsPending = GameFileCache.ContentThreadProc();
if (!fcItemsPending)
{
@ -4413,6 +4432,8 @@ namespace CodeWalker
private Stopwatch lastStatusUpdate = Stopwatch.StartNew();
private TimeSpan updateInterval = TimeSpan.FromSeconds(0.05);
private Renderer renderer;
private void UpdateStatus(string text)
{
try
@ -4527,13 +4548,13 @@ namespace CodeWalker
{
DlcLevelComboBox.Items.Add(dlcname);
}
if (string.IsNullOrEmpty(gameFileCache.SelectedDlc))
if (string.IsNullOrEmpty(GameFileCache.SelectedDlc))
{
DlcLevelComboBox.SelectedIndex = dlcnames.Count - 1;
}
else
{
int idx = DlcLevelComboBox.FindString(gameFileCache.SelectedDlc);
int idx = DlcLevelComboBox.FindString(GameFileCache.SelectedDlc);
DlcLevelComboBox.SelectedIndex = (idx > 0) ? idx : (dlcnames.Count - 1);
}
}
@ -4806,7 +4827,7 @@ namespace CodeWalker
EnableModsCheckBox.Checked = s.EnableMods;
DlcLevelComboBox.Text = s.DLC;
gameFileCache.SelectedDlc = s.DLC;
GameFileCache.SelectedDlc = s.DLC;
EnableDlcCheckBox.Checked = !string.IsNullOrEmpty(s.DLC);
}
private void SaveSettings()
@ -4849,8 +4870,8 @@ namespace CodeWalker
s.Clouds = CloudsComboBox.Text;
//additional settings from gamefilecache...
s.EnableMods = gameFileCache.EnableMods;
s.DLC = gameFileCache.EnableDlc ? gameFileCache.SelectedDlc : "";
s.EnableMods = GameFileCache.EnableMods;
s.DLC = GameFileCache.EnableDlc ? GameFileCache.SelectedDlc : "";
s.Save();
}
@ -6045,9 +6066,10 @@ namespace CodeWalker
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)
@ -6379,7 +6401,7 @@ namespace CodeWalker
Input.KeyDown(e, enablemove);
var k = e.KeyCode;
var kb = Input.keyBindings;
var kb = Input.KeyBindings;
bool ctrl = Input.CtrlPressed;
bool shift = Input.ShiftPressed;
@ -6977,7 +6999,7 @@ namespace CodeWalker
private void ToolsMenuAudioExplorer_Click(object sender, EventArgs e)
{
AudioExplorerForm f = new AudioExplorerForm(gameFileCache);
AudioExplorerForm f = new AudioExplorerForm();
f.Show(this);
}
@ -6988,7 +7010,7 @@ namespace CodeWalker
private void ToolsMenuBinarySearch_Click(object sender, EventArgs e)
{
BinarySearchForm f = new BinarySearchForm(gameFileCache);
BinarySearchForm f = new BinarySearchForm();
f.Show(this);
}
@ -7000,7 +7022,7 @@ namespace CodeWalker
private void ToolsMenuJenkInd_Click(object sender, EventArgs e)
{
JenkIndForm f = new JenkIndForm(gameFileCache);
JenkIndForm f = new JenkIndForm();
f.Show();
}
@ -7934,6 +7956,10 @@ namespace CodeWalker
private void AntiAliasingTrackBar_ValueChanged(object sender, EventArgs e)
{
if (AntiAliasingTrackBar.Value == Settings.Default.AntiAliasing)
{
return;
}
Settings.Default.AntiAliasing = AntiAliasingTrackBar.Value;
Settings.Default.Save();
AntiAliasingValue.Text = Settings.Default.AntiAliasing.ToString();