
#pragma once


#ifdef PRE_TOD_DATA_FILES
#define DAT_HEADER_OFFSET 0x12C
#else
#define DAT_HEADER_OFFSET 0x140
#endif

#ifdef PRE_TOD_DATA_FILES
struct DATHeader
{
	DWORD FileType; // 0x00 'TB' !
	DWORD BlockSize; // 0x04 0x400 for PORTAL : 0x100 for CELL
	DWORD FileSize; // 0x08 Should match file size.
	DWORD Iteration; // 0x0C Version iteration.
	DWORD FreeHead; // 0x10
	DWORD FreeTail; // 0x14
	DWORD FreeCount; // 0x18
	DWORD BTree; // 0x1C BTree offset
	DWORD Unknown0; // 0x20
	DWORD Unknown1; // 0x24
	DWORD Unknown2; // 0x28
};
#else
#pragma pack(push, 4)
struct DATHeader
{
	DWORD FileType;
	DWORD BlockSize;
	DWORD FileSize;
	DWORD DataSet; // 1 = portal
	DWORD DataSubset;
	DWORD FreeHead;
	DWORD FreeTail;
	DWORD FreeCount;
	DWORD BTree;
	DWORD NewLRU; // 0
	DWORD OldLRU; // 0
	bool bUseLRU; // False
	DWORD MasterMapID;
	DWORD EnginePackVersion;
	DWORD GamePackVersion; // 0
	BYTE VersionMajor[16];
	DWORD VersionMinor;
};
#pragma pack(pop)
#endif

#ifdef PRE_TOD_DATA_FILES
struct BTreeEntry
{
	DWORD ID;
	DWORD BlockHead;
	DWORD Length;
};
#else
struct BTreeEntry
{
	DWORD Unknown00;
	DWORD ID;
	DWORD BlockHead;
	DWORD Length;
	DWORD Timestamp;
	DWORD Unknown14;
};
#endif

struct BTreeData
{
	DWORD BlockSpacer;
	DWORD Branches[0x3E];
	DWORD EntryCount;
	BTreeEntry Entries[0x3D];
};

class BlockLoader;

class BTreeNode
{
public:
	BTreeNode(BlockLoader *pBlockLoader);
	virtual ~BTreeNode();

	BOOL LoadData(DWORD BlockHead);
	void LoadChildren();
	void LoadChildrenRecursive();

	void SetFileCallback(void(*)(void *, DWORD, BTreeEntry *));
	void SetProgressCallback(void(*)(void *, float));
	void SetCallbackArg(void *);

	BOOL Lookup(DWORD ID, BTreeEntry *pEntry);

	void FindEntryIDsWithinRange(DWORD Min, DWORD Max, float Progress, float ProgressDelta);

protected:

	DWORD GetBranchCount() const;
	BTreeNode *GetBranch(DWORD index);

	// Using this design, you can't run 2 scans at the same time.
	static void(*m_pfnFileCallback)(void *, DWORD, BTreeEntry *);
	static void(*m_pfnProgressCallback)(void *, float);
	static void *m_pCallbackArg;

	BlockLoader *m_pBlockLoader;

	BTreeData m_TreeData;
	BTreeNode* m_Branches[0x3E];

	BOOL m_bLeaf;
};

class BTree : public BTreeNode
{
public:
	BTree(BlockLoader *pBlockLoader);
	virtual ~BTree();

	BOOL Init();
};

class DiskDev
{
public:
	DiskDev();
	~DiskDev();

	BOOL OpenFile(const char* Path, DATHeader *pHeader);
	void CloseFile();

	BOOL SyncRead(void *pBuffer, DWORD dwLength, DWORD dwPosition);
	BOOL SyncWrite(void *pBuffer, DWORD dwLength, DWORD dwPosition);

private:

	HANDLE m_hFile;
};

class BlockLoader
{
public:
	BlockLoader();
	~BlockLoader();

	BOOL Init(const char *Path, DATHeader *pHeader);

	DWORD GetTreeOrigin();

	BOOL LoadData(DWORD HeadBlock, void *pBuffer, DWORD Length);

private:

	DATHeader *m_pHeader;
	DiskDev m_DiskDev;

};

struct DATEntry
{
	DWORD ID;
	BYTE* Data;
	DWORD Length;
};

class DATDisk
{
public:
	static BOOL OpenDisks(const char *portalPath, const char *cellPath);
	static void CloseDisks();
	static DATDisk *pPortal;
	static DATDisk *pCell;

	DATDisk(const char *Path);
	~DATDisk();

	BOOL Open();
	BOOL GetData(DWORD ID, DATEntry *pEntry);
	BOOL GetDataEx(DWORD BlockHead, void *Data, DWORD Length);
	void FindFileIDsWithinRange(DWORD Min, DWORD Max, void(*FileCallback)(void *, DWORD, BTreeEntry *), void(*ProgressCallback)(void *, float), void *CallbackArg);

	const DATHeader *GetHeader();

private:

	char *m_FilePath;

	DATHeader m_DATHeader;
	BlockLoader m_BlockLoader;
	BTree m_BTree;
};




