2020-01-17 20:02:55 +08:00
|
|
|
|
using CodeWalker.Utils;
|
|
|
|
|
using System;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
2020-01-21 00:12:36 +08:00
|
|
|
|
using System.IO;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2020-01-17 20:02:55 +08:00
|
|
|
|
using System.Xml;
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
namespace CodeWalker.GameFiles
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureDictionary : ResourceFileBase
|
|
|
|
|
{
|
|
|
|
|
public override long BlockLength
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return 64;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// structure data
|
|
|
|
|
public uint Unknown_10h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_14h { get; set; } // 0x00000000
|
2020-01-17 01:32:47 +08:00
|
|
|
|
public uint Unknown_18h { get; set; } = 1; // 0x00000001
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public uint Unknown_1Ch { get; set; } // 0x00000000
|
2019-01-27 14:14:10 +08:00
|
|
|
|
public ResourceSimpleList64_uint TextureNameHashes { get; set; }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public ResourcePointerList64<Texture> Textures { get; set; }
|
|
|
|
|
|
|
|
|
|
public Dictionary<uint, Texture> Dict { get; set; }
|
2019-01-06 02:04:33 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public long MemoryUsage
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
long val = 0;
|
|
|
|
|
if ((Textures != null) && (Textures.data_items != null))
|
|
|
|
|
{
|
|
|
|
|
foreach (var tex in Textures.data_items)
|
|
|
|
|
{
|
|
|
|
|
if (tex != null)
|
|
|
|
|
{
|
|
|
|
|
val += tex.MemoryUsage;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-17 20:02:55 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public TextureDictionary()
|
2020-01-17 20:02:55 +08:00
|
|
|
|
{ }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
base.Read(reader, parameters);
|
|
|
|
|
|
|
|
|
|
// read structure data
|
|
|
|
|
this.Unknown_10h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_14h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_18h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_1Ch = reader.ReadUInt32();
|
2019-01-27 14:14:10 +08:00
|
|
|
|
this.TextureNameHashes = reader.ReadBlock<ResourceSimpleList64_uint>();
|
2017-09-21 18:33:05 +08:00
|
|
|
|
this.Textures = reader.ReadBlock<ResourcePointerList64<Texture>>();
|
|
|
|
|
|
2020-01-17 20:02:55 +08:00
|
|
|
|
BuildDict();
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
base.Write(writer, parameters);
|
|
|
|
|
|
2019-01-27 14:14:10 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
// write structure data
|
|
|
|
|
writer.Write(this.Unknown_10h);
|
|
|
|
|
writer.Write(this.Unknown_14h);
|
|
|
|
|
writer.Write(this.Unknown_18h);
|
|
|
|
|
writer.Write(this.Unknown_1Ch);
|
2019-01-27 14:14:10 +08:00
|
|
|
|
writer.WriteBlock(this.TextureNameHashes);
|
2023-08-15 14:09:06 +08:00
|
|
|
|
if (Textures?.data_items != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var tex in Textures.data_items)
|
|
|
|
|
{
|
|
|
|
|
string textureName = tex.Name;
|
|
|
|
|
if (textureName.StartsWith("pack:/", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
textureName = textureName.Substring(6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (textureName.EndsWith(".dds", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
textureName = textureName.Substring(0, textureName.Length - 4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tex.Name = textureName;
|
|
|
|
|
|
|
|
|
|
tex.Write(writer);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
2020-01-17 20:02:55 +08:00
|
|
|
|
public void WriteXml(StringBuilder sb, int indent, string ddsfolder)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (Textures?.data_items != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var tex in Textures.data_items)
|
|
|
|
|
{
|
|
|
|
|
YtdXml.OpenTag(sb, indent, "Item");
|
2023-08-15 14:09:06 +08:00
|
|
|
|
string textureName = tex.Name;
|
|
|
|
|
|
|
|
|
|
if (textureName.StartsWith("pack:/", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
textureName = textureName.Substring(6);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (textureName.EndsWith(".dds", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
textureName = textureName.Substring(0, textureName.Length - 4); // Strip any existing ".dds" suffix
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tex.Name = textureName;
|
|
|
|
|
|
2020-01-17 20:02:55 +08:00
|
|
|
|
tex.WriteXml(sb, indent + 1, ddsfolder);
|
|
|
|
|
YtdXml.CloseTag(sb, indent, "Item");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public void ReadXml(XmlNode node, string ddsfolder)
|
|
|
|
|
{
|
|
|
|
|
var textures = new List<Texture>();
|
|
|
|
|
|
|
|
|
|
var inodes = node.SelectNodes("Item");
|
|
|
|
|
if (inodes != null)
|
|
|
|
|
{
|
|
|
|
|
foreach (XmlNode inode in inodes)
|
|
|
|
|
{
|
|
|
|
|
var tex = new Texture();
|
|
|
|
|
tex.ReadXml(inode, ddsfolder);
|
|
|
|
|
textures.Add(tex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-28 20:26:35 +08:00
|
|
|
|
BuildFromTextureList(textures);
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
public static void WriteXmlNode(TextureDictionary d, StringBuilder sb, int indent, string ddsfolder, string name = "TextureDictionary")
|
|
|
|
|
{
|
|
|
|
|
if (d == null) return;
|
|
|
|
|
if ((d.Textures?.data_items == null) || (d.Textures.data_items.Length == 0))
|
|
|
|
|
{
|
|
|
|
|
YtdXml.SelfClosingTag(sb, indent, name);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
YtdXml.OpenTag(sb, indent, name);
|
|
|
|
|
d.WriteXml(sb, indent + 1, ddsfolder);
|
|
|
|
|
YtdXml.CloseTag(sb, indent, name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static TextureDictionary ReadXmlNode(XmlNode node, string ddsfolder)
|
|
|
|
|
{
|
|
|
|
|
if (node == null) return null;
|
|
|
|
|
var td = new TextureDictionary();
|
|
|
|
|
td.ReadXml(node, ddsfolder);
|
|
|
|
|
return td;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
public override Tuple<long, IResourceBlock>[] GetParts()
|
|
|
|
|
{
|
|
|
|
|
return new Tuple<long, IResourceBlock>[] {
|
2019-01-27 14:14:10 +08:00
|
|
|
|
new Tuple<long, IResourceBlock>(0x20, TextureNameHashes),
|
2017-09-21 18:33:05 +08:00
|
|
|
|
new Tuple<long, IResourceBlock>(0x30, Textures)
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Texture Lookup(uint hash)
|
|
|
|
|
{
|
|
|
|
|
Texture tex = null;
|
|
|
|
|
if (Dict != null)
|
|
|
|
|
{
|
|
|
|
|
Dict.TryGetValue(hash, out tex);
|
|
|
|
|
}
|
|
|
|
|
return tex;
|
|
|
|
|
}
|
2019-01-06 02:04:33 +08:00
|
|
|
|
|
2020-01-17 20:02:55 +08:00
|
|
|
|
private void BuildDict()
|
|
|
|
|
{
|
|
|
|
|
var dict = new Dictionary<uint, Texture>();
|
|
|
|
|
if ((Textures?.data_items != null) && (TextureNameHashes?.data_items != null))
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; (i < Textures.data_items.Length) && (i < TextureNameHashes.data_items.Length); i++)
|
|
|
|
|
{
|
|
|
|
|
var tex = Textures.data_items[i];
|
|
|
|
|
var hash = TextureNameHashes.data_items[i];
|
|
|
|
|
dict[hash] = tex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Dict = dict;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-28 20:26:35 +08:00
|
|
|
|
public void BuildFromTextureList(List<Texture> textures)
|
|
|
|
|
{
|
|
|
|
|
textures.Sort((a, b) => a.NameHash.CompareTo(b.NameHash));
|
|
|
|
|
|
|
|
|
|
var texturehashes = new List<uint>();
|
|
|
|
|
foreach (var tex in textures)
|
|
|
|
|
{
|
|
|
|
|
texturehashes.Add(tex.NameHash);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextureNameHashes = new ResourceSimpleList64_uint();
|
|
|
|
|
TextureNameHashes.data_items = texturehashes.ToArray();
|
|
|
|
|
Textures = new ResourcePointerList64<Texture>();
|
|
|
|
|
Textures.data_items = textures.ToArray();
|
|
|
|
|
BuildDict();
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock
|
|
|
|
|
{
|
|
|
|
|
public override long BlockLength
|
|
|
|
|
{
|
2020-03-11 21:05:49 +08:00
|
|
|
|
get { return 80; }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// structure data
|
|
|
|
|
public uint VFT { get; set; }
|
2020-01-17 01:32:47 +08:00
|
|
|
|
public uint Unknown_4h { get; set; } = 1; // 0x00000001
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public uint Unknown_8h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_Ch { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_10h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_14h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_18h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_1Ch { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_20h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_24h { get; set; } // 0x00000000
|
|
|
|
|
public ulong NamePointer { get; set; }
|
2020-01-17 01:32:47 +08:00
|
|
|
|
public ushort Unknown_30h { get; set; } = 1;
|
|
|
|
|
public ushort Unknown_32h { get; set; }
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public uint Unknown_34h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_38h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_3Ch { get; set; } // 0x00000000
|
2020-03-11 21:05:49 +08:00
|
|
|
|
public uint UsageData { get; set; }
|
|
|
|
|
public uint Unknown_44h { get; set; } // 0x00000000
|
|
|
|
|
public uint ExtraFlags { get; set; } // 0, 1
|
|
|
|
|
public uint Unknown_4Ch { get; set; } // 0x00000000
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
// reference data
|
|
|
|
|
public string Name { get; set; }
|
|
|
|
|
public uint NameHash { get; set; }
|
|
|
|
|
|
2019-01-27 14:14:10 +08:00
|
|
|
|
private string_r NameBlock = null;
|
|
|
|
|
|
2020-03-11 21:05:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public TextureUsage Usage
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (TextureUsage)(UsageData & 0x1F);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
UsageData = (UsageData & 0xFFFFFFE0) + (((uint)value) & 0x1F);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public TextureUsageFlags UsageFlags
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return (TextureUsageFlags)(UsageData >> 5);
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
UsageData = (UsageData & 0x1F) + (((uint)value) << 5);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
// read structure data
|
|
|
|
|
this.VFT = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_4h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_8h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_Ch = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_10h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_14h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_18h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_1Ch = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_20h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_24h = reader.ReadUInt32();
|
|
|
|
|
this.NamePointer = reader.ReadUInt64();
|
2020-01-17 01:32:47 +08:00
|
|
|
|
this.Unknown_30h = reader.ReadUInt16();
|
|
|
|
|
this.Unknown_32h = reader.ReadUInt16();
|
2017-09-21 18:33:05 +08:00
|
|
|
|
this.Unknown_34h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_38h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_3Ch = reader.ReadUInt32();
|
2020-03-11 21:05:49 +08:00
|
|
|
|
this.UsageData = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_44h = reader.ReadUInt32();
|
|
|
|
|
this.ExtraFlags = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_4Ch = reader.ReadUInt32();
|
|
|
|
|
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
// read reference data
|
|
|
|
|
this.Name = reader.ReadStringAt( //BlockAt<string_r>(
|
|
|
|
|
this.NamePointer // offset
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(Name))
|
|
|
|
|
{
|
2017-12-20 07:52:50 +08:00
|
|
|
|
NameHash = JenkHash.GenHash(Name.ToLowerInvariant());
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
2020-01-17 01:32:47 +08:00
|
|
|
|
|
2020-03-11 21:05:49 +08:00
|
|
|
|
//switch (Unknown_32h)
|
|
|
|
|
//{
|
|
|
|
|
// case 0x20:
|
|
|
|
|
// case 0x28:
|
|
|
|
|
// case 0x30:
|
|
|
|
|
// case 0x38:
|
|
|
|
|
// case 0x40:
|
|
|
|
|
// case 0x48:
|
|
|
|
|
// case 0x80:
|
|
|
|
|
// case 0x90:
|
|
|
|
|
// case 0x2://base/shaderparam
|
|
|
|
|
// break;
|
|
|
|
|
// default:
|
|
|
|
|
// break;//no hit
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//switch (Usage)
|
|
|
|
|
//{
|
|
|
|
|
// case TextureUsage.UNKNOWN:// = 0,
|
|
|
|
|
// case TextureUsage.DEFAULT:// = 1,
|
|
|
|
|
// case TextureUsage.TERRAIN:// = 2,
|
|
|
|
|
// case TextureUsage.CLOUDDENSITY:// = 3,
|
|
|
|
|
// case TextureUsage.CLOUDNORMAL:// = 4,
|
|
|
|
|
// case TextureUsage.CABLE:// = 5,
|
|
|
|
|
// case TextureUsage.FENCE:// = 6,
|
|
|
|
|
// case TextureUsage.SCRIPT:// = 8,
|
|
|
|
|
// case TextureUsage.WATERFLOW:// = 9,
|
|
|
|
|
// case TextureUsage.WATERFOAM:// = 10,
|
|
|
|
|
// case TextureUsage.WATERFOG:// = 11,
|
|
|
|
|
// case TextureUsage.WATEROCEAN:// = 12,
|
|
|
|
|
// case TextureUsage.FOAMOPACITY:// = 14,
|
|
|
|
|
// case TextureUsage.DIFFUSEMIPSHARPEN:// = 16,
|
|
|
|
|
// case TextureUsage.DIFFUSEDARK:// = 18,
|
|
|
|
|
// case TextureUsage.DIFFUSEALPHAOPAQUE:// = 19,
|
|
|
|
|
// case TextureUsage.DIFFUSE:// = 20,
|
|
|
|
|
// case TextureUsage.DETAIL:// = 21,
|
|
|
|
|
// case TextureUsage.NORMAL:// = 22,
|
|
|
|
|
// case TextureUsage.SPECULAR:// = 23,
|
|
|
|
|
// case TextureUsage.EMISSIVE:// = 24,
|
|
|
|
|
// case TextureUsage.TINTPALETTE:// = 25,
|
|
|
|
|
// case TextureUsage.SKIPPROCESSING:// = 26,
|
|
|
|
|
// break;
|
|
|
|
|
// case TextureUsage.ENVEFF:// = 7, //unused by V
|
|
|
|
|
// case TextureUsage.WATER:// = 13, //unused by V
|
|
|
|
|
// case TextureUsage.FOAM:// = 15, //unused by V
|
|
|
|
|
// case TextureUsage.DIFFUSEDETAIL:// = 17, //unused by V
|
|
|
|
|
// case TextureUsage.DONOTOPTIMIZE:// = 27, //unused by V
|
|
|
|
|
// case TextureUsage.TEST:// = 28, //unused by V
|
|
|
|
|
// case TextureUsage.COUNT:// = 29, //unused by V
|
|
|
|
|
// break;//no hit
|
|
|
|
|
// default:
|
|
|
|
|
// break;//no hit
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//var uf = UsageFlags;
|
|
|
|
|
//if ((uf & TextureUsageFlags.EMBEDDEDSCRIPTRT) > 0) // .ydr embedded script_rt textures, only 3 uses
|
|
|
|
|
//{ }
|
|
|
|
|
//if ((uf & TextureUsageFlags.UNK19) > 0)
|
|
|
|
|
//{ }//no hit
|
|
|
|
|
//if ((uf & TextureUsageFlags.UNK20) > 0)
|
|
|
|
|
//{ }//no hit
|
|
|
|
|
//if ((uf & TextureUsageFlags.UNK21) > 0)
|
|
|
|
|
//{ }//no hit
|
|
|
|
|
//if ((uf & TextureUsageFlags.UNK24) == 0)//wtf isthis? only 0 on special resident(?) textures and some reused ones
|
|
|
|
|
//{ }
|
|
|
|
|
|
|
|
|
|
//if (!(this is Texture))
|
|
|
|
|
//{
|
|
|
|
|
// if (Unknown_32h != 0x2)//base/shaderparam
|
|
|
|
|
// { }//no hit
|
|
|
|
|
// if (UsageData != 0)
|
|
|
|
|
// { }//no hit
|
|
|
|
|
// if (Unknown_44h != 0)
|
|
|
|
|
// { }//no hit
|
|
|
|
|
// if (ExtraFlags != 0)
|
|
|
|
|
// { }//no hit
|
|
|
|
|
// if (Unknown_4Ch != 0)
|
|
|
|
|
// { }//no hit
|
|
|
|
|
//}
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
// update structure data
|
2019-01-27 14:14:10 +08:00
|
|
|
|
this.NamePointer = (ulong)(this.NameBlock != null ? this.NameBlock.FilePosition : 0);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
// write structure data
|
|
|
|
|
writer.Write(this.VFT);
|
|
|
|
|
writer.Write(this.Unknown_4h);
|
|
|
|
|
writer.Write(this.Unknown_8h);
|
|
|
|
|
writer.Write(this.Unknown_Ch);
|
|
|
|
|
writer.Write(this.Unknown_10h);
|
|
|
|
|
writer.Write(this.Unknown_14h);
|
|
|
|
|
writer.Write(this.Unknown_18h);
|
|
|
|
|
writer.Write(this.Unknown_1Ch);
|
|
|
|
|
writer.Write(this.Unknown_20h);
|
|
|
|
|
writer.Write(this.Unknown_24h);
|
|
|
|
|
writer.Write(this.NamePointer);
|
|
|
|
|
writer.Write(this.Unknown_30h);
|
2020-01-17 01:32:47 +08:00
|
|
|
|
writer.Write(this.Unknown_32h);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
writer.Write(this.Unknown_34h);
|
|
|
|
|
writer.Write(this.Unknown_38h);
|
|
|
|
|
writer.Write(this.Unknown_3Ch);
|
2020-03-11 21:05:49 +08:00
|
|
|
|
writer.Write(this.UsageData);
|
|
|
|
|
writer.Write(this.Unknown_44h);
|
|
|
|
|
writer.Write(this.ExtraFlags);
|
|
|
|
|
writer.Write(this.Unknown_4Ch);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
2020-01-17 20:02:55 +08:00
|
|
|
|
public virtual void WriteXml(StringBuilder sb, int indent, string ddsfolder)
|
|
|
|
|
{
|
2020-01-18 23:36:28 +08:00
|
|
|
|
YtdXml.StringTag(sb, indent, "Name", YtdXml.XmlEscape(Name));
|
2020-01-17 20:02:55 +08:00
|
|
|
|
YtdXml.ValueTag(sb, indent, "Unk32", Unknown_32h.ToString());
|
2020-03-11 21:05:49 +08:00
|
|
|
|
YtdXml.StringTag(sb, indent, "Usage", Usage.ToString());
|
|
|
|
|
YtdXml.StringTag(sb, indent, "UsageFlags", UsageFlags.ToString());
|
|
|
|
|
YtdXml.ValueTag(sb, indent, "ExtraFlags", ExtraFlags.ToString());
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
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");
|
2020-03-11 21:05:49 +08:00
|
|
|
|
Usage = Xml.GetChildEnumInnerText<TextureUsage>(node, "Usage");
|
|
|
|
|
UsageFlags = Xml.GetChildEnumInnerText<TextureUsageFlags>(node, "UsageFlags");
|
|
|
|
|
ExtraFlags = Xml.GetChildUIntAttribute(node, "ExtraFlags", "value");
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
public override IResourceBlock[] GetReferences()
|
|
|
|
|
{
|
|
|
|
|
var list = new List<IResourceBlock>();
|
2019-01-27 14:14:10 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(Name))
|
|
|
|
|
{
|
|
|
|
|
NameBlock = (string_r)Name;
|
|
|
|
|
list.Add(NameBlock);
|
|
|
|
|
}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
return list.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return "TextureBase: " + Name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class Texture : TextureBase
|
|
|
|
|
{
|
|
|
|
|
public override long BlockLength
|
|
|
|
|
{
|
|
|
|
|
get { return 144; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// structure data
|
|
|
|
|
public ushort Width { get; set; }
|
|
|
|
|
public ushort Height { get; set; }
|
2020-01-17 01:32:47 +08:00
|
|
|
|
public ushort Depth { get; set; } = 1; //is depth > 1 supported?
|
2017-09-21 18:33:05 +08:00
|
|
|
|
public ushort Stride { get; set; }
|
|
|
|
|
public TextureFormat Format { get; set; }
|
|
|
|
|
public byte Unknown_5Ch { get; set; } // 0x00
|
|
|
|
|
public byte Levels { get; set; }
|
|
|
|
|
public ushort Unknown_5Eh { get; set; } // 0x0000
|
|
|
|
|
public uint Unknown_60h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_64h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_68h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_6Ch { get; set; } // 0x00000000
|
|
|
|
|
public ulong DataPointer { get; set; }
|
|
|
|
|
public uint Unknown_78h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_7Ch { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_80h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_84h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_88h { get; set; } // 0x00000000
|
|
|
|
|
public uint Unknown_8Ch { get; set; } // 0x00000000
|
|
|
|
|
|
2020-01-17 14:15:19 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
// reference data
|
|
|
|
|
public TextureData Data { get; set; }
|
|
|
|
|
|
|
|
|
|
public long MemoryUsage
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
long val = 0;
|
|
|
|
|
if (Data != null)
|
|
|
|
|
{
|
|
|
|
|
val += Data.FullData.LongLength;
|
|
|
|
|
}
|
|
|
|
|
return val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
base.Read(reader, parameters);
|
|
|
|
|
|
|
|
|
|
// read structure data
|
|
|
|
|
this.Width = reader.ReadUInt16();
|
|
|
|
|
this.Height = reader.ReadUInt16();
|
2020-01-17 01:32:47 +08:00
|
|
|
|
this.Depth = reader.ReadUInt16();
|
2017-09-21 18:33:05 +08:00
|
|
|
|
this.Stride = reader.ReadUInt16();
|
|
|
|
|
this.Format = (TextureFormat)reader.ReadUInt32();
|
|
|
|
|
this.Unknown_5Ch = reader.ReadByte();
|
|
|
|
|
this.Levels = reader.ReadByte();
|
|
|
|
|
this.Unknown_5Eh = reader.ReadUInt16();
|
|
|
|
|
this.Unknown_60h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_64h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_68h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_6Ch = reader.ReadUInt32();
|
|
|
|
|
this.DataPointer = reader.ReadUInt64();
|
|
|
|
|
this.Unknown_78h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_7Ch = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_80h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_84h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_88h = reader.ReadUInt32();
|
|
|
|
|
this.Unknown_8Ch = reader.ReadUInt32();
|
|
|
|
|
|
|
|
|
|
// read reference data
|
2020-01-17 20:02:55 +08:00
|
|
|
|
this.Data = reader.ReadBlockAt<TextureData>(this.DataPointer, this.Format, this.Width, this.Height, this.Levels, this.Stride);
|
2020-01-17 01:32:47 +08:00
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|
|
|
|
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
base.Write(writer, parameters);
|
|
|
|
|
|
|
|
|
|
this.DataPointer = (ulong)this.Data.FilePosition;
|
|
|
|
|
|
|
|
|
|
// write structure data
|
|
|
|
|
writer.Write(this.Width);
|
|
|
|
|
writer.Write(this.Height);
|
2020-01-17 01:32:47 +08:00
|
|
|
|
writer.Write(this.Depth);
|
2017-09-21 18:33:05 +08:00
|
|
|
|
writer.Write(this.Stride);
|
|
|
|
|
writer.Write((uint)this.Format);
|
|
|
|
|
writer.Write(this.Unknown_5Ch);
|
|
|
|
|
writer.Write(this.Levels);
|
|
|
|
|
writer.Write(this.Unknown_5Eh);
|
|
|
|
|
writer.Write(this.Unknown_60h);
|
|
|
|
|
writer.Write(this.Unknown_64h);
|
|
|
|
|
writer.Write(this.Unknown_68h);
|
|
|
|
|
writer.Write(this.Unknown_6Ch);
|
|
|
|
|
writer.Write(this.DataPointer);
|
|
|
|
|
writer.Write(this.Unknown_78h);
|
|
|
|
|
writer.Write(this.Unknown_7Ch);
|
|
|
|
|
writer.Write(this.Unknown_80h);
|
|
|
|
|
writer.Write(this.Unknown_84h);
|
|
|
|
|
writer.Write(this.Unknown_88h);
|
|
|
|
|
writer.Write(this.Unknown_8Ch);
|
|
|
|
|
}
|
2020-01-17 20:02:55 +08:00
|
|
|
|
public override void WriteXml(StringBuilder sb, int indent, string ddsfolder)
|
|
|
|
|
{
|
|
|
|
|
base.WriteXml(sb, indent, ddsfolder);
|
|
|
|
|
YtdXml.ValueTag(sb, indent, "Width", Width.ToString());
|
|
|
|
|
YtdXml.ValueTag(sb, indent, "Height", Height.ToString());
|
|
|
|
|
YtdXml.ValueTag(sb, indent, "MipLevels", Levels.ToString());
|
|
|
|
|
YtdXml.StringTag(sb, indent, "Format", Format.ToString());
|
2020-02-09 02:22:21 +08:00
|
|
|
|
YtdXml.StringTag(sb, indent, "FileName", YtdXml.XmlEscape((Name ?? "null") + ".dds"));
|
2020-01-17 20:02:55 +08:00
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-01-21 00:12:36 +08:00
|
|
|
|
if (!string.IsNullOrEmpty(ddsfolder))
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(ddsfolder))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(ddsfolder);
|
|
|
|
|
}
|
|
|
|
|
var filepath = Path.Combine(ddsfolder, (Name ?? "null") + ".dds");
|
|
|
|
|
var dds = DDSIO.GetDDSFile(this);
|
|
|
|
|
File.WriteAllBytes(filepath, dds);
|
|
|
|
|
}
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
}
|
|
|
|
|
public override void ReadXml(XmlNode node, string ddsfolder)
|
|
|
|
|
{
|
|
|
|
|
base.ReadXml(node, ddsfolder);
|
|
|
|
|
Width = (ushort)Xml.GetChildUIntAttribute(node, "Width", "value");
|
|
|
|
|
Height = (ushort)Xml.GetChildUIntAttribute(node, "Height", "value");
|
|
|
|
|
Levels = (byte)Xml.GetChildUIntAttribute(node, "MipLevels", "value");
|
|
|
|
|
Format = Xml.GetChildEnumInnerText<TextureFormat>(node, "Format");
|
|
|
|
|
var filename = Xml.GetChildInnerText(node, "FileName");
|
|
|
|
|
|
2021-04-14 12:07:37 +08:00
|
|
|
|
|
|
|
|
|
if ((!string.IsNullOrEmpty(filename)) && (!string.IsNullOrEmpty(ddsfolder)))
|
2020-01-17 20:02:55 +08:00
|
|
|
|
{
|
2020-01-21 00:12:36 +08:00
|
|
|
|
var filepath = Path.Combine(ddsfolder, filename);
|
|
|
|
|
if (File.Exists(filepath))
|
2020-01-17 20:02:55 +08:00
|
|
|
|
{
|
2021-04-14 12:07:37 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var dds = File.ReadAllBytes(filepath);
|
|
|
|
|
var tex = DDSIO.GetTexture(dds);
|
|
|
|
|
if (tex != null)
|
|
|
|
|
{
|
|
|
|
|
Data = tex.Data;
|
|
|
|
|
Width = tex.Width;
|
|
|
|
|
Height = tex.Height;
|
|
|
|
|
Depth = tex.Depth;
|
|
|
|
|
Levels = tex.Levels;
|
|
|
|
|
Format = tex.Format;
|
|
|
|
|
Stride = tex.Stride;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch
|
2020-01-17 20:02:55 +08:00
|
|
|
|
{
|
2021-04-14 12:07:37 +08:00
|
|
|
|
throw new Exception("Texture file format not supported:\n" + filepath);
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-14 12:07:37 +08:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("Texture file not found:\n" + filepath);
|
|
|
|
|
}
|
2020-01-17 20:02:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2017-09-21 18:33:05 +08:00
|
|
|
|
|
|
|
|
|
public override IResourceBlock[] GetReferences()
|
|
|
|
|
{
|
|
|
|
|
var list = new List<IResourceBlock>(base.GetReferences());
|
|
|
|
|
list.Add(Data);
|
|
|
|
|
return list.ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return "Texture: " + Width.ToString() + "x" + Height.ToString() + ": " + Name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureData : ResourceGraphicsBlock
|
|
|
|
|
{
|
|
|
|
|
public override long BlockLength
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return FullData.Length;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] FullData { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Reads the data-block from a stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override void Read(ResourceDataReader reader, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
uint format = Convert.ToUInt32(parameters[0]);
|
|
|
|
|
int Width = Convert.ToInt32(parameters[1]);
|
|
|
|
|
int Height = Convert.ToInt32(parameters[2]);
|
|
|
|
|
int Levels = Convert.ToInt32(parameters[3]);
|
|
|
|
|
int Stride = Convert.ToInt32(parameters[4]);
|
|
|
|
|
|
|
|
|
|
int fullLength = 0;
|
|
|
|
|
int length = Stride * Height;
|
|
|
|
|
for (int i = 0; i < Levels; i++)
|
|
|
|
|
{
|
|
|
|
|
fullLength += length;
|
|
|
|
|
length /= 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FullData = reader.ReadBytes(fullLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Writes the data-block to a stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
|
|
|
|
{
|
|
|
|
|
writer.Write(FullData);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public enum TextureFormat : uint
|
|
|
|
|
{
|
|
|
|
|
D3DFMT_A8R8G8B8 = 21,
|
|
|
|
|
D3DFMT_A1R5G5B5 = 25,
|
|
|
|
|
D3DFMT_A8 = 28,
|
|
|
|
|
D3DFMT_A8B8G8R8 = 32,
|
|
|
|
|
D3DFMT_L8 = 50,
|
|
|
|
|
|
|
|
|
|
// fourCC
|
|
|
|
|
D3DFMT_DXT1 = 0x31545844,
|
|
|
|
|
D3DFMT_DXT3 = 0x33545844,
|
|
|
|
|
D3DFMT_DXT5 = 0x35545844,
|
|
|
|
|
D3DFMT_ATI1 = 0x31495441,
|
|
|
|
|
D3DFMT_ATI2 = 0x32495441,
|
|
|
|
|
D3DFMT_BC7 = 0x20374342,
|
|
|
|
|
|
|
|
|
|
//UNKNOWN
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-17 14:15:19 +08:00
|
|
|
|
public enum TextureUsage : byte
|
|
|
|
|
{
|
|
|
|
|
UNKNOWN = 0,
|
|
|
|
|
DEFAULT = 1,
|
|
|
|
|
TERRAIN = 2,
|
|
|
|
|
CLOUDDENSITY = 3,
|
|
|
|
|
CLOUDNORMAL = 4,
|
|
|
|
|
CABLE = 5,
|
|
|
|
|
FENCE = 6,
|
|
|
|
|
ENVEFF = 7, //unused by V
|
|
|
|
|
SCRIPT = 8,
|
|
|
|
|
WATERFLOW = 9,
|
|
|
|
|
WATERFOAM = 10,
|
|
|
|
|
WATERFOG = 11,
|
|
|
|
|
WATEROCEAN = 12,
|
|
|
|
|
WATER = 13, //unused by V
|
|
|
|
|
FOAMOPACITY = 14,
|
|
|
|
|
FOAM = 15, //unused by V
|
|
|
|
|
DIFFUSEMIPSHARPEN = 16,
|
|
|
|
|
DIFFUSEDETAIL = 17, //unused by V
|
|
|
|
|
DIFFUSEDARK = 18,
|
|
|
|
|
DIFFUSEALPHAOPAQUE = 19,
|
|
|
|
|
DIFFUSE = 20,
|
|
|
|
|
DETAIL = 21,
|
|
|
|
|
NORMAL = 22,
|
|
|
|
|
SPECULAR = 23,
|
|
|
|
|
EMISSIVE = 24,
|
|
|
|
|
TINTPALETTE = 25,
|
|
|
|
|
SKIPPROCESSING = 26,
|
|
|
|
|
DONOTOPTIMIZE = 27, //unused by V
|
|
|
|
|
TEST = 28, //unused by V
|
|
|
|
|
COUNT = 29, //unused by V
|
|
|
|
|
}
|
|
|
|
|
[Flags]
|
|
|
|
|
public enum TextureUsageFlags : uint
|
|
|
|
|
{
|
|
|
|
|
NOT_HALF = 1,
|
|
|
|
|
HD_SPLIT = (1 << 1),
|
2020-01-17 20:02:55 +08:00
|
|
|
|
X2 = (1 << 2),
|
|
|
|
|
X4 = (1 << 3),
|
|
|
|
|
Y4 = (1 << 4),
|
|
|
|
|
X8 = (1 << 5),
|
|
|
|
|
X16 = (1 << 6),
|
|
|
|
|
X32 = (1 << 7),
|
|
|
|
|
X64 = (1 << 8),
|
|
|
|
|
Y64 = (1 << 9),
|
|
|
|
|
X128 = (1 << 10),
|
|
|
|
|
X256 = (1 << 11),
|
|
|
|
|
X512 = (1 << 12),
|
|
|
|
|
Y512 = (1 << 13),
|
|
|
|
|
X1024 = (1 << 14),//wtf is all this?
|
|
|
|
|
Y1024 = (1 << 15),
|
|
|
|
|
X2048 = (1 << 16),
|
|
|
|
|
Y2048 = (1 << 17),
|
|
|
|
|
EMBEDDEDSCRIPTRT = (1 << 18),
|
|
|
|
|
UNK19 = (1 << 19), //unused by V
|
|
|
|
|
UNK20 = (1 << 20), //unused by V
|
|
|
|
|
UNK21 = (1 << 21), //unused by V
|
2020-01-17 14:15:19 +08:00
|
|
|
|
FLAG_FULL = (1 << 22),
|
|
|
|
|
MAPS_HALF = (1 << 23),
|
2020-01-17 20:02:55 +08:00
|
|
|
|
UNK24 = (1 << 24),//used by almost everything...
|
2020-01-17 14:15:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-21 18:33:05 +08:00
|
|
|
|
}
|