// ZipFileHeader.cpp: implementation of the CZipFileHeader class.
//
////////////////////////////////////////////////////////////////////////////////
//  Copyright (C) 2000 Tadeusz Dracz.
//  For conditions of distribution and use, see copyright notice in ZipArchive.h
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ZipFileHeader.h"
#include "zlib.h"
#include "ZipAutoBuffer.h"
#include "ZipArchive.h"

#define ZipFileHeaderSIZE	46
#define LOCALZipFileHeaderSIZE	30
#define VERSIONMADEBY 20
#define ENCR_HEADER_LEN 12
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
char CZipFileHeader::m_gszSignature[] = {0x50, 0x4b, 0x01, 0x02};
char CZipFileHeader::m_gszLocalSignature[] = {0x50, 0x4b, 0x03, 0x04};
CZipFileHeader::CZipFileHeader()
{
	m_uExternalAttr = FILE_ATTRIBUTE_ARCHIVE;
	m_uModDate = m_uModTime = m_uInternalAttr = 0;
	m_uMethod = Z_DEFLATED;
}

CZipFileHeader::~CZipFileHeader()
{

}

// read the header from the central dir
bool CZipFileHeader::Read(CZipStorage *pStorage)
{
// 	// just in case
// 	m_pszComment.Release();
// 	m_pszFileName.Release();
	WORD uFileNameSize, uCommentSize, uExtraFieldSize;
	CZipAutoBuffer buf(ZipFileHeaderSIZE);
	pStorage->Read(buf, ZipFileHeaderSIZE, true);		
	memcpy(&m_szSignature,		buf, 4);
	memcpy(&m_uVersionMadeBy,	buf + 4, 2);
	memcpy(&m_uVersionNeeded,	buf + 6, 2);
	memcpy(&m_uFlag,			buf + 8, 2);
	memcpy(&m_uMethod,			buf + 10, 2);
	memcpy(&m_uModTime,			buf + 12, 2);
	memcpy(&m_uModDate,			buf + 14, 2);
	memcpy(&m_uCrc32,			buf + 16, 4);
	memcpy(&m_uComprSize,		buf + 20, 4);
	memcpy(&m_uUncomprSize,		buf + 24, 4);
	memcpy(&uFileNameSize,		buf + 28, 2);
	memcpy(&uExtraFieldSize,	buf + 30, 2);
	memcpy(&uCommentSize,		buf + 32, 2);
	memcpy(&m_uDiskStart,		buf + 34, 2);
	memcpy(&m_uInternalAttr,	buf + 36, 2);
	memcpy(&m_uExternalAttr,	buf + 38, 4);
	memcpy(&m_uOffset,			buf + 42, 4);
	buf.Release();

	if (memcmp(m_szSignature, m_gszSignature, 4) != 0)
		return false;

	int iCurDsk = pStorage->GetCurrentDisk();
	m_pszFileName.Allocate(uFileNameSize); // don't add NULL at the end
	pStorage->m_pFile->Read(m_pszFileName, uFileNameSize);
	if (uExtraFieldSize)
	{
		ASSERT(!m_pExtraField.IsAllocated());
		m_pExtraField.Allocate(uExtraFieldSize);
		pStorage->m_pFile->Read(m_pExtraField, uExtraFieldSize);
	}
	if (uCommentSize)
	{
		m_pszComment.Allocate(uCommentSize);
		pStorage->m_pFile->Read(m_pszComment, uCommentSize);
	}

	return pStorage->GetCurrentDisk() == iCurDsk; // check that the while header is on the one disk
}

// return CTime representation of m_uModDate, m_uModTime
CTime CZipFileHeader::GetTime()
{
	return CTime(m_uModDate, m_uModTime);
}

