Blob Blame Raw


#include "tiio_tzl.h"
#include "tmachine.h"
#include "tsystem.h"
#include "ttoonzimage.h"
#include "tstream.h"
#include "tfilepath_io.h"
#include "tpixelcm.h"
#include "tpalette.h"
#include "tcodec.h"
#include <stdio.h>
#include "trastercm.h"
#include "timageinfo.h"
#include "timagecache.h"
#include "tcontenthistory.h"
#include "tropcm.h"
#include "tenv.h"
#include "tconvert.h"
#include "trasterimage.h"

#include <QByteArray>

#if !defined(TNZ_LITTLE_ENDIAN)
TNZ_LITTLE_ENDIAN undefined !!
#endif

	const int CURRENT_VERSION = 14;
const int CREATOR_LENGTH = 40;

namespace
{

char *reverse(char *buffer, int size)
{
	char *start = buffer;
	char *end = start + size;
	while (start < --end) {
		char tmp = *start;
		*start++ = *end;
		*end = tmp;
	}
	return buffer;
}
}

int tfwrite(const char *data, const unsigned int count, FILE *f)
{
	return fwrite(data, sizeof(char), count, f);
}

int tfwrite(UCHAR *data, const unsigned int count, FILE *f)
{
	return fwrite(data, sizeof(char), count, f);
}

int tfwrite(TINT32 *data, const unsigned int count, FILE *f)
{
	if (count == 1) {
#if !TNZ_LITTLE_ENDIAN
		TUINT32 dataToWrite = swapTINT32(*data);
#else
		TUINT32 dataToWrite = *data;
#endif
		return fwrite(&dataToWrite, sizeof(TINT32), 1, f);
	}
	assert(0);
	return 0;
}

int tfwrite(TUINT32 *data, const unsigned int count, FILE *f)
{
	if (count == 1) {
#if !TNZ_LITTLE_ENDIAN
		TUINT32 dataToWrite = swapTINT32(*data);
#else
		TUINT32 dataToWrite = *data;
#endif
		return fwrite(&dataToWrite, sizeof(TINT32), 1, f);
	}
	assert(0);
	return 0;
}

int tfwrite(double *data, unsigned int count, FILE *f)
{

	if (count == 1) {
		double v = *data;
		char *ptr = (char *)&v;
#if !TNZ_LITTLE_ENDIAN
		ptr = reverse((char *)&v, sizeof(double));
#endif
		return fwrite(ptr, sizeof(double), 1, f);
	}
	assert(0);
	return 0;
}

//===================================================================
//
// TImageReaderTzl
//
//-------------------------------------------------------------------

namespace
{

bool erasedFrame; // Vera se è stato rimosso almeno un frame.

bool writeVersionAndCreator(FILE *chan, const char *version, QString creator)
{
	if (!chan)
		return false;
	tfwrite(version, strlen(version), chan);

	// creator : CREATOR_LENGTH characater
	char s[CREATOR_LENGTH];
	if (creator.length() == 0)
		creator = "UNKNOWN";
	memset(s, 0, sizeof s);
	if (creator.length() > CREATOR_LENGTH - 1)
#if QT_VERSION >= 0x050000
		memcpy(s, creator.toLatin1(), CREATOR_LENGTH - 1);
#else
		memcpy(s, creator.toAscii(), CREATOR_LENGTH - 1);
#endif
	else
#if QT_VERSION >= 0x050000
		memcpy(s, creator.toLatin1(), creator.length());
#else
		memcpy(s, creator.toAscii(), creator.length());
#endif
	tfwrite(s, CREATOR_LENGTH, chan);

	return true;
}

bool readVersion(FILE *chan, int &version)
{
	char magic[8];
	memset(magic, 0, sizeof(magic));
	fread(&magic, sizeof(char), 8, chan);
	if (std::string(magic).substr(0, 5) == "TLV10")
		version = 10;
	else if (std::string(magic, 5) == "TLV11")
		version = 11;
	else if (std::string(magic, 5) == "TLV12")
		version = 12;
	else if (std::string(magic, 5) == "TLV13")
		version = 13;
	else if (std::string(magic, 5) == "TLV14")
		version = 14;
	else {
		fclose(chan);
		chan = 0;
		return false;
	}
	return true;
}

bool readHeaderAndOffsets(FILE *chan, TzlOffsetMap &frameOffsTable, TzlOffsetMap &iconOffsTable,
						  TDimension &res, int &version, QString &creator, TINT32 *_frameCount, TINT32 *_offsetTablePos, TINT32 *_iconOffsetTablePos, TLevelP level)
{
	TINT32 hdrSize;
	TINT32 lx = 0, ly = 0, frameCount = 0;
	char codec[4];
	TINT32 offsetTablePos;
	TINT32 iconOffsetTablePos;
	//char magic[8];

	assert(frameOffsTable.empty());
	assert(iconOffsTable.empty());

	if (!readVersion(chan, version))
		return false;

	// read creator
	if (version == 14) {
		char buffer[CREATOR_LENGTH + 1];
		memset(buffer, 0, sizeof buffer);
		fread(&buffer, sizeof(char), CREATOR_LENGTH, chan);
		creator = buffer;
	}

	fread(&hdrSize, sizeof(TINT32), 1, chan);
	fread(&lx, sizeof(TINT32), 1, chan);
	fread(&ly, sizeof(TINT32), 1, chan);
	fread(&frameCount, sizeof(TINT32), 1, chan);

	if (version > 10) {
		fread(&offsetTablePos, sizeof(TINT32), 1, chan);
		fread(&iconOffsetTablePos, sizeof(TINT32), 1, chan);
#if !TNZ_LITTLE_ENDIAN
		offsetTablePos = swapTINT32(offsetTablePos);
		iconOffsetTablePos = swapTINT32(iconOffsetTablePos);
#endif
	}

	fread(&codec, 4, 1, chan);

#if !TNZ_LITTLE_ENDIAN
	hdrSize = swapTINT32(hdrSize);
	lx = swapTINT32(lx);
	ly = swapTINT32(ly);
	frameCount = swapTINT32(frameCount);
#endif
	assert(0 < frameCount && frameCount < 60000);

	if (version > 10 && offsetTablePos != 0 && iconOffsetTablePos != 0) {
		//assert(offsetTablePos>0);
		assert(frameCount > 0);

		fseek(chan, offsetTablePos, SEEK_SET);
		TFrameId oldFid(TFrameId::EMPTY_FRAME);
		for (int i = 0; i < (int)frameCount; i++) {
			TINT32 number, offs, length;
			char letter;
			fread(&number, sizeof(TINT32), 1, chan);
			fread(&letter, sizeof(char), 1, chan);
			fread(&offs, sizeof(TINT32), 1, chan);
			if (version >= 12)
				fread(&length, sizeof(TINT32), 1, chan);

#if !TNZ_LITTLE_ENDIAN
			number = swapTINT32(number);
			offs = swapTINT32(offs);
			if (version == 12)
				length = swapTINT32(length);
#endif
			//		std::cout << "#" << i << std::hex << " n 0x" << number << " l 0x" << letter << " o 0x" << offs << std::dec << std::endl;

			TFrameId fid(number, letter);
			//assert(i==0 || oldFid<fid);

			if (version >= 12) {
				frameOffsTable[fid] = TzlChunk(offs, length);
			} else {
				frameOffsTable[fid] = TzlChunk(offs, 0);
				if (i > 0) {
					frameOffsTable[oldFid].m_length = offs - frameOffsTable[oldFid].m_offs;
					assert(frameOffsTable[oldFid].m_length > 0);
				}
				if (i == frameCount - 1) {
					frameOffsTable[fid].m_length = offsetTablePos - offs;
					assert(frameOffsTable[fid].m_length > 0);
				}
			}
			oldFid = fid;
			if (level)
				level->setFrame(fid, TImageP());
		}
		if (version >= 13) {
			// Build IconOffsetTable
			fseek(chan, iconOffsetTablePos, SEEK_SET);

			for (int i = 0; i < (int)frameCount; i++) {
				TINT32 number, thumbnailOffs, thumbnailLength;
				char letter;
				fread(&number, sizeof(TINT32), 1, chan);
				fread(&letter, sizeof(char), 1, chan);
				fread(&thumbnailOffs, sizeof(TINT32), 1, chan);
				fread(&thumbnailLength, sizeof(TINT32), 1, chan);

#if !TNZ_LITTLE_ENDIAN
				number = swapTINT32(number);
				thumbnailOffs = swapTINT32(thumbnailOffs);
				thumbnailLength = swapTINT32(thumbnailLength);
#endif
				TFrameId fid(number, letter);
				iconOffsTable[fid] = TzlChunk(thumbnailOffs, thumbnailLength);
			}
		}
	} else {
		//m_frameOffsTable.resize(frameCount);
		frameOffsTable[TFrameId(1)] = TzlChunk(ftell(chan), 0);
		iconOffsTable[TFrameId(1)] = TzlChunk(ftell(chan), 0);
		int i;
		for (i = 2; i <= (int)frameCount; i++) {
			frameOffsTable[TFrameId(i)] = TzlChunk(0, 0);
			iconOffsTable[TFrameId(i)] = TzlChunk(0, 0);
		}
		if (level)
			for (i = 1; i <= (int)frameCount; i++)
				level->setFrame(TFrameId(i), TImageP());
	}

	res.lx = lx;
	res.ly = ly;
	if (_offsetTablePos)
		*_offsetTablePos = offsetTablePos;
	if (_iconOffsetTablePos)
		*_iconOffsetTablePos = iconOffsetTablePos;

	if (_frameCount)
		*_frameCount = frameCount;

	return true;
}
}

bool adjustIconAspectRatio(TDimension &outDimension, TDimension inDimension, TDimension imageRes)
{
	TINT32 iconLx = inDimension.lx, iconLy = inDimension.ly;
	assert(iconLx > 0 && iconLy > 0);
	assert(imageRes.lx > 0 && imageRes.ly > 0);
	if (iconLx <= 0 || iconLy <= 0 || imageRes.lx <= 0 || imageRes.ly <= 0)
		return false;
	int lx = imageRes.lx;
	int ly = imageRes.ly;

	if (tmax(double(lx) / inDimension.lx, double(ly) / inDimension.ly) == double(ly) / inDimension.ly)
		iconLx = tround((double(lx) * inDimension.ly) / ly);
	else
		iconLy = tround((double(ly) * inDimension.lx) / lx);
	outDimension = TDimension(iconLx, iconLy);
	return true;
}

void getThumbnail(TRasterP ras, int shrink, TRasterP &thumbnail)
{
	int j = 0;
	int y = 0;
	TPixel32 *pix2 = (TPixel32 *)thumbnail->getRawData();
	for (j = 0; j < thumbnail->getLy(); j++) {
		ras->lock();
		TPixel32 *pix = (TPixel32 *)ras->getRawData() + y * ras->getLx();
		TPixel32 *endPix = pix2 + thumbnail->getLx();
		while (pix2 < endPix) {
			*pix2++ = *pix;
			pix += shrink;
		}
		y += shrink;
		ras->unlock();
	}
}

//-------------------------------------------------------------------

