////////////////////////////////////////////////////////////////////////////////
// This source file is part of the ZipArchive library source distribution and
// is Copyrighted 2000 - 2009 by Artpol Software - Tadeusz Dracz
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// For the licensing details refer to the License.txt file.
//
// Web Site: http://www.artpol-software.com
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#if _ZIP_FILE_IMPLEMENTATION == ZIP_ZFI_WIN

#include "ZipFile.h"
#include "ZipException.h"
#include "ZipPathComponent.h"
#include "BitFlag.h"

#if _MSC_VER < 1300 && !defined INVALID_SET_FILE_POINTER
	#define INVALID_SET_FILE_POINTER (DWORD)(-1)
#endif

CZipFile::CZipFile()
{
	m_hFile = INVALID_HANDLE_VALUE;
}

CZipFile::CZipFile(LPCTSTR lpszFileName, UINT openFlags)
{
	m_hFile = INVALID_HANDLE_VALUE;
	Open(lpszFileName, openFlags, true);
}

void CZipFile::ThrowError() const
{
	CZipException::Throw(CZipException::fileError, m_szFileName);
}

bool CZipFile::Open(LPCTSTR lpszFileName, UINT openFlags, bool bThrow)
{
	if (!IsClosed())
		Close();

	CZipString fileName = lpszFileName;
	if (fileName.IsEmpty())
	{
		return false;
	}
	DWORD access;
	DWORD temp = openFlags & 3;
	if (temp == modeWrite)
	{
		access = GENERIC_WRITE;
	}
	else if (temp == modeReadWrite)
	{
		access = GENERIC_READ | GENERIC_WRITE;
	}
	else
	{
		access = GENERIC_READ;
	}

	DWORD share;
	temp = openFlags & 0x70;
	if (temp == shareDenyWrite)
	{
		share = FILE_SHARE_READ;
	}
	else if (temp == shareDenyRead)
	{
		share = FILE_SHARE_WRITE;
	}
	else if (temp == shareDenyNone)
	{
		share = FILE_SHARE_READ | FILE_SHARE_WRITE;
	}
	else
	{
		share = 0;
	}

#if defined _UNICODE && _MSC_VER >= 1400
	if (fileName.GetLength() > MAX_PATH)
	{
		int prefixLength = CZipPathComponent::IsPrefixed(fileName);
		if (prefixLength < CZipPathComponent::ptUnicode)
		{
			if (prefixLength > 0)
			{
				fileName = fileName.Mid(prefixLength);
			}
			fileName.Insert(0, CZipPathComponent::PathPrefix.Left(CZipPathComponent::ptUnicode));
		}
	}
#endif

	DWORD create;
	if (openFlags & modeCreate)
	{
		if (openFlags & modeNoTruncate)
		{
			create = OPEN_ALWAYS;
		}
		else
		{
			create = CREATE_ALWAYS;
		}
	}
	else
	{
		create = OPEN_EXISTING;
	}

	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(sa);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	m_hFile = ::CreateFile(fileName, access, share, &sa, create, FILE_ATTRIBUTE_NORMAL, NULL);
	if (m_hFile == INVALID_HANDLE_VALUE)
	{
		if (bThrow)
			ThrowError();
		else
			return false;
	}
		
	m_szFileName = lpszFileName;
	return true;
}


ULONGLONG CZipFile::GetLength() const
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	ULARGE_INTEGER size;
	size.LowPart = GetFileSize(m_hFile, &size.HighPart);
	if (size.LowPart == INVALID_FILE_SIZE && ::GetLastError() != NO_ERROR)
	{
		ThrowError();
	}

	return size.QuadPart;
}


void CZipFile::SetLength(ULONGLONG uNewLen)
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	if (uNewLen > _I64_MAX)
	{
		CZipException::Throw(CZipException::tooBigSize);
	}
	Seek(uNewLen, FILE_BEGIN);
	if (::SetEndOfFile(m_hFile) == FALSE)
	{
		ThrowError();
	}
}

ULONGLONG CZipFile::GetPosition() const
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);

	// do not call Seek, to keep GetPosition const
#if (defined(NTDDI_VERSION) && NTDDI_VERSION >=NTDDI_WIN2K) || (_WIN32_WINNT >= 0x0500 && WINVER >= 0x0500)	
	LARGE_INTEGER li, oli = {0};
	li.QuadPart = (LONGLONG)0;
	if (::SetFilePointerEx(m_hFile, li, &oli, FILE_CURRENT) == FALSE)	
	{
		ThrowError();
	}
	return oli.QuadPart;
#else
	LARGE_INTEGER li;
	li.QuadPart = (LONGLONG)0;
	li.LowPart = ::SetFilePointer(m_hFile, li.LowPart, &li.HighPart, FILE_CURRENT);
	if (li.LowPart == INVALID_SET_FILE_POINTER  && GetLastError() != NO_ERROR)
	{
		ThrowError();
	}
	return li.QuadPart;
#endif
}

ULONGLONG CZipFile::Seek(LONGLONG dOff, int nFrom)
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	ASSERT(nFrom == FILE_BEGIN || nFrom == FILE_END || nFrom == FILE_CURRENT);

#if (defined(NTDDI_VERSION) && NTDDI_VERSION >=NTDDI_WIN2K) || (_WIN32_WINNT >= 0x0500 && WINVER >= 0x0500)
	
	LARGE_INTEGER li, oli = {0};
	li.QuadPart = (LONGLONG)dOff;
	if (::SetFilePointerEx(m_hFile, li, &oli, (DWORD)nFrom) == FALSE)	
	{
		ThrowError();
	}
	return oli.QuadPart;
#else
	LARGE_INTEGER li;
	li.QuadPart = (LONGLONG)dOff;
	li.LowPart = ::SetFilePointer(m_hFile, li.LowPart, &li.HighPart, (DWORD)nFrom);
	if (li.LowPart == INVALID_SET_FILE_POINTER  && GetLastError() != NO_ERROR)
	{
		ThrowError();
	}
	return li.QuadPart;
#endif
}

void CZipFile::Close()
{
	if (IsClosed())
		return;
	bool ok = ::CloseHandle(m_hFile) == TRUE;
	m_hFile = INVALID_HANDLE_VALUE;
	m_szFileName.Empty();
	if (!ok)
	{
		ThrowError();
	}
}

void CZipFile::Flush()
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	if (::FlushFileBuffers(m_hFile) == FALSE)
	{
		ThrowError();
	}
}

CZipFile::operator HANDLE()
{
	return m_hFile;
}


void CZipFile::Write(const void* lpBuf, UINT nCount)
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	if (nCount == 0)
	{
		return;
	}
	DWORD written = 0;
	if (::WriteFile(m_hFile, lpBuf, nCount, &written, NULL) == FALSE || written != nCount)
	{
		ThrowError();
	}
}

UINT CZipFile::Read(void *lpBuf, UINT nCount)
{
	ASSERT(m_hFile != INVALID_HANDLE_VALUE);
	if (nCount == 0)
	{
		return 0;
	}
	DWORD read = 0;
	if (::ReadFile(m_hFile, lpBuf, nCount, &read, NULL) == FALSE)
	{
		ThrowError();
	}
	return (UINT)read;

}

#endif
