#ifndef _INCLUDED_IFF_HPP_
#define _INCLUDED_IFF_HPP_

#if defined(_WIN32) || defined(WIN32) || defined(WINDOWS) || defined(_WINDOWS)
	#define _IFF_WIN_TARGET
	#include <windows.h>
#else // ! WIN32 && ! _WIN32 && ! WINDOWS && ! _WINDOWS
	#include <stdio.h>
	#include <conio.h>
#endif // ! WIN32 && ! _WIN32 && ! WINDOWS && ! _WINDOWS

#include "media.hpp"

namespace IFF
{
	/*************************/
	/* Class Hierarchy:      */
	/*                       */
	/* + Unknown             */
	/*   + SerializableObj   */
	/*     + GenericFile     */
	/*       + File          */
	/*     + Chunk           */
	/*       + Composite     */
	/*         + Form        */
	/*         + Cat         */
	/*         + List        */
	/*         + Prop        */
	/*       + MiscChunk     */
	/*       + <user chunks> */
	/*   + Arvhive           */
	/*     + ArchvIn         */
	/*     + ArchvOut(*)     */
	/*   + DataBlock         */
	/*   + DataNode          */
	/*   + SerialData        */
	/*     + ArchvOut(*)     */
	/*   + ChildNode         */
	/*   + DeviceHandle      */
	/*************************/
	
	#ifdef _IFF_WIN_TARGET
		inline void DisplayMessage(TCHAR const * pszTitle,TCHAR const * pszText)
		{
			::MessageBox(NULL,pszText,pszTitle,MB_OK|MB_ICONEXCLAMATION|MB_SETFOREGROUND);
		}
	#else
		inline void DisplayMessage(char const * pszTitle,char const * pszText)
		{
			::printf("%s\n%s\n",pszTitle,pszText);
			while (::kbhit())
				::getch();
			while (!::kbhit() || '\r' != ::getch())
				;
		}
	#endif
	
	// forward refs
	//class Unknown;
	//class SerializableObj;
	//class GenericFile;
	//class File;
	//class Chunk;
	  class Composite;
	//class Form;
	//class Cat;
	//class List;
	//class Prop;
	//class MiscChunk;
	//class Archive;
	//class ArchvIn;
	//class ArchvOut;
	//class DataBlock;
	//class DataNode;
	//class SerialData;
	  class ChildNode;
	//class DeviceHandle;
	
	/*****************************/
	/* Original File: iffObj.hpp */
	/*****************************/
	
	class Unknown
	{
		public:
			unsigned AddRef() { return ++m_nRefCnt; }
			unsigned Release() { if (0==(--m_nRefCnt)) { delete this; return 0;} else return m_nRefCnt; }
		protected:
			virtual ~Unknown() {
				#ifndef NDEBUG
					DbForget(this);
				#endif
			}
			Unknown() : m_nRefCnt(1) {
				#ifndef NDEBUG
					DbRemember(this);
				#endif
			}
			Unknown(Unknown const &) : m_nRefCnt(1) {
				#ifndef NDEBUG
					DbRemember(this);
				#endif
			}
		private:
			unsigned m_nRefCnt;
			
		#ifndef NDEBUG
			friend void DbRemember(Unknown * pObj);
			friend void DbForget(Unknown * pObj);
			friend class AllocList;
		#endif
	};
	
	/*******************************/
	/* Original File: iffTypes.hpp */
	/*******************************/
	
	// deal with any silly bastard who's put #define BYTE ... in a header, etc.
	#ifdef BYTE
		#undef BYTE
		#pragma message("BYTE was defined - undefining")
	#endif
	typedef signed char BYTE;
	typedef unsigned char UBYTE;

	typedef signed short INT16;
	typedef unsigned short UINT16;

	typedef signed INT32;
	typedef unsigned UINT32;

	typedef signed __int64 INT64;
	typedef unsigned __int64 UINT64;

	struct RGBTriple
	{
		UBYTE r;
		UBYTE g;
		UBYTE b;
	};

	union ID
	{
		UINT32 m_nID;
		char m_sID[4];
		
		inline ID(){}
		inline ID(char const * pszID) { m_nID = *reinterpret_cast<UINT32 const *>(pszID); }
		inline ID(UINT32 nID) : m_nID(nID) {}
		inline operator UINT32 () const { return m_nID; }
		inline operator char const * () const { return &m_sID[0]; }
		inline bool operator == (ID const & rId) const { return !m_nID || !rId.m_nID || m_nID == rId.m_nID; }
		inline bool operator != (ID const & rId) const { return ! operator == (rId); }
		inline bool operator ! () const { return !m_nID; }
	};
	