class TImageReaderTzl : public TImageReader
{
public:
	TImageReaderTzl(const TFilePath &f, const TFrameId &fid, TLevelReaderTzl *);
	~TImageReaderTzl() {}

private:
	// not implemented
	TImageReaderTzl(const TImageReaderTzl &);
	TImageReaderTzl &operator=(const TImageReaderTzl &src);
	TImageP load10();
	TImageP load11();
	TImageP load13(); // Aggiunta iconcine
	TImageP load14(); //	Aggiunto creator con lunghezza fissa
	const TImageInfo *getImageInfo10() const;
	const TImageInfo *getImageInfo11() const; // vale anche le versioni > di 11

public:
	//! Indice del frame del livello
	TFrameId m_fid;
	TImageP load();
	TImageP loadIcon()
	{
		m_isIcon = true;
		return load();
	}
	const TImageInfo *getImageInfo() const;

	//TImageP doLoad();

	TDimension getSize() const;
	TRect getBBox() const;

private:
	//! Size of image
	int m_lx, m_ly;
	bool m_isIcon;
	//! Reference to level reader
	TLevelReaderTzl *m_lrp;
};

//===================================================================
//
// TImageWriterTzl
//
//-------------------------------------------------------------------

class TImageWriterTzl : public TImageWriter
{
	//! Reference to level writer
	TLevelWriterTzl *m_lwp;
	TFrameId m_fid;
	TDimension m_iconSize; // Dimensioni dell'iconcina salvata all'interno del file tlv
						   //	In genere questo parametro viene settato come quello impostato dall'utente
						   // nelle preferenze.
public:
	TImageWriterTzl(TLevelWriterTzl *lw, TFrameId fid) : TImageWriter(TFilePath()), m_lwp(lw), m_fid(fid), m_iconSize(TDimension(80, 60)) {}
	~TImageWriterTzl() {}

private:
	// not implemented
	TImageWriterTzl(const TImageWriterTzl &);
	TImageWriterTzl &operator=(const TImageWriterTzl &src);

public:
	void save(const TImageP &img) { m_lwp->save(img, m_fid); }
};
/*
//===================================================================
//
// TWriterInfo
//
//-------------------------------------------------------------------


TWriterInfo *TWriterInfoTzl::create(const std::string &ext) 
{ 
  return new TWriterInfoTzl(); 
}

//-------------------------------------------------------------------

TWriterInfo *TWriterInfoTzl::clone() const 
{
  return new TWriterInfoTzl(*this); 
}

//-------------------------------------------------------------------
*/
namespace
{

class Header
{
public:
	enum RasType {
		Raster32RGBM,
		Raster64RGBM,
		Raster32CM,
		RasterGR8,
		RasterUnknown
	};

	int m_lx;
	int m_ly;
	RasType m_rasType;
};

} // namespace

//-------------------------------------------------------------------

//-------------------------------------------------------------------

void TLevelWriterTzl::buildFreeChunksTable()
{
	std::set<TzlChunk> occupiedChunks;
	TzlOffsetMap::const_iterator it1 = m_frameOffsTable.begin();
	TINT32 lastOccupiedPos = 0; // ultima posizione all'interno del file occupata dall'ultima immagine(grande o icona)

	while (it1 != m_frameOffsTable.end()) {
		occupiedChunks.insert(TzlChunk(it1->second.m_offs, it1->second.m_length));
		if (it1->second.m_offs + it1->second.m_length > lastOccupiedPos)
			lastOccupiedPos = it1->second.m_offs + it1->second.m_length - 1;
		it1++;
	}
	TzlOffsetMap::const_iterator iconIt1 = m_iconOffsTable.begin();
	while (iconIt1 != m_iconOffsTable.end()) {
		occupiedChunks.insert(TzlChunk(iconIt1->second.m_offs, iconIt1->second.m_length));
		if (iconIt1->second.m_offs + iconIt1->second.m_length > lastOccupiedPos)
			lastOccupiedPos = iconIt1->second.m_offs + iconIt1->second.m_length - 1;
		iconIt1++;
	}

	std::set<TzlChunk>::const_iterator it2 = occupiedChunks.begin();
	TINT32 curPos; // prima posizione utile nel file in cui vengono memorizzati i dati relativi alle immagini
	if (m_version == 13)
		curPos = 6 * sizeof(TINT32) + 4 * sizeof(char) + 8 * sizeof(char);
	else if (m_version == 14)
		curPos = 6 * sizeof(TINT32) + 4 * sizeof(char) + 8 * sizeof(char) + CREATOR_LENGTH * sizeof(char);
	else
		curPos = it2->m_offs;

	while (it2 != occupiedChunks.end()) {
		assert(it2->m_offs >= curPos);
		if (it2->m_offs > curPos)
			m_freeChunks.insert(TzlChunk(curPos, it2->m_offs - curPos));
		curPos = it2->m_offs + it2->m_length;
		it2++;
	}
	assert(lastOccupiedPos < m_offsetTablePos);
	if (lastOccupiedPos + 1 < m_offsetTablePos)
		m_freeChunks.insert(TzlChunk(lastOccupiedPos + 1, m_offsetTablePos - lastOccupiedPos));
}

//===================================================================
//
// TLevelWriterTzl
//
//-------------------------------------------------------------------

TLevelWriterTzl::TLevelWriterTzl(const TFilePath &path, TPropertyGroup *info)
	: TLevelWriter(path, info), m_headerWritten(false), m_creatorWritten(false), m_chan(0), m_frameCountPos(0), m_frameCount(0), m_palette(0), m_res(0, 0), m_exists(false), m_version(CURRENT_VERSION), m_updatedIconsSize(false), m_currentIconSize(0, 0), m_iconSize(TDimension(80, 60)), m_userIconSize(TDimension(80, 60)), m_adjustRatio(false), m_codec(new TRasterCodecLZO("LZO", true)), m_overwritePaletteFlag(true)
{
	m_path = path;
	m_palettePath = path.withNoFrame().withType("tpl");
	TFileStatus fs(path);
	m_magic = "TLV14B1a"; // actual version
	erasedFrame = false;
	// version TLV10B1a: first version
	// version TLV11B1a: added frameIds
	// version TLV12B1a: incremental writings
	// version TLV13B1a: added thumbnails
	// version TLV15B1a: add creator string (fixed size = CREATOR_LENGTH char)

	if (fs.doesExist()) {
		//if (!fs.isWritable())
		m_chan = fopen(path, "rb+");
		/*--- 誰かが開いている、または権限が無いとき ---*/
		if (!m_chan) {
			throw TSystemException(path, "can't fopen.");
		}
		/*--- TLVファイルのヘッダが正しく読めなかった場合 ---*/
		if (!readHeaderAndOffsets(m_chan,
								  m_frameOffsTable,
								  m_iconOffsTable,
								  m_res,
								  m_version,
								  m_creator,
								  &m_frameCount,
								  &m_offsetTablePos,
								  &m_iconOffsetTablePos,
								  0)) {
			throw TSystemException(path, "can't readHeaderAndOffsets.");
		} else {
			if (m_version >= 12)
				buildFreeChunksTable();
			m_headerWritten = true;
			m_exists = true;
			if (m_version == 14)
				m_frameCountPos = 8 + CREATOR_LENGTH + 3 * sizeof(TINT32);
			else
				m_frameCountPos = 8 + 3 * sizeof(TINT32);
		}
	}
	if (!m_exists) {
		TFilePath parentDir = path.getParentDir();
		if (!TFileStatus(parentDir).doesExist()) {
			try {
				TSystem::mkDir(parentDir);

			} catch (...) {
				return;
			}
		}
		m_chan = fopen(path, "wb");
		if (!m_chan)
			return;
		if (!writeVersionAndCreator(m_chan, m_magic, m_creator))
			return;
	}
}

//-------------------------------------------------------------------

TLevelWriterTzl::~TLevelWriterTzl()
{
	if (m_version < CURRENT_VERSION) {
		if (!convertToLatestVersion())
			return;
		assert(m_version == CURRENT_VERSION);
	}
	delete m_codec;

	TINT32 offsetMapPos = 0;
	TINT32 iconOffsetMapPos = 0;
	if (!m_chan)
		return;

	assert(m_frameCount == (int)m_frameOffsTable.size());
	assert(m_frameCount == (int)m_iconOffsTable.size());

	offsetMapPos = (m_exists ? m_offsetTablePos : ftell(m_chan));
	fseek(m_chan, offsetMapPos, SEEK_SET);

	TzlOffsetMap::iterator it = m_frameOffsTable.begin();
	for (; it != m_frameOffsTable.end(); ++it) {
		TFrameId fid = it->first;
		TINT32 num = fid.getNumber();
		char letter = fid.getLetter();
		TINT32 offs = it->second.m_offs;
		TINT32 length = it->second.m_length;
		tfwrite(&num, 1, m_chan);
		tfwrite(&letter, 1, m_chan);
		tfwrite(&offs, 1, m_chan);
		tfwrite(&length, 1, m_chan);
	}

	// Write Icon Offset Table after frameOffsTable
	iconOffsetMapPos = ftell(m_chan); //(m_exists?m_iconOffsetTablePos: ftell(m_chan));
	fseek(m_chan, iconOffsetMapPos, SEEK_SET);

	TzlOffsetMap::iterator iconIt = m_iconOffsTable.begin();
	for (; iconIt != m_iconOffsTable.end(); ++iconIt) {
		TFrameId fid = iconIt->first;
		TINT32 num = fid.getNumber();
		char letter = fid.getLetter();
		TINT32 thumbnailOffs = iconIt->second.m_offs;
		TINT32 thumbnailLength = iconIt->second.m_length;
		tfwrite(&num, 1, m_chan);
		tfwrite(&letter, 1, m_chan);
		tfwrite(&thumbnailOffs, 1, m_chan);
		tfwrite(&thumbnailLength, 1, m_chan);
	}

	fseek(m_chan, m_frameCountPos, SEEK_SET);
	TINT32 frameCount = m_frameCount;

	tfwrite(&frameCount, 1, m_chan);
	tfwrite(&offsetMapPos, 1, m_chan);
	tfwrite(&iconOffsetMapPos, 1, m_chan);
	fclose(m_chan);
	m_chan = 0;

	if (m_palette && m_overwritePaletteFlag && (m_palette->getDirtyFlag() || !TSystem::doesExistFileOrLevel(m_palettePath))) {
		TOStream os(m_palettePath);
		os << m_palette;
		m_palette->release();
	}

	if (m_contentHistory) {
		TFilePath historyFp = m_path.withNoFrame().withType("hst");
		FILE *historyChan = fopen(historyFp, "w");
		if (historyChan) {
			std::string historyData = m_contentHistory->serialize().toStdString();
			fwrite(&historyData[0], 1, historyData.length(), historyChan);
			fclose(historyChan);
		}
	}
	// Se lo spazio libero (cioè la somma di tutti i buchi che si sono creati tra i frame)
	// è maggiore di una certa soglia oppure è stato rimosso almeno un frame allora ottimizzo il file
	// (in pratica risalvo il file da capo senza buchi).
	if (getFreeSpace() > 0.3 || erasedFrame)
		optimize();
}

//-------------------------------------------------------------------

TImageWriterP TLevelWriterTzl::getFrameWriter(TFrameId fid)
{
	return new TImageWriterTzl(this, fid);
}

//-------------------------------------------------------------------

