1
0
mirror of https://github.com/Rogiel/php-mpq synced 2025-12-06 08:23:05 +00:00

9 Commits

39 changed files with 1546 additions and 74 deletions

4
.gitignore vendored
View File

@@ -1,6 +1,8 @@
# Created by .ignore support plugin (hsz.mobi)
### Composer template
composer.phar
vendor/
composer.lock
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
.idea/

25
LICENSE.md Normal file
View 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.

View File

@@ -13,6 +13,7 @@ The recommended way of installing this library is using Composer.
use Rogiel\MPQ\MPQFile;
$file = MPQFile::parseFile(__DIR__.'/test.SC2Replay');
$file->parse();
$stream = $file->openStream('replay.details');
while($data = $stream->readBytes(100)) {
echo $data;

View File

@@ -15,7 +15,15 @@
"Rogiel\\MPQ\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Rogiel\\MPQ\\Tests\\": "tests/"
}
},
"require": {
"php": ">=5.4"
},
"require-dev": {
"phpunit/phpunit": "^4.8.1"
}
}

29
phpunit.xml Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="MPQ Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">/src</directory>
<exclude>
<directory suffix=".php">/src/Exception</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@@ -29,14 +29,30 @@
namespace Rogiel\MPQ\Compression;
use Rogiel\MPQ\Exception\Compression\InvalidInputDataException;
class BZIPCompression implements Compression {
/**
* {@inheritdoc}
*/
public function compress($data, $length) {
return bzcompress(substr($data, 0, $length));
$output = @bzcompress(substr($data, 0, $length));
if(!is_string($output)) {
throw new InvalidInputDataException('The compression input data is invalid.', $output);
}
return $output;
}
/**
* {@inheritdoc}
*/
public function decompress($data, $length) {
return bzdecompress(substr($data, 0, $length));
$output = @bzdecompress(substr($data, 0, $length));
if(!is_string($output)) {
throw new InvalidInputDataException('The decompression input data is invalid.', $output);
}
return $output;
}
}

View File

@@ -29,9 +29,30 @@
namespace Rogiel\MPQ\Compression;
use Rogiel\MPQ\Exception\Compression\InvalidInputDataException;
interface Compression {
/**
* Compresses a block of data
*
* @param $data string the block of data to be compressed
* @param $length integer the length of the block to be compressed
* @return string the compressed data
*
* @throws InvalidInputDataException if the uncompressed data is invalid and cannot be compressed
*/
public function compress($data, $length);
/**
* Decompresses a block of data
*
* @param $data string the block of data to be decompressed
* @param $length integer the length of the block to be decompressed
* @return string the decompressed data
*
* @throws InvalidInputDataException if the compressed data is invalid and cannot be decompressed
*/
public function decompress($data, $length);
}

View File

@@ -29,14 +29,30 @@
namespace Rogiel\MPQ\Compression;
use Rogiel\MPQ\Exception\Compression\InvalidInputDataException;
class DeflateCompression implements Compression {
/**
* {@inheritdoc}
*/
public function compress($data, $length) {
return gzdeflate(substr($data, 0, $length));
$output = @gzdeflate(substr($data, 0, $length));
if(!is_string($output)) {
throw new InvalidInputDataException('The compression input data is invalid.', $output);
}
return $output;
}
/**
* {@inheritdoc}
*/
public function decompress($data, $length) {
return gzinflate($data, $length);
$output = @gzinflate(substr($data, 0, $length), $length);
if($output === false) {
throw new InvalidInputDataException('The decompression input data is invalid.', $output);
}
return $output;
}
}

View File