// write the header to the central dir
DWORD CZipFileHeader::Write(CZipStorage *pStorage)
{
	WORD uFileNameSize = GetFileNameSize(), uCommentSize = GetCommentSize(),
		uExtraFieldSize = GetExtraFieldSize();
	DWORD iSize = GetSize();
	CZipAutoBuffer buf(iSize);
	memcpy(buf, &m_szSignature, 4);
	memcpy(buf + 4, &m_uVersionMadeBy, 2);
	memcpy(buf + 6, &m_uVersionNeeded, 2);
	memcpy(buf + 8, &m_uFlag, 2);
	memcpy(buf + 10, &m_uMethod, 2);
	memcpy(buf + 12, &m_uModTime, 2);
	memcpy(buf + 14, &m_uModDate, 2);
	memcpy(buf + 16, &m_uCrc32, 4);
	memcpy(buf + 20, &m_uComprSize, 4);
	memcpy(buf + 24, &m_uUncomprSize, 4);
	memcpy(buf + 28, &uFileNameSize, 2);
	memcpy(buf + 30, &uExtraFieldSize, 2);
	memcpy(buf + 32, &uCommentSize, 2);
	memcpy(buf + 34, &m_uDiskStart, 2);
	memcpy(buf + 36, &m_uInternalAttr, 2);
	memcpy(buf + 38, &m_uExternalAttr, 4);
	memcpy(buf + 42, &m_uOffset, 4);

	memcpy(buf + 46, m_pszFileName, uFileNameSize);

	if (uExtraFieldSize)
		memcpy(buf + 46 + uFileNameSize, m_pExtraField, uExtraFieldSize);

	if (uCommentSize)
		memcpy(buf + 46 + uFileNameSize + uExtraFieldSize, m_pszComment, uCommentSize);

	pStorage->Write(buf, iSize, true);
	return iSize;
}

// read local header
bool CZipFileHeader::ReadLocal(CZipStorage *pStorage, WORD& iLocExtrFieldSize)
{
	char buf[LOCALZipFileHeaderSIZE];
	pStorage->Read(buf, LOCALZipFileHeaderSIZE, true);
	if (memcmp(buf, m_gszLocalSignature, 4) != 0)
		return false;

	bool bIsDataDescr = (((WORD)*(buf + 6)) & 8) != 0;

	WORD uFileNameSize = GetFileNameSize();
	if ((memcmp(buf + 6, &m_uFlag, 2) != 0)
		||(memcmp(buf + 8, &m_uMethod, 2) != 0)
		|| (m_uMethod && (m_uMethod != Z_DEFLATED))
		|| (memcmp(buf + 26, &uFileNameSize, 2) != 0))
		return false;

// jeszcze możnaby porównać nazwy plików

	if (!bIsDataDescr/* || !pStorage->IsSpanMode()*/)
		if (!CheckCrcAndSizes(buf + 14))
			return false;

	memcpy(&iLocExtrFieldSize, buf + 28, 2);
	pStorage->m_pFile->Seek(uFileNameSize, CFile::current);

	return true;
}

// set the m_uModDate, m_uModTime values using CTime object
void CZipFileHeader::SetTime(const CTime &time)
{
    WORD year = (WORD)time.GetYear();
    if (year <= 1980)
		year = 0;
	else
		year -= 1980;
    m_uModDate = (WORD) (time.GetDay() + (time.GetMonth() << 5) + (year << 9));
    m_uModTime = (WORD) ((time.GetSecond() >> 1) + (time.GetMinute() << 5) + 
		(time.GetHour() << 11));
}
//	the buffer contains crc32, compressed and uncompressed sizes to be compared 
//	with the actual values
bool CZipFileHeader::CheckCrcAndSizes(char *pBuf)
{
	return (memcmp(pBuf, &m_uCrc32, 4) == 0) && (memcmp(pBuf + 4, &m_uComprSize, 4) == 0)
		&& (memcmp(pBuf + 8, &m_uUncomprSize, 4) == 0);
}

// write the local header
void CZipFileHeader::WriteLocal(CZipStorage& storage)
{
	// extra field is local by now
	WORD uFileNameSize = GetFileNameSize(),	uExtraFieldSize = GetExtraFieldSize();
	DWORD iLocalSize = LOCALZipFileHeaderSIZE + uExtraFieldSize + uFileNameSize;
	CZipAutoBuffer buf(iLocalSize);
	memcpy(buf, m_gszLocalSignature, 4);
	memcpy(buf + 4, &m_uVersionNeeded, 2);
	memcpy(buf + 6, &m_uFlag, 2);
	memcpy(buf + 8, &m_uMethod, 2);
	memcpy(buf + 10, &m_uModTime, 2);
	memcpy(buf + 12, &m_uModDate, 2);
	memcpy(buf + 14, &m_uCrc32, 4);
	memcpy(buf + 18, &m_uComprSize, 4);
	memcpy(buf + 22, &m_uUncomprSize, 4);
	memcpy(buf + 26, &uFileNameSize, 2);
	memcpy(buf + 28, &uExtraFieldSize, 2);
	memcpy(buf + 30, m_pszFileName, uFileNameSize);
	memcpy(buf + 30 + uFileNameSize, m_pExtraField, uExtraFieldSize);

	// possible disk change before writting to the file in the disk spanning mode
	// so write the local header first 
	storage.Write(buf, iLocalSize, true);
	// it was only local information, use CZipArchive::SetExtraField to set the file extra field in the central directory
	m_pExtraField.Release();

	m_uDiskStart = (WORD)storage.GetCurrentDisk();
	m_uOffset = storage.GetPosition() - iLocalSize;
}