void TLevelWriterTzl::writeHeader(const TDimension &size)
{
	m_headerWritten = true;
	const char *codec = "LZO ";
	int codecLen = strlen(codec);
	TINT32 hdrSize = 3 * sizeof(TINT32) + codecLen;
	TINT32 lx = size.lx, ly = size.ly, intval = 1;

	tfwrite(&hdrSize, 1, m_chan);
	tfwrite(&lx, 1, m_chan);
	tfwrite(&ly, 1, m_chan);
	m_frameCountPos = ftell(m_chan);

	assert(m_frameCountPos == 8 + CREATOR_LENGTH + 3 * sizeof(TINT32));

	//I put the place for the frameCount, which I will write in this position at the end  (see in the distructor)
	tfwrite(&intval, 1, m_chan);
	//I put the place for the offsetTableOffset, which I will write in this position at the end  (see in the distructor)
	intval = 0;
	tfwrite(&intval, 1, m_chan);
	//I put the place for the iconOffsetTableOffset, which I will write in this position at the end  (see in the distructor)
	tfwrite(&intval, 1, m_chan);
	tfwrite(codec, codecLen, m_chan);
}

//-------------------------------------------------------------------

void TLevelWriterTzl::addFreeChunk(TINT32 offs, TINT32 length)
{
	std::set<TzlChunk>::iterator it = m_freeChunks.begin();
	while (it != m_freeChunks.end()) {
		//if (it->m_offs>offs+length+1)
		//  break;

		if (it->m_offs + it->m_length == offs) //accorpo due chunks in uno
		{
			TzlChunk chunk(it->m_offs, it->m_length + length);
			m_freeChunks.erase(it);
			m_freeChunks.insert(chunk);
			//it->m_length += length;
			return;
		} else if (offs + length == it->m_offs) {
			TzlChunk chunk(offs, it->m_length + length);
			m_freeChunks.erase(it);
			m_freeChunks.insert(chunk);
			//it->m_offs = offs;
			//it->m_length += length;
			return;
		}
		it++;
	}
	if (it == m_freeChunks.end())
		m_freeChunks.insert(TzlChunk(offs, length));
}
//-------------------------------------------------------------------

TINT32 TLevelWriterTzl::findSavingChunk(const TFrameId &fid, TINT32 length, bool isIcon)
{
	TzlOffsetMap::iterator it;
	//prima libero il chunk del fid, se c'e'. accorpo con altro chunk se trovo uno contiguo.
	if (!isIcon) {
		if ((it = m_frameOffsTable.find(fid)) != m_frameOffsTable.end()) {
			addFreeChunk(it->second.m_offs, it->second.m_length);
			m_frameOffsTable.erase(it);
		} else
			m_frameCount++;
	} else {
		if ((it = m_iconOffsTable.find(fid)) != m_iconOffsTable.end()) {
			addFreeChunk(it->second.m_offs, it->second.m_length);
			m_iconOffsTable.erase(it);
		}
	}

	//ora cerco un cioncone libero con la piu' piccola memoria sufficiente
	std::set<TzlChunk>::iterator it1 = m_freeChunks.begin(), found = m_freeChunks.end();
	for (; it1 != m_freeChunks.end(); it1++) {
		//   TINT32 _length = it1->m_length;

		if (it1->m_length >= length &&
			(found == m_freeChunks.end() ||
			 found->m_length > it1->m_length))
			found = it1;
	}

	if (found != m_freeChunks.end()) {
		//  TINT32 _length = found->m_length;
		TINT32 _offset = found->m_offs;
		if (found->m_length > length) {
			TzlChunk chunk(found->m_offs + length, found->m_length - length);
			m_freeChunks.insert(chunk);
			//found->m_offs+=length;
			//found->m_length-=length;
		} else
			assert(found->m_length == length);
		m_freeChunks.erase(found);
		return _offset;
	} else {
		m_offsetTablePos += length;
		return m_offsetTablePos - length;
	}
}
//-------------------------------------------------------------------
bool TLevelWriterTzl::convertToLatestVersion()
{
	TFileStatus fs(m_path);
	// se il file è di una versione precedente deve necessariamente già esistere su disco
	assert(fs.doesExist());
	if (!fs.doesExist())
		return false;
	m_exists = false;
	m_frameCount = 0;
	m_frameOffsTable = TzlOffsetMap();
	m_iconOffsTable = TzlOffsetMap();
	m_headerWritten = false;

	fclose(m_chan);
	m_chan = 0;

	std::string tempName = "~" + m_path.getName() + "tmp&.tlv";
	TFilePath tempPath = TSystem::getTempDir() + tempName;
	if (TSystem::doesExistFileOrLevel(m_path)) {
		assert(m_path != tempPath);
		if (TSystem::doesExistFileOrLevel(tempPath))
			TSystem::deleteFile(tempPath);
		TSystem::copyFile(tempPath, m_path);
	} else
		return false;
	m_chan = fopen(m_path, "rb+");
	if (!m_chan)
		return false;
	if (!writeVersionAndCreator(m_chan, m_magic, m_creator))
		return false;
	m_creatorWritten = true;
	m_version = CURRENT_VERSION;
	TLevelReaderP lr(tempPath);
	if (!lr)
		return false;
	TLevelP level = lr->loadInfo();
	for (TLevel::Iterator it = level->begin(); it != level->end(); ++it) {
		save(lr->getFrameReader(it->first)->load(), it->first);
	}
	lr = TLevelReaderP();
	if (TSystem::doesExistFileOrLevel(tempPath)) {
		TSystem::deleteFile(tempPath);
	}

	TINT32 offsetMapPos = 0;
	TINT32 iconOffsetMapPos = 0;
	if (!m_chan)
		return false;

	assert(m_frameCount == (int)m_frameOffsTable.size());
	assert(m_frameCount == (int)m_iconOffsTable.size());

	offsetMapPos = (m_exists ? m_offsetTablePos : ftell(m_chan));
	fseek(m_chan, offsetMapPos, SEEK_SET);

	TzlOffsetMap::iterator it2 = m_frameOffsTable.begin();
	for (; it2 != m_frameOffsTable.end(); ++it2) {
		TFrameId fid = it2->first;
		TINT32 num = fid.getNumber();
		char letter = fid.getLetter();
		TINT32 offs = it2->second.m_offs;
		TINT32 length = it2->second.m_length;
		tfwrite(&num, 1, m_chan);
		tfwrite(&letter, 1, m_chan);
		tfwrite(&offs, 1, m_chan);
		tfwrite(&length, 1, m_chan);
	}

	iconOffsetMapPos = ftell(m_chan);
	fseek(m_chan, iconOffsetMapPos, SEEK_SET);

	TzlOffsetMap::iterator iconIt = m_iconOffsTable.begin();
	for (; iconIt != m_iconOffsTable.end(); ++iconIt) {
		TFrameId fid = iconIt->first;
		TINT32 num = fid.getNumber();
		char letter = fid.getLetter();
		TINT32 thumbnailOffs = iconIt->second.m_offs;
		TINT32 thumbnailLength = iconIt->second.m_length;
		tfwrite(&num, 1, m_chan);
		tfwrite(&letter, 1, m_chan);
		tfwrite(&thumbnailOffs, 1, m_chan);
		tfwrite(&thumbnailLength, 1, m_chan);
	}

	fseek(m_chan, m_frameCountPos, SEEK_SET);
	TINT32 frameCount = m_frameCount;
	tfwrite(&frameCount, 1, m_chan);
	tfwrite(&offsetMapPos, 1, m_chan);
	tfwrite(&iconOffsetMapPos, 1, m_chan);
	m_frameOffsTable = TzlOffsetMap();
	m_iconOffsTable = TzlOffsetMap();
	m_frameCount = 0;
	m_res = TDimension();
	fclose(m_chan);
	m_chan = 0;

	if (m_palette && (m_palette->getDirtyFlag() || !TSystem::doesExistFileOrLevel(m_palettePath))) {
		TOStream os(m_palettePath);
		os << m_palette;
	}
	//else
	//	m_palette = 0;

	m_chan = fopen(m_path, "rb+");
	if (!m_chan)
		return false;
	if (!readHeaderAndOffsets(m_chan, m_frameOffsTable, m_iconOffsTable, m_res, m_version, m_creator, &m_frameCount, &m_offsetTablePos, &m_iconOffsetTablePos, 0))
		return false;
	m_freeChunks.clear();
	buildFreeChunksTable();
	m_headerWritten = true;
	m_exists = true;
	m_frameCountPos = 8 + CREATOR_LENGTH + 3 * sizeof(TINT32);
	assert(m_version == CURRENT_VERSION);
	if (!m_renumberTable.empty())
		renumberFids(m_renumberTable);
	return true;
}
//-------------------------------------------------------------------
void TLevelWriterTzl::saveImage(const TImageP &img, const TFrameId &_fid, bool isIcon)
{
	TFrameId fid = _fid;
	if (!m_chan)
		return;

	// se il file è di una versione precedente allora lo converto prima
	if (m_version < CURRENT_VERSION) {
		if (!convertToLatestVersion())
			return;
		assert(m_version == CURRENT_VERSION);
	}

	if (!m_updatedIconsSize && m_exists)
		m_updatedIconsSize = checkIconSize(getIconSize());

	TToonzImageP ti = img;
	if (!ti)
		return;

	if (!m_palette) {
		m_palette = ti->getPalette();
		if (m_palette)
			m_palette->addRef();
	}

	if (!m_creatorWritten) {
		fseek(m_chan, 0, SEEK_SET);
		writeVersionAndCreator(m_chan, m_magic, m_creator);
		m_creatorWritten = true;
	}
	if (!m_headerWritten)
		writeHeader(ti->getSize());

	if (m_res != TDimension())
		if (!isIcon)
			assert(m_res == ti->getSize());
		else
			assert(getIconSize() == ti->getSize());
	else
		m_res = ti->getSize();

	TRect sb = ti->getSavebox();

	TINT32 sbx0 = sb.x0, sby0 = sb.y0, sblx = sb.getLx(), sbly = sb.getLy();
	TINT32 buffSize = 0;

	UCHAR *buff = 0; //  = ti->getCompressedBuffer(buffSize);

	TRasterP cmras = ti->getRaster();

	assert(cmras);

	if (cmras->getBounds() != sb) {
		TRect rectToExtract = sb;
		if (!rectToExtract.isEmpty()) {
			TRasterP smallRaster = cmras->extract(rectToExtract)->clone();
			assert(rectToExtract == sb);
			cmras = smallRaster;
		} else {
			cmras = cmras->create(1, 1);
			cmras->clear();
		}
	}

	TRasterGR8P rCompressed = m_codec->compress(cmras, 1, buffSize);
	if (rCompressed == TRasterGR8P()) {
		static bool firstTime = true;
		if (firstTime) {
			firstTime = false;
			//MessageBox( NULL, "Cannot save level, out of memory!", "Warning!", MB_OK);
		}
		return;
	}

	rCompressed->lock();

	buffSize = rCompressed->getLx();
	buff = rCompressed->getRawData();

	assert(buffSize);
	assert(buff);

#if !TNZ_LITTLE_ENDIAN

	Header *header = (Header *)buff;

	TRasterP ras;
	m_codec->decompress(buff, buffSize, ras);
	delete[] buff;
	assert((TRasterCM32P)ras);
	assert(ras->getLx() == header->m_lx);
	assert(ras->getLy() == header->m_ly);

	for (int y = 0; y < ras->getLy(); ++y) {
		ras->lock();
		TINT32 *pix = ((TINT32 *)ras->getRawData()) + y * ras->getLx();
		TINT32 *endPix = pix + ras->getLx();
		while (pix < endPix) {
			*pix = swapTINT32(*pix);
			pix++;
		}
		ras->unlock();
	}

	rCompressed = m_codec->compress(ras, 1, buffSize);

	header = (Header *)buff;

	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	double xdpi = 1, ydpi = 1;
	ti->getDpi(xdpi, ydpi);

	TINT32 theBuffSize = buffSize;

	TINT32 length = 0;
	if (!isIcon)
		length = 5 * sizeof(TINT32) + 2 * sizeof(double) + theBuffSize;
	else
		length = 3 * sizeof(TINT32) + theBuffSize;

	if (fid == TFrameId()) {
		assert(!m_exists);
		if (m_frameOffsTable.empty())
			fid = (m_frameOffsTable.empty()) ? TFrameId(1, 0) : TFrameId(m_frameOffsTable.rbegin()->first.getNumber() + 1, 0);
		if (m_iconOffsTable.empty())
			fid = (m_iconOffsTable.empty()) ? TFrameId(1, 0) : TFrameId(m_iconOffsTable.rbegin()->first.getNumber() + 1, 0);
	}

	if (!m_exists) {
		long offs = ftell(m_chan);
		if (!isIcon) {
			m_frameOffsTable[fid] = TzlChunk(offs, length);
			m_frameCount++;
		} else
			m_iconOffsTable[fid] = TzlChunk(offs, length);

	} else {
		long frameOffset = findSavingChunk(fid, length, isIcon);
		if (!isIcon)
			m_frameOffsTable[fid] = TzlChunk(frameOffset, length);
		else
			m_iconOffsTable[fid] = TzlChunk(frameOffset, length);
		fseek(m_chan, frameOffset, SEEK_SET);
	}
	if (!isIcon) {
		tfwrite(&sbx0, 1, m_chan);
		tfwrite(&sby0, 1, m_chan);
		tfwrite(&sblx, 1, m_chan);
		tfwrite(&sbly, 1, m_chan);
		tfwrite(&buffSize, 1, m_chan);
		tfwrite(&xdpi, 1, m_chan);
		tfwrite(&ydpi, 1, m_chan);
		tfwrite(buff, theBuffSize, m_chan);
	} else {
		TINT32 iconLx = getIconSize().lx;
		TINT32 iconLy = getIconSize().ly;
		tfwrite(&iconLx, 1, m_chan);
		tfwrite(&iconLy, 1, m_chan);
		tfwrite(&buffSize, 1, m_chan);
		tfwrite(buff, theBuffSize, m_chan);
	}
	rCompressed->unlock();

	//#if !TNZ_LITTLE_ENDIAN
	//delete [] buff;
	//#endif
}

