//Write the nodes out to a file public bool Save(string filename, int version, ref string error) { foreach (var node in Root.IterateAll()) { if (node.Children == null && node.Data == null) { error = string.Format("{0} is empty. Can't write UTF", GetUtfPath(node)); return(false); } } if (version == 0) { return(SaveV1(filename, ref error)); } else { return(SaveV2(filename, ref error)); } }
//Write the nodes out to a file public bool Save(string filename, ref string error) { //Check for invalid UTF foreach (var node in Root.IterateAll()) { if (node.Children == null && node.Data == null) { error = string.Format("{0} is empty. Can't write UTF", GetUtfPath(node)); return(false); } } Dictionary <string, int> stringOffsets = new Dictionary <string, int>(); Dictionary <LUtfNode, int> dataOffsets = new Dictionary <LUtfNode, int>(); List <string> strings = new List <string>(); using (var writer = new BinaryWriter(File.Create(filename))) { int currentDataOffset = 0; int bytesSaved = 0; List <SmallData> smallDatas = new List <SmallData>(); foreach (var node in Root.IterateAll()) { if (!strings.Contains(node.Name)) { strings.Add(node.Name); } if (node.Data != null) { int dataAlloc = node.Data.Length + 3 & ~3; node.Write = true; //De-duplicate data up to 64 bytes if (node.Data.Length <= 64) { var small = new SmallData(node.Data); int idx = -1; for (int i = 0; i < smallDatas.Count; i++) { if (smallDatas[i].Match(ref small)) { idx = i; break; } } if (idx == -1) { small.Offset = currentDataOffset; smallDatas.Add(small); dataOffsets.Add(node, currentDataOffset); currentDataOffset += dataAlloc; } else { node.Write = false; bytesSaved += smallDatas[idx].Length; dataOffsets.Add(node, smallDatas[idx].Offset); } } else { dataOffsets.Add(node, currentDataOffset); currentDataOffset += dataAlloc; } } } LibreLancer.FLLog.Info("UTF", "Bytes Saved: " + bytesSaved); byte[] stringBlock; using (var mem = new MemoryStream()) { foreach (var str in strings) { stringOffsets.Add(str, (int)mem.Position); var strx = str; if (strx == "/") { strx = "\\"; } var strb = Encoding.ASCII.GetBytes(strx); mem.Write(strb, 0, strb.Length); mem.WriteByte(0); //null terminate } strings = null; stringBlock = mem.ToArray(); } byte[] nodeBlock; using (var mem = new MemoryStream()) { WriteNode(Root, new BinaryWriter(mem), stringOffsets, dataOffsets, true); nodeBlock = mem.ToArray(); } int strAlloc = stringBlock.Length + 3 & ~3; /*write signature*/ writer.Write((byte)'U'); writer.Write((byte)'T'); writer.Write((byte)'F'); writer.Write((byte)' '); writer.Write(LibreLancer.Utf.UtfFile.FILE_VERSION); writer.Write((int)56); //nodeBlockOffset writer.Write((int)nodeBlock.Length); //nodeBlockLength writer.Write((int)0); //unused entry offset writer.Write((int)44); //entry Size - Not accurate but FL expects it to be 44 writer.Write((int)56 + nodeBlock.Length); //stringBlockOffset writer.Write((int)strAlloc); //namesAllocatedSize writer.Write((int)stringBlock.Length); //namesUsedSize var dataBlockDesc = writer.BaseStream.Position; writer.Write((int)(56 + nodeBlock.Length + strAlloc)); writer.Write((int)0); //unused writer.Write((int)0); //unused writer.Write((ulong)125596224000000000); //Fake filetime writer.Write(nodeBlock); writer.Write(stringBlock); for (int i = 0; i < (strAlloc - stringBlock.Length); i++) { writer.Write((byte)0); } stringBlock = null; nodeBlock = null; //write out data block foreach (var node in Root.IterateAll()) { if (node.Write && node.Data != null) { writer.Write(node.Data); int dataAlloc = node.Data.Length + 3 & ~3; for (int i = 0; i < (dataAlloc - node.Data.Length); i++) { writer.Write((byte)0); } } } } return(true); }