////////////////////////////////////////////////////////////////////////////////
// ZFSF.cpp
// Interstate '76 file system
// Copyright Bjoern Ganster
// Developed since 2022
////////////////////////////////////////////////////////////////////////////////

#include "ZFSF.h"

#define FILE_NUMBER FILE_NO_ZFSF_CPP

struct ZfsfHeader {
	char sig[4];
	DWORD reserved1, reserved2, entriesPerTocSection, totalFileCount, reserved3, reserved4;
};

struct ZfsfTocEntry {
	char fn[16];
	DWORD offset, unknown1, unknown2, unknown3, fileSize;
};

// Constructor
ZFSF::ZFSF()
: MWArchive("ZFSF", ZFSF_ID)
{
}

// Destructor
ZFSF::~ZFSF()
{
}

// Open file
bool ZFSF::open (const PathChar* PRJFN, bool readOnly)
{
	SET_SCOPE;
	bool openedSuccessfully;
	if (readOnly) {
		openedSuccessfully = mFC.openReadOnly(PRJFN);
	} else {
		openedSuccessfully = mFC.openReadWrite(PRJFN);
	}

	// Read main directory
	bool MainDirReadSuccessfully = false;
	WORD entryCount = 0;
	if (!openedSuccessfully) {
		RETURN false;
	}

	ZfsfHeader header;
	if (!mFC.read (0, (char*)(&header), sizeof(ZfsfHeader))) {
		RETURN false;
	}
	if (memcmp (header.sig, "ZFSF", 4) != 0) {
		RETURN false;
	}

	if (!mFC.read (0x0c, (char*) (&entryCount), 2)) {
		RETURN false;
	}

	SET_OP;
	FileOffset tocOffset = 0x1c;
	size_t tocSize = sizeof(ZfsfTocEntry) * entryCount;

	while (tocOffset < mFC.getFileSize()) {
		DWORD nextToc = 0;
		mFC.read(tocOffset, (char*)(&nextToc), 4);
		auto oldTocSize = mTOC.getSize();
		DWORD readNow = tocSize;
		auto entriesSoFar = oldTocSize / sizeof(ZfsfTocEntry);
		if (entriesSoFar + header.entriesPerTocSection > header.totalFileCount) {
			readNow = (header.totalFileCount - entriesSoFar) * sizeof(ZfsfTocEntry);
			nextToc = mFC.getFileSize();
		}
		mTOC.resize(oldTocSize + readNow);
		mFC.read(tocOffset+4, mTOC.getPtr<char>(oldTocSize), readNow);
		tocOffset = nextToc;
	}

	RETURN true;
}

// Close file
void ZFSF::close()
{
	mTOC.resize(0);
}

// Get name of entry
bool ZFSF::getEntryName(size_t index, BGCPString& name)
{
	SET_SCOPE;
	size_t offset = index * sizeof(ZfsfTocEntry);
	if (offset+16 >= mTOC.getSize()) {
		RETURN false;
	}

	RETURN name.assign(mTOC.getPtr<char>(offset), 16, CP_ASCII);
}

// Get size of entry, 0: directory or error
size_t ZFSF::getFileSize(size_t index)
{
	SET_SCOPE;
	ZfsfTocEntry* entry = mTOC.getPtr<ZfsfTocEntry>(index * sizeof(ZfsfTocEntry));

	if (entry != nullptr) {
		RETURN entry->fileSize;
	} else {
		RETURN 0;
	}
}

// Get offset of file start inside prj file
size_t ZFSF::getFileOffset(size_t index)
{
	SET_SCOPE;
	ZfsfTocEntry* entry = mTOC.getPtr<ZfsfTocEntry>(index * sizeof(ZfsfTocEntry));

	if (entry != nullptr) {
		RETURN entry->offset;
	} else {
		RETURN 0;
	}
}

// Read bufSize bytes from file numbered index at offset into buf
bool ZFSF::getFileChunk(size_t index, size_t offset, void* buf, size_t bufSize)
{
	SET_SCOPE;
	ZfsfTocEntry* entry = mTOC.getPtr<ZfsfTocEntry>(index * sizeof(ZfsfTocEntry));

	if (entry != nullptr) {
		if (offset >= entry->fileSize) {
			RETURN 0;
		}
		auto toRead = bufSize;
		if (offset + toRead > entry->fileSize) {
			toRead = entry->fileSize - offset;
		}
		RETURN mFC.read(entry->offset + offset + 1, (char*)buf, toRead);
	} else {
		RETURN 0;
	}
}
