/// <summary> /// Creates a managed animation entry given a native entry. /// </summary> /// <param name="mtpFileAddress">Address of the first byte of the MTP file in memory.</param> /// <param name="entry">The native animation entry.</param> public static unsafe ManagedAnimationEntry FromAnimationEntry(byte *mtpFileAddress, AnimationEntry entry) { var managedAnimationEntry = new ManagedAnimationEntry(); byte * fileNamePtr = entry.FileNamePtr + mtpFileAddress; byte * fileDataPtr = entry.FileDataPtr + mtpFileAddress; PropertyTuple *tuplePtr = (PropertyTuple *)(entry.PropertyTuplePtr + mtpFileAddress); // Get File Name managedAnimationEntry.FileName = String.Win1252Encoder.GetString(fileNamePtr, String.Strlen(fileNamePtr)); // Get File Data var header = MotionHeader.FromPointer(fileDataPtr); Memory.CurrentProcess.ReadRaw((IntPtr)fileDataPtr, out managedAnimationEntry._fileData, header.FileSize); // Get File Tuples if (entry.HasProperties) { var tuples = new List <PropertyTuple>(32); PropertyTuple tuple; void AddTuple() { tuple = *tuplePtr; tuple.SwapEndian(); tuples.Add(tuple); } while (!tuplePtr->Equals(PropertyTuple.Terminator)) { AddTuple(); tuplePtr++; } AddTuple(); managedAnimationEntry.Tuples = tuples.ToArray(); } return(managedAnimationEntry); }
/// <summary> /// Converts a <see cref="MotionPackage"/> into a native .MTP file. /// </summary> public byte[] ToMtp() { var bytes = new List <byte>(1000000); var header = Header; header.SwapEndian(); bytes.AddRange(Struct.GetBytes(ref header)); // Write entries var dummyAnimationEntry = new AnimationEntry(); var dummyAnimationEntryBytes = Struct.GetBytes(ref dummyAnimationEntry); int[] entryOffsets = Entries.Select(x => AddRange(bytes, dummyAnimationEntryBytes)).ToArray(); // Write file names. int[] fileNameOffsets = Entries.Select(x => { int firstRef = AddRange(bytes, String.GetNullTerminatedBytes(String.Win1252Encoder, x.FileName)); // Must pad to next group of 4-bytes, otherwise game will fail to parse while (bytes.Count % 4 > 0) { bytes.Add(0x00); } return(firstRef); }).ToArray(); // Write file data. int[] fileDataOffsets = Entries.Select(x => AddRange(bytes, x.FileData)).ToArray(); // Write extra properties. int[] filePropertyOffsets = Entries.Select(x => { if (x.Tuples != null) { // Temporarily swap out the endian of all tuples before writing to array, then swap back. for (int i = 0; i < x.Tuples.Length; i++) { x.Tuples[i].SwapEndian(); } var result = AddRange(bytes, StructArray.GetBytes(x.Tuples)); for (int i = 0; i < x.Tuples.Length; i++) { x.Tuples[i].SwapEndian(); } return(result); } return(0); }).ToArray(); // Fix Offsets var byteArray = bytes.ToArray(); fixed(byte *byteArrayPtr = byteArray) { for (int x = 0; x < Entries.Length; x++) { ref var entry = ref Unsafe.AsRef <AnimationEntry>(byteArrayPtr + entryOffsets[x]); Endian.Reverse(ref fileNameOffsets[x]); Endian.Reverse(ref fileDataOffsets[x]); Endian.Reverse(ref filePropertyOffsets[x]); entry.FileNamePtr = fileNameOffsets[x]; entry.FileDataPtr = fileDataOffsets[x]; entry.PropertyTuplePtr = filePropertyOffsets[x]; } }