//-------------------------------------------------------------------

void TLevelWriterTzl::doSave(const TImageP &img, const TFrameId &_fid)
{
	// salvo l'immagine grande
	saveImage(img, _fid);
	if (!img)
		throw(TException("Saving tlv: it is not possible to create the image frame."));
	// creo l'iconcina
	TImageP icon = TImageP();
	createIcon(img, icon);
	assert(icon);
	if (!icon)
		throw(TException("Saving tlv: it is not possible to create the image icon."));
	// salvo l'iconcina
	saveImage(icon, _fid, true);
	return;
}

//-------------------------------------------------------------------
void TLevelWriterTzl::save(const TImageP &img, const TFrameId &fid)
{
	doSave(img, fid);
}
void TLevelWriterTzl::save(const TImageP &img)
{
	doSave(img, TFrameId());
}
//-------------------------------------------------------------------
void TLevelWriterTzl::saveIcon(const TImageP &img, const TFrameId &fid)
{
	// salvo su file come icona
	saveImage(img, fid, true);
}
//-------------------------------------------------------------------

void TLevelWriterTzl::setPalette(TPalette *palette)
{
	if (!m_palette) {
		m_palette = palette;
		m_palette->addRef();
	}
}
//-------------------------------------------------------------------
void TLevelWriterTzl::renumberFids(const std::map<TFrameId, TFrameId> &renumberTable)
{
	m_renumberTable = renumberTable;
	if (m_version < 13)
		return;
	if (m_frameOffsTable.empty() || !m_exists)
		return;

	// Rimozione dei frame non presenti nella prima colonna di renumberTable
	std::map<TFrameId, TzlChunk> frameOffsTableTemp;
	frameOffsTableTemp = m_frameOffsTable;
	TzlOffsetMap::iterator it = frameOffsTableTemp.begin();
	while (it != frameOffsTableTemp.end()) {
		TFrameId fid = it->first;
		std::map<TFrameId, TFrameId>::const_iterator it2 = renumberTable.find(fid);
		if (it2 == renumberTable.end())
			remove(fid);
		it++;
	}

	std::map<TFrameId, TzlChunk> frameOffsTable, iconOffsTable;
	// Renumber Frame
	std::map<TFrameId, TFrameId>::const_iterator it3 = renumberTable.begin();
	while (it3 != renumberTable.end()) {
		TFrameId fidToRenumber = it3->first;
		TzlOffsetMap::iterator it = m_frameOffsTable.find(fidToRenumber);
		TzlOffsetMap::iterator iconIt = m_iconOffsTable.find(fidToRenumber);
		if (it != m_frameOffsTable.end())
			frameOffsTable[it3->second] = it->second;
		if (m_iconOffsTable.size() > 0 && iconIt != m_iconOffsTable.end())
			iconOffsTable[it3->second] = iconIt->second;
		it3++;
	}

	m_frameOffsTable.clear();
	m_frameOffsTable = frameOffsTable;
	m_iconOffsTable.clear();
	m_iconOffsTable = iconOffsTable;
	m_frameCount = m_frameOffsTable.size();
	//buildFreeChunksTable();
}

//-------------------------------------------------------------------

void TLevelWriterTzl::createIcon(const TImageP &imgIn, TImageP &imgOut)
{
	//Creo iconcina e poi la salvo
	TToonzImageP ti = imgIn;
	if (!ti)
		return;
	TRect sb = ti->getSavebox();
	TINT32 sbx0 = sb.x0, sby0 = sb.y0, sblx = sb.getLx(), sbly = sb.getLy();
	TRasterP cmras = ti->getRaster();

	assert(m_iconSize.lx > 0 && m_iconSize.ly > 0);
	if (m_iconSize.lx <= 0 || m_iconSize.ly <= 0)
		return;
	TINT32 iconLx = m_iconSize.lx, iconLy = m_iconSize.ly;
	if (cmras->getLx() <= iconLx && cmras->getLy() <= iconLy) {
		iconLx = cmras->getLx();
		iconLy = cmras->getLy();
		m_iconSize = TDimension(iconLx, iconLy);
		imgOut = imgIn;
		return;
	}

	TRasterCM32P thumbnailRas(iconLx, iconLy);
	if (iconLx > 0 && iconLy > 0) {
		// Do thumbnail
		TRop::makeIcon(thumbnailRas, TRasterCM32P(cmras));
		assert(thumbnailRas);
		if (!thumbnailRas)
			return;
	}
	// Compute savebox
	if (ti->getSize().lx <= 0 || ti->getSize().ly <= 0)
		return;
	TRect tmp_savebox;
	TRect savebox;
	TRop::computeBBox(thumbnailRas, tmp_savebox);
	if (tmp_savebox.isEmpty()) {
		TINT32 iconsbx0 = tround((double)iconLx * sbx0 / ti->getSize().lx);
		TINT32 iconsby0 = tround((double)iconLy * sby0 / ti->getSize().ly);
		TINT32 iconsblx = tmax(tround((double)iconLx * sblx / ti->getSize().lx), 1);
		TINT32 iconsbly = tmax(tround((double)iconLy * sbly / ti->getSize().ly), 1);
		savebox = TRect(TPoint(iconsbx0, iconsby0), TDimension(iconsblx, iconsbly));
	} else {
		savebox = tmp_savebox;
	}

	if (!TRect(m_iconSize).contains(savebox)) //it should be better to use tfloor instead of tround in the previous lines:
											  //sometimes, for rounding problems, the savebox is outside 1 pixel the raster size.
											  //the best solution should be to replace tround with tfloor here
											  //and in the icon reading of tzl 1.4 (method load14),
											  //but this way the old 1.4 tzl are not readed correctly (crash!)
											  //so, this 'if' is a patch. vinz
		savebox = savebox * TRect(m_iconSize);
	thumbnailRas->clearOutside(savebox);
	TToonzImageP ticon(thumbnailRas, savebox);
	double xdpi = 1, ydpi = 1;
	ti->getDpi(xdpi, ydpi);
	ticon->setDpi(xdpi, ydpi);
	ticon->setPalette(ti->getPalette());
	imgOut = TImageP(ticon);
}

//-------------------------------------------------------------------

void TLevelWriterTzl::remove(const TFrameId &fid)
{
	TzlOffsetMap::iterator it = m_frameOffsTable.find(fid);
	// se il fid non esiste non faccio nulla
	if (it == m_frameOffsTable.end())
		return;
	// aggiungo spazio vuoto
	//m_freeChunks.insert(TzlChunk(it->second.m_offs, it->second.m_length));
	addFreeChunk(it->second.m_offs, it->second.m_length);
	// cancello l'immagine dalla tabella
	m_frameOffsTable.erase(it);

	if (m_iconOffsTable.size() > 0) {
		TzlOffsetMap::iterator iconIt = m_iconOffsTable.find(fid);
		// deve necessariamente esserci anche l'iconcina
		assert(iconIt != m_iconOffsTable.end());
		if (iconIt == m_iconOffsTable.end())
			return;
		// aggiungo spazio vuoto
		//m_freeChunks.insert(TzlChunk(iconIt->second.m_offs, iconIt->second.m_length));
		addFreeChunk(iconIt->second.m_offs, iconIt->second.m_length);
		// Cancello la relativa icona
		m_iconOffsTable.erase(iconIt);
		erasedFrame = true;
	}
}

//-------------------------------------------------------------------

void TLevelWriterTzl::setIconSize(TDimension iconSize)
{
	assert(iconSize.lx > 0 && iconSize.ly > 0);

	m_iconSize = TDimension(iconSize.lx, iconSize.ly);
	m_userIconSize = TDimension(iconSize.lx, iconSize.ly); // Used in TLevelWriterTzl::optimize().
														   // Observe that m_iconSize may change below.
	if (m_version >= 13 && m_exists) {
		// A supported level file already exists at the specified path

		// Check whether icons stored in the file already
		// have the required size
		if (!m_updatedIconsSize)							// m_updatedIconsSize tells whether
			m_updatedIconsSize = checkIconSize(m_iconSize); // icon sizes in the file match m_iconSize

		if (!m_updatedIconsSize) {
			// Icons in the stored file mismatch with the
			// required size. They need to be resized.
			m_updatedIconsSize = resizeIcons(m_iconSize);
			assert(m_updatedIconsSize);
		}
	}
}

//-------------------------------------------------------------------

/*!
  \brief    Checks the icons size stored inside an already existing level
            file, and compares it with the specified newSize.
  \return   Whether the specified newSize \a matches the icons size in
            the level file.
*/

