Performance optimalizations and refactoring

This commit is contained in:
Niek Schoemaker 2023-10-27 21:31:09 +02:00
parent ee975e7257
commit aed7a1e051
No known key found for this signature in database
GPG Key ID: BDF9404CFECB0006
203 changed files with 5562 additions and 4128 deletions

1
.gitignore vendored
View File

@ -241,3 +241,4 @@ ModelManifest.xml
# FAKE - F# Make # FAKE - F# Make
.fake/ .fake/
/Toolbar/cammodes.psd /Toolbar/cammodes.psd
Shaders

View File

@ -1,12 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="directxtex_desktop_win10" Version="2023.3.30.1" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1917,7 +1917,7 @@ namespace CodeWalker.GameFiles
Buffer.BlockCopy(data, 16, newdata, 0, newlen); Buffer.BlockCopy(data, 16, newdata, 0, newlen);
data = newdata; data = newdata;
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
ClipDict = rd.ReadBlock<ClipDictionary>(); ClipDict = rd.ReadBlock<ClipDictionary>();

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
@ -171,70 +172,45 @@ namespace CodeWalker.GameFiles
public static class GlobalText public static class GlobalText
{ {
public static Dictionary<uint, string> Index = new Dictionary<uint, string>(); public static ConcurrentDictionary<uint, string> Index = new ();
private static object syncRoot = new object(); private static object syncRoot = new object();
public static volatile bool FullIndexBuilt = false; public static volatile bool FullIndexBuilt = false;
public static void Clear() public static void Clear()
{
lock (syncRoot)
{ {
Index.Clear(); Index.Clear();
} }
}
public static bool Ensure(string str) public static bool Ensure(string str)
{ {
uint hash = JenkHash.GenHash(str); uint hash = JenkHash.GenHash(str);
if (hash == 0) return true; if (hash == 0) return true;
lock (syncRoot) return !Index.TryAdd(hash, str);
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
}
}
return true;
} }
public static bool Ensure(string str, uint hash) public static bool Ensure(string str, uint hash)
{ {
if (hash == 0) return true; if (hash == 0) return true;
lock (syncRoot) return !Index.TryAdd(hash, str);
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
}
}
return true;
} }
public static string GetString(uint hash) public static string GetString(uint hash)
{ {
string res; string res;
lock (syncRoot)
{
if (!Index.TryGetValue(hash, out res)) if (!Index.TryGetValue(hash, out res))
{ {
res = hash.ToString(); res = hash.ToString();
} }
}
return res; return res;
} }
public static string TryGetString(uint hash) public static string TryGetString(uint hash)
{ {
string res; string res;
lock (syncRoot)
{
if (!Index.TryGetValue(hash, out res)) if (!Index.TryGetValue(hash, out res))
{ {
res = string.Empty; res = string.Empty;
} }
}
return res; return res;
} }

View File

@ -48,7 +48,7 @@ namespace CodeWalker.GameFiles
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>(); Meta = rd.ReadBlock<Meta>();

View File

@ -51,7 +51,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Bounds = rd.ReadBlock<Bounds>(); Bounds = rd.ReadBlock<Bounds>();

View File

@ -44,20 +44,20 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = null;
try try
{ {
rd = new ResourceDataReader(resentry, data); using ResourceDataReader rd = new ResourceDataReader(resentry, data);
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
InitDictionaries();
} }
catch (Exception ex) catch (Exception ex)
{ {
//data = entry.File.DecompressBytes(data); //?? //data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString(); LoadException = ex.ToString();
} }
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
InitDictionaries();
} }
public void InitDictionaries() public void InitDictionaries()

View File

@ -43,7 +43,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
DrawableDict = rd.ReadBlock<DrawableDictionary>(); DrawableDict = rd.ReadBlock<DrawableDictionary>();
@ -74,6 +74,7 @@ namespace CodeWalker.GameFiles
{ {
var drawable = drawables[i]; var drawable = drawables[i];
var hash = hashes[i]; var hash = hashes[i];
drawable.Hash = hash;
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd"))) if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
{ {
string hstr = JenkIndex.TryGetString(hash); string hstr = JenkIndex.TryGetString(hash);
@ -101,6 +102,12 @@ namespace CodeWalker.GameFiles
return data; return data;
} }
new public long MemoryUsage {
get {
return DrawableDict.MemoryUsage;
}
}
} }

View File

@ -39,7 +39,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0; //MemoryUsage = 0;
@ -58,6 +58,11 @@ namespace CodeWalker.GameFiles
} }
public void Unload()
{
}
public byte[] Save() public byte[] Save()
{ {
byte[] data = ResourceBuilder.Build(Drawable, 165); //ydr is type/version 165... byte[] data = ResourceBuilder.Build(Drawable, 165); //ydr is type/version 165...
@ -65,7 +70,13 @@ namespace CodeWalker.GameFiles
return data; return data;
} }
new public long MemoryUsage
{
get
{
return Drawable.MemoryUsage;
}
}
} }

View File

@ -41,21 +41,20 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = null;
try try
{ {
rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
ExpressionDictionary = rd?.ReadBlock<ExpressionDictionary>();
InitDictionaries();
} }
catch (Exception ex) catch (Exception ex)
{ {
//data = entry.File.DecompressBytes(data); //?? //data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString(); LoadException = ex.ToString();
} }
ExpressionDictionary = rd?.ReadBlock<ExpressionDictionary>();
InitDictionaries();
} }
public byte[] Save() public byte[] Save()

View File

@ -36,19 +36,17 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = null;
try try
{ {
rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
FrameFilterDictionary = rd.ReadBlock<FrameFilterDictionary>();
} }
catch (Exception ex) catch (Exception ex)
{ {
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString(); LoadException = ex.ToString();
} }
FrameFilterDictionary = rd?.ReadBlock<FrameFilterDictionary>();
} }
public byte[] Save() public byte[] Save()

View File

@ -40,7 +40,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Fragment = rd.ReadBlock<FragType>(); Fragment = rd.ReadBlock<FragType>();

View File

@ -40,20 +40,13 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = null;
try try
{ {
rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ClothDictionary = rd?.ReadBlock<ClothDictionary>(); ClothDictionary = rd?.ReadBlock<ClothDictionary>();
if (ClothDictionary != null) if (ClothDictionary != null)
{ {
Dict = new Dictionary<uint, CharacterCloth>(); Dict = new Dictionary<uint, CharacterCloth>();
@ -71,6 +64,12 @@ namespace CodeWalker.GameFiles
Loaded = true; Loaded = true;
} }
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
}
public byte[] Save() public byte[] Save()
{ {

View File

@ -96,7 +96,7 @@ namespace CodeWalker.GameFiles
return; return;
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>();//maybe null this after load to reduce memory consumption? Meta = rd.ReadBlock<Meta>();//maybe null this after load to reduce memory consumption?
@ -1336,15 +1336,11 @@ namespace CodeWalker.GameFiles
{ {
var newnamel = newname.ToLowerInvariant(); var newnamel = newname.ToLowerInvariant();
var newnamex = newname + ".ymap"; var newnamex = newname + ".ymap";
var newnamexl = newname.ToLowerInvariant();
var newhash = JenkHash.GenHash(newnamel); var newhash = JenkHash.GenHash(newnamel);
JenkIndex.Ensure(newnamel); JenkIndex.Ensure(newnamel);
if (RpfFileEntry != null) if (RpfFileEntry != null)
{ {
RpfFileEntry.Name = newnamex; RpfFileEntry.Name = newnamex;
RpfFileEntry.NameLower = newnamexl;
RpfFileEntry.NameHash = JenkHash.GenHash(newnamexl);
RpfFileEntry.ShortNameHash = newhash;
} }
Name = newnamex; Name = newnamex;
_CMapData.name = newhash; _CMapData.name = newhash;
@ -3034,6 +3030,8 @@ namespace CodeWalker.GameFiles
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public bool Visible { get; set; } = true;
public void Init(YmapLODLights l, YmapDistantLODLights p, int i) public void Init(YmapLODLights l, YmapDistantLODLights p, int i)
{ {
LodLights = l; LodLights = l;
@ -3306,7 +3304,7 @@ namespace CodeWalker.GameFiles
var vertexCount = indicesOffset / 12; var vertexCount = indicesOffset / 12;
var indexCount = (int)(dataSize - indicesOffset);// / 4; var indexCount = (int)(dataSize - indicesOffset);// / 4;
Data = MetaTypes.GetByteArray(meta, vptr, dataSize); Data = MetaTypes.GetByteArray(meta, vptr, dataSize);
Vertices = MetaTypes.ConvertDataArray<Vector3>(Data, 0, vertexCount); Vertices = MetaTypes.ConvertDataArray<Vector3>(Data, 0, vertexCount).ToArray();
Indices = new byte[indexCount]; Indices = new byte[indexCount];
Buffer.BlockCopy(Data, indicesOffset, Indices, 0, indexCount); Buffer.BlockCopy(Data, indicesOffset, Indices, 0, indexCount);
BuildTriangles(); BuildTriangles();

View File

@ -67,14 +67,10 @@ namespace CodeWalker.GameFiles
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>(); Meta = rd.ReadBlock<Meta>();
} }
@ -90,13 +86,13 @@ namespace CodeWalker.GameFiles
MapDataGroups = PsoTypes.GetObjectArray<YmfMapDataGroup, CMapDataGroup>(Pso, d.MapDataGroups); MapDataGroups = PsoTypes.GetObjectArray<YmfMapDataGroup, CMapDataGroup>(Pso, d.MapDataGroups);
imapDependencies = PsoTypes.GetItemArray<CImapDependency>(Pso, d.imapDependencies); imapDependencies = PsoTypes.GetItemArray<CImapDependency>(Pso, d.imapDependencies).ToArray();
imapDependencies2 = PsoTypes.GetObjectArray<YmfImapDependency2, CImapDependencies>(Pso, d.imapDependencies_2); imapDependencies2 = PsoTypes.GetObjectArray<YmfImapDependency2, CImapDependencies>(Pso, d.imapDependencies_2);
itypDependencies2 = PsoTypes.GetObjectArray<YmfItypDependency2, CItypDependencies>(Pso, d.itypDependencies_2); itypDependencies2 = PsoTypes.GetObjectArray<YmfItypDependency2, CItypDependencies>(Pso, d.itypDependencies_2);
HDTxdAssetBindings = PsoTypes.GetItemArray<CHDTxdAssetBinding>(Pso, d.HDTxdBindingArray); HDTxdAssetBindings = PsoTypes.GetItemArray<CHDTxdAssetBinding>(Pso, d.HDTxdBindingArray).ToArray();
Interiors = PsoTypes.GetObjectArray<YmfInterior, CInteriorBoundsFiles>(Pso, d.Interiors); Interiors = PsoTypes.GetObjectArray<YmfInterior, CInteriorBoundsFiles>(Pso, d.Interiors);

View File

@ -62,7 +62,7 @@ namespace CodeWalker.GameFiles
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry; RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry != null) if (resentry != null)
{ {
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>(); Meta = rd.ReadBlock<Meta>();

View File

@ -84,7 +84,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
NodeDictionary = rd.ReadBlock<NodeDictionary>(); NodeDictionary = rd.ReadBlock<NodeDictionary>();
@ -95,8 +95,6 @@ namespace CodeWalker.GameFiles
//links will be populated by the space... maybe move that code here? //links will be populated by the space... maybe move that code here?
string areaidstr = Name.ToLowerInvariant().Replace("nodes", "").Replace(".ynd", ""); string areaidstr = Name.ToLowerInvariant().Replace("nodes", "").Replace(".ynd", "");
int areaid = 0; int areaid = 0;
int.TryParse(areaidstr, out areaid); int.TryParse(areaidstr, out areaid);

View File

@ -90,7 +90,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Nav = rd.ReadBlock<NavMesh>(); Nav = rd.ReadBlock<NavMesh>();

View File

@ -44,7 +44,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0; //MemoryUsage = 0;

View File

@ -31,6 +31,7 @@ namespace CodeWalker.GameFiles
Loaded = true; Loaded = true;
} }
public void Load(byte[] data, RpfFileEntry entry) public void Load(byte[] data, RpfFileEntry entry)
{ {
Name = entry.Name; Name = entry.Name;
@ -43,7 +44,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
TextureDict = rd.ReadBlock<TextureDictionary>(); TextureDict = rd.ReadBlock<TextureDictionary>();
@ -64,7 +65,13 @@ namespace CodeWalker.GameFiles
return data; return data;
} }
new public long MemoryUsage
{
get
{
return TextureDict.MemoryUsage;
}
}
} }

View File

@ -208,7 +208,7 @@ namespace CodeWalker.GameFiles
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
Meta = rd.ReadBlock<Meta>(); Meta = rd.ReadBlock<Meta>();

View File

@ -31,7 +31,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0; //MemoryUsage = 0;

View File

@ -31,7 +31,7 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)"); throw new Exception("File entry wasn't a resource! (is it binary data?)");
} }
ResourceDataReader rd = new ResourceDataReader(resentry, data); using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0; //MemoryUsage = 0;

View File

@ -101,6 +101,28 @@ namespace CodeWalker.GameFiles
Hash = hash; Hash = hash;
Type = type; Type = type;
} }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is not GameFileCacheKey gameFileCacheKey) return false;
return gameFileCacheKey.Hash == Hash && gameFileCacheKey.Type == Type;
}
public static bool operator ==(GameFileCacheKey first, GameFileCacheKey second)
{
return first.Equals(second);
}
public static bool operator !=(GameFileCacheKey first, GameFileCacheKey second)
{
return !first.Equals(second);
}
public override int GetHashCode()
{
return (int)Hash;
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -88,9 +88,25 @@ namespace CodeWalker.GameFiles
public uint TimeFlags { get; set; } public uint TimeFlags { get; set; }
public bool[] ActiveHours { get; set; } public bool[] ActiveHours { get; set; }
public string[] ActiveHoursText { get; set; } private readonly Lazy<string[]> _activeHoursText;
public string[] ActiveHoursText { get => _activeHoursText.Value; }
public bool ExtraFlag { get { return ((TimeFlags >> 24) & 1) == 1; } } public bool ExtraFlag { get { return ((TimeFlags >> 24) & 1) == 1; } }
public TimeArchetype()
{
_activeHoursText = new Lazy<string[]>(() =>
{
var activeHoursText = new string[24];
for (int i = 0; i < ActiveHours.Length; i++)
{
var nxth = (i < 23) ? (i + 1) : 0;
var hrs = string.Format("{0:00}:00 - {1:00}:00", i, nxth);
activeHoursText[i] = (hrs + (ActiveHours[i] ? " - On" : " - Off"));
}
return activeHoursText;
});
}
public void Init(YtypFile ytyp, ref CTimeArchetypeDef arch) public void Init(YtypFile ytyp, ref CTimeArchetypeDef arch)
{ {
@ -117,16 +133,11 @@ namespace CodeWalker.GameFiles
if (ActiveHours == null) if (ActiveHours == null)
{ {
ActiveHours = new bool[24]; ActiveHours = new bool[24];
ActiveHoursText = new string[24];
} }
for (int i = 0; i < 24; i++) for (int i = 0; i < 24; i++)
{ {
bool v = ((TimeFlags >> i) & 1) == 1; bool v = ((TimeFlags >> i) & 1) == 1;
ActiveHours[i] = v; ActiveHours[i] = v;
int nxth = (i < 23) ? (i + 1) : 0;
string hrs = string.Format("{0:00}:00 - {1:00}:00", i, nxth);
ActiveHoursText[i] = (hrs + (v ? " - On" : " - Off"));
} }
} }

View File

@ -1179,6 +1179,29 @@ namespace CodeWalker.GameFiles
{ {
return new MetaHash(v); return new MetaHash(v);
} }
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is not MetaHash metaHash) return false;
return metaHash.Hash == Hash;
}
public override int GetHashCode()
{
return (int)Hash;
}
public static bool operator ==(MetaHash a, MetaHash b)
{
return a.Equals(b);
}
public static bool operator !=(MetaHash a, MetaHash b)
{
return !a.Equals(b);
}
} }

View File

