Initial commit
This commit is contained in:
5135
C#/CodeWalker.Core/GameFiles/Resources/Bounds.cs
Normal file
5135
C#/CodeWalker.Core/GameFiles/Resources/Bounds.cs
Normal file
File diff suppressed because it is too large
Load Diff
4467
C#/CodeWalker.Core/GameFiles/Resources/Clip.cs
Normal file
4467
C#/CodeWalker.Core/GameFiles/Resources/Clip.cs
Normal file
File diff suppressed because it is too large
Load Diff
2643
C#/CodeWalker.Core/GameFiles/Resources/Clothes.cs
Normal file
2643
C#/CodeWalker.Core/GameFiles/Resources/Clothes.cs
Normal file
File diff suppressed because it is too large
Load Diff
6004
C#/CodeWalker.Core/GameFiles/Resources/Drawable.cs
Normal file
6004
C#/CodeWalker.Core/GameFiles/Resources/Drawable.cs
Normal file
File diff suppressed because it is too large
Load Diff
1940
C#/CodeWalker.Core/GameFiles/Resources/Expression.cs
Normal file
1940
C#/CodeWalker.Core/GameFiles/Resources/Expression.cs
Normal file
File diff suppressed because it is too large
Load Diff
153
C#/CodeWalker.Core/GameFiles/Resources/Filter.cs
Normal file
153
C#/CodeWalker.Core/GameFiles/Resources/Filter.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
/*
|
||||
Copyright(c) 2017 Neodymium
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
//ruthlessly stolen
|
||||
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class FrameFilterDictionary : ResourceFileBase
|
||||
{
|
||||
// pgDictionaryBase
|
||||
// pgDictionary<crFrameFilter>
|
||||
public override long BlockLength => 0x40;
|
||||
|
||||
// structure data
|
||||
public uint Unknown_10h { get; set; } // 0x00000000
|
||||
public uint Unknown_14h { get; set; } // 0x00000000
|
||||
public uint Unknown_18h { get; set; } // 0x00000001
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000
|
||||
public ResourceSimpleList64_uint FilterNameHashes { get; set; }
|
||||
public ResourcePointerList64<FrameFilter> Filters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
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();
|
||||
this.FilterNameHashes = reader.ReadBlock<ResourceSimpleList64_uint>();
|
||||
this.Filters = reader.ReadBlock<ResourcePointerList64<FrameFilter>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.Unknown_18h);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
writer.WriteBlock(this.FilterNameHashes);
|
||||
writer.WriteBlock(this.Filters);
|
||||
}
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
new Tuple<long, IResourceBlock>(0x20, FilterNameHashes),
|
||||
new Tuple<long, IResourceBlock>(0x30, Filters)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class FrameFilter : ResourceSystemBlock
|
||||
{
|
||||
// crFrameFilter -> this is polymorphic, there are many filters
|
||||
public override long BlockLength => 0x40;
|
||||
|
||||
// structure data
|
||||
public uint VFT { get; set; }
|
||||
public uint Unknown_4h { get; set; } // 0x00000001
|
||||
public uint Unknown_8h { get; set; } // 0x00000001
|
||||
public uint Unknown_Ch { get; set; }
|
||||
public uint Unknown_10h { get; set; } // 0x00000004
|
||||
public uint Unknown_14h { get; set; } // 0x00000000
|
||||
public ResourceSimpleList64_ulong Unknown_18h { get; set; }
|
||||
public ResourceSimpleList64_uint Unknown_28h { get; set; }
|
||||
public uint Unknown_38h { get; set; } // 0x00000000
|
||||
public uint Unknown_3Ch { get; set; } // 0x00000000
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
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.ReadBlock<ResourceSimpleList64_ulong>();
|
||||
this.Unknown_28h = reader.ReadBlock<ResourceSimpleList64_uint>();
|
||||
this.Unknown_38h = reader.ReadUInt32();
|
||||
this.Unknown_3Ch = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// 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.WriteBlock(this.Unknown_18h);
|
||||
writer.WriteBlock(this.Unknown_28h);
|
||||
writer.Write(this.Unknown_38h);
|
||||
writer.Write(this.Unknown_3Ch);
|
||||
}
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
new Tuple<long, IResourceBlock>(0x18, Unknown_18h),
|
||||
new Tuple<long, IResourceBlock>(0x28, Unknown_28h)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
4396
C#/CodeWalker.Core/GameFiles/Resources/Frag.cs
Normal file
4396
C#/CodeWalker.Core/GameFiles/Resources/Frag.cs
Normal file
File diff suppressed because it is too large
Load Diff
1143
C#/CodeWalker.Core/GameFiles/Resources/Nav.cs
Normal file
1143
C#/CodeWalker.Core/GameFiles/Resources/Nav.cs
Normal file
File diff suppressed because it is too large
Load Diff
573
C#/CodeWalker.Core/GameFiles/Resources/Node.cs
Normal file
573
C#/CodeWalker.Core/GameFiles/Resources/Node.cs
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
Copyright(c) 2016 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//mangled to fit
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NodeDictionary : ResourceFileBase, IMetaXmlItem
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 112; }
|
||||
}
|
||||
|
||||
public ulong NodesPointer { get; set; }
|
||||
public uint NodesCount { get; set; }
|
||||
public uint NodesCountVehicle { get; set; }
|
||||
public uint NodesCountPed { get; set; }
|
||||
public uint Unk24 { get; set; } // 0x00000000
|
||||
public ulong LinksPtr { get; set; }
|
||||
public uint LinksCount { get; set; }
|
||||
public uint Unk34 { get; set; } // 0x00000000
|
||||
public ulong JunctionsPtr { get; set; }
|
||||
public ulong JunctionHeightmapBytesPtr { get; set; }
|
||||
public uint Unk48 { get; set; } = 1; // 0x00000001
|
||||
public uint Unk4C { get; set; } // 0x00000000
|
||||
public ulong JunctionRefsPtr { get; set; }
|
||||
public ushort JunctionRefsCount0 { get; set; }
|
||||
public ushort JunctionRefsCount1 { get; set; } // same as JunctionRefsCount0
|
||||
public uint Unk5C { get; set; } // 0x00000000
|
||||
public uint JunctionsCount { get; set; } // same as JunctionRefsCount0
|
||||
public uint JunctionHeightmapBytesCount { get; set; }
|
||||
public uint Unk68 { get; set; } // 0x00000000
|
||||
public uint Unk6C { get; set; } // 0x00000000
|
||||
|
||||
public Node[] Nodes { get; set; }
|
||||
public NodeLink[] Links { get; set; }
|
||||
public NodeJunction[] Junctions { get; set; }
|
||||
public byte[] JunctionHeightmapBytes { get; set; }
|
||||
public NodeJunctionRef[] JunctionRefs { get; set; }
|
||||
|
||||
|
||||
private ResourceSystemStructBlock<Node> NodesBlock = null;
|
||||
private ResourceSystemStructBlock<NodeLink> LinksBlock = null;
|
||||
private ResourceSystemStructBlock<NodeJunction> JunctionsBlock = null;
|
||||
private ResourceSystemStructBlock<byte> JunctionHeightmapBytesBlock = null;
|
||||
private ResourceSystemStructBlock<NodeJunctionRef> JunctionRefsBlock = null;
|
||||
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.NodesPointer = reader.ReadUInt64();
|
||||
this.NodesCount = reader.ReadUInt32();
|
||||
this.NodesCountVehicle = reader.ReadUInt32();
|
||||
this.NodesCountPed = reader.ReadUInt32();
|
||||
this.Unk24 = reader.ReadUInt32();
|
||||
this.LinksPtr = reader.ReadUInt64();
|
||||
this.LinksCount = reader.ReadUInt32();
|
||||
this.Unk34 = reader.ReadUInt32();
|
||||
this.JunctionsPtr = reader.ReadUInt64();
|
||||
this.JunctionHeightmapBytesPtr = reader.ReadUInt64();
|
||||
this.Unk48 = reader.ReadUInt32();
|
||||
this.Unk4C = reader.ReadUInt32();
|
||||
this.JunctionRefsPtr = reader.ReadUInt64();
|
||||
this.JunctionRefsCount0 = reader.ReadUInt16();
|
||||
this.JunctionRefsCount1 = reader.ReadUInt16();
|
||||
this.Unk5C = reader.ReadUInt32();
|
||||
this.JunctionsCount = reader.ReadUInt32();
|
||||
this.JunctionHeightmapBytesCount = reader.ReadUInt32();
|
||||
this.Unk68 = reader.ReadUInt32();
|
||||
this.Unk6C = reader.ReadUInt32();
|
||||
|
||||
this.Nodes = reader.ReadStructsAt<Node>(this.NodesPointer, this.NodesCount);
|
||||
this.Links = reader.ReadStructsAt<NodeLink>(this.LinksPtr, this.LinksCount);
|
||||
this.Junctions = reader.ReadStructsAt<NodeJunction>(this.JunctionsPtr, this.JunctionsCount);
|
||||
this.JunctionHeightmapBytes = reader.ReadBytesAt(this.JunctionHeightmapBytesPtr, this.JunctionHeightmapBytesCount);
|
||||
this.JunctionRefs = reader.ReadStructsAt<NodeJunctionRef>(this.JunctionRefsPtr, this.JunctionRefsCount1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// update structure data
|
||||
NodesPointer = (ulong)(NodesBlock?.FilePosition ?? 0);
|
||||
NodesCount = (uint)(Nodes?.Length ?? 0); //assume NodesCountVehicle and Ped already updated..
|
||||
LinksPtr = (ulong)(LinksBlock?.FilePosition ?? 0);
|
||||
LinksCount = (uint)(Links?.Length ?? 0);
|
||||
JunctionsPtr = (ulong)(JunctionsBlock?.FilePosition ?? 0);
|
||||
JunctionHeightmapBytesPtr = (ulong)(JunctionHeightmapBytesBlock?.FilePosition ?? 0);
|
||||
JunctionRefsPtr = (ulong)(JunctionRefsBlock?.FilePosition ?? 0);
|
||||
JunctionRefsCount0 = (ushort)(JunctionRefs?.Length ?? 0);
|
||||
JunctionRefsCount1 = JunctionRefsCount1;
|
||||
JunctionsCount = (uint)(Junctions?.Length ?? 0);
|
||||
JunctionHeightmapBytesCount = (uint)(JunctionHeightmapBytes?.Length ?? 0);
|
||||
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.NodesPointer);
|
||||
writer.Write(this.NodesCount);
|
||||
writer.Write(this.NodesCountVehicle);
|
||||
writer.Write(this.NodesCountPed);
|
||||
writer.Write(this.Unk24);
|
||||
writer.Write(this.LinksPtr);
|
||||
writer.Write(this.LinksCount);
|
||||
writer.Write(this.Unk34);
|
||||
writer.Write(this.JunctionsPtr);
|
||||
writer.Write(this.JunctionHeightmapBytesPtr);
|
||||
writer.Write(this.Unk48);
|
||||
writer.Write(this.Unk4C);
|
||||
writer.Write(this.JunctionRefsPtr);
|
||||
writer.Write(this.JunctionRefsCount0);
|
||||
writer.Write(this.JunctionRefsCount1);
|
||||
writer.Write(this.Unk5C);
|
||||
writer.Write(this.JunctionsCount);
|
||||
writer.Write(this.JunctionHeightmapBytesCount);
|
||||
writer.Write(this.Unk68);
|
||||
writer.Write(this.Unk6C);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
|
||||
if ((JunctionRefs != null) && (JunctionRefs.Length > 0))
|
||||
{
|
||||
JunctionRefsBlock = new ResourceSystemStructBlock<NodeJunctionRef>(JunctionRefs);
|
||||
list.Add(JunctionRefsBlock);
|
||||
}
|
||||
if ((JunctionHeightmapBytes != null) && (JunctionHeightmapBytes.Length > 0))
|
||||
{
|
||||
JunctionHeightmapBytesBlock = new ResourceSystemStructBlock<byte>(JunctionHeightmapBytes);
|
||||
list.Add(JunctionHeightmapBytesBlock);
|
||||
}
|
||||
if ((Junctions != null) && (Junctions.Length > 0))
|
||||
{
|
||||
JunctionsBlock = new ResourceSystemStructBlock<NodeJunction>(Junctions);
|
||||
list.Add(JunctionsBlock);
|
||||
}
|
||||
if ((Links != null) && (Links.Length > 0))
|
||||
{
|
||||
LinksBlock = new ResourceSystemStructBlock<NodeLink>(Links);
|
||||
list.Add(LinksBlock);
|
||||
}
|
||||
if ((Nodes != null) && (Nodes.Length > 0))
|
||||
{
|
||||
NodesBlock = new ResourceSystemStructBlock<Node>(Nodes);
|
||||
list.Add(NodesBlock);
|
||||
}
|
||||
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
YndXml.ValueTag(sb, indent, "VehicleNodeCount", NodesCountVehicle.ToString());
|
||||
YndXml.ValueTag(sb, indent, "PedNodeCount", NodesCountPed.ToString());
|
||||
|
||||
XmlNodeWrapper[] nodes = null;
|
||||
int nodecount = Nodes?.Length ?? 0;
|
||||
if (nodecount > 0)
|
||||
{
|
||||
nodes = new XmlNodeWrapper[nodecount];
|
||||
for (int i = 0; i < nodecount; i++)
|
||||
{
|
||||
nodes[i] = new XmlNodeWrapper(Nodes[i], Links);
|
||||
}
|
||||
}
|
||||
YndXml.WriteItemArray(sb, nodes, indent, "Nodes");
|
||||
|
||||
|
||||
XmlJunctionWrapper[] juncs = null;
|
||||
int junccount = Junctions?.Length ?? 0;
|
||||
if (junccount > 0)
|
||||
{
|
||||
juncs = new XmlJunctionWrapper[junccount];
|
||||
for (int i = 0; i < junccount; i++)
|
||||
{
|
||||
juncs[i] = new XmlJunctionWrapper(Junctions[i], JunctionHeightmapBytes);
|
||||
}
|
||||
}
|
||||
YndXml.WriteItemArray(sb, juncs, indent, "Junctions");
|
||||
|
||||
YndXml.WriteItemArray(sb, JunctionRefs, indent, "JunctionRefs");
|
||||
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
NodesCountVehicle = Xml.GetChildUIntAttribute(node, "VehicleNodeCount", "value");
|
||||
NodesCountPed = Xml.GetChildUIntAttribute(node, "PedNodeCount", "value");
|
||||
|
||||
List<Node> nodelist = new List<Node>();
|
||||
List<NodeLink> linklist = new List<NodeLink>();
|
||||
List<NodeJunction> junclist = new List<NodeJunction>();
|
||||
List<byte> jhmblist = new List<byte>();
|
||||
List<NodeJunctionRef> jreflist = new List<NodeJunctionRef>();
|
||||
|
||||
var nodesnode = node.SelectSingleNode("Nodes");
|
||||
if (nodesnode != null)
|
||||
{
|
||||
var nodeitems = nodesnode.SelectNodes("Item");
|
||||
foreach (XmlNode nodeitem in nodeitems)
|
||||
{
|
||||
XmlNodeWrapper n = new XmlNodeWrapper(linklist);
|
||||
n.ReadXml(nodeitem);
|
||||
nodelist.Add(n.Node);
|
||||
}
|
||||
}
|
||||
|
||||
var juncsnode = node.SelectSingleNode("Junctions");
|
||||
if (juncsnode != null)
|
||||
{
|
||||
var juncitems = juncsnode.SelectNodes("Item");
|
||||
foreach (XmlNode juncitem in juncitems)
|
||||
{
|
||||
XmlJunctionWrapper j = new XmlJunctionWrapper(jhmblist);
|
||||
j.ReadXml(juncitem);
|
||||
junclist.Add(j.Junction);
|
||||
}
|
||||
}
|
||||
|
||||
var jrefsnode = node.SelectSingleNode("JunctionRefs");
|
||||
if (jrefsnode != null)
|
||||
{
|
||||
var jrefitems = jrefsnode.SelectNodes("Item");
|
||||
foreach (XmlNode jrefitem in jrefitems)
|
||||
{
|
||||
NodeJunctionRef jref = new NodeJunctionRef();
|
||||
jref.ReadXml(jrefitem);
|
||||
jreflist.Add(jref);
|
||||
}
|
||||
}
|
||||
|
||||
NodesCount = (uint)nodelist.Count;
|
||||
Nodes = nodelist.ToArray();
|
||||
LinksCount = (uint)linklist.Count;
|
||||
Links = linklist.ToArray();
|
||||
JunctionsCount = (uint)junclist.Count;
|
||||
Junctions = junclist.ToArray();
|
||||
JunctionHeightmapBytesCount = (uint)jhmblist.Count;
|
||||
JunctionHeightmapBytes = jhmblist.ToArray();
|
||||
JunctionRefsCount0 = (ushort)jreflist.Count;
|
||||
JunctionRefsCount1 = JunctionRefsCount0;
|
||||
JunctionRefs = jreflist.ToArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
class XmlNodeWrapper : IMetaXmlItem
|
||||
{
|
||||
public Node Node;
|
||||
private NodeLink[] AllLinks;
|
||||
private List<NodeLink> AllLinksList;
|
||||
|
||||
public XmlNodeWrapper(Node node, NodeLink[] allLinks)
|
||||
{
|
||||
Node = node;
|
||||
AllLinks = allLinks;
|
||||
}
|
||||
public XmlNodeWrapper(List<NodeLink> allLinksList)
|
||||
{
|
||||
AllLinksList = allLinksList;
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
Node.WriteXml(sb, indent, AllLinks);
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
Node = new Node();
|
||||
Node.ReadXml(node, AllLinksList);
|
||||
}
|
||||
}
|
||||
class XmlJunctionWrapper : IMetaXmlItem
|
||||
{
|
||||
public NodeJunction Junction;
|
||||
private byte[] AllHeightmapData;
|
||||
private List<byte> AllHeightmapDataList;
|
||||
|
||||
public XmlJunctionWrapper(NodeJunction junc, byte[] allHeightmapData)
|
||||
{
|
||||
Junction = junc;
|
||||
AllHeightmapData = allHeightmapData;
|
||||
}
|
||||
public XmlJunctionWrapper(List<byte> allHeightmapDataList)
|
||||
{
|
||||
AllHeightmapDataList = allHeightmapDataList;
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
Junction.WriteXml(sb, indent, AllHeightmapData);
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
Junction = new NodeJunction();
|
||||
Junction.ReadXml(node, AllHeightmapDataList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct Node
|
||||
{
|
||||
public uint Unused0 { get; set; } // 0x00000000
|
||||
public uint Unused1 { get; set; } // 0x00000000
|
||||
public uint Unused2 { get; set; } // 0x00000000
|
||||
public uint Unused3 { get; set; } // 0x00000000
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public TextHash StreetName { get; set; }
|
||||
public ushort Unused4 { get; set; }
|
||||
public ushort LinkID { get; set; }
|
||||
public short PositionX { get; set; }
|
||||
public short PositionY { get; set; }
|
||||
public FlagsByte Flags0 { get; set; }
|
||||
public FlagsByte Flags1 { get; set; }
|
||||
public short PositionZ { get; set; }
|
||||
public FlagsByte Flags2 { get; set; }
|
||||
public FlagsByte LinkCountFlags { get; set; }
|
||||
public FlagsByte Flags3 { get; set; }
|
||||
public FlagsByte Flags4 { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
//return Unused0.ToString() + ", " + Unused1.ToString() + ", " + Unused2.ToString() + ", " +
|
||||
// Unused3.ToString() + ", " + AreaID.ToString() + ", " + NodeID.ToString() + ", " +
|
||||
// UnknownInterp.ToString() + ", " + HeuristicCost.ToString() + ", " + LinkID.ToString() + ", " +
|
||||
// PositionX.ToString() + ", " + PositionY.ToString() + ", " + Unk20.ToString() + ", " + Unk21.ToString() + ", " +
|
||||
// Unk22.ToString() + ", " + Unk24.ToString() + ", " + Unk26.ToString();
|
||||
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + StreetName.ToString();// + ", X:" +
|
||||
//PositionX.ToString() + ", Y:" + PositionY.ToString() + ", " + PositionZ.ToString();// + ", " +
|
||||
//Flags0.ToString() + ", " + Flags1.ToString() + ", Z:" +
|
||||
//Flags2.ToString() + ", " + LinkCountFlags.ToString() + ", " +
|
||||
//Flags3.ToString() + ", " + Flags4.ToString();
|
||||
|
||||
}
|
||||
|
||||
public void WriteXml(StringBuilder sb, int indent, NodeLink[] allLinks)
|
||||
{
|
||||
Vector3 p = new Vector3();
|
||||
p.X = PositionX / 4.0f;
|
||||
p.Y = PositionY / 4.0f;
|
||||
p.Z = PositionZ / 32.0f;
|
||||
int linkCount = LinkCountFlags.Value >> 3;
|
||||
int linkCountUnk = LinkCountFlags.Value & 7;
|
||||
|
||||
YndXml.ValueTag(sb, indent, "AreaID", AreaID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "NodeID", NodeID.ToString());
|
||||
YndXml.StringTag(sb, indent, "StreetName", YndXml.HashString(StreetName));
|
||||
YndXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(p));
|
||||
YndXml.ValueTag(sb, indent, "Flags0", Flags0.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags1", Flags1.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags2", Flags2.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags3", Flags3.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags4", Flags4.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags5", linkCountUnk.ToString());
|
||||
|
||||
NodeLink[] links = null;
|
||||
if (linkCount > 0)
|
||||
{
|
||||
links = new NodeLink[linkCount];
|
||||
for (int i = 0; i < linkCount; i++)
|
||||
{
|
||||
links[i] = allLinks[LinkID + i];
|
||||
}
|
||||
}
|
||||
YndXml.WriteItemArray(sb, links, indent, "Links");
|
||||
|
||||
}
|
||||
public void ReadXml(XmlNode node, List<NodeLink> allLinksList)
|
||||
{
|
||||
AreaID = (ushort)Xml.GetChildUIntAttribute(node, "AreaID", "value");
|
||||
NodeID = (ushort)Xml.GetChildUIntAttribute(node, "NodeID", "value");
|
||||
StreetName = XmlYnd.GetTextHash(Xml.GetChildInnerText(node, "StreetName"));
|
||||
Vector3 p = Xml.GetChildVector3Attributes(node, "Position");
|
||||
PositionX = (short)(p.X * 4.0f);
|
||||
PositionY = (short)(p.Y * 4.0f);
|
||||
PositionZ = (short)(p.Z * 32.0f);
|
||||
Flags0 = (byte)Xml.GetChildUIntAttribute(node, "Flags0", "value");
|
||||
Flags1 = (byte)Xml.GetChildUIntAttribute(node, "Flags1", "value");
|
||||
Flags2 = (byte)Xml.GetChildUIntAttribute(node, "Flags2", "value");
|
||||
Flags3 = (byte)Xml.GetChildUIntAttribute(node, "Flags3", "value");
|
||||
Flags4 = (byte)Xml.GetChildUIntAttribute(node, "Flags4", "value");
|
||||
int linkCountUnk = (byte)Xml.GetChildUIntAttribute(node, "Flags5", "value");
|
||||
|
||||
LinkID = (ushort)allLinksList.Count;
|
||||
int linkCount = 0;
|
||||
var linksnode = node.SelectSingleNode("Links");
|
||||
if (linksnode != null)
|
||||
{
|
||||
var linkitems = linksnode.SelectNodes("Item");
|
||||
foreach (XmlNode linkitem in linkitems)
|
||||
{
|
||||
NodeLink link = new NodeLink();
|
||||
link.ReadXml(linkitem);
|
||||
allLinksList.Add(link);
|
||||
linkCount++;
|
||||
}
|
||||
}
|
||||
LinkCountFlags = (byte)((linkCount << 3) + (linkCountUnk & 7));
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeLink : IMetaXmlItem
|
||||
{
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public FlagsByte Flags0 { get; set; }
|
||||
public FlagsByte Flags1 { get; set; }
|
||||
public FlagsByte Flags2 { get; set; }
|
||||
public FlagsByte LinkLength { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + Flags0.Value.ToString() + ", " + Flags1.Value.ToString() + ", " + Flags2.Value.ToString() + ", " + LinkLength.Value.ToString();
|
||||
}
|
||||
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
YndXml.ValueTag(sb, indent, "ToAreaID", AreaID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "ToNodeID", NodeID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags0", Flags0.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags1", Flags1.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Flags2", Flags2.Value.ToString());
|
||||
YndXml.ValueTag(sb, indent, "LinkLength", LinkLength.Value.ToString());
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
AreaID = (ushort)Xml.GetChildUIntAttribute(node, "ToAreaID", "value");
|
||||
NodeID = (ushort)Xml.GetChildUIntAttribute(node, "ToNodeID", "value");
|
||||
Flags0 = (byte)Xml.GetChildUIntAttribute(node, "Flags0", "value");
|
||||
Flags1 = (byte)Xml.GetChildUIntAttribute(node, "Flags1", "value");
|
||||
Flags2 = (byte)Xml.GetChildUIntAttribute(node, "Flags2", "value");
|
||||
LinkLength = (byte)Xml.GetChildUIntAttribute(node, "LinkLength", "value");
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeJunction
|
||||
{
|
||||
public short MaxZ { get; set; }
|
||||
public short PositionX { get; set; }
|
||||
public short PositionY { get; set; }
|
||||
public short MinZ { get; set; }
|
||||
public ushort HeightmapPtr { get; set; }
|
||||
public byte HeightmapDimX { get; set; }
|
||||
public byte HeightmapDimY { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return PositionX.ToString() + ", " + PositionY.ToString() + ": " + MinZ.ToString() + ", " + MaxZ.ToString() + ": " + HeightmapDimX.ToString() + " x " + HeightmapDimY.ToString();
|
||||
}
|
||||
|
||||
public void WriteXml(StringBuilder sb, int indent, byte[] allHeightmapData)
|
||||
{
|
||||
Vector2 p = new Vector2();
|
||||
p.X = PositionX / 4.0f;
|
||||
p.Y = PositionY / 4.0f;
|
||||
float minz = MinZ / 32.0f;
|
||||
float maxz = MaxZ / 32.0f;
|
||||
|
||||
YndXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector2XmlString(p));
|
||||
YndXml.ValueTag(sb, indent, "MinZ", FloatUtil.ToString(minz));
|
||||
YndXml.ValueTag(sb, indent, "MaxZ", FloatUtil.ToString(maxz));
|
||||
YndXml.ValueTag(sb, indent, "SizeX", HeightmapDimX.ToString());
|
||||
YndXml.ValueTag(sb, indent, "SizeY", HeightmapDimY.ToString());
|
||||
|
||||
byte[] hmdata = null;
|
||||
int hmbcount = HeightmapDimX * HeightmapDimY;
|
||||
if (hmbcount > 0)
|
||||
{
|
||||
hmdata = new byte[hmbcount];
|
||||
Buffer.BlockCopy(allHeightmapData, HeightmapPtr, hmdata, 0, hmbcount);
|
||||
}
|
||||
YndXml.WriteRawArray(sb, hmdata, indent, "Heightmap", "", RelXml.FormatHexByte, Math.Max(HeightmapDimX, (byte)1));
|
||||
|
||||
}
|
||||
public void ReadXml(XmlNode node, List<byte> allHeightmapDataList)
|
||||
{
|
||||
Vector2 p = Xml.GetChildVector2Attributes(node, "Position");
|
||||
float minz = Xml.GetChildFloatAttribute(node, "MinZ", "value");
|
||||
float maxz = Xml.GetChildFloatAttribute(node, "MaxZ", "value");
|
||||
HeightmapDimX = (byte)Xml.GetChildUIntAttribute(node, "SizeX", "value");
|
||||
HeightmapDimY = (byte)Xml.GetChildUIntAttribute(node, "SizeY", "value");
|
||||
PositionX = (short)(p.X * 4.0f);
|
||||
PositionY = (short)(p.Y * 4.0f);
|
||||
MinZ = (short)(minz * 32.0f);
|
||||
MaxZ = (short)(maxz * 32.0f);
|
||||
|
||||
byte[] hmdata = Xml.GetChildRawByteArray(node, "Heightmap");
|
||||
HeightmapPtr = (ushort)allHeightmapDataList.Count;
|
||||
if (hmdata != null)
|
||||
{
|
||||
allHeightmapDataList.AddRange(hmdata);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeJunctionRef : IMetaXmlItem
|
||||
{
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public ushort JunctionID { get; set; }
|
||||
public ushort Unk0 { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + JunctionID.ToString();
|
||||
}
|
||||
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
YndXml.ValueTag(sb, indent, "AreaID", AreaID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "NodeID", NodeID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "JunctionID", JunctionID.ToString());
|
||||
YndXml.ValueTag(sb, indent, "Unk0", Unk0.ToString());
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
AreaID = (ushort)Xml.GetChildUIntAttribute(node, "AreaID", "value");
|
||||
NodeID = (ushort)Xml.GetChildUIntAttribute(node, "NodeID", "value");
|
||||
JunctionID = (ushort)Xml.GetChildUIntAttribute(node, "JunctionID", "value");
|
||||
Unk0 = (ushort)Xml.GetChildUIntAttribute(node, "Unk0", "value");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
8303
C#/CodeWalker.Core/GameFiles/Resources/Particle.cs
Normal file
8303
C#/CodeWalker.Core/GameFiles/Resources/Particle.cs
Normal file
File diff suppressed because it is too large
Load Diff
149
C#/CodeWalker.Core/GameFiles/Resources/ResourceAnalyzer.cs
Normal file
149
C#/CodeWalker.Core/GameFiles/Resources/ResourceAnalyzer.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class ResourceAnalyzer
|
||||
{
|
||||
public RpfResourceFileEntry FileEntry { get; set; }
|
||||
public ResourcePagesInfo FilePagesInfo { get; set; }
|
||||
public RpfResourcePage[] SystemPages { get; set; }
|
||||
public RpfResourcePage[] GraphicsPages { get; set; }
|
||||
public ResourceAnalyzerItem[] Blocks { get; set; }
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class ResourceAnalyzerItem
|
||||
{
|
||||
public long Position { get; set; }
|
||||
public long Length { get; set; }
|
||||
public long Offset { get { return Position & 0xFFFFFFF; } }
|
||||
public bool Overlapping { get; set; }
|
||||
public ResourceSystemBlock SystemBlock { get; set; }
|
||||
public ResourceGraphicsBlock GraphicsBlock { get; set; }
|
||||
public Array Array { get; set; }
|
||||
public string String { get; set; }
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var type = "########## ??? ##########";
|
||||
var val = "";
|
||||
if (SystemBlock != null)
|
||||
{
|
||||
type = SystemBlock.GetType().Name;
|
||||
//val = SystemBlock.ToString();
|
||||
}
|
||||
else if (GraphicsBlock != null)
|
||||
{
|
||||
type = GraphicsBlock.GetType().Name;
|
||||
//val = GraphicsBlock.ToString();
|
||||
}
|
||||
else if (Array != null)
|
||||
{
|
||||
type = Array.GetType().Name + " (" + Array.Length.ToString() + ")";
|
||||
}
|
||||
else if (String != null)
|
||||
{
|
||||
type = "string";
|
||||
val = "\"" + String + "\"";
|
||||
}
|
||||
var valstr = (string.IsNullOrEmpty(val) ? "" : " - " + val);
|
||||
return Offset.ToString() + " - " + Length.ToString() + " - " + type + valstr + (Overlapping ? " (embedded)" : "");
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceAnalyzer(ResourceDataReader reader)
|
||||
{
|
||||
FileEntry = reader.FileEntry;
|
||||
SystemPages = FileEntry?.SystemFlags.Pages;
|
||||
GraphicsPages = FileEntry?.GraphicsFlags.Pages;
|
||||
|
||||
var dlist = new List<ResourceAnalyzerItem>();
|
||||
foreach (var kvp in reader.blockPool)
|
||||
{
|
||||
var item = new ResourceAnalyzerItem();
|
||||
item.Position = kvp.Key;
|
||||
item.Length = kvp.Value.BlockLength;
|
||||
item.SystemBlock = kvp.Value as ResourceSystemBlock;
|
||||
item.GraphicsBlock = kvp.Value as ResourceGraphicsBlock;
|
||||
if (kvp.Value is ResourcePagesInfo rpi)
|
||||
{
|
||||
item.Length = 16 + (rpi.SystemPagesCount + rpi.GraphicsPagesCount) * 8;
|
||||
FilePagesInfo = rpi;
|
||||
}
|
||||
dlist.Add(item);
|
||||
}
|
||||
foreach (var kvp in reader.arrayPool)
|
||||
{
|
||||
var item = new ResourceAnalyzerItem();
|
||||
item.Position = kvp.Key;
|
||||
item.Array = kvp.Value as Array;
|
||||
if (item.Array != null)
|
||||
{
|
||||
var typ = item.Array.GetType().GetElementType();
|
||||
var siz = Marshal.SizeOf(typ);
|
||||
item.Length = item.Array.Length * siz;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.String = kvp.Value as string;
|
||||
if (item.String != null)
|
||||
{
|
||||
item.Length = item.String.Length + 1;
|
||||
}
|
||||
}
|
||||
dlist.Add(item);
|
||||
}
|
||||
|
||||
dlist.Sort((a, b) => a.Position.CompareTo(b.Position));
|
||||
|
||||
//Blocks = dlist.ToArray();
|
||||
|
||||
|
||||
var dlist2 = new List<ResourceAnalyzerItem>();
|
||||
long pos = 0;
|
||||
bool gfx = false;
|
||||
foreach (var item in dlist)
|
||||
{
|
||||
if ((item.GraphicsBlock != null) && (!gfx))
|
||||
{
|
||||
pos = 0;
|
||||
gfx = true;
|
||||
}
|
||||
if (item.Offset > pos)
|
||||
{
|
||||
var gap = new ResourceAnalyzerItem();
|
||||
gap.Position = pos;
|
||||
gap.Length = item.Offset - pos;
|
||||
dlist2.Add(gap);
|
||||
pos = item.Offset;
|
||||
}
|
||||
if (item.Offset == pos)
|
||||
{
|
||||
dlist2.Add(item);
|
||||
pos = item.Offset + item.Length;
|
||||
if (item.String == null)
|
||||
{
|
||||
if ((pos % 16) != 0) pos += (16 - (pos % 16));//ignore alignment paddings
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Overlapping = true;
|
||||
dlist2.Add(item);
|
||||
var pos2 = item.Offset + item.Length;
|
||||
if (pos2 > pos) pos = pos2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Blocks = dlist2.ToArray();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
1810
C#/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs
Normal file
1810
C#/CodeWalker.Core/GameFiles/Resources/ResourceBaseTypes.cs
Normal file
File diff suppressed because it is too large
Load Diff
485
C#/CodeWalker.Core/GameFiles/Resources/ResourceBuilder.cs
Normal file
485
C#/CodeWalker.Core/GameFiles/Resources/ResourceBuilder.cs
Normal file
@@ -0,0 +1,485 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class ResourceBuilder
|
||||
{
|
||||
protected const int RESOURCE_IDENT = 0x37435352;
|
||||
protected const int BASE_SIZE = 0x2000;
|
||||
private const int SKIP_SIZE = 16;//512;//256;//64;
|
||||
private const int ALIGN_SIZE = 16;//512;//64;
|
||||
|
||||
public class ResourceBuilderBlock
|
||||
{
|
||||
public IResourceBlock Block;
|
||||
public long Length;
|
||||
|
||||
public ResourceBuilderBlock(IResourceBlock block)
|
||||
{
|
||||
Block = block;
|
||||
Length = block?.BlockLength ?? 0;
|
||||
}
|
||||
}
|
||||
public class ResourceBuilderBlockSet
|
||||
{
|
||||
public bool IsSystemSet = false;
|
||||
public ResourceBuilderBlock RootBlock = null;
|
||||
public LinkedList<ResourceBuilderBlock> BlockList = new LinkedList<ResourceBuilderBlock>();
|
||||
public Dictionary<ResourceBuilderBlock, LinkedListNode<ResourceBuilderBlock>> BlockDict = new Dictionary<ResourceBuilderBlock, LinkedListNode<ResourceBuilderBlock>>();
|
||||
|
||||
public int Count => BlockList.Count;
|
||||
|
||||
public ResourceBuilderBlockSet(IList<IResourceBlock> blocks, bool sys)
|
||||
{
|
||||
IsSystemSet = sys;
|
||||
if (sys && (blocks.Count > 0))
|
||||
{
|
||||
RootBlock = new ResourceBuilderBlock(blocks[0]);
|
||||
}
|
||||
var list = new List<ResourceBuilderBlock>();
|
||||
int start = sys ? 1 : 0;
|
||||
for (int i = start; i < blocks.Count; i++)
|
||||
{
|
||||
var bb = new ResourceBuilderBlock(blocks[i]);
|
||||
list.Add(bb);
|
||||
}
|
||||
list.Sort((a, b) => b.Length.CompareTo(a.Length));
|
||||
foreach (var bb in list)
|
||||
{
|
||||
var ln = BlockList.AddLast(bb);
|
||||
BlockDict[bb] = ln;
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceBuilderBlock FindBestBlock(long maxSize)
|
||||
{
|
||||
var n = BlockList.First;
|
||||
while ((n != null) && (n.Value.Length > maxSize))
|
||||
{
|
||||
n = n.Next;
|
||||
}
|
||||
return n?.Value;
|
||||
}
|
||||
|
||||
public ResourceBuilderBlock TakeBestBlock(long maxSize)
|
||||
{
|
||||
var r = FindBestBlock(maxSize);
|
||||
if (r != null)
|
||||
{
|
||||
if (BlockDict.TryGetValue(r, out LinkedListNode<ResourceBuilderBlock> ln))
|
||||
{
|
||||
BlockList.Remove(ln);
|
||||
BlockDict.Remove(r);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void GetBlocks(IResourceBlock rootBlock, out IList<IResourceBlock> sys, out IList<IResourceBlock> gfx)
|
||||
{
|
||||
var systemBlocks = new HashSet<IResourceBlock>();
|
||||
var graphicBlocks = new HashSet<IResourceBlock>();
|
||||
var processed = new HashSet<IResourceBlock>();
|
||||
|
||||
|
||||
void addBlock(IResourceBlock block)
|
||||
{
|
||||
if (block is IResourceSystemBlock)
|
||||
{
|
||||
if (!systemBlocks.Contains(block)) systemBlocks.Add(block);
|
||||
}
|
||||
else if(block is IResourceGraphicsBlock)
|
||||
{
|
||||
if (!graphicBlocks.Contains(block)) graphicBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
void addChildren(IResourceBlock block)
|
||||
{
|
||||
if (block is IResourceSystemBlock sblock)
|
||||
{
|
||||
var references = sblock.GetReferences();
|
||||
foreach (var reference in references)
|
||||
{
|
||||
if (!processed.Contains(reference))
|
||||
{
|
||||
processed.Add(reference);
|
||||
addBlock(reference);
|
||||
addChildren(reference);
|
||||
}
|
||||
}
|
||||
var parts = sblock.GetParts();
|
||||
foreach (var part in parts)
|
||||
{
|
||||
addChildren(part.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addBlock(rootBlock);
|
||||
addChildren(rootBlock);
|
||||
|
||||
|
||||
sys = new List<IResourceBlock>();
|
||||
foreach (var s in systemBlocks)
|
||||
{
|
||||
sys.Add(s);
|
||||
}
|
||||
gfx = new List<IResourceBlock>();
|
||||
foreach (var s in graphicBlocks)
|
||||
{
|
||||
gfx.Add(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssignPositions(IList<IResourceBlock> blocks, uint basePosition, out RpfResourcePageFlags pageFlags)
|
||||
{
|
||||
if ((blocks.Count > 0) && (blocks[0] is Meta))
|
||||
{
|
||||
//use naive packing strategy for Meta resources, due to crashes caused by the improved packing
|
||||
AssignPositionsForMeta(blocks, basePosition, out pageFlags);
|
||||
return;
|
||||
}
|
||||
|
||||
var sys = (basePosition == 0x50000000);
|
||||
|
||||
long pad(long p)
|
||||
{
|
||||
return ((ALIGN_SIZE - (p % ALIGN_SIZE)) % ALIGN_SIZE);
|
||||
}
|
||||
|
||||
long largestBlockSize = 0; // find largest structure
|
||||
long startPageSize = BASE_SIZE;// 0x2000; // find starting page size
|
||||
long totalBlockSize = 0;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
var blockLength = block.BlockLength;
|
||||
totalBlockSize += blockLength;
|
||||
totalBlockSize += pad(totalBlockSize);
|
||||
if (largestBlockSize < blockLength)
|
||||
{
|
||||
largestBlockSize = blockLength;
|
||||
}
|
||||
}
|
||||
while (startPageSize < largestBlockSize)
|
||||
{
|
||||
startPageSize *= 2;
|
||||
}
|
||||
|
||||
|
||||
pageFlags = new RpfResourcePageFlags();
|
||||
var pageSizeMult = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (blocks.Count == 0) break;
|
||||
|
||||
var blockset = new ResourceBuilderBlockSet(blocks, sys);
|
||||
var rootblock = blockset.RootBlock;
|
||||
var currentPosition = 0L;
|
||||
var currentPageSize = startPageSize;
|
||||
var currentPageStart = 0L;
|
||||
var currentPageSpace = startPageSize;
|
||||
var currentRemainder = totalBlockSize;
|
||||
var pageCount = 1;
|
||||
var pageCounts = new uint[9];
|
||||
var pageCountIndex = 0;
|
||||
var targetPageSize = Math.Max(65536 * pageSizeMult, startPageSize >> (sys ? 5 : 2));
|
||||
var minPageSize = Math.Max(512 * pageSizeMult, Math.Min(targetPageSize, startPageSize) >> 4);
|
||||
var baseShift = 0u;
|
||||
var baseSize = 512;
|
||||
while (baseSize < minPageSize)
|
||||
{
|
||||
baseShift++;
|
||||
baseSize *= 2;
|
||||
if (baseShift >= 0xF) break;
|
||||
}
|
||||
var baseSizeMax = baseSize << 8;
|
||||
var baseSizeMaxTest = startPageSize;
|
||||
while (baseSizeMaxTest < baseSizeMax)
|
||||
{
|
||||
pageCountIndex++;
|
||||
baseSizeMaxTest *= 2;
|
||||
}
|
||||
pageCounts[pageCountIndex] = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var isroot = sys && (currentPosition == 0);
|
||||
var block = isroot ? rootblock : blockset.TakeBestBlock(currentPageSpace);
|
||||
var blockLength = block?.Length ?? 0;
|
||||
if (block != null)
|
||||
{
|
||||
//add this block to the current page.
|
||||
block.Block.FilePosition = basePosition + currentPosition;
|
||||
var opos = currentPosition;
|
||||
currentPosition += blockLength;
|
||||
currentPosition += pad(currentPosition);
|
||||
var usedspace = currentPosition - opos;
|
||||
currentPageSpace -= usedspace;
|
||||
currentRemainder -= usedspace;//blockLength;//
|
||||
|
||||
}
|
||||
else if (blockset.Count > 0)
|
||||
{
|
||||
//allocate a new page
|
||||
currentPageStart += currentPageSize;
|
||||
currentPosition = currentPageStart;
|
||||
block = blockset.FindBestBlock(long.MaxValue); //just find the biggest block
|
||||
blockLength = block?.Length ?? 0;
|
||||
while (blockLength <= (currentPageSize >> 1))//determine best new page size
|
||||
{
|
||||
if (currentPageSize <= minPageSize) break;
|
||||
if (pageCountIndex >= 8) break;
|
||||
if ((currentPageSize <= targetPageSize) && (currentRemainder >= (currentPageSize - minPageSize))) break;
|
||||
|
||||
currentPageSize = currentPageSize >> 1;
|
||||
pageCountIndex++;
|
||||
}
|
||||
currentPageSpace = currentPageSize;
|
||||
pageCounts[pageCountIndex]++;
|
||||
pageCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pageFlags = new RpfResourcePageFlags(pageCounts, baseShift);
|
||||
|
||||
if ((pageCount == pageFlags.Count) && (pageFlags.Size >= currentPosition)) //make sure page counts fit in the flags value
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
startPageSize *= 2;
|
||||
pageSizeMult *= 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void AssignPositionsForMeta(IList<IResourceBlock> blocks, uint basePosition, out RpfResourcePageFlags pageFlags)
|
||||
{
|
||||
// find largest structure
|
||||
long largestBlockSize = 0;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (largestBlockSize < block.BlockLength)
|
||||
largestBlockSize = block.BlockLength;
|
||||
}
|
||||
|
||||
// find minimum page size
|
||||
long currentPageSize = 0x2000;
|
||||
while (currentPageSize < largestBlockSize)
|
||||
currentPageSize *= 2;
|
||||
|
||||
long currentPageCount;
|
||||
long currentPosition;
|
||||
while (true)
|
||||
{
|
||||
currentPageCount = 0;
|
||||
currentPosition = 0;
|
||||
|
||||
// reset all positions
|
||||
foreach (var block in blocks)
|
||||
block.FilePosition = -1;
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block.FilePosition != -1)
|
||||
throw new Exception("Block was already assigned a position!");
|
||||
|
||||
// check if new page is necessary...
|
||||
// if yes, add a new page and align to it
|
||||
long maxSpace = currentPageCount * currentPageSize - currentPosition;
|
||||
if (maxSpace < (block.BlockLength + SKIP_SIZE))
|
||||
{
|
||||
currentPageCount++;
|
||||
currentPosition = currentPageSize * (currentPageCount - 1);
|
||||
}
|
||||
|
||||
// set position
|
||||
block.FilePosition = basePosition + currentPosition;
|
||||
currentPosition += block.BlockLength; // + SKIP_SIZE; //is padding everywhere really necessary??
|
||||
|
||||
// align...
|
||||
if ((currentPosition % ALIGN_SIZE) != 0)
|
||||
currentPosition += (ALIGN_SIZE - (currentPosition % ALIGN_SIZE));
|
||||
}
|
||||
|
||||
// break if everything fits...
|
||||
if (currentPageCount < 128)
|
||||
break;
|
||||
|
||||
currentPageSize *= 2;
|
||||
}
|
||||
|
||||
pageFlags = new RpfResourcePageFlags(RpfResourceFileEntry.GetFlagsFromBlocks((uint)currentPageCount, (uint)currentPageSize, 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Build(ResourceFileBase fileBase, int version, bool compress = true)
|
||||
{
|
||||
|
||||
fileBase.FilePagesInfo = new ResourcePagesInfo();
|
||||
|
||||
IList<IResourceBlock> systemBlocks;
|
||||
IList<IResourceBlock> graphicBlocks;
|
||||
GetBlocks(fileBase, out systemBlocks, out graphicBlocks);
|
||||
|
||||
RpfResourcePageFlags systemPageFlags;
|
||||
AssignPositions(systemBlocks, 0x50000000, out systemPageFlags);
|
||||
|
||||
RpfResourcePageFlags graphicsPageFlags;
|
||||
AssignPositions(graphicBlocks, 0x60000000, out graphicsPageFlags);
|
||||
|
||||
|
||||
fileBase.FilePagesInfo.SystemPagesCount = (byte)systemPageFlags.Count;
|
||||
fileBase.FilePagesInfo.GraphicsPagesCount = (byte)graphicsPageFlags.Count;
|
||||
|
||||
|
||||
var systemStream = new MemoryStream();
|
||||
var graphicsStream = new MemoryStream();
|
||||
var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream);
|
||||
|
||||
resourceWriter.Position = 0x50000000;
|
||||
foreach (var block in systemBlocks)
|
||||
{
|
||||
resourceWriter.Position = block.FilePosition;
|
||||
|
||||
var pos_before = resourceWriter.Position;
|
||||
block.Write(resourceWriter);
|
||||
var pos_after = resourceWriter.Position;
|
||||
|
||||
if ((pos_after - pos_before) != block.BlockLength)
|
||||
{
|
||||
throw new Exception("error in system length");
|
||||
}
|
||||
}
|
||||
|
||||
resourceWriter.Position = 0x60000000;
|
||||
foreach (var block in graphicBlocks)
|
||||
{
|
||||
resourceWriter.Position = block.FilePosition;
|
||||
|
||||
var pos_before = resourceWriter.Position;
|
||||
block.Write(resourceWriter);
|
||||
var pos_after = resourceWriter.Position;
|
||||
|
||||
if ((pos_after - pos_before) != block.BlockLength)
|
||||
{
|
||||
throw new Exception("error in graphics length");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var sysDataSize = (int)systemPageFlags.Size;
|
||||
var sysData = new byte[sysDataSize];
|
||||
systemStream.Flush();
|
||||
systemStream.Position = 0;
|
||||
systemStream.Read(sysData, 0, (int)systemStream.Length);
|
||||
|
||||
|
||||
var gfxDataSize = (int)graphicsPageFlags.Size;
|
||||
var gfxData = new byte[gfxDataSize];
|
||||
graphicsStream.Flush();
|
||||
graphicsStream.Position = 0;
|
||||
graphicsStream.Read(gfxData, 0, (int)graphicsStream.Length);
|
||||
|
||||
|
||||
|
||||
uint uv = (uint)version;
|
||||
uint sv = (uv >> 4) & 0xF;
|
||||
uint gv = (uv >> 0) & 0xF;
|
||||
uint sf = systemPageFlags.Value + (sv << 28);
|
||||
uint gf = graphicsPageFlags.Value + (gv << 28);
|
||||
|
||||
|
||||
var tdatasize = sysDataSize + gfxDataSize;
|
||||
var tdata = new byte[tdatasize];
|
||||
Buffer.BlockCopy(sysData, 0, tdata, 0, sysDataSize);
|
||||
Buffer.BlockCopy(gfxData, 0, tdata, sysDataSize, gfxDataSize);
|
||||
|
||||
|
||||
var cdata = compress ? Compress(tdata) : tdata;
|
||||
|
||||
|
||||
var dataSize = 16 + cdata.Length;
|
||||
var data = new byte[dataSize];
|
||||
|
||||
byte[] h1 = BitConverter.GetBytes((uint)0x37435352);
|
||||
byte[] h2 = BitConverter.GetBytes((int)version);
|
||||
byte[] h3 = BitConverter.GetBytes(sf);
|
||||
byte[] h4 = BitConverter.GetBytes(gf);
|
||||
Buffer.BlockCopy(h1, 0, data, 0, 4);
|
||||
Buffer.BlockCopy(h2, 0, data, 4, 4);
|
||||
Buffer.BlockCopy(h3, 0, data, 8, 4);
|
||||
Buffer.BlockCopy(h4, 0, data, 12, 4);
|
||||
Buffer.BlockCopy(cdata, 0, data, 16, cdata.Length);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte[] AddResourceHeader(RpfResourceFileEntry entry, byte[] data)
|
||||
{
|
||||
if (data == null) return null;
|
||||
byte[] newdata = new byte[data.Length + 16];
|
||||
byte[] h1 = BitConverter.GetBytes((uint)0x37435352);
|
||||
byte[] h2 = BitConverter.GetBytes(entry.Version);
|
||||
byte[] h3 = BitConverter.GetBytes(entry.SystemFlags);
|
||||
byte[] h4 = BitConverter.GetBytes(entry.GraphicsFlags);
|
||||
Buffer.BlockCopy(h1, 0, newdata, 0, 4);
|
||||
Buffer.BlockCopy(h2, 0, newdata, 4, 4);
|
||||
Buffer.BlockCopy(h3, 0, newdata, 8, 4);
|
||||
Buffer.BlockCopy(h4, 0, newdata, 12, 4);
|
||||
Buffer.BlockCopy(data, 0, newdata, 16, data.Length);
|
||||
return newdata;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Compress(byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
public static byte[] Decompress(byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream(data))
|
||||
{
|
||||
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
|
||||
MemoryStream outstr = new MemoryStream();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
779
C#/CodeWalker.Core/GameFiles/Resources/ResourceData.cs
Normal file
779
C#/CodeWalker.Core/GameFiles/Resources/ResourceData.cs
Normal file
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource data reader.
|
||||
/// </summary>
|
||||
public class ResourceDataReader : DataReader
|
||||
{
|
||||
private const long SYSTEM_BASE = 0x50000000;
|
||||
private const long GRAPHICS_BASE = 0x60000000;
|
||||
|
||||
private Stream systemStream;
|
||||
private Stream graphicsStream;
|
||||
|
||||
public RpfResourceFileEntry FileEntry { get; set; }
|
||||
|
||||
// this is a dictionary that contains all the resource blocks
|
||||
// which were read from this resource reader
|
||||
public Dictionary<long, IResourceBlock> blockPool = new Dictionary<long, IResourceBlock>();
|
||||
public Dictionary<long, object> arrayPool = new Dictionary<long, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new resource data reader for the specified system- and graphics-stream.
|
||||
/// </summary>
|
||||
public ResourceDataReader(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
this.systemStream = systemStream;
|
||||
this.graphicsStream = graphicsStream;
|
||||
}
|
||||
|
||||
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
FileEntry = resentry;
|
||||
var systemSize = resentry.SystemSize;
|
||||
var graphicsSize = resentry.GraphicsSize;
|
||||
|
||||
//if (data != null)
|
||||
//{
|
||||
// if (systemSize > data.Length)
|
||||
// {
|
||||
// systemSize = data.Length;
|
||||
// graphicsSize = 0;
|
||||
// }
|
||||
// else if ((systemSize + graphicsSize) > data.Length)
|
||||
// {
|
||||
// graphicsSize = data.Length - systemSize;
|
||||
// }
|
||||
//}
|
||||
|
||||
this.systemStream = new MemoryStream(data, 0, systemSize);
|
||||
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
|
||||
Position = 0x50000000;
|
||||
}
|
||||
|
||||
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
|
||||
{
|
||||
// read from system stream...
|
||||
|
||||
systemStream.Position = Position & ~0x50000000;
|
||||
|
||||
var buffer = new byte[count];
|
||||
systemStream.Read(buffer, 0, count);
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
Array.Reverse(buffer);
|
||||
}
|
||||
|
||||
Position = systemStream.Position | 0x50000000;
|
||||
return buffer;
|
||||
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
{
|
||||
// read from graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~0x60000000;
|
||||
|
||||
var buffer = new byte[count];
|
||||
graphicsStream.Read(buffer, 0, count);
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
Array.Reverse(buffer);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | 0x60000000;
|
||||
return buffer;
|
||||
}
|
||||
throw new Exception("illegal position!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a block.
|
||||
/// </summary>
|
||||
public T ReadBlock<T>(params object[] parameters) where T : IResourceBlock, new()
|
||||
{
|
||||
var usepool = !typeof(IResourceNoCacheBlock).IsAssignableFrom(typeof(T));
|
||||
if (usepool)
|
||||
{
|
||||
// make sure to return the same object if the same
|
||||
// block is read again...
|
||||
if (blockPool.ContainsKey(Position))
|
||||
{
|
||||
var block = blockPool[Position];
|
||||
if (block is T tblk)
|
||||
{
|
||||
Position += block.BlockLength;
|
||||
return tblk;
|
||||
}
|
||||
else
|
||||
{
|
||||
usepool = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = new T();
|
||||
|
||||
|
||||
// replace with correct type...
|
||||
if (result is IResourceXXSystemBlock)
|
||||
{
|
||||
result = (T)((IResourceXXSystemBlock)result).GetType(this, parameters);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
if (usepool)
|
||||
{
|
||||
blockPool[Position] = result;
|
||||
}
|
||||
|
||||
result.Read(this, parameters);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a block at a specified position.
|
||||
/// </summary>
|
||||
public T ReadBlockAt<T>(ulong position, params object[] parameters) where T : IResourceBlock, new()
|
||||
{
|
||||
if (position != 0)
|
||||
{
|
||||
var positionBackup = Position;
|
||||
|
||||
Position = (long)position;
|
||||
var result = ReadBlock<T>(parameters);
|
||||
Position = positionBackup;
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public T[] ReadBlocks<T>(ulong[] pointers) where T : IResourceBlock, new()
|
||||
{
|
||||
if (pointers == null) return null;
|
||||
var count = pointers.Length;
|
||||
var items = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
items[i] = ReadBlockAt<T>(pointers[i]);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
public byte[] ReadBytesAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
long pos = (long)position;
|
||||
if ((pos <= 0) || (count == 0)) return null;
|
||||
var posbackup = Position;
|
||||
Position = pos;
|
||||
var result = ReadBytes((int)count);
|
||||
Position = posbackup;
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
return result;
|
||||
}
|
||||
public ushort[] ReadUshortsAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new ushort[count];
|
||||
var length = count * 2;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result2 = new ushort[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result2[i] = ReadUInt16();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public short[] ReadShortsAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
var result = new short[count];
|
||||
var length = count * 2;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public uint[] ReadUintsAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new uint[count];
|
||||
var length = count * 4;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new uint[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadUInt32();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public ulong[] ReadUlongsAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new ulong[count];
|
||||
var length = count * 8;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new ulong[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadUInt64();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public float[] ReadFloatsAt(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new float[count];
|
||||
var length = count * 4;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new float[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadSingle();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public T[] ReadStructsAt<T>(ulong position, uint count, bool cache = true)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var length = count * structsize;
|
||||
byte[] data = ReadBytesAt(position, length, false);
|
||||
|
||||
//var result2 = new T[count];
|
||||
//Buffer.BlockCopy(data, 0, result2, 0, (int)length); //error: "object must be an array of primitives" :(
|
||||
|
||||
//var result = new T[count];
|
||||
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
//var h = handle.AddrOfPinnedObject();
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
|
||||
//}
|
||||
//handle.Free();
|
||||
|
||||
var result = new T[count];
|
||||
GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
Marshal.Copy(data, 0, h, (int)length);
|
||||
handle.Free();
|
||||
|
||||
|
||||
if (cache) arrayPool[(long)position] = result;
|
||||
|
||||
return result;
|
||||
}
|
||||
public T[] ReadStructs<T>(uint count)
|
||||
{
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var result = new T[count];
|
||||
var length = count * structsize;
|
||||
byte[] data = ReadBytes((int)length);
|
||||
|
||||
//GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
//var h = handle.AddrOfPinnedObject();
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
|
||||
//}
|
||||
//handle.Free();
|
||||
|
||||
GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
Marshal.Copy(data, 0, h, (int)length);
|
||||
handle.Free();
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T ReadStruct<T>() where T : struct
|
||||
{
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var length = structsize;
|
||||
byte[] data = ReadBytes((int)length);
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
var result = Marshal.PtrToStructure<T>(h);
|
||||
handle.Free();
|
||||
return result;
|
||||
}
|
||||
|
||||
public T ReadStructAt<T>(long position) where T : struct
|
||||
{
|
||||
if ((position <= 0)) return default(T);
|
||||
var posbackup = Position;
|
||||
Position = (long)position;
|
||||
var result = ReadStruct<T>();
|
||||
Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ReadStringAt(ulong position)
|
||||
{
|
||||
long newpos = (long)position;
|
||||
if ((newpos <= 0)) return null;
|
||||
var lastpos = Position;
|
||||
Position = newpos;
|
||||
var result = ReadString();
|
||||
Position = lastpos;
|
||||
arrayPool[newpos] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource data writer.
|
||||
/// </summary>
|
||||
public class ResourceDataWriter : DataWriter
|
||||
{
|
||||
private const long SYSTEM_BASE = 0x50000000;
|
||||
private const long GRAPHICS_BASE = 0x60000000;
|
||||
|
||||
private Stream systemStream;
|
||||
private Stream graphicsStream;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new resource data reader for the specified system- and graphics-stream.
|
||||
/// </summary>
|
||||
public ResourceDataWriter(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
this.systemStream = systemStream;
|
||||
this.graphicsStream = graphicsStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the underlying stream. This is the only method that directly accesses
|
||||
/// the data in the underlying stream.
|
||||
/// </summary>
|
||||
protected override void WriteToStream(byte[] value, bool ignoreEndianess = true)
|
||||
{
|
||||
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
|
||||
{
|
||||
// write to system stream...
|
||||
|
||||
systemStream.Position = Position & ~SYSTEM_BASE;
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
systemStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
systemStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = systemStream.Position | 0x50000000;
|
||||
return;
|
||||
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
{
|
||||
// write to graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~GRAPHICS_BASE;
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
graphicsStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicsStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | 0x60000000;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("illegal position!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a block.
|
||||
/// </summary>
|
||||
public void WriteBlock(IResourceBlock value)
|
||||
{
|
||||
value.Write(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void WriteStruct<T>(T val) where T : struct
|
||||
{
|
||||
int size = Marshal.SizeOf(typeof(T));
|
||||
byte[] arr = new byte[size];
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(val, ptr, true);
|
||||
Marshal.Copy(ptr, arr, 0, size);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
Write(arr);
|
||||
}
|
||||
public void WriteStructs<T>(T[] val) where T : struct
|
||||
{
|
||||
if (val == null) return;
|
||||
foreach (var v in val)
|
||||
{
|
||||
WriteStruct(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write enough bytes to the stream to get to the specified alignment.
|
||||
/// </summary>
|
||||
/// <param name="alignment">value to align to</param>
|
||||
public void WritePadding(int alignment)
|
||||
{
|
||||
var pad = ((alignment - (Position % alignment)) % alignment);
|
||||
if (pad > 0) Write(new byte[pad]);
|
||||
}
|
||||
|
||||
public void WriteUlongs(ulong[] val)
|
||||
{
|
||||
if (val == null) return;
|
||||
foreach (var v in val)
|
||||
{
|
||||
Write(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
long FilePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
long BlockLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the system segement in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceSystemBlock : IResourceBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are part of this block.
|
||||
/// </summary>
|
||||
Tuple<long, IResourceBlock>[] GetParts();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are referenced by this block.
|
||||
/// </summary>
|
||||
IResourceBlock[] GetReferences();
|
||||
}
|
||||
|
||||
public interface IResourceXXSystemBlock : IResourceSystemBlock
|
||||
{
|
||||
IResourceSystemBlock GetType(ResourceDataReader reader, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the graphics segmenet in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceGraphicsBlock : IResourceBlock
|
||||
{ }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block that won't get cached while loading.
|
||||
/// </summary>
|
||||
public interface IResourceNoCacheBlock : IResourceBlock
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the system segement in a resource file.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class ResourceSystemBlock : IResourceSystemBlock
|
||||
{
|
||||
private long position;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
public virtual long FilePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
foreach (var part in GetParts())
|
||||
{
|
||||
part.Item2.FilePosition = value + part.Item1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
public abstract long BlockLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
public abstract void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
public abstract void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are part of this block.
|
||||
/// </summary>
|
||||
public virtual Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are referenced by this block.
|
||||
/// </summary>
|
||||
public virtual IResourceBlock[] GetReferences()
|
||||
{
|
||||
return new IResourceBlock[0];
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ResourecTypedSystemBlock : ResourceSystemBlock, IResourceXXSystemBlock
|
||||
{
|
||||
public abstract IResourceSystemBlock GetType(ResourceDataReader reader, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the graphics segmenet in a resource file.
|
||||
/// </summary>
|
||||
public abstract class ResourceGraphicsBlock : IResourceGraphicsBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
public virtual long FilePosition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
public abstract long BlockLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
public abstract void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
public abstract void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//public interface ResourceDataStruct
|
||||
//{
|
||||
// void Read(ResourceDataReader reader);
|
||||
// void Write(ResourceDataWriter writer);
|
||||
//}
|
||||
|
||||
}
|
||||
151
C#/CodeWalker.Core/GameFiles/Resources/ResourceFile.cs
Normal file
151
C#/CodeWalker.Core/GameFiles/Resources/ResourceFile.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourceFileBase : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint FileVFT { get; set; }
|
||||
public uint FileUnknown { get; set; } = 1;
|
||||
public ulong FilePagesInfoPointer { get; set; }
|
||||
|
||||
// reference data
|
||||
public ResourcePagesInfo FilePagesInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.FileVFT = reader.ReadUInt32();
|
||||
this.FileUnknown = reader.ReadUInt32();
|
||||
this.FilePagesInfoPointer = reader.ReadUInt64();
|
||||
|
||||
// read reference data
|
||||
this.FilePagesInfo = reader.ReadBlockAt<ResourcePagesInfo>(
|
||||
this.FilePagesInfoPointer // offset
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
this.FilePagesInfoPointer = (ulong)(this.FilePagesInfo != null ? this.FilePagesInfo.FilePosition : 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.FileVFT);
|
||||
writer.Write(this.FileUnknown);
|
||||
writer.Write(this.FilePagesInfoPointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (FilePagesInfo != null) list.Add(FilePagesInfo);
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourcePagesInfo : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 20 + (256 * 16); }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint Unknown_0h { get; set; }
|
||||
public uint Unknown_4h { get; set; }
|
||||
public byte SystemPagesCount { get; set; }
|
||||
public byte GraphicsPagesCount { get; set; }
|
||||
public ushort Unknown_Ah { get; set; }
|
||||
public uint Unknown_Ch { get; set; }
|
||||
public uint Unknown_10h { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.Unknown_0h = reader.ReadUInt32();
|
||||
this.Unknown_4h = reader.ReadUInt32();
|
||||
this.SystemPagesCount = reader.ReadByte();
|
||||
this.GraphicsPagesCount = reader.ReadByte();
|
||||
this.Unknown_Ah = reader.ReadUInt16();
|
||||
this.Unknown_Ch = reader.ReadUInt32();
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_0h);
|
||||
writer.Write(this.Unknown_4h);
|
||||
writer.Write(this.SystemPagesCount);
|
||||
writer.Write(this.GraphicsPagesCount);
|
||||
writer.Write(this.Unknown_Ah);
|
||||
writer.Write(this.Unknown_Ch);
|
||||
writer.Write(this.Unknown_10h);
|
||||
|
||||
var pad = 256 * 16;
|
||||
writer.Write(new byte[pad]);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return SystemPagesCount.ToString() + ", " + GraphicsPagesCount.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
1438
C#/CodeWalker.Core/GameFiles/Resources/ShaderParams.cs
Normal file
1438
C#/CodeWalker.Core/GameFiles/Resources/ShaderParams.cs
Normal file
File diff suppressed because it is too large
Load Diff
742
C#/CodeWalker.Core/GameFiles/Resources/Texture.cs
Normal file
742
C#/CodeWalker.Core/GameFiles/Resources/Texture.cs
Normal file
@@ -0,0 +1,742 @@
|
||||
using CodeWalker.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
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
|
||||
public uint Unknown_18h { get; set; } = 1; // 0x00000001
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000
|
||||
public ResourceSimpleList64_uint TextureNameHashes { get; set; }
|
||||
public ResourcePointerList64<Texture> Textures { get; set; }
|
||||
|
||||
public Dictionary<uint, Texture> Dict { get; set; }
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public TextureDictionary()
|
||||
{ }
|
||||
|
||||
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();
|
||||
this.TextureNameHashes = reader.ReadBlock<ResourceSimpleList64_uint>();
|
||||
this.Textures = reader.ReadBlock<ResourcePointerList64<Texture>>();
|
||||
|
||||
BuildDict();
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.Unknown_18h);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
writer.WriteBlock(this.TextureNameHashes);
|
||||
writer.WriteBlock(this.Textures);
|
||||
}
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
BuildFromTextureList(textures);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
new Tuple<long, IResourceBlock>(0x20, TextureNameHashes),
|
||||
new Tuple<long, IResourceBlock>(0x30, Textures)
|
||||
};
|
||||
}
|
||||
|
||||
public Texture Lookup(uint hash)
|
||||
{
|
||||
Texture tex = null;
|
||||
if (Dict != null)
|
||||
{
|
||||
Dict.TryGetValue(hash, out tex);
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 80; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint VFT { get; set; }
|
||||
public uint Unknown_4h { get; set; } = 1; // 0x00000001
|
||||
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; }
|
||||
public ushort Unknown_30h { get; set; } = 1;
|
||||
public ushort Unknown_32h { get; set; }
|
||||
public uint Unknown_34h { get; set; } // 0x00000000
|
||||
public uint Unknown_38h { get; set; } // 0x00000000
|
||||
public uint Unknown_3Ch { get; set; } // 0x00000000
|
||||
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
|
||||
|
||||
// reference data
|
||||
public string Name { get; set; }
|
||||
public uint NameHash { get; set; }
|
||||
|
||||
private string_r NameBlock = null;
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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();
|
||||
this.Unknown_30h = reader.ReadUInt16();
|
||||
this.Unknown_32h = reader.ReadUInt16();
|
||||
this.Unknown_34h = reader.ReadUInt32();
|
||||
this.Unknown_38h = reader.ReadUInt32();
|
||||
this.Unknown_3Ch = reader.ReadUInt32();
|
||||
this.UsageData = reader.ReadUInt32();
|
||||
this.Unknown_44h = reader.ReadUInt32();
|
||||
this.ExtraFlags = reader.ReadUInt32();
|
||||
this.Unknown_4Ch = reader.ReadUInt32();
|
||||
|
||||
|
||||
|
||||
// read reference data
|
||||
this.Name = reader.ReadStringAt( //BlockAt<string_r>(
|
||||
this.NamePointer // offset
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(Name))
|
||||
{
|
||||
NameHash = JenkHash.GenHash(Name.ToLowerInvariant());
|
||||
}
|
||||
|
||||
//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
|
||||
//}
|
||||
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
this.NamePointer = (ulong)(this.NameBlock != null ? this.NameBlock.FilePosition : 0);
|
||||
|
||||
// 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);
|
||||
writer.Write(this.Unknown_32h);
|
||||
writer.Write(this.Unknown_34h);
|
||||
writer.Write(this.Unknown_38h);
|
||||
writer.Write(this.Unknown_3Ch);
|
||||
writer.Write(this.UsageData);
|
||||
writer.Write(this.Unknown_44h);
|
||||
writer.Write(this.ExtraFlags);
|
||||
writer.Write(this.Unknown_4Ch);
|
||||
}
|
||||
public virtual void WriteXml(StringBuilder sb, int indent, string ddsfolder)
|
||||
{
|
||||
YtdXml.StringTag(sb, indent, "Name", YtdXml.XmlEscape(Name));
|
||||
YtdXml.ValueTag(sb, indent, "Unk32", Unknown_32h.ToString());
|
||||
YtdXml.StringTag(sb, indent, "Usage", Usage.ToString());
|
||||
YtdXml.StringTag(sb, indent, "UsageFlags", UsageFlags.ToString());
|
||||
YtdXml.ValueTag(sb, indent, "ExtraFlags", ExtraFlags.ToString());
|
||||
}
|
||||
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");
|
||||
ExtraFlags = Xml.GetChildUIntAttribute(node, "ExtraFlags", "value");
|
||||
}
|
||||
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (!string.IsNullOrEmpty(Name))
|
||||
{
|
||||
NameBlock = (string_r)Name;
|
||||
list.Add(NameBlock);
|
||||
}
|
||||
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; }
|
||||
public ushort Depth { get; set; } = 1; //is depth > 1 supported?
|
||||
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
|
||||
|
||||
|
||||
// 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();
|
||||
this.Depth = reader.ReadUInt16();
|
||||
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
|
||||
this.Data = reader.ReadBlockAt<TextureData>(this.DataPointer, this.Format, this.Width, this.Height, this.Levels, this.Stride);
|
||||
|
||||
}
|
||||
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);
|
||||
writer.Write(this.Depth);
|
||||
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);
|
||||
}
|
||||
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());
|
||||
YtdXml.StringTag(sb, indent, "FileName", YtdXml.XmlEscape((Name ?? "null") + ".dds"));
|
||||
|
||||
try
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
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");
|
||||
|
||||
|
||||
if ((!string.IsNullOrEmpty(filename)) && (!string.IsNullOrEmpty(ddsfolder)))
|
||||
{
|
||||
var filepath = Path.Combine(ddsfolder, filename);
|
||||
if (File.Exists(filepath))
|
||||
{
|
||||
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
|
||||
{
|
||||
throw new Exception("Texture file format not supported:\n" + filepath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Texture file not found:\n" + filepath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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),
|
||||
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
|
||||
FLAG_FULL = (1 << 22),
|
||||
MAPS_HALF = (1 << 23),
|
||||
UNK24 = (1 << 24),//used by almost everything...
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
275
C#/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs
Normal file
275
C#/CodeWalker.Core/GameFiles/Resources/VehicleRecord.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
public class VehicleRecordList : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength => 0x20;
|
||||
|
||||
public ResourceSimpleList64<VehicleRecordEntry> Entries { get; set; }
|
||||
|
||||
public VehicleRecordList()
|
||||
{
|
||||
this.Entries = new ResourceSimpleList64<VehicleRecordEntry>();
|
||||
}
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.Entries = reader.ReadBlock<ResourceSimpleList64<VehicleRecordEntry>>();
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
writer.WriteBlock(this.Entries);
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
|
||||
if (Entries?.data_items != null)
|
||||
{
|
||||
foreach (var e in Entries.data_items)
|
||||
{
|
||||
YvrXml.OpenTag(sb, indent, "Item");
|
||||
e.WriteXml(sb, indent + 1);
|
||||
YvrXml.CloseTag(sb, indent, "Item");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
var entries = new List<VehicleRecordEntry>();
|
||||
|
||||
var inodes = node.SelectNodes("Item");
|
||||
if (inodes != null)
|
||||
{
|
||||
foreach (XmlNode inode in inodes)
|
||||
{
|
||||
var e = new VehicleRecordEntry();
|
||||
e.ReadXml(inode);
|
||||
entries.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
Entries = new ResourceSimpleList64<VehicleRecordEntry>();
|
||||
Entries.data_items = entries.ToArray();
|
||||
|
||||
}
|
||||
public static void WriteXmlNode(VehicleRecordList l, StringBuilder sb, int indent, string name = "VehicleRecordList")
|
||||
{
|
||||
if (l == null) return;
|
||||
if ((l.Entries?.data_items == null) || (l.Entries.data_items.Length == 0))
|
||||
{
|
||||
YvrXml.SelfClosingTag(sb, indent, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
YvrXml.OpenTag(sb, indent, name);
|
||||
l.WriteXml(sb, indent + 1);
|
||||
YvrXml.CloseTag(sb, indent, name);
|
||||
}
|
||||
}
|
||||
public static VehicleRecordList ReadXmlNode(XmlNode node)
|
||||
{
|
||||
if (node == null) return null;
|
||||
var l = new VehicleRecordList();
|
||||
l.ReadXml(node);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
new Tuple<long, IResourceBlock>(16, Entries)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class VehicleRecordEntry : ResourceSystemBlock
|
||||
{
|
||||
// this looks exactly like an rrr entry:
|
||||
// -> http://www.gtamodding.com/wiki/Carrec
|
||||
|
||||
public override long BlockLength => 0x20;
|
||||
|
||||
// structure data
|
||||
public uint Time;
|
||||
public short VelocityX; //factor to convert to m/s is 273.0583 .. or 1/0.0036622214, or 32767/120
|
||||
public short VelocityY;
|
||||
public short VelocityZ;
|
||||
public sbyte RightX;
|
||||
public sbyte RightY;
|
||||
public sbyte RightZ;
|
||||
public sbyte ForwardX;
|
||||
public sbyte ForwardY;
|
||||
public sbyte ForwardZ;
|
||||
public sbyte SteeringAngle; // factor to convert to game angle is 20 (ie radians)
|
||||
public sbyte GasPedalPower; //-100 to +100, negative = reverse
|
||||
public sbyte BrakePedalPower;//0 to 100
|
||||
public byte HandbrakeUsed;//0 or 1
|
||||
public Vector3 Position;
|
||||
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(VelocityX / 273.0583f, VelocityY / 273.0583f, VelocityZ / 273.0583f);
|
||||
}
|
||||
set
|
||||
{
|
||||
VelocityX = (short)Math.Round(value.X * 273.0583f);
|
||||
VelocityY = (short)Math.Round(value.Y * 273.0583f);
|
||||
VelocityZ = (short)Math.Round(value.Z * 273.0583f);
|
||||
}
|
||||
}
|
||||
public Vector3 Forward
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(ForwardX / 127.0f, ForwardY / 127.0f, ForwardZ / 127.0f);
|
||||
}
|
||||
set
|
||||
{
|
||||
ForwardX = (sbyte)Math.Round(value.X * 127.0f);
|
||||
ForwardY = (sbyte)Math.Round(value.Y * 127.0f);
|
||||
ForwardZ = (sbyte)Math.Round(value.Z * 127.0f);
|
||||
}
|
||||
}
|
||||
public Vector3 Right
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector3(RightX / 127.0f, RightY / 127.0f, RightZ / 127.0f);
|
||||
}
|
||||
set
|
||||
{
|
||||
RightX = (sbyte)Math.Round(value.X * 127.0f);
|
||||
RightY = (sbyte)Math.Round(value.Y * 127.0f);
|
||||
RightZ = (sbyte)Math.Round(value.Z * 127.0f);
|
||||
}
|
||||
}
|
||||
public float Steering
|
||||
{
|
||||
get
|
||||
{
|
||||
return SteeringAngle / 20.0f;
|
||||
}
|
||||
set
|
||||
{
|
||||
SteeringAngle = (sbyte)Math.Round(value * 20.0f);
|
||||
}
|
||||
}
|
||||
public float GasPedal
|
||||
{
|
||||
get
|
||||
{
|
||||
return GasPedalPower / 100.0f;
|
||||
}
|
||||
set
|
||||
{
|
||||
GasPedalPower = (sbyte)Math.Round(value * 100.0f);
|
||||
}
|
||||
}
|
||||
public float BrakePedal
|
||||
{
|
||||
get
|
||||
{
|
||||
return BrakePedalPower / 100.0f;
|
||||
}
|
||||
set
|
||||
{
|
||||
BrakePedalPower = (sbyte)Math.Round(value * 100.0f);
|
||||
}
|
||||
}
|
||||
public bool Handbrake
|
||||
{
|
||||
get
|
||||
{
|
||||
return HandbrakeUsed == 1;
|
||||
}
|
||||
set
|
||||
{
|
||||
HandbrakeUsed = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.Time = reader.ReadUInt32();
|
||||
this.VelocityX = reader.ReadInt16();
|
||||
this.VelocityY = reader.ReadInt16();
|
||||
this.VelocityZ = reader.ReadInt16();
|
||||
this.RightX = (sbyte)reader.ReadByte();
|
||||
this.RightY = (sbyte)reader.ReadByte();
|
||||
this.RightZ = (sbyte)reader.ReadByte();
|
||||
this.ForwardX = (sbyte)reader.ReadByte();
|
||||
this.ForwardY = (sbyte)reader.ReadByte();
|
||||
this.ForwardZ = (sbyte)reader.ReadByte();
|
||||
this.SteeringAngle = (sbyte)reader.ReadByte();
|
||||
this.GasPedalPower = (sbyte)reader.ReadByte();
|
||||
this.BrakePedalPower = (sbyte)reader.ReadByte();
|
||||
this.HandbrakeUsed = reader.ReadByte();
|
||||
this.Position = reader.ReadVector3();
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.Time);
|
||||
writer.Write(this.VelocityX);
|
||||
writer.Write(this.VelocityY);
|
||||
writer.Write(this.VelocityZ);
|
||||
writer.Write((byte)this.RightX);
|
||||
writer.Write((byte)this.RightY);
|
||||
writer.Write((byte)this.RightZ);
|
||||
writer.Write((byte)this.ForwardX);
|
||||
writer.Write((byte)this.ForwardY);
|
||||
writer.Write((byte)this.ForwardZ);
|
||||
writer.Write((byte)this.SteeringAngle);
|
||||
writer.Write((byte)this.GasPedalPower);
|
||||
writer.Write((byte)this.BrakePedalPower);
|
||||
writer.Write(this.HandbrakeUsed);
|
||||
writer.Write(this.Position);
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
YvrXml.ValueTag(sb, indent, "Time", Time.ToString());
|
||||
YvrXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position));
|
||||
YvrXml.SelfClosingTag(sb, indent, "Velocity " + FloatUtil.GetVector3XmlString(Velocity));
|
||||
YvrXml.SelfClosingTag(sb, indent, "Forward " + FloatUtil.GetVector3XmlString(Forward));
|
||||
YvrXml.SelfClosingTag(sb, indent, "Right " + FloatUtil.GetVector3XmlString(Right));
|
||||
YvrXml.ValueTag(sb, indent, "Steering", FloatUtil.ToString(Steering));
|
||||
YvrXml.ValueTag(sb, indent, "GasPedal", FloatUtil.ToString(GasPedal));
|
||||
YvrXml.ValueTag(sb, indent, "BrakePedal", FloatUtil.ToString(BrakePedal));
|
||||
YvrXml.ValueTag(sb, indent, "Handbrake", Handbrake.ToString());
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
Time = Xml.GetChildUIntAttribute(node, "Time", "value");
|
||||
Position = Xml.GetChildVector3Attributes(node, "Position");
|
||||
Velocity = Xml.GetChildVector3Attributes(node, "Velocity");
|
||||
Forward = Xml.GetChildVector3Attributes(node, "Forward");
|
||||
Right = Xml.GetChildVector3Attributes(node, "Right");
|
||||
Steering = Xml.GetChildFloatAttribute(node, "Steering", "value");
|
||||
GasPedal = Xml.GetChildFloatAttribute(node, "GasPedal", "value");
|
||||
BrakePedal = Xml.GetChildFloatAttribute(node, "BrakePedal", "value");
|
||||
Handbrake = Xml.GetChildBoolAttribute(node, "Handbrake", "value");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
195
C#/CodeWalker.Core/GameFiles/Resources/VertexType.cs
Normal file
195
C#/CodeWalker.Core/GameFiles/Resources/VertexType.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public enum VertexComponentType : byte
|
||||
{
|
||||
Nothing = 0,
|
||||
Half2 = 1,
|
||||
Float = 2,
|
||||
Half4 = 3,
|
||||
FloatUnk = 4,
|
||||
Float2 = 5,
|
||||
Float3 = 6,
|
||||
Float4 = 7,
|
||||
UByte4 = 8,
|
||||
Colour = 9,
|
||||
Dec3N = 10,
|
||||
Unk1 = 11,
|
||||
Unk2 = 12,
|
||||
Unk3 = 13,
|
||||
Unk4 = 14,
|
||||
Unk5 = 15,
|
||||
}
|
||||
|
||||
public static class VertexComponentTypes
|
||||
{
|
||||
public static int GetSizeInBytes(VertexComponentType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VertexComponentType.Nothing: return 0;
|
||||
case VertexComponentType.Half2: return 4;
|
||||
case VertexComponentType.Float: return 4;
|
||||
case VertexComponentType.Half4: return 8;
|
||||
case VertexComponentType.FloatUnk: return 0;
|
||||
case VertexComponentType.Float2: return 8;
|
||||
case VertexComponentType.Float3: return 12;
|
||||
case VertexComponentType.Float4: return 16;
|
||||
case VertexComponentType.UByte4: return 4;
|
||||
case VertexComponentType.Colour: return 4;
|
||||
case VertexComponentType.Dec3N: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetComponentCount(VertexComponentType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case VertexComponentType.Nothing: return 0;
|
||||
case VertexComponentType.Half2: return 2;
|
||||
case VertexComponentType.Float: return 1;
|
||||
case VertexComponentType.Half4: return 4;
|
||||
case VertexComponentType.FloatUnk: return 0;
|
||||
case VertexComponentType.Float2: return 2;
|
||||
case VertexComponentType.Float3: return 3;
|
||||
case VertexComponentType.Float4: return 4;
|
||||
case VertexComponentType.UByte4: return 4;
|
||||
case VertexComponentType.Colour: return 4;
|
||||
case VertexComponentType.Dec3N: return 3;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum VertexDeclarationTypes : ulong
|
||||
{
|
||||
GTAV1 = 0x7755555555996996, // GTAV - used by most drawables
|
||||
GTAV2 = 0x030000000199A006, // GTAV - used on cloth?
|
||||
GTAV3 = 0x0300000001996006, // GTAV - used on cloth?
|
||||
GTAV4 = 0x7655555555996996, // GTAV - used by FragGlassWindow
|
||||
|
||||
//Types4 = 0x0000000007097007, // Max Payne 3
|
||||
//Types5 = 0x0700000007097977, // Max Payne 3
|
||||
//Types6 = 0x0700000007997977, // Max Payne 3
|
||||
//Types7 = 0x0700007777097977, // Max Payne 3
|
||||
//Types8 = 0x0700007777997977, // Max Payne 3
|
||||
}
|
||||
|
||||
public enum VertexSemantics : int
|
||||
{
|
||||
Position = 0,
|
||||
BlendWeights = 1,
|
||||
BlendIndices = 2,
|
||||
Normal = 3,
|
||||
Colour0 = 4,
|
||||
Colour1 = 5,
|
||||
TexCoord0 = 6,
|
||||
TexCoord1 = 7,
|
||||
TexCoord2 = 8,
|
||||
TexCoord3 = 9,
|
||||
TexCoord4 = 10,
|
||||
TexCoord5 = 11,
|
||||
TexCoord6 = 12,
|
||||
TexCoord7 = 13,
|
||||
Tangent = 14,
|
||||
Binormal = 15,
|
||||
}
|
||||
|
||||
public enum VertexType : uint
|
||||
{
|
||||
Default = 89, //PNCT
|
||||
DefaultEx = 16473, //PNCTX
|
||||
PNCCT = 121,
|
||||
PNCCTTTT = 1017,
|
||||
PBBNCCTTX = 16639,
|
||||
PBBNCCT = 127,
|
||||
PNCTTTX = 16857,
|
||||
PNCTTX = 16601,
|
||||
PNCTTTX_2 = 19545,
|
||||
PNCTTTX_3 = 17113,
|
||||
PNCCTTX = 16633,
|
||||
PNCCTTX_2 = 17017,
|
||||
PNCCTTTX = 17145,
|
||||
PBBNCCTX = 16511,
|
||||
PBBNCTX = 16479,
|
||||
PBBNCT = 95,
|
||||
PNCCTT = 249,
|
||||
PNCCTX = 16505,
|
||||
PCT = 81,
|
||||
PT = 65,
|
||||
PTT = 193,
|
||||
PNC = 25,
|
||||
PC = 17,
|
||||
PCC = 7,
|
||||
PCCH2H4 = 2147500121, //0x80004059 (16473 + 0x80000000) DefaultEx Cloth?
|
||||
PNCH2 = 2147483737, //0x80000059 (89 + 0x80000000) Default Cloth?
|
||||
PNCTTTTX = 19673, //normal_spec_detail_dpm_vertdecal_tnt
|
||||
PNCTTTT = 985,
|
||||
PBBNCCTT = 255,
|
||||
PCTT = 209,
|
||||
PBBCCT = 119,
|
||||
PBBNC = 31,
|
||||
PBBNCTT = 223,
|
||||
PBBNCTTX = 16607,
|
||||
PBBNCTTT = 479,
|
||||
PNCTT = 217,
|
||||
PNCTTT = 473,
|
||||
PBBNCTTTX = 16863,
|
||||
}
|
||||
|
||||
public struct VertexTypeGTAV1 //0x7755555555996996
|
||||
{
|
||||
public Vector3 Position;
|
||||
public uint BlendWeights;
|
||||
public uint BlendIndices;
|
||||
public Vector3 Normal;
|
||||
public uint Colour0;
|
||||
public uint Colour1;
|
||||
public Vector2 Texcoord0;
|
||||
public Vector2 Texcoord1;
|
||||
public Vector2 Texcoord2;
|
||||
public Vector2 Texcoord3;
|
||||
public Vector2 Texcoord4;
|
||||
public Vector2 Texcoord5;
|
||||
public Vector2 Texcoord6;
|
||||
public Vector2 Texcoord7;
|
||||
public Vector4 Tangent;
|
||||
public Vector4 Binormal;
|
||||
}
|
||||
|
||||
public struct VertexTypeGTAV2 //0x030000000199A006
|
||||
{
|
||||
public Vector3 Position;
|
||||
public uint Normal; // Packed as Dec3N
|
||||
public uint Colour0;
|
||||
public uint Colour1;
|
||||
public Half2 Texcoord0;
|
||||
public Half4 Tangent;
|
||||
}
|
||||
|
||||
public struct VertexTypeGTAV3 //0x0300000001996006
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Vector3 Normal;
|
||||
public uint Colour0;
|
||||
public uint Colour1;
|
||||
public Half2 Texcoord0;
|
||||
public Half4 Tangent;
|
||||
}
|
||||
|
||||
public struct EditorVertex //vertex data to be used by the editor. TODO: maybe move somewhere else.
|
||||
{
|
||||
public Vector3 Position;
|
||||
public uint Colour;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
173
C#/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs
Normal file
173
C#/CodeWalker.Core/GameFiles/Resources/WaypointRecord.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
public class WaypointRecordList : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength => 0x30;
|
||||
|
||||
public uint Unknown_10h; // 0x00000000
|
||||
public uint Unknown_14h; // 0x00000000
|
||||
public ulong EntriesPointer;
|
||||
public uint EntriesCount;
|
||||
public uint Unknown_24h; // 0x00000000
|
||||
public uint Unknown_28h; // 0x00000000
|
||||
public uint Unknown_2Ch; // 0x00000000
|
||||
|
||||
public ResourceSimpleArray<WaypointRecordEntry> Entries;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
this.Unknown_14h = reader.ReadUInt32();
|
||||
this.EntriesPointer = reader.ReadUInt64();
|
||||
this.EntriesCount = reader.ReadUInt32();
|
||||
this.Unknown_24h = reader.ReadUInt32();
|
||||
this.Unknown_28h = reader.ReadUInt32();
|
||||
this.Unknown_2Ch = reader.ReadUInt32();
|
||||
|
||||
this.Entries = reader.ReadBlockAt<ResourceSimpleArray<WaypointRecordEntry>>(
|
||||
this.EntriesPointer, // offset
|
||||
this.EntriesCount
|
||||
);
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// update structure data
|
||||
this.EntriesPointer = (ulong)(this.Entries?.FilePosition ?? 0);
|
||||
this.EntriesCount = (uint)(this.Entries?.Count ?? 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.EntriesPointer);
|
||||
writer.Write(this.EntriesCount);
|
||||
writer.Write(this.Unknown_24h);
|
||||
writer.Write(this.Unknown_28h);
|
||||
writer.Write(this.Unknown_2Ch);
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
|
||||
if (Entries?.Data != null)
|
||||
{
|
||||
foreach (var e in Entries.Data)
|
||||
{
|
||||
YwrXml.OpenTag(sb, indent, "Item");
|
||||
e.WriteXml(sb, indent + 1);
|
||||
YwrXml.CloseTag(sb, indent, "Item");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
var entries = new List<WaypointRecordEntry>();
|
||||
|
||||
var inodes = node.SelectNodes("Item");
|
||||
if (inodes != null)
|
||||
{
|
||||
foreach (XmlNode inode in inodes)
|
||||
{
|
||||
var e = new WaypointRecordEntry();
|
||||
e.ReadXml(inode);
|
||||
entries.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
Entries = new ResourceSimpleArray<WaypointRecordEntry>();
|
||||
Entries.Data = entries;
|
||||
|
||||
}
|
||||
public static void WriteXmlNode(WaypointRecordList l, StringBuilder sb, int indent, string name = "WaypointRecordList")
|
||||
{
|
||||
if (l == null) return;
|
||||
if ((l.Entries?.Data == null) || (l.Entries.Data.Count == 0))
|
||||
{
|
||||
YwrXml.SelfClosingTag(sb, indent, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
YwrXml.OpenTag(sb, indent, name);
|
||||
l.WriteXml(sb, indent + 1);
|
||||
YwrXml.CloseTag(sb, indent, name);
|
||||
}
|
||||
}
|
||||
public static WaypointRecordList ReadXmlNode(XmlNode node)
|
||||
{
|
||||
if (node == null) return null;
|
||||
var l = new WaypointRecordList();
|
||||
l.ReadXml(node);
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
if (Entries != null) list.Add(Entries);
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class WaypointRecordEntry : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength => 20;
|
||||
|
||||
public Vector3 Position;
|
||||
public ushort Unk0;
|
||||
public ushort Unk1;
|
||||
public ushort Unk2;
|
||||
public ushort Unk3;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.Position = reader.ReadVector3();
|
||||
this.Unk0 = reader.ReadUInt16();
|
||||
this.Unk1 = reader.ReadUInt16();
|
||||
this.Unk2 = reader.ReadUInt16();
|
||||
this.Unk3 = reader.ReadUInt16();
|
||||
}
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.Position);
|
||||
writer.Write(this.Unk0);
|
||||
writer.Write(this.Unk1);
|
||||
writer.Write(this.Unk2);
|
||||
writer.Write(this.Unk3);
|
||||
}
|
||||
public void WriteXml(StringBuilder sb, int indent)
|
||||
{
|
||||
YwrXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position));
|
||||
YwrXml.ValueTag(sb, indent, "Unk0", Unk0.ToString());
|
||||
YwrXml.ValueTag(sb, indent, "Unk1", Unk1.ToString());
|
||||
YwrXml.ValueTag(sb, indent, "Unk2", Unk2.ToString());
|
||||
YwrXml.ValueTag(sb, indent, "Unk3", Unk3.ToString());
|
||||
}
|
||||
public void ReadXml(XmlNode node)
|
||||
{
|
||||
Position = Xml.GetChildVector3Attributes(node, "Position");
|
||||
Unk0 = (ushort)Xml.GetChildUIntAttribute(node, "Unk0", "value");
|
||||
Unk1 = (ushort)Xml.GetChildUIntAttribute(node, "Unk1", "value");
|
||||
Unk2 = (ushort)Xml.GetChildUIntAttribute(node, "Unk2", "value");
|
||||
Unk3 = (ushort)Xml.GetChildUIntAttribute(node, "Unk3", "value");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user