	ID const ID_ANY(0U);
	
	#ifndef IFF_READ_ONLY
	
		/*******************************/
		/* Original File: iffSData.hpp */
		/*******************************/
	
		class DataBlock : public Unknown
		{
			public:
				virtual ~DataBlock();
				DataBlock() : m_pBlock(new UBYTE [128]), m_nMaxSize(128), m_nCurSize(0) {}
				
				unsigned GetDataSize() const;
				UBYTE const * GetDataPtr() const;
				
				bool WriteToFile(::MediaMedium * pMedium) const;
				
				void Append(UBYTE byte);
				void Append(void const * pData, unsigned nSize);
				
			private:
				void Expand(unsigned nMinSize);
			
				UBYTE * m_pBlock;
				unsigned m_nMaxSize;
				unsigned m_nCurSize;
		};
		
		inline unsigned DataBlock::GetDataSize() const
		{
			return m_nCurSize;
		}
		
		inline UBYTE const * DataBlock::GetDataPtr() const
		{
			return m_pBlock;
		}
		
		inline void DataBlock::Append(UBYTE byte)
		{
			if (m_nCurSize >= m_nMaxSize)
				Expand(m_nCurSize+1);
			
			m_pBlock[m_nCurSize++] = byte;
		}
		
		inline void DataBlock::Append(void const * pData, unsigned nSize)
		{
			if (m_nCurSize+nSize > m_nMaxSize)
				Expand(m_nCurSize+nSize+32);
			
			memcpy(&m_pBlock[m_nCurSize],pData,nSize);
			
			m_nCurSize += nSize;
		}
		
		class DataNode : public Unknown
		{
			public:
				DataNode(DataNode * pNext, DataBlock * pData, DataNode * pPrev)
					: m_pNext(pNext)
					, m_pData(pData)
					, m_pPrev(pPrev)
				{
					if (pNext) pNext->AddRef();
					if (pData) pData->AddRef();
					if (pPrev) pPrev->AddRef();
				}
				virtual ~DataNode();
				
				unsigned GetDataSize() const;
				
				bool WriteToFile(::MediaMedium * pMedium) const;
				
			private:
				DataNode * m_pNext;
				DataBlock * m_pData;
				DataNode * m_pPrev;
		};
		
		class SerialData : public Unknown
		{
			public:
				virtual ~SerialData();
				SerialData() : m_pPrev(NULL), m_pData(new DataBlock) {};
				
				void Clear();
				
				unsigned GetDataSize() const;

				bool WriteToFile(::MediaMedium * pMedium) const;
				
				void Append(UBYTE byte);
				void Append(void const * pData, unsigned nSize);
				
				void Append(SerialData * pData);
				
			private:
				DataBlock * m_pData;
				DataNode * m_pPrev;
		};
		
		inline void SerialData::Append(UBYTE byte)
		{
			m_pData->Append(byte);
		}
		
		inline void SerialData::Append(void const * pData, unsigned nSize)
		{
			m_pData->Append(pData,nSize);
		}
		
		inline void SerialData::Append(SerialData * pIns)
		{
			DataNode * pNewNode = new DataNode(pIns->m_pPrev,m_pData,m_pPrev);
			
			m_pData->Release();
			m_pData = pIns->m_pData;
			pIns->m_pData->AddRef();
			
			if (m_pPrev)
				m_pPrev->Release();
			m_pPrev = pNewNode;
		}
	
		/*******************************/
		/* Original File: iffArchv.hpp */
		/*******************************/
		
		class Archive : public Unknown
		{
			public:
				// constructor - construct with true iff the archive is for loading data
				Archive(bool bIsLoading) : m_bIsLoading(bIsLoading), m_bError(false) {}
				
				// to determine easily whenever necessary if the archive is loading data
				bool const m_bIsLoading;
				bool m_bError;
				
				// returns either the size in bytes of the data so far written to a storing archive
				// or the number of bytes remaining to be read from a loading archive, could be negative
				// if too many bytes were read
				virtual signed GetSize() const = 0;
				
				// if the archive is loading, nSize bytes are sectioned off to be read from a sub-archive
				// or if the archive is storing, nSize is ignored and data written to the sub-archive
				// will be stored when the sub-archive is closed
				virtual Archive * OpenSubArchive(unsigned nSize = 0) = 0;
				