@ -10,14 +10,21 @@ namespace CodeWalker.GameFiles
public static class MetaNames public static class MetaNames
{ {
public static Dictionary<uint, string> stringCache = new Dictionary<uint, string>();
public static bool TryGetString(uint h, out string str) public static bool TryGetString(uint h, out string str)
{ {
if (stringCache.TryGetValue(h, out str))
{
return str != null;
}
if (Enum.IsDefined(typeof(MetaName), h)) if (Enum.IsDefined(typeof(MetaName), h))
{ {
str = ((MetaName)h).ToString(); str = ((MetaName)h).ToString();
if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry
stringCache.Add(h, str);
return true; return true;
} }
stringCache.Add(h, str);
str = null; str = null;
return false; return false;
} }

View File

@ -10,6 +10,7 @@ using System.Xml;
using TC = System.ComponentModel.TypeConverterAttribute; using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter; using EXP = System.ComponentModel.ExpandableObjectConverter;
using CodeWalker.World; using CodeWalker.World;
using System.Reflection;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -1479,22 +1480,29 @@ namespace CodeWalker.GameFiles
public static T ConvertData<T>(byte[] data) where T : struct public static T ConvertData<T>(byte[] data) where T : struct
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); MemoryMarshal.TryRead<T>(data.AsSpan(), out T value);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h); return value;
handle.Free(); //GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
return r; //var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h);
//handle.Free();
//return r;
} }
public static T ConvertData<T>(byte[] data, int offset) where T : struct public static T ConvertData<T>(byte[] data, int offset) where T : struct
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset); return value;
handle.Free(); //GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
return r; //var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//return r;
} }
public static T[] ConvertDataArray<T>(byte[] data, int offset, int count) where T : struct public static Span<T> ConvertDataArray<T>(byte[] data, int offset, int count) where T : struct
{ {
return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T))));
T[] items = new T[count]; T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T)); int itemsize = Marshal.SizeOf(typeof(T));
//for (int i = 0; i < count; i++) //for (int i = 0; i < count; i++)
@ -1520,8 +1528,6 @@ namespace CodeWalker.GameFiles
{ return null; } { return null; }
T[] items = new T[count]; T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T));
int itemsleft = (int)count; //large arrays get split into chunks...
//MetaName blocktype = 0; //MetaName blocktype = 0;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
@ -1572,13 +1578,18 @@ namespace CodeWalker.GameFiles
int blockcount = ptrblock.DataLength / itemsize; int blockcount = ptrblock.DataLength / itemsize;
int itemcount = blockcount - itemoffset; int itemcount = blockcount - itemoffset;
if (itemcount > itemsleft) if (itemcount > itemsleft)
{ itemcount = itemsleft; } //don't try to read too many items..
for (int i = 0; i < itemcount; i++)
{ {
int offset = (itemoffset + i) * itemsize; itemcount = itemsleft;
int index = curi + i; } //don't try to read too many items..
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.. itemoffset = 0; //start at beginning of next block..
curi += itemcount; curi += itemcount;
itemsleft -= itemcount; itemsleft -= itemcount;

View File

@ -1423,7 +1423,7 @@ namespace CodeWalker.GameFiles
arrEnum.SwapEnd(); arrEnum.SwapEnd();
var enumArr = PsoTypes.GetUintArray(cont.Pso, arrEnum); var enumArr = PsoTypes.GetUintArray(cont.Pso, arrEnum);
var enumDef = cont.GetEnumInfo((MetaName)arrEntry.ReferenceKey); var enumDef = cont.GetEnumInfo((MetaName)arrEntry.ReferenceKey);
WriteItemArray(sb, enumArr, indent, ename, "enum", (ie)=> { WriteItemArray<uint>(sb, enumArr, indent, ename, "enum", (ie)=> {
var eval = enumDef?.FindEntry((int)ie); var eval = enumDef?.FindEntry((int)ie);
return HashString(eval?.EntryNameHash ?? 0); return HashString(eval?.EntryNameHash ?? 0);
}); });
@ -1945,9 +1945,10 @@ namespace CodeWalker.GameFiles
if (lastcol || lastn) sb.AppendLine(); if (lastcol || lastn) sb.AppendLine();
} }
} }
public static void WriteRawArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter = null, int arrRowSize = 10) where T : struct
public static void WriteRawArray<T>(StringBuilder sb, Span<T> arr, int ind, string name, string typeName, Func<T, string> formatter = null, int arrRowSize = 10) where T : struct
{ {
var aCount = arr?.Length ?? 0; var aCount = arr.Length;
//var arrRowSize = 10; //var arrRowSize = 10;
var aind = ind + 1; var aind = ind + 1;
var arrTag = name;// + " itemType=\"" + typeName + "\""; var arrTag = name;// + " itemType=\"" + typeName + "\"";
@ -1986,15 +1987,25 @@ namespace CodeWalker.GameFiles
SelfClosingTag(sb, ind, arrTag); SelfClosingTag(sb, ind, arrTag);
} }
} }
public static void WriteRawArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter = null, int arrRowSize = 10) where T : struct
{
WriteRawArray<T>(sb, arr.AsSpan(), ind, name, typeName, formatter, arrRowSize);
}
public static void WriteItemArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter) where T : struct public static void WriteItemArray<T>(StringBuilder sb, T[] arr, int ind, string name, string typeName, Func<T, string> formatter) where T : struct
{ {
var aCount = arr?.Length ?? 0; WriteItemArray<T>(sb, arr.AsSpan(), ind, name, typeName, formatter);
}
public static void WriteItemArray<T>(StringBuilder sb, Span<T> arr, int ind, string name, string typeName, Func<T, string> formatter) where T : struct
{
var itemCount = arr.Length;
var arrTag = name;// + " itemType=\"Hash\""; var arrTag = name;// + " itemType=\"Hash\"";
var aind = ind + 1; var aind = ind + 1;
if (aCount > 0) if (itemCount > 0)
{ {
OpenTag(sb, ind, arrTag); OpenTag(sb, ind, arrTag);
for (int n = 0; n < aCount; n++) for (int n = 0; n < itemCount; n++)
{ {
Indent(sb, aind); Indent(sb, aind);
sb.Append("<Item>"); sb.Append("<Item>");

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using TC = System.ComponentModel.TypeConverterAttribute; using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter; using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Diagnostics;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -15819,45 +15819,46 @@ namespace CodeWalker.GameFiles
public static T ConvertDataRaw<T>(byte[] data) where T : struct public static T ConvertDataRaw<T>(byte[] data) where T : struct
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); MemoryMarshal.TryRead<T>(data.AsSpan(), out T value);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h); return value;
handle.Free();
return r;
} }
public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset); return value;
handle.Free(); //return MemoryMarshal.GetReference<T>(data.AsSpan(offset));
return r; //GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//return r;
} }
public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset); value.SwapEnd();
handle.Free();
r.SwapEnd(); return value;
return r; //GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//r.SwapEnd();
//return r;
} }
public static T[] ConvertDataArrayRaw<T>(byte[] data, int offset, int count) where T : struct public static Span<T>ConvertDataArrayRaw<T>(byte[] data, int offset, int count) where T : struct
{ {
T[] items = new T[count]; return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T))));
int itemsize = Marshal.SizeOf(typeof(T));
//for (int i = 0; i < count; i++)
//{
// int off = offset + i * itemsize;
// items[i] = ConvertDataRaw<T>(data, off);
//}
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned); //GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject(); //var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, offset, h, itemsize * count); //Marshal.Copy(data, offset, h, itemsize * count);
handle.Free(); //handle.Free();
return items; //return items;
} }
@ -15878,7 +15879,7 @@ namespace CodeWalker.GameFiles
return e; return e;
} }
public static T[] GetItemArrayRaw<T>(PsoFile pso, Array_Structure arr) where T : struct public static Span<T> GetItemArrayRaw<T>(PsoFile pso, Array_Structure arr) where T : struct
{ {
if ((arr.Count1 > 0) && (arr.Pointer > 0)) if ((arr.Count1 > 0) && (arr.Pointer > 0))
{ {
@ -15887,7 +15888,7 @@ namespace CodeWalker.GameFiles
} }
return null; return null;
} }
public static T[] GetItemArray<T>(PsoFile pso, Array_Structure arr) where T : struct, IPsoSwapEnd public static Span<T> GetItemArray<T>(PsoFile pso, Array_Structure arr) where T : struct, IPsoSwapEnd
{ {
if ((arr.Count1 > 0) && (arr.Pointer > 0)) if ((arr.Count1 > 0) && (arr.Pointer > 0))
{ {
@ -15906,7 +15907,7 @@ namespace CodeWalker.GameFiles
} }
public static uint[] GetUintArrayRaw(PsoFile pso, Array_uint arr) public static Span<uint> GetUintArrayRaw(PsoFile pso, Array_uint arr)
{ {
byte[] data = pso.DataSection.Data; byte[] data = pso.DataSection.Data;
var entryid = arr.PointerDataId; var entryid = arr.PointerDataId;
@ -15917,12 +15918,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset; var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1]; var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
int totoffset = arrentry.Offset + (int)entryoffset; int totoffset = arrentry.Offset + (int)entryoffset;
uint[] readdata = ConvertDataArrayRaw<uint>(data, totoffset, arr.Count1); var readdata = ConvertDataArrayRaw<uint>(data, totoffset, arr.Count1);
return readdata; return readdata;
} }
public static uint[] GetUintArray(PsoFile pso, Array_uint arr) public static Span<uint> GetUintArray(PsoFile pso, Array_uint arr)
{ {
uint[] uints = GetUintArrayRaw(pso, arr); var uints = GetUintArrayRaw(pso, arr);
if (uints == null) return null; if (uints == null) return null;
for (int i = 0; i < uints.Length; i++) for (int i = 0; i < uints.Length; i++)
{ {
@ -15933,7 +15934,7 @@ namespace CodeWalker.GameFiles
public static MetaHash[] GetHashArray(PsoFile pso, Array_uint arr) public static MetaHash[] GetHashArray(PsoFile pso, Array_uint arr)
{ {
uint[] uints = GetUintArrayRaw(pso, arr); var uints = GetUintArrayRaw(pso, arr);
if (uints == null) return null; if (uints == null) return null;
MetaHash[] hashes = new MetaHash[uints.Length]; MetaHash[] hashes = new MetaHash[uints.Length];
for (int n = 0; n < uints.Length; n++) for (int n = 0; n < uints.Length; n++)
@ -15946,7 +15947,7 @@ namespace CodeWalker.GameFiles
public static float[] GetFloatArrayRaw(PsoFile pso, Array_float arr) public static Span<float> GetFloatArrayRaw(PsoFile pso, Array_float arr)
{ {
byte[] data = pso.DataSection.Data; byte[] data = pso.DataSection.Data;
var entryid = arr.PointerDataId; var entryid = arr.PointerDataId;
@ -15957,12 +15958,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset; var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1]; var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
int totoffset = arrentry.Offset + (int)entryoffset; int totoffset = arrentry.Offset + (int)entryoffset;
float[] readdata = ConvertDataArrayRaw<float>(data, totoffset, arr.Count1); var readdata = ConvertDataArrayRaw<float>(data, totoffset, arr.Count1);
return readdata; return readdata;
} }
public static float[] GetFloatArray(PsoFile pso, Array_float arr) public static Span<float> GetFloatArray(PsoFile pso, Array_float arr)
{ {
float[] floats = GetFloatArrayRaw(pso, arr); var floats = GetFloatArrayRaw(pso, arr);
if (floats == null) return null; if (floats == null) return null;
for (int i = 0; i < floats.Length; i++) for (int i = 0; i < floats.Length; i++)
{ {
@ -15975,7 +15976,7 @@ namespace CodeWalker.GameFiles
public static ushort[] GetUShortArrayRaw(PsoFile pso, Array_Structure arr) public static Span<ushort> GetUShortArrayRaw(PsoFile pso, Array_Structure arr)
{ {
byte[] data = pso.DataSection.Data; byte[] data = pso.DataSection.Data;
var entryid = arr.PointerDataId; var entryid = arr.PointerDataId;
@ -15986,12 +15987,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset; var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1]; var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
int totoffset = arrentry.Offset + (int)entryoffset; int totoffset = arrentry.Offset + (int)entryoffset;
ushort[] readdata = ConvertDataArrayRaw<ushort>(data, totoffset, arr.Count1); Span<ushort> readdata = ConvertDataArrayRaw<ushort>(data, totoffset, arr.Count1);
return readdata; return readdata;
} }
public static ushort[] GetUShortArray(PsoFile pso, Array_Structure arr) public static Span<ushort> GetUShortArray(PsoFile pso, Array_Structure arr)
{ {
ushort[] ushorts = GetUShortArrayRaw(pso, arr); var ushorts = GetUShortArrayRaw(pso, arr);
if (ushorts == null) return null; if (ushorts == null) return null;
for (int i = 0; i < ushorts.Length; i++) for (int i = 0; i < ushorts.Length; i++)
{ {
@ -16007,7 +16008,7 @@ namespace CodeWalker.GameFiles
public static T[] GetObjectArray<T, U>(PsoFile pso, Array_Structure arr) where U : struct, IPsoSwapEnd where T : PsoClass<U>, new() public static T[] GetObjectArray<T, U>(PsoFile pso, Array_Structure arr) where U : struct, IPsoSwapEnd where T : PsoClass<U>, new()
{ {
U[] items = GetItemArray<U>(pso, arr); Span<U> items = GetItemArray<U>(pso, arr);
if (items == null) return null; if (items == null) return null;
if (items.Length == 0) return null; if (items.Length == 0) return null;
T[] result = new T[items.Length]; T[] result = new T[items.Length];
@ -16038,13 +16039,11 @@ namespace CodeWalker.GameFiles
public static PsoPOINTER[] GetPointerArray(PsoFile pso, Array_StructurePointer array) public static Span<PsoPOINTER> GetPointerArray(PsoFile pso, Array_StructurePointer array)
{ {
uint count = array.Count1; uint count = array.Count1;
if (count == 0) return null; if (count == 0) return null;
int ptrsize = Marshal.SizeOf(typeof(MetaPOINTER));
int itemsleft = (int)count; //large arrays get split into chunks...
uint ptrindex = array.PointerDataIndex; uint ptrindex = array.PointerDataIndex;
uint ptroffset = array.PointerDataOffset; uint ptroffset = array.PointerDataOffset;
var ptrblock = (ptrindex < pso.DataMapSection.EntriesCount) ? pso.DataMapSection.Entries[ptrindex] : null; var ptrblock = (ptrindex < pso.DataMapSection.EntriesCount) ? pso.DataMapSection.Entries[ptrindex] : null;
@ -16071,13 +16070,12 @@ namespace CodeWalker.GameFiles
{ {
uint count = array.Count1; uint count = array.Count1;
if (count == 0) return null; if (count == 0) return null;
PsoPOINTER[] ptrs = GetPointerArray(pso, array); var ptrs = GetPointerArray(pso, array);
if (ptrs == null) return null; if (ptrs == null) return null;
if (ptrs.Length < count) if (ptrs.Length < count)
{ return null; } { return null; }
T[] items = new T[count]; T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T));
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {

View File

@ -243,106 +243,106 @@ namespace CodeWalker.GameFiles
default: return "XML"; default: return "XML";
} }
} }
public static MetaFormat GetXMLFormat(string fnamel, out int trimlength) public static MetaFormat GetXMLFormat(string fileName, out int trimlength)
{ {
var mformat = MetaFormat.RSC; var mformat = MetaFormat.RSC;
trimlength = 4; trimlength = 4;
if (!fnamel.EndsWith(".xml")) if (!fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.XML;//not really correct, but have to return something... mformat = MetaFormat.XML;//not really correct, but have to return something...
} }
if (fnamel.EndsWith(".pso.xml")) if (fileName.EndsWith(".pso.xml"))
{ {
mformat = MetaFormat.PSO; mformat = MetaFormat.PSO;
trimlength = 8; trimlength = 8;
} }
if (fnamel.EndsWith(".rbf.xml")) if (fileName.EndsWith(".rbf.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.RBF; mformat = MetaFormat.RBF;
trimlength = 8; trimlength = 8;
} }
if (fnamel.EndsWith(".rel.xml")) if (fileName.EndsWith(".rel.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.AudioRel; mformat = MetaFormat.AudioRel;
} }
if (fnamel.EndsWith(".ynd.xml")) if (fileName.EndsWith(".ynd.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ynd; mformat = MetaFormat.Ynd;
} }
if (fnamel.EndsWith(".ynv.xml")) if (fileName.EndsWith(".ynv.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ynv; mformat = MetaFormat.Ynv;
} }
if (fnamel.EndsWith(".ycd.xml")) if (fileName.EndsWith(".ycd.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ycd; mformat = MetaFormat.Ycd;
} }
if (fnamel.EndsWith(".ybn.xml")) if (fileName.EndsWith(".ybn.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ybn; mformat = MetaFormat.Ybn;
} }
if (fnamel.EndsWith(".ytd.xml")) if (fileName.EndsWith(".ytd.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ytd; mformat = MetaFormat.Ytd;
} }
if (fnamel.EndsWith(".ydr.xml")) if (fileName.EndsWith(".ydr.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ydr; mformat = MetaFormat.Ydr;
} }
if (fnamel.EndsWith(".ydd.xml")) if (fileName.EndsWith(".ydd.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ydd; mformat = MetaFormat.Ydd;
} }
if (fnamel.EndsWith(".yft.xml")) if (fileName.EndsWith(".yft.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Yft; mformat = MetaFormat.Yft;
} }
if (fnamel.EndsWith(".ypt.xml")) if (fileName.EndsWith(".ypt.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ypt; mformat = MetaFormat.Ypt;
} }
if (fnamel.EndsWith(".yld.xml")) if (fileName.EndsWith(".yld.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Yld; mformat = MetaFormat.Yld;
} }
if (fnamel.EndsWith(".yed.xml")) if (fileName.EndsWith(".yed.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Yed; mformat = MetaFormat.Yed;
} }
if (fnamel.EndsWith(".ywr.xml")) if (fileName.EndsWith(".ywr.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ywr; mformat = MetaFormat.Ywr;
} }
if (fnamel.EndsWith(".yvr.xml")) if (fileName.EndsWith(".yvr.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Yvr; mformat = MetaFormat.Yvr;
} }
if (fnamel.EndsWith(".awc.xml")) if (fileName.EndsWith(".awc.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Awc; mformat = MetaFormat.Awc;
} }
if (fnamel.EndsWith(".fxc.xml")) if (fileName.EndsWith(".fxc.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Fxc; mformat = MetaFormat.Fxc;
} }
if (fnamel.EndsWith("cache_y.dat.xml")) if (fileName.EndsWith("cache_y.dat.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.CacheFile; mformat = MetaFormat.CacheFile;
} }
if (fnamel.EndsWith(".dat.xml") && fnamel.StartsWith("heightmap")) if (fileName.EndsWith(".dat.xml", StringComparison.OrdinalIgnoreCase) && fileName.StartsWith("heightmap", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Heightmap; mformat = MetaFormat.Heightmap;
} }
if (fnamel.EndsWith(".ypdb.xml")) if (fileName.EndsWith(".ypdb.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Ypdb; mformat = MetaFormat.Ypdb;
} }
if (fnamel.EndsWith(".yfd.xml")) if (fileName.EndsWith(".yfd.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Yfd; mformat = MetaFormat.Yfd;
} }
if (fnamel.EndsWith(".mrf.xml")) if (fileName.EndsWith(".mrf.xml", StringComparison.OrdinalIgnoreCase))
{ {
mformat = MetaFormat.Mrf; mformat = MetaFormat.Mrf;
} }

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -5331,6 +5332,15 @@ namespace CodeWalker.GameFiles
var d = new Drawable(); var d = new Drawable();
d.LightAttributes = dd.LightAttributes; d.LightAttributes = dd.LightAttributes;
d.Name = dd.Name; d.Name = dd.Name;
if (d.Hash != 0)
{
d.Hash = dd.Hash;
}
else
{
d.Hash = JenkHash.GenHash(dd.Name);
}
d.Bound = dd.Bound; d.Bound = dd.Bound;
r = d; r = d;
} }
@ -5383,6 +5393,7 @@ namespace CodeWalker.GameFiles
// reference data // reference data
public string Name { get; set; } public string Name { get; set; }
public uint Hash { get; set; }
public Bounds Bound { get; set; } public Bounds Bound { get; set; }
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
@ -5455,6 +5466,7 @@ namespace CodeWalker.GameFiles
public override void WriteXml(StringBuilder sb, int indent, string ddsfolder) public override void WriteXml(StringBuilder sb, int indent, string ddsfolder)
{ {
YdrXml.StringTag(sb, indent, "Name", YdrXml.XmlEscape(Name)); YdrXml.StringTag(sb, indent, "Name", YdrXml.XmlEscape(Name));
YdrXml.StringTag(sb, indent, "Hash", Hash.ToString());
base.WriteXml(sb, indent, ddsfolder); base.WriteXml(sb, indent, ddsfolder);
if (Bound != null) if (Bound != null)
{ {
@ -5468,6 +5480,10 @@ namespace CodeWalker.GameFiles
public override void ReadXml(XmlNode node, string ddsfolder) public override void ReadXml(XmlNode node, string ddsfolder)
{ {
Name = Xml.GetChildInnerText(node, "Name"); Name = Xml.GetChildInnerText(node, "Name");
if (uint.TryParse(Xml.GetChildInnerText(node, "Hash"), out var hash))
{
Hash = hash;
}
base.ReadXml(node, ddsfolder); base.ReadXml(node, ddsfolder);
var bnode = node.SelectSingleNode("Bounds"); var bnode = node.SelectSingleNode("Bounds");
if (bnode != null) if (bnode != null)
@ -5847,8 +5863,16 @@ namespace CodeWalker.GameFiles
var d = new Drawable(); var d = new Drawable();
d.ReadXml(inode, ddsfolder); d.ReadXml(inode, ddsfolder);
drawables.Add(d); drawables.Add(d);
if (d.Hash != 0)
{
drawablehashes.Add(d.Hash);
}
else
{
drawablehashes.Add(JenkHash.GenHash(d.Name));//TODO: check this! drawablehashes.Add(JenkHash.GenHash(d.Name));//TODO: check this!
} }
}
} }
Hashes = drawablehashes.ToArray(); Hashes = drawablehashes.ToArray();

View File

@ -1791,7 +1791,7 @@ namespace CodeWalker.GameFiles
{ {
int datalength = ItemCount * StructureSize; int datalength = ItemCount * StructureSize;
byte[] data = reader.ReadBytes(datalength); byte[] data = reader.ReadBytes(datalength);
Items = MetaTypes.ConvertDataArray<T>(data, 0, ItemCount); Items = MetaTypes.ConvertDataArray<T>(data, 0, ItemCount).ToArray();
} }
public override void Write(ResourceDataWriter writer, params object[] parameters) public override void Write(ResourceDataWriter writer, params object[] parameters)

View File

@ -348,9 +348,9 @@ namespace CodeWalker.GameFiles
fileBase.FilePagesInfo.GraphicsPagesCount = (byte)graphicsPageFlags.Count; fileBase.FilePagesInfo.GraphicsPagesCount = (byte)graphicsPageFlags.Count;
var systemStream = new MemoryStream(); using var systemStream = new MemoryStream();
var graphicsStream = new MemoryStream(); using var graphicsStream = new MemoryStream();
var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream); using var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream);
resourceWriter.Position = 0x50000000; resourceWriter.Position = 0x50000000;
foreach (var block in systemBlocks) foreach (var block in systemBlocks)
@ -456,15 +456,12 @@ namespace CodeWalker.GameFiles
public static byte[] Compress(byte[] data) public static byte[] Compress(byte[] data)
{ {
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = RpfFile.recyclableMemoryStreamManager.GetStream())
{ {
DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true); DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true);
ds.Write(data, 0, data.Length); ds.Write(data, 0, data.Length);
ds.Close(); ds.Close();
byte[] deflated = ms.GetBuffer(); return ms.ToArray();
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer...
Array.Copy(deflated, outbuf, outbuf.Length);
return outbuf;
} }
} }
public static byte[] Decompress(byte[] data) public static byte[] Decompress(byte[] data)
@ -472,14 +469,17 @@ namespace CodeWalker.GameFiles
using (MemoryStream ms = new MemoryStream(data)) using (MemoryStream ms = new MemoryStream(data))
{ {
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = new MemoryStream(); MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length);
ds.CopyTo(outstr); ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer(); return outstr.ToArray();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer...
Array.Copy(deflated, outbuf, outbuf.Length);
return outbuf;
} }
} }
public static DeflateStream Decompress(Stream stream)
{
DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress);
return ds;
}
} }
} }

View File

@ -38,13 +38,16 @@ namespace CodeWalker.GameFiles
/// <summary> /// <summary>
/// Represents a resource data reader. /// Represents a resource data reader.
/// </summary> /// </summary>
public class ResourceDataReader : DataReader public class ResourceDataReader : DataReader, IDisposable
{ {
private const long SYSTEM_BASE = 0x50000000; private const long SYSTEM_BASE = 0x50000000;
private const long GRAPHICS_BASE = 0x60000000; private const long GRAPHICS_BASE = 0x60000000;
private Stream systemStream; private readonly Stream systemStream;
private Stream graphicsStream; private readonly Stream graphicsStream;
private readonly long systemSize = 0;
private readonly long graphicsSize = 0;
public RpfResourceFileEntry FileEntry { get; set; } public RpfResourceFileEntry FileEntry { get; set; }
@ -71,7 +74,7 @@ namespace CodeWalker.GameFiles
{ {
get; get;
set; set;
} } = SYSTEM_BASE;
/// <summary> /// <summary>
/// Initializes a new resource data reader for the specified system- and graphics-stream. /// Initializes a new resource data reader for the specified system- and graphics-stream.
@ -81,14 +84,16 @@ namespace CodeWalker.GameFiles
{ {
this.systemStream = systemStream; this.systemStream = systemStream;
this.graphicsStream = graphicsStream; this.graphicsStream = graphicsStream;
this.systemSize = systemStream.Length;
this.graphicsSize = graphicsStream.Length;
} }
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess) : base((Stream)null, endianess)
{ {
FileEntry = resentry; FileEntry = resentry;
var systemSize = resentry.SystemSize; this.systemSize = resentry.SystemSize;
var graphicsSize = resentry.GraphicsSize; this.graphicsSize = resentry.GraphicsSize;
//if (data != null) //if (data != null)
//{ //{
@ -103,9 +108,8 @@ namespace CodeWalker.GameFiles
// } // }
//} //}
this.systemStream = new MemoryStream(data, 0, systemSize); this.systemStream = new MemoryStream(data, 0, (int)systemSize);
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize); this.graphicsStream = new MemoryStream(data, (int)systemSize, (int)graphicsSize);
Position = 0x50000000;
} }
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian) public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
@ -113,24 +117,21 @@ namespace CodeWalker.GameFiles
{ {
this.systemStream = new MemoryStream(data, 0, systemSize); this.systemStream = new MemoryStream(data, 0, systemSize);
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize); this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
Position = 0x50000000;
} }
/// <summary> /// <summary>
/// Reads data from the underlying stream. This is the only method that directly accesses /// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream. /// the data in the underlying stream.
/// </summary> /// </summary>
protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false) protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[] buffer = null)
{ {
if ((Position & SYSTEM_BASE) == SYSTEM_BASE) if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{ {
// read from system stream... // read from system stream...
systemStream.Position = Position & ~0x50000000; systemStream.Position = Position & ~SYSTEM_BASE;
var buffer = new byte[count]; buffer ??= new byte[count];
systemStream.Read(buffer, 0, count); systemStream.Read(buffer, 0, count);
// handle endianess // handle endianess
@ -139,17 +140,17 @@ namespace CodeWalker.GameFiles
Array.Reverse(buffer); Array.Reverse(buffer);
} }
Position = systemStream.Position | 0x50000000; Position = systemStream.Position | SYSTEM_BASE;
return buffer; return buffer;
} }
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE) else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{ {
// read from graphic stream... // read from graphic stream...
graphicsStream.Position = Position & ~0x60000000; graphicsStream.Position = Position & ~GRAPHICS_BASE;
var buffer = new byte[count]; buffer ??= new byte[count];
graphicsStream.Read(buffer, 0, count); graphicsStream.Read(buffer, 0, count);
// handle endianess // handle endianess
@ -158,12 +159,44 @@ namespace CodeWalker.GameFiles
Array.Reverse(buffer); Array.Reverse(buffer);
} }
Position = graphicsStream.Position | 0x60000000; Position = graphicsStream.Position | GRAPHICS_BASE;
return buffer; return buffer;
} }
throw new Exception("illegal position!"); throw new Exception("illegal position!");
} }
public override byte ReadByte()
{
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{
// read from system stream...
systemStream.Position = Position & ~SYSTEM_BASE;
var readByte = (byte)systemStream.ReadByte();
Position = systemStream.Position | SYSTEM_BASE;
return readByte;
}
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{
// read from graphic stream...
graphicsStream.Position = Position & ~GRAPHICS_BASE;
var readByte = (byte)graphicsStream.ReadByte();
Position = graphicsStream.Position | GRAPHICS_BASE;
return readByte;
}
throw new Exception("illegal position!");
}
/// <summary> /// <summary>
/// Reads a block. /// Reads a block.
/// </summary> /// </summary>
@ -447,6 +480,13 @@ namespace CodeWalker.GameFiles
return result; return result;
} }
public override void Dispose()
{
base.Dispose();
systemStream?.Dispose();
graphicsStream?.Dispose();
}
} }
@ -454,7 +494,7 @@ namespace CodeWalker.GameFiles
/// <summary> /// <summary>
/// Represents a resource data writer. /// Represents a resource data writer.
/// </summary> /// </summary>
public class ResourceDataWriter : DataWriter public class ResourceDataWriter : DataWriter, IDisposable
{ {
private const long SYSTEM_BASE = 0x50000000; private const long SYSTEM_BASE = 0x50000000;
private const long GRAPHICS_BASE = 0x60000000; private const long GRAPHICS_BASE = 0x60000000;
@ -596,7 +636,13 @@ namespace CodeWalker.GameFiles
} }
} }
public override void Dispose()
{
base.Dispose();
systemStream?.Dispose();
graphicsStream?.Dispose();
}
} }

View File

