/* 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */

// Reads data from a resource file and stores the result in memory

#include "sci/include/sci_memory.h"
#include "sci/include/sciresource.h"

namespace Sci {

#define DDEBUG if (0) printf

void decryptinit3();
int decrypt3(guint8* dest, guint8* src, int length, int complength);
int decrypt4(guint8* dest, guint8* src, int length, int complength);

int decompress11(resource_t *result, Common::ReadStream &stream, int sci_version) {
	guint16 compressedLength;
	guint16 compressionMethod, result_size;
	guint8 *buffer;
	guint8 tempid;

	DDEBUG("d1");

	if (stream.read(&tempid, 1) != 1)
		return SCI_ERROR_IO_ERROR;

	result->id = tempid;

	result->type = result->id & 0x7f;
	if (stream.read(&(result->number), 2) != 2)
		return SCI_ERROR_IO_ERROR;

#ifdef SCUMM_BIG_ENDIAN
	result->number = GUINT16_SWAP_LE_BE_CONSTANT(result->number);
#endif
	if ((result->type > sci_invalid_resource))
		return SCI_ERROR_DECOMPRESSION_INSANE;

	if ((stream.read(&compressedLength, 2) != 2) || (stream.read(&result_size, 2) != 2) || (stream.read(&compressionMethod, 2) != 2))
		return SCI_ERROR_IO_ERROR;

#ifdef SCUMM_BIG_ENDIAN
	compressedLength = GUINT16_SWAP_LE_BE_CONSTANT(compressedLength);
	result_size = GUINT16_SWAP_LE_BE_CONSTANT(result_size);
	compressionMethod = GUINT16_SWAP_LE_BE_CONSTANT(compressionMethod);
#endif
	result->size = result_size;

	//if ((result->size < 0) || (compressedLength < 0))
	//	return SCI_ERROR_DECOMPRESSION_INSANE;
	// This return will never happen in SCI0 or SCI1 (does it have any use?)

	if (result->size > SCI_MAX_RESOURCE_SIZE)
		return SCI_ERROR_RESOURCE_TOO_BIG;

	if (compressedLength > 0)
		compressedLength -= 0;
	else { // Object has size zero (e.g. view.000 in sq3) (does this really exist?)
		result->data = 0;
		result->status = SCI_STATUS_NOMALLOC;
		return SCI_ERROR_EMPTY_OBJECT;
	}

	buffer = (guint8*)sci_malloc(compressedLength);
	result->data = (unsigned char*)sci_malloc(result->size);

	if (stream.read(buffer, compressedLength) != compressedLength) {
		free(result->data);
		free(buffer);
		return SCI_ERROR_IO_ERROR;
	};

	if (!(compressedLength & 1)) { // Align
		stream.readByte();
	}

#ifdef _SCI_DECOMPRESS_DEBUG
	fprintf(stderr, "Resource %i.%s encrypted with method SCI1.1/%hi at %.2f%%"
	        " ratio\n",
	        result->number, sci_resource_type_suffixes[result->type],
	        compressionMethod,
	        (result->size == 0) ? -1.0 :
	        (100.0 * compressedLength / result->size));
	fprintf(stderr, "  compressedLength = 0x%hx, actualLength=0x%hx\n",
	        compressedLength, result->size);
#endif

	DDEBUG("/%d[%d]", compressionMethod, result->size);

	switch (compressionMethod) {
	case 0: // no compression
		if (result->size != compressedLength) {
			free(result->data);
			result->data = NULL;
			result->status = SCI_STATUS_NOMALLOC;
			free(buffer);
			return SCI_ERROR_DECOMPRESSION_OVERFLOW;
		}
		memcpy(result->data, buffer, compressedLength);
		result->status = SCI_STATUS_ALLOCATED;
		break;

	case 18:
	case 19:
	case 20:
		if (decrypt4(result->data, buffer, result->size, compressedLength)) {
			free(result->data);
			result->data = 0; // So that we know that it didn't work
			result->status = SCI_STATUS_NOMALLOC;
			free(buffer);
			return SCI_ERROR_DECOMPRESSION_OVERFLOW;
		}
		result->status = SCI_STATUS_ALLOCATED;
		break;

	case 3:
	case 4: // NYI
		fprintf(stderr, "Resource %d.%s: Warning: compression type #%d not yet implemented\n",
		        result->number, sci_resource_type_suffixes[result->type], compressionMethod);
		free(result->data);
		result->data = NULL;
		result->status = SCI_STATUS_NOMALLOC;
		break;

	default:
		fprintf(stderr, "Resource %d.%s: Compression method SCI1/%hi not "
		        "supported!\n", result->number, sci_resource_type_suffixes[result->type],
		        compressionMethod);
		free(result->data);
		result->data = NULL; // So that we know that it didn't work
		result->status = SCI_STATUS_NOMALLOC;
		free(buffer);
		return SCI_ERROR_UNKNOWN_COMPRESSION;
	}

	free(buffer);

	return 0;
}

} // End of namespace Sci