Files
FirstStrike.Unity/Assets/Editor/VPKFile.cs
T
2025-05-13 03:08:33 +03:00

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;
}
}