/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

// StuffIt parsing based on https://github.com/mietek/theunarchiver/wiki/StuffItFormat
// Compressions 13 and 14 based on libxad (http://sourceforge.net/projects/libxad/)

#include "common/compression/stuffit.h"

#include "common/archive.h"
#include "common/bitstream.h"
#include "common/debug.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/macresman.h"
#include "common/memstream.h"
#include "common/substream.h"

namespace Common {

struct SIT14Data;

class StuffItArchive : public Common::MemcachingCaseInsensitiveArchive {
public:
	StuffItArchive();
	~StuffItArchive() override;

	bool open(const Common::String &filename);
	bool open(Common::SeekableReadStream *stream);
	void close();
	bool isOpen() const { return _stream != nullptr; }

	// Common::Archive API implementation
	bool hasFile(const Common::Path &path) const override;
	int listMembers(Common::ArchiveMemberList &list) const override;
	const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
	Common::SharedArchiveContents readContentsForPath(const Common::String& name) const override;
	Common::String translatePath(const Common::Path &path) const override {
		return path.toString();
	}

private:
	struct FileEntry {
		byte compression;
		uint32 uncompressedSize;
		uint32 compressedSize;
		uint32 offset;
	};

	Common::SeekableReadStream *_stream;

	typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
	FileMap _map;

	typedef Common::HashMap<Common::String, Common::MacFinderInfoData, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> MetadataMap;
	MetadataMap _metadataMap;

	// Decompression Functions
	bool decompress13(Common::SeekableReadStream *src, byte *dst, uint32 uncompressedSize) const;
	void decompress14(Common::SeekableReadStream *src, byte *dst, uint32 uncompressedSize) const;

	// Decompression Helpers
	void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const;
	void readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
};

StuffItArchive::StuffItArchive() : Common::MemcachingCaseInsensitiveArchive() {
	_stream = nullptr;
}

StuffItArchive::~StuffItArchive() {
	close();
}

// Some known values of StuffIt FourCC's
// 11H Mac in particular uses ST46, while EMI Mac uses ST65
static const uint32 s_magicNumbers[] = {
	MKTAG('S', 'I', 'T', '!'), MKTAG('S', 'T', '6', '5'), MKTAG('S', 'T', '5', '0'),
	MKTAG('S', 'T', '6', '0'), MKTAG('S', 'T', 'i', 'n'), MKTAG('S', 'T', 'i', '2'),
	MKTAG('S', 'T', 'i', '3'), MKTAG('S', 'T', 'i', '4'), MKTAG('S', 'T', '4', '6')
};

bool StuffItArchive::open(const Common::String &filename) {
	Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename);
	return open(stream);
}

bool StuffItArchive::open(Common::SeekableReadStream *stream) {
	close();

	_stream = stream;

	if (!_stream)
		return false;

	uint32 tag = _stream->readUint32BE();

	// Check all the possible FourCC's
	bool found = false;
	for (int i = 0; i < ARRAYSIZE(s_magicNumbers); i++) {
		if (tag == s_magicNumbers[i]) {
			found = true;
			break;
		}
	}

	// Didn't find one, let's bail out
	if (!found) {
		close();
		return false;
	}

	/* uint16 fileCount = */ _stream->readUint16BE();
	uint32 archiveSize = _stream->readUint32BE();

	// Some sort of second magic number
	if (_stream->readUint32BE() != MKTAG('r', 'L', 'a', 'u')) {
		close();
		return false;
	}

	/* byte version = */ _stream->readByte(); // meaning not clear

	_stream->skip(7); // unknown

	while (_stream->pos() < _stream->size() && !_stream->eos() && _stream->pos() < archiveSize) {
		byte resForkCompression = _stream->readByte();
		byte dataForkCompression = _stream->readByte();

		byte fileNameLength = _stream->readByte();
		Common::String name;

		if (fileNameLength > 63)
			error("File name length too long in stuffit archive: %d at 0x%x", fileNameLength, (int) (_stream->pos() - 3));

		for (byte i = 0; i < fileNameLength; i++)
			name += (char)_stream->readByte();

		// Skip remaining bytes
		_stream->skip(63 - fileNameLength);

		MacFinderInfo finfo;

		_stream->read(finfo.type, 4);
		_stream->read(finfo.creator, 4);
		finfo.flags = _stream->readUint16BE();
		/* uint32 creationDate = */ _stream->readUint32BE();
		/* uint32 modificationDate = */ _stream->readUint32BE();
		uint32 resForkUncompressedSize = _stream->readUint32BE();
		uint32 dataForkUncompressedSize = _stream->readUint32BE();
		uint32 resForkCompressedSize = _stream->readUint32BE();
		uint32 dataForkCompressedSize = _stream->readUint32BE();
		/* uint16 resForkCRC = */ _stream->readUint16BE();
		/* uint16 dataForkCRC = */ _stream->readUint16BE();
		_stream->skip(6); // unknown
		/* uint16 headerCRC = */ _stream->readUint16BE();

		// Ignore directories for now
		if (dataForkCompression == 32 || dataForkCompression == 33)
			continue;

		_metadataMap[name + ".finf"] = finfo.toData();

		if (dataForkUncompressedSize != 0) {
			// We have a data fork

			FileEntry entry;
			entry.compression = dataForkCompression;
			entry.uncompressedSize = dataForkUncompressedSize;
			entry.compressedSize = dataForkCompressedSize;
			entry.offset = _stream->pos() + resForkCompressedSize;
			_map[name] = entry;

			debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
		}

		if (resForkUncompressedSize != 0) {
			// We have a resource fork

			// Add a .rsrc extension so we know it's the resource fork
			name += ".rsrc";

			FileEntry entry;
			entry.compression = resForkCompression;
			entry.uncompressedSize = resForkUncompressedSize;
			entry.compressedSize = resForkCompressedSize;
			entry.offset = _stream->pos();
			_map[name] = entry;

			debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
		}

		// Go to the next entry
		_stream->skip(dataForkCompressedSize + resForkCompressedSize);
	}

	return true;
}

