mirror of
https://github.com/Rogiel/php-mpq
synced 2025-12-06 08:23:05 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fb5f167c4 | |||
|
|
76cb5fb090 | ||
| b01a84d46b | |||
| a8091dfa7d | |||
| 4549549eec |
25
LICENSE.md
Normal file
25
LICENSE.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2020, Rogiel Sulzbach
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
@@ -13,6 +13,7 @@ The recommended way of installing this library is using Composer.
|
|||||||
use Rogiel\MPQ\MPQFile;
|
use Rogiel\MPQ\MPQFile;
|
||||||
|
|
||||||
$file = MPQFile::parseFile(__DIR__.'/test.SC2Replay');
|
$file = MPQFile::parseFile(__DIR__.'/test.SC2Replay');
|
||||||
|
$file->parse();
|
||||||
$stream = $file->openStream('replay.details');
|
$stream = $file->openStream('replay.details');
|
||||||
while($data = $stream->readBytes(100)) {
|
while($data = $stream->readBytes(100)) {
|
||||||
echo $data;
|
echo $data;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class DeflateCompression implements Compression {
|
|||||||
*/
|
*/
|
||||||
public function decompress($data, $length) {
|
public function decompress($data, $length) {
|
||||||
$output = @gzinflate(substr($data, 0, $length), $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);
|
throw new InvalidInputDataException('The decompression input data is invalid.', $output);
|
||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|||||||
@@ -42,19 +42,18 @@ abstract class BaseHashing implements Hashing {
|
|||||||
|
|
||||||
public function hash($string) {
|
public function hash($string) {
|
||||||
$seed1 = 0x7FED7FED;
|
$seed1 = 0x7FED7FED;
|
||||||
$seed2 = ((0xEEEE << 16) | 0xEEEE);
|
$seed2 = 0xEEEEEEEE;
|
||||||
$strLen = strlen($string);
|
$strLen = strlen($string);
|
||||||
|
|
||||||
for ($i = 0;$i < $strLen;$i++) {
|
for ($i = 0;$i < $strLen;$i++) {
|
||||||
$next = ord(strtoupper(substr($string, $i, 1)));
|
$next = ord(strtoupper(substr($string, $i, 1)));
|
||||||
|
|
||||||
$seed1 = CryptoUtils::$cryptTable[($this->hashType << 8) + $next] ^ (CryptoUtils::uPlus($seed1,$seed2));
|
$seed1 = CryptoUtils::$cryptTable[($this->hashType << 8) + $next] ^ (CryptoUtils::uPlus($seed1,$seed2)) & 0xFFFFFFFF;
|
||||||
$seed2 = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($next,$seed1),$seed2),$seed2 << 5),3);
|
$seed2 = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($next,$seed1),$seed2),$seed2 << 5),3) & 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
return $seed1;
|
return $seed1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// function that adds up two integers without allowing them to overflow to floats
|
// function that adds up two integers without allowing them to overflow to floats
|
||||||
private function uPlus($o1, $o2) {
|
private function uPlus($o1, $o2) {
|
||||||
$o1h = ($o1 >> 16) & 0xFFFF;
|
$o1h = ($o1 >> 16) & 0xFFFF;
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ class MPQFile {
|
|||||||
$this->stream = $stream;
|
$this->stream = $stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function __destruct() {
|
||||||
|
$this->stream->close();
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public function isParsed() {
|
public function isParsed() {
|
||||||
@@ -98,9 +102,10 @@ class MPQFile {
|
|||||||
if($signature == "MPQ27") {
|
if($signature == "MPQ27") {
|
||||||
$this->userData = UserData::parse($parser);
|
$this->userData = UserData::parse($parser);
|
||||||
$this->stream->seek($this->getUserDataOffset());
|
$this->stream->seek($this->getUserDataOffset());
|
||||||
|
|
||||||
|
$signature = $this->parseSignature($parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
$signature = $this->parseSignature($parser);
|
|
||||||
if($signature == "MPQ26") {
|
if($signature == "MPQ26") {
|
||||||
$this->header = Header::parse($parser);
|
$this->header = Header::parse($parser);
|
||||||
}
|
}
|
||||||
@@ -130,13 +135,14 @@ class MPQFile {
|
|||||||
$parser->seek($this->getUserDataOffset() + $this->getHeader()->getBlockTablePos());
|
$parser->seek($this->getUserDataOffset() + $this->getHeader()->getBlockTablePos());
|
||||||
$blocks = array();
|
$blocks = array();
|
||||||
|
|
||||||
$offsetFix = 0;
|
// $offsetFix = 0;
|
||||||
for($i = 0; $i<$this->getHeader()->getBlockTableSize(); $i++) {
|
for($i = 0; $i<$this->getHeader()->getBlockTableSize(); $i++) {
|
||||||
$block = $blocks[$i - $offsetFix] = Block::parse($parser);
|
$block = $blocks[$i] = Block::parse($parser);
|
||||||
if($block->getSize() == 0) {
|
// if($block->getSize() == 0) {
|
||||||
$offsetFix++;
|
// $offsetFix++;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BlockTable($blocks);
|
return new BlockTable($blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +157,7 @@ class MPQFile {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private function getUserDataOffset() {
|
public function getUserDataOffset() {
|
||||||
$userData = $this->getUserData();
|
$userData = $this->getUserData();
|
||||||
if($userData === null) {
|
if($userData === null) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -184,6 +190,7 @@ class MPQFile {
|
|||||||
if($hash == NULL) {
|
if($hash == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->getBlockTable()->getBlock($hash->getBlockIndex());
|
return $this->getBlockTable()->getBlock($hash->getBlockIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,25 +206,14 @@ class MPQFile {
|
|||||||
|
|
||||||
$sectors = array();
|
$sectors = array();
|
||||||
if($block->isChecksumed() || !$block->isSingleUnit()) {
|
if($block->isChecksumed() || !$block->isSingleUnit()) {
|
||||||
$blockSize = $block->getCompressedSize();
|
$blockSize = 512 * (1 << $this->getHeader()->getBlockSize());
|
||||||
$fileSize = $block->getSize();
|
$fileSize = $block->getSize();
|
||||||
|
|
||||||
print_r($this->getBlockTable()->getBlocks());
|
|
||||||
|
|
||||||
// print_r($block);
|
|
||||||
// echo $block->isExisting() ? "true" : "false";
|
|
||||||
// echo "\n";
|
|
||||||
//
|
|
||||||
// echo $fileSize."\n";
|
|
||||||
// echo $this->getHeader()->getBlockSize()."\n";
|
|
||||||
|
|
||||||
for ($i = $fileSize; $i > 0; $i -= $blockSize) {
|
for ($i = $fileSize; $i > 0; $i -= $blockSize) {
|
||||||
echo $i;
|
|
||||||
$sectors[] = $parser->readUInt32();
|
$sectors[] = $parser->readUInt32();
|
||||||
$blockSize -= 4;
|
$blockSize -= 4;
|
||||||
}
|
}
|
||||||
print_r($sectors);
|
$sectors[] = $parser->readUInt32();
|
||||||
// $sectors[] = $parser->readUInt32();
|
|
||||||
} else {
|
} else {
|
||||||
$sectors = array(
|
$sectors = array(
|
||||||
0,
|
0,
|
||||||
@@ -268,4 +264,4 @@ class MPQFile {
|
|||||||
return new MPQFile(new MemoryStream($string));
|
return new MPQFile(new MemoryStream($string));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,7 @@ use Rogiel\MPQ\Exception\Compression\CompressionException;
|
|||||||
use Rogiel\MPQ\Metadata\Block;
|
use Rogiel\MPQ\Metadata\Block;
|
||||||
use Rogiel\MPQ\MPQFile;
|
use Rogiel\MPQ\MPQFile;
|
||||||
use Rogiel\MPQ\Stream\CompressedStream;
|
use Rogiel\MPQ\Stream\CompressedStream;
|
||||||
|
use Rogiel\MPQ\Stream\MemoryStream;
|
||||||
use Rogiel\MPQ\Stream\Parser\BinaryStreamParser;
|
use Rogiel\MPQ\Stream\Parser\BinaryStreamParser;
|
||||||
use Rogiel\MPQ\Stream\Stream;
|
use Rogiel\MPQ\Stream\Stream;
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ class BlockStream implements Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function readBytes($bytes) {
|
public function readBytes($bytes) {
|
||||||
if($this->eof()) {
|
if($this->eof()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(($this->position + $bytes) > $this->block->getSize()) {
|
if(($this->position + $bytes) > $this->block->getSize()) {
|
||||||
@@ -119,7 +120,11 @@ class BlockStream implements Stream {
|
|||||||
if($this->buffer === NULL) {
|
if($this->buffer === NULL) {
|
||||||
$this->buffer = $this->readSector($this->currentSector);
|
$this->buffer = $this->readSector($this->currentSector);
|
||||||
$this->positionInSector = 0;
|
$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->currentSector = $this->sectors[$this->currentSector->getIndex() + 1];
|
||||||
$this->buffer = $this->readSector($this->currentSector);
|
$this->buffer = $this->readSector($this->currentSector);
|
||||||
$this->positionInSector = 0;
|
$this->positionInSector = 0;
|
||||||
@@ -157,23 +162,28 @@ class BlockStream implements Stream {
|
|||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private function readSector(Sector $sector) {
|
private function readSector(Sector $sector) {
|
||||||
$this->stream->seek($this->file->getUserData()->getHeaderOffset()
|
$this->stream->seek($this->file->getUserDataOffset()
|
||||||
+ $this->block->getFilePos()
|
+ $this->block->getFilePos()
|
||||||
+ $sector->getStart());
|
+ $sector->getStart());
|
||||||
|
|
||||||
$compressedStream = $this->createCompressedStream();
|
$compressedStream = $this->createCompressedStream($sector);
|
||||||
return $compressedStream->readBytes($sector->getLength());
|
return $compressedStream->readBytes($this->block->getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createCompressedStream() {
|
private function createCompressedStream(Sector $sector) {
|
||||||
$stream = $this->stream;
|
$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);
|
$parser = new BinaryStreamParser($this->stream);
|
||||||
$compressionType = $parser->readByte();
|
$compressionType = $parser->readByte();
|
||||||
switch ($compressionType) {
|
switch ($compressionType) {
|
||||||
case 0x00: return $stream;
|
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());
|
case 0x10: return new CompressedStream($stream, new BZIPCompression());
|
||||||
default:
|
default:
|
||||||
throw new CompressionException(sprintf('Invalid compression format: %s', $compressionType));
|
throw new CompressionException(sprintf('Invalid compression format: %s', $compressionType));
|
||||||
|
|||||||
@@ -40,14 +40,22 @@ class FileStream implements Stream {
|
|||||||
$this->handle = fopen($file, 'r');
|
$this->handle = fopen($file, 'r');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __clone() {
|
||||||
|
$this->handle = fopen($this->file, 'r');
|
||||||
|
}
|
||||||
|
|
||||||
|
function __destruct() {
|
||||||
|
$this->close();
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function close() {
|
public function close() {
|
||||||
fclose($this->handle);
|
@fclose($this->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
@@ -84,10 +92,4 @@ class FileStream implements Stream {
|
|||||||
return feof($this->handle);
|
return feof($this->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public function __clone() {
|
|
||||||
return new FileStream($this->file);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -69,6 +69,17 @@ class BinaryStreamParser {
|
|||||||
return $this->stream->readBytes($size);
|
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() {
|
public function eof() {
|
||||||
return $this->stream->eof();
|
return $this->stream->eof();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user