// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
// Code contributed by skreamz

#include "CPakReader.h"

#ifdef __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_

#include "os.h"
#include "coreutil.h"

namespace irr
{
namespace io
{

namespace
{

inline bool isHeaderValid(const SPAKFileHeader& header)
{
	const c8* tag = header.tag;
	return tag[0] == 'P' &&
		   tag[1] == 'A' &&
		   tag[2] == 'C' &&
		   tag[3] == 'K';
}

} // end namespace

//! Constructor
CArchiveLoaderPAK::CArchiveLoaderPAK( io::IFileSystem* fs)
: FileSystem(fs)
{
#ifdef _DEBUG
	setDebugName("CArchiveLoaderPAK");
#endif
}


//! returns true if the file maybe is able to be loaded by this class
bool CArchiveLoaderPAK::isALoadableFileFormat(const io::path& filename) const
{
	return core::hasFileExtension(filename, "pak");
}

//! Check to see if the loader can create archives of this type.
bool CArchiveLoaderPAK::isALoadableFileFormat(E_FILE_ARCHIVE_TYPE fileType) const
{
	return fileType == EFAT_PAK;
}

//! Creates an archive from the filename
/** \param file File handle to check.
\return Pointer to newly created archive, or 0 upon error. */
IFileArchive* CArchiveLoaderPAK::createArchive(const io::path& filename, bool ignoreCase, bool ignorePaths) const
{
	IFileArchive *archive = 0;
	io::IReadFile* file = FileSystem->createAndOpenFile(filename);

	if (file)
	{
		archive = createArchive(file, ignoreCase, ignorePaths);
		file->drop ();
	}

	return archive;
}

//! creates/loads an archive from the file.
//! \return Pointer to the created archive. Returns 0 if loading failed.
IFileArchive* CArchiveLoaderPAK::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
{
	IFileArchive *archive = 0;
	if ( file )
	{
		file->seek ( 0 );
		archive = new CPakReader(file, ignoreCase, ignorePaths);
	}
	return archive;
}


//! Check if the file might be loaded by this class
/** Check might look into the file.
\param file File handle to check.
\return True if file seems to be loadable. */
bool CArchiveLoaderPAK::isALoadableFileFormat(io::IReadFile* file) const
{
	SPAKFileHeader header;

	file->read(&header, sizeof(header));

	return isHeaderValid(header);
}


/*!
	PAK Reader
*/
CPakReader::CPakReader(IReadFile* file, bool ignoreCase, bool ignorePaths)
: CFileList(file ? file->getFileName() : "", ignoreCase, ignorePaths), File(file)
{
#ifdef _DEBUG
	setDebugName("CPakReader");
#endif

	if (File)
	{
		File->grab();
		scanLocalHeader();
		sort();
	}
}


CPakReader::~CPakReader()
{
	if (File)
		File->drop();
}


const IFileList* CPakReader::getFileList() const
{
	return this;
}

bool CPakReader::scanLocalHeader()
{
	SPAKFileHeader header;
		
	// Read and validate the header
	File->read(&header, sizeof(header));
	if (!isHeaderValid(header))
		return false;

	// Seek to the table of contents	
#ifdef __BIG_ENDIAN__
	header.offset = os::Byteswap::byteswap(header.offset);
	header.length = os::Byteswap::byteswap(header.length);
#endif
	File->seek(header.offset);

	const int numberOfFiles = header.length / sizeof(SPAKFileEntry);

	Offsets.reallocate(numberOfFiles);
	// Loop through each entry in the table of contents
	for(int i = 0; i < numberOfFiles; i++)
	{
		// read an entry
		SPAKFileEntry entry;
		File->read(&entry, sizeof(entry));

#ifdef _DEBUG
		os::Printer::log(entry.name);
#endif

#ifdef __BIG_ENDIAN__
		entry.offset = os::Byteswap::byteswap(entry.offset);
		entry.length = os::Byteswap::byteswap(entry.length);
#endif

		addItem(io::path(entry.name), entry.length, false, Offsets.size());
		Offsets.push_back(entry.offset);
	}
	return true;
}


//! opens a file by file name
IReadFile* CPakReader::createAndOpenFile(const io::path& filename)
{
	s32 index = findFile(filename, false);

	if (index != -1)
		return createAndOpenFile(index);

	return 0;
}


//! opens a file by index
IReadFile* CPakReader::createAndOpenFile(u32 index)
{
	if (index < Files.size())
	{
		return createLimitReadFile(Files[index].FullName, File, Offsets[Files[index].ID], Files[index].Size);
	}
	else
		return 0;
}

} // end namespace io
} // end namespace irr

#endif // __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_