void StuffItArchive::close() {
	delete _stream;
	_stream = nullptr;
	_map.clear();
}

bool StuffItArchive::hasFile(const Common::Path &path) const {
	Common::String name = path.toString();
	return _map.contains(name);
}

int StuffItArchive::listMembers(Common::ArchiveMemberList &list) const {
	for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
		list.push_back(getMember(it->_key));

	return _map.size();
}

const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::Path &path) const {
	Common::String name = path.toString();
	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
}

Common::SharedArchiveContents StuffItArchive::readContentsForPath(const Common::String& name) const {
	if (!_stream || !_map.contains(name)) {
		if (_metadataMap.contains(name)) {
			const Common::MacFinderInfoData &metadata = _metadataMap[name];
			byte *copy = new byte[sizeof(Common::MacFinderInfoData)];
			memcpy(copy, reinterpret_cast<const byte *>(&metadata), sizeof(Common::MacFinderInfoData));
			return Common::SharedArchiveContents(copy, sizeof(Common::MacFinderInfoData));
		}
		return Common::SharedArchiveContents();
	}

	const FileEntry &entry = _map[name];

	if (entry.compression & 0xF0)
		error("Unhandled StuffIt encryption");

	Common::SeekableSubReadStream subStream(_stream, entry.offset, entry.offset + entry.compressedSize);

	byte *uncompressedBlock = new byte[entry.uncompressedSize];

	// We currently only support type 14 compression
	switch (entry.compression) {
	case 0: // Uncompressed
		subStream.read(uncompressedBlock, entry.uncompressedSize);
		break;
	case 13: // TableHuff
		if (!decompress13(&subStream, uncompressedBlock, entry.uncompressedSize))
			error("SIT-13 decompression failed");
		break;
	case 14: // Installer
		decompress14(&subStream, uncompressedBlock, entry.uncompressedSize);
		break;
	default:
		error("Unhandled StuffIt compression %d", entry.compression);
		return Common::SharedArchiveContents();
	}

	return Common::SharedArchiveContents(uncompressedBlock, entry.uncompressedSize);
}

void StuffItArchive::update14(uint16 first, uint16 last, byte *code, uint16 *freq) const {
	uint16 i, j;

	while (last - first > 1) {
		i = first;
		j = last;

		do {
			while (++i < last && code[first] > code[i])
				;

			while (--j > first && code[first] < code[j])
				;

			if (j > i) {
				SWAP(code[i], code[j]);
				SWAP(freq[i], freq[j]);
			}
		} while (j > i);

		if (first != j) {
			SWAP(code[first], code[j]);
			SWAP(freq[first], freq[j]);

			i = j + 1;

			if (last - i <= j - first) {
				update14(i, last, code, freq);
				last = j;
			} else {
				update14(first, j, code, freq);
				first = i;
			}
		} else {
			++first;
		}
	}
}

struct SIT14Data {
	byte code[308];
	byte codecopy[308];
	uint16 freq[308];
	uint32 buff[308];

	byte var1[52];
	uint16 var2[52];
	uint16 var3[75 * 2];

	byte var4[76];
	uint32 var5[75];
	byte var6[1024];
	uint16 var7[308 * 2];
	byte var8[0x4000];

	byte window[0x40000];
};

// Realign to a byte boundary
#define ALIGN_BITS(b) \
	if (b->pos() & 7) \
		b->skip(8 - (b->pos() & 7))

