////////////////////////////////////////////////////////////////////////////////
// MechWarriorIIPRJ.h
// MechWarrior II PRJ file system
// Copyright Bjoern Ganster
// Developed since 2007
////////////////////////////////////////////////////////////////////////////////

#ifndef MechWarriorIIPRJ__H
#define MechWarriorIIPRJ__H

#include "Archive.h"
#include "FileCache.h"
#include "Palette.h"
#include "XMLTree.h"

////////////////////////////////////////////////////////////////////////////////

#ifndef TitanFarPatcher
class Texture;
class Mesh;
class MW2TextureTable;
#endif

class MemoryBlock;

struct LocalTransform {
	int ID;
	double sx, sy, sz; // scale
	double tx, ty, tz; // transform
	double rx, ry, rz; // rotate

	LocalTransform()
	: ID(0),
	  sx(1),
	  sy(1),
	  sz(1),
	  tx(0.0),
	  ty(0.0),
	  tz(0.0),
	  rx(0.0),
	  ry(0.0),
	  rz(0.0)
	{
	}
};

////////////////////////////////////////////////////////////////////////////////
// MW2 PRJ record definitions

#pragma pack (push)
#pragma pack(2) // MW2 file data are WORD aligned?

struct MW2_PRJ_main_directory_entry {
   char dirName[4];
   DWORD dirOffset;
   char unknown[16];
};

struct MW2_PRJ_directory_header {
   char marker[4]; // Marker string "INDX"
   DWORD structSize; // Size of this record, plus MW2_PRJ_Directory_Entries, minus 8
   DWORD unknown;
   char DirName[4];
   WORD reservedCount; // Total number of directory entries 
   WORD altCount; // Alternative number of entries, in most cases the same as entries
   WORD entries; // Used number of entries, plus one
   //char unknown2[8]; // zero in all tested cases
}; // 22 bytes

struct MW2_PRJ_Directory_Entries {
   DWORD offset;
   DWORD size;
}; // 8 bytes

struct MW2_PRJ_local_file_header {
   char marker[4]; // Marker string DATA
   DWORD SizeOfThisFile,      // Size of file plus 54 bytes
         unknown1;
   char parentDir[4];         // Name of directory containing the file
   char unknown2[8]; 
   WORD FileNumberInParentDir; // Entry number of this file in parent dir + 1
   char unknown3[4];          // A flag? 
   char nameWithoutExt[16];   // Null-terminated file name without extension
   char nameWithExt[16];      // Null-terminated file name with extension
}; // 62 bytes

struct SymbolTableHeader {
   char marker[4]; // Marker string "SYMB"
   DWORD size; // Size of this record, plus file name records, minus 8
   char parentDir[4];         // Name of directory containing the file
   WORD maxEntries;
   WORD altCount; // Exact number of entries, same as entries, below?
   WORD unknown;
   DWORD entries; // Exact number of entries
}; // 16h=22 bytes

struct SymbolTableEntry {
   char FileName[16]; // File name without extension, zero-padded
   WORD FileNum; // 1 for first file, 2 for second, ...
}; // 12h=18 bytes

#pragma pack (pop)

////////////////////////////////////////////////////////////////////////////////
// Class to access MW2.PRJ files

class MechWarriorIIPRJ: public MWArchive {
public:
	BGCPString bmIdFn;

	// Constructor
	MechWarriorIIPRJ();

	// Destructor
	~MechWarriorIIPRJ();

	// Open file
	bool open (const PathChar* PRJFN, bool readOnly = true) OVERRIDE;

	// Close file
	void close() OVERRIDE;

	// Enter subdirectory
	bool enterSubDir(size_t /*index*/) OVERRIDE;
	bool enterSubDir(const char* /*dirName*/) OVERRIDE;

	// Leave subdirectory
	bool leaveDir() OVERRIDE;

