/// <summary> /// Adds a file (already in memory) to a vpk. /// </summary> /// <param name="contents">The file contents.</param> /// <param name="internalfilename">The file name, including path, internally.</param> public void AddFile(byte[] contents, string internalfilename) { Crc32.Crc32Algorithm checker = new Crc32.Crc32Algorithm(); byte[] crc = checker.ComputeHash(contents); if (crc.Length != 4) { Console.WriteLine("wtf the crc isn't 32 bits long"); } VPKFile asfile = new VPKFile(null); asfile.CRC32 = BitConverter.ToUInt32(crc, 0); asfile.entry_length = (uint)(contents.Length); // Now that we have the basics of the file, we need to figure out how to store it // This involves first loading the vpk directory information List <VPKExt> dir = GetDirectory(); // Now we need to just add the file, and write new directory information // Get the location to which to write the file Tuple <int, uint> slot = GetNewestSpace(dir); int paddinglength = (int)filealignment - (int)((int)(slot.Item2) % (int)filealignment); int archivenum = slot.Item1; uint offset = slot.Item2; // Create a new archive file if necessary if (contents.Length + slot.Item2 + paddinglength > maxarchivesize) { paddinglength = 0; archivenum++; offset = 0; } // Write the file to the archive using (FileStream foo = File.OpenWrite(String.Format("{0}_{1:D3}.vpk", fileprefix, archivenum))) { using (BinaryWriter bw = new BinaryWriter(foo)) { bw.Seek((int)offset, SeekOrigin.Begin); for (int i = 0; i < paddinglength; i++) { bw.Write((byte)0); } bw.Write(contents); } } // Add the padding to the offset so it properly reflects the file offset += (uint)paddinglength; // Add the file information to the directory string ext = internalfilename.Substring(internalfilename.IndexOf('.') + 1); internalfilename = internalfilename.Substring(0, internalfilename.IndexOf('.')); char[] lookfor = { '\\', '/' }; string path = internalfilename.Substring(0, internalfilename.LastIndexOfAny(lookfor)); string name = internalfilename.Substring(internalfilename.LastIndexOfAny(lookfor) + 1); // Find where to put it VPKExt writeext = null; foreach (VPKExt vext in dir) { if (vext.ext == ext) { writeext = vext; } } if (writeext == null) { writeext = new VPKExt(); writeext.ext = ext; writeext.directories = new List <VPKDirectory>(); dir.Add(writeext); } VPKDirectory writedir = null; foreach (VPKDirectory vpkd in writeext.directories) { if (vpkd.path == path) { writedir = vpkd; } } if (writedir == null) { writedir = new VPKDirectory(); writedir.path = path; writedir.ext = ext; writedir.entries = new List <VPKFileEntry>(); writeext.directories.Add(writedir); } VPKFileEntry writefile = null; foreach (VPKFileEntry vpkfe in writedir.entries) { if (vpkfe.name == name) { writefile = vpkfe; //ok, we gotta do a file replacement //thankfully, this is just overwriting a few values writefile.body.archive_index = (ushort)archivenum; writefile.body.CRC32 = BitConverter.ToUInt32(crc, 0); writefile.body.entry_length = (uint)(contents.Length); writefile.body.entry_offset = offset; writefile.body.preload_bytes = 0; } } if (writefile == null) { // Didn't find a file entry, so let's add one writefile = new VPKFileEntry(); writefile.body.archive_index = (ushort)archivenum; writefile.body.CRC32 = BitConverter.ToUInt32(crc, 0); writefile.body.entry_length = (uint)(contents.Length); writefile.body.entry_offset = offset; writefile.body.preload_bytes = 0; writedir.entries.Add(writefile); } // Write the new directory to the directory file. WriteDirectory(dir); }
/// <summary> /// Read the directory from the file. This is mostly ported code from my [pw] /// vpk.h from vpktool. /// </summary> /// <returns>The VPKDirectory for the file's contents</returns> private List <VPKExt> GetDirectory() { byte[] body = null; int length = 0; using (FileStream vpkfile = File.OpenRead(fileprefix + "_dir.vpk")) { using (BinaryReader reader = new BinaryReader(vpkfile)) { byte[] headerbytes = reader.ReadBytes(12); VPKHeader head = StructTools.RawDeserialize <VPKHeader>(headerbytes, 0); if (head.version != 1) { return(null); } body = reader.ReadBytes((int)head.tree_length); length = (int)head.tree_length; } } if (body == null) { return(null); } List <VPKExt> ret = new List <VPKExt>(); int index = 0; int level = 0; VPKExt curext = new VPKExt(); VPKDirectory curpath = new VPKDirectory(); while (index < length && level >= 0) { if (body[index] == '\0') { switch (level) { case 0: break; case 1: ret.Add(curext); break; case 2: curext.directories.Add(curpath); break; } index++; level--; continue; } switch (level) { case 0: //ext level int extstartindex = index; level++; while (body[index] != 0) { index++; } index++; curext = new VPKExt(); curext.ext = Encoding.ASCII.GetString(body, extstartindex, index - extstartindex); curext.directories = new List <VPKDirectory>(); break; case 1: int pathstartindex = index; level++; while (body[index] != 0) { index++; } index++; curpath = new VPKDirectory(); curpath.path = Encoding.ASCII.GetString(body, pathstartindex, index - pathstartindex); curpath.ext = curext.ext; curpath.entries = new List <VPKFileEntry>(); break; case 2: int filestartindex = index; while (body[index] != 0) { index++; } index++; VPKFileEntry vpkfile = new VPKFileEntry(); vpkfile.name = Encoding.ASCII.GetString(body, filestartindex, index - filestartindex); vpkfile.path = curpath.path; vpkfile.ext = curext.ext; vpkfile.body = StructTools.RawDeserialize <VPKFile>(body, index); index += 18; index += vpkfile.body.preload_bytes; // we ignore preload data curpath.entries.Add(vpkfile); break; } } return(ret); }