bool TLevelWriterTzl::checkIconSize(const TDimension &newSize)
{
	assert(m_version >= 13);

	if (!m_exists ||
		m_iconOffsTable.empty() ||
		!m_chan ||
		m_version < 13)
		return false;

	// Read the size of icons in the file
	TINT32 iconLx = 0, iconLy = 0;

	long currentPos = ftell(m_chan); // Backup current reading position in the file

	TzlOffsetMap::iterator it = m_iconOffsTable.begin();
	long offs = it->second.m_offs;

	fseek(m_chan, offs, SEEK_SET);

	fread(&iconLx, sizeof(TINT32), 1, m_chan);
	fread(&iconLy, sizeof(TINT32), 1, m_chan);

	fseek(m_chan, currentPos, SEEK_SET); // Reset to the original position in the file

	assert(iconLx > 0 && iconLy > 0);
	if (iconLx <= 0 || iconLy <= 0 || iconLx > m_res.lx || iconLy > m_res.ly)
		return false;

	m_currentIconSize = TDimension(iconLx, iconLy); // This is not performed if the above pre-emptive
													// bailout kicks in. BTW, m_currentIconSize
													// is currently UNUSED! Should be cleaned up...
	// Compare newSize against read icon size in the file
	return (m_currentIconSize == newSize);
}

//-------------------------------------------------------------------

bool TLevelWriterTzl::resizeIcons(const TDimension &newSize)
{
	if (!m_exists)
		return false;
	if (m_iconOffsTable.empty())
		return false;
	if (!m_chan)
		return false;
	assert(m_version >= 13);

	// faccio una copia di m_path per poi usarla per la resizeIcons()
	fclose(m_chan);
	m_chan = 0;
	TFileStatus fs(m_path);
	assert(fs.doesExist());
	if (!fs.doesExist())
		return false;
	std::string tempName = "~" + m_path.getName() + "tmpIcon&.tlv";
	TFilePath tempPath = TSystem::getTempDir() + tempName;
	if (TSystem::doesExistFileOrLevel(m_path)) {
		assert(m_path != tempPath);
		if (TSystem::doesExistFileOrLevel(tempPath))
			TSystem::deleteFile(tempPath);
		TSystem::copyFile(tempPath, m_path);
	} else
		return false;
	m_chan = fopen(m_path, "rb+");
	assert(m_chan);

	assert(TSystem::doesExistFileOrLevel(tempPath));
	if (!TSystem::doesExistFileOrLevel(tempPath))
		return false;
	TLevelReaderP lr(tempPath);
	TLevelP level = lr->loadInfo();
	/*
	if(m_currentIconSize.lx>newSize.lx && m_currentIconSize.ly>newSize.ly)
	{
		for(TLevel::Iterator it=level->begin(); it != level->end(); ++it)
		{
			TImageReaderP ir = lr->getFrameReader(it->first);
			
			// carico l'iconcina
		  TImageP img = ir->loadIcon();
			TImageP icon;
			createIcon(img,icon);
			saveIcon(icon,it->first);			
		}
	}
	else
	{				 */
	// leggo tutte le immagini da file e creo le iconcine da da zero
	for (TLevel::Iterator it = level->begin(); it != level->end(); ++it) {
		// carico l'iconcina
		TImageP img = lr->getFrameReader(it->first)->load();
		// creo e salvo
		TImageP icon;
		createIcon(img, icon);
		saveIcon(icon, it->first);
	}

	//}
	lr = TLevelReaderP();
	// delete m_tempPath
	if (TSystem::doesExistFileOrLevel(tempPath)) {
		TSystem::deleteFile(tempPath);
	}

	return true;
}

float TLevelWriterTzl::getFreeSpace()
{
	if (m_exists && m_version >= 13) {
		TINT32 freeSpace = 0;
		std::set<TzlChunk>::iterator it = m_freeChunks.begin();
		for (; it != m_freeChunks.end(); ++it)
			freeSpace += it->m_length;

		TINT32 totalSpace = 0;
		if (m_version == 13)
			totalSpace = m_offsetTablePos - 6 * sizeof(TINT32) - 4 * sizeof(char) - 8 * sizeof(char);
		else if (m_version == 14)
			totalSpace = m_offsetTablePos - 6 * sizeof(TINT32) - 4 * sizeof(char) - 8 * sizeof(char) - CREATOR_LENGTH * sizeof(char);
		assert(totalSpace > 0);
		return (float)freeSpace / totalSpace;
	}
	return 0;
}
// creo il file ottimizzato, cioè senza spazi liberi
bool TLevelWriterTzl::optimize()
{
	TFileStatus fs(m_path);
	assert(fs.doesExist());

	std::string tempName = "~" + m_path.getName() + "tmp&.tlv";
	TFilePath tempPath = TSystem::getTempDir() + tempName; // Path temporaneo del file ottimizzato

	if (TSystem::doesExistFileOrLevel(tempPath))
		TSystem::deleteFile(tempPath);

	TLevelWriterP lw(tempPath);

	TLevelReaderP lr(m_path);
	lw->setIconSize(m_userIconSize);
	TLevelP level = lr->loadInfo();
	if (!level || level->getFrameCount() == 0)
		return false;
	TLevel::Iterator levelIt = level->begin();
	for (levelIt; levelIt != level->end(); ++levelIt) {
		TToonzImageP img = lr->getFrameReader(levelIt->first)->load();
		assert(img);
		lw->getFrameWriter(levelIt->first)->save(img);
	}
	lr = TLevelReaderP();
	lw = TLevelWriterP();

	// Se il file tempPath (ottimizzato) è stato creato
	// lo copio sostituendolo ad m_path
	if (TSystem::doesExistFileOrLevel(tempPath)) {
		assert(m_path != tempPath);
		if (TSystem::doesExistFileOrLevel(m_path))
			TSystem::deleteFile(m_path); // Cancello il file non ottimizzato
		TSystem::copyFile(m_path, tempPath);
		TSystem::deleteFile(tempPath);
	} else
		return false;
	return true;
}

//===================================================================
//
// TLevelReaderTzl
//
//-------------------------------------------------------------------

void TLevelReaderTzl::readPalette()
{
	TFilePath pltfp = m_path.withNoFrame().withType("tpl");
	TFileStatus fs(pltfp);
	TPersist *p = 0;
	TIStream is(pltfp);
	TPalette *palette = 0;

	if (is && fs.doesExist()) {
		is >> p;
		palette = dynamic_cast<TPalette *>(p);
	}

	if (!palette) {
		int i;
		palette = new TPalette();
		for (i = 1; i < 128 + 32; i++)
			palette->addStyle(TPixel32(127, 150, 255));

		for (i = 0; i < 10; i++)
			palette->getPage(0)->addStyle(i + 1);
		for (i = 0; i < 10; i++)
			palette->getPage(0)->addStyle(128 + i);
	}
	if (m_level)
		m_level->setPalette(palette);
}

//---------------------------------------------------

TLevelReaderTzl::TLevelReaderTzl(const TFilePath &path)
	: TLevelReader(path), m_chan(0), m_res(0, 0), m_xDpi(0), m_yDpi(0), m_version(0), m_frameOffsTable(), m_iconOffsTable(), m_level(), m_readPalette(true)
{

	m_chan = fopen(path, "rb");

	if (!m_chan)
		return;

	if (!readHeaderAndOffsets(m_chan, m_frameOffsTable, m_iconOffsTable, m_res, m_version, m_creator, 0, 0, 0, m_level))
		return;

	TFilePath historyFp = path.withNoFrame().withType("hst");
	FILE *historyChan = fopen(historyFp, "r");
	if (historyChan) {
		// read the whole file into historyData string
		fseek(historyChan, 0, SEEK_END);
		long lSize = ftell(historyChan);
		rewind(historyChan);
		std::string historyData(lSize, '\0');
		fread(&historyData[0], 1, lSize, historyChan);
		fclose(historyChan);

		if (!m_contentHistory)
			m_contentHistory = new TContentHistory(true);
		m_contentHistory->deserialize(QString::fromStdString(historyData));
	}

	// UCHAR *imgBuff = new UCHAR[imgBuffSize];

	// fclose(m_chan);
	// m_chan = 0;
}

//-------------------------------------------------------------------

void TLevelReaderTzl::doReadPalette(bool doReadIt)
{
	m_readPalette = doReadIt;
}

//-------------------------------------------------------------------

TLevelReaderTzl::~TLevelReaderTzl()
{
	if (m_chan)
		fclose(m_chan);
	m_chan = 0;
}

//-------------------------------------------------------------------

TLevelP TLevelReaderTzl::loadInfo()
{
	if (m_level && m_level->getPalette() == 0 && m_readPalette)
		readPalette();
	return m_level;
}

//-------------------------------------------------------------------

TImageReaderP TLevelReaderTzl::getFrameReader(TFrameId fid)
{
	if (m_level && m_level->getPalette() == 0 && m_readPalette)
		readPalette();

	return new TImageReaderTzl(getFilePath(), fid, this);
}

//-------------------------------------------------------------------
QString TLevelReaderTzl::getCreator()
{
	if (m_version < 14)
		return "";
	return m_creator;
}

//-------------------------------------------------------------------
bool TLevelReaderTzl::getIconSize(TDimension &iconSize)
{
	if (m_iconOffsTable.empty())
		return false;
	if (m_version < 13)
		return false;
	assert(m_chan);
	TINT32 currentPos = ftell(m_chan);
	TzlOffsetMap::iterator it = m_iconOffsTable.begin();
	TINT32 offs = it->second.m_offs;

	fseek(m_chan, offs, SEEK_SET);
	TINT32 iconLx = 0, iconLy = 0;
	// leggo la dimensione delle iconcine nel file
	fread(&iconLx, sizeof(TINT32), 1, m_chan);
	fread(&iconLy, sizeof(TINT32), 1, m_chan);
	assert(iconLx > 0 && iconLy > 0);
	// ritorno alla posizione corrente
	fseek(m_chan, currentPos, SEEK_SET);
	iconSize = TDimension(iconLx, iconLy);
	return true;
}

//===================================================================
//
// TImageReaderTzl
//
//-------------------------------------------------------------------

TImageReaderTzl::TImageReaderTzl(
	const TFilePath &f,
	const TFrameId &fid,
	TLevelReaderTzl *lr)
	: TImageReader(f), m_fid(fid), m_lrp(lr), m_lx(lr->m_res.lx), m_ly(lr->m_res.ly), m_isIcon(false)
{
}

//-------------------------------------------------------------------