				// commits data written temporarily to a sub-archive (if storing)
				// and advances the archive to the next byte after the sub-archive's data
				virtual void CloseSubArchive(Archive * pSub) = 0;
				
				virtual void Open(::MediaMedium * pMedium) = 0;
				virtual void Close() = 0;
				
				virtual void Transfer(RGBTriple &) = 0;
				virtual void Transfer(BYTE &) = 0;
				virtual void Transfer(UBYTE &) = 0;
				virtual void Transfer(INT16 &) = 0;
				virtual void Transfer(UINT16 &) = 0;
				virtual void Transfer(INT32 &) = 0;
				virtual void Transfer(UINT32 &) = 0;
				virtual void Transfer(INT64 &) = 0;
				virtual void Transfer(UINT64 &) = 0;
				virtual void Transfer(ID &) = 0;
				
				virtual void TransferBlock(void * pData,unsigned nSize) = 0;
		};
		
		/*******************************/
		/* Original File: iffArchO.hpp */
		/*******************************/
		
		class ArchvOut : public Archive, public SerialData
		{
			public:
				ArchvOut() : Archive(false), m_pMedium(NULL) {}
				~ArchvOut();
				
				signed GetSize() const;
				
				Archive * OpenSubArchive(unsigned nSize = 0);
				
				void CloseSubArchive(Archive * pSub);
				
				void Open(::MediaMedium * pMedium);
				void Close();
				
				void Transfer(RGBTriple &);
				void Transfer(BYTE &);
				void Transfer(UBYTE &);
				void Transfer(INT16 &);
				void Transfer(UINT16 &);
				void Transfer(INT32 &);
				void Transfer(UINT32 &);
				void Transfer(INT64 &);
				void Transfer(UINT64 &);
				void Transfer(ID &);
				
				void TransferBlock(void * pData,unsigned nSize);
				
			private:
				::MediaMedium * m_pMedium;
		};
		
		inline signed ArchvOut::GetSize() const
		{
			return GetDataSize();
		}

		inline void ArchvOut::Transfer(RGBTriple & n)
		{
			Append(n.r);
			Append(n.g);
			Append(n.b);
		}
		
		inline void ArchvOut::Transfer(BYTE & n)
		{
			Append(n);
		}
		
		inline void ArchvOut::Transfer(UBYTE & n)
		{
			Append(n);
		}
		
		inline void ArchvOut::Transfer(INT16 & n)
		{
			Append(static_cast<BYTE>(n >> 8));
			Append(static_cast<BYTE>(n));
		}
		
		inline void ArchvOut::Transfer(UINT16 & n)
		{
			Append(static_cast<UBYTE>(n >> 8));
			Append(static_cast<UBYTE>(n));
		}
		
		inline void ArchvOut::Transfer(INT32 & n)
		{
			Append(static_cast<BYTE>(n >> 24));
			Append(static_cast<BYTE>(n >> 16));
			Append(static_cast<BYTE>(n >> 8));
			Append(static_cast<BYTE>(n));
		}

		inline void ArchvOut::Transfer(UINT32 & n)
		{
			Append(static_cast<UBYTE>(n >> 24));
			Append(static_cast<UBYTE>(n >> 16));
			Append(static_cast<UBYTE>(n >> 8));
			Append(static_cast<UBYTE>(n));
		}

		inline void ArchvOut::Transfer(INT64 & n)
		{
			Append(static_cast<BYTE>(n >> 56));
			Append(static_cast<BYTE>(n >> 48));
			Append(static_cast<BYTE>(n >> 40));
			Append(static_cast<BYTE>(n >> 32));
			Append(static_cast<BYTE>(n >> 24));
			Append(static_cast<BYTE>(n >> 16));
			Append(static_cast<BYTE>(n >> 8));
			Append(static_cast<BYTE>(n));
		}

		inline void ArchvOut::Transfer(UINT64 & n)
		{
			Append(static_cast<UBYTE>(n >> 56));
			Append(static_cast<UBYTE>(n >> 48));
			Append(static_cast<UBYTE>(n >> 40));
			Append(static_cast<UBYTE>(n >> 32));
			Append(static_cast<UBYTE>(n >> 24));
			Append(static_cast<UBYTE>(n >> 16));
			Append(static_cast<UBYTE>(n >> 8));
			Append(static_cast<UBYTE>(n));
		}
		
		inline void ArchvOut::Transfer(ID & n)
		{
			Append(&n,4);
		}

		inline void ArchvOut::TransferBlock(void * pData,unsigned nSize)
		{
			Append(pData,nSize);
		}
		
