/** * Read a specific lump. Currently only supports entities. * * @param $index * @throws \Exception */ private function readLump($index) { $info = $this->lumpHeader[$index]; $this->buffer->seek($info['fileofs']); if ($index === 0) { $hdr = $this->buffer->peek(4); if ($hdr == 'LZMA') { // Team Fortress 2 // Decompress $buffer = $this->decompressLZMALump($this->buffer); } else { $buffer = $this->buffer->slice($info['filelen']); } $parts = explode("}", $buffer->getData()); $lump = []; foreach ($parts as $data) { $data = trim($data); if (strlen($data) < 1 || $data[0] != '{') { continue; } $lump[] = KeyValues::decode("\"Entity\"\n" . trim($data) . "\n}")['Entity']; } } else { if ($index === 40) { $file = tempnam(sys_get_temp_dir(), 'zip'); $tmp = fopen($file, 'w'); $remaining = $info['filelen']; while ($remaining > 0) { $chunk = $this->buffer->read($remaining > CHUNK_SIZE ? CHUNK_SIZE : $remaining); fwrite($tmp, $chunk); $remaining -= strlen($chunk); } $lump = new PakFile($file); } } return $lump; }
/** * Load the archive. * * @throws VPKException * @throws \Exception * @throws \VLib\Buffer\BufferUnderflowException */ private function load() { $this->multiChunk = strpos($this->path, '_dir') !== false; // Load it from our filesystem. This uses Flysystem to support remote files. $fh = $this->filesystem->readStream($this->path); if (!$fh) { throw new VPKException('Unable to open path ' . $this->path); } /** * Use a temporary buffer for the header data. */ $buffer = new ResourceBuffer($fh); $sig = $buffer->getInteger(); if ($sig !== 1437209140) { throw new VPKException('Invalid file signature.'); } $this->version = $buffer->getInteger(); switch ($this->version) { case 1: $headerSize = 12; break; case 2: $headerSize = 28; $buffer->skip(16); break; default: throw new VPKException('Invalid file version.'); } $this->dictSize = $buffer->getInteger(); $this->pathEntries = []; while ($type = $buffer->getString()) { while ($dir = $buffer->getString()) { while ($name = $buffer->getString()) { $crc32 = $buffer->getInteger(); $preloadSize = $buffer->getShort(); $chunkIndex = $buffer->getShort(); $offset = $buffer->getInteger(); $size = $buffer->getInteger(); $term = $buffer->getShort(); if ($term !== 0xffff) { throw new VPKException('Unexpected termination character.'); } $preload = ''; if ($preloadSize > 0) { $preload = $buffer->read($preloadSize); } if ($this->multiChunk) { $entryName = sprintf('%s_%03d.vpk', $this->vpkName, $chunkIndex); $entryName = $this->dir . '/' . $entryName; } else { $entryName = $this->file; if ($this->version == 1) { $offset += $headerSize + $this->dictSize; } } $entry = new VPKEntry($this->filesystem, $entryName); $entry->setType($type); $entry->setName($name); $entry->setDir($dir); $entry->setCRC32($crc32); $entry->setOffset($offset); $entry->setSize($size); $entry->setPreload($preload); $this->pathEntries[$entry->getPath()] = $entry; } } } }