	// Read bufSize bytes from file numbered index at offset into buf
	bool getFileChunk(size_t index, size_t offset, void* buf, size_t bufSize) OVERRIDE;

	// Load texture, geometry
#ifndef TitanFarPatcher
	static bool loadXEL (const MemoryBlock& mb, const Palette& palette, Texture& texture);
	static bool loadWTB (const MemoryBlock* block, Mesh* mesh, double scale);
#endif

	// Read bufSize bytes from file numbered index at offset into buf
#ifndef TitanFarPatcher
	static bool loadPCX (const unsigned char* buf, size_t bufSize, Texture& texture, Palette& pal);
#endif

	// Convert SFL to Wav file
	static MemoryBlock* SFL2Wav (const MemoryBlock* mb);

	// SHP suport
#ifndef TitanFarPatcher
	static inline size_t getSHPFrameCount(const MemoryBlock& mb) 
	{ return mb.get<DWORD>(4); }
	static bool loadSHP (const MemoryBlock& mb, size_t picNum, const Palette& palette, Texture& texture);
#endif

	// Check if currently in sub directory
	virtual bool inSubDir() const
	{ return (m_offset > 0x18); }

	// Load MW2 PRJ file into a MemoryBlock
	bool loadFile (const BGCPString& FN, MemoryBlock& mb);

	// TEX file introduced in MW2:Mercs
#ifndef TitanFarPatcher
	static bool loadTEX (const unsigned char* buf, size_t bufSize, Texture& texture);
#endif

	// Save PRJ file to new file name
	bool saveToFile (const TCHAR* fn);

	// Replace numbered file in current directory
	//bool setFile (size_t fileNum, const char* fileBuf, size_t BWDSize);

	// Replace  file
	virtual bool replaceFile (size_t fileNum, const PathChar* srcFN);

	// Check if archive type supports file replacement
	virtual bool replaceFilePossible () const 
	{
		return false;
	}
	/*{
	if (fc.getFileSize() > 15 * 1024 * 1024) 
	return (fc.isReadOnly() == false); 
	else
	return false; // Prevent NetMech fiddling
	}*/

	// Check if entry index is a directory
	virtual bool isDirectory (size_t /*index*/) const OVERRIDE
	{
		return (!inSubDir());
	}

	// Add files to archive
	virtual void addFileToArchive (const PathChar* FN);
	virtual bool canAddFilesToArchive () const
	{ return true; }

	// Get file name for a file in another directory w/ochanging into that directory
	bool getEntryNameFromOtherDir (const char* dirName, size_t index, char* buf, 
		size_t bufSize);

	// Get file from another directory withoutchanging into that directory
	MemoryBlock* getFileFromOtherDir (const char* dirName, size_t index);

private:
	FileCache fc;
	size_t m_offset;
	WORD m_mainDirEntryCount;
	MW2_PRJ_main_directory_entry* m_mainDirEntries;
	BGCPString m_dirName;
	std::vector <MW2_PRJ_Directory_Entries*> m_dirEntries;
	std::vector <MW2_PRJ_directory_header> m_dirHeaders;

	// Return to main directory
	bool readMainDir();

	// Find directory
	size_t findDirectory(const char* dirName);
};

// Decode MW2 555 image
#ifndef TitanFarPatcher
bool Decode_MW2_555 (const MemoryBlock& mb, Texture& texture);
#endif

// Export BWD file as XML
#ifndef TitanFarPatcher
bool BWD2XML (const MemoryBlock& mb, XMLTreeIterator& iter, bool followIncludes, 
	const PathChar* WTB_outputDir, const LocalTransform* parentTransform, const BGCPString& BWDFN, MechWarriorIIPRJ* prj);
#endif

// decodes an SFLX file sitting in incoming, writes pcm to outgoing
char* sflx(const char *incoming, char *outgoing);

// Decode MW2 font (true type fonts are used)
void decodeFNT(const MemoryBlock& block, Texture& texture);

#endif