TImageP TImageReaderTzl::load10()
{
	FILE *chan = m_lrp->m_chan;

	if (!chan)
		return TImageP();

	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0, sby0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	TINT32 imgBuffSize = 0;
	UCHAR *imgBuff = 0;
	int frameIndex = m_fid.getNumber();
	//int pos = ftell(chan);
	/*if(m_lrp->m_frameIndex>=m_frameIndex)
    {
     fseek(chan, m_lrp->m_framesOffset, SEEK_SET);
     m_lrp->m_frameIndex = 0;
    }
  int k = m_lrp->m_frameIndex + 1;
  m_lrp->m_frameIndex = m_frameIndex;*/

	int k;
	if (m_lrp->m_frameOffsTable[TFrameId(frameIndex)].m_offs == 0) {
		int i;
		for (i = 2; m_lrp->m_frameOffsTable[TFrameId(i)].m_offs != 0 && i <= m_fid.getNumber(); i++)
			;
		if (i == frameIndex)
			i = 2;

		fseek(chan, m_lrp->m_frameOffsTable[TFrameId(i - 1)].m_offs, SEEK_SET);
		k = i - 1;
	} else {
		fseek(chan, m_lrp->m_frameOffsTable[TFrameId(frameIndex)].m_offs, SEEK_SET);
		k = frameIndex;
	}
	for (; k <= frameIndex; k++) {
		m_lrp->m_frameOffsTable[TFrameId(k)] = TzlChunk(ftell(chan), 0);

		fread(&sbx0, sizeof(TINT32), 1, chan);
		fread(&sby0, sizeof(TINT32), 1, chan);
		fread(&sblx, sizeof(TINT32), 1, chan);
		fread(&sbly, sizeof(TINT32), 1, chan);
		fread(&actualBuffSize, sizeof(TINT32), 1, chan);
		fread(&xdpi, sizeof(double), 1, chan);
		fread(&ydpi, sizeof(double), 1, chan);

#if !TNZ_LITTLE_ENDIAN
		sbx0 = swapTINT32(sbx0);
		sby0 = swapTINT32(sby0);
		sblx = swapTINT32(sblx);
		sbly = swapTINT32(sbly);
		actualBuffSize = swapTINT32(actualBuffSize);
		reverse((char *)&xdpi, sizeof(double));
		reverse((char *)&ydpi, sizeof(double));
#endif

		imgBuffSize = m_lx * m_ly * sizeof(TPixelCM32);
		assert(actualBuffSize <= imgBuffSize);

		delete[] imgBuff;
		imgBuff = new UCHAR[imgBuffSize];
		//int ret =
		fread(imgBuff, actualBuffSize, 1, chan);
		//assert(ret==1);
	}

	Header *header = (Header *)imgBuff;

#if !TNZ_LITTLE_ENDIAN
	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	TRasterCodecLZO codec("LZO", false);
	TRasterP ras;
	if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
		return TImageP();
	assert((TRasterCM32P)ras);
	assert(ras->getLx() == header->m_lx);
	assert(ras->getLy() == header->m_ly);

#if !TNZ_LITTLE_ENDIAN
	ras->lock();
	for (int y = 0; y < ras->getLy(); ++y) {

		TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
		TINT32 *endPix = pix + ras->getLx();
		while (pix < endPix) {
			*pix = swapTINT32(*pix);
			pix++;
		}
	}
	ras->unlock();
// codec.compress(ras, 1, &imgBuff, actualBuffSize);
#endif

	TRect savebox(TPoint(sbx0, sby0), TDimension(sblx, sbly));
	TDimension imgSize(m_lrp->m_res.lx, m_lrp->m_res.ly);
	assert(TRect(imgSize).contains(savebox));
	if (imgSize != savebox.getSize() && !savebox.isEmpty()) {
		assert(TRect(imgSize).contains(savebox));
		TRasterCM32P fullRas(imgSize);
		TPixelCM32 bgColor;
		fullRas->fillOutside(savebox, bgColor);
		assert(savebox.getSize() == ras->getSize());
		fullRas->extractT(savebox)->copy(ras);
		ras = fullRas;
	}

	delete[] imgBuff;

	// codec.decompress(m_compressedBuffer, m_compressedBufferSize, ras);

	// assert(0);
	// TToonzImageP ti; //  = new TToonzImage(m_lx,m_ly,savebox,imgBuff,actualBuffSize);
	TToonzImageP ti(ras, savebox);
	// if(dpiflag)
	ti->setDpi(xdpi, ydpi);
	// m_lrp->m_level->setFrame(TFrameId(m_frameIndex+1), ti);
	ti->setPalette(m_lrp->m_level->getPalette());
	//delete [] imgBuff;
	//imgBuff = 0;
	return ti;

	//ToonzImageUtils::updateRas32(ti);
}

//-------------------------------------------------------------------

TImageP TImageReaderTzl::load11()
{
	FILE *chan = m_lrp->m_chan;

	if (!chan)
		return TImageP();

	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0, sby0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	TINT32 imgBuffSize = 0;
	UCHAR *imgBuff = 0;

	assert(!m_lrp->m_frameOffsTable.empty());

	//int pos = ftell(chan);
	/*if(m_lrp->m_frameIndex>=m_frameIndex)
    {
     fseek(chan, m_lrp->m_framesOffset, SEEK_SET);
     m_lrp->m_frameIndex = 0;
    }
  int k = m_lrp->m_frameIndex + 1;
  m_lrp->m_frameIndex = m_frameIndex;*/

	TzlOffsetMap::iterator it = m_lrp->m_frameOffsTable.find(m_fid);

	if (it == m_lrp->m_frameOffsTable.end())
		return 0;

	fseek(chan, it->second.m_offs, SEEK_SET);

	fread(&sbx0, sizeof(TINT32), 1, chan);
	fread(&sby0, sizeof(TINT32), 1, chan);
	fread(&sblx, sizeof(TINT32), 1, chan);
	fread(&sbly, sizeof(TINT32), 1, chan);
	fread(&actualBuffSize, sizeof(TINT32), 1, chan);

	fread(&xdpi, sizeof(double), 1, chan);
	fread(&ydpi, sizeof(double), 1, chan);

#if !TNZ_LITTLE_ENDIAN
	sbx0 = swapTINT32(sbx0);
	sby0 = swapTINT32(sby0);
	sblx = swapTINT32(sblx);
	sbly = swapTINT32(sbly);
	actualBuffSize = swapTINT32(actualBuffSize);
	reverse((char *)&xdpi, sizeof(double));
	reverse((char *)&ydpi, sizeof(double));
#endif

	imgBuffSize = m_lx * m_ly * sizeof(TPixelCM32);
	assert(actualBuffSize <= imgBuffSize);

	imgBuff = new UCHAR[imgBuffSize];
	//int ret =
	fread(imgBuff, actualBuffSize, 1, chan);
	//assert(ret==1);

	Header *header = (Header *)imgBuff;

#if !TNZ_LITTLE_ENDIAN
	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	TRasterCodecLZO codec("LZO", false);
	TRasterP ras;
	if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
		return TImageP();
	assert((TRasterCM32P)ras);
	assert(ras->getLx() == header->m_lx);
	assert(ras->getLy() == header->m_ly);

#if !TNZ_LITTLE_ENDIAN

	for (int y = 0; y < ras->getLy(); ++y) {
		ras->lock();
		TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
		TINT32 *endPix = pix + ras->getLx();
		while (pix < endPix) {
			*pix = swapTINT32(*pix);
			pix++;
		}
		ras->unlock();
	}

// codec.compress(ras, 1, &imgBuff, actualBuffSize);
#endif

	TRect savebox(TPoint(sbx0, sby0), TDimension(sblx, sbly));
	TDimension imgSize(m_lrp->m_res.lx, m_lrp->m_res.ly);
	assert(TRect(imgSize).contains(savebox));
	if (imgSize != savebox.getSize() && !savebox.isEmpty()) {
		assert(TRect(imgSize).contains(savebox));
		TRasterCM32P fullRas(imgSize);
		TPixelCM32 bgColor;
		fullRas->fillOutside(savebox, bgColor);
		assert(savebox.getSize() == ras->getSize());
		fullRas->extractT(savebox)->copy(ras);
		ras = fullRas;
	}

	delete[] imgBuff;

	// codec.decompress(m_compressedBuffer, m_compressedBufferSize, ras);

	// assert(0);
	// TToonzImageP ti; //  = new TToonzImage(m_lx,m_ly,savebox,imgBuff,actualBuffSize);
	TToonzImageP ti(ras, savebox);
	// if(dpiflag)
	ti->setDpi(xdpi, ydpi);
	// m_lrp->m_level->setFrame(TFrameId(m_frameIndex+1), ti);
	ti->setPalette(m_lrp->m_level->getPalette());
	//delete [] imgBuff;
	//imgBuff = 0;
	return ti;

	//ToonzImageUtils::updateRas32(ti);
}

//-------------------------------------------------------------------

TImageP TImageReaderTzl::load13()
{
	FILE *chan = m_lrp->m_chan;

	if (!chan)
		return TImageP();
	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0, sby0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	TINT32 imgBuffSize = 0;
	UCHAR *imgBuff = 0;
	TINT32 iconLx = 0, iconLy = 0;
	assert(!m_lrp->m_frameOffsTable.empty());
	assert(!m_lrp->m_iconOffsTable.empty());
	TzlOffsetMap::iterator it = m_lrp->m_frameOffsTable.find(m_fid);
	TzlOffsetMap::iterator iconIt = m_lrp->m_iconOffsTable.find(m_fid);

	if (it == m_lrp->m_frameOffsTable.end() || iconIt == m_lrp->m_iconOffsTable.end())
		return 0;

	fseek(chan, it->second.m_offs, SEEK_SET);
	fread(&sbx0, sizeof(TINT32), 1, chan);
	fread(&sby0, sizeof(TINT32), 1, chan);
	fread(&sblx, sizeof(TINT32), 1, chan);
	fread(&sbly, sizeof(TINT32), 1, chan);
	fread(&actualBuffSize, sizeof(TINT32), 1, chan);
	fread(&xdpi, sizeof(double), 1, chan);
	fread(&ydpi, sizeof(double), 1, chan);

#if !TNZ_LITTLE_ENDIAN
	sbx0 = swapTINT32(sbx0);
	sby0 = swapTINT32(sby0);
	sblx = swapTINT32(sblx);
	sbly = swapTINT32(sbly);
	actualBuffSize = swapTINT32(actualBuffSize);
	reverse((char *)&xdpi, sizeof(double));
	reverse((char *)&ydpi, sizeof(double));
#endif
	// Carico l'icona dal file
	if (m_isIcon) {
		fseek(chan, iconIt->second.m_offs, SEEK_SET);
		fread(&iconLx, sizeof(TINT32), 1, chan);
		fread(&iconLy, sizeof(TINT32), 1, chan);
		assert(iconLx > 0 && iconLy > 0);
		if (iconLx <= 0 || iconLy <= 0)
			throw TException();
		fread(&actualBuffSize, sizeof(TINT32), 1, chan);

		imgBuffSize = (iconLx * iconLy * sizeof(TPixelCM32));
		imgBuff = new UCHAR[imgBuffSize];
		fread(imgBuff, actualBuffSize, 1, chan);

#if !TNZ_LITTLE_ENDIAN
		Header *header = (Header *)imgBuff;
		header->m_lx = swapTINT32(header->m_lx);
		header->m_ly = swapTINT32(header->m_ly);
		header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif
		TRasterCodecLZO codec("LZO", false);
		TRasterP ras;
		if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
			return TImageP();
		assert((TRasterCM32P)ras);
		delete[] imgBuff;

#if !TNZ_LITTLE_ENDIAN

		for (int y = 0; y < ras->getLy(); ++y) {
			ras->lock();
			TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
			TINT32 *endPix = pix + ras->getLx();
			while (pix < endPix) {
				*pix = swapTINT32(*pix);
				pix++;
			}
			ras->unlock();
		}
#endif

		/*
			TINT32 iconsbx0 = tround((double)iconLx*sbx0/m_lrp->m_res.lx);
			TINT32 iconsby0 = tround((double)iconLy*sby0/m_lrp->m_res.ly); 
			TRect savebox(TPoint(iconsbx0,iconsby0), TDimension(ras->getLx(),ras->getLy()));
			 */

		// Compute savebox

		TINT32 iconsbx0 = tround((double)iconLx * sbx0 / m_lrp->m_res.lx);
		TINT32 iconsby0 = tround((double)iconLy * sby0 / m_lrp->m_res.ly); //+tround((double)(iconLy-ly)/2);
		TINT32 iconsblx = tround((double)iconLx * sblx / m_lrp->m_res.lx);
		TINT32 iconsbly = tround((double)iconLy * sbly / m_lrp->m_res.ly);
		TRect savebox(TPoint(iconsbx0, iconsby0), TDimension(iconsblx, iconsbly));

		//int ly = tround((double)m_lrp->m_res.ly*iconLx/m_lrp->m_res.lx);
		//TRect rect(TPoint(0,tround((double)(iconLy-ly)/2)),TDimension(iconLx,ly));
		//TPixelCM32 bgColorRect(1,0,100);

		TDimension imgSize(iconLx, iconLy);
		assert(TRect(imgSize).contains(savebox));

		if (imgSize != savebox.getSize() && !savebox.isEmpty()) {
			assert(TRect(imgSize).contains(savebox));
			TRasterCM32P fullRas(imgSize);
			TPixelCM32 bgColor;

			fullRas->fillOutside(savebox, bgColor);

			//fullRas->extractT(rect)->fill(bgColorRect);
			assert(savebox.getSize() == ras->getSize());
			fullRas->extractT(savebox)->copy(ras);
			ras = fullRas;
		}

		TToonzImageP ti(ras, savebox);
		ti->setDpi(xdpi, ydpi);
		ti->setPalette(m_lrp->m_level->getPalette());
		return ti;
	}

	TRasterCM32P raux = TRasterCM32P(m_lx, m_ly);
	raux->lock();
	imgBuff = (UCHAR *)raux->getRawData(); //new UCHAR[imgBuffSize];
	//imgBuff = new UCHAR[imgBuffSize];
	//imgBuffSize = m_lx*m_ly*sizeof(TPixelCM32);
	//assert(actualBuffSize <= imgBuffSize);

	//imgBuff = new UCHAR[imgBuffSize];
	//int ret =
	fread(imgBuff, actualBuffSize, 1, chan);
	//assert(ret==1);

	Header *header = (Header *)imgBuff;

#if !TNZ_LITTLE_ENDIAN
	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	TRasterCodecLZO codec("LZO", false);
	TRasterP ras;
	if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
		return TImageP();
	assert((TRasterCM32P)ras);
	assert(ras->getLx() == header->m_lx);
	assert(ras->getLy() == header->m_ly);

#if !TNZ_LITTLE_ENDIAN

	for (int y = 0; y < ras->getLy(); ++y) {
		ras->lock();
		TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
		TINT32 *endPix = pix + ras->getLx();
		while (pix < endPix) {
			*pix = swapTINT32(*pix);
			pix++;
		}
		ras->unlock();
	}

// codec.compress(ras, 1, &imgBuff, actualBuffSize);
#endif

	TRect savebox(TPoint(sbx0, sby0), TDimension(sblx, sbly));
	TDimension imgSize(m_lrp->m_res.lx, m_lrp->m_res.ly);
	assert(TRect(imgSize).contains(savebox));
	if (imgSize != savebox.getSize() && !savebox.isEmpty()) {
		assert(TRect(imgSize).contains(savebox));
		TRasterCM32P fullRas(imgSize);
		TPixelCM32 bgColor;
		fullRas->fillOutside(savebox, bgColor);
		assert(savebox.getSize() == ras->getSize());
		fullRas->extractT(savebox)->copy(ras);
		ras = fullRas;
	}
	raux->unlock();
	raux = TRasterCM32P();

	//delete [] imgBuff;

	// codec.decompress(m_compressedBuffer, m_compressedBufferSize, ras);

	// assert(0);
	// TToonzImageP ti; //  = new TToonzImage(m_lx,m_ly,savebox,imgBuff,actualBuffSize);
	TToonzImageP ti(ras, savebox);
	// if(dpiflag)
	ti->setDpi(xdpi, ydpi);
	// m_lrp->m_level->setFrame(TFrameId(m_frameIndex+1), ti);
	ti->setPalette(m_lrp->m_level->getPalette());
	//delete [] imgBuff;
	//imgBuff = 0;
	return ti;

	//ToonzImageUtils::updateRas32(ti);
}

