diff options
| author | Treeki <treeki@gmail.com> | 2012-02-20 05:30:01 +0100 | 
|---|---|---|
| committer | Treeki <treeki@gmail.com> | 2012-02-20 05:30:01 +0100 | 
| commit | 31380b4bb93d1fb65faff8f71753de80fb0a8c9d (patch) | |
| tree | 8880321028df33877fec9af6c424afe48e7ac4ef /NW4RTools | |
| parent | 364e99d849378546323d1d06307b6773e813b742 (diff) | |
| download | nw4rtools-31380b4bb93d1fb65faff8f71753de80fb0a8c9d.tar.gz nw4rtools-31380b4bb93d1fb65faff8f71753de80fb0a8c9d.zip  | |
AnmChr/Clr/TexSrt writing done for now. Not 100% perfect but it works!
Diffstat (limited to 'NW4RTools')
| -rwxr-xr-x[-rw-r--r--] | NW4RTools/BrresReader.cs | 33 | ||||
| -rwxr-xr-x[-rw-r--r--] | NW4RTools/BrresWriter.cs | 967 | ||||
| -rw-r--r-- | NW4RTools/Misc.cs | 40 | ||||
| -rw-r--r-- | NW4RTools/Models/Animation/CharacterAnim.cs | 2 | ||||
| -rw-r--r-- | NW4RTools/Models/Animation/Shared.cs | 40 | ||||
| -rwxr-xr-x[-rw-r--r--] | NW4RTools/Models/ByteCode.cs | 3 | ||||
| -rw-r--r-- | NW4RTools/NW4RTools.csproj | 183 | ||||
| -rw-r--r-- | NW4RTools/Texture.cs | 15 | ||||
| -rw-r--r-- | NW4RTools/Types.cs | 10 | 
9 files changed, 1174 insertions, 119 deletions
diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs index f120943..9616b5b 100644..100755 --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -153,8 +153,15 @@ namespace NW4RTools {  				// handle mipmaps  				tex.Images = new System.Drawing.Bitmap[mipmapCount]; +#if DEV_DONT_MODIFY_TEXTURE +				tex.ImageData = new byte[mipmapCount][]; +#endif  				for (int i = 0; i < mipmapCount; i++) { -					tex.ImportData(i, ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format); +					byte[] data = ins.ReadBytes(Texture.GetDataSize(width, height, format)); +#if DEV_DONT_MODIFY_TEXTURE +					tex.ImageData[i] = data; +#endif +					tex.ImportData(i, data, width, height, format);  					width /= 2;  					height /= 2;  				} @@ -223,6 +230,10 @@ namespace NW4RTools {  				node.Flags = flags & CharacterAnim.Flags.NonComputableFlags; +				node.ScaleUseModel = ((flags & CharacterAnim.Flags.ScaleUseModel) != 0); +				node.RotateUseModel = ((flags & CharacterAnim.Flags.RotateUseModel) != 0); +				node.TranslateUseModel = ((flags & CharacterAnim.Flags.TranslateUseModel) != 0); +  				// SCALES  				bool needScaleYZ = true; @@ -482,7 +493,7 @@ namespace NW4RTools {  				node.Elements = new ColorAnim.Element[(int)ColorAnim.TargetType.Count];  				for (int i = 0; i < node.Elements.Length; i++) { -					node.Elements[i] = ReadAnmClrElement(ins, startPos, flags); +					node.Elements[i] = ReadAnmClrElement(ins, flags);  					flags >>= 2;  				} @@ -490,7 +501,7 @@ namespace NW4RTools {  			}  		} -		private ColorAnim.Element ReadAnmClrElement(InputStream ins, int startPos, UInt32 flags) { +		private ColorAnim.Element ReadAnmClrElement(InputStream ins, UInt32 flags) {  			var elem = new ColorAnim.Element();  			elem.Exists = (flags & 1) != 0; @@ -498,16 +509,16 @@ namespace NW4RTools {  			OffsetMap[ins.Position] = String.Format("Element [{0}]", elem.IsConstant ? "Constant" : "Colours"); -			elem.Mask = ins.ReadUInt32(); -  			if (!elem.Exists)  				return elem; +			elem.Mask = ins.ReadUInt32(); +  			if (elem.IsConstant) {  				elem.ConstValue = ins.ReadColor(); -			} else { +			} else {
  				int savePos = ins.Position; -				ins.Seek(startPos + ins.ReadInt32()); +				ins.Seek(savePos + ins.ReadInt32());  				elem.Colors = new Color[COLOR_ANIM_HACK.FrameCount + 1]; @@ -597,7 +608,9 @@ namespace NW4RTools {  			var elem = new TextureSRTAnim.Element();  			int savePos = ins.Position; -			ins.Seek(startPos + ins.ReadInt32()); +			ins.Seek(startPos + ins.ReadInt32());
 +
 +			elem.Exists = true;  			OffsetMap.Add(ins.Position, String.Format("Element")); @@ -863,8 +876,8 @@ namespace NW4RTools {  					var insn4 = new ByteCode.DrawShapeInstruction();  					insn4.MaterialID = ins.ReadUInt16();  					insn4.ShapeID = ins.ReadUInt16(); -					insn4.NodeID = ins.ReadUInt16(); -					ins.Skip(1); +					insn4.NodeID = ins.ReadUInt16();
 +					insn4.Unk = ins.ReadByte();
  					bc.Instructions.Add(insn4);  					break; diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs index 04750de..033007e 100644..100755 --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -3,6 +3,7 @@ using System.Collections;  using System.Collections.Generic;  using NW4RTools.Models; +using NW4RTools.Models.Animation;  namespace NW4RTools {  	public class BrresWriter { @@ -53,6 +54,7 @@ namespace NW4RTools {  		// This code will also handle building the string table.  		#region Offset Data Storage +		#region Model Data Storage  		private Dictionary<Model, int> ModelOffsets;  		private Dictionary<ByteCode, int> BytecodeOffsets;  		private Dictionary<Node, int> NodeOffsets; @@ -78,9 +80,118 @@ namespace NW4RTools {  			public int PairingLookupByTexture;  			public int BlockSize;  			// Duplicate shader structs are removed from the model, so they require special tracking -			public List<Shader> UniqueShaders; +			public List<Shader> UniqueShaders;
 +			public Dictionary<Material, int> MaterialSizes;
 +
 +			public ModelCalcInfo() {
 +				UniqueShaders = new List<Shader>();
 +				MaterialSizes = new Dictionary<Material,int>();
 +			}  		}  		private Dictionary<Model, ModelCalcInfo> ModelCalcInfos; +		#endregion + +		#region Character Animation Data Storage +		private class ChrAnmNodeInfo { +			public CharacterAnim.Flags Flags; +		} + +		private class ChrAnmInfo { +			public KeyframeWriterInfo KFInfo; +			public Dictionary<CharacterAnim.Node, ChrAnmNodeInfo> NodeInfo; +			public Dictionary<CharacterAnim.Node, int> NodeOffsets; +			public int BlockSize; + +			public ChrAnmInfo() { +				KFInfo = new KeyframeWriterInfo(); +				NodeInfo = new Dictionary<CharacterAnim.Node, ChrAnmNodeInfo>(); +				NodeOffsets = new Dictionary<CharacterAnim.Node, int>(); +			} +		} +		private Dictionary<CharacterAnim, ChrAnmInfo> ChrAnmInfos; +		private Dictionary<CharacterAnim, int> ChrAnmOffsets; +		#endregion + +		#region Color Animation Data Storage +		private class ClrAnmInfo { +			public Dictionary<ColorAnim.Node, int> NodeOffsets; +			public int BlockSize; + +			public List<Color[]> UsedArrays; +			public List<int> UsedArrayPositions; + +			public ClrAnmInfo() { +				NodeOffsets = new Dictionary<ColorAnim.Node, int>(); +				UsedArrays = new List<Color[]>(); +				UsedArrayPositions = new List<int>(); +			} +		} +		private Dictionary<ColorAnim, ClrAnmInfo> ClrAnmInfos; +		private Dictionary<ColorAnim, int> ClrAnmOffsets; +		#endregion + +		#region Texture SRT Animation Data Storage +		private class TexSRTAnmNodeInfo { +			public int[] TextureOffsets; +			public int[] IndirectTextureOffsets;
 +
 +			public TexSRTAnmNodeInfo() {
 +				TextureOffsets = new int[8];
 +				IndirectTextureOffsets = new int[3];
 +			} +		} + +		private class TexSRTAnmInfo { +			public KeyframeWriterInfo KFInfo; +			public Dictionary<TextureSRTAnim.Node, TexSRTAnmNodeInfo> NodeInfo; +			public Dictionary<TextureSRTAnim.Node, int> NodeOffsets; +			public int BlockSize; + +			public TexSRTAnmInfo() { +				KFInfo = new KeyframeWriterInfo(); +				NodeInfo = new Dictionary<TextureSRTAnim.Node,TexSRTAnmNodeInfo>(); +				NodeOffsets = new Dictionary<TextureSRTAnim.Node, int>(); +			} +		} +		private Dictionary<TextureSRTAnim, TexSRTAnmInfo> TexSRTAnmInfos; +		private Dictionary<TextureSRTAnim, int> TexSRTAnmOffsets; +		#endregion + +		private class KeyframeWriterInfo { +			public Dictionary<Pair<object, KeyframeAnim>, int> KeyframeAnimOffsets; +			public List<Pair<object, KeyframeAnim>> KeyframeAnims; + +			public KeyframeWriterInfo() { +				KeyframeAnimOffsets = new Dictionary<Pair<object, KeyframeAnim>, int>(); +				KeyframeAnims = new List<Pair<object, KeyframeAnim>>(); +			} + +			public void Add(KeyframeAnim anim) { +				Add(new Pair<object, KeyframeAnim>(null, anim)); +			} + +			public void Add(object obj, KeyframeAnim anim) { +				Add(new Pair<object, KeyframeAnim>(obj, anim)); +			} + +			public void Add(Pair<object, KeyframeAnim> pair) { +				if (pair.two.IsConstant) +					return; +				if (KeyframeAnimOffsets.ContainsKey(pair)) +					return; + +				KeyframeAnims.Add(pair); +				KeyframeAnimOffsets.Add(pair, -1); +			} + +			public int GetPosition(KeyframeAnim anim) { +				return KeyframeAnimOffsets[new Pair<object, KeyframeAnim>(null, anim)]; +			} + +			public int GetPosition(object obj, KeyframeAnim anim) { +				return KeyframeAnimOffsets[new Pair<object, KeyframeAnim>(obj, anim)]; +			} +		}  		private Dictionary<Texture, int> TextureOffsets;  		private Dictionary<Texture, int> TextureDataOffsets; @@ -130,6 +241,15 @@ namespace NW4RTools {  				case "Textures(NW4R)":  					CalculateTextures();  					break; +				case "AnmChr(NW4R)": +					CalculateAnmChr(); +					break; +				case "AnmClr(NW4R)": +					CalculateAnmClr(); +					break; +				case "AnmTexSrt(NW4R)": +					CalculateAnmTexSrt(); +					break;  				default:  					Debug.Send("[[ UNIMPLEMENTED {0} ]]", name);  					break; @@ -241,7 +361,6 @@ namespace NW4RTools {  				CalculateBytecode(model);  				CalculateNodes(model);  				CalculateMaterials(model); -				calcInfo.UniqueShaders = new List<Shader>();  				CalculateShaders(model);  				CalculateShapes(model);  				CalculateVtxPosData(model); @@ -358,6 +477,10 @@ namespace NW4RTools {  				LogPosition("Material Display Lists: " + kv.Key);  				MaterialDLOffsets[kv.Value] = CurrentPos;  				CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; + +				// Sized!
 +				int size = CurrentPos - MaterialOffsets[kv.Value];
 +				ModelCalcInfos[m].MaterialSizes[kv.Value] = size;  			}  		} @@ -549,8 +672,363 @@ namespace NW4RTools {  		}  		#endregion +		#region Character Animation Calculation +		private void CalculateAnmChr() { +			ChrAnmOffsets = new Dictionary<CharacterAnim, int>(); +			ChrAnmInfos = new Dictionary<CharacterAnim, ChrAnmInfo>(); + +			var chrAnmDict = File.GetGroup<CharacterAnim>("AnmChr(NW4R)"); + +			foreach (var kv in chrAnmDict) { +				AddString(kv.Key); +				AlignCalcPos(0x20); +				CharacterAnim anim = kv.Value; + +				LogPosition("Character Anim: " + kv.Key); +				ChrAnmOffsets.Add(anim, CurrentPos); + +				CurrentPos += 0x20; +				LogPosition("Character Anim Info Struct: " + kv.Key); + +				CurrentPos += 0xC; +				LogPosition("Character Anim Node Dictionary: " + kv.Key); +				CurrentPos += GetSizeForResDict(anim.Nodes.Count); + +				var anminfo = new ChrAnmInfo(); +				ChrAnmInfos.Add(anim, anminfo); + +				// Now size each node +				foreach (var nodepair in anim.Nodes) { +					AddString(nodepair.Key); +					CharacterAnim.Node node = nodepair.Value; + +					anminfo.NodeOffsets.Add(node, CurrentPos); + +					LogPosition("Character Anim Node: " + nodepair.Key); +					CurrentPos += 8; + +					var nodeinfo = new ChrAnmNodeInfo(); +					anminfo.NodeInfo.Add(node, nodeinfo); + +					nodeinfo.Flags = CharacterAnim.Flags.AlwaysSet | (node.Flags & CharacterAnim.Flags.NonComputableFlags); + +					if (node.ScaleUseModel) +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleUseModel; +					if (node.RotateUseModel) +						nodeinfo.Flags |= CharacterAnim.Flags.RotateUseModel; +					if (node.TranslateUseModel) +						nodeinfo.Flags |= CharacterAnim.Flags.TranslateUseModel; + +					// Scale +					if (node.ScaleX.IsConstWith(1) && node.ScaleY.IsConstWith(1) && node.ScaleZ.IsConstWith(1)) { +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleOne; +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; +					} else { +						nodeinfo.Flags |= CharacterAnim.Flags.CalcScale; + +						anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleX); + +						if (node.ScaleX.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; +						CurrentPos += 4; + +						if (node.ScaleX.Equals(node.ScaleY) && node.ScaleX.Equals(node.ScaleZ)) { +							nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; +							if (node.ScaleX.IsConstant) { +								nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; +								nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; +							} +						} else { +							anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleY); +							anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleZ); + +							if (node.ScaleY.IsConstant) +								nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; +							if (node.ScaleZ.IsConstant) +								nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; +							CurrentPos += 8; +						} +					} + +					// Rotation +					if (node.RotateX.IsConstWith(0) && node.RotateY.IsConstWith(0) && node.RotateZ.IsConstWith(0)) { +						nodeinfo.Flags |= CharacterAnim.Flags.RotateZero; +						nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; +					} else { +						nodeinfo.Flags |= CharacterAnim.Flags.CalcRotate; + +						anminfo.KFInfo.Add(node.RotateFormat, node.RotateX); +						anminfo.KFInfo.Add(node.RotateFormat, node.RotateY); +						anminfo.KFInfo.Add(node.RotateFormat, node.RotateZ); + +						if (node.RotateX.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; +						if (node.RotateY.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; +						if (node.RotateZ.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; +						CurrentPos += 12; +					} + +					// Translation +					if (node.TranslateX.IsConstWith(0) && node.TranslateY.IsConstWith(0) && node.TranslateZ.IsConstWith(0)) { +						nodeinfo.Flags |= CharacterAnim.Flags.TranslateZero; +						nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; +						nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; +					} else { +						nodeinfo.Flags |= CharacterAnim.Flags.CalcTranslate; + +						anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateX); +						anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateY); +						anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateZ); + +						if (node.TranslateX.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; +						if (node.TranslateY.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; +						if (node.TranslateZ.IsConstant) +							nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; +						CurrentPos += 12; +					} + + +					if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZero) != 0 && +					    (nodeinfo.Flags & CharacterAnim.Flags.TranslateZero) != 0) +						nodeinfo.Flags |= CharacterAnim.Flags.RtZero; + +					if ((nodeinfo.Flags & CharacterAnim.Flags.RtZero) != 0 && +					    (nodeinfo.Flags & CharacterAnim.Flags.ScaleOne) != 0) +						nodeinfo.Flags |= CharacterAnim.Flags.Identity; + +					nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.ScaleFormat << 25); +					nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.RotateFormat << 27); +					nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.TranslateFormat << 30); +				} + + +				// And do all keyframes +				foreach (var pair in anminfo.KFInfo.KeyframeAnims) { +					CharacterAnim.RotateFormatType format = (CharacterAnim.RotateFormatType)pair.one; +					KeyframeAnim kf = pair.two; + +					LogPosition("Keyframes"); +					anminfo.KFInfo.KeyframeAnimOffsets[pair] = CurrentPos; + +					switch (format) { +					case CharacterAnim.RotateFormatType.Data32: +						CurrentPos += 8 + (kf.Keyframes.Length * 4); +						break; +					case CharacterAnim.RotateFormatType.Data48: +						CurrentPos += 8 + (kf.Keyframes.Length * 6); +						break; +					case CharacterAnim.RotateFormatType.Data96: +						CurrentPos += 8 + (kf.Keyframes.Length * 12); +						break; +					case CharacterAnim.RotateFormatType.DataUnk4: +						CurrentPos += 8 + (kf.Values.Length); +						break; +					case CharacterAnim.RotateFormatType.DataUnk5: +						CurrentPos += 8 + (kf.Values.Length * 2); +						break; +					case CharacterAnim.RotateFormatType.DataUnk6: +						CurrentPos += 4 + (kf.FloatValues.Length * 4); +						break; +					default: +						throw new NotImplementedException(); +					} + +					AlignCalcPos(4); +				} + + +				anminfo.BlockSize = CurrentPos - ChrAnmOffsets[anim]; +			} +		} +		#endregion + +		#region Color Animation Calculation +		private void CalculateAnmClr() { +			ClrAnmOffsets = new Dictionary<ColorAnim, int>(); +			ClrAnmInfos = new Dictionary<ColorAnim, ClrAnmInfo>(); + +			var clrAnmDict = File.GetGroup<ColorAnim>("AnmClr(NW4R)"); + +			foreach (var kv in clrAnmDict) { +				AddString(kv.Key); +				ColorAnim anim = kv.Value; + +				LogPosition("Color Anim: " + kv.Key); +				ClrAnmOffsets.Add(anim, CurrentPos); + +				CurrentPos += 0x20; +				LogPosition("Color Anim Info Struct: " + kv.Key); + +				CurrentPos += 0x8; +				LogPosition("Color Anim Node Dictionary: " + kv.Key); +				CurrentPos += GetSizeForResDict(anim.Nodes.Count); + +				var anminfo = new ClrAnmInfo(); +				ClrAnmInfos.Add(anim, anminfo); + +				// Now size each node +				foreach (var nodepair in anim.Nodes) { +					AddString(nodepair.Key); +					ColorAnim.Node node = nodepair.Value; + +					anminfo.NodeOffsets.Add(node, CurrentPos); + +					LogPosition("Color Anim Node: " + nodepair.Key); +					CurrentPos += 8; + +					for (int i = 0; i < node.Elements.Length; i++) { +						if (node.Elements[i].Exists) { +							CurrentPos += 8; +							if (!node.Elements[i].IsConstant) { +								// Add the array +								// But check to make sure it's not already there +								bool wasFound = false; + +								foreach (var arr in anminfo.UsedArrays) { +									if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { +										wasFound = true; +										break; +									} +								} + +								if (!wasFound) +									anminfo.UsedArrays.Add(node.Elements[i].Colors); +							} +						} +					} +				} + +				// Finally, add every array +				foreach (var arr in anminfo.UsedArrays) { +					anminfo.UsedArrayPositions.Add(CurrentPos); +					CurrentPos += (arr.Length * 4); +				} + +				anminfo.BlockSize = CurrentPos - ClrAnmOffsets[anim]; +			} +		} +		#endregion + +		#region Texture SRT Animation Calculation +		private void CalculateAnmTexSrt() { +			TexSRTAnmOffsets = new Dictionary<TextureSRTAnim, int>(); +			TexSRTAnmInfos = new Dictionary<TextureSRTAnim, TexSRTAnmInfo>(); + +			var chrAnmDict = File.GetGroup<TextureSRTAnim>("AnmTexSrt(NW4R)"); + +			foreach (var kv in chrAnmDict) { +				AddString(kv.Key); +				AlignCalcPos(0x20); +				TextureSRTAnim anim = kv.Value; + +				LogPosition("TextureSRT Anim: " + kv.Key); +				TexSRTAnmOffsets.Add(anim, CurrentPos); + +				CurrentPos += 0x20; +				LogPosition("TextureSRT Anim Info Struct: " + kv.Key); + +				CurrentPos += 0xC; +				LogPosition("TextureSRT Anim Node Dictionary: " + kv.Key); +				CurrentPos += GetSizeForResDict(anim.Nodes.Count); + +				var anminfo = new TexSRTAnmInfo(); +				TexSRTAnmInfos.Add(anim, anminfo);
 +
 +				// Now size each node +				foreach (var nodepair in anim.Nodes) { +					AddString(nodepair.Key); +					TextureSRTAnim.Node node = nodepair.Value; + +					anminfo.NodeOffsets.Add(node, CurrentPos); + +					LogPosition("TextureSRT Anim Node: " + nodepair.Key); +					CurrentPos += 12; + +					var nodeinfo = new TexSRTAnmNodeInfo();
 +					anminfo.NodeInfo.Add(node, nodeinfo);
 +
 +					// Figure out what anims we're using
 +					for (int i = 0; i < 8; i++)
 +						if (node.Textures[i].Exists)
 +							CurrentPos += 4;
 +					for (int i = 0; i < 3; i++)
 +						if (node.IndirectTextures[i].Exists)
 +							CurrentPos += 4;
 +
 +					for (int i = 0; i < 8; i++) {
 +						if (node.Textures[i].Exists) {
 +							nodeinfo.TextureOffsets[i] = CurrentPos;
 +							CalculateAnmTexSrtElement(anminfo.KFInfo, node.Textures[i]);
 +						}
 +					}
 +
 +					for (int i = 0; i < 3; i++) {
 +						if (node.IndirectTextures[i].Exists) {
 +							nodeinfo.IndirectTextureOffsets[i] = CurrentPos;
 +							CalculateAnmTexSrtElement(anminfo.KFInfo, node.IndirectTextures[i]);
 +						}
 +					} +				} +				CalculateKeyframeData(anminfo.KFInfo); +				anminfo.BlockSize = CurrentPos - TexSRTAnmOffsets[anim]; +			} +		}
 +
 +		private void CalculateAnmTexSrtElement(KeyframeWriterInfo kfinfo, TextureSRTAnim.Element elem) {
 +			CurrentPos += 4;
 +
 +			if (!(elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1))) {
 +				// Scale is not all 1
 +				kfinfo.Add(elem.ScaleS);
 +				CurrentPos += 4;
 +
 +				if (elem.ScaleT != elem.ScaleS) {
 +					// Not uniform
 +					kfinfo.Add(elem.ScaleT);
 +					CurrentPos += 4;
 +				}
 +			}
 +
 +			if (!elem.Rotate.IsConstWith(0)) {
 +				// Rotate is not zero
 +				kfinfo.Add(elem.Rotate);
 +				CurrentPos += 4;
 +			}
 +
 +			if (!(elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0))) {
 +				// Translate is not all zero
 +				kfinfo.Add(elem.TransS);
 +				kfinfo.Add(elem.TransT);
 +				CurrentPos += 8;
 +			}
 +		} +		#endregion
 +
 +		#region Keyframe Animation Calculation
 +		private void CalculateKeyframeData(KeyframeWriterInfo kfinfo) {
 +			foreach (var pair in kfinfo.KeyframeAnims) {
 +				var anim = pair.two;
 +
 +				kfinfo.KeyframeAnimOffsets[pair] = CurrentPos;
 +
 +				CurrentPos += 8 + (anim.Keyframes.Length * 12);
 +			}
 +		}
 +		#endregion
 +
 +
  		private void AlignCalcPos(int alignTo) {  			if ((CurrentPos & (alignTo - 1)) == 0)  				return; @@ -594,6 +1072,15 @@ namespace NW4RTools {  					case "Textures(NW4R)":  						WriteResDict<Texture>(kv.Value as ResDict<Texture>, TextureOffsets);  						break; +					case "AnmChr(NW4R)": +						WriteResDict<CharacterAnim>(kv.Value as ResDict<CharacterAnim>, ChrAnmOffsets); +						break; +					case "AnmClr(NW4R)": +						WriteResDict<ColorAnim>(kv.Value as ResDict<ColorAnim>, ClrAnmOffsets); +						break; +					case "AnmTexSrt(NW4R)": +						WriteResDict<TextureSRTAnim>(kv.Value as ResDict<TextureSRTAnim>, TexSRTAnmOffsets); +						break;  					default:  						// for testing, boo  						Output.AddPadding(GetSizeForResDict((kv.Value as ICollection).Count)); @@ -613,6 +1100,15 @@ namespace NW4RTools {  					case "Textures(NW4R)":  						WriteTextures();  						break; +					case "AnmChr(NW4R)": +						WriteAnmChr(); +						break; +					case "AnmClr(NW4R)": +						WriteAnmClr(); +						break; +					case "AnmTexSrt(NW4R)": +						WriteAnmTexSrt(); +						break;  					default:  						Debug.Send("UNHANDLED RESOURCE TYPE: {0}", kv.Key);  						break; @@ -637,7 +1133,9 @@ namespace NW4RTools {  					Model model = kv.Value;  					var calcInfo = ModelCalcInfos[model]; -					// Base struct: magic 'MDL0', block size, version [currently 11], resfile offset +					Misc.Assert(startPos == ModelOffsets[model]); + +					// Base struct: magic 'MDL0', block size, version[currently 11], resfile offset  					Output.WriteUInt32(0x4D444C30);  					Output.WriteUInt32((uint)calcInfo.BlockSize);  					Output.WriteUInt32(11); @@ -680,6 +1178,7 @@ namespace NW4RTools {  					Output.WriteUInt32((uint)model.VertexCount);  					Output.WriteUInt32((uint)model.TriangleCount);  					Output.WriteUInt32(0); +					// TODO: This is not right, fix it  					Output.WriteUInt32((uint)model.MatrixIDtoNodeID.Length);  					Output.WriteByte(model.UsesNrmMtxArray ? (byte)1 : (byte)0);  					Output.WriteByte(model.UsesTexMtxArray ? (byte)1 : (byte)0); @@ -778,8 +1277,8 @@ namespace NW4RTools {  						var op4 = insn as ByteCode.DrawShapeInstruction;  						Output.WriteUInt16(op4.MaterialID);  						Output.WriteUInt16(op4.ShapeID); -						Output.WriteUInt16(op4.NodeID); -						Output.WriteByte(0); +						Output.WriteUInt16(op4.NodeID);
 +						Output.WriteByte(op4.Unk);  						break;  					case ByteCode.OpType.AssignMtxToNode: @@ -847,9 +1346,9 @@ namespace NW4RTools {  				Material mat = kv.Value;  				int startPos = Output.Position; -				// size, model offset, name offset, index -				// is size static, or does it change? must verify -				Output.WriteUInt32(0x5E8); +				// size, model offset, name offset, index
 +				var modelInfo = ModelCalcInfos[m]; +				Output.WriteUInt32((UInt32)modelInfo.MaterialSizes[mat]);  				Output.WriteInt32(ModelOffsets[m] - startPos);  				Output.WriteInt32(StringPositions[kv.Key] - startPos);  				Output.WriteUInt32((uint)currentIndex); @@ -1233,7 +1732,9 @@ namespace NW4RTools {  					int startPos = Output.Position;  					Texture tex = kv.Value; -					// Base struct: magic 'TEX0', block size, version [currently 11], resfile offset +					Misc.Assert(startPos == TextureOffsets[tex]); + +					// Base struct: magic 'TEX0', block size, version[currently 3], resfile offset  					Output.WriteUInt32(0x54455830);  					Output.WriteUInt32((uint)(0x40 + tex.GetDataSize()));  					Output.WriteUInt32(3); @@ -1257,13 +1758,459 @@ namespace NW4RTools {  					Output.AlignTo(0x20);  					for (int i = 0; i < tex.Images.Length; i++) +#if DEV_DONT_MODIFY_TEXTURE +						Output.WriteBytes(tex.ImageData[i]); +#else  						Output.WriteBytes(tex.ExportData(i)); +#endif +				} +			} +		} +		#endregion + +		#region Character Animation Writing +		private void WriteAnmChr() { +			foreach (var kv in File.GetGroup<CharacterAnim>("AnmChr(NW4R)")) { +				using (var c = Debug.Push("Character Animation: {0}", kv.Key)) { +					Output.AlignTo(0x20); + +					int startPos = Output.Position; +					CharacterAnim anm = kv.Value; +					var info = ChrAnmInfos[anm]; + +					Misc.Assert(startPos == ChrAnmOffsets[anm]); + +					// Base struct: magic 'CHR0', block size, version[currently 5], resfile offset +					Output.WriteUInt32(0x43485230); +					Output.WriteUInt32((UInt32)info.BlockSize); +					Output.WriteUInt32(5); +					Output.WriteInt32(-startPos); + +					// Data offset, name offset, other crap +					Output.WriteInt32(0x2C); +					Output.WriteUInt32(0); +					Output.WriteInt32(StringPositions[kv.Key] - startPos); +					Output.WriteUInt32(0); + +					Output.WriteUInt16((UInt16)anm.FrameCount); +					Output.WriteUInt16((UInt16)anm.Nodes.Count); + +					Output.WriteUInt32(anm.Loop ? 1U : 0U); +					Output.WriteUInt32((UInt32)anm.ScaleMode); + +					WriteResDict<CharacterAnim.Node>(anm.Nodes, info.NodeOffsets); + +					WriteAnmChrNodes(anm); +					WriteAnmChrKeyframes(anm); +				} +			} +		} + +		private void WriteAnmChrNodes(CharacterAnim anm) { +			var info = ChrAnmInfos[anm]; + +			foreach (var kv in anm.Nodes) { +				using (var c = Debug.Push("Element: {0}", kv.Key)) { +					int startPos = Output.Position; +					CharacterAnim.Node node = kv.Value; +					ChrAnmNodeInfo nodeinfo = info.NodeInfo[node]; + +					Misc.Assert(startPos == info.NodeOffsets[node]); + +					Output.WriteInt32(StringPositions[kv.Key] - startPos); +					Output.WriteUInt32((UInt32)nodeinfo.Flags); + +					if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleXNotExist) == 0) { +						if (node.ScaleX.IsConstant) +							Output.WriteFloat(node.ScaleX.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleX) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleYNotExist) == 0) { +						if (node.ScaleY.IsConstant) +							Output.WriteFloat(node.ScaleY.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleY) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleZNotExist) == 0) { +						if (node.ScaleZ.IsConstant) +							Output.WriteFloat(node.ScaleZ.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleZ) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.RotateXNotExist) == 0) { +						if (node.RotateX.IsConstant) +							Output.WriteFloat(node.RotateX.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateX) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.RotateYNotExist) == 0) { +						if (node.RotateY.IsConstant) +							Output.WriteFloat(node.RotateY.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateY) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZNotExist) == 0) { +						if (node.RotateZ.IsConstant) +							Output.WriteFloat(node.RotateZ.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateZ) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateXNotExist) == 0) { +						if (node.TranslateX.IsConstant) +							Output.WriteFloat(node.TranslateX.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateX) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateYNotExist) == 0) { +						if (node.TranslateY.IsConstant) +							Output.WriteFloat(node.TranslateY.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateY) - startPos); +					} + +					if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateZNotExist) == 0) { +						if (node.TranslateZ.IsConstant) +							Output.WriteFloat(node.TranslateZ.ConstValue); +						else +							Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateZ) - startPos); +					} +				} +			} +		} + +		private void WriteAnmChrKeyframes(CharacterAnim anm) { +			var anminfo = ChrAnmInfos[anm]; +			foreach (var kv in anminfo.KFInfo.KeyframeAnimOffsets) { +				var format = (CharacterAnim.RotateFormatType)kv.Key.one; +				var kf = kv.Key.two; +				var expectedOffset = kv.Value; + +				Misc.Assert(Output.Position == expectedOffset); + +				if (format == CharacterAnim.RotateFormatType.Data32 || +				    format == CharacterAnim.RotateFormatType.Data48 || +				    format == CharacterAnim.RotateFormatType.Data96) { +					// Header for FVS +					Output.WriteUInt16((UInt16)kf.Keyframes.Length); +					Output.AddPadding(2); + +					float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes [0].Frame; +					float invKeyFrameRange = (1.0f / delta); +					Output.WriteFloat(invKeyFrameRange);  				} + +				switch (format) { +				case CharacterAnim.RotateFormatType.Data32: +					for (int i = 0; i < kf.Keyframes.Length; i++) { +						Output.WriteUInt32( +							(((UInt32)kf.Keyframes[i].Frame) << 24) | +							((((UInt32)kf.Keyframes[i].Value) & 0xFFF) << 12) | +							(((UInt32)(kf.Keyframes[i].Slope * 32.0f)) & 0xFFF)); +					} +					break; +				case CharacterAnim.RotateFormatType.Data48: +					for (int i = 0; i < kf.Keyframes.Length; i++) { +						Output.WriteInt16((Int16)(kf.Keyframes[i].Frame * 32.0f)); +						Output.WriteUInt16((UInt16)(kf.Keyframes[i].Value / (360.0f / 0x10000))); +						Output.WriteInt16((Int16)(kf.Keyframes[i].Slope * 256.0f)); +					} +					break; +				case CharacterAnim.RotateFormatType.Data96: +					for (int i = 0; i < kf.Keyframes.Length; i++) { +						Output.WriteFloat(kf.Keyframes[i].Frame); +						Output.WriteFloat(kf.Keyframes[i].Value); +						Output.WriteFloat(kf.Keyframes[i].Slope); +					} +					break; +				default: +					throw new NotImplementedException(); +				} + +				Output.AlignTo(4);  			}  		}  		#endregion -		#region ResDicts +		#region Color Animation Writing +		private void WriteAnmClr() { +			foreach (var kv in File.GetGroup<ColorAnim>("AnmClr(NW4R)")) { +				using (var c = Debug.Push("Color Animation: {0}", kv.Key)) { +					int startPos = Output.Position; +					ColorAnim anm = kv.Value; +					var info = ClrAnmInfos[anm]; + +					Misc.Assert(startPos == ClrAnmOffsets[anm]); + +					// Base struct: magic 'CLR0', block size, version[currently 5], resfile offset +					Output.WriteUInt32(0x434C5230); +					Output.WriteUInt32((UInt32)info.BlockSize); +					Output.WriteUInt32(4); +					Output.WriteInt32(-startPos); + +					// Data offset, name offset, other crap +					Output.WriteInt32(0x28); +					Output.WriteUInt32(0); +					Output.WriteInt32(StringPositions[kv.Key] - startPos); +					Output.WriteUInt32(0); + +					Output.WriteUInt16((UInt16)anm.FrameCount); +					Output.WriteUInt16((UInt16)anm.Nodes.Count); + +					Output.WriteUInt32(anm.Loop ? 1U : 0U); + +					WriteResDict<ColorAnim.Node>(anm.Nodes, info.NodeOffsets); + +					WriteAnmClrNodes(anm); +					WriteAnmClrArrays(anm); +				} +			} +		} + +		private void WriteAnmClrNodes(ColorAnim anm) { +			ClrAnmInfo info = ClrAnmInfos[anm]; + +			foreach (var kv in anm.Nodes) { +				using (var c = Debug.Push("Element: {0}", kv.Key)) { +					int startPos = Output.Position; +					ColorAnim.Node node = kv.Value; + +					Misc.Assert(startPos == info.NodeOffsets[node]); + +					Output.WriteInt32(StringPositions[kv.Key] - startPos); + +					UInt32 flags = 0; + +					for (int i = node.Elements.Length - 1; i >= 0; i--) { +						flags <<= 2; +						if (node.Elements[i].Exists) +							flags |= 1; +						if (node.Elements[i].IsConstant) +							flags |= 2; +					} + +					Output.WriteUInt32(flags); + +					for (int i = 0; i < node.Elements.Length; i++) { +						if (node.Elements[i].Exists) {
 +							Output.WriteUInt32(node.Elements[i].Mask); +							if (node.Elements[i].IsConstant) { +								Output.WriteColor(node.Elements[i].ConstValue); +							} else { +								// Search for the matching array +								int find = 0; +								bool wasFound = false; +								foreach (var arr in info.UsedArrays) { +									if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { +										wasFound = true; +										break; +									} +									find++; +								} + +								if (!wasFound) +									throw new Exception(); + +								int arrayPos = info.UsedArrayPositions[find]; +								Output.WriteInt32(arrayPos - Output.Position); +							} +						} +					} + +				} +			} +		} + +		private void WriteAnmClrArrays(ColorAnim anm) { +			var info = ClrAnmInfos[anm]; + +			int i = 0; +			foreach (var arr in info.UsedArrays) { +				Misc.Assert(info.UsedArrayPositions[i] == Output.Position); + +				for (int j = 0; j < arr.Length; j++) +					Output.WriteColor(arr[j]); + +				i++; +			} +		} +		#endregion
 +
 +		#region Texture SRT Animation Writing
 +		private void WriteAnmTexSrt() {
 +			foreach (var kv in File.GetGroup<TextureSRTAnim>("AnmTexSrt(NW4R)")) {
 +				using (var c = Debug.Push("TextureSRT Animation: {0}", kv.Key)) {
 +					Output.AlignTo(0x20);
 +
 +					int startPos = Output.Position;
 +					TextureSRTAnim anm = kv.Value;
 +					var info = TexSRTAnmInfos[anm];
 +
 +					Misc.Assert(startPos == TexSRTAnmOffsets[anm]);
 +
 +					// Base struct: magic 'SRT0', block size, version[currently 5], resfile offset
 +					Output.WriteUInt32(0x53525430);
 +					Output.WriteUInt32((UInt32)info.BlockSize);
 +					Output.WriteUInt32(5);
 +					Output.WriteInt32(-startPos);
 +
 +					// Data offset, name offset, other crap
 +					Output.WriteInt32(0x2C);
 +					Output.WriteUInt32(0);
 +					Output.WriteInt32(StringPositions[kv.Key] - startPos);
 +					Output.WriteUInt32(0);
 +
 +					Output.WriteUInt16((UInt16)anm.FrameCount);
 +					Output.WriteUInt16((UInt16)anm.Nodes.Count);
 +
 +					Output.WriteUInt32((UInt32)anm.MatrixMode);
 +					Output.WriteUInt32(anm.Loop ? 1U : 0U);
 +
 +					WriteResDict<TextureSRTAnim.Node>(anm.Nodes, info.NodeOffsets);
 +
 +					WriteAnmTexSrtNodes(anm);
 +					WriteKeyframeData(info.KFInfo);
 +				}
 +			}
 +		}
 +
 +		private void WriteAnmTexSrtNodes(TextureSRTAnim anm) {
 +			var info = TexSRTAnmInfos[anm];
 +
 +			foreach (var kv in anm.Nodes) {
 +				using (var c = Debug.Push("Element: {0}", kv.Key)) {
 +					int startPos = Output.Position;
 +					TextureSRTAnim.Node node = kv.Value;
 +					TexSRTAnmNodeInfo nodeinfo = info.NodeInfo[node];
 +
 +					Misc.Assert(startPos == info.NodeOffsets[node]);
 +
 +					Output.WriteInt32(StringPositions[kv.Key] - startPos);
 +
 +					UInt32 flags = 0, indFlags = 0;
 +
 +					for (int i = 0; i < 8; i++)
 +						if (node.Textures[i].Exists)
 +							flags |= (UInt32)(1 << i);
 +					for (int i = 0; i < 3; i++)
 +						if (node.IndirectTextures[i].Exists)
 +							indFlags |= (UInt32)(1 << i);
 +
 +					Output.WriteUInt32(flags);
 +					Output.WriteUInt32(indFlags);
 +
 +					var elemsToAddLater = new List<TextureSRTAnim.Element>();
 +
 +					for (int i = 0; i < 8; i++)
 +						if (node.Textures[i].Exists) {
 +							Output.WriteInt32(nodeinfo.TextureOffsets[i] - startPos);
 +							elemsToAddLater.Add(node.Textures[i]);
 +						}
 +
 +					for (int i = 0; i < 3; i++)
 +						if (node.IndirectTextures[i].Exists) {
 +							Output.WriteInt32(nodeinfo.IndirectTextureOffsets[i] - startPos);
 +							elemsToAddLater.Add(node.IndirectTextures[i]);
 +						}
 +
 +					foreach (var elem in elemsToAddLater)
 +						WriteAnmTexSrtElement(elem, info.KFInfo);
 +				}
 +			}
 +		}
 +
 +		private void WriteAnmTexSrtElement(TextureSRTAnim.Element elem, KeyframeWriterInfo kfinfo) {
 +			UInt32 flags = 1;
 +
 +			int flagPos = Output.Position;
 +			Output.WriteUInt32(0); // placeholder!
 +
 +			if (elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1)) {
 +				flags |= 2 | 0x10 | 0x20 | 0x40;
 +			} else {
 +				WriteKeyframeAnim(elem.ScaleS, kfinfo);
 +				if (elem.ScaleS.IsConstant)
 +					flags |= 0x20;
 +
 +				if (elem.ScaleS == elem.ScaleT) {
 +					flags |= 0x10;
 +					if (elem.ScaleS.IsConstant)
 +						flags |= 0x40;
 +				} else {
 +					WriteKeyframeAnim(elem.ScaleT, kfinfo);
 +					if (elem.ScaleT.IsConstant)
 +						flags |= 0x40;
 +				}
 +			}
 +
 +			if (elem.Rotate.IsConstWith(0)) {
 +				flags |= 4 | 0x80;
 +			} else {
 +				WriteKeyframeAnim(elem.Rotate, kfinfo);
 +				if (elem.Rotate.IsConstant)
 +					flags |= 0x80;
 +			}
 +
 +			if (elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0)) {
 +				flags |= 8 | 0x100 | 0x200;
 +			} else {
 +				WriteKeyframeAnim(elem.TransS, kfinfo);
 +				WriteKeyframeAnim(elem.TransT, kfinfo);
 +
 +				if (elem.TransS.IsConstant)
 +					flags |= 0x100;
 +				if (elem.TransT.IsConstant)
 +					flags |= 0x200;
 +			}
 +
 +			// Replace the flags
 +			int savePos = Output.Position;
 +			Output.Seek(flagPos);
 +			Output.WriteUInt32(flags);
 +			Output.Seek(savePos);
 +		}
 +		#endregion
 +
 +		#region Keyframe Data Writing
 +		private void WriteKeyframeAnim(KeyframeAnim anim, KeyframeWriterInfo kfinfo) {
 +			if (anim.IsConstant)
 +				Output.WriteFloat(anim.ConstValue);
 +			else
 +				Output.WriteInt32(kfinfo.GetPosition(anim) - Output.Position);
 +		}
 +
 +		private void WriteKeyframeData(KeyframeWriterInfo kfinfo) {
 +			foreach (var pair in kfinfo.KeyframeAnims) {
 +				var anim = pair.two;
 +				var kf = pair.two;
 +				var expectedOffset = kfinfo.KeyframeAnimOffsets[pair];
 +
 +				Misc.Assert(expectedOffset == Output.Position);
 +
 +				Output.WriteUInt16((UInt16)kf.Keyframes.Length);
 +				Output.AddPadding(2);
 +
 +				float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes[0].Frame;
 +				float invKeyFrameRange = (1.0f / delta);
 +				Output.WriteFloat(invKeyFrameRange);
 +
 +				for (int i = 0; i < kf.Keyframes.Length; i++) {
 +					Output.WriteFloat(kf.Keyframes[i].Frame);
 +					Output.WriteFloat(kf.Keyframes[i].Value);
 +					Output.WriteFloat(kf.Keyframes[i].Slope);
 +				}
 +			}
 +		}
 +		#endregion
 +
 +		#region ResDicts
  		private struct RawDictEntry {  			public ushort Ref;  			public ushort Unk; diff --git a/NW4RTools/Misc.cs b/NW4RTools/Misc.cs index c42b185..118f164 100644 --- a/NW4RTools/Misc.cs +++ b/NW4RTools/Misc.cs @@ -16,6 +16,46 @@ namespace NW4RTools {  		public static uint AlignDown(uint val, uint to) {  			return val & ~(to - 1);  		} + + +		public static void Assert(bool condition) { +			if (!condition) +				throw new Exception("Assert failed"); +		} +		public static void Assert(bool condition, string format, params object[] args) { +			if (!condition) +				throw new Exception(String.Format(format, args)); +		} + + +		public static bool ArrayCompare<T>(T[] one, T[] two) { +			if (one == two) +				return true; +			if (one == null || two == null) +				return false; +			if (one.Length != two.Length) +				return false; + +			for (int i = 0; i < one.Length; i++) { +				if (!one[i].Equals(two[i])) +					return false; +			} + +			return true; +		} + + +		public static int ArrayHash<T>(T[] arr) { +			if (arr == null) +				return 0xEA7BEEF; + +			int hash = 0; + +			for (int i = 0; i < arr.Length; i++) +				hash ^= arr[i].GetHashCode(); + +			return hash; +		}  	}  } diff --git a/NW4RTools/Models/Animation/CharacterAnim.cs b/NW4RTools/Models/Animation/CharacterAnim.cs index dd496f6..72efd56 100644 --- a/NW4RTools/Models/Animation/CharacterAnim.cs +++ b/NW4RTools/Models/Animation/CharacterAnim.cs @@ -81,6 +81,8 @@ namespace NW4RTools.Models.Animation {  			public STFormatType ScaleFormat;  			public STFormatType TranslateFormat;  			public RotateFormatType RotateFormat; + +			public bool ScaleUseModel, RotateUseModel, TranslateUseModel;  		}  		public CharacterAnim() { diff --git a/NW4RTools/Models/Animation/Shared.cs b/NW4RTools/Models/Animation/Shared.cs index f87bea6..62cca94 100644 --- a/NW4RTools/Models/Animation/Shared.cs +++ b/NW4RTools/Models/Animation/Shared.cs @@ -28,5 +28,45 @@ namespace NW4RTools.Models.Animation {  			Values = null;  			FloatValues = null;  		} + + +		public bool IsConstWith(float cv) { +			return (IsConstant && (ConstValue == cv)); +		} + + +		public override bool Equals(object obj) { +			return obj is KeyframeAnim && this == (KeyframeAnim)obj; +		} + +		public override int GetHashCode() { +			return +				IsConstant.GetHashCode() ^ +					ConstValue.GetHashCode() ^ +					BaseValue.GetHashCode() ^ +					Multiplier.GetHashCode() ^ +					Misc.ArrayHash(Keyframes) ^ +					Misc.ArrayHash(Values) ^ +					Misc.ArrayHash(FloatValues); +		} + +		public static bool operator ==(KeyframeAnim x, KeyframeAnim y) { +			return +				(x.IsConstant == y.IsConstant) && +				(x.ConstValue == y.ConstValue) && +					(x.BaseValue == y.BaseValue) && +					(x.Multiplier == y.Multiplier) && +			Misc.ArrayCompare(x.Keyframes, y.Keyframes) && +					Misc.ArrayCompare(x.Values, y.Values) && +					Misc.ArrayCompare(x.FloatValues, y.FloatValues); +		} + +		public static bool operator !=(KeyframeAnim x, KeyframeAnim y) { +			return !(x == y); +		} + +		public void Dump() { +			Console.WriteLine("IsC:{0} CV:{1} KF:{2} BV:{3} M:{4} V:{5} FV:{6}", IsConstant, ConstValue, Keyframes, BaseValue, Multiplier, Values, FloatValues); +		}  	}  } diff --git a/NW4RTools/Models/ByteCode.cs b/NW4RTools/Models/ByteCode.cs index 6df6dee..62968e9 100644..100755 --- a/NW4RTools/Models/ByteCode.cs +++ b/NW4RTools/Models/ByteCode.cs @@ -50,7 +50,8 @@ namespace NW4RTools.Models {  			public UInt16 MaterialID;  			public UInt16 ShapeID; -			public UInt16 NodeID; +			public UInt16 NodeID;
 +			public byte Unk;  		}  		// According to BrawlLib: in NodeMix, this is for "primary influences" diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj index bcdc413..50b4320 100644 --- a/NW4RTools/NW4RTools.csproj +++ b/NW4RTools/NW4RTools.csproj @@ -1,94 +1,89 @@ -<?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> -    <AllowUnsafeBlocks>true</AllowUnsafeBlocks> -  </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> -    <AllowUnsafeBlocks>true</AllowUnsafeBlocks> -  </PropertyGroup> -  <ItemGroup> -    <Reference Include="System" /> -    <Reference Include="System.Xml" /> -    <Reference Include="System.Drawing" /> -    <Reference Include="System.Core" /> -    <Reference Include="OpenTK"> -      <HintPath>..\ExternalLibs\OpenTK.dll</HintPath> -    </Reference> -  </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" /> -    <Compile Include="Models\Shader.cs" /> -    <Compile Include="Models\Shape.cs" /> -    <Compile Include="Models\TextureInfo.cs" /> -    <Compile Include="Enums.cs" /> -    <Compile Include="DisplayList.cs" /> -    <Compile Include="VertexSettings.cs" /> -    <Compile Include="Util\collada_schema_1_4.cs" /> -    <Compile Include="Texture.cs" /> -    <Compile Include="Misc.cs" /> -    <Compile Include="Models\OpenGL\GLModel.cs" /> -    <Compile Include="Models\OpenGL\GLTexture.cs" /> -    <Compile Include="Models\OpenGL\GLDisplayList.cs" /> -    <Compile Include="BrresWriter.cs" /> -    <Compile Include="OutputStream.cs" /> -    <Compile Include="Models\Animation\ColorAnim.cs" /> -    <Compile Include="Models\Animation\CharacterAnim.cs" /> -    <Compile Include="Models\Animation\TextureSRTAnim.cs" /> -    <Compile Include="Util\NVDXT.cs" /> -    <Compile Include="ColladaExporter.cs" /> -    <Compile Include="ObjExporter.cs" /> -    <Compile Include="ObjImporter.cs" /> -    <Compile Include="DisplayListWriter.cs" /> -    <Compile Include="Models\Animation\Shared.cs" /> -  </ItemGroup> -  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> -  <ItemGroup> -    <Folder Include="Models\" /> -    <Folder Include="Util\" /> -    <Folder Include="Models\OpenGL\" /> -    <Folder Include="Models\Animation\" /> -  </ItemGroup> -  <ItemGroup> -    <None Include="OpenTK.dll.config"> -      <CopyToOutputDirectory>Always</CopyToOutputDirectory> -    </None> -  </ItemGroup> -</Project> +<?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 DEV_DONT_MODIFY_TEXTURE</DefineConstants>
 +    <ErrorReport>prompt</ErrorReport>
 +    <WarningLevel>4</WarningLevel>
 +    <ConsolePause>false</ConsolePause>
 +    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 +  </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>
 +    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
 +    <DefineConstants>DEV_DONT_MODIFY_TEXTURE</DefineConstants>
 +  </PropertyGroup>
 +  <ItemGroup>
 +    <Reference Include="System" />
 +    <Reference Include="System.Xml" />
 +    <Reference Include="System.Drawing" />
 +    <Reference Include="System.Core" />
 +    <Reference Include="OpenTK">
 +      <HintPath>..\ExternalLibs\OpenTK.dll</HintPath>
 +    </Reference>
 +  </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" />
 +    <Compile Include="Models\Shader.cs" />
 +    <Compile Include="Models\Shape.cs" />
 +    <Compile Include="Models\TextureInfo.cs" />
 +    <Compile Include="Enums.cs" />
 +    <Compile Include="DisplayList.cs" />
 +    <Compile Include="VertexSettings.cs" />
 +    <Compile Include="Util\collada_schema_1_4.cs" />
 +    <Compile Include="Texture.cs" />
 +    <Compile Include="Misc.cs" />
 +    <Compile Include="Models\OpenGL\GLModel.cs" />
 +    <Compile Include="Models\OpenGL\GLTexture.cs" />
 +    <Compile Include="Models\OpenGL\GLDisplayList.cs" />
 +    <Compile Include="BrresWriter.cs" />
 +    <Compile Include="OutputStream.cs" />
 +    <Compile Include="Models\Animation\ColorAnim.cs" />
 +    <Compile Include="Models\Animation\CharacterAnim.cs" />
 +    <Compile Include="Models\Animation\TextureSRTAnim.cs" />
 +    <Compile Include="Util\NVDXT.cs" />
 +    <Compile Include="ColladaExporter.cs" />
 +    <Compile Include="ObjExporter.cs" />
 +    <Compile Include="ObjImporter.cs" />
 +    <Compile Include="DisplayListWriter.cs" />
 +    <Compile Include="Models\Animation\Shared.cs" />
 +  </ItemGroup>
 +  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 +  <ItemGroup>
 +    <None Include="OpenTK.dll.config">
 +      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
 +    </None>
 +  </ItemGroup>
 +</Project>
\ No newline at end of file diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs index a328648..f2cf231 100644 --- a/NW4RTools/Texture.cs +++ b/NW4RTools/Texture.cs @@ -63,6 +63,9 @@ namespace NW4RTools {  		} +#if DEV_DONT_MODIFY_TEXTURE +		public byte[][] ImageData; +#endif  		public Bitmap[] Images; @@ -95,7 +98,11 @@ namespace NW4RTools {  		}  		public int GetDataSize(int imageID) { +#if DEV_DONT_MODIFY_TEXTURE +			return ImageData[imageID].Length; +#else  			return GetDataSize(Images[imageID].Width, Images[imageID].Height, Format); +#endif  		} @@ -104,9 +111,9 @@ namespace NW4RTools {  			int blkWidth = info.TexelWidth;  			int blkHeight = info.TexelHeight; -			int width = Images[imageID].Width; -			int height = Images[imageID].Height; -			var bits = Images[imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); +			int width = Images [imageID].Width; +			int height = Images [imageID].Height; +			var bits = Images [imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);  			var data = new OutputStream(ByteEndian.BigEndian); @@ -238,7 +245,7 @@ namespace NW4RTools {  							for (int iBlockX = 0; iBlockX < 2; iBlockX++) {  								var block = Util.NVDXT.compressDXT1a((Util.NVDXT.ARGBPixel*)bits.Scan0,  									x + (iBlockX * 4), y + (iBlockY * 4), -									bits.Width, bits.Height); +									(bits.Stride / 4), bits.Height);  								data.WriteUInt16(block.Color0);  								data.WriteUInt16(block.Color1); diff --git a/NW4RTools/Types.cs b/NW4RTools/Types.cs index 619303e..9bfa0db 100644 --- a/NW4RTools/Types.cs +++ b/NW4RTools/Types.cs @@ -1,6 +1,16 @@  using System;  namespace NW4RTools { +	public struct Pair<TOne, TTwo> { +		public TOne one; +		public TTwo two; + +		public Pair(TOne _one, TTwo _two) { +			one = _one; +			two = _two; +		} +	} +  	public enum ByteEndian {  		LittleEndian,  		BigEndian  | 
