Skip to content

Instantly share code, notes, and snippets.

@BenLubar
Last active March 8, 2022 21:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BenLubar/1f517a66f328731b291ac6acb21821b8 to your computer and use it in GitHub Desktop.
Save BenLubar/1f517a66f328731b291ac6acb21821b8 to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"bytes"
"encoding/binary"
"io"
"os"
)
const (
ExpectedSignature = 0x55aa1234
ExpectedVersion = 1
)
type VPKHeader struct {
Signature uint32
Version uint32
TreeSize uint32
}
type VPKDirectoryEntry struct {
CRC uint32
PreloadBytes uint16
ArchiveIndex uint16
EntryOffset uint32
EntryLength uint32
Terminator int16
}
type VPKDirectoryEntryInfo struct {
Ext string
Path string
Base string
Ent VPKDirectoryEntry
Preload []byte
}
func main() {
f, err := os.Open("pak01_dir.vpk")
if err != nil {
panic(err)
}
defer f.Close()
var header VPKHeader
err = binary.Read(f, binary.LittleEndian, &header)
if err != nil {
panic(err)
}
if header.Signature != ExpectedSignature {
panic("Invalid signature")
}
if header.Version != ExpectedVersion {
panic("Invalid version")
}
br := bufio.NewReader(io.LimitReader(f, int64(header.TreeSize)))
var entries []VPKDirectoryEntryInfo
nextString := func() string {
s, err := br.ReadString(0)
if err != nil {
panic(err)
}
return s[:len(s)-1]
}
var maxArchive uint16
for {
ext := nextString()
if ext == "" {
break
}
for {
path := nextString()
if path == "" {
break
}
for {
base := nextString()
if base == "" {
break
}
var ent VPKDirectoryEntry
err = binary.Read(br, binary.LittleEndian, &ent)
if err != nil {
panic(err)
}
preload := make([]byte, ent.PreloadBytes)
_, err = io.ReadFull(br, preload)
if err != nil {
panic(err)
}
if ent.ArchiveIndex > maxArchive {
maxArchive = ent.ArchiveIndex
}
entries = append(entries, VPKDirectoryEntryInfo{
Ext: ext,
Path: path,
Base: base,
Ent: ent,
Preload: preload,
})
}
}
}
var directory bytes.Buffer
var lastExt, lastPath string
for _, e := range entries {
if e.Ent.ArchiveIndex == maxArchive {
continue
}
if e.Ext != lastExt {
if lastExt != "" {
directory.WriteByte(0) // end path
directory.WriteByte(0) // end ext
}
directory.WriteString(e.Ext)
directory.WriteByte(0)
directory.WriteString(e.Path)
directory.WriteByte(0)
lastExt = e.Ext
lastPath = e.Path
} else if e.Path != lastPath {
directory.WriteByte(0)
directory.WriteString(e.Path)
directory.WriteByte(0)
lastPath = e.Path
}
directory.WriteString(e.Base)
directory.WriteByte(0)
err = binary.Write(&directory, binary.LittleEndian, &e.Ent)
if err != nil {
panic(err)
}
directory.Write(e.Preload)
}
if lastExt != "" {
directory.WriteByte(0)
directory.WriteByte(0)
directory.WriteByte(0)
}
header.TreeSize = uint32(directory.Len())
f1, err := os.Create("pak01_dir2.vpk")
if err != nil {
panic(err)
}
defer f1.Close()
err = binary.Write(f1, binary.LittleEndian, &header)
if err != nil {
panic(err)
}
_, err = f1.Write(directory.Bytes())
if err != nil {
panic(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment