using SourceFormatParser.Common; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using UnityEngine; public class VPKFile { public struct Header { public uint Magic; public uint Version; public uint TreeSizeInBytes; public uint FileDataSectionSize; public uint ArchiveMD5SectionSize; public uint OtherMD5SectionSize; public uint SignatureSectionSize; //Assert(0x55aa1234 == Magic, "WTF"); } public struct FileDescription { public uint CRC; public ushort PreloadBytes; public ushort ArchiveId; public uint EntryOffset; public uint EntryLength; public ushort ConstFooter; } public class FileRecord { public string Name; public FileDescription Description; public byte[] PreloadData; } public class DirectoryRecord { public string Name; public List Files = new List(); } public class ExtensionRecord { public string Name; public List Directories = new List(); } public static string ReadSZString(BinaryReader reader) { var result = new StringBuilder(); while (true) { byte b = reader.ReadByte(); if (0 == b) break; result.Append((char)b); } return result.ToString(); } public static FileRecord FindFile(List tree, string path) { var fileNameSeparator = path.LastIndexOf('/'); var dir = path.Substring(0, fileNameSeparator); var fileNameWithExtension = path.Substring(fileNameSeparator + 1); var extSeparator = fileNameWithExtension.LastIndexOf('.'); var fileName = fileNameWithExtension.Substring(0, extSeparator); var ext = fileNameWithExtension.Substring(extSeparator + 1); var extRecord = tree.FirstOrDefault(x => x.Name == ext); if (extRecord == null) { return null; } var dirRecord = extRecord.Directories.FirstOrDefault(x => x.Name == dir); if (dirRecord == null) { return null; } var fileRecord = dirRecord.Files.FirstOrDefault(x => x.Name == fileName); return fileRecord; } public static byte[] ReadFile(string vpkDirectory, string pakNamePrefix, FileRecord record) { var archiveId = record.Description.ArchiveId; var archiveFileName = pakNamePrefix + "_" + archiveId.ToString("D3") + ".vpk"; var archiveFilePath = System.IO.Path.Combine(vpkDirectory, archiveFileName); var stream = System.IO.File.OpenRead(archiveFilePath); stream.Seek(record.Description.EntryOffset, SeekOrigin.Begin); var reader = new BinaryReader(stream); var result = reader.ReadBytes((int)record.Description.EntryLength); reader.Close(); return result; } //pak01_dir.vpk public static List ReadDirTree(string dirVpkFile) { var stream = File.OpenRead(dirVpkFile); BinaryReader br = new BinaryReader(stream, Encoding.ASCII, false); Header header = new Header(); header.Magic = br.ReadUInt32(); if(header.Magic != 0x55aa1234) { throw new Exception("Invalid magic number"); } header.Version = br.ReadUInt32(); if(header.Version != 2) { throw new Exception($"Version {header.Version} is not supported"); } header.TreeSizeInBytes = br.ReadUInt32(); header.FileDataSectionSize = br.ReadUInt32(); header.ArchiveMD5SectionSize = br.ReadUInt32(); header.OtherMD5SectionSize = br.ReadUInt32(); header.SignatureSectionSize = br.ReadUInt32(); List result = new List(); while (true) { string extName = ReadSZString(br); if (string.IsNullOrEmpty(extName)) { break; } ExtensionRecord extRecord = new ExtensionRecord(); extRecord.Name = extName; while (true) { string dirName = ReadSZString(br); if (string.IsNullOrEmpty(dirName)) { break; } DirectoryRecord dirRecord = new DirectoryRecord(); dirRecord.Name = dirName; while (true) { string fileName = ReadSZString(br); if (string.IsNullOrEmpty(fileName)) { break; } FileRecord fileRecord = new FileRecord(); fileRecord.Name = fileName; //read file desc FileDescription desc = new FileDescription(); desc.CRC = br.ReadUInt32(); desc.PreloadBytes = br.ReadUInt16(); desc.ArchiveId = br.ReadUInt16(); desc.EntryOffset = br.ReadUInt32(); desc.EntryLength = br.ReadUInt32(); desc.ConstFooter = br.ReadUInt16(); fileRecord.Description = desc; if(desc.PreloadBytes != 0) { fileRecord.PreloadData = br.ReadBytes(desc.PreloadBytes); } dirRecord.Files.Add(fileRecord); } extRecord.Directories.Add(dirRecord); } result.Add(extRecord); } return result; } }