@@ -29,56 +29,96 @@
namespace Rogiel\MPQ\Encryption;
use Rogiel\MPQ\Exception\Encryption\InvalidBlockSizeException;
use Rogiel\MPQ\Exception\Encryption\InvalidKeyException;
use Rogiel\MPQ\Util\CryptoUtils;
class DefaultEncryption implements Encryption {
/**
* The encryption/decryption key
*
* @var string
*/
private $key;
/**
* The encryption/decryption seed
*
* @var string
*/
private $seed;
/**
* DefaultEncryption constructor.
* @param $key string the encryption key. The key must have exactly 10 bytes
*/
public function __construct($key) {
$this->key = $key;
$this->seed = ((0xEEEE << 16) | 0xEEEE);
CryptoUtils::initTable();
$this->reset($key);
}
/**
* {@inheritdoc}
*/
public function reset($key) {
if(strlen($key) != 10) {
throw new InvalidKeyException(sprintf('The key is expected to have 10 bytes, %i given.', strlen($key)));
}
$this->key = $key;
$this->seed = ((0xEEEE << 16) | 0xEEEE);
}
/**
* {@inheritdoc}
*/
public function decrypt($string, $length) {
if($length % 4 != 0) {
throw new InvalidBlockSizeException(sprintf('The block size is invalid. Input expected to be a multiple of 4, %s given', $length));
}
$data = $this->createBlockArray($string, $length);
$datalen = $length / 4;
for($i = 0;$i < $datalen;$i++) {
$blocks = $length / 4;
for($block = 0;$block < $blocks; $block++) {
$this->seed = CryptoUtils::uPlus($this->seed,CryptoUtils::$cryptTable[0x400 + ($this->key & 0xFF)]);
$ch = $data[$i] ^ (CryptoUtils::uPlus($this->key,$this->seed));
$ch = $data[$block] ^ (CryptoUtils::uPlus($this->key,$this->seed));
$this->key = (CryptoUtils::uPlus(((~$this->key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($this->key,0x0B));
$this->seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($ch,$this->seed),($this->seed << 5)),3);
$data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF);
$data[$block] = $ch & ((0xFFFF << 16) | 0xFFFF);
}
return $this->createDataStream($data, $length / 4);
return $this->createDataStream($data, $length);
}
public function encrypt($data, $length) {
$key = clone $this->key;
$seed = ((0xEEEE << 16) | 0xEEEE);
$datalen = $length;
for($i = 0;$i < $datalen;$i++) {
$seed = CryptoUtils::uPlus($seed,CryptoUtils::$cryptTable[0x400 + ($key & 0xFF)]);
$ch = $data[$i] ^ (CryptoUtils::uPlus($key,$seed));
$key = (CryptoUtils::uPlus(((~$key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($key,0x0B));
$seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($data[$i],$seed),($seed << 5)),3);
$data[$i] = $ch & ((0xFFFF << 16) | 0xFFFF);
/**
* {@inheritdoc}
*/
public function encrypt($string, $length) {
if($length % 4 != 0) {
throw new InvalidBlockSizeException(sprintf('The block size is invalid. Input expected to be a multiple of 4, %s given', $length));
}
return $data;
$data = $this->createBlockArray($string, $length);
$blocks = $length / 4;
for($block = 0;$block < $blocks; $block++) {
$this->seed = CryptoUtils::uPlus($this->seed,CryptoUtils::$cryptTable[0x400 + ($this->key & 0xFF)]);
$ch = $data[$block] ^ (CryptoUtils::uPlus($this->key,$this->seed));
$this->key = (CryptoUtils::uPlus(((~$this->key) << 0x15), 0x11111111)) | (CryptoUtils::rShift($this->key,0x0B));
$this->seed = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($data[$block],$this->seed),($this->seed << 5)),3);
$data[$block] = $ch & ((0xFFFF << 16) | 0xFFFF);
}
return $this->createDataStream($data, $length);
}
/**
* {@inheritdoc}
*/
public function getBlockSize() {
return 4;
}

View File

@@ -28,14 +28,47 @@
namespace Rogiel\MPQ\Encryption;
use Rogiel\MPQ\Exception\Encryption\InvalidBlockSizeException;
use Rogiel\MPQ\Exception\Encryption\InvalidKeyException;
interface Encryption {
/**
* Resets the encryption context
*
* @param $key string the new encryption key
*
* @throws InvalidKeyException if the given key is invalid
*/
public function reset($key);
/**
* Encrypts a block of data
*
* @param $data string the data block
* @param $length integer the data block size
* @return string the encrypted block
*
* @throws InvalidBlockSizeException if the block size is incorrect
*/
public function encrypt($data, $length);
/**
* Decrypts a block of data
*
* @param $data string the data block
* @param $length integer the data block size
* @return string the decrypted block
*
* @throws InvalidBlockSizeException if the block size is incorrect
*/
public function decrypt($data, $length);
/**
* Gets the cipher block size
*
* @return integer
*/
public function getBlockSize();
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Compression;
use Rogiel\MPQ\Exception\MPQException;
class CompressionException extends MPQException {
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Compression;
class InvalidInputDataException extends CompressionException {
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Encryption;
use Rogiel\MPQ\Exception\MPQException;
class EncryptionException extends MPQException {
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Encryption;
class InvalidBlockSizeException extends EncryptionException {
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Encryption;
class InvalidKeyException extends EncryptionException {
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception\Hashing;
use Rogiel\MPQ\Exception\MPQException;
class HashingException extends MPQException {
}

View File

@@ -0,0 +1,34 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Exception;
class MPQException extends \Exception {
}

View File

@@ -42,19 +42,18 @@ abstract class BaseHashing implements Hashing {
public function hash($string) {
$seed1 = 0x7FED7FED;
$seed2 = ((0xEEEE << 16) | 0xEEEE);
$seed2 = 0xEEEEEEEE;
$strLen = strlen($string);
for ($i = 0;$i < $strLen;$i++) {
$next = ord(strtoupper(substr($string, $i, 1)));
$seed1 = CryptoUtils::$cryptTable[($this->hashType << 8) + $next] ^ (CryptoUtils::uPlus($seed1,$seed2));
$seed2 = CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus(CryptoUtils::uPlus($next,$seed1),$seed2),$seed2 << 5),3);
$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) & 0xFFFFFFFF;
}
return $seed1;
}
// function that adds up two integers without allowing them to overflow to floats
private function uPlus($o1, $o2) {
$o1h = ($o1 >> 16) & 0xFFFF;

View File

@@ -52,6 +52,11 @@ class MPQFile {
*/
private $stream;
/**
* @var boolean
*/
private $parsed;
/**
* @var UserData
*/
@@ -74,36 +79,50 @@ class MPQFile {
public function __construct(Stream $stream) {
$this->stream = $stream;
$this->parse();
}
function __destruct() {
$this->stream->close();
}
// -----------------------------------------------------------------------------------------------------------------
private function parse() {
public function isParsed() {
return $this->parsed;
}
public function parse() {
if($this->isParsed()) {
return;
}
$parser = new BinaryStreamParser($this->stream);
$signature = $this->parseSignature($parser);
if($signature == "MPQ27") {
$this->userData = UserData::parse($parser);
$this->stream->seek($this->userData->getHeaderOffset());
$this->stream->seek($this->getUserDataOffset());
$signature = $this->parseSignature($parser);
}
$signature = $this->parseSignature($parser);
if($signature == "MPQ26") {
$this->header = Header::parse($parser);
}
$this->hashTable = $this->parseHashTable();
$this->blockTable = $this->parseBlockTable();
$this->parsed = true;
}
private function parseHashTable() {
$hashing = new FileKeyHashing();
$encryptedStream = new EncryptedStream($this->stream, new DefaultEncryption($hashing->hash('(hash table)')));
$parser = new BinaryStreamParser($encryptedStream);
$parser->seek($this->userData->getHeaderOffset() + $this->header->getHashTablePos());
$parser->seek($this->getUserDataOffset() + $this->getHeader()->getHashTablePos());
$hashes = array();
for($i = 0; $i<$this->header->getHashTableSize(); $i++) {
for($i = 0; $i<$this->getHeader()->getHashTableSize(); $i++) {
$hashes[$i] = Hash::parse($parser);
}
return new HashTable($hashes);
@@ -113,11 +132,17 @@ class MPQFile {
$hashing = new FileKeyHashing();
$encryptedStream = new EncryptedStream($this->stream, new DefaultEncryption($hashing->hash('(block table)')));
$parser = new BinaryStreamParser($encryptedStream);
$parser->seek($this->userData->getHeaderOffset() + $this->header->getBlockTablePos());
$parser->seek($this->getUserDataOffset() + $this->getHeader()->getBlockTablePos());
$blocks = array();
for($i = 0; $i<$this->header->getBlockTableSize(); $i++) {
$blocks[$i] = Block::parse($parser);
// $offsetFix = 0;
for($i = 0; $i<$this->getHeader()->getBlockTableSize(); $i++) {
$block = $blocks[$i] = Block::parse($parser);
// if($block->getSize() == 0) {
// $offsetFix++;
// }
}
return new BlockTable($blocks);
}
@@ -132,6 +157,16 @@ class MPQFile {
// -----------------------------------------------------------------------------------------------------------------
public function getUserDataOffset() {
$userData = $this->getUserData();
if($userData === null) {
return 0;
}
return $userData->getHeaderOffset();
}
// -----------------------------------------------------------------------------------------------------------------
/**
* @param $fileName
* @return null|Hash
@@ -143,7 +178,7 @@ class MPQFile {
$hashA = $hashingA->hash($fileName);
$hashB = $hashingB->hash($fileName);
return $this->hashTable->findHashByHash($hashA, $hashB);
return $this->getHashTable()->findHashByHash($hashA, $hashB);
}
/**
@@ -155,6 +190,7 @@ class MPQFile {
if($hash == NULL) {
return NULL;
}
return $this->getBlockTable()->getBlock($hash->getBlockIndex());
}
@@ -165,15 +201,15 @@ class MPQFile {
}
$stream = clone $this->stream;
$stream->seek($this->userData->getHeaderOffset() + $block->getFilePos());
$stream->seek($this->getUserDataOffset() + $block->getFilePos());
$parser = new BinaryStreamParser($stream);
$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 -= $this->header->getBlockSize()) {
for ($i = $fileSize; $i > 0; $i -= $blockSize) {
$sectors[] = $parser->readUInt32();
$blockSize -= 4;
}
@@ -228,4 +264,4 @@ class MPQFile {
return new MPQFile(new MemoryStream($string));
}
}
}

View File

@@ -62,9 +62,6 @@ class Hash {
$hash->name1 = $parser->readUInt32();
$hash->name2 = $parser->readUInt32();
$hash->locale = $parser->readUInt16();
// echo $hash->locale;
// die();
$hash->platform = $parser->readUInt16();
$hash->blockIndex = $parser->readUInt32();

View File

@@ -30,9 +30,12 @@ namespace Rogiel\MPQ\Stream\Block;
use Rogiel\MPQ\Compression\BZIPCompression;
use Rogiel\MPQ\Compression\DeflateCompression;
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;
@@ -93,6 +96,7 @@ class BlockStream implements Stream {
$this->position = 0;
$this->buffer = NULL;
$this->currentSector = $this->sectors[0];
$this->positionInSector = 0;
}
// -----------------------------------------------------------------------------------------------------------------
@@ -106,16 +110,21 @@ class BlockStream implements Stream {
}
public function readBytes($bytes) {
if($this->position >= $this->block->getSize()) {
if($this->eof()) {
return false;
}
if(($this->position + $bytes) > $this->block->getSize()) {
$bytes = $this->block->getSize() - $this->position;
}
if($this->buffer == NULL) {
if($this->buffer === NULL) {
$this->buffer = $this->readSector($this->currentSector);
} else if($this->positionInSector >= strlen($this->buffer)) {
$this->positionInSector = 0;
} 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;
@@ -146,40 +155,41 @@ class BlockStream implements Stream {
$this->position += $bytes;
}
public function eof() {
return $this->position >= $this->block->getSize();
}
// -----------------------------------------------------------------------------------------------------------------
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()) {
if($this->block->isCompressed() && $this->block->getSize() > $this->block->getCompressedSize()) {
$parser = new BinaryStreamParser($this->stream);
$compressionType = $parser->readByte();
switch ($compressionType) {
case 0x10:
return new CompressedStream($stream, new BZIPCompression());
break;
case 0x00: return $stream;
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));
}
}
return $stream;
}
private function computeSectors($start, $length) {
$sectors = array();
foreach($this->sectors as $sector) {
/** @var $sector Sector */
if($sector->contains($start, $length)) {
$sectors[] = $sector;
}
}
return $sectors;
}
}

View File

@@ -86,4 +86,11 @@ class CompressedStream implements Stream{
$this->stream->skip($position);
}
/**
* {@inheritdoc}
*/
public function eof() {
return $this->stream->eof();
}
}

View File

@@ -107,4 +107,11 @@ class EncryptedStream implements Stream{
$this->bufferPointer = 0xFF;
}
/**
* {@inheritdoc}
*/
public function eof() {
return $this->stream->eof();
}
}

View File

@@ -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}
@@ -77,10 +85,11 @@ class FileStream implements Stream {
fseek($this->handle, $position, SEEK_CUR);
}
// -----------------------------------------------------------------------------------------------------------------
public function __clone() {
return new FileStream($this->file);
/**
* {@inheritdoc}
*/
public function eof() {
return feof($this->handle);
}
}

View File

@@ -77,4 +77,11 @@ class MemoryStream implements Stream {
$this->pointer += $position;
}
/**
* {@inheritdoc}
*/
public function eof() {
return strlen($this->data) >= $this->pointer;
}
}

View File

@@ -69,4 +69,19 @@ 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();
}
}

View File

@@ -64,4 +64,11 @@ interface Stream {
*/
public function skip($bytes);
/**
* Checks if the stream has already reached the EOF
*
* @return boolean if the end of file has been reached
*/
public function eof();
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests;
use Rogiel\MPQ\Stream\MemoryStream;
use Rogiel\MPQ\Stream\Parser\BinaryStreamParser;
abstract class AbstractTestCase extends \PHPUnit_Framework_TestCase {
protected function createMemoryParser($string) {
$stream = new MemoryStream($string);
return new BinaryStreamParser($stream);
}
}

View File

@@ -0,0 +1,86 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Compression;
use Rogiel\MPQ\Compression\BZIPCompression;
use Rogiel\MPQ\Exception\Compression\InvalidInputDataException;
use Rogiel\MPQ\Tests\AbstractTestCase;
class BZIPCompressionTest extends AbstractTestCase {
const TEST_COMPRESSED_DATA = "425a6834314159265359065c89da0000009780400000400080060490002000310c082031a916c41d41e2ee48a70a1200cb913b40";
const TEST_UNCOMPRESSED_DATA = "Hello World";
public function testCompress() {
$compression = new BZIPCompression();
$compressed = $compression->compress(
self::TEST_UNCOMPRESSED_DATA, strlen(self::TEST_UNCOMPRESSED_DATA)
);
$this->assertEquals(hex2bin(self::TEST_COMPRESSED_DATA), $compressed);
}
public function testDecompress() {
$compression = new BZIPCompression();
$compressed = $compression->decompress(
hex2bin(self::TEST_COMPRESSED_DATA), strlen(hex2bin(self::TEST_COMPRESSED_DATA))
);
$this->assertEquals(self::TEST_UNCOMPRESSED_DATA, $compressed);
}
const TEST_EMPTY_COMPRESSED_DATA = "425a683417724538509000000000";
public function testEmptyCompress() {
$compression = new BZIPCompression();
$compressed = $compression->compress("", 0);
$this->assertEquals(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), $compressed);
}
public function testEmptyDecompress() {
$compression = new BZIPCompression();
$compressed = $compression->decompress(
hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), strlen(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA))
);
$this->assertEquals("", $compressed);
}
const TEST_INVALID_COMPRESSED_DATA = "425a6417724538509000000000";
public function testInvalidDecompress() {
$this->setExpectedException(InvalidInputDataException::class);
$compression = new BZIPCompression();
$compression->decompress(
hex2bin(self::TEST_INVALID_COMPRESSED_DATA), strlen(hex2bin(self::TEST_INVALID_COMPRESSED_DATA))
);
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Compression;
use Rogiel\MPQ\Compression\DeflateCompression;
use Rogiel\MPQ\Exception\Compression\InvalidInputDataException;
use Rogiel\MPQ\Tests\AbstractTestCase;
class DeflateCompressionTest extends AbstractTestCase {
const TEST_COMPRESSED_DATA = "f348cdc9c95708cf2fca490100";
const TEST_UNCOMPRESSED_DATA = "Hello World";
public function testCompress() {
$compression = new DeflateCompression();
$compressed = $compression->compress(
self::TEST_UNCOMPRESSED_DATA, strlen(self::TEST_UNCOMPRESSED_DATA)
);
$this->assertEquals(hex2bin(self::TEST_COMPRESSED_DATA), $compressed);
}
public function testDecompress() {
$compression = new DeflateCompression();
$compressed = $compression->decompress(
hex2bin(self::TEST_COMPRESSED_DATA), strlen(hex2bin(self::TEST_COMPRESSED_DATA))
);
$this->assertEquals(self::TEST_UNCOMPRESSED_DATA, $compressed);
}
const TEST_EMPTY_COMPRESSED_DATA = "0300";
public function testEmptyCompress() {
$compression = new DeflateCompression();
$compressed = $compression->compress("", 0);
$this->assertEquals(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), $compressed);
}
public function testEmptyDecompress() {
$compression = new DeflateCompression();
$compressed = $compression->decompress(
hex2bin(self::TEST_EMPTY_COMPRESSED_DATA), strlen(hex2bin(self::TEST_EMPTY_COMPRESSED_DATA))
);
$this->assertEquals("", $compressed);
}
const TEST_INVALID_COMPRESSED_DATA = NULL;
public function testInvalidDecompress() {
$this->setExpectedException(InvalidInputDataException::class);
$compression = new DeflateCompression();
$compression->decompress(
hex2bin(self::TEST_INVALID_COMPRESSED_DATA), strlen(hex2bin(self::TEST_INVALID_COMPRESSED_DATA))
);
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Encryption;
use Rogiel\MPQ\Encryption\DefaultEncryption;
use Rogiel\MPQ\Exception\Encryption\InvalidBlockSizeException;
use Rogiel\MPQ\Exception\Encryption\InvalidKeyException;
use Rogiel\MPQ\Tests\AbstractTestCase;
class DefaultEncryptionTest extends AbstractTestCase {
const TEST_KEY = "c89a7de0c5fea9caf3f0";
const TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING = "3ef8cc61";
const TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING = "b86de569";
public function testSingleBlockEncrypt() {
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$cipher = $encryption->encrypt(
hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING))
);
$this->assertEquals(hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING), $cipher);
}
public function testSingleBlockDencrypt() {
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$plain = $encryption->decrypt(
hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_SINGLE_BLOCK_CIPHER_TEXT_STRING))
);
$this->assertEquals(hex2bin(self::TEST_SINGLE_BLOCK_PLAIN_TEXT_STRING), $plain);
}
const TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING = "87d1d07454135b170e89045025ed1e8309ee2f14aaaab586d9e81505a8ba72c5";
const TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING = "0144f97cc3492b88aed652bb914e8748c58871e1aea753ff04fe731f7cce9a38";
public function testMultipleBlocksEncrypt() {
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$cipher = $encryption->encrypt(
hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING))
);
$this->assertEquals(hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING), $cipher);
}
public function testMultipleBlocksDencrypt() {
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$plain = $encryption->decrypt(
hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_MULTIPLE_BLOCKS_CIPHER_TEXT_STRING))
);
$this->assertEquals(hex2bin(self::TEST_MULTIPLE_BLOCKS_PLAIN_TEXT_STRING), $plain);
}
// -----------------------------------------------------------------------------------------------------------------
public function testBlocklSize() {
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$this->assertEquals(4, $encryption->getBlockSize());
}
// -----------------------------------------------------------------------------------------------------------------
const TEST_SMALL_KEY = "c89a7de0c5fea9ca";
public function testSmallKey() {
$this->setExpectedException(InvalidKeyException::class);
new DefaultEncryption(hex2bin(self::TEST_SMALL_KEY));
}
const TEST_LARGE_KEY = "c89a7de0c5fea9cac89a7de0c5fea9ca";
public function testLargeKey() {
$this->setExpectedException(InvalidKeyException::class);
new DefaultEncryption(hex2bin(self::TEST_LARGE_KEY));
}
const TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING = "3ef8cc";
const TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING = "b86de5";
public function testInvalidBlockSizeEncrypt() {
$this->setExpectedException(InvalidBlockSizeException::class);
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$plain = $encryption->encrypt(
hex2bin(self::TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING), strlen(hex2bin(self::TEST_INVALID_BLOCK_SIZE_PLAIN_TEXT_STRING))
);
}
public function testInvalidBlockSizeDencrypt() {
$this->setExpectedException(InvalidBlockSizeException::class);
$encryption = new DefaultEncryption(hex2bin(self::TEST_KEY));
$plain = $encryption->decrypt(
hex2bin(self::TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING), strlen(hex2bin(self::TEST_INVALID_BLOCK_SIZE_CIPHER_TEXT_STRING))
);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Hashing;
use Rogiel\MPQ\Hashing\FileKeyHashing;
use Rogiel\MPQ\Hashing\Hashing;
use Rogiel\MPQ\Tests\AbstractTestCase;
class FileKeyHashingTest extends AbstractTestCase {
/**
* @var Hashing
*/
private $hashing;
public function setUp() {
$this->hashing = new FileKeyHashing();
}
const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592";
const TEST_HASHED_TEXT = 3812245591;
public function testHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT));
$this->assertEquals(self::TEST_HASHED_TEXT, $hash);
}
const TEST_EMPTY_TEXT = "";
const TEST_HASHED_EMPTY = 2146271213;
public function testEmptyHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT));
$this->assertEquals(self::TEST_HASHED_EMPTY, $hash);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Hashing;
use Rogiel\MPQ\Hashing\NameAHashing;
use Rogiel\MPQ\Hashing\Hashing;
use Rogiel\MPQ\Tests\AbstractTestCase;
class NameAHashingTest extends AbstractTestCase {
/**
* @var Hashing
*/
private $hashing;
public function setUp() {
$this->hashing = new NameAHashing();
}
const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592";
const TEST_HASHED_TEXT = 1258300789;
public function testHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT));
$this->assertEquals(self::TEST_HASHED_TEXT, $hash);
}
const TEST_EMPTY_TEXT = "";
const TEST_HASHED_EMPTY = 2146271213;
public function testEmptyHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT));
$this->assertEquals(self::TEST_HASHED_EMPTY, $hash);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Hashing;
use Rogiel\MPQ\Hashing\NameBHashing;
use Rogiel\MPQ\Hashing\Hashing;
use Rogiel\MPQ\Tests\AbstractTestCase;
class NameBHashingTest extends AbstractTestCase {
/**
* @var Hashing
*/
private $hashing;
public function setUp() {
$this->hashing = new NameBHashing();
}
const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592";
const TEST_HASHED_TEXT = 2342994917;
public function testHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT));
$this->assertEquals(self::TEST_HASHED_TEXT, $hash);
}
const TEST_EMPTY_TEXT = "";
const TEST_HASHED_EMPTY = 2146271213;
public function testEmptyHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT));
$this->assertEquals(self::TEST_HASHED_EMPTY, $hash);
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Hashing;
use Rogiel\MPQ\Hashing\TableOffsetHashing;
use Rogiel\MPQ\Hashing\Hashing;
use Rogiel\MPQ\Tests\AbstractTestCase;
class TableOffsetHashingTest extends AbstractTestCase {
/**
* @var Hashing
*/
private $hashing;
public function setUp() {
$this->hashing = new TableOffsetHashing();
}
const TEST_PLAIN_TEXT = "b99cb06efcddccdf861494b2b431a592";
const TEST_HASHED_TEXT = 4211952812;
public function testHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_PLAIN_TEXT));
$this->assertEquals(self::TEST_HASHED_TEXT, $hash);
}
const TEST_EMPTY_TEXT = "";
const TEST_HASHED_EMPTY = 2146271213;
public function testEmptyHash() {
$hash = $this->hashing->hash(hex2bin(self::TEST_EMPTY_TEXT));
$this->assertEquals(self::TEST_HASHED_EMPTY, $hash);
}
}

