From a8091dfa7dfc10f3f39536f92864e0b9e7aeb7e0 Mon Sep 17 00:00:00 2001 From: Rogiel Sulzbach Date: Sat, 23 Jul 2016 23:50:43 -0300 Subject: [PATCH] Fix a memory leak issue and a problem parsing MPQ26 headers --- src/Compression/DeflateCompression.php | 2 +- src/MPQFile.php | 14 +++++++++---- src/Stream/Block/BlockStream.php | 26 ++++++++++++++++-------- src/Stream/FileStream.php | 18 ++++++++-------- src/Stream/Parser/BinaryStreamParser.php | 11 ++++++++++ 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/Compression/DeflateCompression.php b/src/Compression/DeflateCompression.php index 3f7275d..fc2a34b 100644 --- a/src/Compression/DeflateCompression.php +++ b/src/Compression/DeflateCompression.php @@ -49,7 +49,7 @@ class DeflateCompression implements Compression { */ public function decompress($data, $length) { $output = @gzinflate(substr($data, 0, $length), $length); - if(!is_string($output)) { + if($output === false) { throw new InvalidInputDataException('The decompression input data is invalid.', $output); } return $output; diff --git a/src/MPQFile.php b/src/MPQFile.php index 77b076a..aeeac9a 100644 --- a/src/MPQFile.php +++ b/src/MPQFile.php @@ -81,6 +81,10 @@ class MPQFile { $this->stream = $stream; } + function __destruct() { + $this->stream->close(); + } + // ----------------------------------------------------------------------------------------------------------------- public function isParsed() { @@ -98,9 +102,10 @@ class MPQFile { if($signature == "MPQ27") { $this->userData = UserData::parse($parser); $this->stream->seek($this->getUserDataOffset()); + + $signature = $this->parseSignature($parser); } - $signature = $this->parseSignature($parser); if($signature == "MPQ26") { $this->header = Header::parse($parser); } @@ -137,6 +142,7 @@ class MPQFile { // $offsetFix++; // } } + return new BlockTable($blocks); } @@ -151,7 +157,7 @@ class MPQFile { // ----------------------------------------------------------------------------------------------------------------- - private function getUserDataOffset() { + public function getUserDataOffset() { $userData = $this->getUserData(); if($userData === null) { return 0; @@ -200,7 +206,7 @@ class MPQFile { $sectors = array(); if($block->isChecksumed() || !$block->isSingleUnit()) { - $blockSize = $block->getCompressedSize(); + $blockSize = 512 * (1 << $this->getHeader()->getBlockSize()); $fileSize = $block->getSize(); for ($i = $fileSize; $i > 0; $i -= $blockSize) { @@ -258,4 +264,4 @@ class MPQFile { return new MPQFile(new MemoryStream($string)); } -} +} \ No newline at end of file diff --git a/src/Stream/Block/BlockStream.php b/src/Stream/Block/BlockStream.php index 7f907c1..3f030d5 100644 --- a/src/Stream/Block/BlockStream.php +++ b/src/Stream/Block/BlockStream.php @@ -35,6 +35,7 @@ use Rogiel\MPQ\Exception\Compression\CompressionException; use Rogiel\MPQ\Metadata\Block; use Rogiel\MPQ\MPQFile; use Rogiel\MPQ\Stream\CompressedStream; +use Rogiel\MPQ\Stream\MemoryStream; use Rogiel\MPQ\Stream\Parser\BinaryStreamParser; use Rogiel\MPQ\Stream\Stream; @@ -109,7 +110,7 @@ class BlockStream implements Stream { } public function readBytes($bytes) { - if($this->eof()) { + if($this->eof()) { return false; } if(($this->position + $bytes) > $this->block->getSize()) { @@ -119,7 +120,11 @@ class BlockStream implements Stream { if($this->buffer === NULL) { $this->buffer = $this->readSector($this->currentSector); $this->positionInSector = 0; - } else if($this->positionInSector >= strlen($this->buffer)) { + } else if($this->positionInSector >= strlen($this->buffer)) { + if(!isset($this->sectors[$this->currentSector->getIndex() + 1])) { + return false; + } + $this->currentSector = $this->sectors[$this->currentSector->getIndex() + 1]; $this->buffer = $this->readSector($this->currentSector); $this->positionInSector = 0; @@ -157,23 +162,28 @@ class BlockStream implements Stream { // ----------------------------------------------------------------------------------------------------------------- private function readSector(Sector $sector) { - $this->stream->seek($this->file->getUserData()->getHeaderOffset() + $this->stream->seek($this->file->getUserDataOffset() + $this->block->getFilePos() + $sector->getStart()); - $compressedStream = $this->createCompressedStream(); - return $compressedStream->readBytes($sector->getLength()); + $compressedStream = $this->createCompressedStream($sector); + return $compressedStream->readBytes($this->block->getSize()); } - private function createCompressedStream() { + private function createCompressedStream(Sector $sector) { $stream = $this->stream; - if($this->block->isCompressed() && $this->block->getSize() > $this->block->getCompressedSize()) { + if($this->block->isCompressed() && $this->block->getSize() > $this->block->getCompressedSize()) { $parser = new BinaryStreamParser($this->stream); $compressionType = $parser->readByte(); switch ($compressionType) { case 0x00: return $stream; - case 0x02: return new CompressedStream($stream, new DeflateCompression()); + case 0x02: { + $len = $parser->readUInt16(); + $blockData = $stream->readBytes($len); + return new MemoryStream(gzinflate($blockData)); + } +// case 0x02: return new CompressedStream($stream, new DeflateCompression()); case 0x10: return new CompressedStream($stream, new BZIPCompression()); default: throw new CompressionException(sprintf('Invalid compression format: %s', $compressionType)); diff --git a/src/Stream/FileStream.php b/src/Stream/FileStream.php index 511bba8..0ee5a0e 100644 --- a/src/Stream/FileStream.php +++ b/src/Stream/FileStream.php @@ -40,14 +40,22 @@ class FileStream implements Stream { $this->handle = fopen($file, 'r'); } + public function __clone() { + $this->handle = fopen($this->file, 'r'); + } + + function __destruct() { + $this->close(); + } + // ----------------------------------------------------------------------------------------------------------------- /** * {@inheritdoc} */ public function close() { - fclose($this->handle); - } + @fclose($this->handle); + } /** * {@inheritdoc} @@ -84,10 +92,4 @@ class FileStream implements Stream { return feof($this->handle); } - // ----------------------------------------------------------------------------------------------------------------- - - public function __clone() { - return new FileStream($this->file); - } - } \ No newline at end of file diff --git a/src/Stream/Parser/BinaryStreamParser.php b/src/Stream/Parser/BinaryStreamParser.php index d6e8932..505a043 100644 --- a/src/Stream/Parser/BinaryStreamParser.php +++ b/src/Stream/Parser/BinaryStreamParser.php @@ -69,6 +69,17 @@ class BinaryStreamParser { return $this->stream->readBytes($size); } + public function readCString($debug=false) { + $str = ''; + while(true) { + $c = $this->stream->readBytes(1); + if($c == "\0") { + return $str; + } + $str .= (string) $c; + } + } + public function eof() { return $this->stream->eof(); }