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/
/Toolbar/cammodes.psd
Shaders

View File

@ -1,12 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<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.Mathematics" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup>

View File

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

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
@ -171,69 +172,44 @@ namespace CodeWalker.GameFiles
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();
public static volatile bool FullIndexBuilt = false;
public static void Clear()
{
lock (syncRoot)
{
Index.Clear();
}
Index.Clear();
}
public static bool Ensure(string str)
{
uint hash = JenkHash.GenHash(str);
if (hash == 0) return true;
lock (syncRoot)
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
}
}
return true;
return !Index.TryAdd(hash, str);
}
public static bool Ensure(string str, uint hash)
{
if (hash == 0) return true;
lock (syncRoot)
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
}
}
return true;
return !Index.TryAdd(hash, str);
}
public static string GetString(uint hash)
{
string res;
lock (syncRoot)
if (!Index.TryGetValue(hash, out res))
{
if (!Index.TryGetValue(hash, out res))
{
res = hash.ToString();
}
res = hash.ToString();
}
return res;
}
public static string TryGetString(uint hash)
{
string res;
lock (syncRoot)
if (!Index.TryGetValue(hash, out res))
{
if (!Index.TryGetValue(hash, out res))
{
res = string.Empty;
}
res = string.Empty;
}
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>();

View File

@ -51,7 +51,7 @@ namespace CodeWalker.GameFiles
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>();

View File

@ -44,20 +44,20 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
ResourceDataReader rd = null;
try
{
rd = new ResourceDataReader(resentry, data);
using ResourceDataReader rd = new ResourceDataReader(resentry, data);
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
InitDictionaries();
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ClipDictionary = rd?.ReadBlock<ClipDictionary>();
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
DrawableDict = rd.ReadBlock<DrawableDictionary>();
@ -74,6 +74,7 @@ namespace CodeWalker.GameFiles
{
var drawable = drawables[i];
var hash = hashes[i];
drawable.Hash = hash;
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
{
string hstr = JenkIndex.TryGetString(hash);
@ -101,6 +102,12 @@ namespace CodeWalker.GameFiles
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0;
@ -58,6 +58,11 @@ namespace CodeWalker.GameFiles
}
public void Unload()
{
}
public byte[] Save()
{
byte[] data = ResourceBuilder.Build(Drawable, 165); //ydr is type/version 165...
@ -65,7 +70,13 @@ namespace CodeWalker.GameFiles
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?)");
}
ResourceDataReader rd = null;
try
{
rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
ExpressionDictionary = rd?.ReadBlock<ExpressionDictionary>();
InitDictionaries();
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ExpressionDictionary = rd?.ReadBlock<ExpressionDictionary>();
InitDictionaries();
}
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?)");
}
ResourceDataReader rd = null;
try
{
rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
FrameFilterDictionary = rd.ReadBlock<FrameFilterDictionary>();
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
FrameFilterDictionary = rd?.ReadBlock<FrameFilterDictionary>();
}
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
Fragment = rd.ReadBlock<FragType>();

View File

@ -40,36 +40,35 @@ namespace CodeWalker.GameFiles
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
ResourceDataReader rd = null;
try
{
rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
ClothDictionary = rd?.ReadBlock<ClothDictionary>();
if (ClothDictionary != null)
{
Dict = new Dictionary<uint, CharacterCloth>();
int n = ClothDictionary.ClothNameHashes?.data_items?.Length ?? 0;
for (int i = 0; i < n; i++)
{
if (i >= (ClothDictionary.Clothes?.data_items?.Length ?? 0)) break;
var hash = ClothDictionary.ClothNameHashes.data_items[i];
var cloth = ClothDictionary.Clothes.data_items[i];
Dict[hash] = cloth;
}
}
Loaded = true;
}
catch (Exception ex)
{
//data = entry.File.DecompressBytes(data); //??
LoadException = ex.ToString();
}
ClothDictionary = rd?.ReadBlock<ClothDictionary>();
if (ClothDictionary != null)
{
Dict = new Dictionary<uint, CharacterCloth>();
int n = ClothDictionary.ClothNameHashes?.data_items?.Length ?? 0;
for (int i = 0; i < n; i++)
{
if (i >= (ClothDictionary.Clothes?.data_items?.Length ?? 0)) break;
var hash = ClothDictionary.ClothNameHashes.data_items[i];
var cloth = ClothDictionary.Clothes.data_items[i];
Dict[hash] = cloth;
}
}
Loaded = true;
}
public byte[] Save()

View File

@ -96,7 +96,7 @@ namespace CodeWalker.GameFiles
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?
@ -1336,15 +1336,11 @@ namespace CodeWalker.GameFiles
{
var newnamel = newname.ToLowerInvariant();
var newnamex = newname + ".ymap";
var newnamexl = newname.ToLowerInvariant();
var newhash = JenkHash.GenHash(newnamel);
JenkIndex.Ensure(newnamel);
if (RpfFileEntry != null)
{
RpfFileEntry.Name = newnamex;
RpfFileEntry.NameLower = newnamexl;
RpfFileEntry.NameHash = JenkHash.GenHash(newnamexl);
RpfFileEntry.ShortNameHash = newhash;
}
Name = newnamex;
_CMapData.name = newhash;
@ -3034,6 +3030,8 @@ namespace CodeWalker.GameFiles
public bool Enabled { get; set; } = true;
public bool Visible { get; set; } = true;
public void Init(YmapLODLights l, YmapDistantLODLights p, int i)
{
LodLights = l;
@ -3306,7 +3304,7 @@ namespace CodeWalker.GameFiles
var vertexCount = indicesOffset / 12;
var indexCount = (int)(dataSize - indicesOffset);// / 4;
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];
Buffer.BlockCopy(Data, indicesOffset, Indices, 0, indexCount);
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>();
}
@ -90,13 +86,13 @@ namespace CodeWalker.GameFiles
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);
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);

View File

@ -62,7 +62,7 @@ namespace CodeWalker.GameFiles
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
if (resentry != null)
{
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
NodeDictionary = rd.ReadBlock<NodeDictionary>();
@ -95,8 +95,6 @@ namespace CodeWalker.GameFiles
//links will be populated by the space... maybe move that code here?
string areaidstr = Name.ToLowerInvariant().Replace("nodes", "").Replace(".ynd", "");
int areaid = 0;
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
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?)");
}
ResourceDataReader rd = new ResourceDataReader(resentry, data);
using var rd = new ResourceDataReader(resentry, data);
//MemoryUsage = 0;

View File

@ -31,6 +31,7 @@ namespace CodeWalker.GameFiles
Loaded = true;
}
public void Load(byte[] data, RpfFileEntry entry)
{
Name = entry.Name;
@ -43,7 +44,7 @@ namespace CodeWalker.GameFiles
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>();
@ -64,7 +65,13 @@ namespace CodeWalker.GameFiles
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>();

View File

@ -31,7 +31,7 @@ namespace CodeWalker.GameFiles
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;

View File

@ -31,7 +31,7 @@ namespace CodeWalker.GameFiles
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;

View File

@ -101,6 +101,28 @@ namespace CodeWalker.GameFiles
Hash = hash;
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 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 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)
{
@ -117,16 +133,11 @@ namespace CodeWalker.GameFiles
if (ActiveHours == null)
{
ActiveHours = new bool[24];
ActiveHoursText = new string[24];
}
for (int i = 0; i < 24; i++)
{
bool v = ((TimeFlags >> i) & 1) == 1;
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);
}
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 Dictionary<uint, string> stringCache = new Dictionary<uint, string>();
public static bool TryGetString(uint h, out string str)
{
if (stringCache.TryGetValue(h, out str))
{
return str != null;
}
if (Enum.IsDefined(typeof(MetaName), h))
{
str = ((MetaName)h).ToString();
if (str.StartsWith("@")) str = str.Substring(1); //mainly to handle the @null entry
stringCache.Add(h, str);
return true;
}
stringCache.Add(h, str);
str = null;
return false;
}

View File

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

View File