View File

@@ -0,0 +1,170 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Metadata;
use Rogiel\MPQ\Metadata\Block;
use Rogiel\MPQ\Tests\AbstractTestCase;
class BlockTest extends AbstractTestCase {
const TEST_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000000"; /* flags */
public function testParse() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(8208, $block->getFilePos());
$this->assertEquals(1048576, $block->getCompressedSize());
$this->assertEquals(1048576, $block->getSize());
$this->assertEquals(0, $block->getFlags());
}
const TEST_IMPLODED_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00010000"; /* flags */
public function testImplodedFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_IMPLODED_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isImploded());
}
const TEST_COMPRESSED_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00020000"; /* flags */
public function testCompressedFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_COMPRESSED_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isCompressed());
}
const TEST_ENCRYPTED_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000100"; /* flags */
public function testEncryptedFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_ENCRYPTED_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isEncrypted());
}
const TEST_FIX_KEY_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000200"; /* flags */
public function testKeyBasedOnPositionFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_FIX_KEY_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isKeyBasedOnPosition());
}
const TEST_PATCHED_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00001000"; /* flags */
public function testPatchedFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_PATCHED_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isPatched());
}
const TEST_SINGLE_UNIT_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000001"; /* flags */
public function testSingleUnitFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_SINGLE_UNIT_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isSingleUnit());
}
const TEST_DELETE_MARKER_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000002"; /* flags */
public function testDeleteMarkerFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_DELETE_MARKER_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isDeleted());
}
const TEST_CHECKSUMED_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000004"; /* flags */
public function testChecksumedFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_CHECKSUMED_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isChecksumed());
}
const TEST_EXISTS_BLOCK_DATA =
"10200000". /* file position */
"00001000". /* compressed size */
"00001000". /* size */
"00000080"; /* flags */
public function testExistsFlag() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_EXISTS_BLOCK_DATA));
$block = Block::parse($parser);
$this->assertEquals(true, $block->isExisting());
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Metadata;
use Rogiel\MPQ\Metadata\Hash;
use Rogiel\MPQ\Tests\AbstractTestCase;
class HashTest extends AbstractTestCase {
const TEST_HASH_DATA =
"10200000". /* name 1 */
"00001000". /* name 2 */
"00001000". /* locale/platform */
"DEAD0000"; /* block index */
public function testParse() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_HASH_DATA));
$hash = Hash::parse($parser);
$this->assertEquals(8208, $hash->getName1());
$this->assertEquals(1048576, $hash->getName2());
$this->assertEquals(0, $hash->getLocale());
$this->assertEquals(16, $hash->getPlatform());
$this->assertEquals(44510, $hash->getBlockIndex());
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Metadata;
use Rogiel\MPQ\Metadata\UserData;
use Rogiel\MPQ\Tests\AbstractTestCase;
class UserDataTest extends AbstractTestCase {
const TEST_USER_RAW_DATA = "67685b0ba71a3e0c4783877483b6c41cddae8669eb33b9ada05452e362dadd16739e84f2683ff3ff550608dae02ac3d8cd4b0bbcdfa353146f3d9516f327f490d678fbe06b725ced05e405e6476e70ee3bdb217fd02ec1db0a2a374ef6d9d000a66ad6c53e69d1841a95837f5adcf33214b03c277f5c0e49ce2a0623232061573de9352a43eb1f9c9294940e4e98b272e66095a0ceb11c830779f8cc04a2224c1ec2bb510f74f96a2e212eda9156bd1edb7144d37480bd746098f4b25f117b41ffede36645c0413d6880eeb07b60b6915b3d1dfdcfc9cbc10d3ffcd9f0e8f19127210c20d38308e0ebcd672c6741679a772d17893975b4024b640e513aa5bca1ce3c38c180c9eb416e98d93d68fbda47e884ddc4af60c6194ddc3664b05f8d7bd5f4cb0a4ba4aaf213c4c4ddbe978c901bfdc6a8ec0533693b5961dc398a48629b299d9a6e5fc6e33e7d5e41e5b38e80f7c559496d70fb5759a77c0b8a2580976cb7a9578c232b891cc1e14623b37a5274cf3474f7350717522279487483956d3ec9dc73aea5656ba44ca243c913fab9feb5085f41a5ad08d31371d230eb6a6334490263c248c6207e4624dac5ff2777e25686446161024539826f54be01579dbea2877f25165f33b78803915f733bbcda0364f8b1a4ac04ba3556286ca22c946c2aacaf2810fd909b8d4be4fb9133c8b55fb742";
const TEST_USER_DATA =
"00020000". /* size */
"00040000". /* header offset */
"01000000". /* user data header */
self::TEST_USER_RAW_DATA; /* data */
public function testParse() {
$parser = $this->createMemoryParser(hex2bin(self::TEST_USER_DATA));
$userData = UserData::parse($parser);
$this->assertEquals(512, $userData->getSize());
$this->assertEquals(1024, $userData->getHeaderOffset());
$this->assertEquals(1, $userData->getHeader());
$this->assertEquals(hex2bin(self::TEST_USER_RAW_DATA), $userData->getRawContent());
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Copyright (c) 2016, 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.
*/
namespace Rogiel\MPQ\Tests\Stream\Parser;
use Rogiel\MPQ\Tests\AbstractTestCase;
class BinaryStreamParserTest extends AbstractTestCase {
public function testReadByte() {
$parser = $this->createMemoryParser(
hex2bin("00FF")
);
$this->assertEquals(0x00, $parser->readByte());
$this->assertEquals(0xFF, $parser->readByte());
}
public function testReadBytes() {
$parser = $this->createMemoryParser(
hex2bin("00FF")
);
$this->assertEquals(hex2bin("00FF"), $parser->readBytes(2));
}
public function testReadUInt32() {
$parser = $this->createMemoryParser(
hex2bin("0000BEEF")
);
$this->assertEquals(4022206464, $parser->readUInt32());
}
public function testReadUInt16() {
$parser = $this->createMemoryParser(
hex2bin("0E0A")
);
$this->assertEquals(2574, $parser->readUInt16());
}
public function testSkip() {
$parser = $this->createMemoryParser(
hex2bin("DEADBEEF")
);
$parser->skip(2);
$this->assertEquals(0xBE, $parser->readByte());
}
public function testSeek() {
$parser = $this->createMemoryParser(
hex2bin("DEADBEEF")
);
$parser->seek(3);
$this->assertEquals(0xEF, $parser->readByte());
}
}