187 lines
5.5 KiB
C#
187 lines
5.5 KiB
C#
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<FileRecord> Files = new List<FileRecord>();
|
|
}
|
|
|
|
public class ExtensionRecord
|
|
{
|
|
public string Name;
|
|
public List<DirectoryRecord> Directories = new List<DirectoryRecord>();
|
|
}
|
|
|
|
|
|
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<ExtensionRecord> 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<ExtensionRecord> 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<ExtensionRecord> result = new List<ExtensionRecord>();
|
|
|
|
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;
|
|
}
|
|
|
|
}
|