// Restituisce la regione del raster shrinkata e la relativa savebox.
TRect applyShrinkAndRegion(TRasterP &ras, int shrink, TRect region, TRect savebox)
{
	// estraggo la regione solo se essa ha coordinate valide.
	if (!region.isEmpty() && region != TRect() && region.getLx() > 0 && region.getLy() > 0)
		ras = ras->extract(region);
	else
		region = TRect(0, 0, ras->getLx() - 1, ras->getLy() - 1);
	if (shrink > 1) {
		ras = TRop::shrink(ras, shrink);
	}
	// calcolo la nuova savebox
	savebox *= region;
	if (savebox == TRect() || savebox.getLx() <= 0 || savebox.getLy() <= 0)
		return TRect();
	int firstColIndexOfLayer = (savebox.x0 - region.x0) - 1 + shrink - (abs(savebox.x0 - region.x0 - 1) % shrink);
	int firstRowIndexOfLayer = (savebox.y0 - region.y0) - 1 + shrink - (abs(savebox.y0 - region.y0 - 1) % shrink);
	savebox.x0 = (firstColIndexOfLayer) / shrink;
	savebox.y0 = (firstRowIndexOfLayer) / shrink;
	savebox.x1 = savebox.x0 + (savebox.getLx()) / shrink - 1;
	savebox.y1 = savebox.y0 + (savebox.getLy()) / shrink - 1;
	return savebox;
}

//-------------------------------------------------------------------

TImageP TImageReaderTzl::load14()
{
	FILE *chan = m_lrp->m_chan;

	if (!chan)
		return TImageP();
	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0 = 0, sby0 = 0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	//TINT32 imgBuffSize = 0;
	UCHAR *imgBuff = 0;
	TINT32 iconLx = 0, iconLy = 0;
	assert(!m_lrp->m_frameOffsTable.empty());
	assert(!m_lrp->m_iconOffsTable.empty());
	if (m_lrp->m_frameOffsTable.empty())
		throw TException("Loading tlv: the frames table is empty.");
	if (m_lrp->m_iconOffsTable.empty())
		throw TException("Loading tlv: the frames icons table is empty.");

	TzlOffsetMap::iterator it = m_lrp->m_frameOffsTable.find(m_fid);
	TzlOffsetMap::iterator iconIt = m_lrp->m_iconOffsTable.find(m_fid);
	if (it == m_lrp->m_frameOffsTable.end() || iconIt == m_lrp->m_iconOffsTable.end())
		throw TException("Loading tlv: frame ID not found.");

	fseek(chan, it->second.m_offs, SEEK_SET);
	fread(&sbx0, sizeof(TINT32), 1, chan);
	fread(&sby0, sizeof(TINT32), 1, chan);
	fread(&sblx, sizeof(TINT32), 1, chan);
	fread(&sbly, sizeof(TINT32), 1, chan);
	fread(&actualBuffSize, sizeof(TINT32), 1, chan);
	fread(&xdpi, sizeof(double), 1, chan);
	fread(&ydpi, sizeof(double), 1, chan);

	if (sbx0 < 0 || sby0 < 0 || sblx < 0 || sbly < 0 || sblx > m_lx || sbly > m_ly)
		throw TException("Loading tlv: savebox dimension error.");

#if !TNZ_LITTLE_ENDIAN
	sbx0 = swapTINT32(sbx0);
	sby0 = swapTINT32(sby0);
	sblx = swapTINT32(sblx);
	sbly = swapTINT32(sbly);
	actualBuffSize = swapTINT32(actualBuffSize);
	reverse((char *)&xdpi, sizeof(double));
	reverse((char *)&ydpi, sizeof(double));
#endif

	// Carico l'icona dal file
	if (m_isIcon) {
		fseek(chan, iconIt->second.m_offs, SEEK_SET);
		fread(&iconLx, sizeof(TINT32), 1, chan);
		fread(&iconLy, sizeof(TINT32), 1, chan);
		assert(iconLx > 0 && iconLy > 0);
		if (iconLx < 0 || iconLy < 0 || iconLx > m_lx || iconLy > m_ly)
			throw TException("Loading tlv: bad icon size.");
		fread(&actualBuffSize, sizeof(TINT32), 1, chan);

		assert(actualBuffSize > 0 && actualBuffSize <= (int)(iconLx * iconLx * sizeof(TPixelCM32)));
		if (actualBuffSize <= 0 || actualBuffSize > (int)(iconLx * iconLx * sizeof(TPixelCM32)))
			throw TException("Loading tlv: icon buffer size error.");

		TRasterCM32P raux = TRasterCM32P(iconLx, iconLy);
		if (!raux)
			return TImageP();
		raux->lock();
		imgBuff = (UCHAR *)raux->getRawData(); //new UCHAR[imgBuffSize];
		fread(imgBuff, actualBuffSize, 1, chan);

#if !TNZ_LITTLE_ENDIAN
		Header *header = (Header *)imgBuff;
		header->m_lx = swapTINT32(header->m_lx);
		header->m_ly = swapTINT32(header->m_ly);
		header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

		TRasterCodecLZO codec("LZO", false);
		TRasterP ras;
		if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
			return TImageP();
		assert((TRasterCM32P)ras);
		raux->unlock();
		raux = TRasterCM32P();

#if !TNZ_LITTLE_ENDIAN

		for (int y = 0; y < ras->getLy(); ++y) {
			ras->lock();
			TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
			TINT32 *endPix = pix + ras->getLx();
			while (pix < endPix) {
				*pix = swapTINT32(*pix);
				pix++;
			}
			ras->unlock();
		}
#endif

		/*
			TINT32 iconsbx0 = tround((double)iconLx*sbx0/m_lrp->m_res.lx);
			TINT32 iconsby0 = tround((double)iconLy*sby0/m_lrp->m_res.ly); 
			TRect savebox(TPoint(iconsbx0,iconsby0), TDimension(ras->getLx(),ras->getLy()));
			 */
		if (m_lrp->m_res.lx == 0 || m_lrp->m_res.ly == 0)
			return TImageP();
		// Compute savebox
		if (m_lrp->m_res.lx < 0 || m_lrp->m_res.ly < 0)
			throw TException("Loading tlv: icon resolution error");

		TRect tmp_savebox;
		TRect savebox;
		TRop::computeBBox(ras, tmp_savebox);
		if (tmp_savebox.isEmpty()) {
			TINT32 iconsbx0 = tround((double)iconLx * sbx0 / m_lrp->m_res.lx);
			TINT32 iconsby0 = tround((double)iconLy * sby0 / m_lrp->m_res.ly);
			TINT32 iconsblx = tmax(tround((double)iconLx * sblx / m_lrp->m_res.lx), 1);
			TINT32 iconsbly = tmax(tround((double)iconLy * sbly / m_lrp->m_res.ly), 1);
			savebox = TRect(TPoint(iconsbx0, iconsby0), TDimension(iconsblx, iconsbly));
		} else {
			TINT32 iconsbx0 = tfloor((double)iconLx * sbx0 / m_lrp->m_res.lx);
			TINT32 iconsby0 = tfloor((double)iconLy * sby0 / m_lrp->m_res.ly);
			savebox = TRect(TPoint(iconsbx0, iconsby0), TDimension(tmp_savebox.getLx(), tmp_savebox.getLy()));
		}

		//int ly = tround((double)m_lrp->m_res.ly*iconLx/m_lrp->m_res.lx);
		//TRect rect(TPoint(0,tround((double)(iconLy-ly)/2)),TDimension(iconLx,ly));
		//TPixelCM32 bgColorRect(1,0,100);

		TDimension imgSize(iconLx, iconLy);
		if (!TRect(imgSize).contains(savebox)) //for this 'if', see comment in createIcon method. vinz
			savebox = savebox * TRect(imgSize);
		//if(!TRect(imgSize).contains(savebox)) throw TException("Loading tlv: bad icon savebox size.");

		if (imgSize != savebox.getSize()) {
			TRasterCM32P fullRas(imgSize);
			TPixelCM32 bgColor;

			fullRas->fillOutside(savebox, bgColor);

			//fullRas->extractT(rect)->fill(bgColorRect);
			//assert(savebox.getSize() == ras->getSize());
			if (savebox.getSize() != ras->getSize())
				throw TException("Loading tlv: bad icon savebox size.");
			fullRas->extractT(savebox)->copy(ras);
			ras = fullRas;
		}

		TToonzImageP ti(ras, savebox);
		ti->setDpi(xdpi, ydpi);
		ti->setPalette(m_lrp->m_level->getPalette());
		return ti;
	}

	assert(actualBuffSize > 0 && actualBuffSize <= (int)(m_lx * m_ly * sizeof(TPixelCM32)));
	if (actualBuffSize <= 0 || actualBuffSize > (int)(m_lx * m_ly * sizeof(TPixelCM32)))
		throw TException("Loading tlv: buffer size error");

	TRasterCM32P raux = TRasterCM32P(m_lx, m_ly);

	//imgBuffSize = m_lx*m_ly*sizeof(TPixelCM32);

	raux->lock();
	imgBuff = (UCHAR *)raux->getRawData(); //new UCHAR[imgBuffSize];
										   //int ret =

	fread(imgBuff, actualBuffSize, 1, chan);
	//assert(ret==1);

	Header *header = (Header *)imgBuff;

#if !TNZ_LITTLE_ENDIAN
	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	TRasterCodecLZO codec("LZO", false);
	TRasterP ras;
	if (!codec.decompress(imgBuff, actualBuffSize, ras, m_safeMode))
		return TImageP();
	assert((TRasterCM32P)ras);
	assert(ras->getLx() == header->m_lx);
	assert(ras->getLy() == header->m_ly);
	if (ras->getLx() != header->m_lx)
		throw TException("Loading tlv: lx dimension error.");
	if (ras->getLy() != header->m_ly)
		throw TException("Loading tlv: ly dimension error.");
	raux->unlock();
	raux = TRasterCM32P();

#if !TNZ_LITTLE_ENDIAN

	for (int y = 0; y < ras->getLy(); ++y) {
		ras->lock();
		TINT32 *pix = ((TINT32 *)ras->getRawData(0, y));
		TINT32 *endPix = pix + ras->getLx();
		while (pix < endPix) {
			*pix = swapTINT32(*pix);
			pix++;
		}
		ras->unlock();
	}

// codec.compress(ras, 1, &imgBuff, actualBuffSize);
#endif

	TRect savebox(TPoint(sbx0, sby0), TDimension(sblx, sbly));
	TDimension imgSize(m_lrp->m_res.lx, m_lrp->m_res.ly);
	assert(TRect(imgSize).contains(savebox));
	if (!TRect(imgSize).contains(savebox))
		throw TException("Loading tlv: bad savebox size.");
	if (imgSize != savebox.getSize()) {
		TRasterCM32P fullRas(imgSize);
		TPixelCM32 bgColor;
		if (!savebox.isEmpty()) {
			fullRas->fillOutside(savebox, bgColor);
			assert(savebox.getSize() == ras->getSize());
			if (savebox.getSize() != ras->getSize())
				throw TException("Loading tlv: bad icon savebox size.");
			fullRas->extractT(savebox)->copy(ras);
		} else
			fullRas->clear();
		ras = fullRas;
	}

	TToonzImageP ti(ras, savebox);
	// if(dpiflag)
	ti->setDpi(xdpi, ydpi);
	// m_lrp->m_level->setFrame(TFrameId(m_frameIndex+1), ti);
	ti->setPalette(m_lrp->m_level->getPalette());
	//delete [] imgBuff;
	//imgBuff = 0;
	return ti;

	//ToonzImageUtils::updateRas32(ti);
}