@ -1423,7 +1423,7 @@ namespace CodeWalker.GameFiles
arrEnum.SwapEnd();
var enumArr = PsoTypes.GetUintArray(cont.Pso, arrEnum);
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);
return HashString(eval?.EntryNameHash ?? 0);
});
@ -1945,9 +1945,10 @@ namespace CodeWalker.GameFiles
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 aind = ind + 1;
var arrTag = name;// + " itemType=\"" + typeName + "\"";
@ -1986,15 +1987,25 @@ namespace CodeWalker.GameFiles
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
{
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 aind = ind + 1;
if (aCount > 0)
if (itemCount > 0)
{
OpenTag(sb, ind, arrTag);
for (int n = 0; n < aCount; n++)
for (int n = 0; n < itemCount; n++)
{
Indent(sb, aind);
sb.Append("<Item>");

View File

@ -8,7 +8,7 @@ using System.Threading.Tasks;
using TC = System.ComponentModel.TypeConverterAttribute;
using EXP = System.ComponentModel.ExpandableObjectConverter;
using System.Diagnostics;
namespace CodeWalker.GameFiles
{
@ -15819,45 +15819,46 @@ namespace CodeWalker.GameFiles
public static T ConvertDataRaw<T>(byte[] data) where T : struct
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h);
handle.Free();
return r;
MemoryMarshal.TryRead<T>(data.AsSpan(), out T value);
return value;
}
public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset);
handle.Free();
return r;
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
return value;
//return MemoryMarshal.GetReference<T>(data.AsSpan(offset));
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//return r;
}
public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
var r = Marshal.PtrToStructure<T>(h + offset);
handle.Free();
r.SwapEnd();
return r;
MemoryMarshal.TryRead<T>(data.AsSpan(offset), out T value);
value.SwapEnd();
return value;
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//var r = Marshal.PtrToStructure<T>(h + offset);
//handle.Free();
//r.SwapEnd();
//return r;
}
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];
int itemsize = Marshal.SizeOf(typeof(T));
//for (int i = 0; i < count; i++)
//{
// int off = offset + i * itemsize;
// items[i] = ConvertDataRaw<T>(data, off);
//}
return MemoryMarshal.Cast<byte, T>(data.AsSpan(offset, count * Marshal.SizeOf(typeof(T))));
GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
var h = handle.AddrOfPinnedObject();
Marshal.Copy(data, offset, h, itemsize * count);
handle.Free();
//GCHandle handle = GCHandle.Alloc(items, GCHandleType.Pinned);
//var h = handle.AddrOfPinnedObject();
//Marshal.Copy(data, offset, h, itemsize * count);
//handle.Free();
return items;
//return items;
}
@ -15878,7 +15879,7 @@ namespace CodeWalker.GameFiles
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))
{
@ -15887,7 +15888,7 @@ namespace CodeWalker.GameFiles
}
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))
{
@ -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;
var entryid = arr.PointerDataId;
@ -15917,12 +15918,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
int totoffset = arrentry.Offset + (int)entryoffset;
uint[] readdata = ConvertDataArrayRaw<uint>(data, totoffset, arr.Count1);
var readdata = ConvertDataArrayRaw<uint>(data, totoffset, arr.Count1);
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;
for (int i = 0; i < uints.Length; i++)
{
@ -15933,7 +15934,7 @@ namespace CodeWalker.GameFiles
public static MetaHash[] GetHashArray(PsoFile pso, Array_uint arr)
{
uint[] uints = GetUintArrayRaw(pso, arr);
var uints = GetUintArrayRaw(pso, arr);
if (uints == null) return null;
MetaHash[] hashes = new MetaHash[uints.Length];
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;
var entryid = arr.PointerDataId;
@ -15957,12 +15958,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
int totoffset = arrentry.Offset + (int)entryoffset;
float[] readdata = ConvertDataArrayRaw<float>(data, totoffset, arr.Count1);
var readdata = ConvertDataArrayRaw<float>(data, totoffset, arr.Count1);
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;
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;
var entryid = arr.PointerDataId;
@ -15986,12 +15987,12 @@ namespace CodeWalker.GameFiles
var entryoffset = arr.PointerDataOffset;
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
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;
}
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;
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()
{
U[] items = GetItemArray<U>(pso, arr);
Span<U> items = GetItemArray<U>(pso, arr);
if (items == null) return null;
if (items.Length == 0) return null;
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;
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 ptroffset = array.PointerDataOffset;
var ptrblock = (ptrindex < pso.DataMapSection.EntriesCount) ? pso.DataMapSection.Entries[ptrindex] : null;
@ -16071,13 +16070,12 @@ namespace CodeWalker.GameFiles
{
uint count = array.Count1;
if (count == 0) return null;
PsoPOINTER[] ptrs = GetPointerArray(pso, array);
var ptrs = GetPointerArray(pso, array);
if (ptrs == null) return null;
if (ptrs.Length < count)
{ return null; }
T[] items = new T[count];
int itemsize = Marshal.SizeOf(typeof(T));
for (int i = 0; i < count; i++)
{

View File

@ -243,106 +243,106 @@ namespace CodeWalker.GameFiles
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;
trimlength = 4;
if (!fnamel.EndsWith(".xml"))
if (!fileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.XML;//not really correct, but have to return something...
}
if (fnamel.EndsWith(".pso.xml"))
if (fileName.EndsWith(".pso.xml"))
{
mformat = MetaFormat.PSO;
trimlength = 8;
}
if (fnamel.EndsWith(".rbf.xml"))
if (fileName.EndsWith(".rbf.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.RBF;
trimlength = 8;
}
if (fnamel.EndsWith(".rel.xml"))
if (fileName.EndsWith(".rel.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.AudioRel;
}
if (fnamel.EndsWith(".ynd.xml"))
if (fileName.EndsWith(".ynd.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ynd;
}
if (fnamel.EndsWith(".ynv.xml"))
if (fileName.EndsWith(".ynv.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ynv;
}
if (fnamel.EndsWith(".ycd.xml"))
if (fileName.EndsWith(".ycd.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ycd;
}
if (fnamel.EndsWith(".ybn.xml"))
if (fileName.EndsWith(".ybn.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ybn;
}
if (fnamel.EndsWith(".ytd.xml"))
if (fileName.EndsWith(".ytd.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ytd;
}
if (fnamel.EndsWith(".ydr.xml"))
if (fileName.EndsWith(".ydr.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ydr;
}
if (fnamel.EndsWith(".ydd.xml"))
if (fileName.EndsWith(".ydd.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ydd;
}
if (fnamel.EndsWith(".yft.xml"))
if (fileName.EndsWith(".yft.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Yft;
}
if (fnamel.EndsWith(".ypt.xml"))
if (fileName.EndsWith(".ypt.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ypt;
}
if (fnamel.EndsWith(".yld.xml"))
if (fileName.EndsWith(".yld.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Yld;
}
if (fnamel.EndsWith(".yed.xml"))
if (fileName.EndsWith(".yed.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Yed;
}
if (fnamel.EndsWith(".ywr.xml"))
if (fileName.EndsWith(".ywr.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ywr;
}
if (fnamel.EndsWith(".yvr.xml"))
if (fileName.EndsWith(".yvr.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Yvr;
}
if (fnamel.EndsWith(".awc.xml"))
if (fileName.EndsWith(".awc.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Awc;
}
if (fnamel.EndsWith(".fxc.xml"))
if (fileName.EndsWith(".fxc.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Fxc;
}
if (fnamel.EndsWith("cache_y.dat.xml"))
if (fileName.EndsWith("cache_y.dat.xml", StringComparison.OrdinalIgnoreCase))
{
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;
}
if (fnamel.EndsWith(".ypdb.xml"))
if (fileName.EndsWith(".ypdb.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Ypdb;
}
if (fnamel.EndsWith(".yfd.xml"))
if (fileName.EndsWith(".yfd.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Yfd;
}
if (fnamel.EndsWith(".mrf.xml"))
if (fileName.EndsWith(".mrf.xml", StringComparison.OrdinalIgnoreCase))
{
mformat = MetaFormat.Mrf;
}

View File

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

View File

@ -1791,7 +1791,7 @@ namespace CodeWalker.GameFiles
{
int datalength = ItemCount * StructureSize;
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)

View File

@ -348,9 +348,9 @@ namespace CodeWalker.GameFiles
fileBase.FilePagesInfo.GraphicsPagesCount = (byte)graphicsPageFlags.Count;
var systemStream = new MemoryStream();
var graphicsStream = new MemoryStream();
var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream);
using var systemStream = new MemoryStream();
using var graphicsStream = new MemoryStream();
using var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream);
resourceWriter.Position = 0x50000000;
foreach (var block in systemBlocks)
@ -456,15 +456,12 @@ namespace CodeWalker.GameFiles
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);
ds.Write(data, 0, data.Length);
ds.Close();
byte[] deflated = ms.GetBuffer();
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer...
Array.Copy(deflated, outbuf, outbuf.Length);
return outbuf;
return ms.ToArray();
}
}
public static byte[] Decompress(byte[] data)
@ -472,14 +469,17 @@ namespace CodeWalker.GameFiles
using (MemoryStream ms = new MemoryStream(data))
{
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = new MemoryStream();
MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream("Decompress", data.Length);
ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer();
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer...
Array.Copy(deflated, outbuf, outbuf.Length);
return outbuf;
return outstr.ToArray();
}
}
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>
/// Represents a resource data reader.
/// </summary>
public class ResourceDataReader : DataReader
public class ResourceDataReader : DataReader, IDisposable
{
private const long SYSTEM_BASE = 0x50000000;
private const long GRAPHICS_BASE = 0x60000000;
private Stream systemStream;
private Stream graphicsStream;
private readonly Stream systemStream;
private readonly Stream graphicsStream;
private readonly long systemSize = 0;
private readonly long graphicsSize = 0;
public RpfResourceFileEntry FileEntry { get; set; }
@ -71,7 +74,7 @@ namespace CodeWalker.GameFiles
{
get;
set;
}
} = SYSTEM_BASE;
/// <summary>
/// Initializes a new resource data reader for the specified system- and graphics-stream.
@ -81,14 +84,16 @@ namespace CodeWalker.GameFiles
{
this.systemStream = systemStream;
this.graphicsStream = graphicsStream;
this.systemSize = systemStream.Length;
this.graphicsSize = graphicsStream.Length;
}
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
: base((Stream)null, endianess)
{
FileEntry = resentry;
var systemSize = resentry.SystemSize;
var graphicsSize = resentry.GraphicsSize;
this.systemSize = resentry.SystemSize;
this.graphicsSize = resentry.GraphicsSize;
//if (data != null)
//{
@ -103,9 +108,8 @@ namespace CodeWalker.GameFiles
// }
//}
this.systemStream = new MemoryStream(data, 0, systemSize);
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
Position = 0x50000000;
this.systemStream = new MemoryStream(data, 0, (int)systemSize);
this.graphicsStream = new MemoryStream(data, (int)systemSize, (int)graphicsSize);
}
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
@ -113,24 +117,21 @@ namespace CodeWalker.GameFiles
{
this.systemStream = new MemoryStream(data, 0, systemSize);
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
Position = 0x50000000;
}
/// <summary>
/// Reads data from the underlying stream. This is the only method that directly accesses
/// the data in the underlying stream.
/// </summary>
protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false)
protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false, byte[] buffer = null)
{
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
{
// 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);
// handle endianess
@ -139,17 +140,17 @@ namespace CodeWalker.GameFiles
Array.Reverse(buffer);
}
Position = systemStream.Position | 0x50000000;
Position = systemStream.Position | SYSTEM_BASE;
return buffer;
}
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
else if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
{
// 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);
// handle endianess
@ -158,12 +159,44 @@ namespace CodeWalker.GameFiles
Array.Reverse(buffer);
}
Position = graphicsStream.Position | 0x60000000;
Position = graphicsStream.Position | GRAPHICS_BASE;
return buffer;
}
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>
/// Reads a block.
/// </summary>
@ -447,6 +480,13 @@ namespace CodeWalker.GameFiles
return result;
}
public override void Dispose()
{
base.Dispose();
systemStream?.Dispose();
graphicsStream?.Dispose();
}
}
@ -454,7 +494,7 @@ namespace CodeWalker.GameFiles
/// <summary>
/// Represents a resource data writer.
/// </summary>
public class ResourceDataWriter : DataWriter
public class ResourceDataWriter : DataWriter, IDisposable
{
private const long SYSTEM_BASE = 0x50000000;
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
// reference data
public string Name { get; set; }
public uint NameHash { get; set; }
public string Name { get => name; 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 uint nameHash;
private string name;
public TextureUsage Usage
{
@ -281,11 +294,6 @@ namespace CodeWalker.GameFiles
this.NamePointer // offset
);
if (!string.IsNullOrEmpty(Name))
{
NameHash = JenkHash.GenHash(Name.ToLowerInvariant());
}
//switch (Unknown_32h)
//{
// case 0x20:
@ -405,7 +413,6 @@ namespace CodeWalker.GameFiles
public virtual void ReadXml(XmlNode node, string ddsfolder)
{
Name = Xml.GetChildInnerText(node, "Name");
NameHash = JenkHash.GenHash(Name?.ToLowerInvariant());
Unknown_32h = (ushort)Xml.GetChildUIntAttribute(node, "Unk32", "value");
Usage = Xml.GetChildEnumInnerText<TextureUsage>(node, "Usage");
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.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.IO.Pipes;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
@ -13,7 +20,24 @@ namespace CodeWalker.GameFiles
public class RpfFile
{
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 FilePath { get; set; } //full file path of the RPF
public long FileSize { get; set; }
@ -61,7 +85,6 @@ namespace CodeWalker.GameFiles
{
FileInfo fi = new FileInfo(fpath);
Name = fi.Name;
NameLower = Name.ToLowerInvariant();
Path = relpath.ToLowerInvariant();
FilePath = fpath;
FileSize = fi.Length;
@ -69,7 +92,6 @@ namespace CodeWalker.GameFiles
public RpfFile(string name, string path, long filesize) //for a child RPF
{
Name = name;
NameLower = Name.ToLowerInvariant();
Path = path.ToLowerInvariant();
FilePath = path;
FileSize = filesize;
@ -147,8 +169,11 @@ namespace CodeWalker.GameFiles
throw new Exception("Invalid Resource - not GTAV!");
}
byte[] entriesdata = br.ReadBytes((int)EntryCount * 16); //4x uints each
byte[] namesdata = br.ReadBytes((int)NamesLength);
byte[] entriesdata = ArrayPool<byte>.Shared.Rent((int)EntryCount * 16);
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)
{
@ -156,25 +181,25 @@ namespace CodeWalker.GameFiles
case RpfEncryption.OPEN: //OpenIV style RPF with unencrypted TOC
break;
case RpfEncryption.AES:
entriesdata = GTACrypto.DecryptAES(entriesdata);
namesdata = GTACrypto.DecryptAES(namesdata);
GTACrypto.DecryptAES(entriesdata, (int)EntryCount * 16);
GTACrypto.DecryptAES(namesdata, (int)NamesLength);
IsAESEncrypted = true;
break;
case RpfEncryption.NG:
entriesdata = GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize);
namesdata = GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize);
default:
GTACrypto.DecryptNG(entriesdata, Name, (uint)FileSize, 0, (int)EntryCount * 16);
GTACrypto.DecryptNG(namesdata, Name, (uint)FileSize, 0, (int)NamesLength);
IsNGEncrypted = true;
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>();
AllEntries = new List<RpfEntry>((int)EntryCount);
TotalFileCount = 0;
TotalFolderCount = 0;
TotalResourceCount = 0;
@ -213,28 +238,24 @@ namespace CodeWalker.GameFiles
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);
}
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.Path = Path.ToLowerInvariant();// + "\\" + Root.Name;
@ -251,17 +272,16 @@ namespace CodeWalker.GameFiles
{
RpfEntry e = AllEntries[i];
e.Parent = item;
if (e is RpfDirectoryEntry)
if (e is RpfDirectoryEntry rde)
{
RpfDirectoryEntry rde = e as RpfDirectoryEntry;
rde.Path = item.Path + "\\" + rde.NameLower;
rde.Path = item.Path + "\\" + rde.Name;
item.Directories.Add(rde);
stack.Push(rde);
}
else if (e is RpfFileEntry)
{
RpfFileEntry rfe = e as RpfFileEntry;
rfe.Path = item.Path + "\\" + rfe.NameLower;
rfe.Path = item.Path + "\\" + rfe.Name;
item.Files.Add(rfe);
}
}
@ -270,32 +290,41 @@ namespace CodeWalker.GameFiles
br.BaseStream.Position = StartPos;
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);
}
catch (Exception ex)
{
LastError = ex.ToString();
LastException = ex;
errorLog(FilePath + ": " + LastError);
}
return ScanStructure(br, updateStatus, errorLog);
}
catch (Exception ex)
{
LastError = ex.ToString();
LastException = ex;
errorLog?.Invoke(FilePath + ": " + LastError);
return false;
}
}
private void ScanStructure(BinaryReader br, Action<string> updateStatus, Action<string> errorLog)
private bool ScanStructure(BinaryReader br, Action<string> updateStatus, Action<string> errorLog)
{
ReadHeader(br);
if (FilePath == "update\\update.rpf\\dlc_patch\\patchday1ng\\x64\\patch\\data\\lang\\chinesesimp.rpf") return false;
try
{
ReadHeader(br);
} catch
{
return false;
}
GrandTotalRpfCount = 1; //count this file..
GrandTotalFileCount = 1; //start with this one.
@ -311,13 +340,10 @@ namespace CodeWalker.GameFiles
{
try
{
if (entry is RpfBinaryFileEntry)
if (entry is RpfBinaryFileEntry binentry)
{
RpfBinaryFileEntry binentry = entry as RpfBinaryFileEntry;
//search all the sub resources for YSC files. (recurse!)
string lname = binentry.NameLower;
if (lname.EndsWith(".rpf") && binentry.Path.Length < 5000) // a long path is most likely an attempt to crash CW, so skip it
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
{
br.BaseStream.Position = StartPos + ((long)binentry.FileOffset * 512);
@ -327,15 +353,16 @@ namespace CodeWalker.GameFiles
subfile.Parent = this;
subfile.ParentFileEntry = binentry;
subfile.ScanStructure(br, updateStatus, errorLog);
if (subfile.ScanStructure(br, updateStatus, errorLog))
{
GrandTotalRpfCount += subfile.GrandTotalRpfCount;
GrandTotalFileCount += subfile.GrandTotalFileCount;
GrandTotalFolderCount += subfile.GrandTotalFolderCount;
GrandTotalResourceCount += subfile.GrandTotalResourceCount;
GrandTotalBinaryFileCount += subfile.GrandTotalBinaryFileCount;
GrandTotalRpfCount += subfile.GrandTotalRpfCount;
GrandTotalFileCount += subfile.GrandTotalFileCount;
GrandTotalFolderCount += subfile.GrandTotalFolderCount;
GrandTotalResourceCount += subfile.GrandTotalResourceCount;
GrandTotalBinaryFileCount += subfile.GrandTotalBinaryFileCount;
Children.Add(subfile);
Children.Add(subfile);
}
}
else
{
@ -359,7 +386,7 @@ namespace CodeWalker.GameFiles
errorLog?.Invoke(entry.Path + ": " + ex.ToString());
}
}
return true;
}
@ -437,26 +464,25 @@ namespace CodeWalker.GameFiles
br.Read(tbytes, 0, (int)totlen);
byte[] decr;
if (IsAESEncrypted)
{
decr = GTACrypto.DecryptAES(tbytes);
GTACrypto.DecryptAES(tbytes);
//special case! probable duplicate pilot_school.ysc
ofpath = outputfolder + "\\" + Name + "___" + resentry.Name;
}
else
{
decr = GTACrypto.DecryptNG(tbytes, resentry.Name, resentry.FileSize);
GTACrypto.DecryptNG(tbytes, resentry.Name, resentry.FileSize);
}
try
{
MemoryStream ms = new MemoryStream(decr);
MemoryStream ms = new MemoryStream(tbytes);
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
MemoryStream outstr = new MemoryStream();
MemoryStream outstr = recyclableMemoryStreamManager.GetStream();
ds.CopyTo(outstr);
byte[] deflated = outstr.GetBuffer();
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)
{
@ -506,13 +558,13 @@ namespace CodeWalker.GameFiles
{
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
{
@ -543,27 +595,25 @@ namespace CodeWalker.GameFiles
br.BaseStream.Position += offset;
br.Read(tbytes, 0, (int)totlen);
byte[] decr = tbytes;
if (entry.IsEncrypted)
{
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)
{
decr = GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize);
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileUncompressedSize);
}
//else
//{ }
}
byte[] defl = decr;
byte[] defl = tbytes;
if (entry.FileSize > 0) //apparently this means it's compressed
{
defl = DecompressBytes(decr);
defl = DecompressBytes(tbytes);
}
else
{
@ -596,23 +646,21 @@ namespace CodeWalker.GameFiles
br.Read(tbytes, 0, (int)totlen);
byte[] decr = tbytes;
if (entry.IsEncrypted)
{
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)
{
decr = GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize);
GTACrypto.DecryptNG(tbytes, entry.Name, entry.FileSize);
}
//else
//{ }
}
byte[] deflated = DecompressBytes(decr);
byte[] deflated = DecompressBytes(tbytes);
byte[] data = null;
@ -623,7 +671,7 @@ namespace CodeWalker.GameFiles
else
{
entry.FileSize -= offset;
data = decr;
data = tbytes;
}
@ -665,6 +713,22 @@ namespace CodeWalker.GameFiles
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()
@ -679,6 +743,36 @@ namespace CodeWalker.GameFiles
}
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
{
//direct load from a raw, compressed resource file (openIV-compatible format)
@ -695,9 +789,6 @@ namespace CodeWalker.GameFiles
oldresentry.SystemFlags = resentry.SystemFlags;
oldresentry.GraphicsFlags = resentry.GraphicsFlags;
resentry.Name = oldresentry.Name;
resentry.NameHash = oldresentry.NameHash;
resentry.NameLower = oldresentry.NameLower;
resentry.ShortNameHash = oldresentry.ShortNameHash;
}
else
{
@ -710,6 +801,45 @@ namespace CodeWalker.GameFiles
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)
{
var resentry = new RpfResourceFileEntry();
@ -742,7 +872,6 @@ namespace CodeWalker.GameFiles
}
resentry.Name = "";
resentry.NameLower = "";
return resentry;
}
@ -896,14 +1025,14 @@ namespace CodeWalker.GameFiles
public static RecyclableMemoryStreamManager recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
public byte[] DecompressBytes(byte[] bytes)
{
try
{
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);
byte[] deflated = outstr.GetBuffer();
@ -929,7 +1058,7 @@ namespace CodeWalker.GameFiles
}
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))
{
@ -1023,7 +1152,6 @@ namespace CodeWalker.GameFiles
Root = new RpfDirectoryEntry();
Root.File = this;
Root.Name = string.Empty;
Root.NameLower = string.Empty;
Root.Path = Path.ToLowerInvariant();
}
if (Children == null)
@ -1397,7 +1525,7 @@ namespace CodeWalker.GameFiles
file.Path = dir.Path + "\\" + file.NameLower;
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);
if (childrpf != null)
@ -1413,7 +1541,7 @@ namespace CodeWalker.GameFiles
}
foreach (var subdir in dir.Directories)
{
subdir.Path = dir.Path + "\\" + subdir.NameLower;
subdir.Path = dir.Path + "\\" + subdir.Name;
UpdatePaths(subdir);
}
}
@ -1531,9 +1659,6 @@ namespace CodeWalker.GameFiles
entry.File = parent;
entry.Path = rpath;
entry.Name = name;
entry.NameLower = namel;
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
dir.Files.Add(entry);
@ -1574,13 +1699,10 @@ namespace CodeWalker.GameFiles
entry.File = parent;
entry.Path = rpath;
entry.Name = name;
entry.NameLower = namel;
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
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!");
}
@ -1600,14 +1722,61 @@ namespace CodeWalker.GameFiles
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)
{
string namel = name.ToLowerInvariant();
if (overwrite)
{
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!
//this should probably be optimised to just replace the existing one...
@ -1621,6 +1790,7 @@ namespace CodeWalker.GameFiles
RpfFile parent = dir.File;
string fpath = parent.GetPhysicalFilePath();
string namel = name.ToLowerInvariant();
string rpath = dir.Path + "\\" + namel;
if (!File.Exists(fpath))
{
@ -1644,7 +1814,6 @@ namespace CodeWalker.GameFiles
{
//RSC header is present... import as resource
var rentry = new RpfResourceFileEntry();
var version = BitConverter.ToUInt32(data, 4);
rentry.SystemFlags = BitConverter.ToUInt32(data, 8);
rentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
rentry.FileSize = len;
@ -1661,11 +1830,11 @@ namespace CodeWalker.GameFiles
entry = rentry;
}
if (namel.EndsWith(".rpf") && (hdr == 0x52504637)) //'RPF7'
if ((hdr == 0x52504637) && name.EndsWith(".rpf", StringComparison.OrdinalIgnoreCase)) //'RPF7'
{
isrpf = true;
}
if (namel.EndsWith(".awc"))
if (name.EndsWith(".awc", StringComparison.OrdinalIgnoreCase))
{
isawc = true;
}
@ -1693,16 +1862,14 @@ namespace CodeWalker.GameFiles
entry.File = parent;
entry.Path = rpath;
entry.Name = name;
entry.NameLower = name.ToLowerInvariant();
entry.NameHash = JenkHash.GenHash(name);
entry.ShortNameHash = JenkHash.GenHash(entry.GetShortNameLower());
entry.NameLower = namel;
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!");
}
@ -1715,15 +1882,13 @@ namespace CodeWalker.GameFiles
using (var fstream = File.Open(fpath, FileMode.Open, FileAccess.ReadWrite))
{
using (var bw = new BinaryWriter(fstream))
{
parent.InsertFileSpace(bw, entry);
long bbeg = parent.StartPos + (entry.FileOffset * 512);
long bend = bbeg + (GetBlockCount(entry.GetFileSize()) * 512);
fstream.Position = bbeg;
fstream.Write(data, 0, data.Length);
WritePadding(fstream, bend); //write 0's until the end of the block.
}
using var bw = new BinaryWriter(fstream);
parent.InsertFileSpace(bw, entry);
long bbeg = parent.StartPos + (entry.FileOffset * 512);
long bend = bbeg + (GetBlockCount(entry.GetFileSize()) * 512);
fstream.Position = bbeg;
fstream.Write(data, 0, data.Length);
WritePadding(fstream, bend); //write 0's until the end of the block.
}
@ -1736,14 +1901,10 @@ namespace CodeWalker.GameFiles
file.StartPos = parent.StartPos + (entry.FileOffset * 512);
parent.Children.Add(file);
using (var fstream = File.OpenRead(fpath))
{
using (var br = new BinaryReader(fstream))
{
fstream.Position = file.StartPos;
file.ScanStructure(br, null, null);
}
}
using var fstream = File.OpenRead(fpath);
using var br = new BinaryReader(fstream);
fstream.Position = file.StartPos;
file.ScanStructure(br, null, null);
}
return entry;
@ -1756,7 +1917,6 @@ namespace CodeWalker.GameFiles
//(since all the paths are generated at runtime and not stored)
file.Name = newname;
file.NameLower = newname.ToLowerInvariant();
file.Path = GetParentPath(file.Path) + newname;
file.FilePath = GetParentPath(file.FilePath) + newname;
@ -1772,13 +1932,9 @@ namespace CodeWalker.GameFiles
string dirpath = GetParentPath(entry.Path);
entry.Name = newname;
entry.NameLower = newname.ToLowerInvariant();
entry.Path = dirpath + newname;
string sname = entry.GetShortNameLower();
JenkIndex.Ensure(sname);//could be anything... but it needs to be there
entry.NameHash = JenkHash.GenHash(newname);
entry.ShortNameHash = JenkHash.GenHash(sname);
JenkIndex.EnsureLower(entry.ShortName);//could be anything... but it needs to be there
RpfFile parent = entry.File;
string fpath = parent.GetPhysicalFilePath();
@ -2011,7 +2167,7 @@ namespace CodeWalker.GameFiles
}
if (!dirpath.EndsWith("\\"))
{
dirpath = dirpath + "\\";
dirpath += "\\";
}
return dirpath;
}
@ -2038,16 +2194,89 @@ namespace CodeWalker.GameFiles
public RpfFile File { get; set; }
public RpfDirectoryEntry Parent { get; set; }
public uint NameHash { get; set; }
public uint ShortNameHash { get; set; }
public uint NameHash { get
{
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 string Name { get; set; }
public string NameLower { get; set; }
public string Name { get => name; 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 uint H1; //first 2 header values from RPF table...
public uint H2;
private string name;
private string nameLower;
private uint shortNameHash;
private uint nameHash;
private string shortName;
public abstract void Read(DataReader reader);
public abstract void Write(DataWriter writer);
@ -2059,29 +2288,12 @@ namespace CodeWalker.GameFiles
public string GetShortName()
{
int ind = Name.LastIndexOf('.');
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;
return ShortName;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public class RpfDirectoryEntry : RpfEntry
[TypeConverter(typeof(ExpandableObjectConverter))]
public class RpfDirectoryEntry : RpfEntry
{
public uint EntriesIndex { 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 FileSize { get; set; }
@ -2144,7 +2357,7 @@ namespace CodeWalker.GameFiles
case 0: IsEncrypted = false; break;
case 1: IsEncrypted = true; break;
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)
{
var buffer = ArrayPool<byte>.Shared.Rent(3);
NameOffset = reader.ReadUInt16();
var buf1 = reader.ReadBytes(3);
FileSize = (uint)buf1[0] + (uint)(buf1[1] << 8) + (uint)(buf1[2] << 16);
var buf2 = reader.ReadBytes(3);
FileOffset = ((uint)buf2[0] + (uint)(buf2[1] << 8) + (uint)(buf2[2] << 16)) & 0x7FFFFF;
reader.ReadBytes(3, buffer);
FileSize = (uint)buffer[0] + (uint)(buffer[1] << 8) + (uint)(buffer[2] << 16);
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();
GraphicsFlags = reader.ReadUInt32();
@ -2751,6 +2965,10 @@ namespace CodeWalker.GameFiles
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.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
@ -10,14 +13,19 @@ namespace CodeWalker.GameFiles
{
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.
public string Folder { get; private set; }
public string[] ExcludePaths { get; set; }
public bool EnableMods { get; set; }
public bool BuildExtendedJenkIndex { get; set; } = true;
public Action<string> UpdateStatus { get; private set; }
public Action<string> ErrorLog { get; private set; }
public event Action<string> UpdateStatus;
public event Action<string> ErrorLog;
public List<RpfFile> BaseRpfs { 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)
{
UpdateStatus = updateStatus;
ErrorLog = errorLog;
using var _ = new DisposableTimer("RpfManager.Init");
UpdateStatus += updateStatus;
ErrorLog += errorLog;
string replpath = folder + "\\";
var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
@ -52,7 +61,8 @@ namespace CodeWalker.GameFiles
ModRpfDict = new Dictionary<string, RpfFile>();
ModEntryDict = new Dictionary<string, RpfEntry>();
foreach (string rpfpath in allfiles)
var rpfs = new ConcurrentBag<RpfFile>();
Parallel.ForEach(allfiles, (rpfpath) =>
{
try
{
@ -69,37 +79,66 @@ namespace CodeWalker.GameFiles
break;
}
}
if (excl) continue; //skip files in exclude paths.
if (excl) return; //skip files in exclude paths.
}
rf.ScanStructure(updateStatus, errorLog);
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)
{
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)
{
updateStatus("Building jenkindex...");
BuildBaseJenkIndex();
updateStatus?.Invoke("Building jenkindex...");
Task.Run(() =>
{
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)
{
using var _ = new DisposableTimer("RpfManager.Init");
//fast init used by RPF explorer's File cache
AllRpfs = allRpfs;
@ -122,9 +161,11 @@ namespace CodeWalker.GameFiles
}
}
BuildBaseJenkIndex();
IsInited = true;
Task.Run(() =>
{
BuildBaseJenkIndex();
IsInited = true;
});
}
@ -182,25 +223,13 @@ namespace CodeWalker.GameFiles
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)
{
file.LastError = ex.ToString();
file.LastException = ex;
ErrorLog(entry.Path + ": " + ex.ToString());
ErrorLog?.Invoke(entry.Path + ": " + ex.ToString());
}
}
}
@ -348,12 +377,12 @@ namespace CodeWalker.GameFiles
public void BuildBaseJenkIndex()
{
JenkIndex.Clear();
StringBuilder sb = new StringBuilder();
foreach (RpfFile file in AllRpfs)
using var _ = new DisposableTimer("BuildBaseJenkIndex");
Parallel.ForEach(AllRpfs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, (file) =>
{
try
{
StringBuilder sb = new StringBuilder();
JenkIndex.Ensure(file.Name);
foreach (RpfEntry entry in file.AllEntries)
{
@ -364,6 +393,7 @@ namespace CodeWalker.GameFiles
int ind = nlow.LastIndexOf('.');
if (ind > 0)
{
JenkIndex.Ensure(entry.Name.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
}
}
});
//foreach (RpfFile file in AllRpfs)
//{
//}
for (int i = 0; i < 100; i++)
{

View File

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

View File

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

View File

@ -23,8 +23,12 @@
//shamelessly stolen
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
@ -34,7 +38,10 @@ namespace CodeWalker.GameFiles
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)
{
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)
{
return DecryptAESData(data, key, data.Length, rounds);
}
public static byte[] DecryptAESData(byte[] data, byte[] key, int length, int rounds = 1)
{
var rijndael = Rijndael.Create();
rijndael.KeySize = 256;
@ -53,18 +65,17 @@ namespace CodeWalker.GameFiles
rijndael.Mode = CipherMode.ECB;
rijndael.Padding = PaddingMode.None;
var buffer = (byte[])data.Clone();
var length = data.Length - data.Length % 16;
length = length - length % 16;
// decrypt...
if (length > 0)
{
var decryptor = rijndael.CreateDecryptor();
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)
{
@ -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 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);
return DecryptNG(data, key);
var key = GetNGKey(name, fileSize);
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];
var keyuints = new uint[key.Length / 4];
Buffer.BlockCopy(key, 0, keyuints, 0, key.Length);
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
length ??= data.Length;
fixed (byte* bptr = data)
{
var encryptedBlock = new byte[16];
Array.Copy(data, 16 * blockIndex, encryptedBlock, 0, 16);
var decryptedBlock = DecryptNGBlock(encryptedBlock, keyuints);
Array.Copy(decryptedBlock, 0, decryptedData, 16 * blockIndex, 16);
for (int blockIndex = offset * 16; blockIndex < length / 16; blockIndex++)
{
DecryptNGBlock(bptr + 16 * blockIndex, key);
}
}
if (data.Length % 16 != 0)
{
var left = data.Length % 16;
Buffer.BlockCopy(data, data.Length - left, decryptedData, data.Length - left, left);
}
return decryptedData;
return data;
}
public static byte[] DecryptNGBlock(byte[] data, uint[] key)
public unsafe static void DecryptNGBlock(byte[] data, uint[][] key)
{
var buffer = data;
// prepare key...
var subKeys = new uint[17][];
for (int i = 0; i < 17; i++)
fixed(byte* bptr = data)
{
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];
DecryptNGBlock(data, key);
}
}
buffer = DecryptNGRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
buffer = DecryptNGRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
public unsafe static void DecryptNGBlock(byte* data, uint[][] key)
{
DecryptNGRoundA(data, key[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
DecryptNGRoundA(data, key[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
for (int k = 2; k <= 15; k++)
buffer = DecryptNGRoundB(buffer, subKeys[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]);
buffer = DecryptNGRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
return buffer;
DecryptNGRoundB(data, key[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]);
DecryptNGRoundA(data, key[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
}
public unsafe static void DecryptNGRoundA(byte[] data, uint[] key, uint[][] table)
{
fixed(byte* bptr = data)
{
DecryptNGRoundA(bptr, key, table);
}
}
public unsafe static void DecryptNGRoundB(byte[] data, uint[] key, uint[][] table)
{
fixed (byte* bptr = data)
{
DecryptNGRoundB(bptr, key, table);
}
}
// round 1,2,16
public static byte[] DecryptNGRoundA(byte[] data, uint[] key, uint[][] table)
public unsafe static void DecryptNGRoundA(byte* data, uint[] key, uint[][] table)
{
var x1 =
table[0][data[0]] ^
@ -184,18 +193,14 @@ namespace CodeWalker.GameFiles
table[15][data[15]] ^
key[3];
var result = new byte[16];
Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4);
Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4);
Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4);
Array.Copy(BitConverter.GetBytes(x4), 0, result, 12, 4);
return result;
*(uint*)data = x1;
*(uint*)(data + 4) = x2;
*(uint*)(data + 8) = x3;
*(uint*)(data + 12) = x4;
}
// 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 =
table[0][data[0]] ^
@ -222,31 +227,10 @@ namespace CodeWalker.GameFiles
table[12][data[12]] ^
key[3];
//var result = new byte[16];
//Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4);
//Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4);
//Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4);
//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;
*(uint*)data = x1;
*(uint*)(data + 4) = x2;
*(uint*)(data + 8) = x3;
*(uint*)(data + 12) = x4;
}
@ -267,11 +251,11 @@ namespace CodeWalker.GameFiles
public static byte[] EncryptNG(byte[] data, string name, uint length)
{
byte[] key = GetNGKey(name, length);
var key = GetNGKey(name, length);
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))
{
@ -280,14 +264,11 @@ namespace CodeWalker.GameFiles
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++)
{
byte[] decryptedBlock = new byte[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);
}
@ -300,26 +281,15 @@ namespace CodeWalker.GameFiles
return encryptedData;
}
public static byte[] EncryptBlock(byte[] data, uint[] key)
public static byte[] EncryptBlock(byte[] data, uint[][] key)
{
var buffer = data;
// 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 = EncryptRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_ENCRYPT_TABLES[16]);
buffer = EncryptRoundA(buffer, key[16], GTA5Keys.PC_NG_ENCRYPT_TABLES[16]);
for (int k = 15; k >= 2; k--)
buffer = EncryptRoundB_LUT(buffer, subKeys[k], GTA5Keys.PC_NG_ENCRYPT_LUTs[k]);
buffer = EncryptRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_ENCRYPT_TABLES[1]);
buffer = EncryptRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_ENCRYPT_TABLES[0]);
buffer = EncryptRoundB_LUT(buffer, key[k], GTA5Keys.PC_NG_ENCRYPT_LUTs[k]);
buffer = EncryptRoundA(buffer, key[1], GTA5Keys.PC_NG_ENCRYPT_TABLES[1]);
buffer = EncryptRoundA(buffer, key[0], GTA5Keys.PC_NG_ENCRYPT_TABLES[0]);
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
// ng decryption/encryption expanded keys...
public static byte[][] PC_NG_KEYS; // 101, 272
public static uint[][][] PC_NG_KEYS; // 101, 272
// ng decryption tables...
public static uint[][][] PC_NG_DECRYPT_TABLES; // 17, 16, 256
@ -73,15 +73,26 @@ namespace CodeWalker.GameFiles
var exeStr = new MemoryStream(exeData);
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("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("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("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 = GTACrypto.DecryptAESData(db, PC_AES_KEY);
GTACrypto.DecryptAESData(db, PC_AES_KEY);
byte[] b = null;
using (MemoryStream dms = new MemoryStream(db))
{
using (DeflateStream ds = new DeflateStream(dms, CompressionMode.Decompress))
{
using (MemoryStream outstr = new MemoryStream())
using (MemoryStream outstr = RpfFile.recyclableMemoryStreamManager.GetStream())
{
ds.CopyTo(outstr);
b = outstr.GetBuffer();
@ -848,11 +859,13 @@ namespace CodeWalker.GameFiles
random.NextBytes(buf_encrypted);
// decrypt
var buf_decrypted = GTACrypto.DecryptNGRoundA(
GTACrypto.DecryptNGRoundA(
buf_encrypted,
noKey,
tables);
var buf_decrypted = buf_encrypted;
// make row
var row = new RandomGaussRow();
//row.A[0 + buf_decrypted[inByte0]] = true;
@ -1148,33 +1161,35 @@ namespace CodeWalker.GameFiles
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);
var rd = new DataReader(fs);
result = new byte[101][];
for (int i = 0; i < 101; i++)
{
result[i] = rd.ReadBytes(272);
}
fs.Close();
return result;
return ReadNgKeys(rd);
}
public static byte[][] ReadNgKeys(byte[] data)
public static uint[][][] ReadNgKeys(byte[] data)
{
byte[][] result;
using var rd = new DataReader(new MemoryStream(data));
var rd = new DataReader(new MemoryStream(data));
return ReadNgKeys(rd);
}
result = new byte[101][];
public static uint[][][] ReadNgKeys(DataReader rd)
{
var result = new uint[101][][];
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 wr = new DataWriter(fs);
var ms = new MemoryStream();
var wr = new DataWriter(ms);
using var ms = new MemoryStream();
using var wr = new DataWriter(ms);
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();
@ -1207,8 +1229,8 @@ namespace CodeWalker.GameFiles
{
uint[][][] result;
var fs = new FileStream(fileName, FileMode.Open);
var rd = new DataReader(fs);
using var fs = new FileStream(fileName, FileMode.Open);
using var rd = new DataReader(fs);
// 17 rounds...
result = new uint[17][][];
@ -1235,7 +1257,7 @@ namespace CodeWalker.GameFiles
{
uint[][][] result;
var rd = new DataReader(new MemoryStream(data));
using var rd = new DataReader(new MemoryStream(data));
// 17 rounds...
result = new uint[17][][];
@ -1263,8 +1285,8 @@ namespace CodeWalker.GameFiles
{
//var fs = new FileStream(fileName, FileMode.Create);
//var wr = new DataWriter(fs);
var ms = new MemoryStream();
var wr = new DataWriter(ms);
using var ms = new MemoryStream();
using var wr = new DataWriter(ms);
// 17 rounds...
for (int i = 0; i < 17; i++)
@ -1293,8 +1315,8 @@ namespace CodeWalker.GameFiles
{
GTA5NGLUT[][] result;
var fs = new FileStream(fileName, FileMode.Open);
var rd = new DataReader(fs);
using var fs = new FileStream(fileName, FileMode.Open);
using var rd = new DataReader(fs);
// 17 rounds...
result = new GTA5NGLUT[17][];

View File

@ -1,8 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
@ -55,6 +58,24 @@ namespace CodeWalker.GameFiles
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)
{
if (text == null) return 0;
@ -192,59 +213,96 @@ 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 Dictionary<uint, string> Index = new Dictionary<uint, string>();
private static object syncRoot = new object();
public static ConcurrentDictionary<uint, string> Index = new ConcurrentDictionary<uint, string>(Environment.ProcessorCount, 1500000);
public static void Clear()
{
lock (syncRoot)
{
Index.Clear();
}
Index.Clear();
}
public static bool Ensure(string str)
{
uint hash = JenkHash.GenHash(str);
if (hash == 0) return true;
lock (syncRoot)
if (Index.ContainsKey(hash))
{
if (!Index.ContainsKey(hash))
{
Index.Add(hash, str);
return false;
}
return true;
}
lock (Index)
{
Index[hash] = str;
return false;
}
}
public static bool EnsureLower(string str)
{
uint hash = JenkHash.GenHashLower(str);
if (hash == 0) return true;
if (Index.ContainsKey(hash))
{
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;
}
return true;
}
public static string GetString(uint hash)
{
string res;
lock (syncRoot)
if (!Index.TryGetValue(hash, out res))
{
if (!Index.TryGetValue(hash, out res))
{
res = hash.ToString();
}
res = hash.ToString();
}
return res;
}
public static string TryGetString(uint hash)
{
string res;
lock (syncRoot)
if (!Index.TryGetValue(hash, out res))
{
if (!Index.TryGetValue(hash, out res))
{
res = string.Empty;
}
res = string.Empty;
}
return res;
}
@ -252,10 +310,7 @@ namespace CodeWalker.GameFiles
public static string[] GetAllStrings()
{
string[] res = null;
lock (syncRoot)
{
res = Index.Values.ToArray();
}
res = Index.Values.ToArray();
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.Linq;
using System.Text;
@ -15,6 +17,7 @@ namespace CodeWalker
public DateTime CurrentTime = DateTime.Now;
private LinkedList<TVal> loadedList = new LinkedList<TVal>();
private object loadedListLock = new object();
private Dictionary<TKey, LinkedListNode<TVal>> loadedListDict = new Dictionary<TKey, LinkedListNode<TVal>>();
public int Count
@ -43,25 +46,30 @@ namespace CodeWalker
public TVal TryGet(TKey key)
{
LinkedListNode<TVal> lln = null;
if (loadedListDict.TryGetValue(key, out lln))
lock (loadedListLock)
{
loadedList.Remove(lln);
loadedList.AddLast(lln);
lln.Value.LastUseTime = CurrentTime;
if (loadedListDict.TryGetValue(key, out lln))
{
loadedList.Remove(lln);
loadedList.AddLast(lln);
lln.Value.LastUseTime = CurrentTime;
}
}
return (lln != null) ? lln.Value : null;
}
public bool TryAdd(TKey key, TVal item)
{
if (item.MemoryUsage == 0)
{
}
item.Key = key;
if (CanAdd())
{
var lln = loadedList.AddLast(item);
loadedListDict.Add(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
lock(loadedListLock)
{
var lln = loadedList.AddLast(item);
loadedListDict.Add(key, lln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
}
return true;
}
else
@ -74,9 +82,13 @@ namespace CodeWalker
{
while ((!CanAdd()) && (oldlln != null) && ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds > cachetime))
{
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later..
lock(loadedListLock)
{
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later..
}
oldlln.Value = null;
oldlln = null;
//GC.Collect();
@ -87,9 +99,12 @@ namespace CodeWalker
}
if (CanAdd()) //see if there's enough memory now...
{
var newlln = loadedList.AddLast(item);
loadedListDict.Add(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
lock(loadedListLock)
{
var newlln = loadedList.AddLast(item);
loadedListDict.Add(key, newlln);
Interlocked.Add(ref CurrentMemoryUsage, item.MemoryUsage);
}
return true;
}
else
@ -108,35 +123,49 @@ namespace CodeWalker
public void Clear()
{
loadedList.Clear();
loadedListDict.Clear();
CurrentMemoryUsage = 0;
lock(loadedList)
{
loadedList.Clear();
loadedListDict.Clear();
CurrentMemoryUsage = 0;
}
}
public void Remove(TKey key)
{
LinkedListNode<TVal> n;
if (loadedListDict.TryGetValue(key, out n))
if (!loadedListDict.ContainsKey(key))
{
loadedListDict.Remove(key);
loadedList.Remove(n);
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
return;
}
lock(loadedListLock)
{
LinkedListNode<TVal> n;
if (loadedListDict.TryGetValue(key, out n))
{
loadedListDict.Remove(key);
loadedList.Remove(n);
Interlocked.Add(ref CurrentMemoryUsage, -n.Value.MemoryUsage);
}
}
}
public void Compact()
{
var oldlln = loadedList.First;
while (oldlln != null)
lock(loadedList)
{
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break;
var nextln = oldlln.Next;
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later..
oldlln.Value = null;
oldlln = nextln;
var oldlln = loadedList.First;
while (oldlln != null)
{
if ((CurrentTime - oldlln.Value.LastUseTime).TotalSeconds < CacheTime) break;
var nextln = oldlln.Next;
Interlocked.Add(ref CurrentMemoryUsage, -oldlln.Value.MemoryUsage);
loadedListDict.Remove(oldlln.Value.Key);
loadedList.Remove(oldlln); //gc should free up memory later..
oldlln.Value = null;
oldlln = nextln;
}
}
}

View File

@ -781,7 +781,6 @@ namespace CodeWalker
texParam.Unknown_30h = 1;// 131073;//wtf is this? 2x shorts, 0x00020001
texParam.Unknown_32h = 2;
texParam.Name = name;
texParam.NameHash = JenkHash.GenHash(name.ToLowerInvariant());
return texParam;
}
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.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -100,6 +101,10 @@ namespace CodeWalker
}
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>
<ApplicationIcon>CW.ico</ApplicationIcon>
<AssemblyName>CodeWalker Error Report Tool</AssemblyName>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>

View File

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

View File

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

View File

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

View File

@ -9,6 +9,22 @@ struct VS_INPUT
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)
{

View File

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

View File

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

View File

@ -17,7 +17,8 @@ PS_OUTPUT main(VS_Output input)
{
uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location
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 normal = NormalTex.Load(ssloc);
@ -29,9 +30,15 @@ PS_OUTPUT main(VS_Output input)
switch (RenderMode)
{
case 5: output.Colour = float4(diffuse.rgb, 1); return output;
case 6: output.Colour = float4(normal.rgb, 1); return output;
case 7: output.Colour = float4(specular.rgb, 1); return output;
case 5:
output.Colour = float4(diffuse.rgb, 1);
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);

View File

@ -34,7 +34,8 @@ PS_OUTPUT main(VS_Output input)
for (int i = 0; i < sc; 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 normal = NormalTex.Load(ssloc, i);
@ -56,7 +57,8 @@ PS_OUTPUT main(VS_Output input)
d *= SampleMult;
a *= SampleMult;
if (d <= 0) discard;
if (d <= 0)
discard;
PS_OUTPUT output;
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
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 normal = NormalTex.Load(ssloc);
@ -31,7 +32,8 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1;
float4 lcol = DeferredLight(camRel, norm, diffuse, specular, irradiance);
if (lcol.a <= 0) discard;
if (lcol.a <= 0)
discard;
return lcol;
}

View File

@ -117,6 +117,7 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
LODLight lodlight = LODLights[iid];
float3 srpos = lodlight.Position - (camRel + CameraPos.xyz); //light position relative to surface position
float ldist = length(srpos);
if (LightType == 4)//capsule
{
float3 ext = lodlight.Direction.xyz * lodlight.OuterAngleOrCapExt;
@ -124,14 +125,17 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
ldist = lsn.w;
srpos.xyz = lsn.xyz;
}
if (ldist > lodlight.Falloff) return 0; //out of range of the light...
if (ldist <= 0) return 0;
if (ldist > lodlight.Falloff)
return 0; //out of range of the light...
if (ldist <= 0)
return 0;
float4 rgbi = Unpack4x8UNF(lodlight.Colour).gbar;
float3 lcol = rgbi.rgb * rgbi.a * 96.0f;
float3 ldir = srpos / ldist;
float pclit = saturate(dot(ldir, norm));
float lamt = 1;
if (LightType == 1)//point (sphere)
@ -154,7 +158,8 @@ float4 DeferredLODLight(float3 camRel, float3 norm, float4 diffuse, float4 specu
pclit *= lamt;
if (pclit <= 0) return 0;
if (pclit <= 0)
return 0;
float3 refl = GetReflectedDir(camRel, norm);
float specb = saturate(dot(refl, ldir));
@ -177,11 +182,14 @@ float4 DeferredLight(float3 camRel, float3 norm, float4 diffuse, float4 specular
ldist = lsn.w;
srpos.xyz = lsn.xyz;
}
if (ldist > InstFalloff) return 0;
if (ldist <= 0) return 0;
if (ldist > InstFalloff)
return 0;
if (ldist <= 0)
return 0;
float d = dot(srpos, InstCullingPlaneNormal) - InstCullingPlaneOffset;
if (d > 0) return 0;
if (d > 0)
return 0;
float4 rgbi = float4(InstColour, InstIntensity);
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 iang = InstConeInnerAngle;
float oang = InstConeOuterAngle;
if (ang > oang) return 0;
if (ang > oang)
return 0;
lamt *= saturate(1 - ((ang - iang) / (oang - iang)));
lamt *= GetAttenuation(ldist, InstFalloff, InstFalloffExponent);
}
@ -209,7 +218,8 @@ float4 DeferredLight(float3 camRel, float3 norm, float4 diffuse, float4 specular
pclit *= lamt;
if (pclit <= 0) return 0;
if (pclit <= 0)
return 0;
float3 refl = GetReflectedDir(camRel, norm);
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
float2 spos = float2(input.Screen.xy / input.Screen.w);
float4 c = 0;
float d = 0;
int sc = min(SampleCount, 8);
[unroll]
for (int i = 0; i < sc; 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 normal = NormalTex.Load(ssloc, i);
@ -37,15 +37,16 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1;
float4 colour = DeferredLight(camRel, norm, diffuse, specular, irradiance);
if (colour.a <= 0)
discard;
c += colour;
d += depth;
}
c *= SampleMult;
d *= SampleMult;
if (d <= 0) discard;
if (c.a <= 0)
discard;
return c;
}

View File

@ -18,7 +18,8 @@ float4 main(VS_Output input) : SV_TARGET
{
uint3 ssloc = uint3(input.Pos.xy, 0); //pixel location
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 normal = NormalTex.Load(ssloc);
@ -31,7 +32,8 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1;
float4 lcol = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID);
if (lcol.a <= 0) discard;
if (lcol.a <= 0)
discard;
return lcol;
}

View File

@ -26,7 +26,8 @@ float4 main(VS_Output input) : SV_TARGET
for (int i = 0; i < sc; 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 normal = NormalTex.Load(ssloc, i);
@ -38,15 +39,16 @@ float4 main(VS_Output input) : SV_TARGET
float3 norm = normal.xyz * 2 - 1;
float4 colour = DeferredLODLight(camRel, norm, diffuse, specular, irradiance, input.IID);
if (colour.a <= 0)
discard;
c += colour;
d += depth;
}
c *= SampleMult;
d *= SampleMult;
if (d <= 0) discard;
if (c.a <= 0)
discard;
return c;
}

View File

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

View File

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

View File

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

View File

@ -43,27 +43,6 @@
<setting name="Skydome" serializeAs="String">
<value>True</value>
</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">
<value>True</value>
</setting>
@ -247,6 +226,30 @@
<setting name="RPFExplorerStartFolder" serializeAs="String">
<value />
</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>
</userSettings>
<runtime>

View File

@ -9,6 +9,7 @@
<Copyright>dexyfex</Copyright>
<Company>dexyfex software</Company>
<Authors>dexyfex</Authors>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
@ -18,6 +19,7 @@
<ItemGroup>
<PackageReference Include="DockPanelSuite.ThemeVS2015" Version="3.0.6" />
<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.D3DCompiler" 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.XAudio2" Version="4.2.0" />
<PackageReference Include="SharpDX.XInput" Version="4.2.0" />
<PackageReference Include="System.Buffers" Version="4.5.1" />
</ItemGroup>
<ItemGroup>

View File

@ -1,4 +1,6 @@
namespace CodeWalker
using CodeWalker.Core.Utils;
namespace CodeWalker
{
partial class ExploreForm
{
@ -158,6 +160,7 @@
this.OpenFileDialog = new System.Windows.Forms.OpenFileDialog();
this.FolderBrowserDialog = new System.Windows.Forms.FolderBrowserDialog();
this.VSExtender = new WeifenLuo.WinFormsUI.Docking.VisualStudioToolStripExtender(this.components);
this.openConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.MainMenu.SuspendLayout();
this.MainToolbar.SuspendLayout();
this.MainStatusBar.SuspendLayout();
@ -409,6 +412,7 @@
this.ViewSmallIconsMenu,
this.ViewListMenu,
this.ViewDetailsMenu,
this.openConsoleToolStripMenuItem,
this.toolStripSeparator11,
this.ViewThemeMenu});
this.ViewMenu.Name = "ViewMenu";
@ -418,21 +422,21 @@
// 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.Click += new System.EventHandler(this.ViewLargeIconsMenu_Click);
//
// 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.Click += new System.EventHandler(this.ViewSmallIconsMenu_Click);
//
// 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.Click += new System.EventHandler(this.ViewListMenu_Click);
//
@ -441,14 +445,14 @@
this.ViewDetailsMenu.Checked = true;
this.ViewDetailsMenu.CheckState = System.Windows.Forms.CheckState.Checked;
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.Click += new System.EventHandler(this.ViewDetailsMenu_Click);
//
// toolStripSeparator11
//
this.toolStripSeparator11.Name = "toolStripSeparator11";
this.toolStripSeparator11.Size = new System.Drawing.Size(131, 6);
this.toolStripSeparator11.Size = new System.Drawing.Size(177, 6);
//
// ViewThemeMenu
//
@ -458,7 +462,7 @@
this.ViewThemeLightMenu,
this.ViewThemeDarkMenu});
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";
//
// ViewThemeWindowsMenu
@ -506,35 +510,35 @@
// 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.Click += new System.EventHandler(this.ToolsBinSearchMenu_Click);
//
// 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.Click += new System.EventHandler(this.ToolsAudioExplorerMenu_Click);
//
// 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.Click += new System.EventHandler(this.ToolsRpfBrowserMenu_Click);
//
// 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.Click += new System.EventHandler(this.ToolsJenkGenMenu_Click);
//
// 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.Click += new System.EventHandler(this.ToolsJenkIndMenu_Click);
//
@ -1308,6 +1312,13 @@
//
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
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1476,5 +1487,6 @@
private System.Windows.Forms.ToolStripSeparator toolStripSeparator13;
private System.Windows.Forms.ToolStripMenuItem ListContextNewYtdFileMenu;
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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
vwAADr8BOAVTJAAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS
EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM
K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+
@ -134,7 +134,7 @@
<data name="EditViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
vgAADr4B6kKxwAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi
+uZiNACH5wAAAABJRU5ErkJggg==
</value>
@ -142,7 +142,7 @@
<data name="EditExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
vgAADr4B6kKxwAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2
btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP
rxwb6AQLOW8AAAAASUVORK5CYII=
@ -151,7 +151,7 @@
<data name="EditExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
vwAADr8BOAVTJAAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI
4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+
B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg==
@ -160,7 +160,7 @@
<data name="EditImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
vwAADr8BOAVTJAAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL
XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu
QmCC
@ -169,7 +169,7 @@
<data name="EditImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
vwAADr8BOAVTJAAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
+s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS
lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV
AAAAAElFTkSuQmCC
@ -178,7 +178,7 @@
<data name="EditCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value>
@ -186,14 +186,14 @@
<data name="EditRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
vgAADr4B6kKxwAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC
</value>
</data>
<data name="EditDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAVdJREFUOE+lk7FKw1AUhn0BwQdwcHJwEpzcsjkJmZ06+AABfYDqA1h3h+AiHQrS
vwAADr8BOAVTJAAAAVdJREFUOE+lk7FKw1AUhn0BwQdwcHJwEpzcsjkJmZ06+AABfYDqA1h3h+AiHQrS
xUkoOjiIEFwERSgOtoqDdcl6/b+bc9OmJHbwhx+Sc/7z33NyT5acc/9iiTzP18S2mIgrFq6AuJiKA2Mr
JKKfz7F7PDv11PtQrJjoPZ58jL4fTo7d2+01mqzU6CGl8Hx92fPu6ADBpeU4tTPK7l0v2nD93W2HkWKb
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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAANNJREFUOE+lUjEOgzAM5El9A3skRthZy0yZOvE9vpCh7UCGtAtr6otslIgENepJ
vwAADr8BOAVTJAAAANNJREFUOE+lUjEOgzAM5El9A3skRthZy0yZOvE9vpCh7UCGtAtr6otslIgENepJ
J2TfnZ0A1Rm2bWuIC9EFRN2wJQ8y3bXWbpomV9f1TtToQ2frEdgAk1IqCgvR5yHpk5CwyOZxvLnn4+Xe
9uOfw3XwfejwcSQGCfs2CQtRiwYfR2LgeGIKw8K2bc8H/HUCal4g9H3vTUXvgMN2nud9S4rJr5ALd10X
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">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKJJREFUOE/VjLENAjEQBL8ESqEE5NxdfAFU4VYcUocjWnDu4DOnhwdZJyQfFoaI
vwAADr8BOAVTJAAAAKJJREFUOE/VjLENAjEQBL8ESqEE5NxdfAFU4VYcUocjWnDu4DOnhwdZJyQfFoaI
lVY67e7NJiI/2QxXrEetVVZsAY5938U5NzUbthbgknMW7735iOnYsB0AqBXXlJL5jOnYsDUBqA1uMcbh
mYyuz6aAU/M9hKDP3GR0ffYegNrwXEpRADdZr5+aAlB7UAB3j1V/Anh1j1UD4Fub4YrN8HPL9gAVE1vf
J6IiRgAAAABJRU5ErkJggg==
@ -304,7 +304,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADo
HwAAAk1TRnQBSQFMAgEBGAEAAdABAQHQAQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
HwAAAk1TRnQBSQFMAgEBGAEAAfgBAQH4AQEBEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAAXADAAEBAQABCAYAARwYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
@ -446,7 +446,7 @@
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJtJREFUOE+Vj8ENgCAMRdnE1ZzLCwt5cQJvjkDiRfyfUIJQEJo8U2rof5hWOedW
vwAADr8BOAVTJAAAAJtJREFUOE+Vj8ENgCAMRdnE1ZzLCwt5cQJvjkDiRfyfUIJQEJo8U2rof5hWOedW
4CNrHI8XLp3PsXjCPo7Hion3tXm/mwD7KQtJlwVTFmW6MGyRp+MYYD9kUaZjlBaQX4s8XVvQtSjTtQWk
aVGmE4yrBaqFlt6jstDSCX5VBuRj0UtvLSDJopX+R7LAx868X4gGVp5hAQcz4LIxLycs8rg+vnkMAAAA
AElFTkSuQmCC
@ -455,7 +455,7 @@
<data name="pictureBox2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAALVJREFUOE/dk70NQjEMhLMHDZuRBZiAadgD6YmGNjNAhaB4okkb/IEtvfgZIaDj
vwAADr8BOAVTJAAAALVJREFUOE/dk70NQjEMhLMHDZuRBZiAadgD6YmGNjNAhaB4okkb/IEtvfgZIaDj
pCvi853y4ySPWmsWDsLmSC1r2xwiLoVlON7aandui+2pIzU0euhV2xNqHtf7y8zouTlcCRm7EFkUBN9s
8HUNKWbObM03QUOk6XEyAUN05nfEg5eAsAEaIg3i/ZOAl5doiLTpJf72jDoLJZpCg693gwRk8RjlaBo9
w1EGGvLdZ5pCxA++c0p3WGOjVX9N2kUAAAAASUVORK5CYII=
@ -467,7 +467,7 @@
<data name="ListContextViewMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
vwAADr8BOAVTJAAAAQFJREFUOE+dkz0LglAUhv0H/ZLA3+De1FrQ4NJkY9Du3FRLEAZuRVNQS2FLRDQE
UWu0BBX4Mbie7nvxmslNrQMPXI/nfTgXUUlXGIaUgctQo1F5YVDTNCmmaeZLsgQgV5IUeK7/QSFJEUFS
EsXelSUQiPeYjWLvQtMwDNJ1XRoGXwWsMTxfH9Sxt5zT5U6+FxQTsIfGYn+hUt2itrWhZt9h5xGXFBVM
K+acerNDPAgJNklv8U2wgsBenuJBbAJB4OcI2EFluBCUW2MuwSa4jrM7xoE0XCDCte6SlOqAIBFM1kf+
@ -477,7 +477,7 @@
<data name="ListContextViewHexMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
vgAADr4B6kKxwAAAAGlJREFUOE/ljlsKgEAIRV1Sa3BxbncKxN9CUKbCdIboqwsHxMdB8IjIntCYebHV
OLqIiCFEVEsygVJKzoK1bReGJHdBVCsusbOe7APH57prZz1PH0S9IUHElMDrqPeHDyq+E8xgZ28DcADi
+uZiNACH5wAAAABJRU5ErkJggg==
</value>
@ -485,7 +485,7 @@
<data name="ListContextExportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
vgAADr4B6kKxwAAAAKZJREFUOE/Nk0sOwyAMRDlSz8CFOEQqtsldOBitYrFNNa1NXMQnCxaxNIplzzxE
ophplVI6OopE9GBrvWC01lblvR9DegBoCNGAV3z/6RLkCkBDOHZWDyCSPbwcO+segBDCd1lqCMBbFfO2
btmIHjPaqQ3gcJTFsjzziegxww6QKkDC/HmySZuxQ48ryozjEwDlFZxzOYxeoHhqcfxXAilNSo1/wJgP
rxwb6AQLOW8AAAAASUVORK5CYII=
@ -494,7 +494,7 @@
<data name="ListContextExtractRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
vwAADr8BOAVTJAAAAKtJREFUOE/NkNENwjAMRDMSM2QFfpkn3wzQLfLflQqiyq/JmVzkthZFIKRaOtU6
3z0pDSLyk0Ip5RTq1K9YNU9vb6eGJgS1dB5U2OmtpNlWfY05rAHqxRi7UkpbCEKX66glC6BnAS6ERRQI
4I7bbbp3uRAstmwh82N2ARYCAH7WAoK9eouyFSHI8BkdAuWcNeCVoQ0AQwhMBrwy5AIwDfI9AHMswJ7+
B/hcRZ4ta4FRmq6ouAAAAABJRU5ErkJggg==
@ -503,7 +503,7 @@
<data name="ListContextImportXmlMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
vwAADr8BOAVTJAAAAJVJREFUOE+1kNEJwzAMRDNSZvBCHsLFv8068VJpf/yr+oSckuQCVUMMD8Tp/MAe
ROQSNPRAQw809PAd7NRax8bSkB3IRqtxAQpWlOk5SZmLghmZ7VRyEGCBQs5Zyyk95LW8FczIbKcSJtDL
XRBC2NAFXXKLYPOEGON6GXMXoIPuQbD/xBPOP/FfaOiBhh5o6IGGvyPDB6ug8Uv1Fi1pAAAAAElFTkSu
QmCC
@ -512,7 +512,7 @@
<data name="ListContextImportRawMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
vwAADr8BOAVTJAAAAJ5JREFUOE/VkcEJwzAMRTNSZ/AKuWYenztAIZtkJbcp+OrkK5L5qIbEhxwqeBjZ
+s9gD1w55xlo21caLkqfxMLTcxFI5Ek7D40dtW/U8DC+BJPEGEsIoYL+R+LDXsKCpgRDFuAwVpy906fS
lGDIbjMB77UELBGBwQKDBV4kGR2Uh2SB3nBJIH+OhsHAun7PBb4sDFph8EeCM+4TXCeXDdS/V/ydKVLV
AAAAAElFTkSuQmCC
@ -521,7 +521,7 @@
<data name="ListContextCopyMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value>
@ -529,14 +529,14 @@
<data name="ListContextRenameMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
vgAADr4B6kKxwAAAAFBJREFUOE9joBh8+/btPz4MVYYbgBQ5OTmBMUwTMh+qDDegigHYNBKL4S6A0chs
YmjauYAQhqkbTi5A1ojMJ0QTNIAQRvECNgxNb4MWMDAAAPsOTf9dAGOQAAAAAElFTkSuQmCC
</value>
</data>
<data name="ListContextDeleteMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1
MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAFjSURBVDhPpVOxSsNQFM0PCP2A
MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADr8AAA6/ATgFUyQAAAFjSURBVDhPpVOxSsNQFM0PCP2A
Dp0cOgmd3Lo5CU4OTh0E14B+QPQDrLtDcBEHRRx0KgQdFKQQXARRENFGxeHpkvV5zs195kUSHXrg0Mc9
956c5N0GRJ7nLTAGLWjAUIRfQD3SHsclJ4RPFyO732sLs/S6FAGcO2B6s7tj92Zn7P3pEfVIZWlIxttb
IpIni/P28y1jEiYLeT7fWBWNfagd62gBNjGBMyAvN9fZaD4e7sSQNZqgloItHS3AAoXbg7hiwqfxlXj2
@ -551,7 +551,7 @@
<data name="TreeContextCopyPathMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
vgAADr4B6kKxwAAAAIhJREFUOE+9jlEKgCAQRD1KR+gI4Vm8u58WLfpZCbNU22oW0cJD1JnHGm1SSkOB
DpH6xBgXDe99IKIesfLksLX2gnNOl8hVWTCG6URRclxTIgUs2f4C6vWVpYRF+R31XaAFNckjAUtoptN7
k4Dhgryj/qMgn8wrgQT1dgHi12FBjVtBC4h/OcasZcFBjvFV4nkAAAAASUVORK5CYII=
</value>
@ -559,7 +559,7 @@
<data name="TreeContextWinExplorerMenu.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAALNJREFUOE9jIAS+fftmAMTvgfg/NgxVhh0AFYA1t7W1/XdycsLAKAbATETHIM3N
vwAADr8BOAVTJAAAALNJREFUOE9jIAS+fftmAMTvgfg/NgxVhh0AFYA1t7W1/XdycsLAKAbATETHIM3N
zS3/P7z/iIKxGoBuAwgja4aJ4TUAppgQpp4BIAKGyTYAxCEWYzPgfH5+PoZCXPjokaMYBjQsX74cQyEu
3NfXj2GAwZUrVzAU4sLJScmoBkBj4H1kZCSGYlz41s3bYBrZgPm4kis2vH7dejCNbEDCvn37MBQSwsgG
CIA45GCG//8pwf8ZANa5gGyReLItAAAAAElFTkSuQmCC

View File

@ -185,7 +185,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) 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;
var fn = FilePath;
@ -456,7 +456,7 @@ namespace CodeWalker.Forms
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)
{

View File

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

View File

@ -29,7 +29,6 @@ namespace CodeWalker.Forms
volatile bool formopen = false;
//volatile bool running = false;
volatile bool pauserendering = false;
//volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch();
@ -46,6 +45,8 @@ namespace CodeWalker.Forms
System.Drawing.Point MouseLastPoint;
bool MouseInvert = Settings.Default.MouseInvert;
int VerticesCount = 0;
Vector3 prevworldpos = new Vector3(0, 0, 0); //also the start pos
@ -69,6 +70,7 @@ namespace CodeWalker.Forms
}
}
public string FilePath { get; set; }
public bool Pauserendering { get; set; }
YdrFile Ydr = null;
YddFile Ydd = null;
@ -128,13 +130,70 @@ namespace CodeWalker.Forms
public ModelForm(ExploreForm ExpForm = null)
{
if (this.DesignMode) return;
InitializeComponent();
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.waitforchildrentoload = false;
camera = Renderer.camera;
timecycle = Renderer.timecycle;
weather = Renderer.weather;
@ -159,7 +218,7 @@ namespace CodeWalker.Forms
private void Init()
{
//called from ModelForm_Load
if (this.DesignMode) return;
if (!initedOk)
{
Close();
@ -260,13 +319,21 @@ namespace CodeWalker.Forms
public void BuffersResized(int w, int 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)
{
float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart();
if (pauserendering) return;
if (Pauserendering) return;
if (!Monitor.TryEnter(Renderer.RenderSyncRoot, 50))
{ return; } //couldn't get a lock, try again next time
@ -627,6 +694,47 @@ namespace CodeWalker.Forms
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()
{
@ -794,10 +902,10 @@ namespace CodeWalker.Forms
Yft = yft;
rpfFileEntry = Yft.RpfFileEntry;
ModelHash = Yft.RpfFileEntry?.ShortNameHash ?? 0;
var namelower = Yft.RpfFileEntry?.GetShortNameLower();
if (namelower?.EndsWith("_hi") ?? false)
var namelower = Yft.RpfFileEntry?.ShortName;
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)
{
@ -843,6 +951,7 @@ namespace CodeWalker.Forms
UpdateBoundsUI(ybn);
}
public void LoadParticles(YptFile ypt)
{
if (ypt == null) return;
@ -948,7 +1057,7 @@ namespace CodeWalker.Forms
if (gameFileCache == null) return;
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);
while ((ycd != null) && (!ycd.Loaded))
{
@ -1097,19 +1206,15 @@ namespace CodeWalker.Forms
}
private void UpdateModelsUI(DrawableBase drawable, object detailsObject = null)
public void UpdateDrawableUI(DrawableBase drawable)
{
DetailsPropertyGrid.SelectedObject = detailsObject ?? drawable;
DrawableDrawFlags.Clear();
Renderer.SelectionModelDrawFlags.Clear();
Renderer.SelectionGeometryDrawFlags.Clear();
ModelsTreeView.Nodes.Clear();
ModelsTreeView.ShowRootLines = false;
TexturesTreeView.Nodes.Clear();
if (drawable != null)
{
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)
{
//DetailsPropertyGrid.SelectedObject = dict; //this won't look good...
@ -1238,6 +1352,7 @@ namespace CodeWalker.Forms
dnode.Tag = drawable;
dnode.Checked = check;
VerticesCount = 0;
AddDrawableModelsTreeNodes(drawable.DrawableModels?.High, "High Detail", true, dnode);
AddDrawableModelsTreeNodes(drawable.DrawableModels?.Med, "Medium Detail", false, dnode);
AddDrawableModelsTreeNodes(drawable.DrawableModels?.Low, "Low Detail", false, dnode);
@ -1272,6 +1387,7 @@ namespace CodeWalker.Forms
foreach (var geom in model.Geometries)
{
var gname = geom.ToString();
VerticesCount += geom.VerticesCount;
var gnode = mnode.Nodes.Add(gname);
gnode.Tag = geom;
gnode.Checked = true;// check;
@ -1545,6 +1661,10 @@ namespace CodeWalker.Forms
{
td = Ypt?.PtfxList?.TextureDictionary;
}
else if ((Ydd != null) && (Ydd.Loaded))
{
td = Ydd?.Drawables.First().ShaderGroup?.TextureDictionary;
}
if (td != null)
{
@ -1649,7 +1769,7 @@ namespace CodeWalker.Forms
}
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;
}
@ -1727,7 +1847,7 @@ namespace CodeWalker.Forms
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)
{
@ -1985,7 +2105,7 @@ namespace CodeWalker.Forms
File.WriteAllBytes(fpath, dds);
successcount++;
}
catch
catch(Exception ex)
{
errordds.Add(tex.Name ?? "???");
}
@ -2303,7 +2423,7 @@ namespace CodeWalker.Forms
private void StatsUpdateTimer_Tick(object sender, EventArgs e)
{
StatsLabel.Text = Renderer.GetStatusText();
StatsLabel.Text = Renderer.GetStatusText() + $" verts: {VerticesCount}";
if (Renderer.timerunning)
{

View File

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

View File

@ -246,7 +246,7 @@ namespace CodeWalker.Forms
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)
{
@ -383,7 +383,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) 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;
var fn = FilePath;
@ -536,8 +536,8 @@ namespace CodeWalker.Forms
{
if (textsearch)
{
if (((rd.Name?.ToLowerInvariant().Contains(textl)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) ||
(rd.NameHash.ToString().ToLowerInvariant().Contains(textl)))
if (((rd.Name?.Contains(textl, StringComparison.OrdinalIgnoreCase)) ?? false) || (rd.NameHash == hash) || (rd.NameHash == hashl) ||
rd.NameHash.ToString().Contains(textl, StringComparison.OrdinalIgnoreCase))
{
results.Add(rd);
}

View File

@ -197,7 +197,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) 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;
var fn = FilePath;
@ -277,7 +277,7 @@ namespace CodeWalker.Forms
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)
{

View File

@ -184,7 +184,7 @@ namespace CodeWalker.Forms
if (string.IsNullOrEmpty(FileName)) 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;
var fn = FilePath;
@ -228,7 +228,7 @@ namespace CodeWalker.Forms
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)
{

View File

@ -249,7 +249,6 @@ namespace CodeWalker.Forms
if (tex == null) return;
tex.Name = CurrentTexture.Name;
tex.NameHash = CurrentTexture.NameHash;
tex.Usage = CurrentTexture.Usage;
tex.UsageFlags = CurrentTexture.UsageFlags;
tex.Unknown_32h = CurrentTexture.Unknown_32h;
@ -285,7 +284,6 @@ namespace CodeWalker.Forms
var tex = CurrentTexture;
tex.Name = name;
tex.NameHash = JenkHash.GenHash(name.ToLowerInvariant());
var textures = new List<Texture>();
textures.AddRange(TexDict.Textures.data_items);
@ -323,8 +321,7 @@ namespace CodeWalker.Forms
var dds = File.ReadAllBytes(fn);
var tex = DDSIO.GetTexture(dds);
tex.Name = Path.GetFileNameWithoutExtension(fn);
tex.NameHash = JenkHash.GenHash(tex.Name?.ToLowerInvariant());
JenkIndex.Ensure(tex.Name?.ToLowerInvariant());
JenkIndex.EnsureLower(tex.Name);
return tex;
}
catch
@ -456,7 +453,7 @@ namespace CodeWalker.Forms
if (!saveas)
{
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)
{

View File

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

View File

@ -27,9 +27,10 @@ namespace CodeWalker
public Renderer Renderer = null;
public object RenderSyncRoot { get { return Renderer.RenderSyncRoot; } }
public bool Pauserendering { get; set; }
volatile bool formopen = false;
volatile bool running = false;
volatile bool pauserendering = false;
//volatile bool initialised = false;
Stopwatch frametimer = new Stopwatch();
@ -211,7 +212,7 @@ namespace CodeWalker
float elapsed = (float)frametimer.Elapsed.TotalSeconds;
frametimer.Restart();
if (pauserendering) return;
if (Pauserendering) return;
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.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Shell;
@ -12,18 +18,23 @@ namespace CodeWalker
{
static class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
ConsoleWindow.Hide();
bool menumode = false;
bool explorermode = false;
bool projectmode = false;
bool vehiclesmode = false;
bool pedsmode = false;
string path = null;
if ((args != null) && (args.Length > 0))
{
foreach (string arg in args)
@ -49,6 +60,18 @@ namespace CodeWalker
{
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());
}
else if (path != null)
{
var modelForm = new ModelForm();
modelForm.Load += new EventHandler(async (sender, eventArgs) => {
modelForm.ViewModel(path);
});
Application.Run(modelForm);
}
else
{
Application.Run(new WorldForm());

View File

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

View File

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

View File

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

View File

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

View File

@ -7102,12 +7102,6 @@ namespace CodeWalker.Project
var ymap = CurrentProjectFile.YmapFiles[i];
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;
}
}

View File

@ -12,7 +12,7 @@ namespace CodeWalker.Properties {
[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 {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -142,7 +142,7 @@ namespace CodeWalker.Properties {
this["Wireframe"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -154,43 +154,7 @@ namespace CodeWalker.Properties {
this["Skydome"] = value;
}
}
[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.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -526,55 +490,7 @@ namespace CodeWalker.Properties {
this["RenderMode"] = value;
}
}
[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.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("DiffuseSampler")]
@ -914,5 +830,101 @@ namespace CodeWalker.Properties {
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">
<Value Profile="(Default)" />
</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>
</SettingsFile>

View File

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

View File

@ -7,12 +7,14 @@ using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Color = SharpDX.Color;
using Device = SharpDX.Direct3D11.Device;
using Buffer = SharpDX.Direct3D11.Buffer;
using DriverType = SharpDX.Direct3D.DriverType;
using System.Threading;
using System.Windows.Forms;
using SharpDX;
using SharpDX.Direct3D;
using System.Runtime;
using CodeWalker.Core.Utils;
using CodeWalker.Properties;
namespace CodeWalker.Rendering
{
@ -32,7 +34,7 @@ namespace CodeWalker.Rendering
private volatile bool Rendering = false;
private volatile bool Resizing = false;
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 Color clearcolour { get; private set; } = new Color(0.2f, 0.4f, 0.6f, 1.0f); //gross
private System.Drawing.Size beginSize;
@ -209,7 +211,6 @@ namespace CodeWalker.Rendering
{
if (Resizing) return;
Monitor.Enter(syncroot);
int width = dxform.Form.ClientSize.Width;
int height = dxform.Form.ClientSize.Height;
@ -246,6 +247,7 @@ namespace CodeWalker.Rendering
Cleanup();
}
}
private void Dxform_ClientSizeChanged(object sender, EventArgs e)
{
Resize();
@ -273,21 +275,45 @@ namespace CodeWalker.Rendering
private void StartRenderLoop()
{
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()
{
//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 (Resizing)
{
swapchain.Present(1, PresentFlags.None); //just flip buffers when resizing; don't draw
}
while (dxform.Form.WindowState == FormWindowState.Minimized)
if (dxform.Form.WindowState == FormWindowState.Minimized)
{
Thread.Sleep(10); //don't hog CPU when minimised
if (dxform.Form.IsDisposed) return; //if closed while minimised
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)
{
Thread.Sleep(100); //don't hog CPU when minimised
if (dxform.Form.IsDisposed) return; //if closed while minimised
}
dxform.Pauserendering = false;
Console.WriteLine("Window is maximized");
}
if (Form.ActiveForm == null)
{
Thread.Sleep(100); //reduce the FPS when the app isn't active (maybe this should be configurable?)
@ -295,48 +321,54 @@ namespace CodeWalker.Rendering
}
Rendering = true;
if(!Monitor.TryEnter(syncroot, 50))
{
Thread.Sleep(10); //don't hog CPU when not able to render...
continue;
}
bool ok = true;
try
{
context.OutputMerger.SetRenderTargets(depthview, targetview);
context.Rasterizer.SetViewport(0, 0, dxform.Form.ClientSize.Width, dxform.Form.ClientSize.Height);
}
catch (Exception ex)
{
MessageBox.Show("Error setting main render target!\n" + ex.ToString());
ok = false;
}
if (ok)
{
if (dxform.Form.IsDisposed)
if (!Monitor.TryEnter(syncroot, 50))
{
Monitor.Exit(syncroot);
Rendering = false;
return; //the form was closed... stop!!
Console.WriteLine("Failed to get lock for syncroot");
Thread.Sleep(10); //don't hog CPU when not able to render...
continue;
}
dxform.RenderScene(context);
bool ok = true;
try
{
swapchain.Present(1, PresentFlags.None);
context.OutputMerger.SetRenderTargets(depthview, targetview);
context.Rasterizer.SetViewport(0, 0, dxform.Form.ClientSize.Width, dxform.Form.ClientSize.Height);
}
catch (Exception ex)
{
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
MessageBox.Show("Error setting main render target!\n" + ex.ToString());
ok = false;
}
if (ok)
{
if (dxform.Form.IsDisposed)
{
Monitor.Exit(syncroot);
Rendering = false;
return; //the form was closed... stop!!
}
dxform.RenderScene(context);
try
{
swapchain.Present(1, PresentFlags.None);
}
catch (Exception ex)
{
MessageBox.Show("Error presenting swap chain!\n" + ex.ToString());
}
}
}
Monitor.Exit(syncroot);
Rendering = false;
finally
{
Monitor.Exit(syncroot);
Rendering = false;
}
}
}

View File

@ -1475,6 +1475,7 @@ namespace CodeWalker.Rendering
public float FalloffExponent;
public float InnerAngle;//for cone
public float OuterAngleOrCapExt;//outer angle for cone, cap extent for capsule
public float Distance;
}
public LODLight[] Points;
@ -1512,7 +1513,7 @@ namespace CodeWalker.Rendering
for (int i = 0; i < n; i++)
{
var l = ll.LodLights[i];
if (l.Enabled == false) continue;
if (l.Enabled == false || l.Visible == false) continue;
var light = new LODLight();
light.Position = l.Position;
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)
{
if ((Points != null) && (Points.Length > 0))

View File

@ -9,6 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static CodeWalker.Rendering.RenderableLODLights;
namespace CodeWalker.Rendering
{
@ -172,11 +173,7 @@ namespace CodeWalker.Rendering
public Renderer(DXForm form, GameFileCache cache)
{
Form = form;
gameFileCache = cache;
if (gameFileCache == null)
{
gameFileCache = GameFileCacheFactory.Create();
}
gameFileCache = cache ?? GameFileCacheFactory.Create();
renderableCache = new RenderableCache();
var s = Settings.Default;
@ -384,15 +381,13 @@ namespace CodeWalker.Rendering
public string GetStatusText()
{
int rgc = (shaders != null) ? shaders.RenderedGeometries : 0;
int crc = renderableCache.LoadedRenderableCount;
int ctc = renderableCache.LoadedTextureCount;
int tcrc = renderableCache.MemCachedRenderableCount;
int tctc = renderableCache.MemCachedTextureCount;
long vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
string vram = TextUtil.GetBytesReadable(vr);
//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);
var rgc = shaders?.RenderedGeometries ?? 0;
var crc = renderableCache.LoadedRenderableCount;
var ctc = renderableCache.LoadedTextureCount;
var vr = renderableCache.TotalGraphicsMemoryUse + (shaders != null ? shaders.TotalGraphicsMemoryUse : 0);
var vram = TextUtil.GetBytesReadable(vr);
return $"Drawn: {rgc} geom, Loaded: {crc} dr, {ctc} tx, Vram: {vram}, Fps: {fps}";
}
@ -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();
renderworldrenderables.Clear();
@ -1860,7 +1855,7 @@ namespace CodeWalker.Rendering
RequiredParents.Clear();
RenderEntities.Clear();
foreach (var ymap in renderworldVisibleYmapDict.Values)
foreach (var ymap in ymapsWithinStreamingExtents.Values)
{
if (!RenderWorldYmapIsVisible(ymap)) continue;
VisibleYmaps.Add(ymap);
@ -1875,7 +1870,7 @@ namespace CodeWalker.Rendering
LodManager.ShowScriptedYmaps = ShowScriptedYmaps;
LodManager.LODLightsEnabled = renderlodlights;
LodManager.HDLightsEnabled = renderlights;
LodManager.Update(renderworldVisibleYmapDict, camera, currentElapsedTime);
LodManager.Update(ymapsWithinStreamingExtents, camera, currentElapsedTime);
foreach (var updatelodlights in LodManager.UpdateLodLights)
{
@ -2012,120 +2007,6 @@ namespace CodeWalker.Rendering
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)
{
float dist = (ent.Position - camera.Position).Length();
@ -3231,7 +3112,7 @@ namespace CodeWalker.Rendering
Vector3 bbmax = (arche != null) ? arche.BBMax : rndbl.Key.BoundingBoxMax;
Vector3 bscen = (arche != null) ? arche.BSCenter : rndbl.Key.BoundingCenter;
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 castshadow = true;
@ -4154,9 +4035,9 @@ namespace CodeWalker.Rendering
{
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);
}
}
@ -4165,18 +4046,36 @@ namespace CodeWalker.Rendering
{
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);
}
}
}
//foreach (var light in LodLightsDict.Values)
//{
// if (LightVisible(light))
// {
// if (light.Visible)
// {
// light.Visible = false;
// UpdateLodLights.Add(light.LodLights);
// }
// } else
// {
// if (!light.Visible)
// {
// light.Visible = true;
// UpdateLodLights.Add(light.LodLights);
// }
// }
//}
var vl = VisibleLights;
VisibleLights = VisibleLightsPrev;
VisibleLightsPrev = vl;
(VisibleLightsPrev, VisibleLights) = (VisibleLights, VisibleLightsPrev);
}
private void RecurseAddVisibleLeaves(YmapEntityDef ent)
@ -4254,6 +4153,21 @@ namespace CodeWalker.Rendering
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)
{
if (MaxLOD != rage__eLodType.LODTYPES_DEPTH_ORPHANHD)

View File

@ -18,6 +18,9 @@ namespace CodeWalker.Rendering
private int GeometryCount;
public int RenderedGeometries;
private int VerticesCount;
public int RenderedVeritices;
private Device Device;
public bool wireframe = Settings.Default.Wireframe;
@ -148,7 +151,7 @@ namespace CodeWalker.Rendering
IsFrontCounterClockwise = true,
IsMultisampleEnabled = true,
IsScissorEnabled = false,
SlopeScaledDepthBias = 0.0f
SlopeScaledDepthBias = 0.0f,
};
rsSolid = new RasterizerState(device, rsd);
rsd.FillMode = FillMode.Wireframe;
@ -157,6 +160,7 @@ namespace CodeWalker.Rendering
rsWireframeDblSided = new RasterizerState(device, rsd);
rsd.FillMode = FillMode.Solid;
rsSolidDblSided = new RasterizerState(device, rsd);
rsd.CullMode = CullMode.Back;
BlendStateDescription bsd = new BlendStateDescription()
@ -203,6 +207,7 @@ namespace CodeWalker.Rendering
PassOperation = StencilOperation.Zero
},
IsDepthEnabled = true,
IsStencilEnabled = false,
StencilReadMask = 0,
StencilWriteMask = 0
@ -406,6 +411,7 @@ namespace CodeWalker.Rendering
public void RenderQueued(DeviceContext context, Camera camera, Vector4 wind)
{
GeometryCount = 0;
VerticesCount = 0;
Camera = camera;
@ -662,7 +668,7 @@ namespace CodeWalker.Rendering
RenderedGeometries = GeometryCount;
RenderedVeritices = VerticesCount;
}
public void RenderFinalPass(DeviceContext context)
@ -789,6 +795,8 @@ namespace CodeWalker.Rendering
var gmodel = geom.Geom.Owner;
shader.SetEntityVars(context, ref geom.Inst);
VerticesCount += geom.Geom.VertexCount;
if (gmodel != model)
{
model = gmodel;
@ -829,6 +837,7 @@ namespace CodeWalker.Rendering
foreach (var geom in batch)
{
VerticesCount += geom.Geom.VertexCount;
Basic.RenderBoundGeom(context, geom);
}
@ -1050,6 +1059,18 @@ namespace CodeWalker.Rendering
{
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
{
@ -1097,7 +1118,7 @@ namespace CodeWalker.Rendering
ClothBatches.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;

View File

@ -14,6 +14,7 @@ using SharpDX;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using System.Diagnostics;
using CodeWalker.Properties;
namespace CodeWalker.Rendering
{
@ -110,7 +111,7 @@ namespace CodeWalker.Rendering
public int SSAASampleCount = 1;
public int MSAASampleCount = 4;
public int MSAASampleCount = 8;
@ -460,7 +461,8 @@ namespace CodeWalker.Rendering
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.PixelShader.SetShaderResources(6, rll.PointsBuffer.SRV);
@ -470,9 +472,10 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 1;
LightPSVars.Update(context);
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.PixelShader.SetShaderResources(6, rll.SpotsBuffer.SRV);
@ -482,9 +485,9 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 2;
LightPSVars.Update(context);
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.PixelShader.SetShaderResources(6, rll.CapsBuffer.SRV);
@ -494,7 +497,7 @@ namespace CodeWalker.Rendering
LightPSVars.Vars.LightType = 4;
LightPSVars.Update(context);
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:
dtexf = Format.R32_Typeless;
break;
case Format.R8G8B8A8_SNorm:
dtexf = Format.R8G8B8A8_SNorm;
break;
case Format.D32_Float_S8X24_UInt:
dtexf = Format.R32G8X24_Typeless;//is this right? who uses this anyway??
break;

View File

@ -96,7 +96,7 @@ namespace CodeWalker.Rendering
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);
DepthRenderVP = new ViewportF();

View File

@ -127,7 +127,7 @@ namespace CodeWalker.Rendering
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());
indexcount = idata.Count;

View File

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

View File

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

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