diff options
28 files changed, 2012 insertions, 0 deletions
diff --git a/NW4RTools.sln b/NW4RTools.sln new file mode 100644 index 0000000..eb42975 --- /dev/null +++ b/NW4RTools.sln @@ -0,0 +1,58 @@ +
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NW4RTools", "NW4RTools\NW4RTools.csproj", "{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A9C9FABD-0A5F-4DAB-979D-9F288F96866F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A9C9FABD-0A5F-4DAB-979D-9F288F96866F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A9C9FABD-0A5F-4DAB-979D-9F288F96866F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A9C9FABD-0A5F-4DAB-979D-9F288F96866F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = TestApp\TestApp.csproj
+ Policies = $0
+ $0.DotNetNamingPolicy = $1
+ $1.DirectoryNamespaceAssociation = None
+ $1.ResourceNamePolicy = FileFormatDefault
+ $0.TextStylePolicy = $2
+ $2.inheritsSet = null
+ $2.scope = text/x-csharp
+ $0.CSharpFormattingPolicy = $3
+ $3.NamespaceBraceStyle = EndOfLine
+ $3.ClassBraceStyle = EndOfLine
+ $3.InterfaceBraceStyle = EndOfLine
+ $3.StructBraceStyle = EndOfLine
+ $3.EnumBraceStyle = EndOfLine
+ $3.MethodBraceStyle = EndOfLine
+ $3.ConstructorBraceStyle = EndOfLine
+ $3.DestructorBraceStyle = EndOfLine
+ $3.BeforeMethodCallParentheses = False
+ $3.BeforeMethodDeclarationParentheses = False
+ $3.BeforeConstructorDeclarationParentheses = False
+ $3.BeforeDelegateDeclarationParentheses = False
+ $3.NewParentheses = False
+ $3.inheritsSet = Mono
+ $3.inheritsScope = text/x-csharp
+ $3.scope = text/x-csharp
+ $0.TextStylePolicy = $4
+ $4.FileWidth = 120
+ $4.TabWidth = 4
+ $4.RemoveTrailingWhitespace = True
+ $4.EolMarker = Unix
+ $4.inheritsSet = Mono
+ $4.inheritsScope = text/plain
+ $4.scope = text/plain
+ EndGlobalSection
+EndGlobal
diff --git a/NW4RTools.userprefs b/NW4RTools.userprefs new file mode 100644 index 0000000..f308c35 --- /dev/null +++ b/NW4RTools.userprefs @@ -0,0 +1,17 @@ +<Properties> + <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> + <MonoDevelop.Ide.Workbench ActiveDocument="NW4RTools/Models/Material.cs"> + <Files> + <File FileName="NW4RTools/Types.cs" Line="11" Column="26" /> + <File FileName="TestApp/Main.cs" Line="9" Column="44" /> + <File FileName="NW4RTools/BrresReader.cs" Line="412" Column="1" /> + <File FileName="NW4RTools/Models/Model.cs" Line="17" Column="54" /> + <File FileName="NW4RTools/Models/Material.cs" Line="48" Column="16" /> + <File FileName="NW4RTools/InputStream.cs" Line="94" Column="15" /> + </Files> + </MonoDevelop.Ide.Workbench> + <MonoDevelop.Ide.DebuggingService.Breakpoints> + <BreakpointStore /> + </MonoDevelop.Ide.DebuggingService.Breakpoints> + <MonoDevelop.Ide.DebuggingService.PinnedWatches /> +</Properties>
\ No newline at end of file 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<InputStream> { + } + + + + private ResFile File; + private Logger Debug; + private SortedDictionary<int, string> OffsetMap; + + private BrresReader() { + Debug = new Logger(); + OffsetMap = new SortedDictionary<int, string>(); + } + + 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<Model>(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<ByteCode>(ins.At(startPos + bytecodeOffset), ConvertModelBytecode); + + // Load nodes and fix up pointers + StartNodeLoading(); + + using (var c2 = Debug.Push("Nodes")) + mdl.Nodes = ReadAndConvertDict<Node>(ins.At(startPos + nodeOffset), ConvertModelNode); + + FinishNodeLoading(); + + // Load vertex data + using (var c2 = Debug.Push("Vertex Position Data")) + mdl.VtxPosData = ReadAndConvertDict<VertexPosData>(ins.At(startPos + vtxPosOffset), ConvertVtxPosData); + using (var c2 = Debug.Push("Vertex Normal Data")) + mdl.VtxNrmData = ReadAndConvertDict<VertexNrmData>(ins.At(startPos + vtxNrmOffset), ConvertVtxNrmData); + using (var c2 = Debug.Push("Vertex Colour Data")) + mdl.VtxClrData = ReadAndConvertDict<VertexClrData>(ins.At(startPos + vtxClrOffset), ConvertVtxClrData); + using (var c2 = Debug.Push("Vertex TexCoord Data")) + mdl.VtxTexCoordData = ReadAndConvertDict<VertexTexCoordData>(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<int, NodeLoadInfo> NodeLoadData; + + private void StartNodeLoading() { + NodeLoadData = new Dictionary<int, NodeLoadInfo>(); + } + + 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<TValue>(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<TValue> ReadAndConvertDict<TValue>(InputStream ins, ConvertResourceDelegate<TValue> func) { + int dictPos = ins.Position; + RawStreamResDict theDict = ReadDict(ins); + var outDict = new ResDict<TValue>(); + + // 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<string> PrefixElements; + private List<string> Names; + private int BlockCount; + private string Prefix; + + public Logger() { + Names = new List<string>(); + Names.Add("root"); + + PrefixElements = new List<string>(); + 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<Instruction> Instructions; + + public ByteCode() { + Instructions = new List<Instruction>(); + } + } +} + 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> Bytecode; + public ResDict<Node> Nodes; + public ResDict<VertexPosData> VtxPosData; + public ResDict<VertexNrmData> VtxNrmData; + public ResDict<VertexClrData> VtxClrData; + public ResDict<VertexTexCoordData> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.21022</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</ProjectGuid> + <OutputType>Library</OutputType> + <RootNamespace>NW4RTools</RootNamespace> + <AssemblyName>NW4RTools</AssemblyName> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>none</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <ConsolePause>false</ConsolePause> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AssemblyInfo.cs" /> + <Compile Include="Logger.cs" /> + <Compile Include="InputStream.cs" /> + <Compile Include="Types.cs" /> + <Compile Include="ResDict.cs" /> + <Compile Include="BrresReader.cs" /> + <Compile Include="ResFile.cs" /> + <Compile Include="Models\Model.cs" /> + <Compile Include="Models\ByteCode.cs" /> + <Compile Include="Models\Node.cs" /> + <Compile Include="Util\IOrderedDictionary.cs" /> + <Compile Include="Util\OrderedDictionary.cs" /> + <Compile Include="Models\VertexData.cs" /> + <Compile Include="Models\Material.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <ItemGroup> + <Folder Include="Models\" /> + <Folder Include="Util\" /> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/NW4RTools/NW4RTools.pidb b/NW4RTools/NW4RTools.pidb Binary files differnew file mode 100644 index 0000000..726779f --- /dev/null +++ b/NW4RTools/NW4RTools.pidb 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<TValue> : Util.OrderedDictionary<string, TValue> { + } +} + 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<object> { + public UInt16 version; + + public ResDict<TValue> GetGroup<TValue>(string name) { + return this[name] as ResDict<TValue>; + } + } +} + 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
+{
+ /// <summary>
+ /// Represents a generic collection of key/value pairs that are ordered independently of the key and value.
+ /// </summary>
+ /// <typeparam name="TKey">The type of the keys in the dictionary</typeparam>
+ /// <typeparam name="TValue">The type of the values in the dictionary</typeparam>
+ public interface IOrderedDictionary<TKey, TValue> : IOrderedDictionary, IDictionary<TKey, TValue>
+ {
+ /// <summary>
+ /// Adds an entry with the specified key and value into the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> collection with the lowest available index.
+ /// </summary>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add.</param>
+ /// <returns>The index of the newly added entry</returns>
+ /// <remarks>
+ /// <para>You can also use the <see cref="P:System.Collections.Generic.IDictionary{TKey,TValue}.Item(TKey)"/> property to add new elements by setting the value of a key that does not exist in the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> collection; however, if the specified key already exists in the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see>, setting the <see cref="P:Item(TKey)"/> property overwrites the old value. In contrast, the <see cref="M:Add"/> method does not modify existing elements.</para></remarks>
+ /// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see></exception>
+ /// <exception cref="NotSupportedException">The <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> is read-only.<br/>
+ /// -or-<br/>
+ /// The <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> has a fized size.</exception>
+ new int Add(TKey key, TValue value);
+
+ /// <summary>
+ /// Inserts a new entry into the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> collection with the specified key and value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which the element should be inserted.</param>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. The value can be <null/> if the type of the values in the dictionary is a reference type.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// <paramref name="index"/> is greater than <see cref="System.Collections.ICollection.Count"/>.</exception>
+ /// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see>.</exception>
+ /// <exception cref="NotSupportedException">The <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> is read-only.<br/>
+ /// -or-<br/>
+ /// The <see cref="IOrderedDictionary{TKey,TValue}">IOrderedDictionary<TKey,TValue></see> has a fized size.</exception>
+ void Insert(int index, TKey key, TValue value);
+
+ /// <summary>
+ /// Gets or sets the value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index of the value to get or set.</param>
+ /// <value>The value of the item at the specified index.</value>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// <paramref name="index"/> is equal to or greater than <see cref="System.Collections.ICollection.Count"/>.</exception>
+ 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
+{
+ /// <summary>
+ /// Represents a generic collection of key/value pairs that are ordered independently of the key and value.
+ /// </summary>
+ /// <typeparam name="TKey">The type of the keys in the dictionary</typeparam>
+ /// <typeparam name="TValue">The type of the values in the dictionary</typeparam>
+ public class OrderedDictionary<TKey, TValue> : IOrderedDictionary<TKey, TValue>
+ {
+ 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<TKey, TValue> _dictionary;
+ private List<KeyValuePair<TKey, TValue>> _list;
+ private IEqualityComparer<TKey> _comparer;
+ private object _syncRoot;
+ private int _initialCapacity;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> class.
+ /// </summary>
+ public OrderedDictionary()
+ : this(DefaultInitialCapacity, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> class using the specified initial capacity.
+ /// </summary>
+ /// <param name="capacity">The initial number of elements that the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> can contain.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity"/> is less than 0</exception>
+ public OrderedDictionary(int capacity)
+ : this(capacity, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> class using the specified comparer.
+ /// </summary>
+ /// <param name="comparer">The <see cref="IEqualityComparer{TKey}">IEqualityComparer<TKey></see> to use when comparing keys, or <null/> to use the default <see cref="EqualityComparer{TKey}">EqualityComparer<TKey></see> for the type of the key.</param>
+ public OrderedDictionary(IEqualityComparer<TKey> comparer)
+ : this(DefaultInitialCapacity, comparer)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> class using the specified initial capacity and comparer.
+ /// </summary>
+ /// <param name="capacity">The initial number of elements that the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection can contain.</param>
+ /// <param name="comparer">The <see cref="IEqualityComparer{TKey}">IEqualityComparer<TKey></see> to use when comparing keys, or <null/> to use the default <see cref="EqualityComparer{TKey}">EqualityComparer<TKey></see> for the type of the key.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity"/> is less than 0</exception>
+ public OrderedDictionary(int capacity, IEqualityComparer<TKey> comparer)
+ {
+ if(0 > capacity)
+ throw new ArgumentOutOfRangeException("capacity", "'capacity' must be non-negative");
+
+ _initialCapacity = capacity;
+ _comparer = comparer;
+ }
+
+ /// <summary>
+ /// Converts the object passed as a key to the key type of the dictionary
+ /// </summary>
+ /// <param name="keyObject">The key object to check</param>
+ /// <returns>The key object, cast as the key type of the dictionary</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="keyObject"/> is <null/>.</exception>
+ /// <exception cref="ArgumentException">The key type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="keyObject"/>.</exception>
+ 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");
+ }
+
+ /// <summary>
+ /// Converts the object passed as a value to the value type of the dictionary
+ /// </summary>
+ /// <param name="value">The object to convert to the value type of the dictionary</param>
+ /// <returns>The value object, converted to the value type of the dictionary</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="valueObject"/> is <null/>, and the value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is a value type.</exception>
+ /// <exception cref="ArgumentException">The value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="valueObject"/>.</exception>
+ 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");
+ }
+
+ /// <summary>
+ /// Gets the dictionary object that stores the keys and values
+ /// </summary>
+ /// <value>The dictionary object that stores the keys and values for the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></value>
+ /// <remarks>Accessing this property will create the dictionary object if necessary</remarks>
+ private Dictionary<TKey, TValue> Dictionary
+ {
+ get
+ {
+ if(null == _dictionary)
+ {
+ _dictionary = new Dictionary<TKey, TValue>(_initialCapacity, _comparer);
+ }
+ return _dictionary;
+ }
+ }
+
+ /// <summary>
+ /// Gets the list object that stores the key/value pairs.
+ /// </summary>
+ /// <value>The list object that stores the key/value pairs for the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></value>
+ /// <remarks>Accessing this property will create the list object if necessary.</remarks>
+ private List<KeyValuePair<TKey, TValue>> List
+ {
+ get
+ {
+ if(null == _list)
+ {
+ _list = new List<KeyValuePair<TKey, TValue>>(_initialCapacity);
+ }
+ return _list;
+ }
+ }
+
+ IDictionaryEnumerator IOrderedDictionary.GetEnumerator()
+ {
+ return Dictionary.GetEnumerator();
+ }
+
+ IDictionaryEnumerator IDictionary.GetEnumerator()
+ {
+ return Dictionary.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return List.GetEnumerator();
+ }
+
+ IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey,TValue>>.GetEnumerator()
+ {
+ return List.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Inserts a new entry into the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection with the specified key and value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which the element should be inserted.</param>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. The value can be <null/> if the type of the values in the dictionary is a reference type.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// <paramref name="index"/> is greater than <see cref="Count"/>.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/>.</exception>
+ /// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</exception>
+ 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<TKey, TValue>(key, value));
+ }
+
+ /// <summary>
+ /// Inserts a new entry into the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection with the specified key and value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index at which the element should be inserted.</param>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. The value can be <null/> if the type of the values in the dictionary is a reference type.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// <paramref name="index"/> is greater than <see cref="Count"/>.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/>.<br/>
+ /// -or-<br/>
+ /// <paramref name="value"/> is <null/>, and the value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is a value type.</exception>
+ /// <exception cref="ArgumentException">The key type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="key"/>.<br/>
+ /// -or-<br/>
+ /// The value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="value"/>.<br/>
+ /// -or-<br/>
+ /// An element with the same key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</exception>
+ void IOrderedDictionary.Insert(int index, object key, object value)
+ {
+ Insert(index, ConvertToKeyType(key), ConvertToValueType(value));
+ }
+
+ /// <summary>
+ /// Removes the entry at the specified index from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <param name="index">The zero-based index of the entry to remove.</param>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// index is equal to or greater than <see cref="Count"/>.</exception>
+ 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);
+ }
+
+ /// <summary>
+ /// Gets or sets the value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index of the value to get or set.</param>
+ /// <value>The value of the item at the specified index.</value>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// index is equal to or greater than <see cref="Count"/>.</exception>
+ 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<TKey, TValue>(key, value);
+ Dictionary[key] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value at the specified index.
+ /// </summary>
+ /// <param name="index">The zero-based index of the value to get or set.</param>
+ /// <value>The value of the item at the specified index.</value>
+ /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is less than 0.<br/>
+ /// -or-<br/>
+ /// index is equal to or greater than <see cref="Count"/>.</exception>
+ /// <exception cref="ArgumentNullException"><paramref name="valueObject"/> is a null reference, and the value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is a value type.</exception>
+ /// <exception cref="ArgumentException">The value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="valueObject"/>.</exception>
+ object IOrderedDictionary.this[int index]
+ {
+ get
+ {
+ return this[index];
+ }
+
+ set
+ {
+ this[index] = ConvertToValueType(value);
+ }
+ }
+
+ /// <summary>
+ /// Adds an entry with the specified key and value into the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection with the lowest available index.
+ /// </summary>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. This value can be <null/>.</param>
+ /// <remarks>A key cannot be <null/>, but a value can be.
+ /// <para>You can also use the <see cref="P:OrderedDictionary{TKey,TValue}.Item(TKey)"/> property to add new elements by setting the value of a key that does not exist in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection; however, if the specified key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>, setting the <see cref="P:OrderedDictionary{TKey,TValue}.Item(TKey)"/> property overwrites the old value. In contrast, the <see cref="M:Add"/> method does not modify existing elements.</para></remarks>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/></exception>
+ /// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></exception>
+ void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
+ {
+ Add(key, value);
+ }
+
+ /// <summary>
+ /// Adds an entry with the specified key and value into the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection with the lowest available index.
+ /// </summary>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. This value can be <null/>.</param>
+ /// <returns>The index of the newly added entry</returns>
+ /// <remarks>A key cannot be <null/>, but a value can be.
+ /// <para>You can also use the <see cref="P:OrderedDictionary{TKey,TValue}.Item(TKey)"/> property to add new elements by setting the value of a key that does not exist in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection; however, if the specified key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>, setting the <see cref="P:OrderedDictionary{TKey,TValue}.Item(TKey)"/> property overwrites the old value. In contrast, the <see cref="M:Add"/> method does not modify existing elements.</para></remarks>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/></exception>
+ /// <exception cref="ArgumentException">An element with the same key already exists in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></exception>
+ public int Add(TKey key, TValue value)
+ {
+ Dictionary.Add(key, value);
+ List.Add(new KeyValuePair<TKey,TValue>(key, value));
+ return Count - 1;
+ }
+
+ /// <summary>
+ /// Adds an entry with the specified key and value into the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection with the lowest available index.
+ /// </summary>
+ /// <param name="key">The key of the entry to add.</param>
+ /// <param name="value">The value of the entry to add. This value can be <null/>.</param>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/>.<br/>
+ /// -or-<br/>
+ /// <paramref name="value"/> is <null/>, and the value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is a value type.</exception>
+ /// <exception cref="ArgumentException">The key type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="key"/>.<br/>
+ /// -or-<br/>
+ /// The value type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="value"/>.</exception>
+ void IDictionary.Add(object key, object value)
+ {
+ Add(ConvertToKeyType(key), ConvertToValueType(value));
+ }
+
+ /// <summary>
+ /// Removes all elements from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <remarks>The capacity is not changed as a result of calling this method.</remarks>
+ public void Clear()
+ {
+ Dictionary.Clear();
+ List.Clear();
+ }
+
+ /// <summary>
+ /// Determines whether the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection contains a specific key.
+ /// </summary>
+ /// <param name="key">The key to locate in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.</param>
+ /// <returns><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/></exception>
+ public bool ContainsKey(TKey key)
+ {
+ return Dictionary.ContainsKey(key);
+ }
+ /// <summary>
+ /// Determines whether the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection contains a specific key.
+ /// </summary>
+ /// <param name="key">The key to locate in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.</param>
+ /// <returns><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
+ /// <exception cref="ArgumentNullException"><paramref name="key"/> is <null/></exception>
+ /// <exception cref="ArgumentException">The key type of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is not in the inheritance hierarchy of <paramref name="key"/>.</exception>
+ bool IDictionary.Contains(object key)
+ {
+ return ContainsKey(ConvertToKeyType(key));
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> has a fixed size.
+ /// </summary>
+ /// <value><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> has a fixed size; otherwise, <see langword="false"/>. The default is <see langword="false"/>.</value>
+ bool IDictionary.IsFixedSize
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection is read-only.
+ /// </summary>
+ /// <value><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> is read-only; otherwise, <see langword="false"/>. The default is <see langword="false"/>.</value>
+ /// <remarks>
+ /// A collection that is read-only does not allow the addition, removal, or modification of elements after the collection is created.
+ /// <para>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.</para>
+ /// </remarks>
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="ICollection"/> object containing the keys in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.
+ /// </summary>
+ /// <value>An <see cref="ICollection"/> object containing the keys in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</value>
+ /// <remarks>The returned <see cref="ICollection"/> object is not a static copy; instead, the collection refers back to the keys in the original <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>. Therefore, changes to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> continue to be reflected in the key collection.</remarks>
+ ICollection IDictionary.Keys
+ {
+ get
+ {
+ return (ICollection)Keys;
+ }
+ }
+
+ /// <summary>
+ /// Returns the zero-based index of the specified key in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>
+ /// </summary>
+ /// <param name="key">The key to locate in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></param>
+ /// <returns>The zero-based index of <paramref name="key"/>, if <paramref name="ley"/> is found in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>; otherwise, -1</returns>
+ /// <remarks>This method performs a linear search; therefore it has a cost of O(n) at worst.</remarks>
+ public int IndexOfKey(TKey key)
+ {
+ if(null == key)
+ throw new ArgumentNullException("key");
+
+ for(int index = 0; index < List.Count; index++)
+ {
+ KeyValuePair<TKey, TValue> 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;
+ }
+
+ /// <summary>
+ /// Removes the entry with the specified key from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <param name="key">The key of the entry to remove</param>
+ /// <returns><see langword="true"/> if the key was found and the corresponding element was removed; otherwise, <see langword="false"/></returns>
+ 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;
+ }
+
+ /// <summary>
+ /// Removes the entry with the specified key from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <param name="key">The key of the entry to remove</param>
+ void IDictionary.Remove(object key)
+ {
+ Remove(ConvertToKeyType(key));
+ }
+
+ /// <summary>
+ /// Gets an <see cref="ICollection"/> object containing the values in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <value>An <see cref="ICollection"/> object containing the values in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.</value>
+ /// <remarks>The returned <see cref="ICollection"/> object is not a static copy; instead, the <see cref="ICollection"/> refers back to the values in the original <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection. Therefore, changes to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> continue to be reflected in the <see cref="ICollection"/>.</remarks>
+ ICollection IDictionary.Values
+ {
+ get
+ {
+ return (ICollection)Values;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ /// <value>The value associated with the specified key. If the specified key is not found, attempting to get it returns <null/>, and attempting to set it creates a new element using the specified key.</value>
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return Dictionary[key];
+ }
+ set
+ {
+ if(Dictionary.ContainsKey(key))
+ {
+ Dictionary[key] = value;
+ List[IndexOfKey(key)] = new KeyValuePair<TKey, TValue>(key, value);
+ }
+ else
+ {
+ Add(key, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the value with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get or set.</param>
+ /// <value>The value associated with the specified key. If the specified key is not found, attempting to get it returns <null/>, and attempting to set it creates a new element using the specified key.</value>
+ object IDictionary.this[object key]
+ {
+ get
+ {
+ return this[ConvertToKeyType(key)];
+ }
+ set
+ {
+ this[ConvertToKeyType(key)] = ConvertToValueType(value);
+ }
+ }
+
+ /// <summary>
+ /// Copies the elements of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> elements to a one-dimensional Array object at the specified index.
+ /// </summary>
+ /// <param name="array">The one-dimensional <see cref="Array"/> object that is the destination of the <see cref="T:KeyValuePair`2>"/> objects copied from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>. The <see cref="Array"/> must have zero-based indexing.</param>
+ /// <param name="index">The zero-based index in <paramref name="array"/> at which copying begins.</param>
+ /// <remarks>The <see cref="M:CopyTo"/> method preserves the order of the elements in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see></remarks>
+ void ICollection.CopyTo(Array array, int index)
+ {
+ ((ICollection)List).CopyTo(array, index);
+ }
+
+ /// <summary>
+ /// Gets the number of key/values pairs contained in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.
+ /// </summary>
+ /// <value>The number of key/value pairs contained in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> collection.</value>
+ public int Count
+ {
+ get
+ {
+ return List.Count;
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether access to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> object is synchronized (thread-safe).
+ /// </summary>
+ /// <value>This method always returns false.</value>
+ bool ICollection.IsSynchronized
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets an object that can be used to synchronize access to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> object.
+ /// </summary>
+ /// <value>An object that can be used to synchronize access to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> object.</value>
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ if(this._syncRoot == null)
+ {
+ System.Threading.Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
+ }
+ return this._syncRoot;
+ }
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:System.Collections.Generic.ICollection{TKey}">ICollection<TKey></see> object containing the keys in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.
+ /// </summary>
+ /// <value>An <see cref="T:System.Collections.Generic.ICollection{TKey}">ICollection<TKey></see> object containing the keys in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</value>
+ /// <remarks>The returned <see cref="T:System.Collections.Generic.ICollection{TKey}">ICollection<TKey></see> object is not a static copy; instead, the collection refers back to the keys in the original <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>. Therefore, changes to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> continue to be reflected in the key collection.</remarks>
+ public ICollection<TKey> Keys
+ {
+ get
+ {
+ return Dictionary.Keys;
+ }
+ }
+
+ /// <summary>
+ /// Gets the value associated with the specified key.
+ /// </summary>
+ /// <param name="key">The key of the value to get.</param>
+ /// <param name="value">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 <paramref name="value"/>. This parameter can be passed uninitialized.</param>
+ /// <returns><see langword="true"/> if the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> contains an element with the specified key; otherwise, <see langword="false"/>.</returns>
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return Dictionary.TryGetValue(key, out value);
+ }
+
+ /// <summary>
+ /// Gets an <see cref="T:ICollection{TValue}">ICollection<TValue></see> object containing the values in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.
+ /// </summary>
+ /// <value>An <see cref="T:ICollection{TValue}">ICollection<TValue></see> object containing the values in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</value>
+ /// <remarks>The returned <see cref="T:ICollection{TValue}">ICollection<TKey></see> object is not a static copy; instead, the collection refers back to the values in the original <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>. Therefore, changes to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> continue to be reflected in the value collection.</remarks>
+ public ICollection<TValue> Values
+ {
+ get
+ {
+ return Dictionary.Values;
+ }
+ }
+
+ /// <summary>
+ /// Adds the specified value to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> with the specified key.
+ /// </summary>
+ /// <param name="item">The <see cref="T:KeyValuePair{TKey,TValue}">KeyValuePair<TKey,TValue></see> structure representing the key and value to add to the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</param>
+ void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ /// <summary>
+ /// Determines whether the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> contains a specific key and value.
+ /// </summary>
+ /// <param name="item">The <see cref="T:KeyValuePair{TKey,TValue}">KeyValuePair<TKey,TValue></see> structure to locate in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</param>
+ /// <returns><see langword="true"/> if <paramref name="keyValuePair"/> is found in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>; otherwise, <see langword="false"/>.</returns>
+ bool ICollection<KeyValuePair<TKey,TValue>>.Contains(KeyValuePair<TKey, TValue> item)
+ {
+ return ((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).Contains(item);
+ }
+
+ /// <summary>
+ /// Copies the elements of the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see> to an array of type <see cref="T:KeyValuePair`2>"/>, starting at the specified index.
+ /// </summary>
+ /// <param name="array">The one-dimensional array of type <see cref="T:KeyValuePair{TKey,TValue}">KeyValuePair<TKey,TValue></see> that is the destination of the <see cref="T:KeyValuePair{TKey,TValue}">KeyValuePair<TKey,TValue></see> elements copied from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>. The array must have zero-based indexing.</param>
+ /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
+ void ICollection<KeyValuePair<TKey,TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
+ {
+ ((ICollection<KeyValuePair<TKey,TValue>>)Dictionary).CopyTo(array, arrayIndex);
+ }
+
+ /// <summary>
+ /// Removes a key and value from the dictionary.
+ /// </summary>
+ /// <param name="item">The <see cref="T:KeyValuePair{TKey,TValue}">KeyValuePair<TKey,TValue></see> structure representing the key and value to remove from the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</param>
+ /// <returns><see langword="true"/> if the key and value represented by <paramref name="keyValuePair"/> is successfully found and removed; otherwise, <see langword="false"/>. This method returns <see langword="false"/> if <paramref name="keyValuePair"/> is not found in the <see cref="OrderedDictionary{TKey,TValue}">OrderedDictionary<TKey,TValue></see>.</returns>
+ bool ICollection<KeyValuePair<TKey,TValue>>.Remove(KeyValuePair<TKey, TValue> item)
+ {
+ return Remove(item.Key);
+ }
+ }
+}
diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll Binary files differnew file mode 100755 index 0000000..adab05f --- /dev/null +++ b/NW4RTools/bin/Debug/NW4RTools.dll diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb Binary files differnew file mode 100644 index 0000000..51a7fb0 --- /dev/null +++ b/NW4RTools/bin/Debug/NW4RTools.dll.mdb diff --git a/TestApp/AssemblyInfo.cs b/TestApp/AssemblyInfo.cs new file mode 100644 index 0000000..293a655 --- /dev/null +++ b/TestApp/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("TestApp")] +[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/TestApp/Main.cs b/TestApp/Main.cs new file mode 100644 index 0000000..9fb3771 --- /dev/null +++ b/TestApp/Main.cs @@ -0,0 +1,13 @@ +using System; +using NW4RTools; + +namespace TestApp { + class MainClass { + public static void Main(string[] args) { + //byte[] file = System.IO.File.ReadAllBytes("/home/me/Games/Newer/miscStuff/CS_W1/g3d/model.brres"); + byte[] file = System.IO.File.ReadAllBytes("/mnt/h/ISOs/NSMBWii/ais0.1.3/model.brres"); + ResFile rf = BrresReader.LoadFile(file); + } + } +} + diff --git a/TestApp/TestApp.csproj b/TestApp/TestApp.csproj new file mode 100644 index 0000000..5c96e3b --- /dev/null +++ b/TestApp/TestApp.csproj @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProductVersion>9.0.21022</ProductVersion> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>TestApp</RootNamespace> + <AssemblyName>TestApp</AssemblyName> + <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug</OutputPath> + <DefineConstants>DEBUG</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <Externalconsole>true</Externalconsole> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>none</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Release</OutputPath> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + <Externalconsole>true</Externalconsole> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Main.cs" /> + <Compile Include="AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> + <ItemGroup> + <ProjectReference Include="..\NW4RTools\NW4RTools.csproj"> + <Project>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</Project> + <Name>NW4RTools</Name> + </ProjectReference> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/TestApp/TestApp.pidb b/TestApp/TestApp.pidb Binary files differnew file mode 100644 index 0000000..07e4928 --- /dev/null +++ b/TestApp/TestApp.pidb diff --git a/TestApp/bin/Debug/NW4RTools.dll b/TestApp/bin/Debug/NW4RTools.dll Binary files differnew file mode 100755 index 0000000..adab05f --- /dev/null +++ b/TestApp/bin/Debug/NW4RTools.dll diff --git a/TestApp/bin/Debug/NW4RTools.dll.mdb b/TestApp/bin/Debug/NW4RTools.dll.mdb Binary files differnew file mode 100644 index 0000000..51a7fb0 --- /dev/null +++ b/TestApp/bin/Debug/NW4RTools.dll.mdb diff --git a/TestApp/bin/Debug/TestApp.exe b/TestApp/bin/Debug/TestApp.exe Binary files differnew file mode 100755 index 0000000..7adcab2 --- /dev/null +++ b/TestApp/bin/Debug/TestApp.exe diff --git a/TestApp/bin/Debug/TestApp.exe.mdb b/TestApp/bin/Debug/TestApp.exe.mdb Binary files differnew file mode 100644 index 0000000..7e547e0 --- /dev/null +++ b/TestApp/bin/Debug/TestApp.exe.mdb |