void StuffItArchive::readTree14(Common::BitStream8LSB *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const {
	uint32 i, l, n;
	uint32 k = bits->getBit();
	uint32 j = bits->getBits<2>() + 2;
	uint32 o = bits->getBits<3>() + 1;
	uint32 size = 1 << j;
	uint32 m = size - 1;
	k = k ? (m - 1) : 0xFFFFFFFF;

	if (bits->getBits<2>() & 1) { // skip 1 bit!
		// requirements for this call: dat->buff[32], dat->code[32], dat->freq[32*2]
		readTree14(bits, dat, size, dat->freq);

		for (i = 0; i < codesize; ) {
			l = 0;

			do {
				l = dat->freq[l + bits->getBit()];
				n = size << 1;
			} while (n > l);

			l -= n;

			if (k != l) {
				if (l == m) {
					l = 0;

					do {
						l = dat->freq[l + bits->getBit()];
						n = size <<  1;
					} while (n > l);

					l += 3 - n;

					while (l--) {
						dat->code[i] = dat->code[i - 1];
						++i;
					}
				} else {
					dat->code[i++] = l + o;
				}
			} else {
				dat->code[i++] = 0;
			}
		}
	} else {
		for (i = 0; i < codesize; ) {
			l = bits->getBits(j);

			if (k != l) {
				if (l == m) {
					l = bits->getBits(j) + 3;

					while (l--) {
						dat->code[i] = dat->code[i - 1];
						++i;
					}
				} else {
					dat->code[i++] = l + o;
				}
			} else {
				dat->code[i++] = 0;
			}
		}
	}

	for (i = 0; i < codesize; ++i) {
		dat->codecopy[i] = dat->code[i];
		dat->freq[i] = i;
	}

	update14(0, codesize, dat->codecopy, dat->freq);

	for (i = 0; i < codesize && !dat->codecopy[i]; ++i)
		; // find first nonempty

	for (j = 0; i < codesize; ++i, ++j) {
		if (i)
			j <<= (dat->codecopy[i] - dat->codecopy[i - 1]);

		k = dat->codecopy[i];
		m = 0;

		for (l = j; k--; l >>= 1)
			m = (m << 1) | (l & 1);

		dat->buff[dat->freq[i]] = m;
	}

	for (i = 0; i < (uint32)codesize * 2; ++i)
		result[i] = 0;

	j = 2;

	for (i = 0; i < codesize; ++i) {
		l = 0;
		m = dat->buff[i];

		for (k = 0; k < dat->code[i]; ++k) {
			l += (m & 1);

			if (dat->code[i] - 1 <= (int32)k) {
				result[l] = codesize * 2 + i;
			} else {
				if (!result[l]) {
					result[l] = j;
					j += 2;
				}

				l = result[l];
			}

			m >>= 1;
		}
	}

	ALIGN_BITS(bits);
}

struct SIT13Buffer {
	uint16 data;
	int8  bits;

	SIT13Buffer() : data(0), bits(0) {}
};

struct SIT13Store {
	int16  freq;
	uint16 d1;
	uint16 d2;

	SIT13Store() : freq(0), d1(0), d2(0) {}
};

struct SIT13Data {
	uint16              MaxBits;
	struct SIT13Store  Buffer4[0xE08];
	struct SIT13Buffer Buffer1[0x1000];
	struct SIT13Buffer Buffer2[0x1000];
	struct SIT13Buffer Buffer3[0x1000];
	struct SIT13Buffer Buffer3b[0x1000];
	struct SIT13Buffer Buffer5[0x141];
	uint8              TextBuf[658];
	uint8              Window[0x10000];

	SIT13Data() : MaxBits(0) {
		memset(TextBuf, 0, sizeof(TextBuf));
		memset(Window, 0, sizeof(Window));
	}
};

static const uint8 SIT13Bits[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
static const uint16 SIT13Info[37] = {
	0x5D8, 0x058, 0x040, 0x0C0, 0x000, 0x078, 0x02B, 0x014,
	0x00C, 0x01C, 0x01B, 0x00B, 0x010, 0x020, 0x038, 0x018,
	0x0D8, 0xBD8, 0x180, 0x680, 0x380, 0xF80, 0x780, 0x480,
	0x080, 0x280, 0x3D8, 0xFD8, 0x7D8, 0x9D8, 0x1D8, 0x004,
	0x001, 0x002, 0x007, 0x003, 0x008
};
static const uint16 SIT13InfoBits[37] = {
	11,  8,  8,  8,  8,  7,  6,  5,  5,  5,  5,  6,  5,  6,  7,  7,
	9, 12, 10, 11, 11, 12, 12, 11, 11, 11, 12, 12, 12, 12, 12,  5,
	2,  2,  3,  4,  5
};
static const uint16 SIT13StaticPos[5] = {0, 330, 661, 991, 1323};
static const uint8 SIT13StaticBits[5] = {11, 13, 14, 11, 11};
static const uint8 SIT13Static[1655] = {
	0xB8,0x98,0x78,0x77,0x75,0x97,0x76,0x87,0x77,0x77,0x77,0x78,0x67,0x87,0x68,0x67,0x3B,0x77,0x78,0x67,
	0x77,0x77,0x77,0x59,0x76,0x87,0x77,0x77,0x77,0x77,0x77,0x77,0x76,0x87,0x67,0x87,0x77,0x77,0x75,0x88,
	0x59,0x75,0x79,0x77,0x78,0x68,0x77,0x67,0x73,0xB6,0x65,0xB6,0x76,0x97,0x67,0x47,0x9A,0x2A,0x4A,0x87,
	0x77,0x78,0x67,0x86,0x78,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x77,0x77,
	0x68,0x77,0x77,0x77,0x67,0x87,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x68,0x77,0x77,0x77,
	0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x67,0x87,
	0x68,0x77,0x77,0x77,0x68,0x77,0x68,0x63,0x86,0x7A,0x87,0x77,0x77,0x87,0x76,0x87,0x77,0x77,0x77,0x77,
	0x77,0x77,0x77,0x77,0x77,0x76,0x86,0x77,0x86,0x86,0x86,0x86,0x87,0x76,0x86,0x87,0x67,0x74,0xA7,0x86,
	0x36,0x88,0x78,0x76,0x87,0x76,0x96,0x87,0x77,0x84,0xA6,0x86,0x87,0x76,0x92,0xB5,0x94,0xA6,0x96,0x85,
	0x78,0x75,0x96,0x86,0x86,0x75,0xA7,0x67,0x87,0x85,0x87,0x85,0x95,0x77,0x77,0x85,0xA3,0xA7,0x93,0x87,
	0x86,0x94,0x85,0xA8,0x67,0x85,0xA5,0x95,0x86,0x68,0x67,0x77,0x96,0x78,0x75,0x86,0x77,0xA5,0x67,0x87,
	0x85,0xA6,0x75,0x96,0x85,0x87,0x95,0x95,0x87,0x86,0x94,0xA5,0x86,0x85,0x87,0x86,0x86,0x86,0x86,0x77,
	0x67,0x76,0x66,0x9A,0x75,0xA5,0x94,0x97,0x76,0x96,0x76,0x95,0x86,0x77,0x86,0x87,0x75,0xA5,0x96,0x85,
	0x86,0x96,0x86,0x86,0x85,0x96,0x86,0x76,0x95,0x86,0x95,0x95,0x95,0x87,0x76,0x87,0x76,0x96,0x85,0x78,
	0x75,0xA6,0x85,0x86,0x95,0x86,0x95,0x86,0x45,0x69,0x78,0x77,0x87,0x67,0x69,0x58,0x79,0x68,0x78,0x87,
	0x78,0x66,0x88,0x68,0x68,0x77,0x76,0x87,0x68,0x68,0x69,0x58,0x5A,0x4B,0x76,0x88,0x69,0x67,0xA7,0x70,
	0x9F,0x90,0xA4,0x84,0x77,0x77,0x77,0x89,0x17,0x77,0x7B,0xA7,0x86,0x87,0x77,0x68,0x68,0x69,0x67,0x78,
	0x77,0x78,0x76,0x87,0x77,0x76,0x73,0xB6,0x87,0x96,0x66,0x87,0x76,0x85,0x87,0x78,0x77,0x77,0x86,0x77,
	0x86,0x78,0x66,0x76,0x77,0x87,0x86,0x78,0x76,0x76,0x86,0xA5,0x67,0x97,0x77,0x87,0x87,0x76,0x66,0x59,
	0x67,0x59,0x77,0x6A,0x65,0x86,0x78,0x94,0x77,0x88,0x77,0x78,0x86,0x86,0x76,0x88,0x76,0x87,0x67,0x87,
	0x77,0x77,0x76,0x87,0x86,0x77,0x77,0x77,0x86,0x86,0x76,0x96,0x77,0x77,0x76,0x78,0x86,0x86,0x86,0x95,
	0x86,0x96,0x85,0x95,0x86,0x87,0x75,0x88,0x77,0x87,0x57,0x78,0x76,0x86,0x76,0x96,0x86,0x87,0x76,0x87,
	0x86,0x76,0x77,0x86,0x78,0x78,0x57,0x87,0x86,0x76,0x85,0xA5,0x87,0x76,0x86,0x86,0x85,0x86,0x53,0x98,
	0x78,0x78,0x77,0x87,0x79,0x67,0x79,0x85,0x87,0x69,0x67,0x68,0x78,0x69,0x68,0x69,0x58,0x87,0x66,0x97,
	0x68,0x68,0x76,0x85,0x78,0x87,0x67,0x97,0x67,0x74,0xA2,0x28,0x77,0x78,0x77,0x77,0x78,0x68,0x67,0x78,
	0x77,0x78,0x68,0x68,0x77,0x59,0x67,0x5A,0x68,0x68,0x68,0x68,0x68,0x68,0x67,0x77,0x78,0x68,0x68,0x78,
	0x59,0x58,0x76,0x77,0x68,0x78,0x68,0x59,0x69,0x58,0x68,0x68,0x67,0x78,0x77,0x78,0x69,0x58,0x68,0x57,
	0x78,0x67,0x78,0x76,0x88,0x58,0x67,0x7A,0x46,0x88,0x77,0x78,0x68,0x68,0x66,0x78,0x78,0x68,0x68,0x59,
	0x68,0x69,0x68,0x59,0x67,0x78,0x59,0x58,0x69,0x59,0x67,0x68,0x67,0x69,0x69,0x57,0x79,0x68,0x59,0x59,
	0x59,0x68,0x68,0x68,0x58,0x78,0x67,0x59,0x68,0x78,0x59,0x58,0x78,0x58,0x76,0x78,0x68,0x68,0x68,0x69,
	0x59,0x67,0x68,0x69,0x59,0x59,0x58,0x69,0x59,0x59,0x58,0x5A,0x58,0x68,0x68,0x59,0x58,0x68,0x66,0x47,
	0x88,0x77,0x87,0x77,0x87,0x76,0x87,0x87,0x87,0x77,0x77,0x87,0x67,0x96,0x78,0x76,0x87,0x68,0x77,0x77,
	0x76,0x86,0x96,0x86,0x88,0x77,0x85,0x86,0x8B,0x76,0x0A,0xF9,0x07,0x38,0x57,0x67,0x77,0x78,0x77,0x91,
	0x77,0xD7,0x77,0x7A,0x67,0x3C,0x68,0x68,0x77,0x68,0x78,0x59,0x77,0x68,0x77,0x68,0x76,0x77,0x69,0x68,
	0x68,0x68,0x68,0x67,0x68,0x68,0x77,0x87,0x77,0x67,0x78,0x68,0x67,0x58,0x78,0x68,0x77,0x68,0x78,0x67,
	0x68,0x68,0x67,0x78,0x77,0x77,0x87,0x77,0x76,0x67,0x86,0x85,0x87,0x86,0x97,0x58,0x67,0x79,0x57,0x77,
	0x87,0x77,0x87,0x77,0x76,0x59,0x78,0x77,0x77,0x68,0x77,0x77,0x76,0x78,0x77,0x77,0x77,0x76,0x87,0x77,
	0x77,0x68,0x77,0x77,0x77,0x67,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x76,0x68,0x87,0x77,
	0x77,0x77,0x77,0x68,0x77,0x68,0x77,0x77,0x77,0x77,0x77,0x77,0x76,0x78,0x77,0x77,0x76,0x87,0x77,0x77,
	0x67,0x78,0x77,0x77,0x76,0x78,0x67,0x68,0x68,0x29,0x77,0x88,0x78,0x78,0x77,0x68,0x77,0x77,0x77,0x77,
	0x77,0x77,0x77,0x77,0x4A,0x77,0x4A,0x74,0x77,0x77,0x68,0xA4,0x7A,0x47,0x76,0x86,0x78,0x76,0x7A,0x4A,
	0x83,0xB2,0x87,0x77,0x87,0x76,0x96,0x86,0x96,0x76,0x78,0x87,0x77,0x85,0x87,0x85,0x96,0x65,0xB5,0x95,
	0x96,0x77,0x77,0x86,0x76,0x86,0x86,0x87,0x86,0x86,0x76,0x96,0x96,0x57,0x77,0x85,0x97,0x85,0x86,0xA5,
	0x86,0x85,0x87,0x77,0x68,0x78,0x77,0x95,0x86,0x75,0x87,0x76,0x86,0x79,0x68,0x84,0x96,0x76,0xB3,0x87,
	0x77,0x68,0x86,0xA5,0x77,0x56,0xB6,0x68,0x85,0x93,0xB6,0x95,0x95,0x85,0x95,0xA5,0x95,0x95,0x69,0x85,
	0x95,0x85,0x86,0x86,0x97,0x84,0x85,0xB6,0x84,0xA5,0x95,0xA4,0x95,0x95,0x95,0x68,0x95,0x66,0xA6,0x95,
	0x95,0x95,0x86,0x93,0xB5,0x86,0x77,0x94,0x96,0x95,0x96,0x85,0x68,0x94,0x87,0x95,0x86,0x86,0x93,0xB4,
	0xA3,0xB3,0xA6,0x86,0x85,0x85,0x96,0x76,0x86,0x64,0x69,0x78,0x68,0x78,0x78,0x77,0x67,0x79,0x68,0x79,
	0x59,0x56,0x87,0x98,0x68,0x78,0x76,0x88,0x68,0x68,0x67,0x76,0x87,0x68,0x78,0x76,0x78,0x77,0x78,0xA6,
	0x80,0xAF,0x81,0x38,0x47,0x67,0x77,0x78,0x77,0x89,0x07,0x79,0xB7,0x87,0x86,0x86,0x87,0x86,0x87,0x76,
	0x78,0x77,0x87,0x66,0x96,0x86,0x86,0x74,0xA6,0x87,0x86,0x77,0x86,0x77,0x76,0x77,0x77,0x87,0x77,0x77,
	0x77,0x77,0x87,0x65,0x78,0x77,0x78,0x75,0x88,0x85,0x76,0x87,0x95,0x77,0x86,0x87,0x86,0x96,0x85,0x76,
	0x69,0x67,0x59,0x77,0x6A,0x65,0x86,0x78,0x94,0x77,0x88,0x77,0x78,0x85,0x96,0x65,0x98,0x77,0x87,0x67,
	0x86,0x77,0x87,0x66,0x87,0x86,0x86,0x86,0x77,0x86,0x86,0x76,0x87,0x86,0x77,0x76,0x87,0x77,0x86,0x86,
	0x86,0x87,0x76,0x95,0x86,0x86,0x87,0x65,0x97,0x86,0x87,0x76,0x86,0x86,0x87,0x75,0x88,0x76,0x87,0x76,
	0x87,0x76,0x77,0x77,0x86,0x78,0x76,0x76,0x96,0x78,0x76,0x77,0x86,0x77,0x77,0x76,0x96,0x75,0x95,0x56,
	0x87,0x87,0x87,0x78,0x88,0x67,0x87,0x87,0x58,0x87,0x77,0x87,0x77,0x76,0x87,0x96,0x59,0x88,0x37,0x89,
	0x69,0x69,0x84,0x96,0x67,0x77,0x57,0x4B,0x58,0xB7,0x80,0x8E,0x0D,0x78,0x87,0x77,0x87,0x68,0x79,0x49,
	0x76,0x78,0x77,0x5A,0x67,0x69,0x68,0x68,0x68,0x4A,0x68,0x69,0x67,0x69,0x59,0x58,0x68,0x67,0x69,0x77,
	0x77,0x69,0x68,0x68,0x66,0x68,0x87,0x68,0x77,0x5A,0x68,0x67,0x68,0x68,0x67,0x78,0x78,0x67,0x6A,0x59,
	0x67,0x57,0x95,0x78,0x77,0x86,0x88,0x57,0x77,0x68,0x67,0x79,0x76,0x76,0x98,0x68,0x75,0x68,0x88,0x58,
	0x87,0x5A,0x57,0x79,0x67,0x59,0x78,0x49,0x58,0x77,0x79,0x49,0x68,0x59,0x77,0x68,0x78,0x48,0x79,0x67,
	0x68,0x59,0x68,0x68,0x59,0x75,0x6A,0x68,0x76,0x4C,0x67,0x77,0x78,0x59,0x69,0x56,0x96,0x68,0x68,0x68,
	0x77,0x69,0x67,0x68,0x67,0x78,0x69,0x68,0x58,0x59,0x68,0x68,0x69,0x49,0x77,0x59,0x67,0x69,0x67,0x68,
	0x65,0x48,0x77,0x87,0x86,0x96,0x88,0x75,0x87,0x96,0x87,0x95,0x87,0x77,0x68,0x86,0x77,0x77,0x96,0x68,
	0x86,0x77,0x85,0x5A,0x81,0xD5,0x95,0x68,0x99,0x74,0x98,0x77,0x09,0xF9,0x0A,0x5A,0x66,0x58,0x77,0x87,
	0x91,0x77,0x77,0xE9,0x77,0x77,0x77,0x76,0x87,0x75,0x97,0x77,0x77,0x77,0x78,0x68,0x68,0x68,0x67,0x3B,
	0x59,0x77,0x77,0x57,0x79,0x57,0x86,0x87,0x67,0x97,0x77,0x57,0x79,0x77,0x77,0x75,0x95,0x77,0x79,0x75,
	0x97,0x57,0x77,0x79,0x58,0x69,0x77,0x77,0x77,0x77,0x77,0x75,0x86,0x77,0x87,0x58,0x95,0x78,0x65,0x8A,
	0x39,0x58,0x87,0x96,0x87,0x77,0x77,0x77,0x86,0x87,0x76,0x78,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,
	0x77,0x68,0x77,0x68,0x77,0x67,0x86,0x77,0x78,0x77,0x77,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,0x68,
	0x77,0x68,0x77,0x67,0x78,0x77,0x77,0x68,0x68,0x76,0x87,0x68,0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x77,
	0x77,0x77,0x77,0x68,0x77,0x77,0x77,0x68,0x68,0x68,0x76,0x38,0x97,0x67,0x79,0x77,0x77,0x77,0x77,0x77,
	0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x68,
	0x72,0xC5,0x86,0x86,0x98,0x77,0x86,0x78,0x1C,0x85,0x2E,0x77,0x77,0x77,0x87,0x86,0x76,0x86,0x86,0xA0,
	0xBD,0x49,0x97,0x66,0x48,0x88,0x48,0x68,0x86,0x78,0x77,0x77,0x78,0x66,0xA6,0x87,0x83,0x85,0x88,0x78,
	0x66,0xA7,0x56,0x87,0x6A,0x46,0x89,0x76,0xA7,0x76,0x87,0x74,0xA2,0x86,0x77,0x79,0x66,0xB6,0x48,0x67,
	0x8A,0x36,0x88,0x77,0xA5,0xA5,0xB1,0xE9,0x39,0x78,0x78,0x75,0x87,0x77,0x77,0x77,0x68,0x58,0x79,0x69,
	0x4A,0x59,0x29,0x6A,0x3C,0x3B,0x46,0x78,0x75,0x89,0x76,0x89,0x4A,0x56,0x88,0x3B,0x66,0x88,0x68,0x87,
	0x57,0x97,0x38,0x87,0x56,0xB7,0x84,0x88,0x67,0x57,0x95,0xA8,0x59,0x77,0x68,0x4A,0x49,0x69,0x57,0x6A,
	0x59,0x58,0x67,0x87,0x5A,0x75,0x78,0x69,0x56,0x97,0x77,0x73,0x08,0x78,0x78,0x77,0x87,0x78,0x77,0x78,
	0x77,0x77,0x87,0x78,0x68,0x77,0x77,0x87,0x78,0x76,0x86,0x97,0x58,0x77,0x78,0x58,0x78,0x77,0x68,0x78,
	0x75,0x95,0xB7,0x70,0x8F,0x80,0xA6,0x87,0x65,0x66,0x78,0x7A,0x17,0x77,0x70,
};

static void SIT13_Func1(struct SIT13Data *s, struct SIT13Buffer *buf, uint32 info, uint16 bits, uint16 num)
{
	uint32 i, j;

	if(bits <= 12)
	{
		for(i = 0; i < (1<<12); i += (1<<bits))
		{
			buf[info+i].data = num;
			buf[info+i].bits = bits;
		}
	}
	else
	{
		j = bits-12;

		if(buf[info & 0xFFF].bits != 0x1F)
		{
			buf[info & 0xFFF].bits = 0x1F;
			buf[info & 0xFFF].data = s->MaxBits++;
		}
		bits = buf[info & 0xFFF].data;
		info >>= 12;

		while(j--)
		{
			uint16 *a;

			a = info & 1 ? &s->Buffer4[bits].d2 : &s->Buffer4[bits].d1;
			if(!*a)
				*a = s->MaxBits++;
			bits = *a;
			info >>= 1;
		}
		s->Buffer4[bits].freq = num;
	}
}

static void SIT13_SortTree(struct SIT13Data *s, struct SIT13Buffer *buf, struct SIT13Buffer *buf2)
{
	uint16 td;
	int8 tb;

	struct SIT13Buffer *a, *b;

	while(buf2-1 > buf)
	{
		a = buf;
		b = buf2;

		for(;;)
		{
			while(++a < buf2)
			{
				tb = a->bits - buf->bits;
				if(tb > 0 || (!tb && (a->data >= buf->data)))
					break;
			}
			while(--b > buf)
			{
				tb = b->bits - buf->bits;
				if(tb < 0 || (!tb && (b->data <= buf->data)))
					break;
			}
			if(b < a)
				break;
			else
			{
				tb = a->bits;
				td = a->data;
				a->bits = b->bits;
				a->data = b->data;
				b->bits = tb;
				b->data = td;
			}
		}
		if(b == buf)
			++buf;
		else
		{
			tb = buf->bits;
			td = buf->data;
			buf->bits = b->bits;
			buf->data = b->data;
			b->bits = tb;
			b->data = td;
			if(buf2-b-1 > b-buf)
			{
				SIT13_SortTree(s, buf, b);
				buf = b+1;
			}
			else
			{
				SIT13_SortTree(s, b+1, buf2);
				buf2 = b;
			}
		}
	}
}

static void SIT13_Func2(struct SIT13Data *s, struct SIT13Buffer *buf, uint16 bits, struct SIT13Buffer *buf2)
{
	int32 i, j, k, l, m, n;

	SIT13_SortTree(s, buf2, buf2 + bits);

	l = k = j = 0;
	for(i = 0; i < bits; ++i)
	{
		l += k;
		m = buf2[i].bits;
		if(m != j)
		{
			if((j = m) == -1)
				k = 0;
			else
				k = 1 << (32-j);
		}
		if(j > 0)
		{
			for(n = m = 0; n < 8*4; n += 4)
				m += SIT13Bits[(l>>n)&0xF]<<(7*4-n);
			SIT13_Func1(s, buf, m, j, buf2[i].data);
		}
	}
}

static void SIT13_CreateStaticTree(struct SIT13Data *s, struct SIT13Buffer *buf, uint16 bits, uint8 *bitsbuf)
{
	uint32 i;

	for(i = 0; i < bits; ++i)
	{
		s->Buffer5[i].data = i;
		s->Buffer5[i].bits = bitsbuf[i];
	}
	SIT13_Func2(s, buf, bits, s->Buffer5);
}

static void SIT13InitInfo(struct SIT13Data *s, uint8 id)
{
	int32 i;
	uint8 k, l = 0, *a;
	const uint8 *b;

	a = s->TextBuf;
	b = (const uint8 *) SIT13Static+SIT13StaticPos[id-1];
	id &= 1;

	for(i = 658; i; --i)
	{
		k = id ? *b >> 4 : *(b++) & 0xF; id ^=1;

		if(!k)
		{
			l -= id ? *b >> 4 : *(b++) & 0xF; id ^= 1;
		}
		else
		{
			if(k == 15)
			{
				l += id ? *b >> 4 : *(b++) & 0xF; id ^= 1;
			}
			else
				l += k-7;
		}
		*(a++) = l;
	}
}

static bool SIT13_Extract(struct SIT13Data *s, Common::BitStream8LSB *bits, Common::MemoryWriteStream& out)
{
	uint32 wpos = 0, j, k, l, size;
	struct SIT13Buffer *buf = s->Buffer3;

	while(!bits->eos())
	{
		k = bits->peekBits<12>();
		if((j = buf[k].bits) <= 12)
		{
			l = buf[k].data;
			if (j == 0)
				return false;
			bits->getBits(j);
		}
		else
		{
			bits->getBits<12>();

			j = buf[k].data;
			while(s->Buffer4[j].freq == -1)
				j = bits->getBit() ? s->Buffer4[j].d2 : s->Buffer4[j].d1;
			l = s->Buffer4[j].freq;
		}
		if(l < 0x100)
		{
			s->Window[wpos++] = l;
			out.writeByte(l);
			wpos &= 0xFFFF;
			buf = s->Buffer3;
		}
		else
		{
			buf = s->Buffer3b;
			if(l < 0x13E)
				size = l - 0x100 + 3;
			else
			{
				if(l == 0x13E)
					size = bits->getBits<10>();
				else
				{
					if(l == 0x140)
						return true;
					size = bits->getBits<15>();
				}
				size += 65;
			}
			j = bits->peekBits<12>();
			k = s->Buffer2[j].bits;
			if(k <= 12)
			{
				l = s->Buffer2[j].data;
				bits->getBits(k);
			}
			else
			{
				bits->getBits<12>();
				j = s->Buffer2[j].data;
				while(s->Buffer4[j].freq == -1)
					j = bits->getBit() ? s->Buffer4[j].d2 : s->Buffer4[j].d1;
				l = s->Buffer4[j].freq;
			}
			k = 0;
			if(l--)
				k = (1 << l) | bits->getBits(l);
			l = wpos+0x10000-(k+1);
			while(size--)
			{
				l &= 0xFFFF;
				byte b = s->Window[l++];
				out.writeByte(b);
				s->Window[wpos++] = b;
				wpos &= 0xFFFF;
			}
		} /* l >= 0x100 */
	}

	return false;
}

static void SIT13_CreateTree(struct SIT13Data *s, Common::BitStream8LSB *bits, struct SIT13Buffer *buf, uint16 num)
{
	struct SIT13Buffer *b;
	uint32 i;
	uint16 data;
	int8 bi = 0;

	for(i = 0; i < num; ++i)
	{
		b = &s->Buffer1[bits->peekBits<12>()];
		data = b->data;
		bits->getBits(b->bits);

		switch(data-0x1F)
		{
		case 0: bi = -1; break;
		case 1: ++bi; break;
		case 2: --bi; break;
		case 3:
			if(bits->getBit())
				s->Buffer5[i++].bits = bi;
			break;
		case 4:
			data = bits->getBits<3>()+2;
			while(data--)
				s->Buffer5[i++].bits = bi;
			break;
		case 5:
			data = bits->getBits<6>()+10;
			while(data--)
				s->Buffer5[i++].bits = bi;
			break;
		default: bi = data+1; break;
		}
		s->Buffer5[i].bits = bi;
	}
	for(i = 0; i < num; ++i)
		s->Buffer5[i].data = i;
	SIT13_Func2(s, buf, num, s->Buffer5);
}

bool StuffItArchive::decompress13(Common::SeekableReadStream *src, byte *dst, uint32 uncompressedSize) const {
	Common::MemoryWriteStream out(dst, uncompressedSize);

	Common::BitStream8LSB *bits = new Common::BitStream8LSB(src);

	uint32 i, j;

	SIT13Data *s = new SIT13Data();

	s->MaxBits = 1;
	for(i = 0; i < 37; ++i)
		SIT13_Func1(s, s->Buffer1, SIT13Info[i], SIT13InfoBits[i], i);
	for(i = 1; i < 0x704; ++i)
	{
		/* s->Buffer4[i].d1 = s->Buffer4[i].d2 = 0; */
		s->Buffer4[i].freq = -1;
	}

	j = bits->getBits<8>();
	i = j>>4;
	if(i > 5)
		return false;
	if(i)
	{
		SIT13InitInfo(s, i--);
		SIT13_CreateStaticTree(s, s->Buffer3, 0x141, s->TextBuf);
		SIT13_CreateStaticTree(s, s->Buffer3b, 0x141, s->TextBuf+0x141);
		SIT13_CreateStaticTree(s, s->Buffer2, SIT13StaticBits[i], s->TextBuf+0x282);
	}
	else
	{
		SIT13_CreateTree(s, bits, s->Buffer3, 0x141);
		if(j&8)
			memcpy(s->Buffer3b, s->Buffer3, 0x1000*sizeof(struct SIT13Buffer));
		else
			SIT13_CreateTree(s, bits, s->Buffer3b, 0x141);
		j = (j&7)+10;
		SIT13_CreateTree(s, bits, s->Buffer2, j);
	}
	return SIT13_Extract(s, bits, out);
}

#define OUTPUT_VAL(x) \
	out.writeByte(x); \
	dat->window[j++] = x; \
	j &= 0x3FFFF

void StuffItArchive::decompress14(Common::SeekableReadStream *src, byte *dst, uint32 uncompressedSize) const {
	Common::MemoryWriteStream out(dst, uncompressedSize);

	Common::BitStream8LSB *bits = new Common::BitStream8LSB(src);

	uint32 i, j, k, l, m, n;

	SIT14Data *dat = new SIT14Data();

	// initialization
	for (i = k = 0; i < 52; ++i) {
		dat->var2[i] = k;
		k += (1 << (dat->var1[i] = ((i >= 4) ? ((i - 4) >> 2) : 0)));
	}

	for (i = 0; i < 4; ++i)
		dat->var8[i] = i;

	for (m = 1, l = 4; i < 0x4000; m <<= 1) // i is 4
		for (n = l + 4; l < n; ++l)
			for (j = 0; j < m; ++j)
				dat->var8[i++] = l;

	for (i = 0, k = 1; i < 75; ++i) {
		dat->var5[i] = k;
		k += (1 << (dat->var4[i] = (i >= 3 ? ((i - 3) >> 2) : 0)));
	}

	for (i = 0; i < 4; ++i)
		dat->var6[i] = i - 1;

	for (m = 1, l = 3; i < 0x400; m <<= 1) // i is 4
		for (n = l + 4; l < n; ++l)
			for (j = 0; j < m; ++j)
				dat->var6[i++] = l;

	m = bits->getBits<16>(); // number of blocks
	j = 0; // window position

	while (m-- && !bits->eos()) {
		bits->getBits<16>(); // skip crunched block size
		bits->getBits<16>();
		n = bits->getBits<16>(); // number of uncrunched bytes
		n |= bits->getBits<16>() << 16;
		readTree14(bits, dat, 308, dat->var7);
		readTree14(bits, dat, 75, dat->var3);

		while (n && !bits->eos()) {
			for (i = 0; i < 616;)
				i = dat->var7[i + bits->getBit()];

			i -= 616;

			if (i < 0x100) {
				OUTPUT_VAL(i);
				--n;
			} else {
				i -= 0x100;
				k = dat->var2[i] + 4;
				i = dat->var1[i];

				if (i)
					k += bits->getBits(i);

				for (i = 0; i < 150;)
					i = dat->var3[i + bits->getBit()];

				i -= 150;
				l = dat->var5[i];
				i = dat->var4[i];

				if (i)
					l += bits->getBits(i);

				n -= k;
				l = j + 0x40000 - l;

				while (k--) {
					l &= 0x3FFFF;
					OUTPUT_VAL(dat->window[l]);
					l++;
				}
			}
		}

		ALIGN_BITS(bits);
	}

	delete dat;
	delete bits;
}

#undef OUTPUT_VAL
#undef ALIGN_BITS

Common::Archive *createStuffItArchive(const Common::String &fileName) {
	StuffItArchive *archive = new StuffItArchive();

	if (!archive->open(fileName)) {
		delete archive;
		return nullptr;
	}

	return archive;
}

Common::Archive *createStuffItArchive(Common::SeekableReadStream *stream) {
	StuffItArchive *archive = new StuffItArchive();

	if (!archive->open(stream)) {
		delete archive;
		return nullptr;
	}

	return archive;
}

} // End of namespace Common
