// Copyright (C) 2002-2009 Thomas Alten
// 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 "IrrCompileConfig.h"

#ifdef __IRR_COMPILE_WITH_WAD_ARCHIVE_LOADER_

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

namespace irr
{
namespace io
{

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


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


//! Creates an archive from the filename
/** \param file File handle to check.
\return Pointer to newly created archive, or 0 upon error. */
IFileArchive* CArchiveLoaderWAD::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* CArchiveLoaderWAD::createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const
{
	IFileArchive *archive = 0;
	if ( file )
	{
		file->seek ( 0 );
		archive = new CWADReader(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 CArchiveLoaderWAD::isALoadableFileFormat(io::IReadFile* file) const
{
	SWADFileHeader header;
	memset(&header, 0, sizeof(header));

	file->read( &header.tag, 4 );

	return !strncmp ( header.tag, "WAD2", 4 ) || !strncmp ( header.tag, "WAD3", 4 );
}

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

void createDir ( const c8 *full );


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

	if (File)
	{
		File->grab();

		Base = File->getFileName();
		Base.replace ( '\\', '/' );

		// scan local headers
		scanLocalHeader();

		sort();
	}

#if 0
	for ( u32 i = 0; i < FileList.size(); ++i )
	{
		SWADFileEntry &e = FileList[i];
		char buf[128];
		snprintf ( buf, 128, "c:\\h2\\%s", e.wadFileName.c_str() );

		createDir ( buf );
		FILE * f = fopen ( buf, "wb" );
		if ( 0 == f )
			continue;

		u8 * mem = new u8 [ e.header.disksize ];
		File->seek ( e.header.filepos );
		File->read ( mem, e.header.disksize );
		fwrite ( mem, e.header.disksize, 1, f );
		delete [] mem;
		fclose ( f );
	}
#endif

}


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


//! return the id of the file Archive
const io::path& CWADReader::getArchiveName () const
{ 
	return Base;
}

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


//! scans for a local header, returns false if there is no more local file header.
bool CWADReader::scanLocalHeader()
{
	SWADFileEntryOriginal entry;
	SWADFileEntry save;

	memset(&Header, 0, sizeof(SWADFileHeader));
	File->read(&Header, sizeof(SWADFileHeader));

	if ( 0 == strncmp ( Header.tag, "WAD2", 4 ) )
		WadType = WAD_FORMAT_QUAKE2;
	else
	if ( 0 == strncmp ( Header.tag, "WAD3", 4 ) )
		WadType = WAD_FORMAT_HALFLIFE;
	else
		WadType = WAD_FORMAT_UNKNOWN;

	if ( WadType == WAD_FORMAT_UNKNOWN )
		return false;

#ifdef __BIG_ENDIAN__
	Header.numlumps = os::Byteswap::byteswap(Header.numlumps);
	Header.infotableofs = os::Byteswap::byteswap(Header.infotableofs);
#endif

	File->seek ( Header.infotableofs );

	c8 buf[16];
	for ( u32 i = 0; i < Header.numlumps; ++i )
	{
		// read entry
		File->read(&entry, sizeof ( SWADFileEntryOriginal ));
		entry.name[ sizeof ( entry.name ) - 1 ] = 0;

		save.header = entry;
		save.wadFileName = "/";
		save.wadFileName += entry.name;

		if ( WadType == WAD_FORMAT_HALFLIFE )
		{
			// don't know about the types! i'm guessing
			switch ( entry.type )
			{
				case WAD_TYP_MIPTEX_HALFLIFE:
					save.wadFileName += ".wal2";
					break;
				default:
					snprintf ( buf, 16, ".%02d", entry.type );
					save.wadFileName += buf;
					break;
			}
		}
		else
		if ( WadType == WAD_FORMAT_QUAKE2 )
		{
			switch ( entry.type )
			{
				case WAD_TYP_MIPTEX: save.wadFileName += ".miptex"; break;
				case WAD_TYP_SOUND: save.wadFileName += ".sound"; break;
				case WAD_TYP_PALETTE: save.wadFileName += ".palette"; break;
				case WAD_TYP_QTEX: save.wadFileName += ".qtex"; break;
				case WAD_TYP_QPIC: save.wadFileName += ".qpic"; break;
				case WAD_TYP_FONT: save.wadFileName += ".font"; break;
				default:
					snprintf ( buf, 16, ".%02d", entry.type );
					save.wadFileName += buf;
					break;
			}
		}

		// add file to list
		addItem(save.wadFileName,save.header.filepos, save.header.disksize, false );
		//FileInfo.push_back(save);
	}
	return true;
}


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

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

	return 0;
}


//! opens a file by index
IReadFile* CWADReader::createAndOpenFile(u32 index)
{
	if (index >= Files.size() )
		return 0;

	const SFileListEntry &entry = Files[index];
	return createLimitReadFile( entry.FullName, File, entry.Offset, entry.Size );
}



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


#endif // __IRR_COMPILE_WITH_WAD_ARCHIVE_LOADER_