// prepare the data before adding a new file
bool CZipFileHeader::PrepareData(int iLevel, bool bExtraHeader, bool bEncrypted)
{
	memcpy(m_szSignature, m_gszSignature, 4);
	m_uInternalAttr = 0;
	m_uVersionMadeBy = VERSIONMADEBY;
	m_uVersionNeeded = 20;

	m_uCrc32 = 0;
	m_uComprSize = 0;
	m_uUncomprSize = 0;
	if (iLevel == 0)
		m_uMethod = 0;

	if ((m_uMethod != Z_DEFLATED) && (m_uMethod != 0))
		m_uMethod = Z_DEFLATED;

	m_uFlag  = 0;
	if (m_uMethod == Z_DEFLATED)
		switch (iLevel)
		{
		case 1:
			m_uFlag  |= 6;
			break;
		case 2:
			m_uFlag  |= 4;
			break;
		case 8:
		case 9:
			m_uFlag  |= 2;
			break;
		}

	if (bExtraHeader)
		m_uFlag  |= 8; // data descriptor present

	if (bEncrypted)
	{
		m_uComprSize = ENCR_HEADER_LEN;	// encrypted header
		m_uFlag  |= 9;		// encrypted file
	}

	return !(m_pszComment.GetSize() > USHRT_MAX || m_pszFileName.GetSize() > USHRT_MAX
		|| m_pExtraField.GetSize() > USHRT_MAX);
}

// fill the buffer with the current values
void CZipFileHeader::GetCrcAndSizes(char * pBuffer)
{
	memcpy(pBuffer, &m_uCrc32, 4);
	memcpy(pBuffer + 4, &m_uComprSize, 4);
	memcpy(pBuffer + 8, &m_uUncomprSize, 4);
}

DWORD CZipFileHeader::GetSize()
{
	return ZipFileHeaderSIZE + GetExtraFieldSize() + GetFileNameSize() + GetCommentSize();
}


bool CZipFileHeader::IsEncrypted()
{
	return (m_uFlag & (WORD) 1) != 0;
}

bool CZipFileHeader::IsDataDescr()
{
	return (m_uFlag & (WORD) 8) != 0;
}

bool CZipFileHeader::SetComment(LPCTSTR lpszComment)
{
	return CZipArchive::WideToSingle(lpszComment, m_pszComment)	!= -1;
}

CString CZipFileHeader::GetComment()
{
	CString temp;
	CZipArchive::SingleToWide(m_pszComment, temp);
	return temp;

}

bool CZipFileHeader::SetFileName(LPCTSTR lpszFileName)
{
	return CZipArchive::WideToSingle(lpszFileName, m_pszFileName) != -1;
}

CString CZipFileHeader::GetFileName()
{
	CString temp;
	CZipArchive::SingleToWide(m_pszFileName, temp);
	return temp;
}


void CZipFileHeader::SlashChange(bool bWindowsStyle)
{
	char t1 = '\\', t2 = '/', c1, c2;
	if (bWindowsStyle)
	{
		c1 = t1;
		c2 = t2;
	}
	else
	{
		c1 = t2;
		c2 = t1;
	}
	for (DWORD i = 0; i < m_pszFileName.GetSize(); i++)
	{
		if (m_pszFileName[i] == c2)
			m_pszFileName[i] = c1;
	}
}

void CZipFileHeader::AnsiOem(bool bAnsiToOem)
{
	if (bAnsiToOem)
		CharToOemBuffA(m_pszFileName, m_pszFileName, m_pszFileName.GetSize());
	else
		OemToCharBuffA(m_pszFileName, m_pszFileName, m_pszFileName.GetSize());
}