	#endif // ! IFF_READ_ONLY

	/*******************************/
	/* Original File: iffArchI.hpp */
	/*******************************/
	
	#ifdef IFF_READ_ONLY
		#define _IFF_ARCHI_BASE Unknown
		#define _IFF_ARCHI_GENR ArchvIn
		#define _IFF_ARCHI_FLAG m_bIsLoading
		#define _IFF_ARCHI_INIT ,m_bError(false)
	#else // ! IFF_READ_ONLY
		#define _IFF_ARCHI_BASE Archive
		#define _IFF_ARCHI_GENR Archive
		#define _IFF_ARCHI_FLAG Archive
		#define _IFF_ARCHI_INIT
	#endif // ! IFF_READ_ONLY
	
	class ArchvIn : public _IFF_ARCHI_BASE
	{
		public:
			ArchvIn() : _IFF_ARCHI_FLAG(true), m_pMedium(NULL) _IFF_ARCHI_INIT {}
			~ArchvIn();
			
			#ifdef IFF_READ_ONLY
				bool const m_bIsLoading;
				bool m_bError;
			#endif // IFF_READ_ONLY
			
			signed GetSize() const;
			
			_IFF_ARCHI_GENR * OpenSubArchive(unsigned nSize = 0);
			
			void CloseSubArchive(_IFF_ARCHI_GENR * pSub);
			
			void Open(::MediaMedium * pMedium);
			void Close();
			
			void Transfer(RGBTriple &);
			void Transfer(BYTE &);
			void Transfer(UBYTE &);
			void Transfer(INT16 &);
			void Transfer(UINT16 &);
			void Transfer(INT32 &);
			void Transfer(UINT32 &);
			void Transfer(INT64 &);
			void Transfer(UINT64 &);
			void Transfer(ID &);
			
			void TransferBlock(void * pData,unsigned nSize);
			
		private:
			ArchvIn(ArchvIn * pParent, unsigned nSize);

			::MediaMedium * m_pMedium;
			signed m_nBytesRemaining;
			unsigned m_nEndPos;
	};
	
	#ifdef IFF_READ_ONLY
		typedef ArchvIn Archive;
	#endif // IFF_READ_ONLY
	
	inline signed ArchvIn::GetSize() const
	{
		return m_nBytesRemaining;
	}