@ -218,12 +218,25 @@ namespace CodeWalker.GameFiles
public uint Unknown_4Ch { get; set; } // 0x00000000 public uint Unknown_4Ch { get; set; } // 0x00000000
// reference data // reference data
public string Name { get; set; } public string Name { get => name; set
public uint NameHash { get; set; } {
name = value;
nameHash = 0;
}
}
public uint NameHash { get
{
if (nameHash == 0)
{
nameHash = JenkHash.GenHashLower(name);
}
return nameHash;
}
set => nameHash = value; }
private string_r NameBlock = null; private string_r NameBlock = null;
private uint nameHash;
private string name;
public TextureUsage Usage public TextureUsage Usage
{ {
@ -281,11 +294,6 @@ namespace CodeWalker.GameFiles
this.NamePointer // offset this.NamePointer // offset
); );
if (!string.IsNullOrEmpty(Name))
{
NameHash = JenkHash.GenHash(Name.ToLowerInvariant());
}
//switch (Unknown_32h) //switch (Unknown_32h)
//{ //{
// case 0x20: // case 0x20:
@ -405,7 +413,6 @@ namespace CodeWalker.GameFiles
public virtual void ReadXml(XmlNode node, string ddsfolder) public virtual void ReadXml(XmlNode node, string ddsfolder)
{ {
Name = Xml.GetChildInnerText(node, "Name"); Name = Xml.GetChildInnerText(node, "Name");
NameHash = JenkHash.GenHash(Name?.ToLowerInvariant());
Unknown_32h = (ushort)Xml.GetChildUIntAttribute(node, "Unk32", "value"); Unknown_32h = (ushort)Xml.GetChildUIntAttribute(node, "Unk32", "value");
Usage = Xml.GetChildEnumInnerText<TextureUsage>(node, "Usage"); Usage = Xml.GetChildEnumInnerText<TextureUsage>(node, "Usage");
UsageFlags = Xml.GetChildEnumInnerText<TextureUsageFlags>(node, "UsageFlags"); UsageFlags = Xml.GetChildEnumInnerText<TextureUsageFlags>(node, "UsageFlags");

View File

@ -1,10 +1,17 @@
using System; using Microsoft.IO;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Pipes;
using System.Linq; using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
@ -13,7 +20,24 @@ namespace CodeWalker.GameFiles
public class RpfFile public class RpfFile
{ {
public string Name { get; set; } //name of this RPF file/package public string Name { get; set; } //name of this RPF file/package
public string NameLower { get; set; }
private string _nameLower;
public string NameLower
{
get
{
if (_nameLower == null)
{
_nameLower = Name.ToLowerInvariant();
}
return _nameLower;
}
set
{
_nameLower = value;
}
}
public string Path { get; set; } //path within the RPF structure public string Path { get; set; } //path within the RPF structure
public string FilePath { get; set; } //full file path of the RPF public string FilePath { get; set; } //full file path of the RPF
public long FileSize { get; set; } public long FileSize { get; set; }
@ -61,7 +85,6 @@ namespace CodeWalker.GameFiles
{ {
FileInfo fi = new FileInfo(fpath); FileInfo fi = new FileInfo(fpath);
Name = fi.Name; Name = fi.Name;
NameLower = Name.ToLowerInvariant();
Path = relpath.ToLowerInvariant(); Path = relpath.ToLowerInvariant();
FilePath = fpath; FilePath = fpath;
FileSize = fi.Length; FileSize = fi.Length;
@ -69,7 +92,6 @@ namespace CodeWalker.GameFiles
public RpfFile(string name, string path, long filesize) //for a child RPF public RpfFile(string name, string path, long filesize) //for a child RPF
{ {
Name = name; Name = name;
NameLower = Name.ToLowerInvariant();
Path = path.ToLowerInvariant(); Path = path.ToLowerInvariant();
FilePath = path; FilePath = path;
FileSize = filesize; FileSize = filesize;
@ -147,8 +169,11 @@ namespace CodeWalker.GameFiles
throw new Exception("Invalid Resource - not GTAV!"); throw new Exception("Invalid Resource - not GTAV!");
} }
byte[] entriesdata = br.ReadBytes((int)EntryCount * 16); //4x uints each byte[] entriesdata = ArrayPool<byte>.Shared.Rent((int)EntryCount * 16);
byte[] namesdata = br.ReadBytes((int)NamesLength); byte[] namesdata = ArrayPool<byte>.Shared.Rent((int)NamesLength);
br.BaseStream.Read(entriesdata, 0, (int)EntryCount * 16);
br.BaseStream.Read(namesdata, 0, (int)NamesLength);
switch (Encryption) switch (Encryption)
{ {
@ -156,25 +181,25 @@ namespace CodeWalker.GameFiles
case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC
break; break;
case RpfEncryption.AES: case RpfEncryption.AES:
entriesdata = GTACrypto.DecryptAES(entriesdata); GTACrypto.DecryptAES(entriesdata, (int)EntryCount * 16);
namesdata = GTACrypto.DecryptAES(namesdata); GTACrypto.DecryptAES(namesdata, (int)NamesLength);
IsAESEncrypted = true; IsAESEncrypted = true;
break; break;
case RpfEncryption.NG: case RpfEncryption.NG:
entriesdata = GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize); default:
namesdata = GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize); GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, (int)EntryCount * 16);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, (int)NamesLength);
IsNGEncrypted = true; IsNGEncrypted = true;
break; break;
default: //unknown encryption type? assume NG.. never seems to get here
entriesdata = GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize);
namesdata = GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize);
break;
} }
using var entriesrdr = new DataReader(new MemoryStream(entriesdata, 0, (int)EntryCount * 16));
using var namesrdr = new DataReader(new MemoryStream(namesdata, 0, (int)NamesLength));
var entriesrdr = new DataReader(new MemoryStream(entriesdata));
var namesrdr = new DataReader(new MemoryStream(namesdata)); AllEntries = new List<RpfEntry>((int)EntryCount);
AllEntries = new List<RpfEntry>();
TotalFileCount = 0; TotalFileCount = 0;
TotalFolderCount = 0; TotalFolderCount = 0;
TotalResourceCount = 0; TotalResourceCount = 0;
@ -213,28 +238,24 @@ namespace CodeWalker.GameFiles
e.Read(entriesrdr); e.Read(entriesrdr);
namesrdr.Position = e.NameOffset;
e.Name = namesrdr.ReadString();
if (e.Name.Length > 256)
{
// long names can freeze the RPFExplorer
e.Name = e.Name.Substring(0, 256);
}
e.NameLower = e.Name.ToLowerInvariant();
if ((e is RpfFileEntry) && string.IsNullOrEmpty(e.Name))
{
}
if ((e is RpfResourceFileEntry))// && string.IsNullOrEmpty(e.Name))
{
var rfe = e as RpfResourceFileEntry;
rfe.IsEncrypted = rfe.NameLower.EndsWith(".ysc");//any other way to know..?
}
AllEntries.Add(e); AllEntries.Add(e);
} }
foreach(var entry in AllEntries)
{
namesrdr.Position = entry.NameOffset;
entry.Name = namesrdr.ReadString(256);
if (entry.Name.Length > 256)
{
// long names can freeze the RPFExplorer
entry.Name = entry.Name.Substring(0, 256);
}
if (entry is RpfResourceFileEntry rfe)// && string.IsNullOrEmpty(e.Name))
{
rfe.IsEncrypted = rfe.Name.EndsWith(".ysc", StringComparison.OrdinalIgnoreCase);//any other way to know..?
}
}
Root = (RpfDirectoryEntry)AllEntries[0]; Root = (RpfDirectoryEntry)AllEntries[0];
Root.Path = Path.ToLowerInvariant();// + "\\" + Root.Name; Root.Path = Path.ToLowerInvariant();// + "\\" + Root.Name;
@ -251,17 +272,16 @@ namespace CodeWalker.GameFiles
{ {
RpfEntry e = AllEntries[i]; RpfEntry e = AllEntries[i];
e.Parent = item; e.Parent = item;
if (e is RpfDirectoryEntry) if (e is RpfDirectoryEntry rde)
{ {
RpfDirectoryEntry rde = e as RpfDirectoryEntry; rde.Path = item.Path + "\\" + rde.Name;
rde.Path = item.Path + "\\" + rde.NameLower;
item.Directories.Add(rde); item.Directories.Add(rde);
stack.Push(rde); stack.Push(rde);
} }
else if (e is RpfFileEntry) else if (e is RpfFileEntry)
{ {
RpfFileEntry rfe = e as RpfFileEntry; RpfFileEntry rfe = e as RpfFileEntry;
rfe.Path = item.Path + "\\" + rfe.NameLower; rfe.Path = item.Path + "\\" + rfe.Name;
item.Files.Add(rfe); item.Files.Add(rfe);
} }
} }
@ -270,32 +290,41 @@ namespace CodeWalker.GameFiles
br.BaseStream.Position = StartPos; br.BaseStream.Position = StartPos;
CurrentFileReader = null; CurrentFileReader = null;
ArrayPool<byte>.Shared.Return(entriesdata);
ArrayPool<byte>.Shared.Return(namesdata);
} }
public void ScanStructure(Action<string> updateStatus, Action<string> errorLog) public bool ScanStructure(Action<string> updateStatus, Action<string> errorLog)
{
using (BinaryReader br = new BinaryReader(File.OpenRead(FilePath)))
{ {
using var fileStream = File.OpenRead(FilePath);
using var br = new BinaryReader(fileStream);
try try
{ {
ScanStructure(br, updateStatus, errorLog); return ScanStructure(br, updateStatus, errorLog);
} }
catch (Exception ex) catch (Exception ex)
{ {
LastError = ex.ToString(); LastError = ex.ToString();
LastException = ex; LastException = ex;
errorLog(FilePath + ": " + LastError); errorLog?.Invoke(FilePath + ": " + LastError);
return false;
} }
} }
} private bool ScanStructure(BinaryReader br, Action<string> updateStatus, Action<string> errorLog)
private void 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;
try
{ {
ReadHeader(br); ReadHeader(br);
} catch
{
return false;
}
GrandTotalRpfCount = 1; //count this file.. GrandTotalRpfCount = 1; //count this file..
GrandTotalFileCount = 1; //start with this one. GrandTotalFileCount = 1; //start with this one.
@ -311,13 +340,10 @@ namespace CodeWalker.GameFiles
{ {
try try
{ {
if (entry is RpfBinaryFileEntry) if (entry is RpfBinaryFileEntry binentry)
{ {
RpfBinaryFileEntry binentry = entry as RpfBinaryFileEntry;
//search all the sub resources for YSC files. (recurse!) //search all the sub resources for YSC files. (recurse!)
string lname = binentry.NameLower; if (binentry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase) && binentry.Path.Length < 5000) // a long path is most likely an attempt to crash CW, so skip it
if (lname.EndsWith(".rpf") && binentry.Path.Length < 5000) // a long path is most likely an attempt to crash CW, so skip it
{ {
br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512); br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512);
@ -327,8 +353,8 @@ namespace CodeWalker.GameFiles
subfile.Parent = this; subfile.Parent = this;
subfile.ParentFileEntry = binentry; subfile.ParentFileEntry = binentry;
subfile.ScanStructure(br, updateStatus, errorLog); if (subfile.ScanStructure(br, updateStatus, errorLog))
{
GrandTotalRpfCount += subfile.GrandTotalRpfCount; GrandTotalRpfCount += subfile.GrandTotalRpfCount;
GrandTotalFileCount += subfile.GrandTotalFileCount; GrandTotalFileCount += subfile.GrandTotalFileCount;
GrandTotalFolderCount += subfile.GrandTotalFolderCount; GrandTotalFolderCount += subfile.GrandTotalFolderCount;
@ -337,6 +363,7 @@ namespace CodeWalker.GameFiles
Children.Add(subfile); Children.Add(subfile);
} }
}
else else
{ {
//binary file that's not an rpf... //binary file that's not an rpf...
@ -359,7 +386,7 @@ namespace CodeWalker.GameFiles
errorLog?.Invoke(entry.Path + ": " + ex.ToString()); errorLog?.Invoke(entry.Path + ": " + ex.ToString());
} }
} }
return true;
} }
@ -437,26 +464,25 @@ namespace CodeWalker.GameFiles
br.Read(tbytes, 0, (int)totlen); br.Read(tbytes, 0, (int)totlen);
byte[] decr;
if (IsAESEncrypted) if (IsAESEncrypted)
{ {
decr = GTACrypto.DecryptAES(tbytes); GTACrypto.DecryptAES(tbytes);
//special case! probable duplicate pilot_school.ysc //special case! probable duplicate pilot_school.ysc
ofpath = outputfolder + "\\" + Name + "___" + resentry.Name; ofpath = outputfolder + "\\" + Name + "___" + resentry.Name;
} }
else else
{ {
decr = GTACrypto.DecryptNG(tbytes, resentry.Name, resentry.FileSize); GTACrypto.DecryptNG(tbytes, resentry.Name, resentry.FileSize);
} }
try try
{ {
MemoryStream ms = new MemoryStream(decr); MemoryStream ms = new MemoryStream(tbytes);
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress); DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = new MemoryStream(); MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyTo(outstr); ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer(); byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes(). byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
@ -498,7 +524,33 @@ namespace CodeWalker.GameFiles
//public Stream ExtractFileStream(RpfFileEntry entry)
//{
// try
// {
// using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath())))
// {
// if (entry is RpfBinaryFileEntry binaryFileEntry)
// {
// return ExtractFileBinary(binaryFileEntry, br);
// }
// else if (entry is RpfResourceFileEntry resourceFileEntry)
// {
// return ExtractFileResource(resourceFileEntry, br);
// }
// else
// {
// return null;
// }
// }
// }
// catch (Exception ex)
// {
// LastError = ex.ToString();
// LastException = ex;
// return null;
// }
//}
public byte[] ExtractFile(RpfFileEntry entry) public byte[] ExtractFile(RpfFileEntry entry)
{ {
@ -506,13 +558,13 @@ namespace CodeWalker.GameFiles
{ {
using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath()))) using (BinaryReader br = new BinaryReader(File.OpenRead(GetPhysicalFilePath())))
{ {
if (entry is RpfBinaryFileEntry) if (entry is RpfBinaryFileEntry binaryFileEntry)
{ {
return ExtractFileBinary(entry as RpfBinaryFileEntry, br); return ExtractFileBinary(binaryFileEntry, br);
} }
else if (entry is RpfResourceFileEntry) else if (entry is RpfResourceFileEntry resourceFileEntry)
{ {
return ExtractFileResource(entry as RpfResourceFileEntry, br); return ExtractFileResource(resourceFileEntry, br);
} }
else else
{ {
@ -543,27 +595,25 @@ namespace CodeWalker.GameFiles
br.BaseStream.Position += offset; br.BaseStream.Position += offset;
br.Read(tbytes, 0, (int)totlen); br.Read(tbytes, 0, (int)totlen);
byte[] decr = tbytes;
if (entry.IsEncrypted) if (entry.IsEncrypted)
{ {
if (IsAESEncrypted) if (IsAESEncrypted)
{ {
decr = GTACrypto.DecryptAES(tbytes); GTACrypto.DecryptAES(tbytes);
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
decr = GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize); GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize);
} }
//else //else
//{ } //{ }
} }
byte[] defl = decr; byte[] defl = tbytes;
if (entry.FileSize > 0) //apparently this means it's compressed if (entry.FileSize > 0) //apparently this means it's compressed
{ {
defl = DecompressBytes(decr); defl = DecompressBytes(tbytes);
} }
else else
{ {
@ -596,23 +646,21 @@ namespace CodeWalker.GameFiles
br.Read(tbytes, 0, (int)totlen); br.Read(tbytes, 0, (int)totlen);
byte[] decr = tbytes;
if (entry.IsEncrypted) if (entry.IsEncrypted)
{ {
if (IsAESEncrypted) if (IsAESEncrypted)
{ {
decr = GTACrypto.DecryptAES(tbytes); GTACrypto.DecryptAES(tbytes);
} }
else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files) else //if (IsNGEncrypted) //assume the archive is set to NG encryption if not AES... (comment: fix for openIV modded files)
{ {
decr = GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize); GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize);
} }
//else //else
//{ } //{ }
} }
byte[] deflated = DecompressBytes(decr); byte[] deflated = DecompressBytes(tbytes);
byte[] data = null; byte[] data = null;
@ -623,7 +671,7 @@ namespace CodeWalker.GameFiles
else else
{ {
entry.FileSize -= offset; entry.FileSize -= offset;
data = decr; data = tbytes;
} }
@ -665,6 +713,22 @@ namespace CodeWalker.GameFiles
return file; return file;
} }
public static T GetFile<T>(RpfEntry e, Stream data) where T : class, PackedFileStream, new()
{
T file = null;
RpfFileEntry entry = e as RpfFileEntry;
if ((data != null))
{
if (entry == null)
{
entry = CreateResourceFileEntry(data, 0);
}
file = new T();
file.Load(data, entry);
}
return file;
}
public static T GetResourceFile<T>(byte[] data) where T : class, PackedFile, new() public static T GetResourceFile<T>(byte[] data) where T : class, PackedFile, new()
@ -679,6 +743,36 @@ namespace CodeWalker.GameFiles
} }
return file; return file;
} }
public static void LoadResourceFile<T>(T file, Stream data, uint ver) where T : class, PackedFileStream
{
//direct load from a raw, compressed resource file (openIV-compatible format)
RpfResourceFileEntry resentry = CreateResourceFileEntry(data, ver);
if (file is GameFile)
{
GameFile gfile = file as GameFile;
var oldresentry = gfile.RpfFileEntry as RpfResourceFileEntry;
if (oldresentry != null) //update the existing entry with the new one
{
oldresentry.SystemFlags = resentry.SystemFlags;
oldresentry.GraphicsFlags = resentry.GraphicsFlags;
resentry.Name = oldresentry.Name;
}
else
{
gfile.RpfFileEntry = resentry; //just stick it in there for later...
}
}
data = ResourceBuilder.Decompress(data);
file.Load(data, resentry);
}
public static void LoadResourceFile<T>(T file, byte[] data, uint ver) where T : class, PackedFile public static void LoadResourceFile<T>(T file, byte[] data, uint ver) where T : class, PackedFile
{ {
//direct load from a raw, compressed resource file (openIV-compatible format) //direct load from a raw, compressed resource file (openIV-compatible format)
@ -695,9 +789,6 @@ namespace CodeWalker.GameFiles
oldresentry.SystemFlags = resentry.SystemFlags; oldresentry.SystemFlags = resentry.SystemFlags;
oldresentry.GraphicsFlags = resentry.GraphicsFlags; oldresentry.GraphicsFlags = resentry.GraphicsFlags;
resentry.Name = oldresentry.Name; resentry.Name = oldresentry.Name;
resentry.NameHash = oldresentry.NameHash;
resentry.NameLower = oldresentry.NameLower;
resentry.ShortNameHash = oldresentry.ShortNameHash;
} }
else else
{ {
@ -710,6 +801,45 @@ namespace CodeWalker.GameFiles
file.Load(data, resentry); file.Load(data, resentry);
} }
public static RpfResourceFileEntry CreateResourceFileEntry(Stream stream, uint ver, uint? header = null)
{
var resentry = new RpfResourceFileEntry();
using var reader = new BinaryReader(stream, Encoding.UTF8, true);
//hopefully this data has an RSC7 header...
uint rsc7 = header ?? reader.ReadUInt32(); // BitConverter.ToUInt32(data, 0);
if (rsc7 == 0x37435352) //RSC7 header present!
{
int version = reader.ReadInt32(); // BitConverter.ToInt32(data, 4);//use this instead of what was given...
resentry.SystemFlags = reader.ReadUInt32();// BitConverter.ToUInt32(data, 8);
resentry.GraphicsFlags = reader.ReadUInt32(); //BitConverter.ToUInt32(data, 12);
//if (stream.Length > 16)
//{
// int newlen = stream.Length - 16; //trim the header from the data passed to the next step.
// byte[] newdata = new byte[newlen];
// Buffer.BlockCopy(data, 16, newdata, 0, newlen);
// data = newdata;
//}
//else
//{
// data = null; //shouldn't happen... empty..
//}
}
else
{
//direct load from file without the rpf header..
//assume it's in resource meta format
resentry.SystemFlags = RpfResourceFileEntry.GetFlagsFromSize((int)stream.Length, 0);
resentry.GraphicsFlags = RpfResourceFileEntry.GetFlagsFromSize(0, ver);
}
resentry.Name = "";
return resentry;
}
public static RpfResourceFileEntry CreateResourceFileEntry(ref byte[] data, uint ver) public static RpfResourceFileEntry CreateResourceFileEntry(ref byte[] data, uint ver)
{ {
var resentry = new RpfResourceFileEntry(); var resentry = new RpfResourceFileEntry();
@ -742,7 +872,6 @@ namespace CodeWalker.GameFiles
} }
resentry.Name = ""; resentry.Name = "";
resentry.NameLower = "";
return resentry; return resentry;
} }
@ -896,14 +1025,14 @@ namespace CodeWalker.GameFiles
public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
public byte[] DecompressBytes(byte[] bytes) public byte[] DecompressBytes(byte[] bytes)
{ {
try try
{ {
using (DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress)) using (DeflateStream ds = new DeflateStream(new MemoryStream(bytes), CompressionMode.Decompress))
{ {
using (var outstr = new MemoryStream()) using (var outstr = recyclableMemoryStreamManager.GetStream("DecompressBytes", bytes.Length))
{ {
ds.CopyTo(outstr); ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer(); byte[] deflated = outstr.GetBuffer();
@ -929,7 +1058,7 @@ namespace CodeWalker.GameFiles
} }
public static byte[] CompressBytes(byte[] data) //TODO: refactor this with ResourceBuilder.Compress/Decompress public static byte[] CompressBytes(byte[] data) //TODO: refactor this with ResourceBuilder.Compress/Decompress
{ {
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = recyclableMemoryStreamManager.GetStream("CompressBytes", data.Length))
{ {
using (var ds = new DeflateStream(ms, CompressionMode.Compress, true)) using (var ds = new DeflateStream(ms, CompressionMode.Compress, true))
{ {
@ -1023,7 +1152,6 @@ namespace CodeWalker.GameFiles
Root = new RpfDirectoryEntry(); Root = new RpfDirectoryEntry();
Root.File = this; Root.File = this;
Root.Name = string.Empty; Root.Name = string.Empty;
Root.NameLower = string.Empty;
Root.Path = Path.ToLowerInvariant(); Root.Path = Path.ToLowerInvariant();
} }
if (Children == null) if (Children == null)
@ -1397,7 +1525,7 @@ namespace CodeWalker.GameFiles
file.Path = dir.Path + "\\" + file.NameLower; file.Path = dir.Path + "\\" + file.NameLower;
RpfBinaryFileEntry binf = file as RpfBinaryFileEntry; RpfBinaryFileEntry binf = file as RpfBinaryFileEntry;
if ((binf != null) && file.NameLower.EndsWith(".rpf")) if ((binf != null) && file.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
{ {
RpfFile childrpf = FindChildArchive(binf); RpfFile childrpf = FindChildArchive(binf);
if (childrpf != null) if (childrpf != null)
@ -1413,7 +1541,7 @@ namespace CodeWalker.GameFiles
} }
foreach (var subdir in dir.Directories) foreach (var subdir in dir.Directories)
{ {
subdir.Path = dir.Path + "\\" + subdir.NameLower; subdir.Path = dir.Path + "\\" + subdir.Name;
UpdatePaths(subdir); UpdatePaths(subdir);
} }
} }
@ -1531,9 +1659,6 @@ namespace CodeWalker.GameFiles
entry.File = parent; entry.File = parent;
entry.Path = rpath; entry.Path = rpath;
entry.Name = name; entry.Name = name;
entry.NameLower = namel;
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
dir.Files.Add(entry); dir.Files.Add(entry);
@ -1574,13 +1699,10 @@ namespace CodeWalker.GameFiles
entry.File = parent; entry.File = parent;
entry.Path = rpath; entry.Path = rpath;
entry.Name = name; entry.Name = name;
entry.NameLower = namel;
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
foreach (var exdir in dir.Directories) foreach (var exdir in dir.Directories)
{ {
if (exdir.NameLower == entry.NameLower) if (exdir.Name.Equals(entry.Name, StringComparison.OrdinalIgnoreCase))
{ {
throw new Exception("RPF Directory \"" + entry.Name + "\" already exists!"); throw new Exception("RPF Directory \"" + entry.Name + "\" already exists!");
} }
@ -1600,14 +1722,61 @@ namespace CodeWalker.GameFiles
return entry; return entry;
} }
public static RpfFileEntry CreateFileEntry(string name, string path, Stream data)
{
//this should only really be used when loading a file from the filesystem.
RpfFileEntry e;
using var reader = new BinaryReader(data, Encoding.UTF8, true);
uint rsc7 = (reader.BaseStream.Length > 4) ? reader.ReadUInt32() : 0;
if (rsc7 == 0x37435352) //RSC7 header present! create RpfResourceFileEntry and decompress data...
{
e = RpfFile.CreateResourceFileEntry(data, 0);//"version" should be loadable from the header in the data..
data = ResourceBuilder.Decompress(data);
}
else
{
var be = new RpfBinaryFileEntry
{
FileSize = (uint)(data?.Length ?? 0),
FileUncompressedSize = (uint)(data?.Length ?? 0),
};
e = be;
}
e.Name = name;
e.Path = path;
return e;
}
public static RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data)
{
//this should only really be used when loading a file from the filesystem.
RpfFileEntry e = null;
uint rsc7 = (data?.Length > 4) ? BitConverter.ToUInt32(data, 0) : 0;
if (rsc7 == 0x37435352) //RSC7 header present! create RpfResourceFileEntry and decompress data...
{
e = RpfFile.CreateResourceFileEntry(ref data, 0);//"version" should be loadable from the header in the data..
data = ResourceBuilder.Decompress(data);
}
else
{
var be = new RpfBinaryFileEntry();
be.FileSize = (uint)data?.Length;
be.FileUncompressedSize = be.FileSize;
e = be;
}
e.Name = name;
e.Path = path;
return e;
}
public static RpfFileEntry CreateFile(RpfDirectoryEntry dir, string name, byte[] data, bool overwrite = true) public static RpfFileEntry CreateFile(RpfDirectoryEntry dir, string name, byte[] data, bool overwrite = true)
{ {
string namel = name.ToLowerInvariant();
if (overwrite) if (overwrite)
{ {
foreach (var exfile in dir.Files) foreach (var exfile in dir.Files)
{ {
if (exfile.NameLower == namel) if (exfile.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{ {
//file already exists. delete the existing one first! //file already exists. delete the existing one first!
//this should probably be optimised to just replace the existing one... //this should probably be optimised to just replace the existing one...
@ -1621,6 +1790,7 @@ namespace CodeWalker.GameFiles
RpfFile parent = dir.File; RpfFile parent = dir.File;
string fpath = parent.GetPhysicalFilePath(); string fpath = parent.GetPhysicalFilePath();
string namel = name.ToLowerInvariant();
string rpath = dir.Path + "\\" + namel; string rpath = dir.Path + "\\" + namel;
if (!File.Exists(fpath)) if (!File.Exists(fpath))
{ {
@ -1644,7 +1814,6 @@ namespace CodeWalker.GameFiles
{ {
//RSC header is present... import as resource //RSC header is present... import as resource
var rentry = new RpfResourceFileEntry(); var rentry = new RpfResourceFileEntry();
var version = BitConverter.ToUInt32(data, 4);
rentry.SystemFlags = BitConverter.ToUInt32(data, 8); rentry.SystemFlags = BitConverter.ToUInt32(data, 8);
rentry.GraphicsFlags = BitConverter.ToUInt32(data, 12); rentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
rentry.FileSize = len; rentry.FileSize = len;
@ -1661,11 +1830,11 @@ namespace CodeWalker.GameFiles
entry = rentry; entry = rentry;
} }
if (namel.EndsWith(".rpf") && (hdr == 0x52504637)) //'RPF7' if ((hdr == 0x52504637) && name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)) //'RPF7'
{ {
isrpf = true; isrpf = true;
} }
if (namel.EndsWith(".awc")) if (name.EndsWith(".awc", StringComparison.OrdinalIgnoreCase))
{ {
isawc = true; isawc = true;
} }
@ -1693,16 +1862,14 @@ namespace CodeWalker.GameFiles
entry.File = parent; entry.File = parent;
entry.Path = rpath; entry.Path = rpath;
entry.Name = name; entry.Name = name;
entry.NameLower = name.ToLowerInvariant(); entry.NameLower = namel;
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
foreach (var exfile in dir.Files) foreach (var exfile in dir.Files)
{ {
if (exfile.NameLower == entry.NameLower) if (exfile.Name.Equals(entry.Name, StringComparison.OrdinalIgnoreCase))
{ {
throw new Exception("File \"" + entry.Name + "\" already exists!"); throw new Exception("File \"" + entry.Name + "\" already exists!");
} }
@ -1715,8 +1882,7 @@ namespace CodeWalker.GameFiles
using (var fstream = File.Open(fpath, FileMode.Open, FileAccess.ReadWrite)) using (var fstream = File.Open(fpath, FileMode.Open, FileAccess.ReadWrite))
{ {
using (var bw = new BinaryWriter(fstream)) using var bw = new BinaryWriter(fstream);
{
parent.InsertFileSpace(bw, entry); parent.InsertFileSpace(bw, entry);
long bbeg = parent.StartPos + (entry.FileOffset * 512); long bbeg = parent.StartPos + (entry.FileOffset * 512);
long bend = bbeg + (GetBlockCount(entry.GetFileSize()) * 512); long bend = bbeg + (GetBlockCount(entry.GetFileSize()) * 512);
@ -1724,7 +1890,6 @@ namespace CodeWalker.GameFiles
fstream.Write(data, 0, data.Length); fstream.Write(data, 0, data.Length);
WritePadding(fstream, bend); //write 0's until the end of the block. WritePadding(fstream, bend); //write 0's until the end of the block.
} }
}
if (isrpf) if (isrpf)
@ -1736,15 +1901,11 @@ namespace CodeWalker.GameFiles
file.StartPos = parent.StartPos + (entry.FileOffset * 512); file.StartPos = parent.StartPos + (entry.FileOffset * 512);
parent.Children.Add(file); parent.Children.Add(file);
using (var fstream = File.OpenRead(fpath)) using var fstream = File.OpenRead(fpath);
{ using var br = new BinaryReader(fstream);
using (var br = new BinaryReader(fstream))
{
fstream.Position = file.StartPos; fstream.Position = file.StartPos;
file.ScanStructure(br, null, null); file.ScanStructure(br, null, null);
} }
}
}
return entry; return entry;
} }
@ -1756,7 +1917,6 @@ namespace CodeWalker.GameFiles
//(since all the paths are generated at runtime and not stored) //(since all the paths are generated at runtime and not stored)
file.Name = newname; file.Name = newname;
file.NameLower = newname.ToLowerInvariant();
file.Path = GetParentPath(file.Path) + newname; file.Path = GetParentPath(file.Path) + newname;
file.FilePath = GetParentPath(file.FilePath) + newname; file.FilePath = GetParentPath(file.FilePath) + newname;
@ -1772,13 +1932,9 @@ namespace CodeWalker.GameFiles
string dirpath = GetParentPath(entry.Path); string dirpath = GetParentPath(entry.Path);
entry.Name = newname; entry.Name = newname;
entry.NameLower = newname.ToLowerInvariant();
entry.Path = dirpath + newname; entry.Path = dirpath + newname;
string sname = entry.GetShortNameLower(); JenkIndex.EnsureLower(entry.ShortName);//could be anything... but it needs to be there
JenkIndex.Ensure(sname);//could be anything... but it needs to be there
entry.NameHash = JenkHash.GenHash(newname);
entry.ShortNameHash = JenkHash.GenHash(sname);
RpfFile parent = entry.File; RpfFile parent = entry.File;
string fpath = parent.GetPhysicalFilePath(); string fpath = parent.GetPhysicalFilePath();
@ -2011,7 +2167,7 @@ namespace CodeWalker.GameFiles
} }
if (!dirpath.EndsWith("\\")) if (!dirpath.EndsWith("\\"))
{ {
dirpath = dirpath + "\\"; dirpath += "\\";
} }
return dirpath; return dirpath;
} }
@ -2038,16 +2194,89 @@ namespace CodeWalker.GameFiles
public RpfFile File { get; set; } public RpfFile File { get; set; }
public RpfDirectoryEntry Parent { get; set; } public RpfDirectoryEntry Parent { get; set; }
public uint NameHash { get; set; } public uint NameHash { get
public uint ShortNameHash { get; set; } {
if (nameHash == 0 && !string.IsNullOrEmpty(Name))
{
nameHash = JenkHash.GenHashLower(Name);
}
return nameHash;
}
set
{
nameHash = value;
}
}
public uint ShortNameHash { get
{
if (shortNameHash == 0 && !string.IsNullOrEmpty(ShortName))
{
shortNameHash = JenkHash.GenHashLower(ShortName);
}
return shortNameHash;
}
set
{
shortNameHash = value;
}
}
public uint NameOffset { get; set; } public uint NameOffset { get; set; }
public string Name { get; set; } public string Name { get => name; set
public string NameLower { get; set; } {
if (name == value)
{
return;
}
name = value;
nameLower = null;
nameHash = 0;
shortNameHash = 0;
shortName = null;
}
}
public string NameLower
{
get
{
return nameLower ??= Name?.ToLowerInvariant();
}
set { nameLower = value; }
}
public string ShortName {
get
{
if (string.IsNullOrEmpty(shortName) && !string.IsNullOrEmpty(Name))
{
int ind = Name.LastIndexOf('.');
if (ind > 0)
{
shortName = Name.Substring(0, ind);
}
else
{
shortName = Name;
}
}
return shortName;
}
set
{
shortName = value;
}
}
public string Path { get; set; } public string Path { get; set; }
public uint H1; //first 2 header values from RPF table... public uint H1; //first 2 header values from RPF table...
public uint H2; public uint H2;
private string name;
private string nameLower;
private uint shortNameHash;
private uint nameHash;
private string shortName;
public abstract void Read(DataReader reader); public abstract void Read(DataReader reader);
public abstract void Write(DataWriter writer); public abstract void Write(DataWriter writer);
@ -2059,29 +2288,12 @@ namespace CodeWalker.GameFiles
public string GetShortName() public string GetShortName()
{ {
int ind = Name.LastIndexOf('.'); return ShortName;
if (ind > 0)
{
return Name.Substring(0, ind);
}
return Name;
}
public string GetShortNameLower()
{
if (NameLower == null)
{
NameLower = Name.ToLowerInvariant();
}
int ind = NameLower.LastIndexOf('.');
if (ind > 0)
{
return NameLower.Substring(0, ind);
}
return NameLower;
} }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public class RpfDirectoryEntry : RpfEntry [TypeConverter(typeof(ExpandableObjectConverter))]
public class RpfDirectoryEntry : RpfEntry
{ {
public uint EntriesIndex { get; set; } public uint EntriesIndex { get; set; }
public uint EntriesCount { get; set; } public uint EntriesCount { get; set; }
@ -2113,7 +2325,8 @@ namespace CodeWalker.GameFiles
} }
} }
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class RpfFileEntry : RpfEntry [TypeConverter(typeof(ExpandableObjectConverter))]
public abstract class RpfFileEntry : RpfEntry
{ {
public uint FileOffset { get; set; } public uint FileOffset { get; set; }
public uint FileSize { get; set; } public uint FileSize { get; set; }
@ -2144,7 +2357,7 @@ namespace CodeWalker.GameFiles
case 0: IsEncrypted = false; break; case 0: IsEncrypted = false; break;
case 1: IsEncrypted = true; break; case 1: IsEncrypted = true; break;
default: default:
throw new Exception("Error in RPF7 file entry."); throw new Exception($"Error in RPF7 file entry. {EncryptionType}");
} }
} }
@ -2516,14 +2729,15 @@ namespace CodeWalker.GameFiles
public override void Read(DataReader reader) public override void Read(DataReader reader)
{ {
var buffer = ArrayPool<byte>.Shared.Rent(3);
NameOffset = reader.ReadUInt16(); NameOffset = reader.ReadUInt16();
var buf1 = reader.ReadBytes(3); reader.ReadBytes(3, buffer);
FileSize = (uint)buf1[0] + (uint)(buf1[1] << 8) + (uint)(buf1[2] << 16); FileSize = (uint)buffer[0] + (uint)(buffer[1] << 8) + (uint)(buffer[2] << 16);
var buf2 = reader.ReadBytes(3);
FileOffset = ((uint)buf2[0] + (uint)(buf2[1] << 8) + (uint)(buf2[2] << 16)) & 0x7FFFFF;
reader.ReadBytes(3, buffer);
FileOffset = ((uint)buffer[0] + (uint)(buffer[1] << 8) + (uint)(buffer[2] << 16)) & 0x7FFFFF;
ArrayPool<byte>.Shared.Return(buffer);
SystemFlags = reader.ReadUInt32(); SystemFlags = reader.ReadUInt32();
GraphicsFlags = reader.ReadUInt32(); GraphicsFlags = reader.ReadUInt32();
@ -2751,6 +2965,10 @@ namespace CodeWalker.GameFiles
void Load(byte[] data, RpfFileEntry entry); void Load(byte[] data, RpfFileEntry entry);
} }
public interface PackedFileStream : PackedFile
{
void Load(Stream stream, RpfFileEntry entry);
}

View File

@ -1,5 +1,8 @@
using System; using CodeWalker.Core.Utils;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -10,14 +13,19 @@ namespace CodeWalker.GameFiles
{ {
public class RpfManager public class RpfManager
{ {
private static RpfManager _instance = new RpfManager();
public static RpfManager GetInstance()
{
return _instance ??= new RpfManager();
}
//for caching and management of RPF file data. //for caching and management of RPF file data.
public string Folder { get; private set; } public string Folder { get; private set; }
public string[] ExcludePaths { get; set; } public string[] ExcludePaths { get; set; }
public bool EnableMods { get; set; } public bool EnableMods { get; set; }
public bool BuildExtendedJenkIndex { get; set; } = true; public bool BuildExtendedJenkIndex { get; set; } = true;
public Action<string> UpdateStatus { get; private set; } public event Action<string> UpdateStatus;
public Action<string> ErrorLog { get; private set; } public event Action<string> ErrorLog;
public List<RpfFile> BaseRpfs { get; private set; } public List<RpfFile> BaseRpfs { get; private set; }
public List<RpfFile> ModRpfs { get; private set; } public List<RpfFile> ModRpfs { get; private set; }
@ -34,8 +42,9 @@ namespace CodeWalker.GameFiles
public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true) public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true)
{ {
UpdateStatus = updateStatus; using var _ = new DisposableTimer("RpfManager.Init");
ErrorLog = errorLog; UpdateStatus += updateStatus;
ErrorLog += errorLog;
string replpath = folder + "\\"; string replpath = folder + "\\";
var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories; var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
@ -52,7 +61,8 @@ namespace CodeWalker.GameFiles
ModRpfDict = new Dictionary<string, RpfFile>(); ModRpfDict = new Dictionary<string, RpfFile>();
ModEntryDict = new Dictionary<string, RpfEntry>(); ModEntryDict = new Dictionary<string, RpfEntry>();
foreach (string rpfpath in allfiles) var rpfs = new ConcurrentBag<RpfFile>();
Parallel.ForEach(allfiles, (rpfpath) =>
{ {
try try
{ {
@ -69,37 +79,66 @@ namespace CodeWalker.GameFiles
break; break;
} }
} }
if (excl) continue; //skip files in exclude paths. if (excl) return; //skip files in exclude paths.
} }
rf.ScanStructure(updateStatus, errorLog); rf.ScanStructure(updateStatus, errorLog);
if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF) if (rf.LastException != null) //incase of corrupted rpf (or renamed NG encrypted RPF)
{ {
continue; return;
} }
AddRpfFile(rf, false, false); rpfs.Add(rf);
} }
catch (Exception ex) catch (Exception ex)
{ {
errorLog(rpfpath + ": " + ex.ToString()); errorLog(rpfpath + ": " + ex.ToString());
} }
});
var calculateSum = (RpfFile rpf) => { return 0; };
calculateSum = (RpfFile rpf) =>
{
return rpf.AllEntries?.Count ?? 0 + rpf.Children?.Sum(calculateSum) ?? 0;
};
var minCapacity = rpfs.Sum(calculateSum);
if (minCapacity > AllRpfs.Capacity)
{
AllRpfs.Capacity = minCapacity;
}
foreach(var rpf in rpfs)
{
AddRpfFile(rpf, false, false);
} }
if (buildIndex) if (buildIndex)
{ {
updateStatus("Building jenkindex..."); updateStatus?.Invoke("Building jenkindex...");
Task.Run(() =>
{
BuildBaseJenkIndex(); BuildBaseJenkIndex();
IsInited = true;
});
updateStatus?.Invoke("Scan complete");
}
else
{
updateStatus?.Invoke("Scan complete");
IsInited = true;
} }
updateStatus("Scan complete");
IsInited = true;
} }
public void Init(List<RpfFile> allRpfs) public void Init(List<RpfFile> allRpfs)
{ {
using var _ = new DisposableTimer("RpfManager.Init");
//fast init used by RPF explorer's File cache //fast init used by RPF explorer's File cache
AllRpfs = allRpfs; AllRpfs = allRpfs;
@ -122,9 +161,11 @@ namespace CodeWalker.GameFiles
} }
} }
Task.Run(() =>
{
BuildBaseJenkIndex(); BuildBaseJenkIndex();
IsInited = true; IsInited = true;
});
} }
@ -182,25 +223,13 @@ namespace CodeWalker.GameFiles
EntryDict[entry.Path] = entry; EntryDict[entry.Path] = entry;
} }
if (entry is RpfFileEntry)
{
RpfFileEntry fentry = entry as RpfFileEntry;
entry.NameHash = JenkHash.GenHash(entry.NameLower);
int ind = entry.NameLower.LastIndexOf('.');
entry.ShortNameHash = (ind > 0) ? JenkHash.GenHash(entry.NameLower.Substring(0, ind)) : entry.NameHash;
if (entry.ShortNameHash != 0)
{
//EntryHashDict[entry.ShortNameHash] = entry;
}
}
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
file.LastError = ex.ToString(); file.LastError = ex.ToString();
file.LastException = ex; file.LastException = ex;
ErrorLog(entry.Path + ": " + ex.ToString()); ErrorLog?.Invoke(entry.Path + ": " + ex.ToString());
} }
} }
} }
@ -348,12 +377,12 @@ namespace CodeWalker.GameFiles
public void BuildBaseJenkIndex() public void BuildBaseJenkIndex()
{ {
JenkIndex.Clear(); using var _ = new DisposableTimer("BuildBaseJenkIndex");
StringBuilder sb = new StringBuilder(); Parallel.ForEach(AllRpfs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (file) =>
foreach (RpfFile file in AllRpfs)
{ {
try try
{ {
StringBuilder sb = new StringBuilder();
JenkIndex.Ensure(file.Name); JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries) foreach (RpfEntry entry in file.AllEntries)
{ {
@ -364,6 +393,7 @@ namespace CodeWalker.GameFiles
int ind = nlow.LastIndexOf('.'); int ind = nlow.LastIndexOf('.');
if (ind > 0) if (ind > 0)
{ {
JenkIndex.Ensure(entry.Name.Substring(0, ind)); JenkIndex.Ensure(entry.Name.Substring(0, ind));
JenkIndex.Ensure(nlow.Substring(0, ind)); JenkIndex.Ensure(nlow.Substring(0, ind));
@ -485,11 +515,16 @@ namespace CodeWalker.GameFiles
} }
} }
catch catch(Exception err)
{ {
ErrorLog?.Invoke(err.ToString());
//failing silently!! not so good really //failing silently!! not so good really
} }
} });
//foreach (RpfFile file in AllRpfs)
//{
//}
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {

View File

@ -105,6 +105,20 @@ using System.Threading.Tasks;
namespace CodeWalker.Utils namespace CodeWalker.Utils
{ {
public class AssertionFailedException : Exception
{
public AssertionFailedException() : base("Assertion failed")
{
}
public AssertionFailedException(string message) : base(message)
{
}
public AssertionFailedException(string message, Exception inner) : base(message, inner)
{
}
}
public static class DDSIO public static class DDSIO
{ {
@ -176,6 +190,7 @@ namespace CodeWalker.Utils
swaprb = false; swaprb = false;
break; break;
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: // TextureFormat.D3DFMT_A8B8G8R8 case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM: // TextureFormat.D3DFMT_A8B8G8R8
case DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_TYPELESS:
px = imgdata; px = imgdata;
break; break;
case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: // TextureFormat.D3DFMT_L8 case DXGI_FORMAT.DXGI_FORMAT_R8_UNORM: // TextureFormat.D3DFMT_L8
@ -221,8 +236,8 @@ namespace CodeWalker.Utils
MemoryStream ms = new MemoryStream(); using MemoryStream ms = new MemoryStream(texture.Data.FullData.Length + 128);
BinaryWriter bw = new BinaryWriter(ms); using BinaryWriter bw = new BinaryWriter(ms);
int nimages = img.MipMapLevels; int nimages = img.MipMapLevels;
@ -353,8 +368,8 @@ namespace CodeWalker.Utils
throw new Exception("Unsupported texture dimension"); throw new Exception("Unsupported texture dimension");
} }
byte[] buff = ms.GetBuffer(); byte[] buff = ms.GetBuffer();
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer for File.WriteAllBytes(). byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer for File.WriteAllBytes().
Array.Copy(buff, outbuf, outbuf.Length); Array.Copy(buff, outbuf, outbuf.Length);
@ -544,8 +559,8 @@ namespace CodeWalker.Utils
int add = 0; int add = 0;
for (int i = 0; i < img.MipMapLevels; i++) for (int i = 0; i < img.MipMapLevels; i++)
{ {
images[i].width = img.Width / div; images[i].width = Math.Max(img.Width / div, 1);
images[i].height = img.Height / div; images[i].height = Math.Max(img.Height / div, 1);
images[i].format = format; //(DXGI_FORMAT)img.Format; images[i].format = format; //(DXGI_FORMAT)img.Format;
images[i].pixels = buf + add; images[i].pixels = buf + add;
@ -773,7 +788,7 @@ namespace CodeWalker.Utils
break; break;
default: default:
assert(IsValid(fmt)); assert(IsValid(fmt), $"{fmt} is not a valid texture format");
assert(!IsCompressed(fmt) && !IsPacked(fmt) && !IsPlanar(fmt)); assert(!IsCompressed(fmt) && !IsPacked(fmt) && !IsPlanar(fmt));
{ {
@ -904,11 +919,17 @@ namespace CodeWalker.Utils
} }
} }
public static void assert(bool b) public static void assert(bool b, string message = null)
{ {
if (!b) if (!b)
{ {
throw new Exception(); if (message is null)
{
throw new AssertionFailedException();
} else
{
throw new AssertionFailedException(message);
}
} }
} }

View File

@ -26,11 +26,9 @@
using SharpDX; using SharpDX;
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
@ -54,10 +52,12 @@ namespace CodeWalker.GameFiles
String = 9, String = 9,
} }
public class DataReader public class DataReader : IDisposable
{ {
private Stream baseStream; private Stream baseStream;
private readonly byte[] _buffer = new byte[8];
/// <summary> /// <summary>
/// Gets or sets the endianess of the underlying stream. /// Gets or sets the endianess of the underlying stream.
/// </summary> /// </summary>
@ -106,15 +106,15 @@ namespace CodeWalker.GameFiles
/// Reads data from the underlying stream. This is the only method that directly accesses /// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream. /// the data in the underlying stream.
/// </summary> /// </summary>
protected virtual byte[] ReadFromStream(int count, bool ignoreEndianess = false) protected virtual byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[] buffer = null)
{ {
var buffer = new byte[count]; buffer ??= new byte[count];
baseStream.Read(buffer, 0, count); baseStream.Read(buffer, 0, count);
// handle endianess // handle endianess
if (!ignoreEndianess && (Endianess == Endianess.BigEndian)) if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
{ {
Array.Reverse(buffer); Array.Reverse(buffer, 0, count);
} }
return buffer; return buffer;
@ -123,17 +123,22 @@ namespace CodeWalker.GameFiles
/// <summary> /// <summary>
/// Reads a byte. /// Reads a byte.
/// </summary> /// </summary>
public byte ReadByte() public virtual byte ReadByte()
{ {
return ReadFromStream(1)[0]; var result = baseStream.ReadByte();
if (result == -1)
{
throw new InvalidOperationException("Tried to read from stream beyond end!");
}
return (byte) result;
} }
/// <summary> /// <summary>
/// Reads a sequence of bytes. /// Reads a sequence of bytes.
/// </summary> /// </summary>
public byte[] ReadBytes(int count) public byte[] ReadBytes(int count, byte[] buffer = null)
{ {
return ReadFromStream(count, true); return ReadFromStream(count, true, buffer);
} }
/// <summary> /// <summary>
@ -141,7 +146,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public short ReadInt16() public short ReadInt16()
{ {
return BitConverter.ToInt16(ReadFromStream(2), 0); return BitConverter.ToInt16(ReadFromStream(2, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -149,7 +154,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public int ReadInt32() public int ReadInt32()
{ {
return BitConverter.ToInt32(ReadFromStream(4), 0); return BitConverter.ToInt32(ReadFromStream(4, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -157,7 +162,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public long ReadInt64() public long ReadInt64()
{ {
return BitConverter.ToInt64(ReadFromStream(8), 0); return BitConverter.ToInt64(ReadFromStream(8, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -165,7 +170,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public ushort ReadUInt16() public ushort ReadUInt16()
{ {
return BitConverter.ToUInt16(ReadFromStream(2), 0); return BitConverter.ToUInt16(ReadFromStream(2, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -173,7 +178,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public uint ReadUInt32() public uint ReadUInt32()
{ {
return BitConverter.ToUInt32(ReadFromStream(4), 0); return BitConverter.ToUInt32(ReadFromStream(4, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -181,7 +186,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public ulong ReadUInt64() public ulong ReadUInt64()
{ {
return BitConverter.ToUInt64(ReadFromStream(8), 0); return BitConverter.ToUInt64(ReadFromStream(8, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -189,7 +194,7 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public float ReadSingle() public float ReadSingle()
{ {
return BitConverter.ToSingle(ReadFromStream(4), 0); return BitConverter.ToSingle(ReadFromStream(4, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
@ -197,23 +202,51 @@ namespace CodeWalker.GameFiles
/// </summary> /// </summary>
public double ReadDouble() public double ReadDouble()
{ {
return BitConverter.ToDouble(ReadFromStream(8), 0); return BitConverter.ToDouble(ReadFromStream(8, buffer: _buffer), 0);
} }
/// <summary> /// <summary>
/// Reads a string. /// Reads a string.
/// </summary> /// </summary>
public string ReadString() unsafe public string ReadString(int maxLength = 1024)
{ {
var bytes = new List<byte>(); var bytes = stackalloc byte[Math.Min(maxLength, 1024)];
var temp = ReadFromStream(1)[0]; var temp = ReadByte();
while (temp != 0) var charsRead = 0;
while (temp != 0 && (Length == -1 || Position <= Length))
{ {
bytes.Add(temp); if (charsRead > 1023)
temp = ReadFromStream(1)[0]; {
throw new Exception("String too long!");
}
if (charsRead < maxLength)
{
bytes[charsRead] = temp;
}
temp = ReadByte();
charsRead++;
} }
return Encoding.UTF8.GetString(bytes.ToArray()); 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);
} }
@ -279,12 +312,13 @@ namespace CodeWalker.GameFiles
} }
} }
public virtual void Dispose()
{
baseStream?.Dispose();
}
} }
public class DataWriter public class DataWriter : IDisposable
{ {
private Stream baseStream; private Stream baseStream;
@ -476,6 +510,15 @@ namespace CodeWalker.GameFiles
Write(value.M44); Write(value.M44);
} }
public virtual void Dispose()
{
baseStream.Dispose();
}
public virtual void Close()
{
baseStream.Close();
}
} }

View File

@ -23,8 +23,12 @@
//shamelessly stolen //shamelessly stolen
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -34,7 +38,10 @@ namespace CodeWalker.GameFiles
public class GTACrypto public class GTACrypto
{ {
public static byte[] DecryptAES(byte[] data, int length)
{
return DecryptAESData(data, GTA5Keys.PC_AES_KEY, length, 1);
}
public static byte[] DecryptAES(byte[] data) public static byte[] DecryptAES(byte[] data)
{ {
return DecryptAESData(data, GTA5Keys.PC_AES_KEY); return DecryptAESData(data, GTA5Keys.PC_AES_KEY);
@ -45,6 +52,11 @@ namespace CodeWalker.GameFiles
} }
public static byte[] DecryptAESData(byte[] data, byte[] key, int rounds = 1) public static byte[] DecryptAESData(byte[] data, byte[] key, int rounds = 1)
{
return DecryptAESData(data, key, data.Length, rounds);
}
public static byte[] DecryptAESData(byte[] data, byte[] key, int length, int rounds = 1)
{ {
var rijndael = Rijndael.Create(); var rijndael = Rijndael.Create();
rijndael.KeySize = 256; rijndael.KeySize = 256;
@ -53,18 +65,17 @@ namespace CodeWalker.GameFiles
rijndael.Mode = CipherMode.ECB; rijndael.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.None; rijndael.Padding = PaddingMode.None;
var buffer = (byte[])data.Clone(); length = length - length % 16;
var length = data.Length - data.Length % 16;
// decrypt... // decrypt...
if (length > 0) if (length > 0)
{ {
var decryptor = rijndael.CreateDecryptor(); var decryptor = rijndael.CreateDecryptor();
for (var roundIndex = 0; roundIndex < rounds; roundIndex++) for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
decryptor.TransformBlock(buffer, 0, length, buffer, 0); decryptor.TransformBlock(data, 0, length, data, 0);
} }
return buffer; return data;
} }
public static byte[] EncryptAESData(byte[] data, byte[] key, int rounds = 1) public static byte[] EncryptAESData(byte[] data, byte[] key, int rounds = 1)
{ {
@ -93,7 +104,7 @@ namespace CodeWalker.GameFiles
public static byte[] GetNGKey(string name, uint length) public static uint[][] GetNGKey(string name, uint length)
{ {
uint hash = GTA5Hash.CalculateHash(name); uint hash = GTA5Hash.CalculateHash(name);
uint keyidx = (hash + length + (101 - 40)) % 0x65; uint keyidx = (hash + length + (101 - 40)) % 0x65;
@ -101,63 +112,61 @@ namespace CodeWalker.GameFiles
} }
public static byte[] DecryptNG(byte[] data, string name, uint length) public static byte[] DecryptNG(byte[] data, string name, uint fileSize, int offset = 0, int? length = null)
{ {
byte[] key = GetNGKey(name, length); var key = GetNGKey(name, fileSize);
return DecryptNG(data, key); return DecryptNG(data, key, offset, length);
} }
public static byte[] DecryptNG(byte[] data, byte[] key) public unsafe static byte[] DecryptNG(byte[] data, uint[][] key, int offset = 0, int? length = null)
{ {
var decryptedData = new byte[data.Length]; length ??= data.Length;
fixed (byte* bptr = data)
var keyuints = new uint[key.Length / 4];
Buffer.BlockCopy(key, 0, keyuints, 0, key.Length);
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
{ {
var encryptedBlock = new byte[16]; for (int blockIndex = offset * 16; blockIndex < length / 16; blockIndex++)
Array.Copy(data, 16 * blockIndex, encryptedBlock, 0, 16); {
var decryptedBlock = DecryptNGBlock(encryptedBlock, keyuints); DecryptNGBlock(bptr + 16 * blockIndex, key);
Array.Copy(decryptedBlock, 0, decryptedData, 16 * blockIndex, 16); }
} }
if (data.Length % 16 != 0) return data;
{
var left = data.Length % 16;
Buffer.BlockCopy(data, data.Length - left, decryptedData, data.Length - left, left);
} }
return decryptedData; public unsafe static void DecryptNGBlock(byte[] data, uint[][] key)
{
fixed(byte* bptr = data)
{
DecryptNGBlock(data, key);
}
} }
public static byte[] DecryptNGBlock(byte[] data, uint[] key) public unsafe static void DecryptNGBlock(byte* data, uint[][] key)
{ {
var buffer = data; DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
DecryptNGRoundA(data, key[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
// prepare key...
var subKeys = new uint[17][];
for (int i = 0; i < 17; i++)
{
subKeys[i] = new uint[4];
subKeys[i][0] = key[4 * i + 0];
subKeys[i][1] = key[4 * i + 1];
subKeys[i][2] = key[4 * i + 2];
subKeys[i][3] = key[4 * i + 3];
}
buffer = DecryptNGRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
buffer = DecryptNGRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
for (int k = 2; k <= 15; k++) for (int k = 2; k <= 15; k++)
buffer = DecryptNGRoundB(buffer, subKeys[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]); DecryptNGRoundB(data, key[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]);
buffer = DecryptNGRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]); DecryptNGRoundA(data, key[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
return buffer;
} }
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 // round 1,2,16
public static byte[] DecryptNGRoundA(byte[] data, uint[] key, uint[][] table) public unsafe static void DecryptNGRoundA(byte* data, uint[] key, uint[][] table)
{ {
var x1 = var x1 =
table[0][data[0]] ^ table[0][data[0]] ^
@ -184,18 +193,14 @@ namespace CodeWalker.GameFiles
table[15][data[15]] ^ table[15][data[15]] ^
key[3]; key[3];
var result = new byte[16]; *(uint*)data = x1;
Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4); *(uint*)(data + 4) = x2;
Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4); *(uint*)(data + 8) = x3;
Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4); *(uint*)(data + 12) = x4;
Array.Copy(BitConverter.GetBytes(x4), 0, result, 12, 4);
return result;
} }
// round 3-15 // round 3-15
public static byte[] DecryptNGRoundB(byte[] data, uint[] key, uint[][] table) public unsafe static void DecryptNGRoundB(byte* data, uint[] key, uint[][] table)
{ {
var x1 = var x1 =
table[0][data[0]] ^ table[0][data[0]] ^
@ -222,31 +227,10 @@ namespace CodeWalker.GameFiles
table[12][data[12]] ^ table[12][data[12]] ^
key[3]; key[3];
//var result = new byte[16]; *(uint*)data = x1;
//Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4); *(uint*)(data + 4) = x2;
//Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4); *(uint*)(data + 8) = x3;
//Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4); *(uint*)(data + 12) = x4;
//Array.Copy(BitConverter.GetBytes(x4), 0, result, 12, 4);
//return result;
var result = new byte[16];
result[0] = (byte)((x1 >> 0) & 0xFF);
result[1] = (byte)((x1 >> 8) & 0xFF);
result[2] = (byte)((x1 >> 16) & 0xFF);
result[3] = (byte)((x1 >> 24) & 0xFF);
result[4] = (byte)((x2 >> 0) & 0xFF);
result[5] = (byte)((x2 >> 8) & 0xFF);
result[6] = (byte)((x2 >> 16) & 0xFF);
result[7] = (byte)((x2 >> 24) & 0xFF);
result[8] = (byte)((x3 >> 0) & 0xFF);
result[9] = (byte)((x3 >> 8) & 0xFF);
result[10] = (byte)((x3 >> 16) & 0xFF);
result[11] = (byte)((x3 >> 24) & 0xFF);
result[12] = (byte)((x4 >> 0) & 0xFF);
result[13] = (byte)((x4 >> 8) & 0xFF);
result[14] = (byte)((x4 >> 16) & 0xFF);
result[15] = (byte)((x4 >> 24) & 0xFF);
return result;
} }
@ -267,11 +251,11 @@ namespace CodeWalker.GameFiles
public static byte[] EncryptNG(byte[] data, string name, uint length) public static byte[] EncryptNG(byte[] data, string name, uint length)
{ {
byte[] key = GetNGKey(name, length); var key = GetNGKey(name, length);
return EncryptNG(data, key); return EncryptNG(data, key);
} }
public static byte[] EncryptNG(byte[] data, byte[] key) public static byte[] EncryptNG(byte[] data, uint[][] key)
{ {
if ((GTA5Keys.PC_NG_ENCRYPT_TABLES == null) || (GTA5Keys.PC_NG_ENCRYPT_LUTs == null)) if ((GTA5Keys.PC_NG_ENCRYPT_TABLES == null) || (GTA5Keys.PC_NG_ENCRYPT_LUTs == null))
{ {
@ -280,14 +264,11 @@ namespace CodeWalker.GameFiles
var encryptedData = new byte[data.Length]; var encryptedData = new byte[data.Length];
var keyuints = new uint[key.Length / 4];
Buffer.BlockCopy(key, 0, keyuints, 0, key.Length);
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++) for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
{ {
byte[] decryptedBlock = new byte[16]; byte[] decryptedBlock = new byte[16];
Array.Copy(data, 16 * blockIndex, decryptedBlock, 0, 16); Array.Copy(data, 16 * blockIndex, decryptedBlock, 0, 16);
byte[] encryptedBlock = EncryptBlock(decryptedBlock, keyuints); byte[] encryptedBlock = EncryptBlock(decryptedBlock, key);
Array.Copy(encryptedBlock, 0, encryptedData, 16 * blockIndex, 16); Array.Copy(encryptedBlock, 0, encryptedData, 16 * blockIndex, 16);
} }
@ -300,26 +281,15 @@ namespace CodeWalker.GameFiles
return encryptedData; return encryptedData;
} }
public static byte[] EncryptBlock(byte[] data, uint[] key) public static byte[] EncryptBlock(byte[] data, uint[][] key)
{ {
var buffer = data; var buffer = data;
// prepare key... buffer = EncryptRoundA(buffer, key[16], GTA5Keys.PC_NG_ENCRYPT_TABLES[16]);
var subKeys = new uint[17][];
for (int i = 0; i < 17; i++)
{
subKeys[i] = new uint[4];
subKeys[i][0] = key[4 * i + 0];
subKeys[i][1] = key[4 * i + 1];
subKeys[i][2] = key[4 * i + 2];
subKeys[i][3] = key[4 * i + 3];
}
buffer = EncryptRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_ENCRYPT_TABLES[16]);
for (int k = 15; k >= 2; k--) for (int k = 15; k >= 2; k--)
buffer = EncryptRoundB_LUT(buffer, subKeys[k], GTA5Keys.PC_NG_ENCRYPT_LUTs[k]); buffer = EncryptRoundB_LUT(buffer, key[k], GTA5Keys.PC_NG_ENCRYPT_LUTs[k]);
buffer = EncryptRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_ENCRYPT_TABLES[1]); buffer = EncryptRoundA(buffer, key[1], GTA5Keys.PC_NG_ENCRYPT_TABLES[1]);
buffer = EncryptRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_ENCRYPT_TABLES[0]); buffer = EncryptRoundA(buffer, key[0], GTA5Keys.PC_NG_ENCRYPT_TABLES[0]);
return buffer; return buffer;
} }
@ -425,5 +395,136 @@ 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

@ -47,7 +47,7 @@ namespace CodeWalker.GameFiles
public static byte[] PC_AES_KEY; // 32 public static byte[] PC_AES_KEY; // 32
// ng decryption/encryption expanded keys... // ng decryption/encryption expanded keys...
public static byte[][] PC_NG_KEYS; // 101, 272 public static uint[][][] PC_NG_KEYS; // 101, 272
// ng decryption tables... // ng decryption tables...
public static uint[][][] PC_NG_DECRYPT_TABLES; // 17, 16, 256 public static uint[][][] PC_NG_DECRYPT_TABLES; // 17, 16, 256
@ -73,15 +73,26 @@ namespace CodeWalker.GameFiles
var exeStr = new MemoryStream(exeData); var exeStr = new MemoryStream(exeData);
updateStatus("Searching for AES key..."); updateStatus("Searching for AES key...");
PC_AES_KEY = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_AES_KEY_HASH, 0x20); PC_AES_KEY = HashSearch.SearchHash(exeStr, GTA5KeyHashes.PC_AES_KEY_HASH, 32);
//updateStatus("aes key found"); //updateStatus("aes key found");
updateStatus("Searching for NG keys..."); updateStatus("Searching for NG keys...");
PC_NG_KEYS = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_KEY_HASHES, 0x110); //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][][];
for (int i = 0; i < tabs.Length; i++)
{
PC_NG_KEYS[i] = new uint[tabs[i].Length / 4 / 4][];
for (int j = 0; j < tabs[i].Length / 4; j++)
{
Buffer.BlockCopy(tabs[i], 0, PC_NG_KEYS[i][j], 0, 16);
}
//Buffer.BlockCopy(tabs[i], 0, PC_NG_DECRYPT_TABLES[i][j], 0, tabs[i].Length);
}
//updateStatus("ng keys found"); //updateStatus("ng keys found");
updateStatus("Searching for NG decrypt tables..."); updateStatus("Searching for NG decrypt tables...");
var tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_DECRYPT_TABLE_HASHES, 0x400); tabs = HashSearch.SearchHashes(exeStr, GTA5KeyHashes.PC_NG_DECRYPT_TABLE_HASHES, 1024);
//updateStatus("ng decrypt tables found"); //updateStatus("ng decrypt tables found");
updateStatus("Searching for NG hash lookup tables..."); updateStatus("Searching for NG hash lookup tables...");
@ -277,14 +288,14 @@ namespace CodeWalker.GameFiles
db[i] = (byte)((m[i] - rb1[i] - rb2[i] - rb3[i] - rb4[i]) & 0xFF); db[i] = (byte)((m[i] - rb1[i] - rb2[i] - rb3[i] - rb4[i]) & 0xFF);
} }
db = GTACrypto.DecryptAESData(db, PC_AES_KEY); GTACrypto.DecryptAESData(db, PC_AES_KEY);
byte[] b = null; byte[] b = null;
using (MemoryStream dms = new MemoryStream(db)) using (MemoryStream dms = new MemoryStream(db))
{ {
using (DeflateStream ds = new DeflateStream(dms, CompressionMode.Decompress)) using (DeflateStream ds = new DeflateStream(dms, CompressionMode.Decompress))
{ {
using (MemoryStream outstr = new MemoryStream()) using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream())
{ {
ds.CopyTo(outstr); ds.CopyTo(outstr);
b = outstr.GetBuffer(); b = outstr.GetBuffer();
@ -848,11 +859,13 @@ namespace CodeWalker.GameFiles
random.NextBytes(buf_encrypted); random.NextBytes(buf_encrypted);
// decrypt // decrypt
var buf_decrypted = GTACrypto.DecryptNGRoundA( GTACrypto.DecryptNGRoundA(
buf_encrypted, buf_encrypted,
noKey, noKey,
tables); tables);
var buf_decrypted = buf_encrypted;
// make row // make row
var row = new RandomGaussRow(); var row = new RandomGaussRow();
//row.A[0 + buf_decrypted[inByte0]] = true; //row.A[0 + buf_decrypted[inByte0]] = true;
@ -1148,33 +1161,35 @@ namespace CodeWalker.GameFiles
public class CryptoIO public class CryptoIO
{ {
public static byte[][] ReadNgKeys(string fileName) public static uint[][][] ReadNgKeys(string fileName)
{ {
byte[][] result; using var fs = new FileStream(fileName, FileMode.Open);
using var rd = new DataReader(fs);
var fs = new FileStream(fileName, FileMode.Open); return ReadNgKeys(rd);
var rd = new DataReader(fs); }
public static uint[][][] ReadNgKeys(byte[] data)
result = new byte[101][];
for (int i = 0; i < 101; i++)
{ {
result[i] = rd.ReadBytes(272); using var rd = new DataReader(new MemoryStream(data));
return ReadNgKeys(rd);
} }
fs.Close(); public static uint[][][] ReadNgKeys(DataReader rd)
return result;
}
public static byte[][] ReadNgKeys(byte[] data)
{ {
byte[][] result; var result = new uint[101][][];
var rd = new DataReader(new MemoryStream(data));
result = new byte[101][];
for (int i = 0; i < 101; i++) for (int i = 0; i < 101; i++)
{ {
result[i] = rd.ReadBytes(272); result[i] = new uint[68 / 4][];
for (int j = 0; j < 68 / 4; j++)
{
result[i][j] = new uint[4];
for (int k = 0; k < 4; k++)
{
result[i][j][k] = rd.ReadUInt32();
}
}
} }
@ -1182,16 +1197,23 @@ namespace CodeWalker.GameFiles
} }
public static void WriteNgKeys(string fileName, byte[][] keys) public static void WriteNgKeys(string fileName, uint[][][] keys)
{ {
//var fs = new FileStream(fileName, FileMode.Create); //var fs = new FileStream(fileName, FileMode.Create);
//var wr = new DataWriter(fs); //var wr = new DataWriter(fs);
var ms = new MemoryStream(); using var ms = new MemoryStream();
var wr = new DataWriter(ms); using var wr = new DataWriter(ms);
for (int i = 0; i < 101; i++) for (int i = 0; i < 101; i++)
{ {
wr.Write(keys[i]); for (int j = 0; j < keys[i].Length; j++)
{
for (int k = 0; j < keys[i][j].Length; k++)
{
wr.Write(keys[i][j][k]);
}
}
} }
//fs.Close(); //fs.Close();
@ -1207,8 +1229,8 @@ namespace CodeWalker.GameFiles
{ {
uint[][][] result; uint[][][] result;
var fs = new FileStream(fileName, FileMode.Open); using var fs = new FileStream(fileName, FileMode.Open);
var rd = new DataReader(fs); using var rd = new DataReader(fs);
// 17 rounds... // 17 rounds...
result = new uint[17][][]; result = new uint[17][][];
@ -1235,7 +1257,7 @@ namespace CodeWalker.GameFiles
{ {
uint[][][] result; uint[][][] result;
var rd = new DataReader(new MemoryStream(data)); using var rd = new DataReader(new MemoryStream(data));
// 17 rounds... // 17 rounds...
result = new uint[17][][]; result = new uint[17][][];
@ -1263,8 +1285,8 @@ namespace CodeWalker.GameFiles
{ {
//var fs = new FileStream(fileName, FileMode.Create); //var fs = new FileStream(fileName, FileMode.Create);
//var wr = new DataWriter(fs); //var wr = new DataWriter(fs);
var ms = new MemoryStream(); using var ms = new MemoryStream();
var wr = new DataWriter(ms); using var wr = new DataWriter(ms);
// 17 rounds... // 17 rounds...
for (int i = 0; i < 17; i++) for (int i = 0; i < 17; i++)
@ -1293,8 +1315,8 @@ namespace CodeWalker.GameFiles
{ {
GTA5NGLUT[][] result; GTA5NGLUT[][] result;
var fs = new FileStream(fileName, FileMode.Open); using var fs = new FileStream(fileName, FileMode.Open);
var rd = new DataReader(fs); using var rd = new DataReader(fs);
// 17 rounds... // 17 rounds...
result = new GTA5NGLUT[17][]; result = new GTA5NGLUT[17][];

View File

@ -1,8 +1,11 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
@ -55,6 +58,24 @@ namespace CodeWalker.GameFiles
return h; return h;
} }
public static uint GenHashLower(string text)
{
if (text == null) return 0;
uint h = 0;
for (int i = 0; i < text.Length; i++)
{
h += (byte)char.ToLowerInvariant(text[i]);
h += (h << 10);
h ^= (h >> 6);
}
h += (h << 3);
h ^= (h >> 11);
h += (h << 15);
return h;
}
public static uint GenHash(string text) public static uint GenHash(string text)
{ {
if (text == null) return 0; if (text == null) return 0;
@ -192,70 +213,104 @@ namespace CodeWalker.GameFiles
public static class DictionaryExtension
{
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value)
{
if (dict.ContainsKey(key))
{
return false;
}
dict[key] = value;
return true;
}
}
public static class JenkIndex public static class JenkIndex
{ {
public static Dictionary<uint, string> Index = new Dictionary<uint, string>(); public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount, 1500000);
private static object syncRoot = new object();
public static void Clear() public static void Clear()
{
lock (syncRoot)
{ {
Index.Clear(); Index.Clear();
} }
}
public static bool Ensure(string str) public static bool Ensure(string str)
{ {
uint hash = JenkHash.GenHash(str); uint hash = JenkHash.GenHash(str);
if (hash == 0) return true; if (hash == 0) return true;
lock (syncRoot) if (Index.ContainsKey(hash))
{ {
if (!Index.ContainsKey(hash)) return true;
}
lock (Index)
{ {
Index.Add(hash, str); Index[hash] = str;
return false; return false;
} }
} }
public static bool EnsureLower(string str)
{
uint hash = JenkHash.GenHashLower(str);
if (hash == 0) return true;
if (Index.ContainsKey(hash))
{
return true; return true;
} }
Index.TryAdd(hash, str);
return false;
}
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)
{
foreach (var s in strings)
{
uint hash = JenkHash.GenHashLower(s);
if (hash == 0) continue;
Index[hash] = s;
}
}
public static string GetString(uint hash) public static string GetString(uint hash)
{ {
string res; string res;
lock (syncRoot)
{
if (!Index.TryGetValue(hash, out res)) if (!Index.TryGetValue(hash, out res))
{ {
res = hash.ToString(); res = hash.ToString();
} }
}
return res; return res;
} }
public static string TryGetString(uint hash) public static string TryGetString(uint hash)
{ {
string res; string res;
lock (syncRoot)
{
if (!Index.TryGetValue(hash, out res)) if (!Index.TryGetValue(hash, out res))
{ {
res = string.Empty; res = string.Empty;
} }
}
return res; return res;
} }
public static string[] GetAllStrings() public static string[] GetAllStrings()
{ {
string[] res = null; string[] res = null;
lock (syncRoot)
{
res = Index.Values.ToArray(); res = Index.Values.ToArray();
}
return res; return res;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
using System; using CodeWalker.GameFiles;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -15,6 +17,7 @@ namespace CodeWalker
public DateTime CurrentTime = DateTime.Now; public DateTime CurrentTime = DateTime.Now;
private LinkedList<TVal> loadedList = new LinkedList<TVal>(); private LinkedList<TVal> loadedList = new LinkedList<TVal>();
private object loadedListLock = new object();
private Dictionary<TKey, LinkedListNode<TVal>> loadedListDict = new Dictionary<TKey, LinkedListNode<TVal>>(); private Dictionary<TKey, LinkedListNode<TVal>> loadedListDict = new Dictionary<TKey, LinkedListNode<TVal>>();
public int Count public int Count
@ -43,25 +46,30 @@ namespace CodeWalker
public TVal TryGet(TKey key) public TVal TryGet(TKey key)
{ {
LinkedListNode<TVal> lln = null; LinkedListNode<TVal> lln = null;
lock (loadedListLock)
{
if (loadedListDict.TryGetValue(key, out lln)) if (loadedListDict.TryGetValue(key, out lln))
{ {
loadedList.Remove(lln); loadedList.Remove(lln);
loadedList.AddLast(lln); loadedList.AddLast(lln);
lln.Value.LastUseTime = CurrentTime; lln.Value.LastUseTime = CurrentTime;
} }
}
return (lln != null) ? lln.Value : null; return (lln != null) ? lln.Value : null;
} }
public bool TryAdd(TKey key, TVal item) public bool TryAdd(TKey key, TVal item)
{ {
if (item.MemoryUsage == 0)
{
}
item.Key = key; item.Key = key;
if (CanAdd()) if (CanAdd())
{
lock(loadedListLock)
{ {
var lln = loadedList.AddLast(item); var lln = loadedList.AddLast(item);
loadedListDict.Add(key, lln); loadedListDict.Add(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
}
return true; return true;
} }
else else
@ -73,10 +81,14 @@ namespace CodeWalker
while (!CanAdd() && (iter<maxiter)) while (!CanAdd() && (iter<maxiter))
{ {
while ((!CanAdd()) && (oldlln != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime)) while ((!CanAdd()) && (oldlln != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime))
{
lock(loadedListLock)
{ {
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key); loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later.. loadedList.Remove(oldlln); //gc should free up memory later..
}
oldlln.Value = null; oldlln.Value = null;
oldlln = null; oldlln = null;
//GC.Collect(); //GC.Collect();
@ -86,10 +98,13 @@ namespace CodeWalker
iter++; iter++;
} }
if (CanAdd()) //see if there's enough memory now... if (CanAdd()) //see if there's enough memory now...
{
lock(loadedListLock)
{ {
var newlln = loadedList.AddLast(item); var newlln = loadedList.AddLast(item);
loadedListDict.Add(key, newlln); loadedListDict.Add(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
}
return true; return true;
} }
else else
@ -107,13 +122,22 @@ namespace CodeWalker
public void Clear() public void Clear()
{
lock(loadedList)
{ {
loadedList.Clear(); loadedList.Clear();
loadedListDict.Clear(); loadedListDict.Clear();
CurrentMemoryUsage = 0; CurrentMemoryUsage = 0;
} }
}
public void Remove(TKey key) public void Remove(TKey key)
{
if (!loadedListDict.ContainsKey(key))
{
return;
}
lock(loadedListLock)
{ {
LinkedListNode<TVal> n; LinkedListNode<TVal> n;
if (loadedListDict.TryGetValue(key, out n)) if (loadedListDict.TryGetValue(key, out n))
@ -123,9 +147,12 @@ namespace CodeWalker
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
} }
} }
}
public void Compact() public void Compact()
{
lock(loadedList)
{ {
var oldlln = loadedList.First; var oldlln = loadedList.First;
while (oldlln != null) while (oldlln != null)
@ -135,10 +162,12 @@ namespace CodeWalker
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage); Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key); loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later.. loadedList.Remove(oldlln); //gc should free up memory later..
oldlln.Value = null; oldlln.Value = null;
oldlln = nextln; oldlln = nextln;
} }
} }
}
} }

View File

@ -781,7 +781,6 @@ namespace CodeWalker
texParam.Unknown_30h = 1;// 131073;//wtf is this? 2x shorts, 0x00020001 texParam.Unknown_30h = 1;// 131073;//wtf is this? 2x shorts, 0x00020001
texParam.Unknown_32h = 2; texParam.Unknown_32h = 2;
texParam.Name = name; texParam.Name = name;
texParam.NameHash = JenkHash.GenHash(name.ToLowerInvariant());
return texParam; return texParam;
} }
private void AddShaderParam(List<ShaderParamNames> paramNames, List<ShaderParameter> paramValues, ShaderParamNames paramName, object paramValue) private void AddShaderParam(List<ShaderParamNames> paramNames, List<ShaderParameter> paramValues, ShaderParamNames paramName, object paramValue)

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CodeWalker.Core.Utils
{
public class DisposableTimer : IDisposable
{
public static event Action<TimeSpan, string> TimerStopped;
public readonly Stopwatch _stopwatch;
static DisposableTimer()
{
#if DEBUG
TimerStopped += (timeSpan, name) => Debug.WriteLine($"{name} took {timeSpan.TotalMilliseconds} ms");
#endif
TimerStopped += (timeSpan, name) => Console.WriteLine($"{name} took {timeSpan.TotalMilliseconds} ms");
}
public string Name { get; private set; }
public DisposableTimer(string name)
{
_stopwatch = Stopwatch.StartNew();
Name = name;
}
public void Dispose()
{
_stopwatch.Stop();
TimerStopped?.Invoke(_stopwatch.Elapsed, Name ?? string.Empty);
}
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -100,6 +101,10 @@ namespace CodeWalker
} }
return Encoding.UTF8.GetString(bytes); return Encoding.UTF8.GetString(bytes);
} }
public static bool Contains(this string source, string toCheck, StringComparison comp)
{
return source?.IndexOf(toCheck, comp) >= 0;
}
} }
@ -271,5 +276,23 @@ namespace CodeWalker
} }
} }
public static class SpanExtensions
{
public static int Read(this Stream stream, Span<byte> buffer)
{
var n = Math.Min(stream.Length - stream.Position, buffer.Length);
if (n <= 0)
return 0;
for (int i = 0; i < buffer.Length; i++)
{
var result = stream.ReadByte();
buffer[i] = (byte)result;
}
return buffer.Length;
}
}
} }

View File

@ -6,6 +6,7 @@
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<ApplicationIcon>CW.ico</ApplicationIcon> <ApplicationIcon>CW.ico</ApplicationIcon>
<AssemblyName>CodeWalker Error Report Tool</AssemblyName> <AssemblyName>CodeWalker Error Report Tool</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -15,6 +15,11 @@
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -10,12 +10,18 @@
<Company>dexyfex software</Company> <Company>dexyfex software</Company>
<Authors>dexyfex</Authors> <Authors>dexyfex</Authors>
<AssemblyName>CodeWalker RPF Explorer</AssemblyName> <AssemblyName>CodeWalker RPF Explorer</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using CodeWalker.Utils;
namespace CodeWalker.RPFExplorer namespace CodeWalker.RPFExplorer
{ {
@ -18,7 +19,7 @@ namespace CodeWalker.RPFExplorer
static void Main() static void Main()
{ {
//Process.Start("CodeWalker.exe", "explorer"); //Process.Start("CodeWalker.exe", "explorer");
ConsoleWindow.Hide();
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new ExploreForm()); Application.Run(new ExploreForm());

View File

@ -9,6 +9,22 @@ struct VS_INPUT
float4 Tangent : TANGENT; float4 Tangent : TANGENT;
}; };
struct PNCTX_OUTPUT
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
float2 Texcoord0 : TEXCOORD0;
float2 Texcoord1 : TEXCOORD1;
float2 Texcoord2 : TEXCOORD2;
float4 Shadows : TEXCOORD3;
float4 LightShadow : TEXCOORD4;
float4 Colour0 : COLOR0;
float4 Colour1 : COLOR1;
float4 Tint : COLOR2;
float4 Tangent : TEXCOORD5;
float4 Bitangent : TEXCOORD6;
float3 CamRelPos : TEXCOORD7;
};
VS_OUTPUT main(VS_INPUT input, uint iid : SV_InstanceID) VS_OUTPUT main(VS_INPUT input, uint iid : SV_InstanceID)
{ {

View File

@ -106,6 +106,7 @@
<FxCompile> <FxCompile>
<ObjectFileOutput>$(ProjectDir)..\Shaders\%(Filename).cso</ObjectFileOutput> <ObjectFileOutput>$(ProjectDir)..\Shaders\%(Filename).cso</ObjectFileOutput>
<ShaderModel>4.0</ShaderModel> <ShaderModel>4.0</ShaderModel>
<EnableDebuggingInformation>true</EnableDebuggingInformation>
</FxCompile> </FxCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">

View File

@ -1,6 +1,3 @@
struct ShaderGlobalLightParams struct ShaderGlobalLightParams
{ {
float3 LightDir; float3 LightDir;

View File

@ -17,7 +17,8 @@ PS_OUTPUT main(VS_Output input)
{ {
uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location
float depth = DepthTex.Load(ssloc).r; float depth = DepthTex.Load(ssloc).r;
if (depth == 0) discard; //no existing pixel rendered here if (depth <= 0)
discard; //no existing pixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc); float4 diffuse = DiffuseTex.Load(ssloc);
float4 normal = NormalTex.Load(ssloc); float4 normal = NormalTex.Load(ssloc);
@ -29,9 +30,15 @@ PS_OUTPUT main(VS_Output input)
switch (RenderMode) switch (RenderMode)
{ {
case 5: output.Colour = float4(diffuse.rgb, 1); return output; case 5:
case 6: output.Colour = float4(normal.rgb, 1); return output; output.Colour = float4(diffuse.rgb, 1);
case 7: output.Colour = float4(specular.rgb, 1); return output; return output;
case 6:
output.Colour = float4(normal.rgb, 1);
return output;
case 7:
output.Colour = float4(specular.rgb, 1);
return output;
} }
float4 spos = float4(input.Screen.xy/input.Screen.w, depth, 1); float4 spos = float4(input.Screen.xy/input.Screen.w, depth, 1);

View File

@ -34,7 +34,8 @@ PS_OUTPUT main(VS_Output input)
for (int i = 0; i < sc; i++) for (int i = 0; i < sc; i++)
{ {
float depth = DepthTex.Load(ssloc, i); float depth = DepthTex.Load(ssloc, i);
if (depth == 0) continue; //no existing subpixel rendered here if (depth == 0)
continue; //no existing subpixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc, i); float4 diffuse = DiffuseTex.Load(ssloc, i);
float4 normal = NormalTex.Load(ssloc, i); float4 normal = NormalTex.Load(ssloc, i);
@ -56,7 +57,8 @@ PS_OUTPUT main(VS_Output input)
d *= SampleMult; d *= SampleMult;
a *= SampleMult; a *= SampleMult;
if (d <= 0) discard; if (d <= 0)
discard;
PS_OUTPUT output; PS_OUTPUT output;
output.Colour = float4(c, a); output.Colour = float4(c, a);

View File

@ -18,7 +18,8 @@ float4 main(VS_Output input) : SV_TARGET
{ {
uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location
float depth = DepthTex.Load(ssloc).r; float depth = DepthTex.Load(ssloc).r;
if (depth == 0) discard; //no existing pixel rendered here if (depth == 0)
discard; //no existing pixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc); float4 diffuse = DiffuseTex.Load(ssloc);
float4 normal = NormalTex.Load(ssloc); float4 normal = NormalTex.Load(ssloc);
@ -31,7 +32,8 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1; float3 norm = normal.xyz * 2 - 1;
float4 lcol = DeferredLight(camRel, norm, diffuse, specular, irradiance); float4 lcol = DeferredLight(camRel, norm, diffuse, specular, irradiance);
if (lcol.a <= 0) discard; if (lcol.a <= 0)
discard;
return lcol; return lcol;
} }

View File

@ -117,6 +117,7 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
LODLight lodlight = LODLights[iid]; LODLight lodlight = LODLights[iid];
float3 srpos = lodlight.Position - (camRel + CameraPos.xyz); //light position relative to surface position float3 srpos = lodlight.Position - (camRel + CameraPos.xyz); //light position relative to surface position
float ldist = length(srpos); float ldist = length(srpos);
if (LightType == 4)//capsule if (LightType == 4)//capsule
{ {
float3 ext = lodlight.Direction.xyz * lodlight.OuterAngleOrCapExt; float3 ext = lodlight.Direction.xyz * lodlight.OuterAngleOrCapExt;
@ -125,13 +126,16 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
srpos.xyz = lsn.xyz; srpos.xyz = lsn.xyz;
} }
if (ldist > lodlight.Falloff) return 0; //out of range of the light... if (ldist > lodlight.Falloff)
if (ldist <= 0) return 0; return 0; //out of range of the light...
if (ldist <= 0)
return 0;
float4 rgbi = Unpack4x8UNF(lodlight.Colour).gbar; float4 rgbi = Unpack4x8UNF(lodlight.Colour).gbar;
float3 lcol = rgbi.rgb * rgbi.a * 96.0f; float3 lcol = rgbi.rgb * rgbi.a * 96.0f;
float3 ldir = srpos / ldist; float3 ldir = srpos / ldist;
float pclit = saturate(dot(ldir, norm)); float pclit = saturate(dot(ldir, norm));
float lamt = 1; float lamt = 1;
if (LightType == 1)//point (sphere) if (LightType == 1)//point (sphere)
@ -154,7 +158,8 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
pclit *= lamt; pclit *= lamt;
if (pclit <= 0) return 0; if (pclit <= 0)
return 0;
float3 refl = GetReflectedDir(camRel, norm); float3 refl = GetReflectedDir(camRel, norm);
float specb = saturate(dot(refl, ldir)); float specb = saturate(dot(refl, ldir));
@ -177,11 +182,14 @@ float4 DeferredLight(float3 camRel, float3 norm, float4 diffuse, float4 specular
ldist = lsn.w; ldist = lsn.w;
srpos.xyz = lsn.xyz; srpos.xyz = lsn.xyz;
} }
if (ldist > InstFalloff) return 0; if (ldist > InstFalloff)
if (ldist <= 0) return 0; return 0;
if (ldist <= 0)
return 0;
float d = dot(srpos, InstCullingPlaneNormal) - InstCullingPlaneOffset; float d = dot(srpos, InstCullingPlaneNormal) - InstCullingPlaneOffset;
if (d > 0) return 0; if (d > 0)
return 0;
float4 rgbi = float4(InstColour, InstIntensity); float4 rgbi = float4(InstColour, InstIntensity);
float3 lcol = rgbi.rgb;// * rgbi.a; // * 5.0f; float3 lcol = rgbi.rgb;// * rgbi.a; // * 5.0f;
@ -198,7 +206,8 @@ float4 DeferredLight(float3 camRel, float3 norm, float4 diffuse, float4 specular
float ang = acos(-dot(ldir, InstDirection)); float ang = acos(-dot(ldir, InstDirection));
float iang = InstConeInnerAngle; float iang = InstConeInnerAngle;
float oang = InstConeOuterAngle; float oang = InstConeOuterAngle;
if (ang > oang) return 0; if (ang > oang)
return 0;
lamt *= saturate(1 - ((ang - iang) / (oang - iang))); lamt *= saturate(1 - ((ang - iang) / (oang - iang)));
lamt *= GetAttenuation(ldist, InstFalloff, InstFalloffExponent); lamt *= GetAttenuation(ldist, InstFalloff, InstFalloffExponent);
} }
@ -209,7 +218,8 @@ float4 DeferredLight(float3 camRel, float3 norm, float4 diffuse, float4 specular
pclit *= lamt; pclit *= lamt;
if (pclit <= 0) return 0; if (pclit <= 0)
return 0;
float3 refl = GetReflectedDir(camRel, norm); float3 refl = GetReflectedDir(camRel, norm);
float specb = saturate(dot(refl, ldir)); float specb = saturate(dot(refl, ldir));

View File

@ -18,14 +18,14 @@ float4 main(VS_Output input) : SV_TARGET
uint2 ssloc = uint2(input.Pos.xy); //pixel location uint2 ssloc = uint2(input.Pos.xy); //pixel location
float2 spos = float2(input.Screen.xy / input.Screen.w); float2 spos = float2(input.Screen.xy / input.Screen.w);
float4 c = 0; float4 c = 0;
float d = 0;
int sc = min(SampleCount, 8); int sc = min(SampleCount, 8);
[unroll] [unroll]
for (int i = 0; i < sc; i++) for (int i = 0; i < sc; i++)
{ {
float depth = DepthTex.Load(ssloc, i); float depth = DepthTex.Load(ssloc, i);
if (depth == 0) continue; //no existing subpixel rendered here if (depth == 0)
continue; //no existing subpixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc, i); float4 diffuse = DiffuseTex.Load(ssloc, i);
float4 normal = NormalTex.Load(ssloc, i); float4 normal = NormalTex.Load(ssloc, i);
@ -37,15 +37,16 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1; float3 norm = normal.xyz * 2 - 1;
float4 colour = DeferredLight(camRel, norm, diffuse, specular, irradiance); float4 colour = DeferredLight(camRel, norm, diffuse, specular, irradiance);
if (colour.a <= 0)
discard;
c += colour; c += colour;
d += depth;
} }
c *= SampleMult; c *= SampleMult;
d *= SampleMult;
if (d <= 0) discard; if (c.a <= 0)
discard;
return c; return c;
} }

View File

@ -18,7 +18,8 @@ float4 main(VS_Output input) : SV_TARGET
{ {
uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location
float depth = DepthTex.Load(ssloc).r; float depth = DepthTex.Load(ssloc).r;
if (depth == 0) discard; //no existing pixel rendered here if (depth == 0)
discard; //no existing pixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc); float4 diffuse = DiffuseTex.Load(ssloc);
float4 normal = NormalTex.Load(ssloc); float4 normal = NormalTex.Load(ssloc);
@ -31,7 +32,8 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1; float3 norm = normal.xyz * 2 - 1;
float4 lcol = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID); float4 lcol = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID);
if (lcol.a <= 0) discard; if (lcol.a <= 0)
discard;
return lcol; return lcol;
} }

View File

@ -26,7 +26,8 @@ float4 main(VS_Output input) : SV_TARGET
for (int i = 0; i < sc; i++) for (int i = 0; i < sc; i++)
{ {
float depth = DepthTex.Load(ssloc, i); float depth = DepthTex.Load(ssloc, i);
if (depth == 0) continue; //no existing subpixel rendered here if (depth == 0)
continue; //no existing subpixel rendered here
float4 diffuse = DiffuseTex.Load(ssloc, i); float4 diffuse = DiffuseTex.Load(ssloc, i);
float4 normal = NormalTex.Load(ssloc, i); float4 normal = NormalTex.Load(ssloc, i);
@ -38,15 +39,16 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1; float3 norm = normal.xyz * 2 - 1;
float4 colour = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID); float4 colour = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID);
if (colour.a <= 0)
discard;
c += colour; c += colour;
d += depth;
} }
c *= SampleMult; c *= SampleMult;
d *= SampleMult;
if (d <= 0) discard; if (c.a <= 0)
discard;
return c; return c;
} }

View File

@ -12,6 +12,7 @@ struct LODLight
float FalloffExponent; float FalloffExponent;
float InnerAngle; //for cone float InnerAngle; //for cone
float OuterAngleOrCapExt; //outer angle for cone, cap extent for capsule float OuterAngleOrCapExt; //outer angle for cone, cap extent for capsule
float Distance;
}; };
struct VS_Output struct VS_Output

View File

@ -9,12 +9,18 @@
<Company>dexyfex software</Company> <Company>dexyfex software</Company>
<Authors>dexyfex</Authors> <Authors>dexyfex</Authors>
<AssemblyName>CodeWalker Vehicle Viewer</AssemblyName> <AssemblyName>CodeWalker Vehicle Viewer</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\CodeWalker\CodeWalker.csproj" /> <ProjectReference Include="..\CodeWalker\CodeWalker.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -1,9 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<TargetFramework>net48</TargetFramework> <TargetFramework>net48</TargetFramework>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -43,27 +43,6 @@
<setting name="Skydome" serializeAs="String"> <setting name="Skydome" serializeAs="String">
<value>True</value> <value>True</value>
</setting> </setting>
<setting name="TimeOfDay" serializeAs="String">
<value>720</value>
</setting>
<setting name="LODLights" serializeAs="String">
<value>True</value>
</setting>
<setting name="Region" serializeAs="String">
<value>Global</value>
</setting>
<setting name="Clouds" serializeAs="String">
<value>contrails</value>
</setting>
<setting name="Weather" serializeAs="String">
<value>True</value>
</setting>
<setting name="NatrualAmbientLight" serializeAs="String">
<value>True</value>
</setting>
<setting name="ArtificialAmbientLight" serializeAs="String">
<value>True</value>
</setting>
<setting name="ShowTimedEntities" serializeAs="String"> <setting name="ShowTimedEntities" serializeAs="String">
<value>True</value> <value>True</value>
</setting> </setting>
@ -247,6 +226,30 @@
<setting name="RPFExplorerStartFolder" serializeAs="String"> <setting name="RPFExplorerStartFolder" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="TimeOfDay" serializeAs="String">
<value>720</value>
</setting>
<setting name="LODLights" serializeAs="String">
<value>False</value>
</setting>
<setting name="Region" serializeAs="String">
<value>Global</value>
</setting>
<setting name="Clouds" serializeAs="String">
<value>contrails</value>
</setting>
<setting name="Weather" serializeAs="String">
<value>True</value>
</setting>
<setting name="NatrualAmbientLight" serializeAs="String">
<value>True</value>
</setting>
<setting name="ArtificialAmbientLight" serializeAs="String">
<value>True</value>
</setting>
<setting name="AntiAliasing" serializeAs="String">
<value>2</value>
</setting>
</CodeWalker.Properties.Settings> </CodeWalker.Properties.Settings>
</userSettings> </userSettings>
<runtime> <runtime>

View File

@ -9,6 +9,7 @@
<Copyright>dexyfex</Copyright> <Copyright>dexyfex</Copyright>
<Company>dexyfex software</Company> <Company>dexyfex software</Company>
<Authors>dexyfex</Authors> <Authors>dexyfex</Authors>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@ -18,6 +19,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" /> <PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" />
<PackageReference Include="FCTB" Version="2.16.24" /> <PackageReference Include="FCTB" Version="2.16.24" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX" Version="4.2.0" />
<PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" /> <PackageReference Include="SharpDX.D3DCompiler" Version="4.2.0" />
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" /> <PackageReference Include="SharpDX.Direct2D1" Version="4.2.0" />
@ -25,6 +27,7 @@
<PackageReference Include="SharpDX.Mathematics" Version="4.2.0" /> <PackageReference Include="SharpDX.Mathematics" Version="4.2.0" />
<PackageReference Include="SharpDX.XAudio2" Version="4.2.0" /> <PackageReference Include="SharpDX.XAudio2" Version="4.2.0" />
<PackageReference Include="SharpDX.XInput" Version="4.2.0" /> <PackageReference Include="SharpDX.XInput" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,6 @@
namespace CodeWalker using CodeWalker.Core.Utils;
namespace CodeWalker
{ {
partial class ExploreForm partial class ExploreForm
{ {
@ -158,6 +160,7 @@
this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog(); this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog();
this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog(); this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
this.VSExtender = new WeifenLuo.WinFormsUI.Docking.VisualStudioToolStripExtender(this.components); this.VSExtender = new WeifenLuo.WinFormsUI.Docking.VisualStudioToolStripExtender(this.components);
this.openConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainMenu.SuspendLayout(); this.MainMenu.SuspendLayout();
this.MainToolbar.SuspendLayout(); this.MainToolbar.SuspendLayout();
this.MainStatusBar.SuspendLayout(); this.MainStatusBar.SuspendLayout();
@ -409,6 +412,7 @@
this.ViewSmallIconsMenu, this.ViewSmallIconsMenu,
this.ViewListMenu, this.ViewListMenu,
this.ViewDetailsMenu, this.ViewDetailsMenu,
this.openConsoleToolStripMenuItem,
this.toolStripSeparator11, this.toolStripSeparator11,
this.ViewThemeMenu}); this.ViewThemeMenu});
this.ViewMenu.Name = "ViewMenu"; this.ViewMenu.Name = "ViewMenu";
@ -418,21 +422,21 @@
// ViewLargeIconsMenu // ViewLargeIconsMenu
// //
this.ViewLargeIconsMenu.Name = "ViewLargeIconsMenu"; this.ViewLargeIconsMenu.Name = "ViewLargeIconsMenu";
this.ViewLargeIconsMenu.Size = new System.Drawing.Size(134, 22); this.ViewLargeIconsMenu.Size = new System.Drawing.Size(180, 22);
this.ViewLargeIconsMenu.Text = "Large Icons"; this.ViewLargeIconsMenu.Text = "Large Icons";
this.ViewLargeIconsMenu.Click += new System.EventHandler(this.ViewLargeIconsMenu_Click); this.ViewLargeIconsMenu.Click += new System.EventHandler(this.ViewLargeIconsMenu_Click);
// //
// ViewSmallIconsMenu // ViewSmallIconsMenu
// //
this.ViewSmallIconsMenu.Name = "ViewSmallIconsMenu"; this.ViewSmallIconsMenu.Name = "ViewSmallIconsMenu";
this.ViewSmallIconsMenu.Size = new System.Drawing.Size(134, 22); this.ViewSmallIconsMenu.Size = new System.Drawing.Size(180, 22);
this.ViewSmallIconsMenu.Text = "Small Icons"; this.ViewSmallIconsMenu.Text = "Small Icons";
this.ViewSmallIconsMenu.Click += new System.EventHandler(this.ViewSmallIconsMenu_Click); this.ViewSmallIconsMenu.Click += new System.EventHandler(this.ViewSmallIconsMenu_Click);
// //
// ViewListMenu // ViewListMenu
// //
this.ViewListMenu.Name = "ViewListMenu"; this.ViewListMenu.Name = "ViewListMenu";
this.ViewListMenu.Size = new System.Drawing.Size(134, 22); this.ViewListMenu.Size = new System.Drawing.Size(180, 22);
this.ViewListMenu.Text = "List"; this.ViewListMenu.Text = "List";
this.ViewListMenu.Click += new System.EventHandler(this.ViewListMenu_Click); this.ViewListMenu.Click += new System.EventHandler(this.ViewListMenu_Click);
// //
@ -441,14 +445,14 @@
this.ViewDetailsMenu.Checked = true; this.ViewDetailsMenu.Checked = true;
this.ViewDetailsMenu.CheckState = System.Windows.Forms.CheckState.Checked; this.ViewDetailsMenu.CheckState = System.Windows.Forms.CheckState.Checked;
this.ViewDetailsMenu.Name = "ViewDetailsMenu"; this.ViewDetailsMenu.Name = "ViewDetailsMenu";
this.ViewDetailsMenu.Size = new System.Drawing.Size(134, 22); this.ViewDetailsMenu.Size = new System.Drawing.Size(180, 22);
this.ViewDetailsMenu.Text = "Details"; this.ViewDetailsMenu.Text = "Details";
this.ViewDetailsMenu.Click += new System.EventHandler(this.ViewDetailsMenu_Click); this.ViewDetailsMenu.Click += new System.EventHandler(this.ViewDetailsMenu_Click);
// //
// toolStripSeparator11 // toolStripSeparator11
// //
this.toolStripSeparator11.Name = "toolStripSeparator11"; this.toolStripSeparator11.Name = "toolStripSeparator11";
this.toolStripSeparator11.Size = new System.Drawing.Size(131, 6); this.toolStripSeparator11.Size = new System.Drawing.Size(177, 6);
// //
// ViewThemeMenu // ViewThemeMenu
// //
@ -458,7 +462,7 @@
this.ViewThemeLightMenu, this.ViewThemeLightMenu,
this.ViewThemeDarkMenu}); this.ViewThemeDarkMenu});
this.ViewThemeMenu.Name = "ViewThemeMenu"; this.ViewThemeMenu.Name = "ViewThemeMenu";
this.ViewThemeMenu.Size = new System.Drawing.Size(134, 22); this.ViewThemeMenu.Size = new System.Drawing.Size(180, 22);
this.ViewThemeMenu.Text = "Theme"; this.ViewThemeMenu.Text = "Theme";
// //
// ViewThemeWindowsMenu // ViewThemeWindowsMenu
@ -506,35 +510,35 @@
// ToolsBinSearchMenu // ToolsBinSearchMenu
// //
this.ToolsBinSearchMenu.Name = "ToolsBinSearchMenu"; this.ToolsBinSearchMenu.Name = "ToolsBinSearchMenu";
this.ToolsBinSearchMenu.Size = new System.Drawing.Size(180, 22); this.ToolsBinSearchMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsBinSearchMenu.Text = "Binary Search..."; this.ToolsBinSearchMenu.Text = "Binary Search...";
this.ToolsBinSearchMenu.Click += new System.EventHandler(this.ToolsBinSearchMenu_Click); this.ToolsBinSearchMenu.Click += new System.EventHandler(this.ToolsBinSearchMenu_Click);
// //
// ToolsAudioExplorerMenu // ToolsAudioExplorerMenu
// //
this.ToolsAudioExplorerMenu.Name = "ToolsAudioExplorerMenu"; this.ToolsAudioExplorerMenu.Name = "ToolsAudioExplorerMenu";
this.ToolsAudioExplorerMenu.Size = new System.Drawing.Size(180, 22); this.ToolsAudioExplorerMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsAudioExplorerMenu.Text = "Audio Explorer"; this.ToolsAudioExplorerMenu.Text = "Audio Explorer";
this.ToolsAudioExplorerMenu.Click += new System.EventHandler(this.ToolsAudioExplorerMenu_Click); this.ToolsAudioExplorerMenu.Click += new System.EventHandler(this.ToolsAudioExplorerMenu_Click);
// //
// ToolsRpfBrowserMenu // ToolsRpfBrowserMenu
// //
this.ToolsRpfBrowserMenu.Name = "ToolsRpfBrowserMenu"; this.ToolsRpfBrowserMenu.Name = "ToolsRpfBrowserMenu";
this.ToolsRpfBrowserMenu.Size = new System.Drawing.Size(180, 22); this.ToolsRpfBrowserMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsRpfBrowserMenu.Text = "Old RPF Browser"; this.ToolsRpfBrowserMenu.Text = "Old RPF Browser";
this.ToolsRpfBrowserMenu.Click += new System.EventHandler(this.ToolsRpfBrowserMenu_Click); this.ToolsRpfBrowserMenu.Click += new System.EventHandler(this.ToolsRpfBrowserMenu_Click);
// //
// ToolsJenkGenMenu // ToolsJenkGenMenu
// //
this.ToolsJenkGenMenu.Name = "ToolsJenkGenMenu"; this.ToolsJenkGenMenu.Name = "ToolsJenkGenMenu";
this.ToolsJenkGenMenu.Size = new System.Drawing.Size(180, 22); this.ToolsJenkGenMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsJenkGenMenu.Text = "JenkGen"; this.ToolsJenkGenMenu.Text = "JenkGen";
this.ToolsJenkGenMenu.Click += new System.EventHandler(this.ToolsJenkGenMenu_Click); this.ToolsJenkGenMenu.Click += new System.EventHandler(this.ToolsJenkGenMenu_Click);
// //
// ToolsJenkIndMenu // ToolsJenkIndMenu
// //
this.ToolsJenkIndMenu.Name = "ToolsJenkIndMenu"; this.ToolsJenkIndMenu.Name = "ToolsJenkIndMenu";
this.ToolsJenkIndMenu.Size = new System.Drawing.Size(180, 22); this.ToolsJenkIndMenu.Size = new System.Drawing.Size(161, 22);
this.ToolsJenkIndMenu.Text = "JenkInd"; this.ToolsJenkIndMenu.Text = "JenkInd";
this.ToolsJenkIndMenu.Click += new System.EventHandler(this.ToolsJenkIndMenu_Click); this.ToolsJenkIndMenu.Click += new System.EventHandler(this.ToolsJenkIndMenu_Click);
// //
@ -1308,6 +1312,13 @@
// //
this.VSExtender.DefaultRenderer = null; this.VSExtender.DefaultRenderer = null;
// //
// openConsoleToolStripMenuItem
//
this.openConsoleToolStripMenuItem.Name = "openConsoleToolStripMenuItem";
this.openConsoleToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.openConsoleToolStripMenuItem.Text = "Open Console";
this.openConsoleToolStripMenuItem.Click += new System.EventHandler(this.ViewConsoleMenu_Click);
//
// ExploreForm // ExploreForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1476,5 +1487,6 @@
private System.Windows.Forms.ToolStripSeparator toolStripSeparator13; private System.Windows.Forms.ToolStripSeparator toolStripSeparator13;
private System.Windows.Forms.ToolStripMenuItem ListContextNewYtdFileMenu; private System.Windows.Forms.ToolStripMenuItem ListContextNewYtdFileMenu;
private System.Windows.Forms.ToolStripMenuItem ToolsAudioExplorerMenu; private System.Windows.Forms.ToolStripMenuItem ToolsAudioExplorerMenu;
private System.Windows.Forms.ToolStripMenuItem openConsoleToolStripMenuItem;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@
<data name="EditViewMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditViewMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE vwAADr8BOAVTJAAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS
EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM
K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+ K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+
@ -134,7 +134,7 @@
<data name="EditViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV vgAADr4B6kKxwAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi
+uZiNACH5wAAAABJRU5ErkJggg== +uZiNACH5wAAAABJRU5ErkJggg==
</value> </value>
@ -142,7 +142,7 @@
<data name="EditExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE vgAADr4B6kKxwAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2 ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2
btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP
rxwb6AQLOW8AAAAASUVORK5CYII= rxwb6AQLOW8AAAAASUVORK5CYII=
@ -151,7 +151,7 @@
<data name="EditExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6 vwAADr8BOAVTJAAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI 3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI
4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+ 4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+
B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg== B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg==
@ -160,7 +160,7 @@
<data name="EditImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe vwAADr8BOAVTJAAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL
XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu
QmCC QmCC
@ -169,7 +169,7 @@
<data name="EditImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ vwAADr8BOAVTJAAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
+s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS +s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS
lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV
AAAAAElFTkSuQmCC AAAAAElFTkSuQmCC
@ -178,7 +178,7 @@
<data name="EditCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7 DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII= k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value> </value>
@ -186,14 +186,14 @@
<data name="EditRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs vgAADr4B6kKxwAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="EditDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="EditDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAVdJREFUOE+lk7FKw1AUhn0BwQdwcHJwEpzcsjkJmZ06+AABfYDqA1h3h+AiHQrS vwAADr8BOAVTJAAAAVdJREFUOE+lk7FKw1AUhn0BwQdwcHJwEpzcsjkJmZ06+AABfYDqA1h3h+AiHQrS
xUkoOjiIEFwERSgOtoqDdcl6/b+bc9OmJHbwhx+Sc/7z33NyT5acc/9iiTzP18S2mIgrFq6AuJiKA2Mr xUkoOjiIEFwERSgOtoqDdcl6/b+bc9OmJHbwhx+Sc/7z33NyT5acc/9iiTzP18S2mIgrFq6AuJiKA2Mr
JKKfz7F7PDv11PtQrJjoPZ58jL4fTo7d2+01mqzU6CGl8Hx92fPu6ADBpeU4tTPK7l0v2nD93W2HkWKb JKKfz7F7PDv11PtQrJjoPZ58jL4fTo7d2+01mqzU6CGl8Hx92fPu6ADBpeU4tTPK7l0v2nD93W2HkWKb
vhjMG0A7hZGGT93UXWytemKkWGylBRTwI+AeDBATo5s508TKqlCiVWcSnulCmtTk9agzgTeH+xRPP1oT vhjMG0A7hZGGT93UXWytemKkWGylBRTwI+AeDBATo5s508TKqlCiVWcSnulCmtTk9agzgTeH+xRPP1oT
@ -267,7 +267,7 @@
<data name="SearchGlobalButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="SearchGlobalButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAANNJREFUOE+lUjEOgzAM5El9A3skRthZy0yZOvE9vpCh7UCGtAtr6otslIgENepJ vwAADr8BOAVTJAAAANNJREFUOE+lUjEOgzAM5El9A3skRthZy0yZOvE9vpCh7UCGtAtr6otslIgENepJ
J2TfnZ0A1Rm2bWuIC9EFRN2wJQ8y3bXWbpomV9f1TtToQ2frEdgAk1IqCgvR5yHpk5CwyOZxvLnn4+Xe J2TfnZ0A1Rm2bWuIC9EFRN2wJQ8y3bXWbpomV9f1TtToQ2frEdgAk1IqCgvR5yHpk5CwyOZxvLnn4+Xe
9uOfw3XwfejwcSQGCfs2CQtRiwYfR2LgeGIKw8K2bc8H/HUCal4g9H3vTUXvgMN2nud9S4rJr5ALd10X 9uOfw3XwfejwcSQGCfs2CQtRiwYfR2LgeGIKw8K2bc8H/HUCal4g9H3vTUXvgMN2nud9S4rJr5ALd10X
1cn/IBfGNYwx/q4B4z+Rimx4XVcLna1ppAb8HBaEQ4rDAhnCLAsLeEhBuKq+87Osda2nLc8AAAAASUVO 1cn/IBfGNYwx/q4B4z+Rimx4XVcLna1ppAb8HBaEQ4rDAhnCLAsLeEhBuKq+87Osda2nLc8AAAAASUVO
@ -277,7 +277,7 @@
<data name="SearchFilterButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="SearchFilterButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKJJREFUOE/VjLENAjEQBL8ESqEE5NxdfAFU4VYcUocjWnDu4DOnhwdZJyQfFoaI vwAADr8BOAVTJAAAAKJJREFUOE/VjLENAjEQBL8ESqEE5NxdfAFU4VYcUocjWnDu4DOnhwdZJyQfFoaI
lVY67e7NJiI/2QxXrEetVVZsAY5938U5NzUbthbgknMW7735iOnYsB0AqBXXlJL5jOnYsDUBqA1uMcbh lVY67e7NJiI/2QxXrEetVVZsAY5938U5NzUbthbgknMW7735iOnYsB0AqBXXlJL5jOnYsDUBqA1uMcbh
mYyuz6aAU/M9hKDP3GR0ffYegNrwXEpRADdZr5+aAlB7UAB3j1V/Anh1j1UD4Fub4YrN8HPL9gAVE1vf mYyuz6aAU/M9hKDP3GR0ffYegNrwXEpRADdZr5+aAlB7UAB3j1V/Anh1j1UD4Fub4YrN8HPL9gAVE1vf
J6IiRgAAAABJRU5ErkJggg== J6IiRgAAAABJRU5ErkJggg==
@ -304,7 +304,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo
HwAAAk1TRnQBSQFMAgEBGAEAAdABAQHQAQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo HwAAAk1TRnQBSQFMAgEBGAEAAfgBAQH4AQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAXADAAEBAQABCAYAARwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA AwABQAMAAXADAAEBAQABCAYAARwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@ -446,7 +446,7 @@
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJtJREFUOE+Vj8ENgCAMRdnE1ZzLCwt5cQJvjkDiRfyfUIJQEJo8U2rof5hWOedW vwAADr8BOAVTJAAAAJtJREFUOE+Vj8ENgCAMRdnE1ZzLCwt5cQJvjkDiRfyfUIJQEJo8U2rof5hWOedW
4CNrHI8XLp3PsXjCPo7Hion3tXm/mwD7KQtJlwVTFmW6MGyRp+MYYD9kUaZjlBaQX4s8XVvQtSjTtQWk 4CNrHI8XLp3PsXjCPo7Hion3tXm/mwD7KQtJlwVTFmW6MGyRp+MYYD9kUaZjlBaQX4s8XVvQtSjTtQWk
aVGmE4yrBaqFlt6jstDSCX5VBuRj0UtvLSDJopX+R7LAx868X4gGVp5hAQcz4LIxLycs8rg+vnkMAAAA aVGmE4yrBaqFlt6jstDSCX5VBuRj0UtvLSDJopX+R7LAx868X4gGVp5hAQcz4LIxLycs8rg+vnkMAAAA
AElFTkSuQmCC AElFTkSuQmCC
@ -455,7 +455,7 @@
<data name="pictureBox2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="pictureBox2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAALVJREFUOE/dk70NQjEMhLMHDZuRBZiAadgD6YmGNjNAhaB4okkb/IEtvfgZIaDj vwAADr8BOAVTJAAAALVJREFUOE/dk70NQjEMhLMHDZuRBZiAadgD6YmGNjNAhaB4okkb/IEtvfgZIaDj
pCvi853y4ySPWmsWDsLmSC1r2xwiLoVlON7aandui+2pIzU0euhV2xNqHtf7y8zouTlcCRm7EFkUBN9s pCvi853y4ySPWmsWDsLmSC1r2xwiLoVlON7aandui+2pIzU0euhV2xNqHtf7y8zouTlcCRm7EFkUBN9s
8HUNKWbObM03QUOk6XEyAUN05nfEg5eAsAEaIg3i/ZOAl5doiLTpJf72jDoLJZpCg693gwRk8RjlaBo9 8HUNKWbObM03QUOk6XEyAUN05nfEg5eAsAEaIg3i/ZOAl5doiLTpJf72jDoLJZpCg693gwRk8RjlaBo9
w1EGGvLdZ5pCxA++c0p3WGOjVX9N2kUAAAAASUVORK5CYII= w1EGGvLdZ5pCxA++c0p3WGOjVX9N2kUAAAAASUVORK5CYII=
@ -467,7 +467,7 @@
<data name="ListContextViewMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextViewMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE vwAADr8BOAVTJAAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS
EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM
K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+ K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+
@ -477,7 +477,7 @@
<data name="ListContextViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV vgAADr4B6kKxwAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi
+uZiNACH5wAAAABJRU5ErkJggg== +uZiNACH5wAAAABJRU5ErkJggg==
</value> </value>
@ -485,7 +485,7 @@
<data name="ListContextExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE vgAADr4B6kKxwAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2 ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2
btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP
rxwb6AQLOW8AAAAASUVORK5CYII= rxwb6AQLOW8AAAAASUVORK5CYII=
@ -494,7 +494,7 @@
<data name="ListContextExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6 vwAADr8BOAVTJAAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI 3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI
4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+ 4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+
B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg== B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg==
@ -503,7 +503,7 @@
<data name="ListContextImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe vwAADr8BOAVTJAAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL
XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu
QmCC QmCC
@ -512,7 +512,7 @@
<data name="ListContextImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ vwAADr8BOAVTJAAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
+s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS +s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS
lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV
AAAAAElFTkSuQmCC AAAAAElFTkSuQmCC
@ -521,7 +521,7 @@
<data name="ListContextCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7 DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII= k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value> </value>
@ -529,14 +529,14 @@
<data name="ListContextRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs vgAADr4B6kKxwAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ListContextDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ListContextDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1
MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAFjSURBVDhPpVOxSsNQFM0PCP2A MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADr8AAA6/ATgFUyQAAAFjSURBVDhPpVOxSsNQFM0PCP2A
Dp0cOgmd3Lo5CU4OTh0E14B+QPQDrLtDcBEHRRx0KgQdFKQQXARRENFGxeHpkvV5zs195kUSHXrg0Mc9 Dp0cOgmd3Lo5CU4OTh0E14B+QPQDrLtDcBEHRRx0KgQdFKQQXARRENFGxeHpkvV5zs195kUSHXrg0Mc9
956c5N0GRJ7nLTAGLWjAUIRfQD3SHsclJ4RPFyO732sLs/S6FAGcO2B6s7tj92Zn7P3pEfVIZWlIxttb 956c5N0GRJ7nLTAGLWjAUIRfQD3SHsclJ4RPFyO732sLs/S6FAGcO2B6s7tj92Zn7P3pEfVIZWlIxttb
IpIni/P28y1jEiYLeT7fWBWNfagd62gBNjGBMyAvN9fZaD4e7sSQNZqgloItHS3AAoXbg7hiwqfxlXj2 IpIni/P28y1jEiYLeT7fWBWNfagd62gBNjGBMyAvN9fZaD4e7sSQNZqgloItHS3AAoXbg7hiwqfxlXj2
@ -551,7 +551,7 @@
<data name="TreeContextCopyPathMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="TreeContextCopyPathMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7 DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII= k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value> </value>
@ -559,7 +559,7 @@
<data name="TreeContextWinExplorerMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="TreeContextWinExplorerMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAALNJREFUOE9jIAS+fftmAMTvgfg/NgxVhh0AFYA1t7W1/XdycsLAKAbATETHIM3N vwAADr8BOAVTJAAAALNJREFUOE9jIAS+fftmAMTvgfg/NgxVhh0AFYA1t7W1/XdycsLAKAbATETHIM3N
zS3/P7z/iIKxGoBuAwgja4aJ4TUAppgQpp4BIAKGyTYAxCEWYzPgfH5+PoZCXPjokaMYBjQsX74cQyEu zS3/P7z/iIKxGoBuAwgja4aJ4TUAppgQpp4BIAKGyTYAxCEWYzPgfH5+PoZCXPjokaMYBjQsX74cQyEu
3NfXj2GAwZUrVzAU4sLJScmoBkBj4H1kZCSGYlz41s3bYBrZgPm4kis2vH7dejCNbEDCvn37MBQSwsgG 3NfXj2GAwZUrVzAU4sLJScmoBkBj4H1kZCSGYlz41s3bYBrZgPm4kis2vH7dejCNbEDCvn37MBQSwsgG
CIA45GCG//8pwf8ZANa5gGyReLItAAAAAElFTkSuQmCC CIA45GCG//8pwf8ZANa5gGyReLItAAAAAElFTkSuQmCC

View File

@ -185,7 +185,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) saveAs = true; if (string.IsNullOrEmpty(FileName)) saveAs = true;
if (string.IsNullOrEmpty(FilePath)) saveAs = true; if (string.IsNullOrEmpty(FilePath)) saveAs = true;
else if ((FilePath.ToLowerInvariant().StartsWith(GTAFolder.CurrentGTAFolder.ToLowerInvariant()))) saveAs = true; else if (FilePath.StartsWith(GTAFolder.CurrentGTAFolder, StringComparison.OrdinalIgnoreCase)) saveAs = true;
if (!File.Exists(FilePath)) saveAs = true; if (!File.Exists(FilePath)) saveAs = true;
var fn = FilePath; var fn = FilePath;
@ -456,7 +456,7 @@ namespace CodeWalker.Forms
if (rpfFileEntry?.Parent != null) if (rpfFileEntry?.Parent != null)
{ {
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {

View File

@ -1,4 +1,6 @@
namespace CodeWalker.Forms using CodeWalker.WinForms;
namespace CodeWalker.Forms
{ {
partial class ModelForm partial class ModelForm
{ {

View File

@ -29,7 +29,6 @@ namespace CodeWalker.Forms
volatile bool formopen = false; volatile bool formopen = false;
//volatile bool running = false; //volatile bool running = false;
volatile bool pauserendering = false;
//volatile bool initialised = false; //volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch(); Stopwatch frametimer = new Stopwatch();
@ -46,6 +45,8 @@ namespace CodeWalker.Forms
System.Drawing.Point MouseLastPoint; System.Drawing.Point MouseLastPoint;
bool MouseInvert = Settings.Default.MouseInvert; bool MouseInvert = Settings.Default.MouseInvert;
int VerticesCount = 0;
Vector3 prevworldpos = new Vector3(0, 0, 0); //also the start pos Vector3 prevworldpos = new Vector3(0, 0, 0); //also the start pos
@ -69,6 +70,7 @@ namespace CodeWalker.Forms
} }
} }
public string FilePath { get; set; } public string FilePath { get; set; }
public bool Pauserendering { get; set; }
YdrFile Ydr = null; YdrFile Ydr = null;
YddFile Ydd = null; YddFile Ydd = null;
@ -128,13 +130,70 @@ namespace CodeWalker.Forms
public ModelForm(ExploreForm ExpForm = null) public ModelForm(ExploreForm ExpForm = null)
{ {
if (this.DesignMode) return;
InitializeComponent(); InitializeComponent();
exploreForm = ExpForm; exploreForm = ExpForm;
gameFileCache = ExpForm?.GetFileCache(); gameFileCache = ExpForm?.GetFileCache() ?? GameFileCacheFactory.Create();
if (ExpForm == null)
{
gameFileCache.EnableDlc = false;
gameFileCache.EnableMods = false;
gameFileCache.LoadPeds = false;
gameFileCache.LoadVehicles = false;
gameFileCache.LoadArchetypes = false;//to speed things up a little
gameFileCache.BuildExtendedJenkIndex = true;//to speed things up a little
gameFileCache.DoFullStringIndex = false;//to get all global text from DLC...
Task.Run(() => {
try
{
gameFileCache.Init();
if (DetailsPropertyGrid.SelectedObject is DrawableBase drawableBase)
{
UpdateDrawableUI(drawableBase);
}
if (Yft != null)
{
UpdateDrawableUI(Yft.Fragment?.Drawable);
}
UpdateStatus("Done loading");
}
catch(Exception ex)
{
Console.WriteLine(ex);
throw ex;
}
});
Task.Run(() =>
{
while (!IsDisposed) //run the file cache content thread until the form exits.
{
if (gameFileCache.IsInited)
{
gameFileCache.BeginFrame();
bool fcItemsPending = gameFileCache.ContentThreadProc();
if (!fcItemsPending)
{
Thread.Sleep(10);
}
}
else
{
Thread.Sleep(20);
}
}
});
}
Renderer = new Renderer(this, gameFileCache); Renderer = new Renderer(this, gameFileCache);
Renderer.waitforchildrentoload = false;
camera = Renderer.camera; camera = Renderer.camera;
timecycle = Renderer.timecycle; timecycle = Renderer.timecycle;
weather = Renderer.weather; weather = Renderer.weather;
@ -159,7 +218,7 @@ namespace CodeWalker.Forms
private void Init() private void Init()
{ {
//called from ModelForm_Load //called from ModelForm_Load
if (this.DesignMode) return;
if (!initedOk) if (!initedOk)
{ {
Close(); Close();
@ -260,13 +319,21 @@ namespace CodeWalker.Forms
public void BuffersResized(int w, int h) public void BuffersResized(int w, int h)
{ {
Renderer.BuffersResized(w, h); Renderer.BuffersResized(w, h);
if (WindowState == FormWindowState.Minimized && gameFileCache.IsInited)
{
Console.WriteLine("Clearing cache");
gameFileCache.Clear();
gameFileCache.IsInited = true;
GC.Collect();
}
} }
public void RenderScene(DeviceContext context) public void RenderScene(DeviceContext context)
{ {
float elapsed = (float)frametimer.Elapsed.TotalSeconds; float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart(); frametimer.Restart();
if (pauserendering) return; if (Pauserendering) return;
if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50)) if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50))
{ return; } //couldn't get a lock, try again next time { return; } //couldn't get a lock, try again next time
@ -627,6 +694,47 @@ namespace CodeWalker.Forms
selectedLight.UpdateRenderable = true; selectedLight.UpdateRenderable = true;
} }
public void ViewModel(string path)
{
var data = File.ReadAllBytes(path);
var fileEntry = RpfFile.CreateFileEntry(Path.GetFileName(path), path, ref data);
ViewModel(data, fileEntry);
}
public void ViewModel(byte[] data, RpfFileEntry e)
{
var nl = e?.NameLower ?? "";
var fe = Path.GetExtension(nl);
Show();
switch (fe)
{
case ".ydr":
var ydr = RpfFile.GetFile<YdrFile>(e, data);
LoadModel(ydr);
break;
case ".ydd":
var ydd = RpfFile.GetFile<YddFile>(e, data);
LoadModels(ydd);
break;
case ".yft":
var yft = RpfFile.GetFile<YftFile>(e, data);
LoadModel(yft);
break;
case ".ybn":
var ybn = RpfFile.GetFile<YbnFile>(e, data);
LoadModel(ybn);
break;
case ".ypt":
var ypt = RpfFile.GetFile<YptFile>(e, data);
LoadParticles(ypt);
break;
case ".ynv":
var ynv = RpfFile.GetFile<YnvFile>(e, data);
LoadNavmesh(ynv);
break;
}
}
private void RenderSingleItem() private void RenderSingleItem()
{ {
@ -794,10 +902,10 @@ namespace CodeWalker.Forms
Yft = yft; Yft = yft;
rpfFileEntry = Yft.RpfFileEntry; rpfFileEntry = Yft.RpfFileEntry;
ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0; ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0;
var namelower = Yft.RpfFileEntry?.GetShortNameLower(); var namelower = Yft.RpfFileEntry?.ShortName;
if (namelower?.EndsWith("_hi") ?? false) if (namelower?.EndsWith("_hi", StringComparison.OrdinalIgnoreCase) ?? false)
{ {
ModelHash = JenkHash.GenHash(namelower.Substring(0, namelower.Length - 3)); ModelHash = JenkHash.GenHashLower(namelower.Substring(0, namelower.Length - 3));
} }
if (ModelHash != 0) if (ModelHash != 0)
{ {
@ -843,6 +951,7 @@ namespace CodeWalker.Forms
UpdateBoundsUI(ybn); UpdateBoundsUI(ybn);
} }
public void LoadParticles(YptFile ypt) public void LoadParticles(YptFile ypt)
{ {
if (ypt == null) return; if (ypt == null) return;
@ -948,7 +1057,7 @@ namespace CodeWalker.Forms
if (gameFileCache == null) return; if (gameFileCache == null) return;
if (!gameFileCache.IsInited) return;//what to do here? wait for it..? if (!gameFileCache.IsInited) return;//what to do here? wait for it..?
var ycdhash = JenkHash.GenHash(name.ToLowerInvariant()); var ycdhash = JenkHash.GenHashLower(name);
var ycd = gameFileCache.GetYcd(ycdhash); var ycd = gameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded)) while ((ycd != null) && (!ycd.Loaded))
{ {
@ -1097,19 +1206,15 @@ namespace CodeWalker.Forms
} }
public void UpdateDrawableUI(DrawableBase drawable)
private void UpdateModelsUI(DrawableBase drawable, object detailsObject = null)
{ {
DetailsPropertyGrid.SelectedObject = detailsObject ?? drawable;
DrawableDrawFlags.Clear(); DrawableDrawFlags.Clear();
Renderer.SelectionModelDrawFlags.Clear(); Renderer.SelectionModelDrawFlags.Clear();
Renderer.SelectionGeometryDrawFlags.Clear(); Renderer.SelectionGeometryDrawFlags.Clear();
ModelsTreeView.Nodes.Clear(); ModelsTreeView.Nodes.Clear();
ModelsTreeView.ShowRootLines = false; ModelsTreeView.ShowRootLines = false;
TexturesTreeView.Nodes.Clear(); TexturesTreeView.Nodes.Clear();
if (drawable != null) if (drawable != null)
{ {
AddDrawableModelsTreeNodes(drawable.DrawableModels?.High, "High Detail", true); AddDrawableModelsTreeNodes(drawable.DrawableModels?.High, "High Detail", true);
@ -1162,6 +1267,15 @@ namespace CodeWalker.Forms
} }
} }
} }
private void UpdateModelsUI(DrawableBase drawable, object detailsObject = null)
{
DetailsPropertyGrid.SelectedObject = detailsObject ?? drawable;
UpdateDrawableUI(drawable);
}
private void UpdateModelsUI(Dictionary<uint, Drawable> dict) private void UpdateModelsUI(Dictionary<uint, Drawable> dict)
{ {
//DetailsPropertyGrid.SelectedObject = dict; //this won't look good... //DetailsPropertyGrid.SelectedObject = dict; //this won't look good...
@ -1238,6 +1352,7 @@ namespace CodeWalker.Forms
dnode.Tag = drawable; dnode.Tag = drawable;
dnode.Checked = check; dnode.Checked = check;
VerticesCount = 0;
AddDrawableModelsTreeNodes(drawable.DrawableModels?.High, "High Detail", true, dnode); AddDrawableModelsTreeNodes(drawable.DrawableModels?.High, "High Detail", true, dnode);
AddDrawableModelsTreeNodes(drawable.DrawableModels?.Med, "Medium Detail", false, dnode); AddDrawableModelsTreeNodes(drawable.DrawableModels?.Med, "Medium Detail", false, dnode);
AddDrawableModelsTreeNodes(drawable.DrawableModels?.Low, "Low Detail", false, dnode); AddDrawableModelsTreeNodes(drawable.DrawableModels?.Low, "Low Detail", false, dnode);
@ -1272,6 +1387,7 @@ namespace CodeWalker.Forms
foreach (var geom in model.Geometries) foreach (var geom in model.Geometries)
{ {
var gname = geom.ToString(); var gname = geom.ToString();
VerticesCount += geom.VerticesCount;
var gnode = mnode.Nodes.Add(gname); var gnode = mnode.Nodes.Add(gname);
gnode.Tag = geom; gnode.Tag = geom;
gnode.Checked = true;// check; gnode.Checked = true;// check;
@ -1545,6 +1661,10 @@ namespace CodeWalker.Forms
{ {
td = Ypt?.PtfxList?.TextureDictionary; td = Ypt?.PtfxList?.TextureDictionary;
} }
else if ((Ydd != null) && (Ydd.Loaded))
{
td = Ydd?.Drawables.First().ShaderGroup?.TextureDictionary;
}
if (td != null) if (td != null)
{ {
@ -1649,7 +1769,7 @@ namespace CodeWalker.Forms
} }
else else
{ {
if ((FilePath.ToLowerInvariant().StartsWith(GTAFolder.CurrentGTAFolder.ToLowerInvariant()))) saveAs = true; if (FilePath.StartsWith(GTAFolder.CurrentGTAFolder, StringComparison.OrdinalIgnoreCase)) saveAs = true;
if (!File.Exists(FilePath)) saveAs = true; if (!File.Exists(FilePath)) saveAs = true;
} }
@ -1727,7 +1847,7 @@ namespace CodeWalker.Forms
if (rpfSave) if (rpfSave)
{ {
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {
@ -1985,7 +2105,7 @@ namespace CodeWalker.Forms
File.WriteAllBytes(fpath, dds); File.WriteAllBytes(fpath, dds);
successcount++; successcount++;
} }
catch catch(Exception ex)
{ {
errordds.Add(tex.Name ?? "???"); errordds.Add(tex.Name ?? "???");
} }
@ -2303,7 +2423,7 @@ namespace CodeWalker.Forms
private void StatsUpdateTimer_Tick(object sender, EventArgs e) private void StatsUpdateTimer_Tick(object sender, EventArgs e)
{ {
StatsLabel.Text = Renderer.GetStatusText(); StatsLabel.Text = Renderer.GetStatusText() + $" verts: {VerticesCount}";
if (Renderer.timerunning) if (Renderer.timerunning)
{ {

View File

@ -226,7 +226,7 @@ namespace CodeWalker.Forms
if (ttex == null)//don't do this for embedded textures! if (ttex == null)//don't do this for embedded textures!
{ {
tex.Name = txt; tex.Name = txt;
tex.NameHash = JenkHash.GenHash(txt.ToLowerInvariant()); tex.NameHash = JenkHash.GenHashLower(txt);
} }
else else
{ {

View File

@ -246,7 +246,7 @@ namespace CodeWalker.Forms
if (rpfFileEntry?.Parent != null) if (rpfFileEntry?.Parent != null)
{ {
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {
@ -383,7 +383,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) saveAs = true; if (string.IsNullOrEmpty(FileName)) saveAs = true;
if (string.IsNullOrEmpty(FilePath)) saveAs = true; if (string.IsNullOrEmpty(FilePath)) saveAs = true;
else if ((FilePath.ToLowerInvariant().StartsWith(GTAFolder.CurrentGTAFolder.ToLowerInvariant()))) saveAs = true; else if (FilePath.StartsWith(GTAFolder.CurrentGTAFolder, StringComparison.OrdinalIgnoreCase)) saveAs = true;
if (!File.Exists(FilePath)) saveAs = true; if (!File.Exists(FilePath)) saveAs = true;
var fn = FilePath; var fn = FilePath;
@ -536,8 +536,8 @@ namespace CodeWalker.Forms
{ {
if (textsearch) if (textsearch)
{ {
if (((rd.Name?.ToLowerInvariant().Contains(textl)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) || if (((rd.Name?.Contains(textl, StringComparison.OrdinalIgnoreCase)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) ||
(rd.NameHash.ToString().ToLowerInvariant().Contains(textl))) rd.NameHash.ToString().Contains(textl, StringComparison.OrdinalIgnoreCase))
{ {
results.Add(rd); results.Add(rd);
} }

View File

@ -197,7 +197,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) saveAs = true; if (string.IsNullOrEmpty(FileName)) saveAs = true;
if (string.IsNullOrEmpty(FilePath)) saveAs = true; if (string.IsNullOrEmpty(FilePath)) saveAs = true;
else if ((FilePath.ToLowerInvariant().StartsWith(GTAFolder.CurrentGTAFolder.ToLowerInvariant()))) saveAs = true; else if (FilePath.StartsWith(GTAFolder.CurrentGTAFolder, StringComparison.OrdinalIgnoreCase)) saveAs = true;
if (!File.Exists(FilePath)) saveAs = true; if (!File.Exists(FilePath)) saveAs = true;
var fn = FilePath; var fn = FilePath;
@ -277,7 +277,7 @@ namespace CodeWalker.Forms
return false; return false;
} }
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {

View File

@ -184,7 +184,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) saveAs = true; if (string.IsNullOrEmpty(FileName)) saveAs = true;
if (string.IsNullOrEmpty(FilePath)) saveAs = true; if (string.IsNullOrEmpty(FilePath)) saveAs = true;
else if ((FilePath.ToLowerInvariant().StartsWith(GTAFolder.CurrentGTAFolder.ToLowerInvariant()))) saveAs = true; else if (FilePath.StartsWith(GTAFolder.CurrentGTAFolder, StringComparison.OrdinalIgnoreCase)) saveAs = true;
if (!File.Exists(FilePath)) saveAs = true; if (!File.Exists(FilePath)) saveAs = true;
var fn = FilePath; var fn = FilePath;
@ -228,7 +228,7 @@ namespace CodeWalker.Forms
return false; return false;
} }
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {

View File

@ -249,7 +249,6 @@ namespace CodeWalker.Forms
if (tex == null) return; if (tex == null) return;
tex.Name = CurrentTexture.Name; tex.Name = CurrentTexture.Name;
tex.NameHash = CurrentTexture.NameHash;
tex.Usage = CurrentTexture.Usage; tex.Usage = CurrentTexture.Usage;
tex.UsageFlags = CurrentTexture.UsageFlags; tex.UsageFlags = CurrentTexture.UsageFlags;
tex.Unknown_32h = CurrentTexture.Unknown_32h; tex.Unknown_32h = CurrentTexture.Unknown_32h;
@ -285,7 +284,6 @@ namespace CodeWalker.Forms
var tex = CurrentTexture; var tex = CurrentTexture;
tex.Name = name; tex.Name = name;
tex.NameHash = JenkHash.GenHash(name.ToLowerInvariant());
var textures = new List<Texture>(); var textures = new List<Texture>();
textures.AddRange(TexDict.Textures.data_items); textures.AddRange(TexDict.Textures.data_items);
@ -323,8 +321,7 @@ namespace CodeWalker.Forms
var dds = File.ReadAllBytes(fn); var dds = File.ReadAllBytes(fn);
var tex = DDSIO.GetTexture(dds); var tex = DDSIO.GetTexture(dds);
tex.Name = Path.GetFileNameWithoutExtension(fn); tex.Name = Path.GetFileNameWithoutExtension(fn);
tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant()); JenkIndex.EnsureLower(tex.Name);
JenkIndex.Ensure(tex.Name?.ToLowerInvariant());
return tex; return tex;
} }
catch catch
@ -456,7 +453,7 @@ namespace CodeWalker.Forms
if (!saveas) if (!saveas)
{ {
isinrpf = true; isinrpf = true;
if (!rpfFileEntry.Path.ToLowerInvariant().StartsWith("mods")) if (!rpfFileEntry.Path.StartsWith("mods", StringComparison.OrdinalIgnoreCase))
{ {
if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes) if (MessageBox.Show("This file is NOT located in the mods folder - Are you SURE you want to save this file?\r\nWARNING: This could cause permanent damage to your game!!!", "WARNING: Are you sure about this?", MessageBoxButtons.YesNo) != DialogResult.Yes)
{ {

View File

@ -2,17 +2,25 @@
using CodeWalker.Properties; using CodeWalker.Properties;
using System;
using System.Diagnostics;
namespace CodeWalker.GameFiles namespace CodeWalker.GameFiles
{ {
public static class GameFileCacheFactory public static class GameFileCacheFactory
{ {
public static GameFileCache _instance = null;
public static GameFileCache Create() public static GameFileCache Create()
{
if (_instance == null)
{ {
var s = Settings.Default; var s = Settings.Default;
return new GameFileCache(s.CacheSize, s.CacheTime, GTAFolder.CurrentGTAFolder, s.DLC, s.EnableMods, s.ExcludeFolders); GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
_instance = new GameFileCache(s.CacheSize, s.CacheTime, GTAFolder.CurrentGTAFolder, s.DLC, s.EnableMods, s.ExcludeFolders);
GTAFolder.OnGTAFolderChanged += _instance.SetGtaFolder;
}
return _instance;
} }
} }

View File

@ -27,9 +27,10 @@ namespace CodeWalker
public Renderer Renderer = null; public Renderer Renderer = null;
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } } public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
public bool Pauserendering { get; set; }
volatile bool formopen = false; volatile bool formopen = false;
volatile bool running = false; volatile bool running = false;
volatile bool pauserendering = false;
//volatile bool initialised = false; //volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch(); Stopwatch frametimer = new Stopwatch();
@ -211,7 +212,7 @@ namespace CodeWalker
float elapsed = (float)frametimer.Elapsed.TotalSeconds; float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart(); frametimer.Restart();
if (pauserendering) return; if (Pauserendering) return;
GameFileCache.BeginFrame(); GameFileCache.BeginFrame();

View File

@ -1,9 +1,15 @@
using CodeWalker.Properties; using CodeWalker.Forms;
using CodeWalker.GameFiles;
using CodeWalker.Properties;
using CodeWalker.Utils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Shell; using System.Windows.Shell;
@ -12,18 +18,23 @@ namespace CodeWalker
{ {
static class Program static class Program
{ {
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main(string[] args) static void Main(string[] args)
{ {
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ConsoleWindow.Hide();
bool menumode = false; bool menumode = false;
bool explorermode = false; bool explorermode = false;
bool projectmode = false; bool projectmode = false;
bool vehiclesmode = false; bool vehiclesmode = false;
bool pedsmode = false; bool pedsmode = false;
string path = null;
if ((args != null) && (args.Length > 0)) if ((args != null) && (args.Length > 0))
{ {
foreach (string arg in args) foreach (string arg in args)
@ -49,6 +60,18 @@ namespace CodeWalker
{ {
pedsmode = true; pedsmode = true;
} }
try
{
if (File.Exists(arg))
{
path = arg;
}
} catch (Exception ex)
{
Debug.WriteLine(ex);
Console.WriteLine(ex);
}
} }
} }
@ -89,6 +112,14 @@ namespace CodeWalker
{ {
Application.Run(new PedsForm()); Application.Run(new PedsForm());
} }
else if (path != null)
{
var modelForm = new ModelForm();
modelForm.Load += new EventHandler(async (sender, eventArgs) => {
modelForm.ViewModel(path);
});
Application.Run(modelForm);
}
else else
{ {
Application.Run(new WorldForm()); Application.Run(new WorldForm());

View File

@ -123,8 +123,8 @@ namespace CodeWalker.Project
XmlElement enode = node as XmlElement; XmlElement enode = node as XmlElement;
var hashstr = Xml.GetChildInnerText(node, "ModelHash").ToLowerInvariant(); var hashstr = Xml.GetChildInnerText(node, "ModelHash");
if (hashstr.StartsWith("0x")) hashstr = hashstr.Substring(2); if (hashstr.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) hashstr = hashstr.Substring(2);
ModelHash = Convert.ToUInt32(hashstr, 16); ModelHash = Convert.ToUInt32(hashstr, 16);
Type = Xml.GetChildIntInnerText(node, "Type"); Type = Xml.GetChildIntInnerText(node, "Type");

View File

@ -102,7 +102,7 @@ namespace CodeWalker.Project.Panels
{ {
ymapname = ymap.Name.ToLowerInvariant(); ymapname = ymap.Name.ToLowerInvariant();
} }
if (ymapname.EndsWith(".ymap")) if (ymapname.EndsWith(".ymap", StringComparison.OrdinalIgnoreCase))
{ {
ymapname = ymapname.Substring(0, ymapname.Length - 5); ymapname = ymapname.Substring(0, ymapname.Length - 5);
} }

View File

@ -264,12 +264,10 @@ namespace CodeWalker.Project.Panels
lodymap._CMapData.name = JenkHash.GenHash(lodname); lodymap._CMapData.name = JenkHash.GenHash(lodname);
lodymap.RpfFileEntry = new RpfResourceFileEntry(); lodymap.RpfFileEntry = new RpfResourceFileEntry();
lodymap.RpfFileEntry.Name = lodname + ".ymap"; lodymap.RpfFileEntry.Name = lodname + ".ymap";
lodymap.RpfFileEntry.NameLower = lodname + ".ymap";
distymap.Name = distname; distymap.Name = distname;
distymap._CMapData.name = JenkHash.GenHash(distname); distymap._CMapData.name = JenkHash.GenHash(distname);
distymap.RpfFileEntry = new RpfResourceFileEntry(); distymap.RpfFileEntry = new RpfResourceFileEntry();
distymap.RpfFileEntry.Name = distname + ".ymap"; distymap.RpfFileEntry.Name = distname + ".ymap";
distymap.RpfFileEntry.NameLower = distname + ".ymap";
lodymap._CMapData.parent = distymap._CMapData.name; lodymap._CMapData.parent = distymap._CMapData.name;
lodymap.Loaded = true; lodymap.Loaded = true;

View File

@ -510,13 +510,11 @@ namespace CodeWalker.Project
} }
public bool RenameYmap(string oldfilename, string newfilename) public bool RenameYmap(string oldfilename, string newfilename)
{ {
oldfilename = oldfilename.ToLowerInvariant();
newfilename = newfilename.ToLowerInvariant();
for (int i = 0; i < YmapFilenames.Count; i++) for (int i = 0; i < YmapFilenames.Count; i++)
{ {
if (YmapFilenames[i]?.ToLowerInvariant() == oldfilename) if (YmapFilenames[i]?.Equals(oldfilename, StringComparison.OrdinalIgnoreCase) ?? false)
{ {
YmapFilenames[i] = newfilename; YmapFilenames[i] = newfilename.ToLowerInvariant();
HasChanged = true; HasChanged = true;
return true; return true;
} }
@ -580,13 +578,11 @@ namespace CodeWalker.Project
} }
public bool RenameYtyp(string oldfilename, string newfilename) public bool RenameYtyp(string oldfilename, string newfilename)
{ {
oldfilename = oldfilename.ToLowerInvariant();
newfilename = newfilename.ToLowerInvariant();
for (int i = 0; i < YtypFilenames.Count; i++) for (int i = 0; i < YtypFilenames.Count; i++)
{ {
if (YtypFilenames[i]?.ToLowerInvariant() == oldfilename) if (YtypFilenames[i]?.Equals(oldfilename, StringComparison.OrdinalIgnoreCase) ?? false)
{ {
YtypFilenames[i] = newfilename; YtypFilenames[i] = newfilename.ToLowerInvariant();
HasChanged = true; HasChanged = true;
return true; return true;
} }
@ -609,7 +605,7 @@ namespace CodeWalker.Project
{ {
string relpath = GetRelativePath(ybn.FilePath); string relpath = GetRelativePath(ybn.FilePath);
if (string.IsNullOrEmpty(relpath)) relpath = ybn.Name; if (string.IsNullOrEmpty(relpath)) relpath = ybn.Name;
if (YndFilenames.Contains(relpath)) return false; if (YndFilenames.Contains(relpath, StringComparer.OrdinalIgnoreCase)) return false;
YbnFilenames.Add(relpath); YbnFilenames.Add(relpath);
YbnFiles.Add(ybn); YbnFiles.Add(ybn);
return true; return true;
@ -626,10 +622,9 @@ namespace CodeWalker.Project
public bool ContainsYbn(string filename) public bool ContainsYbn(string filename)
{ {
bool found = false; bool found = false;
filename = filename.ToLowerInvariant();
foreach (var yndfn in YbnFilenames) foreach (var yndfn in YbnFilenames)
{ {
if (yndfn == filename) if (yndfn.Equals(filename, StringComparison.OrdinalIgnoreCase))
{ {
found = true; found = true;
break; break;
@ -651,7 +646,7 @@ namespace CodeWalker.Project
newfilename = newfilename.ToLowerInvariant(); newfilename = newfilename.ToLowerInvariant();
for (int i = 0; i < YbnFilenames.Count; i++) for (int i = 0; i < YbnFilenames.Count; i++)
{ {
if (YbnFilenames[i]?.ToLowerInvariant() == oldfilename) if (YbnFilenames[i]?.Equals(oldfilename, StringComparison.OrdinalIgnoreCase) ?? false)
{ {
YbnFilenames[i] = newfilename; YbnFilenames[i] = newfilename;
HasChanged = true; HasChanged = true;
@ -937,7 +932,6 @@ namespace CodeWalker.Project
RelFile relfile = new RelFile(); RelFile relfile = new RelFile();
relfile.RpfFileEntry = new RpfResourceFileEntry(); relfile.RpfFileEntry = new RpfResourceFileEntry();
relfile.RpfFileEntry.Name = Path.GetFileName(filename); relfile.RpfFileEntry.Name = Path.GetFileName(filename);
relfile.RpfFileEntry.NameHash = JenkHash.GenHash(relfile.RpfFileEntry.Name);
relfile.FilePath = GetFullFilePath(filename); relfile.FilePath = GetFullFilePath(filename);
relfile.Name = relfile.RpfFileEntry.Name; relfile.Name = relfile.RpfFileEntry.Name;
if (!AddAudioRelFile(relfile)) return null; if (!AddAudioRelFile(relfile)) return null;

View File

@ -7102,12 +7102,6 @@ namespace CodeWalker.Project
var ymap = CurrentProjectFile.YmapFiles[i]; var ymap = CurrentProjectFile.YmapFiles[i];
if (ymap.Loaded) if (ymap.Loaded)
{ {
// make sure we're replacing ymaps that have been added by the end-user.
if (ymap.RpfFileEntry.ShortNameHash == 0)
{
ymap.RpfFileEntry.ShortNameHash = JenkHash.GenHash(ymap.RpfFileEntry.GetShortNameLower());
}
ymaps[ymap.RpfFileEntry.ShortNameHash] = ymap; ymaps[ymap.RpfFileEntry.ShortNameHash] = ymap;
} }
} }

View File

@ -12,7 +12,7 @@ namespace CodeWalker.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.5.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.7.0.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -155,42 +155,6 @@ namespace CodeWalker.Properties {
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool LODLights {
get {
return ((bool)(this["LODLights"]));
}
set {
this["LODLights"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool NatrualAmbientLight {
get {
return ((bool)(this["NatrualAmbientLight"]));
}
set {
this["NatrualAmbientLight"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool ArtificialAmbientLight {
get {
return ((bool)(this["ArtificialAmbientLight"]));
}
set {
this["ArtificialAmbientLight"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")] [global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -527,54 +491,6 @@ namespace CodeWalker.Properties {
} }
} }
[global::System.Configuration.UserScopedSetting()]
[global::System.Diagnostics.DebuggerNonUserCode()]
[global::System.Configuration.DefaultSettingValue("EXTRASUNNY")]
public string Weather {
get {
return ((string)(this["Weather"]));
}
set {
this["Weather"] = value;
}
}
[global::System.Configuration.UserScopedSetting()]
[global::System.Diagnostics.DebuggerNonUserCode()]
[global::System.Configuration.DefaultSettingValue("GLOBAL")]
public string Region {
get {
return ((string)(this["Region"]));
}
set {
this["Region"] = value;
}
}
[global::System.Configuration.UserScopedSetting()]
[global::System.Diagnostics.DebuggerNonUserCode()]
[global::System.Configuration.DefaultSettingValue("contrails")]
public string Clouds {
get {
return ((string)(this["Clouds"]));
}
set {
this["Clouds"] = value;
}
}
[global::System.Configuration.UserScopedSetting()]
[global::System.Diagnostics.DebuggerNonUserCode()]
[global::System.Configuration.DefaultSettingValue("720")]
public int TimeOfDay {
get {
return ((int)(this["TimeOfDay"]));
}
set {
this["TimeOfDay"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("DiffuseSampler")] [global::System.Configuration.DefaultSettingValueAttribute("DiffuseSampler")]
@ -914,5 +830,101 @@ namespace CodeWalker.Properties {
this["RPFExplorerStartFolder"] = value; this["RPFExplorerStartFolder"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("720")]
public int TimeOfDay {
get {
return ((int)(this["TimeOfDay"]));
}
set {
this["TimeOfDay"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool LODLights {
get {
return ((bool)(this["LODLights"]));
}
set {
this["LODLights"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Global")]
public string Region {
get {
return ((string)(this["Region"]));
}
set {
this["Region"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("contrails")]
public string Clouds {
get {
return ((string)(this["Clouds"]));
}
set {
this["Clouds"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public string Weather {
get {
return ((string)(this["Weather"]));
}
set {
this["Weather"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool NatrualAmbientLight {
get {
return ((bool)(this["NatrualAmbientLight"]));
}
set {
this["NatrualAmbientLight"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool ArtificialAmbientLight {
get {
return ((bool)(this["ArtificialAmbientLight"]));
}
set {
this["ArtificialAmbientLight"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2")]
public int AntiAliasing {
get {
return ((int)(this["AntiAliasing"]));
}
set {
this["AntiAliasing"] = value;
}
}
} }
} }

View File

@ -216,5 +216,29 @@
<Setting Name="RPFExplorerStartFolder" Type="System.String" Scope="User"> <Setting Name="RPFExplorerStartFolder" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="TimeOfDay" Type="System.Int32" Scope="User">
<Value Profile="(Default)">720</Value>
</Setting>
<Setting Name="LODLights" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="Region" Type="System.String" Scope="User">
<Value Profile="(Default)">Global</Value>
</Setting>
<Setting Name="Clouds" Type="System.String" Scope="User">
<Value Profile="(Default)">contrails</Value>
</Setting>
<Setting Name="Weather" Type="System.String" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="NatrualAmbientLight" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ArtificialAmbientLight" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="AntiAliasing" Type="System.Int32" Scope="User">
<Value Profile="(Default)">2</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -19,7 +19,7 @@ namespace CodeWalker.Rendering
Form Form { get; } Form Form { get; }
public bool Pauserendering { get; set; }
void InitScene(Device device); void InitScene(Device device);
void CleanupScene(); void CleanupScene();
void RenderScene(DeviceContext context); void RenderScene(DeviceContext context);

View File

@ -7,12 +7,14 @@ using SharpDX.Direct3D11;
using SharpDX.DXGI; using SharpDX.DXGI;
using Color = SharpDX.Color; using Color = SharpDX.Color;
using Device = SharpDX.Direct3D11.Device; using Device = SharpDX.Direct3D11.Device;
using Buffer = SharpDX.Direct3D11.Buffer;
using DriverType = SharpDX.Direct3D.DriverType; using DriverType = SharpDX.Direct3D.DriverType;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using SharpDX; using SharpDX;
using SharpDX.Direct3D; using SharpDX.Direct3D;
using System.Runtime;
using CodeWalker.Core.Utils;
using CodeWalker.Properties;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -32,7 +34,7 @@ namespace CodeWalker.Rendering
private volatile bool Rendering = false; private volatile bool Rendering = false;
private volatile bool Resizing = false; private volatile bool Resizing = false;
private object syncroot = new object(); //for thread safety private object syncroot = new object(); //for thread safety
public int multisamplecount { get; private set; } = 4; //should be a setting.. public int multisamplecount { get; private set; } = Settings.Default.AntiAliasing;
public int multisamplequality { get; private set; } = 0; //should be a setting... public int multisamplequality { get; private set; } = 0; //should be a setting...
public Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross public Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross
private System.Drawing.Size beginSize; private System.Drawing.Size beginSize;
@ -209,7 +211,6 @@ namespace CodeWalker.Rendering
{ {
if (Resizing) return; if (Resizing) return;
Monitor.Enter(syncroot); Monitor.Enter(syncroot);
int width = dxform.Form.ClientSize.Width; int width = dxform.Form.ClientSize.Width;
int height = dxform.Form.ClientSize.Height; int height = dxform.Form.ClientSize.Height;
@ -246,6 +247,7 @@ namespace CodeWalker.Rendering
Cleanup(); Cleanup();
} }
} }
private void Dxform_ClientSizeChanged(object sender, EventArgs e) private void Dxform_ClientSizeChanged(object sender, EventArgs e)
{ {
Resize(); Resize();
@ -273,21 +275,45 @@ namespace CodeWalker.Rendering
private void StartRenderLoop() private void StartRenderLoop()
{ {
Running = true; Running = true;
new Thread(new ThreadStart(RenderLoop)).Start(); new Task(RenderLoop, TaskCreationOptions.LongRunning).Start(TaskScheduler.Default);
//new Thread(new ThreadStart(RenderLoop)).Start();
} }
private void RenderLoop() private void RenderLoop()
{ {
//SharpDX.Configuration.EnableObjectTracking = true;
//Task.Run(async () =>
//{
// while (true)
// {
// Console.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
// await Task.Delay(5000);
// }
//});
Thread.CurrentThread.Name = "RenderLoop";
while (Running) while (Running)
{ {
while (Resizing) while (Resizing)
{ {
swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw
} }
if (dxform.Form.WindowState == FormWindowState.Minimized)
{
dxform.Pauserendering = true;
Console.WriteLine("Window is minimized");
dxform.RenderScene(context);
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
while (dxform.Form.WindowState == FormWindowState.Minimized) while (dxform.Form.WindowState == FormWindowState.Minimized)
{ {
Thread.Sleep(10); //don't hog CPU when minimised Thread.Sleep(100); //don't hog CPU when minimised
if (dxform.Form.IsDisposed) return; //if closed while minimised if (dxform.Form.IsDisposed) return; //if closed while minimised
} }
dxform.Pauserendering = false;
Console.WriteLine("Window is maximized");
}
if (Form.ActiveForm == null) if (Form.ActiveForm == null)
{ {
Thread.Sleep(100); //reduce the FPS when the app isn't active (maybe this should be configurable?) Thread.Sleep(100); //reduce the FPS when the app isn't active (maybe this should be configurable?)
@ -295,13 +321,17 @@ namespace CodeWalker.Rendering
} }
Rendering = true; Rendering = true;
if(!Monitor.TryEnter(syncroot, 50)) try
{ {
if (!Monitor.TryEnter(syncroot, 50))
{
Console.WriteLine("Failed to get lock for syncroot");
Thread.Sleep(10); //don't hog CPU when not able to render... Thread.Sleep(10); //don't hog CPU when not able to render...
continue; continue;
} }
bool ok = true; bool ok = true;
try try
{ {
context.OutputMerger.SetRenderTargets(depthview, targetview); context.OutputMerger.SetRenderTargets(depthview, targetview);
@ -333,10 +363,12 @@ namespace CodeWalker.Rendering
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString()); MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
} }
} }
}
finally
{
Monitor.Exit(syncroot); Monitor.Exit(syncroot);
Rendering = false; Rendering = false;
}
} }
} }

View File

@ -1475,6 +1475,7 @@ namespace CodeWalker.Rendering
public float FalloffExponent; public float FalloffExponent;
public float InnerAngle;//for cone public float InnerAngle;//for cone
public float OuterAngleOrCapExt;//outer angle for cone, cap extent for capsule public float OuterAngleOrCapExt;//outer angle for cone, cap extent for capsule
public float Distance;
} }
public LODLight[] Points; public LODLight[] Points;
@ -1512,7 +1513,7 @@ namespace CodeWalker.Rendering
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
{ {
var l = ll.LodLights[i]; var l = ll.LodLights[i];
if (l.Enabled == false) continue; if (l.Enabled == false || l.Visible == false) continue;
var light = new LODLight(); var light = new LODLight();
light.Position = l.Position; light.Position = l.Position;
light.Colour = (uint)l.Colour.ToBgra(); light.Colour = (uint)l.Colour.ToBgra();
@ -1549,6 +1550,58 @@ namespace CodeWalker.Rendering
} }
public (int, int, int) UpdateLods(Vector3 refPos)
{
for (int i = 0; i < Points.Length; i++)
{
Points[i].Distance = Vector3.DistanceSquared(refPos, Points[i].Position);
}
for (int i = 0; i < Spots.Length; i++)
{
Spots[i].Distance = Vector3.DistanceSquared(refPos, Spots[i].Position);
}
for (int i = 0; i < Caps.Length; i++)
{
Caps[i].Distance = Vector3.DistanceSquared(refPos, Caps[i].Position);
}
Array.Sort(Points, (a, b) => a.Distance.CompareTo(b.Distance));
Array.Sort(Spots, (a, b) => a.Distance.CompareTo(b.Distance));
Array.Sort(Caps, (a, b) => a.Distance.CompareTo(b.Distance));
var spotsIndex = 0;
var pointsIndex = 0;
var capsIndex = 0;
for (int i = 0; i < Points.Length; i++)
{
if (Points[i].Distance > 100_000)
{
pointsIndex = i;
break;
}
}
for (int i = 0; i < Spots.Length; i++)
{
if (Spots[i].Distance > 100_000)
{
spotsIndex = i;
break;
}
}
for (int i = 0; i < Caps.Length; i++)
{
if (Caps[i].Distance > 100_000)
{
capsIndex = i;
break;
}
}
return (pointsIndex, spotsIndex, capsIndex);
}
public override void Load(Device device) public override void Load(Device device)
{ {
if ((Points != null) && (Points.Length > 0)) if ((Points != null) && (Points.Length > 0))

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static CodeWalker.Rendering.RenderableLODLights;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -172,11 +173,7 @@ namespace CodeWalker.Rendering
public Renderer(DXForm form, GameFileCache cache) public Renderer(DXForm form, GameFileCache cache)
{ {
Form = form; Form = form;
gameFileCache = cache; gameFileCache = cache ?? GameFileCacheFactory.Create();
if (gameFileCache == null)
{
gameFileCache = GameFileCacheFactory.Create();
}
renderableCache = new RenderableCache(); renderableCache = new RenderableCache();
var s = Settings.Default; var s = Settings.Default;
@ -384,15 +381,13 @@ namespace CodeWalker.Rendering
public string GetStatusText() public string GetStatusText()
{ {
int rgc = (shaders != null) ? shaders.RenderedGeometries : 0; var rgc = shaders?.RenderedGeometries ?? 0;
int crc = renderableCache.LoadedRenderableCount; var crc = renderableCache.LoadedRenderableCount;
int ctc = renderableCache.LoadedTextureCount; var ctc = renderableCache.LoadedTextureCount;
int tcrc = renderableCache.MemCachedRenderableCount; var vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
int tctc = renderableCache.MemCachedTextureCount; var vram = TextUtil.GetBytesReadable(vr);
long vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
string vram = TextUtil.GetBytesReadable(vr); return $"Drawn: {rgc} geom, Loaded: {crc} dr, {ctc} tx, Vram: {vram}, Fps: {fps}";
//StatsLabel.Text = string.Format("Drawn: {0} geom, Loaded: {1}/{5} dr, {2}/{6} tx, Vram: {3}, Fps: {4}", rgc, crc, ctc, vram, fps, tcrc, tctc);
return string.Format("Drawn: {0} geom, Loaded: {1} dr, {2} tx, Vram: {3}, Fps: {4}", rgc, crc, ctc, vram, fps);
} }
@ -1850,7 +1845,7 @@ namespace CodeWalker.Rendering
public void RenderWorld(Dictionary<MetaHash, YmapFile> renderworldVisibleYmapDict, IEnumerable<Entity> spaceEnts) public void RenderWorld(Dictionary<MetaHash, YmapFile> ymapsWithinStreamingExtents, IEnumerable<Entity> spaceEnts)
{ {
renderworldentities.Clear(); renderworldentities.Clear();
renderworldrenderables.Clear(); renderworldrenderables.Clear();
@ -1860,7 +1855,7 @@ namespace CodeWalker.Rendering
RequiredParents.Clear(); RequiredParents.Clear();
RenderEntities.Clear(); RenderEntities.Clear();
foreach (var ymap in renderworldVisibleYmapDict.Values) foreach (var ymap in ymapsWithinStreamingExtents.Values)
{ {
if (!RenderWorldYmapIsVisible(ymap)) continue; if (!RenderWorldYmapIsVisible(ymap)) continue;
VisibleYmaps.Add(ymap); VisibleYmaps.Add(ymap);
@ -1875,7 +1870,7 @@ namespace CodeWalker.Rendering
LodManager.ShowScriptedYmaps = ShowScriptedYmaps; LodManager.ShowScriptedYmaps = ShowScriptedYmaps;
LodManager.LODLightsEnabled = renderlodlights; LodManager.LODLightsEnabled = renderlodlights;
LodManager.HDLightsEnabled = renderlights; LodManager.HDLightsEnabled = renderlights;
LodManager.Update(renderworldVisibleYmapDict, camera, currentElapsedTime); LodManager.Update(ymapsWithinStreamingExtents, camera, currentElapsedTime);
foreach (var updatelodlights in LodManager.UpdateLodLights) foreach (var updatelodlights in LodManager.UpdateLodLights)
{ {
@ -2012,120 +2007,6 @@ namespace CodeWalker.Rendering
RenderWorldYmapExtras(); RenderWorldYmapExtras();
} }
public void RenderWorld_Orig(Dictionary<MetaHash, YmapFile> renderworldVisibleYmapDict, IEnumerable<Entity> spaceEnts)
{
renderworldentities.Clear();
renderworldrenderables.Clear();
VisibleYmaps.Clear();
foreach (var ymap in renderworldVisibleYmapDict.Values)
{
if (!RenderWorldYmapIsVisible(ymap)) continue;
VisibleYmaps.Add(ymap);
}
RenderWorldAdjustMapViewCamera();
for (int y = 0; y < VisibleYmaps.Count; y++)
{
var ymap = VisibleYmaps[y];
YmapFile pymap = ymap.Parent;
if ((pymap == null) && (ymap._CMapData.parent != 0))
{
renderworldVisibleYmapDict.TryGetValue(ymap._CMapData.parent, out pymap);
ymap.Parent = pymap;
}
if (ymap.RootEntities != null)
{
for (int i = 0; i < ymap.RootEntities.Length; i++)
{
var ent = ymap.RootEntities[i];
int pind = ent._CEntityDef.parentIndex;
if (pind >= 0) //connect root entities to parents if they have them..
{
YmapEntityDef p = null;
if ((pymap != null) && (pymap.AllEntities != null))
{
if ((pind < pymap.AllEntities.Length))
{
p = pymap.AllEntities[pind];
ent.Parent = p;
ent.ParentName = p._CEntityDef.archetypeName;
}
}
else
{ }//should only happen if parent ymap not loaded yet...
}
RenderWorldRecurseCalcEntityVisibility(ent);
}
}
}
for (int y = 0; y < VisibleYmaps.Count; y++)
{
var ymap = VisibleYmaps[y];
if (ymap.RootEntities != null)
{
for (int i = 0; i < ymap.RootEntities.Length; i++)
{
var ent = ymap.RootEntities[i];
RenderWorldRecurseAddEntities(ent);
}
}
}
if (spaceEnts != null)
{
foreach (var ae in spaceEnts) //used by active space entities (eg "bullets")
{
if (ae.EntityDef == null) continue; //nothing to render...
RenderWorldCalcEntityVisibility(ae.EntityDef);
renderworldentities.Add(ae.EntityDef);
}
}
if (renderentities)
{
for (int i = 0; i < renderworldentities.Count; i++)
{
var ent = renderworldentities[i];
var arch = ent.Archetype;
var pent = ent.Parent;
var drawable = gameFileCache.TryGetDrawable(arch);
Renderable rndbl = TryGetRenderable(arch, drawable);
if ((rndbl != null) && rndbl.IsLoaded && (rndbl.AllTexturesLoaded || !waitforchildrentoload))
{
RenderableEntity rent = new RenderableEntity();
rent.Entity = ent;
rent.Renderable = rndbl;
renderworldrenderables.Add(rent);
}
else if (waitforchildrentoload)
{
//todo: render parent if children loading.......
}
}
for (int i = 0; i < renderworldrenderables.Count; i++)
{
var rent = renderworldrenderables[i];
var ent = rent.Entity;
var arch = ent.Archetype;
if (HideEntities.ContainsKey(ent.EntityHash)) continue; //don't render hidden entities!
RenderArchetype(arch, ent, rent.Renderable, false);
}
}
RenderWorldYmapExtras();
}
private void RenderWorldCalcEntityVisibility(YmapEntityDef ent) private void RenderWorldCalcEntityVisibility(YmapEntityDef ent)
{ {
float dist = (ent.Position - camera.Position).Length(); float dist = (ent.Position - camera.Position).Length();
@ -3231,7 +3112,7 @@ namespace CodeWalker.Rendering
Vector3 bbmax = (arche != null) ? arche.BBMax : rndbl.Key.BoundingBoxMax; Vector3 bbmax = (arche != null) ? arche.BBMax : rndbl.Key.BoundingBoxMax;
Vector3 bscen = (arche != null) ? arche.BSCenter : rndbl.Key.BoundingCenter; Vector3 bscen = (arche != null) ? arche.BSCenter : rndbl.Key.BoundingCenter;
float radius = (arche != null) ? arche.BSRadius : rndbl.Key.BoundingSphereRadius; float radius = (arche != null) ? arche.BSRadius : rndbl.Key.BoundingSphereRadius;
float distance = 0;// (camrel + bscen).Length(); float distance = 0;//(camrel + bscen).Length();
bool interiorent = false; bool interiorent = false;
bool castshadow = true; bool castshadow = true;
@ -4154,9 +4035,9 @@ namespace CodeWalker.Rendering
{ {
if (VisibleLightsPrev.Contains(light) == false) if (VisibleLightsPrev.Contains(light) == false)
{ {
if (LodLightsDict.TryGetValue(light.Hash, out var lodlight)) if (LodLightsDict.TryGetValue(light.Hash, out var lodlight) && lodlight.Enabled)
{ {
lodlight.Enabled = false; lodlight.Enabled = true;
UpdateLodLights.Add(lodlight.LodLights); UpdateLodLights.Add(lodlight.LodLights);
} }
} }
@ -4165,18 +4046,36 @@ namespace CodeWalker.Rendering
{ {
if (VisibleLights.Contains(light) == false) if (VisibleLights.Contains(light) == false)
{ {
if (LodLightsDict.TryGetValue(light.Hash, out var lodlight)) if (LodLightsDict.TryGetValue(light.Hash, out var lodlight) && !lodlight.Enabled)
{ {
lodlight.Enabled = true; lodlight.Enabled = false;
UpdateLodLights.Add(lodlight.LodLights); UpdateLodLights.Add(lodlight.LodLights);
} }
} }
} }
//foreach (var light in LodLightsDict.Values)
//{
// if (LightVisible(light))
// {
// if (light.Visible)
// {
// light.Visible = false;
// UpdateLodLights.Add(light.LodLights);
// }
var vl = VisibleLights; // } else
VisibleLights = VisibleLightsPrev; // {
VisibleLightsPrev = vl; // if (!light.Visible)
// {
// light.Visible = true;
// UpdateLodLights.Add(light.LodLights);
// }
// }
//}
(VisibleLightsPrev, VisibleLights) = (VisibleLights, VisibleLightsPrev);
} }
private void RecurseAddVisibleLeaves(YmapEntityDef ent) private void RecurseAddVisibleLeaves(YmapEntityDef ent)
@ -4254,6 +4153,21 @@ namespace CodeWalker.Rendering
return Camera.ViewFrustum.ContainsAABBNoClip(ref ent.BBCenter, ref ent.BBExtent); return Camera.ViewFrustum.ContainsAABBNoClip(ref ent.BBCenter, ref ent.BBExtent);
} }
} }
private bool LightVisible(YmapLODLight lodLight)
{
var position = lodLight.Position;
var extent = new Vector3(lodLight.Falloff, lodLight.Falloff, lodLight.Falloff);
if (MapViewEnabled)
{
return true;
}
else
{
return Camera.ViewFrustum.ContainsAABBNoClip(ref position, ref extent);
}
}
private bool EntityVisibleAtMaxLodLevel(YmapEntityDef ent) private bool EntityVisibleAtMaxLodLevel(YmapEntityDef ent)
{ {
if (MaxLOD != rage__eLodType.LODTYPES_DEPTH_ORPHANHD) if (MaxLOD != rage__eLodType.LODTYPES_DEPTH_ORPHANHD)

View File

@ -18,6 +18,9 @@ namespace CodeWalker.Rendering
private int GeometryCount; private int GeometryCount;
public int RenderedGeometries; public int RenderedGeometries;
private int VerticesCount;
public int RenderedVeritices;
private Device Device; private Device Device;
public bool wireframe = Settings.Default.Wireframe; public bool wireframe = Settings.Default.Wireframe;
@ -148,7 +151,7 @@ namespace CodeWalker.Rendering
IsFrontCounterClockwise = true, IsFrontCounterClockwise = true,
IsMultisampleEnabled = true, IsMultisampleEnabled = true,
IsScissorEnabled = false, IsScissorEnabled = false,
SlopeScaledDepthBias = 0.0f SlopeScaledDepthBias = 0.0f,
}; };
rsSolid = new RasterizerState(device, rsd); rsSolid = new RasterizerState(device, rsd);
rsd.FillMode = FillMode.Wireframe; rsd.FillMode = FillMode.Wireframe;
@ -157,6 +160,7 @@ namespace CodeWalker.Rendering
rsWireframeDblSided = new RasterizerState(device, rsd); rsWireframeDblSided = new RasterizerState(device, rsd);
rsd.FillMode = FillMode.Solid; rsd.FillMode = FillMode.Solid;
rsSolidDblSided = new RasterizerState(device, rsd); rsSolidDblSided = new RasterizerState(device, rsd);
rsd.CullMode = CullMode.Back;
BlendStateDescription bsd = new BlendStateDescription() BlendStateDescription bsd = new BlendStateDescription()
@ -203,6 +207,7 @@ namespace CodeWalker.Rendering
PassOperation = StencilOperation.Zero PassOperation = StencilOperation.Zero
}, },
IsDepthEnabled = true, IsDepthEnabled = true,
IsStencilEnabled = false, IsStencilEnabled = false,
StencilReadMask = 0, StencilReadMask = 0,
StencilWriteMask = 0 StencilWriteMask = 0
@ -406,6 +411,7 @@ namespace CodeWalker.Rendering
public void RenderQueued(DeviceContext context, Camera camera, Vector4 wind) public void RenderQueued(DeviceContext context, Camera camera, Vector4 wind)
{ {
GeometryCount = 0; GeometryCount = 0;
VerticesCount = 0;
Camera = camera; Camera = camera;
@ -662,7 +668,7 @@ namespace CodeWalker.Rendering
RenderedGeometries = GeometryCount; RenderedGeometries = GeometryCount;
RenderedVeritices = VerticesCount;
} }
public void RenderFinalPass(DeviceContext context) public void RenderFinalPass(DeviceContext context)
@ -789,6 +795,8 @@ namespace CodeWalker.Rendering
var gmodel = geom.Geom.Owner; var gmodel = geom.Geom.Owner;
shader.SetEntityVars(context, ref geom.Inst); shader.SetEntityVars(context, ref geom.Inst);
VerticesCount += geom.Geom.VertexCount;
if (gmodel != model) if (gmodel != model)
{ {
model = gmodel; model = gmodel;
@ -829,6 +837,7 @@ namespace CodeWalker.Rendering
foreach (var geom in batch) foreach (var geom in batch)
{ {
VerticesCount += geom.Geom.VertexCount;
Basic.RenderBoundGeom(context, geom); Basic.RenderBoundGeom(context, geom);
} }
@ -1050,6 +1059,18 @@ namespace CodeWalker.Rendering
{ {
return ShaderFile.ToString() + ": " + ShaderName.ToString(); return ShaderFile.ToString() + ": " + ShaderName.ToString();
} }
public override int GetHashCode()
{
return ShaderName.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null) return false;
if (obj is not ShaderKey shaderKey) return false;
return shaderKey.ShaderName == ShaderName && shaderKey.ShaderFile == ShaderFile;
}
} }
public class ShaderRenderBucket public class ShaderRenderBucket
{ {
@ -1097,7 +1118,7 @@ namespace CodeWalker.Rendering
ClothBatches.Clear(); ClothBatches.Clear();
VehicleBatches.Clear(); VehicleBatches.Clear();
foreach (var kvp in Batches) foreach (var kvp in Batches.Where(p => p.Value.Geometries.Count > 0).OrderBy(p => p.Value.Geometries.Average(p => p.Inst.Distance)))
{ {
if (kvp.Value.Geometries.Count == 0) continue; if (kvp.Value.Geometries.Count == 0) continue;

View File

@ -14,6 +14,7 @@ using SharpDX;
using SharpDX.DXGI; using SharpDX.DXGI;
using SharpDX.Mathematics.Interop; using SharpDX.Mathematics.Interop;
using System.Diagnostics; using System.Diagnostics;
using CodeWalker.Properties;
namespace CodeWalker.Rendering namespace CodeWalker.Rendering
{ {
@ -110,7 +111,7 @@ namespace CodeWalker.Rendering
public int SSAASampleCount = 1; public int SSAASampleCount = 1;
public int MSAASampleCount = 4; public int MSAASampleCount = 8;
@ -460,7 +461,8 @@ namespace CodeWalker.Rendering
foreach (var rll in lodlights) foreach (var rll in lodlights)
{ {
if (rll.PointsBuffer != null) var (pointsIndex, spotsIndex, capsIndex) = rll.UpdateLods(camera.Position);
if (rll.PointsBuffer != null && pointsIndex > 0)
{ {
context.VertexShader.SetShaderResources(0, rll.PointsBuffer.SRV); context.VertexShader.SetShaderResources(0, rll.PointsBuffer.SRV);
context.PixelShader.SetShaderResources(6, rll.PointsBuffer.SRV); context.PixelShader.SetShaderResources(6, rll.PointsBuffer.SRV);
@ -470,9 +472,10 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 1; LightPSVars.Vars.LightType = 1;
LightPSVars.Update(context); LightPSVars.Update(context);
LightPSVars.SetPSCBuffer(context, 0); LightPSVars.SetPSCBuffer(context, 0);
LightSphere.DrawInstanced(context, rll.PointsBuffer.StructCount);
LightSphere.DrawInstanced(context, pointsIndex + 1);
} }
if (rll.SpotsBuffer != null) if (rll.SpotsBuffer != null && spotsIndex > 0)
{ {
context.VertexShader.SetShaderResources(0, rll.SpotsBuffer.SRV); context.VertexShader.SetShaderResources(0, rll.SpotsBuffer.SRV);
context.PixelShader.SetShaderResources(6, rll.SpotsBuffer.SRV); context.PixelShader.SetShaderResources(6, rll.SpotsBuffer.SRV);
@ -482,9 +485,9 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 2; LightPSVars.Vars.LightType = 2;
LightPSVars.Update(context); LightPSVars.Update(context);
LightPSVars.SetPSCBuffer(context, 0); LightPSVars.SetPSCBuffer(context, 0);
LightCone.DrawInstanced(context, rll.SpotsBuffer.StructCount); LightCone.DrawInstanced(context, spotsIndex + 1);
} }
if (rll.CapsBuffer != null) if (rll.CapsBuffer != null && capsIndex > 0)
{ {
context.VertexShader.SetShaderResources(0, rll.CapsBuffer.SRV); context.VertexShader.SetShaderResources(0, rll.CapsBuffer.SRV);
context.PixelShader.SetShaderResources(6, rll.CapsBuffer.SRV); context.PixelShader.SetShaderResources(6, rll.CapsBuffer.SRV);
@ -494,7 +497,7 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 4; LightPSVars.Vars.LightType = 4;
LightPSVars.Update(context); LightPSVars.Update(context);
LightPSVars.SetPSCBuffer(context, 0); LightPSVars.SetPSCBuffer(context, 0);
LightCapsule.DrawInstanced(context, rll.CapsBuffer.StructCount); LightCapsule.DrawInstanced(context, capsIndex + 1);
} }
} }

View File

@ -440,6 +440,9 @@ namespace CodeWalker.Rendering
case Format.D32_Float: case Format.D32_Float:
dtexf = Format.R32_Typeless; dtexf = Format.R32_Typeless;
break; break;
case Format.R8G8B8A8_SNorm:
dtexf = Format.R8G8B8A8_SNorm;
break;
case Format.D32_Float_S8X24_UInt: case Format.D32_Float_S8X24_UInt:
dtexf = Format.R32G8X24_Typeless;//is this right? who uses this anyway?? dtexf = Format.R32G8X24_Typeless;//is this right? who uses this anyway??
break; break;

View File

@ -96,7 +96,7 @@ namespace CodeWalker.Rendering
Cascades.Add(c); Cascades.Add(c);
} }
DepthRenderRS = DXUtility.CreateRasterizerState(device, FillMode.Solid, CullMode.None, true, false, true, 0, 0.0f, 1.0f); DepthRenderRS = DXUtility.CreateRasterizerState(device, FillMode.Solid, CullMode.None, true, false, Settings.Default.AntiAliasing > 1, 0, 0.0f, 1.0f);
DepthRenderDS = DXUtility.CreateDepthStencilState(device, true, DepthWriteMask.All); DepthRenderDS = DXUtility.CreateDepthStencilState(device, true, DepthWriteMask.All);
DepthRenderVP = new ViewportF(); DepthRenderVP = new ViewportF();

View File

@ -127,7 +127,7 @@ namespace CodeWalker.Rendering
VertexBuffer = Buffer.Create(device, BindFlags.VertexBuffer, vdata.ToArray()); VertexBuffer = Buffer.Create(device, BindFlags.VertexBuffer, vdata.ToArray());
vbbinding = new VertexBufferBinding(VertexBuffer, 16, 0); vbbinding = new VertexBufferBinding(VertexBuffer, 8, 0);
IndexBuffer = Buffer.Create(device, BindFlags.IndexBuffer, idata.ToArray()); IndexBuffer = Buffer.Create(device, BindFlags.IndexBuffer, idata.ToArray());
indexcount = idata.Count; indexcount = idata.Count;

View File

@ -1,5 +1,6 @@
using CodeWalker.GameFiles; using CodeWalker.GameFiles;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Data; using System.Data;

View File

@ -37,12 +37,12 @@ namespace CodeWalker.Tools
DataTextBox.SetTabStopWidth(3); DataTextBox.SetTabStopWidth(3);
if (RpfMan == null) if (RpfMan == null || !RpfMan.IsInited)
{ {
Task.Run(() => Task.Run(() =>
{ {
GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key); GTA5Keys.LoadFromPath(GTAFolder.CurrentGTAFolder, Settings.Default.Key);
RpfMan = new RpfManager(); RpfMan ??= RpfManager.GetInstance();
RpfMan.Init(GTAFolder.CurrentGTAFolder, UpdateStatus, UpdateStatus, false, false); RpfMan.Init(GTAFolder.CurrentGTAFolder, UpdateStatus, UpdateStatus, false, false);
RPFScanComplete(); RPFScanComplete();
}); });
@ -417,17 +417,13 @@ namespace CodeWalker.Tools
DateTime starttime = DateTime.Now; DateTime starttime = DateTime.Now;
int resultcount = 0; int resultcount = 0;
for (int f = 0; f < scannedFiles.Count; f++) foreach(var rpffile in scannedFiles)
{ {
var rpffile = scannedFiles[f];
totfiles += rpffile.TotalFileCount; totfiles += rpffile.TotalFileCount;
} }
foreach(var rpffile in scannedFiles)
for (int f = 0; f < scannedFiles.Count; f++)
{ {
var rpffile = scannedFiles[f];
foreach (var entry in rpffile.AllEntries) foreach (var entry in rpffile.AllEntries)
{ {
var duration = DateTime.Now - starttime; var duration = DateTime.Now - starttime;
@ -444,7 +440,7 @@ namespace CodeWalker.Tools
curfile++; curfile++;
if (fentry.NameLower.EndsWith(".rpf")) if (fentry.Name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase))
{ continue; } { continue; }
if (onlyexts != null) if (onlyexts != null)
@ -452,7 +448,7 @@ namespace CodeWalker.Tools
bool ignore = true; bool ignore = true;
for (int i = 0; i < onlyexts.Length; i++) for (int i = 0; i < onlyexts.Length; i++)
{ {
if (fentry.NameLower.EndsWith(onlyexts[i])) if (fentry.Name.EndsWith(onlyexts[i], StringComparison.OrdinalIgnoreCase))
{ {
ignore = false; ignore = false;
break; break;
@ -467,7 +463,7 @@ namespace CodeWalker.Tools
bool ignore = false; bool ignore = false;
for (int i = 0; i < ignoreexts.Length; i++) for (int i = 0; i < ignoreexts.Length; i++)
{ {
if (fentry.NameLower.EndsWith(ignoreexts[i])) if (fentry.Name.EndsWith(ignoreexts[i], StringComparison.OrdinalIgnoreCase))
{ {
ignore = true; ignore = true;
break; break;

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