mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-16 20:17:30 +08:00
Performance optimalizations and refactoring
This commit is contained in:
parent
ee975e7257
commit
aed7a1e051
1
.gitignore
vendored
1
.gitignore
vendored
@ -241,3 +241,4 @@ ModelManifest.xml
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
/Toolbar/cammodes.psd
|
||||
Shaders
|
@ -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>
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace CodeWalker.GameFiles
|
||||
}
|
||||
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
using var rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -208,7 +208,7 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
using var rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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>");
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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][];
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
2496
CodeWalker.Core/Tests/GameFileCache.cs
Normal file
2496
CodeWalker.Core/Tests/GameFileCache.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
35
CodeWalker.Core/Utils/Timer.cs
Normal file
35
CodeWalker.Core/Utils/Timer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ApplicationIcon>CW.ico</ApplicationIcon>
|
||||
<AssemblyName>CodeWalker Error Report Tool</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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'">
|
||||
|
@ -1,6 +1,3 @@
|
||||
|
||||
|
||||
|
||||
struct ShaderGlobalLightParams
|
||||
{
|
||||
float3 LightDir;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
36
CodeWalker/ExploreForm.Designer.cs
generated
36
CodeWalker/ExploreForm.Designer.cs
generated
@ -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
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
4
CodeWalker/Forms/ModelForm.Designer.cs
generated
4
CodeWalker/Forms/ModelForm.Designer.cs
generated
@ -1,4 +1,6 @@
|
||||
namespace CodeWalker.Forms
|
||||
using CodeWalker.WinForms;
|
||||
|
||||
namespace CodeWalker.Forms
|
||||
{
|
||||
partial class ModelForm
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
188
CodeWalker/Properties/Settings.Designer.cs
generated
188
CodeWalker/Properties/Settings.Designer.cs
generated
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -1,5 +1,6 @@
|
||||
using CodeWalker.GameFiles;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user