	inline void ArchvIn::Transfer(RGBTriple & n)
	{
		m_nBytesRemaining -= 3;
		if (m_nBytesRemaining >= 0)
		{
			::MediaRead(m_pMedium, &n.r);
			::MediaRead(m_pMedium, &n.g);
			::MediaRead(m_pMedium, &n.b);
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}
	
	inline void ArchvIn::Transfer(UBYTE & n)
	{
		m_nBytesRemaining -= 1;
		if (m_nBytesRemaining >= 0)
		{
			::MediaRead(m_pMedium, &n);
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(BYTE & n)
	{
		m_nBytesRemaining -= 1;
		if (m_nBytesRemaining >= 0)
		{
			::MediaRead(m_pMedium, &n);
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(UINT16 & n)
	{
		m_nBytesRemaining -= 2;
		if (m_nBytesRemaining >= 0)
		{
			UBYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(INT16 & n)
	{
		m_nBytesRemaining -= 2;
		if (m_nBytesRemaining >= 0)
		{
			BYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(UINT32 & n)
	{
		m_nBytesRemaining -= 4;
		if (m_nBytesRemaining >= 0)
		{
			UBYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(INT32 & n)
	{
		m_nBytesRemaining -= 4;
		if (m_nBytesRemaining >= 0)
		{
			BYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(UINT64 & n)
	{
		m_nBytesRemaining -= 8;
		if (m_nBytesRemaining >= 0)
		{
			UBYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(INT64 & n)
	{
		m_nBytesRemaining -= 8;
		if (m_nBytesRemaining >= 0)
		{
			BYTE byte;
			::MediaRead(m_pMedium, &byte);
			n = byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			::MediaRead(m_pMedium, &byte);
			n <<= 8;
			n |= byte;
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::Transfer(ID & n)
	{
		m_nBytesRemaining -= 4;
		if (m_nBytesRemaining >= 0)
		{
			// cast pointer to pointer to 4 byte data type to force 4 byte 'fast' read
			::MediaRead(m_pMedium, reinterpret_cast<UINT32 *>(&n.m_sID[0]));
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}

	inline void ArchvIn::TransferBlock(void * pData,unsigned nSize)
	{
		m_nBytesRemaining -= nSize;
		if (m_nBytesRemaining >= 0)
		{
			m_pMedium->ReadBlock(pData,nSize);
			if (m_pMedium->m_fError) m_bError = true;
		}
		else m_bError = true;
	}
	
	/*******************************/
	/* Original File: iffSrlOb.hpp */
	/*******************************/
	
	class SerializableObj : public Unknown
	{
		public:
			virtual void Serialize(Archive * pArchv) = 0;
	};
	
	/*******************************/
	/* Original File: iffChunk.hpp */
	/*******************************/
	
	class Chunk : public SerializableObj
	{
		public:
			ID m_idCk;
			
			Chunk * GetProperty(ID idProp) const;
			
			virtual bool IsUnknown() const { return false; }
			
			void Write(Archive * pArchv);
			static Chunk * Load(ID idParent, Archive * pArchv, ID idChunk = ID_ANY, bool bKnown = true);
			
			static Chunk * DynCreate(ID idParent, ID idChunk);
			static void Register(ID idParent, ID idChunk, Chunk * (* pfnCreate) () );
			
			Composite const * GetParent() const { return m_pParent; }
			
		protected:
			Chunk() : m_pParent(NULL) {}
			
		private:
			Composite const * m_pParent; // mot reference counted
			ChildNode const * m_pNode; // not reference counted either
			
			friend class Composite;
	};
	
	#define IFF_IMPLEMENT_DYNCREATE(idParent,idChunk,tokenClassName) _IFF_IMPLEMENT_DYNCREATE_LINE_EX(idParent,idChunk,tokenClassName,__LINE__)
	#define _IFF_IMPLEMENT_DYNCREATE_LINE_EX(idParent,idChunk,tokenClassName,nLine) _IFF_IMPLEMENT_DYNCREATE_LINE(idParent,idChunk,tokenClassName,nLine)
	
	#define _IFF_IMPLEMENT_DYNCREATE_LINE(idParent,idChunk,tokenClassName,nLine) \
	static IFF::Chunk * CreateClassObject ## tokenClassName ##_## nLine () { \
		IFF::Chunk * pChunk = new IFF::tokenClassName; \
		pChunk->m_idCk = idChunk; \
		return pChunk; \
	} \
	 class RegisterChunkClass ## tokenClassName ##_## nLine { \
		public: RegisterChunkClass ## tokenClassName ##_## nLine () { \
			IFF::Chunk::Register(idParent , idChunk , CreateClassObject ## tokenClassName ##_## nLine); \
		} \
	} rcc ## tokenClassName ##_## nLine;
	
	/*******************************/
	/* Original File: iffBlock.hpp */
	/*******************************/
	
	class ChildNode : public Unknown
	{
		public:
			Chunk * GetChunk() const { return m_pChunk; }
		private:
			ChildNode * m_pNext;
			ChildNode * m_pPrev;
			Chunk * m_pChunk;
			
			friend class Composite;
	};
	
	class Composite : public Chunk
	{
		public:
			ID m_idData;
			
			Composite() : m_pFirst(NULL), m_pLast(NULL) {}
			virtual ~Composite();
			
			ChildNode * GetFirstChild() const { return m_pFirst; }
			ChildNode * GetFirstChild(ID idMatch) const;
			ChildNode * GetLastChild() const { return m_pLast; }
			ChildNode * GetLastChild(ID idMatch) const;
			static ChildNode * GetNextChild(ChildNode const * pNode) { return pNode->m_pNext; }
			static ChildNode * GetNextChild(ChildNode const * pNode, ID idMatch);
			static ChildNode * GetPrevChild(ChildNode const * pNode) { return pNode->m_pPrev; }
			static ChildNode * GetPrevChild(ChildNode const * pNode, ID idMatch);
			
			Chunk * GetProperty(ChildNode const * pNode, ID idProp) const;
			
			void DeleteChild(ChildNode * pNode);
			void DeleteAllChildren();
			
			ChildNode * InsertChildFirst(Chunk * pChunk);
			ChildNode * InsertChildLast(Chunk * pChunk);
			ChildNode * InsertChildAfter(ChildNode * pNode, Chunk * pChunk);
			ChildNode * InsertChildBefore(ChildNode * pNode, Chunk * pChunk);
			
			// pfnCallback should return true to continue the enumeration, false to finish
			virtual bool EnumChildren(ID idData, ID idChunk, bool (* pfnCallback) (Chunk *, void *), void * pData) const;
			
		protected:
			virtual void Serialize(Archive * pArchv);
			
			virtual Chunk * LoadChunk(Archive * pArchv) const;
			virtual bool IsValidChildID(ID id) const;
			
		private:
			ChildNode * m_pFirst;
			ChildNode * m_pLast;
	};
	
	class Form : public Composite
	{
		public:
			Form() { m_idCk = "FORM"; }
		protected:
			virtual bool IsValidChildID(ID id) const;
	};
	
	class Cat : public Composite
	{
		public:
			Cat() { m_idCk = "CAT "; }
			virtual bool EnumChildren(ID idData, ID idChunk, bool (* pfnCallback) (Chunk *, void *), void * pData) const;
		protected:
			virtual bool IsValidChildID(ID id) const;
	};
	
	class List : public Composite
	{
		public:
			List() { m_idCk = "LIST"; }
			virtual bool EnumChildren(ID idData, ID idChunk, bool (* pfnCallback) (Chunk *, void *), void * pData) const;
		protected:
			virtual bool IsValidChildID(ID id) const;
	};
	
	class Prop : public Composite
	{
		public:
			Prop() { m_idCk = "PROP"; }
		protected:
			virtual bool IsValidChildID(ID id) const;
	};
	
	/*******************************/
	/* Original File: iffMscCk.hpp */
	/*******************************/
	
	class MiscChunk : public Chunk
	{
		public:
			MiscChunk(ID id)
				#ifndef IFF_READ_ONLY
					: m_pData(NULL)
				#endif // ! IFF_READ_ONLY
				{
					m_idCk = id;
				}
				
			virtual bool IsUnknown() const { return true; }
				
		protected:
			virtual void Serialize(Archive * pArchv);
			#ifndef IFF_READ_ONLY
				virtual ~MiscChunk();
			#endif // ! IFF_READ_ONLY
			
		private:
			#ifndef IFF_READ_ONLY
				BYTE * m_pData;
				unsigned m_nSize;
			#endif // ! IFF_READ_ONLY
	};
	
	/******************************/
	/* Original File: iffFile.hpp */
	/******************************/

	class GenericFile : public SerializableObj
	{
		public:
			#ifndef IFF_READ_ONLY
				GenericFile() : m_pszFileName(NULL) {}
				virtual ~GenericFile() { if (m_pszFileName) delete[] m_pszFileName; }
			#endif
		
			bool Load(TCHAR const * pszFileName);
			bool Load(::MediaMedium * pMedium);
			
			#ifndef IFF_READ_ONLY
				bool Write(TCHAR const * pszFileName = NULL);
				bool Write(::MediaMedium * pMedium);
			#endif // ! IFF_READ_ONLY
			
		private:
			#ifndef IFF_READ_ONLY
				TCHAR * m_pszFileName;
			#endif // ! IFF_READ_ONLY
	};
	
	class File : public GenericFile
	{
		public:
			virtual ~File();
			File() : m_pContents(NULL) {}
			
			Composite * GetContents() const;
			void SetContents(Composite * pContents);
			
		protected:
			virtual void Serialize(Archive * pArchv);
			
		private:
			ID m_idType;
			Composite * m_pContents;
	};
	
	inline Composite * File::GetContents() const
	{
		return m_pContents;
	}
	
	inline void File::SetContents(Composite * pContents)
	{
		if (m_pContents) m_pContents->Release();
		if (pContents)
		{
			pContents->AddRef();
			m_idType = pContents->m_idCk;
		}
		m_pContents = pContents;
	}
	
	// Have a static object of this in each file:
	// It will detect if some files are compiled with
	// IFF_READ_ONLY defined differently to each other
	static class ConsistencyCheck
	{
		public: inline ConsistencyCheck()
		{
			#ifdef IFF_READ_ONLY
				static bool bReadOnly = true;
				if (!bReadOnly)
			#else
				static bool bReadOnly = false;
				if (bReadOnly)
			#endif
			{
				DisplayMessage
				(
					#ifdef NDEBUG
						TEXT("Error"),
						TEXT("IFF_READ_ONLY definition not consistent")
					#else
						TEXT("IFF Compile Option Error"),
						TEXT("Some files which #include \"iff.hpp\"\n")
						TEXT("have the macro IFF_READ_ONLY defined\n")
						TEXT("and some don't.\n\n")
						TEXT("Please ensure this is consistent and recompile.")
					#endif
				);
				exit(-45);
			}
		}
	}
		consistencyCheck;
	
} // namespace IFF

#endif // ! _INCLUDED_IFF_HPP_
