From db8fc350b2bffda63d27797bfe7ae4afb4af327e Mon Sep 17 00:00:00 2001 From: Treeki Date: Fri, 4 Feb 2011 15:40:12 +0100 Subject: Initial commit of NW4RTools. Supports BRRES reading for: Model Bytecode, Model Nodes, Model Vertex Data, and incomplete Materials. Writing is completely unimplemented so far. --- NW4RTools/AssemblyInfo.cs | 27 ++ NW4RTools/BrresReader.cs | 442 +++++++++++++++++++++++ NW4RTools/InputStream.cs | 148 ++++++++ NW4RTools/Logger.cs | 81 +++++ NW4RTools/Models/ByteCode.cs | 74 ++++ NW4RTools/Models/Material.cs | 54 +++ NW4RTools/Models/Model.cs | 37 ++ NW4RTools/Models/Node.cs | 26 ++ NW4RTools/Models/VertexData.cs | 46 +++ NW4RTools/NW4RTools.csproj | 56 +++ NW4RTools/NW4RTools.pidb | Bin 0 -> 71720 bytes NW4RTools/ResDict.cs | 8 + NW4RTools/ResFile.cs | 12 + NW4RTools/Types.cs | 137 ++++++++ NW4RTools/Util/IOrderedDictionary.cs | 59 ++++ NW4RTools/Util/OrderedDictionary.cs | 644 ++++++++++++++++++++++++++++++++++ NW4RTools/bin/Debug/NW4RTools.dll | Bin 0 -> 25600 bytes NW4RTools/bin/Debug/NW4RTools.dll.mdb | Bin 0 -> 8825 bytes 18 files changed, 1851 insertions(+) create mode 100644 NW4RTools/AssemblyInfo.cs create mode 100644 NW4RTools/BrresReader.cs create mode 100644 NW4RTools/InputStream.cs create mode 100644 NW4RTools/Logger.cs create mode 100644 NW4RTools/Models/ByteCode.cs create mode 100644 NW4RTools/Models/Material.cs create mode 100644 NW4RTools/Models/Model.cs create mode 100644 NW4RTools/Models/Node.cs create mode 100644 NW4RTools/Models/VertexData.cs create mode 100644 NW4RTools/NW4RTools.csproj create mode 100644 NW4RTools/NW4RTools.pidb create mode 100644 NW4RTools/ResDict.cs create mode 100644 NW4RTools/ResFile.cs create mode 100644 NW4RTools/Types.cs create mode 100644 NW4RTools/Util/IOrderedDictionary.cs create mode 100644 NW4RTools/Util/OrderedDictionary.cs create mode 100755 NW4RTools/bin/Debug/NW4RTools.dll create mode 100644 NW4RTools/bin/Debug/NW4RTools.dll.mdb (limited to 'NW4RTools') diff --git a/NW4RTools/AssemblyInfo.cs b/NW4RTools/AssemblyInfo.cs new file mode 100644 index 0000000..aa11f3f --- /dev/null +++ b/NW4RTools/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("NW4RTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs new file mode 100644 index 0000000..a157fe9 --- /dev/null +++ b/NW4RTools/BrresReader.cs @@ -0,0 +1,442 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using NW4RTools.Models; + +namespace NW4RTools { + public class BrresReader { + public static ResFile LoadFile(byte[] data) { + return new BrresReader().Load(new InputStream(data, ByteEndian.BigEndian)); + } + + + + private class RawStreamResDict : ResDict { + } + + + + private ResFile File; + private Logger Debug; + private SortedDictionary OffsetMap; + + private BrresReader() { + Debug = new Logger(); + OffsetMap = new SortedDictionary(); + } + + public ResFile Load(InputStream ins) { + File = new ResFile(); + + // Read BRRES header + byte[] magic = ins.ReadBytes(4); + UInt16 endian = ins.ReadUInt16(); + UInt16 version = ins.ReadUInt16(); + UInt32 fileSize = ins.ReadUInt32(); + UInt16 headerSize = ins.ReadUInt16(); + UInt16 blockCount = ins.ReadUInt16(); + + File.version = version; + + // Read first block header + byte[] blkMagic = ins.ReadBytes(4); + UInt32 blkSize = ins.ReadUInt32(); + + using (var c = Debug.Push("Root Dictionary")) { + ReadAndParseDict(ins, ParseRootEntry); + } + + + // DONE! + using (var c = Debug.Push("Offset Map")) { + foreach (var e in OffsetMap) { + Debug.Send("0x{0:X} : {1}", e.Key, e.Value); + } + } + + return File; + } + + private void ParseRootEntry(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + switch (name) { + case "3DModels(NW4R)": + File.Add(name, ReadAndConvertDict(ins, ConvertModelResource)); + break; + default: + Debug.Send("Not implemented"); + return; + } + } + } + + private Model ConvertModelResource(string name, InputStream ins) { + using (var c = Debug.Push(name)) { + Model mdl = new Model(); + + int startPos = ins.Position; + + OffsetMap.Add(startPos, "Model: " + name); + + byte[] magic = ins.ReadBytes(4); + UInt32 size = ins.ReadUInt32(); + UInt32 version = ins.ReadUInt32(); + + Debug.Send("Offset: 0x{0:X}; Size: 0x{1:X}; Version: {2}", startPos, size, version); + + Int32 resFileOffset = ins.ReadInt32(); + Int32 bytecodeOffset = ins.ReadInt32(); + Int32 nodeOffset = ins.ReadInt32(); + Int32 vtxPosOffset = ins.ReadInt32(); + Int32 vtxNrmOffset = ins.ReadInt32(); + Int32 vtxClrOffset = ins.ReadInt32(); + Int32 texCoordOffset = ins.ReadInt32(); + Int32 vtxFurVecOffset = ins.ReadInt32(); + Int32 vtxFurPosOffset = ins.ReadInt32(); + Int32 materialOffset = ins.ReadInt32(); + Int32 shaderOffset = ins.ReadInt32(); + Int32 shapeOffset = ins.ReadInt32(); + Int32 textureOffset = ins.ReadInt32(); + Int32 paletteOffset = ins.ReadInt32(); + Int32 unkOffset = ins.ReadInt32(); + Int32 nameOffset = ins.ReadInt32(); + + int infoStructPos = ins.Position; + OffsetMap.Add(infoStructPos, "Model Info Struct for: " + name); + UInt32 infoSize = ins.ReadUInt32(); + // should be 0x40 + + Int32 mdlOffset = ins.ReadInt32(); + mdl.ScaleMode = (Model.ScaleModeType)ins.ReadUInt32(); + mdl.TexMatrixMode = (Model.TexMatrixModeType)ins.ReadUInt32(); + UInt32 vtxCount = ins.ReadUInt32(); + UInt32 triCount = ins.ReadUInt32(); + UInt32 unk = ins.ReadUInt32(); + UInt32 nodeCount = ins.ReadUInt32(); + mdl.UsesNrmMtxArray = (bool)(ins.ReadByte() != 0); + mdl.UsesTexMtxArray = (bool)(ins.ReadByte() != 0); + ins.Skip(2); + Int32 mtxDataOffset = ins.ReadInt32(); + mdl.Minimum = ins.ReadVec3(); + mdl.Maximum = ins.ReadVec3(); + + + ins.Seek(infoStructPos + mtxDataOffset); + UInt32 mtxDataCount = ins.ReadUInt32(); + mdl.MatrixIDtoNodeID = new int[mtxDataCount]; + + for (int i = 0; i < mtxDataCount; i++) { + mdl.MatrixIDtoNodeID[i] = ins.ReadInt32(); + } + + + + // Load bytecode + using (var c2 = Debug.Push("Bytecode")) + mdl.Bytecode = ReadAndConvertDict(ins.At(startPos + bytecodeOffset), ConvertModelBytecode); + + // Load nodes and fix up pointers + StartNodeLoading(); + + using (var c2 = Debug.Push("Nodes")) + mdl.Nodes = ReadAndConvertDict(ins.At(startPos + nodeOffset), ConvertModelNode); + + FinishNodeLoading(); + + // Load vertex data + using (var c2 = Debug.Push("Vertex Position Data")) + mdl.VtxPosData = ReadAndConvertDict(ins.At(startPos + vtxPosOffset), ConvertVtxPosData); + using (var c2 = Debug.Push("Vertex Normal Data")) + mdl.VtxNrmData = ReadAndConvertDict(ins.At(startPos + vtxNrmOffset), ConvertVtxNrmData); + using (var c2 = Debug.Push("Vertex Colour Data")) + mdl.VtxClrData = ReadAndConvertDict(ins.At(startPos + vtxClrOffset), ConvertVtxClrData); + using (var c2 = Debug.Push("Vertex TexCoord Data")) + mdl.VtxTexCoordData = ReadAndConvertDict(ins.At(startPos + texCoordOffset), ConvertVtxTexCoordData); + + return mdl; + } + } + + private Models.ByteCode ConvertModelBytecode(string name, InputStream ins) { + var bc = new Models.ByteCode(); + + OffsetMap.Add(ins.Position, "ByteCode: " + name); + + while (true) { + ByteCode.OpType op = (ByteCode.OpType)ins.ReadByte(); + + switch (op) { + case ByteCode.OpType.Done: + bc.Instructions.Add(new ByteCode.DoneInstruction()); + return bc; + + case ByteCode.OpType.AssignNodeToParentMtx: + var insn2 = new ByteCode.AssignNodeToParentMtxInstruction(); + insn2.NodeID = ins.ReadUInt16(); + insn2.ParentMatrixID = ins.ReadUInt16(); + bc.Instructions.Add(insn2); + break; + + case ByteCode.OpType.BlendMatrices: + var insn3 = new ByteCode.BlendMatricesInstruction(); + insn3.MatrixID = ins.ReadUInt16(); + insn3.BlendedMatrices = new ByteCode.BlendMatricesInstruction.BlendedMatrix[ins.ReadByte()]; + for (int i = 0; i < insn3.BlendedMatrices.Length; i++) { + insn3.BlendedMatrices[i].MatrixID = ins.ReadUInt16(); + insn3.BlendedMatrices[i].Ratio = ins.ReadFloat(); + } + bc.Instructions.Add(insn3); + break; + + case ByteCode.OpType.DrawShape: + var insn4 = new ByteCode.DrawShapeInstruction(); + insn4.MaterialID = ins.ReadUInt16(); + insn4.ShapeID = ins.ReadUInt16(); + insn4.NodeID = ins.ReadUInt16(); + ins.Skip(1); + bc.Instructions.Add(insn4); + break; + + case ByteCode.OpType.AssignMtxToNode: + var insn5 = new ByteCode.AssignMtxToNodeInstruction(); + insn5.MatrixID = ins.ReadUInt16(); + insn5.NodeID = ins.ReadUInt16(); + bc.Instructions.Add(insn5); + break; + + default: + bc.Instructions.Add(new ByteCode.Instruction()); + break; + } + } + } + + + + + private class NodeLoadInfo { + public Models.Node Node; + public string Name; + public int Position; + public Int32 ParentOffset; + public Int32 ChildOffset; + public Int32 NextOffset; + public Int32 PrevOffset; + } + + private Dictionary NodeLoadData; + + private void StartNodeLoading() { + NodeLoadData = new Dictionary(); + } + + private Models.Node ConvertModelNode(string name, InputStream ins) { + var n = new Models.Node(); + + OffsetMap.Add(ins.Position, "Node: " + name); + + int startPos = ins.Position; + + UInt32 size = ins.ReadUInt32(); + Int32 mdlOffset = ins.ReadInt32(); + Int32 nameOffset = ins.ReadInt32(); + + n.Index = ins.ReadUInt32(); + n.MatrixID = ins.ReadUInt32(); + n.Flags = ins.ReadUInt32(); + n.BillboardMode = (Node.BillboardType)ins.ReadUInt32(); + UInt32 unk = ins.ReadUInt32(); + // might be swapped with bbmode, check asm to confirm + + n.Scale = ins.ReadVec3(); + n.Rotation = ins.ReadVec3(); + n.Translation = ins.ReadVec3(); + n.BoxMin = ins.ReadVec3(); + n.BoxMax = ins.ReadVec3(); + + NodeLoadInfo loadInfo = new NodeLoadInfo(); + loadInfo.ParentOffset = ins.ReadInt32(); + loadInfo.ChildOffset = ins.ReadInt32(); + loadInfo.NextOffset = ins.ReadInt32(); + loadInfo.PrevOffset = ins.ReadInt32(); + loadInfo.Position = startPos; + loadInfo.Node = n; + loadInfo.Name = ins.At(startPos + nameOffset - 4).ReadName(); + NodeLoadData[startPos] = loadInfo; + + n.NodeMatrix = ins.ReadMatrix(); + n.NodeInvMatrix = ins.ReadMatrix(); + + return n; + } + + private void FinishNodeLoading() { + foreach (var dictEntry in NodeLoadData) { + var entry = dictEntry.Value; + var node = entry.Node; + + if (entry.ParentOffset == 0) { + node.Parent = null; + } else { + NodeLoadInfo n = NodeLoadData[entry.Position + entry.ParentOffset]; + node.Parent = n.Node; + Debug.Send("Set {0}'s parent to {1}", entry.Name, n.Name); + } + + if (entry.ChildOffset == 0) + node.FirstChild = null; + else + node.FirstChild = NodeLoadData[entry.Position + entry.ChildOffset].Node; + + if (entry.NextOffset == 0) + node.Next = null; + else + node.Next = NodeLoadData[entry.Position + entry.NextOffset].Node; + + if (entry.PrevOffset == 0) + node.Previous = null; + else + node.Previous = NodeLoadData[entry.Position + entry.PrevOffset].Node; + } + + NodeLoadData = null; + } + + + + + private void LoadVertexDataBase(InputStream ins, Models.VertexDataBase n) { + UInt32 size = ins.ReadUInt32(); + + Int32 mdlOffset = ins.ReadInt32(); + Int32 dataOffset = ins.ReadInt32(); + Int32 nameOffset = ins.ReadInt32(); + + n.Index = ins.ReadUInt32(); + n.ComponentCount = ins.ReadUInt32(); + n.ComponentType = ins.ReadUInt32(); + n.Fraction = ins.ReadByte(); + n.EntrySize = ins.ReadByte(); + n.EntryCount = ins.ReadUInt16(); + + n.Data = ins.ReadBytes(n.EntrySize * n.EntryCount); + } + + private Models.VertexPosData ConvertVtxPosData(string name, InputStream ins) { + var n = new Models.VertexPosData(); + + OffsetMap.Add(ins.Position, "VertexPosData: " + name); + + LoadVertexDataBase(ins, n); + + n.Minimum = ins.ReadVec3(); + n.Maximum = ins.ReadVec3(); + + return n; + } + + private Models.VertexNrmData ConvertVtxNrmData(string name, InputStream ins) { + var n = new Models.VertexNrmData(); + + OffsetMap.Add(ins.Position, "VertexNrmData: " + name); + + LoadVertexDataBase(ins, n); + + return n; + } + + private Models.VertexClrData ConvertVtxClrData(string name, InputStream ins) { + var n = new Models.VertexClrData(); + + OffsetMap.Add(ins.Position, "VertexClrData: " + name); + + LoadVertexDataBase(ins, n); + + return n; + } + + private Models.VertexTexCoordData ConvertVtxTexCoordData(string name, InputStream ins) { + var n = new Models.VertexTexCoordData(); + + OffsetMap.Add(ins.Position, "VertexTexCoordData: " + name); + + LoadVertexDataBase(ins, n); + + n.Minimum = ins.ReadVec2(); + n.Maximum = ins.ReadVec2(); + + return n; + } + + + + + private RawStreamResDict ReadDict(InputStream ins) { + RawStreamResDict output = new RawStreamResDict(); + int dictPos = ins.Position; + + UInt32 dataSize = ins.ReadUInt32(); + UInt32 entryCount = ins.ReadUInt32(); + + for (int i = 0; i <= entryCount; i++) { + UInt16 eRef = ins.ReadUInt16(); + UInt16 flag = ins.ReadUInt16(); + UInt16 iLeft = ins.ReadUInt16(); + UInt16 iRight = ins.ReadUInt16(); + Int32 nameOffset = ins.ReadInt32(); + Int32 dataOffset = ins.ReadInt32(); + + //Debug.Send("Position: {6:X}, eRef: {0:X}, flag: {1:X}, iLeft: {2:X}, iRight: {3:X}, nameOffset: {4:X}, dataOffset: {5:X}", eRef, flag, iLeft, iRight, nameOffset, dataOffset, ins.Position); + + if (nameOffset != 0 && dataOffset != 0) { + string name = ins.At(dictPos + nameOffset - 4).ReadName(); + Debug.Send("Entry: {0} at offset 0x{1:X}", name, dictPos + dataOffset); + + InputStream entryStream = ins.At(dictPos + dataOffset); + output.Add(name, entryStream); + } + } + + return output; + } + + + + private delegate void ParseResourceDelegate(string name, InputStream ins); + private delegate TValue ConvertResourceDelegate(string name, InputStream ins); + + private void ReadAndParseDict(InputStream ins, ParseResourceDelegate func) { + ReadAndParseDict(ins, func, "Unnamed"); + } + + private void ReadAndParseDict(InputStream ins, ParseResourceDelegate func, string dictName) { + int dictPos = ins.Position; + RawStreamResDict theDict = ReadDict(ins); + OffsetMap.Add(dictPos, string.Format("ResDict: {0} [Data ends at 0x{1:X}]", dictName, ins.Position)); + + foreach (var entry in theDict) { + func(entry.Key, entry.Value); + } + } + + private ResDict ReadAndConvertDict(InputStream ins, ConvertResourceDelegate func) { + int dictPos = ins.Position; + RawStreamResDict theDict = ReadDict(ins); + var outDict = new ResDict(); + + // Hack to get the type name + string valueTypeName = outDict.GetType().GetGenericArguments()[0].ToString(); + + OffsetMap.Add(dictPos, string.Format("ResDict: {0} [Data ends at 0x{1:X}]", valueTypeName, ins.Position)); + + foreach (var entry in theDict) { + TValue returnedObject = func(entry.Key, entry.Value); + outDict.Add(entry.Key, returnedObject); + } + + return outDict; + } + } +} + diff --git a/NW4RTools/InputStream.cs b/NW4RTools/InputStream.cs new file mode 100644 index 0000000..3a776b4 --- /dev/null +++ b/NW4RTools/InputStream.cs @@ -0,0 +1,148 @@ +using System; +using System.IO; + +namespace NW4RTools { + public class InputStream { + public readonly byte[] Data; + public readonly ByteEndian Endian; + private readonly bool MustReverseArrays; + + public int Position { + get; private set; + } + + public InputStream(byte[] data) { + Data = data; + Endian = ByteEndian.BigEndian; + Position = 0; + MustReverseArrays = BitConverter.IsLittleEndian; + } + + public InputStream(byte[] data, ByteEndian endian) { + Data = data; + Endian = endian; + Position = 0; + + if (Endian == ByteEndian.BigEndian) + MustReverseArrays = BitConverter.IsLittleEndian; + else + MustReverseArrays = !BitConverter.IsLittleEndian; + } + + public void Seek(int pos) { + if (pos < 0 || pos >= Data.Length) + throw new ArgumentOutOfRangeException(); + + Position = pos; + } + + public void Skip(int count) { + Seek(Position + count); + } + + public byte[] ReadBytes(int count) { + byte[] ret = new byte[count]; + Array.Copy(Data, Position, ret, 0, count); + Skip(count); + return ret; + } + + private byte[] ReadReversedBytes(int count) { + byte[] buf = ReadBytes(count); + if (MustReverseArrays) + Array.Reverse(buf); + return buf; + } + + public byte ReadByte() { + byte ret = Data[Position]; + Skip(1); + return ret; + } + + public Int16 ReadInt16() { + return BitConverter.ToInt16(ReadReversedBytes(2), 0); + } + + public UInt16 ReadUInt16() { + return BitConverter.ToUInt16(ReadReversedBytes(2), 0); + } + + public Int32 ReadInt32() { + return BitConverter.ToInt32(ReadReversedBytes(4), 0); + } + + public UInt32 ReadUInt32() { + return BitConverter.ToUInt32(ReadReversedBytes(4), 0); + } + + public float ReadFloat() { + return BitConverter.ToSingle(ReadReversedBytes(4), 0); + } + + public double ReadDouble() { + return BitConverter.ToDouble(ReadReversedBytes(8), 0); + } + + public Color ReadColor() { + var ret = new Color(); + ret.r = Data[Position]; + ret.g = Data[Position + 1]; + ret.b = Data[Position + 2]; + ret.a = Data[Position + 3]; + Skip(4); + return ret; + } + + public Vec2 ReadVec2() { + float x = ReadFloat(); + float y = ReadFloat(); + return new Vec2 { x = x, y = y }; + } + + public Vec3 ReadVec3() { + float x = ReadFloat(); + float y = ReadFloat(); + float z = ReadFloat(); + return new Vec3 { x = x, y = y, z = z }; + } + + public Matrix ReadMatrix() { + var ret = new Matrix(); + ret.v00 = ReadFloat(); + ret.v01 = ReadFloat(); + ret.v02 = ReadFloat(); + ret.v03 = ReadFloat(); + ret.v10 = ReadFloat(); + ret.v11 = ReadFloat(); + ret.v12 = ReadFloat(); + ret.v13 = ReadFloat(); + ret.v20 = ReadFloat(); + ret.v21 = ReadFloat(); + ret.v22 = ReadFloat(); + ret.v23 = ReadFloat(); + return ret; + } + + public string ReadName() { + int length = ReadInt32(); + string name = System.Text.Encoding.GetEncoding("Shift_JIS").GetString(ReadBytes(length)); + if ((length & 3) != 0) { + Skip(4 - (length & 3)); + } + return name; + } + + + public InputStream At(int pos) { + var ret = new InputStream(Data, Endian); + ret.Seek(pos); + return ret; + } + + public InputStream Copy() { + return At(Position); + } + } +} + diff --git a/NW4RTools/Logger.cs b/NW4RTools/Logger.cs new file mode 100644 index 0000000..d5a76f3 --- /dev/null +++ b/NW4RTools/Logger.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace NW4RTools { + public class LogContext : IDisposable { + // An awful, awful hack + private readonly Logger Owner; + + public LogContext(Logger owner) { + Owner = owner; + } + + void IDisposable.Dispose() { + Owner.Pop(); + } + } + + public class Logger { + private List PrefixElements; + private List Names; + private int BlockCount; + private string Prefix; + + public Logger() { + Names = new List(); + Names.Add("root"); + + PrefixElements = new List(); + PrefixElements.Add("|"); + + RebuildPrefix(); + } + + private void RebuildPrefix() { + Prefix = string.Join(" ", PrefixElements.ToArray()) + "- "; + } + + public void Send(string format, params object[] args) { + if (BlockCount > 0) + return; + + Console.Write(Prefix); + Console.WriteLine(format, args); + } + + public LogContext Push(string format, params object[] args) { + string n = string.Format(format, args); + Console.Write(Prefix); + Console.Write("=== "); + Console.WriteLine(n); + + Names.Add(n); + + PrefixElements.Add("-"); + RebuildPrefix(); + + return new LogContext(this); + } + + public void Pop() { + PrefixElements.RemoveAt(PrefixElements.Count - 1); + RebuildPrefix(); + + string n = Names[Names.Count - 1]; + Names.RemoveAt(Names.Count - 1); + Console.Write(Prefix); + Console.Write("=/= end "); + Console.WriteLine(n); + } + + public void Block() { + BlockCount += 1; + } + + public void Unblock() { + BlockCount -= 1; + } + } +} + diff --git a/NW4RTools/Models/ByteCode.cs b/NW4RTools/Models/ByteCode.cs new file mode 100644 index 0000000..438c009 --- /dev/null +++ b/NW4RTools/Models/ByteCode.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; + +namespace NW4RTools.Models { + public class ByteCode { + public enum OpType { + None, Done, AssignNodeToParentMtx, BlendMatrices, DrawShape, AssignMtxToNode + } + + public class Instruction { + public virtual OpType GetOp() { + return OpType.None; + } + } + + public class DoneInstruction : Instruction { + public override OpType GetOp() { + return OpType.Done; + } + } + + public class AssignNodeToParentMtxInstruction : Instruction { + public override OpType GetOp() { + return OpType.AssignNodeToParentMtx; + } + + public UInt16 NodeID; + public UInt16 ParentMatrixID; + } + + public class BlendMatricesInstruction : Instruction { + public override OpType GetOp() { + return OpType.BlendMatrices; + } + + public UInt16 MatrixID; + public BlendedMatrix[] BlendedMatrices; + + public struct BlendedMatrix { + public UInt16 MatrixID; + public float Ratio; + } + } + + public class DrawShapeInstruction : Instruction { + public override OpType GetOp() { + return OpType.DrawShape; + } + + public UInt16 MaterialID; + public UInt16 ShapeID; + public UInt16 NodeID; + } + + public class AssignMtxToNodeInstruction : Instruction { + public override OpType GetOp() { + return OpType.AssignMtxToNode; + } + + public UInt16 MatrixID; + public UInt16 NodeID; + } + + + + + public List Instructions; + + public ByteCode() { + Instructions = new List(); + } + } +} + diff --git a/NW4RTools/Models/Material.cs b/NW4RTools/Models/Material.cs new file mode 100644 index 0000000..bb1b85a --- /dev/null +++ b/NW4RTools/Models/Material.cs @@ -0,0 +1,54 @@ +using System; + +namespace NW4RTools.Models { + public class ChanCtrl { + public UInt32 Flags; + public Color MatColor, AmbColor; + public UInt32 FlagC, FlagA; + } + + public class SRTSettingInfo { + public float ScaleX, ScaleY, Rotate, TranslateX, TranslateY; + + public byte CameraID; + public byte LightID; + public byte MapType; + public byte Flags; + public Matrix TexMatrix; + } + + public class BoundTextureInfo { + public string TextureName, PaletteName; + public UInt32 TexMapID, TlutID, WrapS, WrapT, MinFilt, MagFilt; + public float LODBias; + public UInt32 MaxAniso; + } + + + + public class Material { + public UInt32 Index, Flags; + + // ResGenMode + public byte TexCoordGenCount, ChanCount, TevStageCount, IndStageCount; + public UInt32 CullMode; + + // ResMatMisc + public byte ZCompLoc, LightSetID, FogID; + public byte[] IndirectTexMtxCalcMethod1; + public byte[] IndirectTexMtxCalcMethod2; + + // ResTexObj and ResTlutObj + public byte[][] TexObj; + public byte[] TlutObj; + + // ResTexSrt + public SRTSettingInfo[] SRTSettings; + + // UNFINISHED + + public Material() { + } + } +} + diff --git a/NW4RTools/Models/Model.cs b/NW4RTools/Models/Model.cs new file mode 100644 index 0000000..ea95f4f --- /dev/null +++ b/NW4RTools/Models/Model.cs @@ -0,0 +1,37 @@ +using System; +namespace NW4RTools.Models { + public class Model { + public enum ScaleModeType { + Standard, SoftImage, Maya + } + + public enum TexMatrixModeType { + Maya, SoftImage, Max + } + + public ResDict Bytecode; + public ResDict Nodes; + public ResDict VtxPosData; + public ResDict VtxNrmData; + public ResDict VtxClrData; + public ResDict VtxTexCoordData; + + /*public ResDict Bytecode, Nodes, VtxPosData, VtsNrmData, VtxClrData, VtxTexCoordData; + public ResDict VtxFurVecData, VtxFurPosData, Materials, Shaders, Shapes; + public ResDict Textures, Palettes;*/ + + public ScaleModeType ScaleMode; + public TexMatrixModeType TexMatrixMode; + + public bool UsesNrmMtxArray, UsesTexMtxArray; + + public Int32[] MatrixIDtoNodeID; + + public Vec3 Minimum, Maximum; + + + public Model() { + } + } +} + diff --git a/NW4RTools/Models/Node.cs b/NW4RTools/Models/Node.cs new file mode 100644 index 0000000..4031ae9 --- /dev/null +++ b/NW4RTools/Models/Node.cs @@ -0,0 +1,26 @@ +using System; + +namespace NW4RTools.Models { + public class Node { + public enum BillboardType { + None, Type1, Type2, Type3, Type4, Type5, Type6 + } + + public UInt32 Index; + public UInt32 MatrixID; + public UInt32 Flags; + public BillboardType BillboardMode; + + public Vec3 Scale, Rotation, Translation; + public Vec3 BoxMin, BoxMax; + + public Node Parent, FirstChild, Next, Previous; + public byte[] UserData; + + public Matrix NodeMatrix, NodeInvMatrix; + + public Node() { + } + } +} + diff --git a/NW4RTools/Models/VertexData.cs b/NW4RTools/Models/VertexData.cs new file mode 100644 index 0000000..d8733b3 --- /dev/null +++ b/NW4RTools/Models/VertexData.cs @@ -0,0 +1,46 @@ +using System; + +namespace NW4RTools.Models { + public class VertexDataBase { + public UInt32 Index; + + public UInt32 ComponentCount, ComponentType; // todo: enums + public byte Fraction /* Not used for colours */, EntrySize; + public UInt16 EntryCount; + + // Todo, decode data when reading + public byte[] Data; + + public VertexDataBase() { + } + } + + + public class VertexPosData : VertexDataBase { + public Vec3 Minimum, Maximum; + + public VertexPosData() { + } + } + + + public class VertexNrmData : VertexDataBase { + public VertexNrmData() { + } + } + + + public class VertexClrData : VertexDataBase { + public VertexClrData() { + } + } + + + public class VertexTexCoordData : VertexDataBase { + public Vec2 Minimum, Maximum; + + public VertexTexCoordData() { + } + } +} + diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj new file mode 100644 index 0000000..f8f89e8 --- /dev/null +++ b/NW4RTools/NW4RTools.csproj @@ -0,0 +1,56 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {A9C9FABD-0A5F-4DAB-979D-9F288F96866F} + Library + NW4RTools + NW4RTools + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + false + + + none + false + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NW4RTools/NW4RTools.pidb b/NW4RTools/NW4RTools.pidb new file mode 100644 index 0000000..726779f Binary files /dev/null and b/NW4RTools/NW4RTools.pidb differ diff --git a/NW4RTools/ResDict.cs b/NW4RTools/ResDict.cs new file mode 100644 index 0000000..30e25e3 --- /dev/null +++ b/NW4RTools/ResDict.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections; + +namespace NW4RTools { + public class ResDict : Util.OrderedDictionary { + } +} + diff --git a/NW4RTools/ResFile.cs b/NW4RTools/ResFile.cs new file mode 100644 index 0000000..865db93 --- /dev/null +++ b/NW4RTools/ResFile.cs @@ -0,0 +1,12 @@ +using System; + +namespace NW4RTools { + public class ResFile : ResDict { + public UInt16 version; + + public ResDict GetGroup(string name) { + return this[name] as ResDict; + } + } +} + diff --git a/NW4RTools/Types.cs b/NW4RTools/Types.cs new file mode 100644 index 0000000..7887f1c --- /dev/null +++ b/NW4RTools/Types.cs @@ -0,0 +1,137 @@ +using System; + +namespace NW4RTools { + public enum ByteEndian { + LittleEndian, + BigEndian + } + + + public struct Color { + public byte r, g, b, a; + } + + public struct Vec3 { + public float x, y, z; + } + + public struct Vec2 { + public float x, y; + } + + public struct Matrix { + public float v00, v01, v02, v03, v10, v11, v12, v13, v20, v21, v22, v23; + + public float this[int x, int y] { + get { + switch (y) { + case 0: + switch (x) { + case 0: + return v00; + break; + case 1: + return v01; + break; + case 2: + return v02; + break; + case 3: + return v03; + break; + } + break; + case 1: + switch (x) { + case 0: + return v10; + break; + case 1: + return v11; + break; + case 2: + return v12; + break; + case 3: + return v13; + break; + } + break; + case 2: + switch (x) { + case 0: + return v20; + break; + case 1: + return v21; + break; + case 2: + return v22; + break; + case 3: + return v23; + break; + } + break; + } + + throw new IndexOutOfRangeException(); + } + + set { + switch (y) { + case 0: + switch (x) { + case 0: + v00 = value; + break; + case 1: + v01 = value; + break; + case 2: + v02 = value; + break; + case 3: + v03 = value; + break; + } + break; + case 1: + switch (x) { + case 0: + v10 = value; + break; + case 1: + v11 = value; + break; + case 2: + v12 = value; + break; + case 3: + v13 = value; + break; + } + break; + case 2: + switch (x) { + case 0: + v20 = value; + break; + case 1: + v21 = value; + break; + case 2: + v22 = value; + break; + case 3: + v23 = value; + break; + } + break; + } + + throw new IndexOutOfRangeException(); + } + } + } +} diff --git a/NW4RTools/Util/IOrderedDictionary.cs b/NW4RTools/Util/IOrderedDictionary.cs new file mode 100644 index 0000000..a574c7c --- /dev/null +++ b/NW4RTools/Util/IOrderedDictionary.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +// From http://www.codeproject.com/KB/recipes/GenericOrderedDictionary.aspx + +namespace NW4RTools.Util +{ + /// + /// Represents a generic collection of key/value pairs that are ordered independently of the key and value. + /// + /// The type of the keys in the dictionary + /// The type of the values in the dictionary + public interface IOrderedDictionary : IOrderedDictionary, IDictionary + { + /// + /// Adds an entry with the specified key and value into the IOrderedDictionary<TKey,TValue> collection with the lowest available index. + /// + /// The key of the entry to add. + /// The value of the entry to add. + /// The index of the newly added entry + /// + /// You can also use the property to add new elements by setting the value of a key that does not exist in the IOrderedDictionary<TKey,TValue> collection; however, if the specified key already exists in the IOrderedDictionary<TKey,TValue>, setting the property overwrites the old value. In contrast, the method does not modify existing elements. + /// An element with the same key already exists in the IOrderedDictionary<TKey,TValue> + /// The IOrderedDictionary<TKey,TValue> is read-only.
+ /// -or-
+ /// The IOrderedDictionary<TKey,TValue> has a fized size.
+ new int Add(TKey key, TValue value); + + /// + /// Inserts a new entry into the IOrderedDictionary<TKey,TValue> collection with the specified key and value at the specified index. + /// + /// The zero-based index at which the element should be inserted. + /// The key of the entry to add. + /// The value of the entry to add. The value can be if the type of the values in the dictionary is a reference type. + /// is less than 0.
+ /// -or-
+ /// is greater than .
+ /// An element with the same key already exists in the IOrderedDictionary<TKey,TValue>. + /// The IOrderedDictionary<TKey,TValue> is read-only.
+ /// -or-
+ /// The IOrderedDictionary<TKey,TValue> has a fized size.
+ void Insert(int index, TKey key, TValue value); + + /// + /// Gets or sets the value at the specified index. + /// + /// The zero-based index of the value to get or set. + /// The value of the item at the specified index. + /// is less than 0.
+ /// -or-
+ /// is equal to or greater than .
+ new TValue this[int index] + { + get; + set; + } + } +} diff --git a/NW4RTools/Util/OrderedDictionary.cs b/NW4RTools/Util/OrderedDictionary.cs new file mode 100644 index 0000000..3dd4fb4 --- /dev/null +++ b/NW4RTools/Util/OrderedDictionary.cs @@ -0,0 +1,644 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; + +// From http://www.codeproject.com/KB/recipes/GenericOrderedDictionary.aspx + +namespace NW4RTools.Util +{ + /// + /// Represents a generic collection of key/value pairs that are ordered independently of the key and value. + /// + /// The type of the keys in the dictionary + /// The type of the values in the dictionary + public class OrderedDictionary : IOrderedDictionary + { + private const int DefaultInitialCapacity = 0; + + private static readonly string _keyTypeName = typeof(TKey).FullName; + private static readonly string _valueTypeName = typeof(TValue).FullName; + private static readonly bool _valueTypeIsReferenceType = !typeof(ValueType).IsAssignableFrom(typeof(TValue)); + + private Dictionary _dictionary; + private List> _list; + private IEqualityComparer _comparer; + private object _syncRoot; + private int _initialCapacity; + + /// + /// Initializes a new instance of the OrderedDictionary<TKey,TValue> class. + /// + public OrderedDictionary() + : this(DefaultInitialCapacity, null) + { + } + + /// + /// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified initial capacity. + /// + /// The initial number of elements that the OrderedDictionary<TKey,TValue> can contain. + /// is less than 0 + public OrderedDictionary(int capacity) + : this(capacity, null) + { + } + + /// + /// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified comparer. + /// + /// The IEqualityComparer<TKey> to use when comparing keys, or to use the default EqualityComparer<TKey> for the type of the key. + public OrderedDictionary(IEqualityComparer comparer) + : this(DefaultInitialCapacity, comparer) + { + } + + /// + /// Initializes a new instance of the OrderedDictionary<TKey,TValue> class using the specified initial capacity and comparer. + /// + /// The initial number of elements that the OrderedDictionary<TKey,TValue> collection can contain. + /// The IEqualityComparer<TKey> to use when comparing keys, or to use the default EqualityComparer<TKey> for the type of the key. + /// is less than 0 + public OrderedDictionary(int capacity, IEqualityComparer comparer) + { + if(0 > capacity) + throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative"); + + _initialCapacity = capacity; + _comparer = comparer; + } + + /// + /// Converts the object passed as a key to the key type of the dictionary + /// + /// The key object to check + /// The key object, cast as the key type of the dictionary + /// is . + /// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of . + private static TKey ConvertToKeyType(object keyObject) + { + if(null == keyObject) + { + throw new ArgumentNullException("key"); + } + else + { + if(keyObject is TKey) + return (TKey)keyObject; + } + throw new ArgumentException("'key' must be of type " + _keyTypeName, "key"); + } + + /// + /// Converts the object passed as a value to the value type of the dictionary + /// + /// The object to convert to the value type of the dictionary + /// The value object, converted to the value type of the dictionary + /// is , and the value type of the OrderedDictionary<TKey,TValue> is a value type. + /// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of . + private static TValue ConvertToValueType(object value) + { + if(null == value) + { + if(_valueTypeIsReferenceType) + return default(TValue); + else + throw new ArgumentNullException("value"); + } + else + { + if(value is TValue) + return (TValue)value; + } + throw new ArgumentException("'value' must be of type " + _valueTypeName, "value"); + } + + /// + /// Gets the dictionary object that stores the keys and values + /// + /// The dictionary object that stores the keys and values for the OrderedDictionary<TKey,TValue> + /// Accessing this property will create the dictionary object if necessary + private Dictionary Dictionary + { + get + { + if(null == _dictionary) + { + _dictionary = new Dictionary(_initialCapacity, _comparer); + } + return _dictionary; + } + } + + /// + /// Gets the list object that stores the key/value pairs. + /// + /// The list object that stores the key/value pairs for the OrderedDictionary<TKey,TValue> + /// Accessing this property will create the list object if necessary. + private List> List + { + get + { + if(null == _list) + { + _list = new List>(_initialCapacity); + } + return _list; + } + } + + IDictionaryEnumerator IOrderedDictionary.GetEnumerator() + { + return Dictionary.GetEnumerator(); + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + return Dictionary.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return List.GetEnumerator(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return List.GetEnumerator(); + } + + /// + /// Inserts a new entry into the OrderedDictionary<TKey,TValue> collection with the specified key and value at the specified index. + /// + /// The zero-based index at which the element should be inserted. + /// The key of the entry to add. + /// The value of the entry to add. The value can be if the type of the values in the dictionary is a reference type. + /// is less than 0.
+ /// -or-
+ /// is greater than .
+ /// is . + /// An element with the same key already exists in the OrderedDictionary<TKey,TValue>. + public void Insert(int index, TKey key, TValue value) + { + if(index > Count || index < 0) + throw new ArgumentOutOfRangeException("index"); + + Dictionary.Add(key, value); + List.Insert(index, new KeyValuePair(key, value)); + } + + /// + /// Inserts a new entry into the OrderedDictionary<TKey,TValue> collection with the specified key and value at the specified index. + /// + /// The zero-based index at which the element should be inserted. + /// The key of the entry to add. + /// The value of the entry to add. The value can be if the type of the values in the dictionary is a reference type. + /// is less than 0.
+ /// -or-
+ /// is greater than .
+ /// is .
+ /// -or-
+ /// is , and the value type of the OrderedDictionary<TKey,TValue> is a value type.
+ /// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
+ /// -or-
+ /// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
+ /// -or-
+ /// An element with the same key already exists in the OrderedDictionary<TKey,TValue>.
+ void IOrderedDictionary.Insert(int index, object key, object value) + { + Insert(index, ConvertToKeyType(key), ConvertToValueType(value)); + } + + /// + /// Removes the entry at the specified index from the OrderedDictionary<TKey,TValue> collection. + /// + /// The zero-based index of the entry to remove. + /// is less than 0.
+ /// -or-
+ /// index is equal to or greater than .
+ public void RemoveAt(int index) + { + if(index >= Count || index < 0) + throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection"); + + TKey key = List[index].Key; + + List.RemoveAt(index); + Dictionary.Remove(key); + } + + /// + /// Gets or sets the value at the specified index. + /// + /// The zero-based index of the value to get or set. + /// The value of the item at the specified index. + /// is less than 0.
+ /// -or-
+ /// index is equal to or greater than .
+ public TValue this[int index] + { + get + { + return List[index].Value; + } + + set + { + if(index >= Count || index < 0) + throw new ArgumentOutOfRangeException("index", "'index' must be non-negative and less than the size of the collection"); + + TKey key = List[index].Key; + + List[index] = new KeyValuePair(key, value); + Dictionary[key] = value; + } + } + + /// + /// Gets or sets the value at the specified index. + /// + /// The zero-based index of the value to get or set. + /// The value of the item at the specified index. + /// is less than 0.
+ /// -or-
+ /// index is equal to or greater than .
+ /// is a null reference, and the value type of the OrderedDictionary<TKey,TValue> is a value type. + /// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of . + object IOrderedDictionary.this[int index] + { + get + { + return this[index]; + } + + set + { + this[index] = ConvertToValueType(value); + } + } + + /// + /// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index. + /// + /// The key of the entry to add. + /// The value of the entry to add. This value can be . + /// A key cannot be , but a value can be. + /// You can also use the property to add new elements by setting the value of a key that does not exist in the OrderedDictionary<TKey,TValue> collection; however, if the specified key already exists in the OrderedDictionary<TKey,TValue>, setting the property overwrites the old value. In contrast, the method does not modify existing elements. + /// is + /// An element with the same key already exists in the OrderedDictionary<TKey,TValue> + void IDictionary.Add(TKey key, TValue value) + { + Add(key, value); + } + + /// + /// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index. + /// + /// The key of the entry to add. + /// The value of the entry to add. This value can be . + /// The index of the newly added entry + /// A key cannot be , but a value can be. + /// You can also use the property to add new elements by setting the value of a key that does not exist in the OrderedDictionary<TKey,TValue> collection; however, if the specified key already exists in the OrderedDictionary<TKey,TValue>, setting the property overwrites the old value. In contrast, the method does not modify existing elements. + /// is + /// An element with the same key already exists in the OrderedDictionary<TKey,TValue> + public int Add(TKey key, TValue value) + { + Dictionary.Add(key, value); + List.Add(new KeyValuePair(key, value)); + return Count - 1; + } + + /// + /// Adds an entry with the specified key and value into the OrderedDictionary<TKey,TValue> collection with the lowest available index. + /// + /// The key of the entry to add. + /// The value of the entry to add. This value can be . + /// is .
+ /// -or-
+ /// is , and the value type of the OrderedDictionary<TKey,TValue> is a value type.
+ /// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
+ /// -or-
+ /// The value type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of .
+ void IDictionary.Add(object key, object value) + { + Add(ConvertToKeyType(key), ConvertToValueType(value)); + } + + /// + /// Removes all elements from the OrderedDictionary<TKey,TValue> collection. + /// + /// The capacity is not changed as a result of calling this method. + public void Clear() + { + Dictionary.Clear(); + List.Clear(); + } + + /// + /// Determines whether the OrderedDictionary<TKey,TValue> collection contains a specific key. + /// + /// The key to locate in the OrderedDictionary<TKey,TValue> collection. + /// if the OrderedDictionary<TKey,TValue> collection contains an element with the specified key; otherwise, . + /// is + public bool ContainsKey(TKey key) + { + return Dictionary.ContainsKey(key); + } + /// + /// Determines whether the OrderedDictionary<TKey,TValue> collection contains a specific key. + /// + /// The key to locate in the OrderedDictionary<TKey,TValue> collection. + /// if the OrderedDictionary<TKey,TValue> collection contains an element with the specified key; otherwise, . + /// is + /// The key type of the OrderedDictionary<TKey,TValue> is not in the inheritance hierarchy of . + bool IDictionary.Contains(object key) + { + return ContainsKey(ConvertToKeyType(key)); + } + + /// + /// Gets a value indicating whether the OrderedDictionary<TKey,TValue> has a fixed size. + /// + /// if the OrderedDictionary<TKey,TValue> has a fixed size; otherwise, . The default is . + bool IDictionary.IsFixedSize + { + get + { + return false; + } + } + + /// + /// Gets a value indicating whether the OrderedDictionary<TKey,TValue> collection is read-only. + /// + /// if the OrderedDictionary<TKey,TValue> is read-only; otherwise, . The default is . + /// + /// A collection that is read-only does not allow the addition, removal, or modification of elements after the collection is created. + /// A collection that is read-only is simply a collection with a wrapper that prevents modification of the collection; therefore, if changes are made to the underlying collection, the read-only collection reflects those changes. + /// + public bool IsReadOnly + { + get + { + return false; + } + } + + /// + /// Gets an object containing the keys in the OrderedDictionary<TKey,TValue>. + /// + /// An object containing the keys in the OrderedDictionary<TKey,TValue>. + /// The returned object is not a static copy; instead, the collection refers back to the keys in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the key collection. + ICollection IDictionary.Keys + { + get + { + return (ICollection)Keys; + } + } + + /// + /// Returns the zero-based index of the specified key in the OrderedDictionary<TKey,TValue> + /// + /// The key to locate in the OrderedDictionary<TKey,TValue> + /// The zero-based index of , if is found in the OrderedDictionary<TKey,TValue>; otherwise, -1 + /// This method performs a linear search; therefore it has a cost of O(n) at worst. + public int IndexOfKey(TKey key) + { + if(null == key) + throw new ArgumentNullException("key"); + + for(int index = 0; index < List.Count; index++) + { + KeyValuePair entry = List[index]; + TKey next = entry.Key; + if(null != _comparer) + { + if(_comparer.Equals(next, key)) + { + return index; + } + } + else if(next.Equals(key)) + { + return index; + } + } + + return -1; + } + + /// + /// Removes the entry with the specified key from the OrderedDictionary<TKey,TValue> collection. + /// + /// The key of the entry to remove + /// if the key was found and the corresponding element was removed; otherwise, + public bool Remove(TKey key) + { + if(null == key) + throw new ArgumentNullException("key"); + + int index = IndexOfKey(key); + if(index >= 0) + { + if(Dictionary.Remove(key)) + { + List.RemoveAt(index); + return true; + } + } + return false; + } + + /// + /// Removes the entry with the specified key from the OrderedDictionary<TKey,TValue> collection. + /// + /// The key of the entry to remove + void IDictionary.Remove(object key) + { + Remove(ConvertToKeyType(key)); + } + + /// + /// Gets an object containing the values in the OrderedDictionary<TKey,TValue> collection. + /// + /// An object containing the values in the OrderedDictionary<TKey,TValue> collection. + /// The returned object is not a static copy; instead, the refers back to the values in the original OrderedDictionary<TKey,TValue> collection. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the . + ICollection IDictionary.Values + { + get + { + return (ICollection)Values; + } + } + + /// + /// Gets or sets the value with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. If the specified key is not found, attempting to get it returns , and attempting to set it creates a new element using the specified key. + public TValue this[TKey key] + { + get + { + return Dictionary[key]; + } + set + { + if(Dictionary.ContainsKey(key)) + { + Dictionary[key] = value; + List[IndexOfKey(key)] = new KeyValuePair(key, value); + } + else + { + Add(key, value); + } + } + } + + /// + /// Gets or sets the value with the specified key. + /// + /// The key of the value to get or set. + /// The value associated with the specified key. If the specified key is not found, attempting to get it returns , and attempting to set it creates a new element using the specified key. + object IDictionary.this[object key] + { + get + { + return this[ConvertToKeyType(key)]; + } + set + { + this[ConvertToKeyType(key)] = ConvertToValueType(value); + } + } + + /// + /// Copies the elements of the OrderedDictionary<TKey,TValue> elements to a one-dimensional Array object at the specified index. + /// + /// The one-dimensional object that is the destination of the objects copied from the OrderedDictionary<TKey,TValue>. The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// The method preserves the order of the elements in the OrderedDictionary<TKey,TValue> + void ICollection.CopyTo(Array array, int index) + { + ((ICollection)List).CopyTo(array, index); + } + + /// + /// Gets the number of key/values pairs contained in the OrderedDictionary<TKey,TValue> collection. + /// + /// The number of key/value pairs contained in the OrderedDictionary<TKey,TValue> collection. + public int Count + { + get + { + return List.Count; + } + } + + /// + /// Gets a value indicating whether access to the OrderedDictionary<TKey,TValue> object is synchronized (thread-safe). + /// + /// This method always returns false. + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + /// + /// Gets an object that can be used to synchronize access to the OrderedDictionary<TKey,TValue> object. + /// + /// An object that can be used to synchronize access to the OrderedDictionary<TKey,TValue> object. + object ICollection.SyncRoot + { + get + { + if(this._syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null); + } + return this._syncRoot; + } + } + + /// + /// Gets an ICollection<TKey> object containing the keys in the OrderedDictionary<TKey,TValue>. + /// + /// An ICollection<TKey> object containing the keys in the OrderedDictionary<TKey,TValue>. + /// The returned ICollection<TKey> object is not a static copy; instead, the collection refers back to the keys in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the key collection. + public ICollection Keys + { + get + { + return Dictionary.Keys; + } + } + + /// + /// Gets the value associated with the specified key. + /// + /// The key of the value to get. + /// When this method returns, contains the value associated with the specified key, if the key is found; otherwise, the default value for the type of . This parameter can be passed uninitialized. + /// if the OrderedDictionary<TKey,TValue> contains an element with the specified key; otherwise, . + public bool TryGetValue(TKey key, out TValue value) + { + return Dictionary.TryGetValue(key, out value); + } + + /// + /// Gets an ICollection<TValue> object containing the values in the OrderedDictionary<TKey,TValue>. + /// + /// An ICollection<TValue> object containing the values in the OrderedDictionary<TKey,TValue>. + /// The returned ICollection<TKey> object is not a static copy; instead, the collection refers back to the values in the original OrderedDictionary<TKey,TValue>. Therefore, changes to the OrderedDictionary<TKey,TValue> continue to be reflected in the value collection. + public ICollection Values + { + get + { + return Dictionary.Values; + } + } + + /// + /// Adds the specified value to the OrderedDictionary<TKey,TValue> with the specified key. + /// + /// The KeyValuePair<TKey,TValue> structure representing the key and value to add to the OrderedDictionary<TKey,TValue>. + void ICollection>.Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + /// + /// Determines whether the OrderedDictionary<TKey,TValue> contains a specific key and value. + /// + /// The KeyValuePair<TKey,TValue> structure to locate in the OrderedDictionary<TKey,TValue>. + /// if is found in the OrderedDictionary<TKey,TValue>; otherwise, . + bool ICollection>.Contains(KeyValuePair item) + { + return ((ICollection>)Dictionary).Contains(item); + } + + /// + /// Copies the elements of the OrderedDictionary<TKey,TValue> to an array of type , starting at the specified index. + /// + /// The one-dimensional array of type KeyValuePair<TKey,TValue> that is the destination of the KeyValuePair<TKey,TValue> elements copied from the OrderedDictionary<TKey,TValue>. The array must have zero-based indexing. + /// The zero-based index in at which copying begins. + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)Dictionary).CopyTo(array, arrayIndex); + } + + /// + /// Removes a key and value from the dictionary. + /// + /// The KeyValuePair<TKey,TValue> structure representing the key and value to remove from the OrderedDictionary<TKey,TValue>. + /// if the key and value represented by is successfully found and removed; otherwise, . This method returns if is not found in the OrderedDictionary<TKey,TValue>. + bool ICollection>.Remove(KeyValuePair item) + { + return Remove(item.Key); + } + } +} diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll new file mode 100755 index 0000000..adab05f Binary files /dev/null and b/NW4RTools/bin/Debug/NW4RTools.dll differ diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb new file mode 100644 index 0000000..51a7fb0 Binary files /dev/null and b/NW4RTools/bin/Debug/NW4RTools.dll.mdb differ -- cgit v1.2.3