//-------------------------------------------------------------------

TImageP TImageReaderTzl::load()
{
	int version = m_lrp->m_version;
	TImageP image = TImageP();
	switch (version) {
	case 11:
		if (!m_lrp->m_frameOffsTable.empty())
			image = load11();
		break;
	case 12:
		if (!m_lrp->m_frameOffsTable.empty())
			image = load11();
		break;
	case 13:
		if (!m_lrp->m_frameOffsTable.empty() && !m_lrp->m_iconOffsTable.empty())
			image = load13();
		break;
	case 14:
		if (!m_lrp->m_frameOffsTable.empty() && !m_lrp->m_iconOffsTable.empty())
			image = load14();
		break;
	default:
		image = load10();
	}
	if (image == TImageP())
		return TImageP();

	if (!m_isIcon) {
		TToonzImageP ti = image;
		if (!ti)
			return TImageP();
		TRasterP ras = ti->getRaster();
		TRect savebox = ti->getSavebox();
		if (m_region != TRect() && m_region.getLx() > 0 && m_region.getLy() > 0) {
			m_region *= TRect(0, 0, ti->getSize().lx, ti->getSize().ly);
			if (m_region.isEmpty() || m_region == TRect() || m_region.getLx() <= 0 || m_region.getLy() <= 0)
				return TImageP();
		}
		savebox = applyShrinkAndRegion(ras, m_shrink, m_region, savebox);
		if (savebox == TRect()) {
			if (m_region != TRect()) {
				ras = ras->create(m_region.getLx(), m_region.getLy());
				ras->clear();
				savebox = m_region;
			} else {
				//se sia la savebox che la regione sono vuote non faccio nulla
			}
		}
		ti->setCMapped(ras);
		ti->setSavebox(savebox);
		return ti;
	}
	return image;
}
//-------------------------------------------------------------------

const TImageInfo *TImageReaderTzl::getImageInfo11() const
{
	assert(!m_lrp->m_frameOffsTable.empty());
	FILE *chan = m_lrp->m_chan;
	if (!chan)
		return 0;

	TzlOffsetMap::iterator it = m_lrp->m_frameOffsTable.find(m_fid);

	if (it == m_lrp->m_frameOffsTable.end())
		return 0;

	fseek(chan, it->second.m_offs, SEEK_SET);

	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0, sby0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	//  TINT32 imgBuffSize = 0;

	//int pos = ftell(chan);

	fread(&sbx0, sizeof(TINT32), 1, chan);
	fread(&sby0, sizeof(TINT32), 1, chan);
	fread(&sblx, sizeof(TINT32), 1, chan);
	fread(&sbly, sizeof(TINT32), 1, chan);
	fread(&actualBuffSize, sizeof(TINT32), 1, chan);

	fread(&xdpi, sizeof(double), 1, chan);
	fread(&ydpi, sizeof(double), 1, chan);

#if !TNZ_LITTLE_ENDIAN
	sbx0 = swapTINT32(sbx0);
	sby0 = swapTINT32(sby0);
	sblx = swapTINT32(sblx);
	sbly = swapTINT32(sbly);
	actualBuffSize = swapTINT32(actualBuffSize);
	reverse((char *)&xdpi, sizeof(double));
	reverse((char *)&ydpi, sizeof(double));
#endif

	static TImageInfo info;
	info.m_x0 = sbx0;
	info.m_y0 = sby0;
	info.m_x1 = sbx0 + sblx - 1;
	info.m_y1 = sby0 + sbly - 1;
	info.m_lx = m_lx;
	info.m_ly = m_ly;
	info.m_dpix = xdpi;
	info.m_dpiy = ydpi;

	//m_lrp->m_frameIndex = m_frameIndex;
	return &info;
}

//-------------------------------------------------------------------

const TImageInfo *TImageReaderTzl::getImageInfo10() const
{
	FILE *chan = m_lrp->m_chan;
	if (!chan)
		return 0;

	// SAVEBOX_X0 SAVEBOX_Y0 SAVEBOX_LX SAVEBOX_LY BUFFER_SIZE
	TINT32 sbx0, sby0, sblx, sbly;
	TINT32 actualBuffSize;
	double xdpi = 1, ydpi = 1;
	TINT32 imgBuffSize = 0;
	UCHAR *imgBuff = 0;
	int frameIndex = m_fid.getNumber();

	//int pos = ftell(chan);
	assert(m_lrp->m_frameOffsTable[TFrameId(1)].m_offs > 0);
	int k;
	if (m_lrp->m_frameOffsTable[TFrameId(frameIndex)].m_offs == 0) {
		int i;
		for (i = 2; m_lrp->m_frameOffsTable[TFrameId(i)].m_offs != 0 && i <= frameIndex; i++)
			;
		if (i == frameIndex)
			i = 2;

		fseek(chan, m_lrp->m_frameOffsTable[TFrameId(i - 1)].m_offs, SEEK_SET);
		k = i - 1;
	} else {
		fseek(chan, m_lrp->m_frameOffsTable[TFrameId(frameIndex)].m_offs, SEEK_SET);
		k = frameIndex;
	}

	for (; k <= frameIndex; k++) {
		m_lrp->m_frameOffsTable[TFrameId(k)] = TzlChunk(ftell(chan), 0);
		fread(&sbx0, sizeof(TINT32), 1, chan);
		fread(&sby0, sizeof(TINT32), 1, chan);
		fread(&sblx, sizeof(TINT32), 1, chan);
		fread(&sbly, sizeof(TINT32), 1, chan);
		fread(&actualBuffSize, sizeof(TINT32), 1, chan);

		fread(&xdpi, sizeof(double), 1, chan);
		fread(&ydpi, sizeof(double), 1, chan);

#if !TNZ_LITTLE_ENDIAN
		sbx0 = swapTINT32(sbx0);
		sby0 = swapTINT32(sby0);
		sblx = swapTINT32(sblx);
		sbly = swapTINT32(sbly);
		actualBuffSize = swapTINT32(actualBuffSize);
		reverse((char *)&xdpi, sizeof(double));
		reverse((char *)&ydpi, sizeof(double));
#endif

		imgBuffSize = m_lx * m_ly * sizeof(TPixelCM32);
		assert(actualBuffSize <= imgBuffSize);
		delete[] imgBuff;
		imgBuff = new UCHAR[imgBuffSize];
		fread(imgBuff, actualBuffSize, 1, chan);
	}

	Header *header = (Header *)imgBuff;

#if !TNZ_LITTLE_ENDIAN
	header->m_lx = swapTINT32(header->m_lx);
	header->m_ly = swapTINT32(header->m_ly);
	header->m_rasType = (Header::RasType)swapTINT32(header->m_rasType);
#endif

	delete[] imgBuff;

	static TImageInfo info;
	info.m_x0 = sbx0;
	info.m_y0 = sby0;
	info.m_x1 = sbx0 + sblx - 1;
	info.m_y1 = sby0 + sbly - 1;
	info.m_lx = m_lx;
	info.m_ly = m_ly;
	info.m_dpix = xdpi;
	info.m_dpiy = ydpi;

	//m_lrp->m_frameIndex = m_frameIndex;
	return &info;
}

//-------------------------------------------------------------------

const TImageInfo *TImageReaderTzl::getImageInfo() const
{
	if (m_lrp->m_version > 10 && !m_lrp->m_frameOffsTable.empty())
		return getImageInfo11();
	else
		return getImageInfo10();
}

//-------------------------------------------------------------------

TDimension TImageReaderTzl::getSize() const
{
	return TDimension(m_lx, m_ly);
}

//-------------------------------------------------------------------

TRect TImageReaderTzl::getBBox() const
{
	return TRect(getSize());
}