// Archive/ZipItem.cpp

#include "StdAfx.h"

#include "ZipHeader.h"
#include "ZipItem.h"
#include "../Common/ItemNameUtils.h"
#include "../../../../C/CpuArch.h"

namespace NArchive {
namespace NZip {

bool operator==(const CVersion &v1, const CVersion &v2)
{
  return (v1.Version == v2.Version) && (v1.HostOS == v2.HostOS);
}

bool operator!=(const CVersion &v1, const CVersion &v2)
{
  return !(v1 == v2);
}

bool CExtraSubBlock::ExtractNtfsTime(int index, FILETIME &ft) const
{
  ft.dwHighDateTime = ft.dwLowDateTime = 0;
  UInt32 size = (UInt32)Data.GetCapacity();
  if (ID != NFileHeader::NExtraID::kNTFS || size < 32)
    return false;
  const Byte *p = (const Byte *)Data;
  p += 4; // for reserved
  size -= 4;
  while (size > 4)
  {
    UInt16 tag = GetUi16(p);
    UInt32 attrSize = GetUi16(p + 2);
    p += 4;
    size -= 4;
    if (attrSize > size)
      attrSize = size;
    
    if (tag == NFileHeader::NNtfsExtra::kTagTime && attrSize >= 24)
    {
      p += 8 * index;
      ft.dwLowDateTime = GetUi32(p);
      ft.dwHighDateTime = GetUi32(p + 4);
      return true;
    }
    p += attrSize;
    size -= attrSize;
  }
  return false;
}

bool CExtraSubBlock::ExtractUnixTime(int index, UInt32 &res) const
{
  res = 0;
  UInt32 size = (UInt32)Data.GetCapacity();
  if (ID != NFileHeader::NExtraID::kUnixTime || size < 5)
    return false;
  const Byte *p = (const Byte *)Data;
  Byte flags = *p++;
  size--;
  for (int i = 0; i < 3; i++)
    if ((flags & (1 << i)) != 0)
    {
      if (size < 4)
        return false;
      if (index == i)
      {
        res = GetUi32(p);
        return true;
      }
      p += 4;
      size -= 4;
    }
  return false;
}

bool CLocalItem::IsDir() const
{
  return NItemName::HasTailSlash(Name, GetCodePage());
}

bool CItem::IsDir() const
{
  if (NItemName::HasTailSlash(Name, GetCodePage()))
    return true;
  if (!FromCentral)
    return false;
  WORD highAttributes = WORD((ExternalAttributes >> 16 ) & 0xFFFF);
  switch(MadeByVersion.HostOS)
  {
    case NFileHeader::NHostOS::kAMIGA:
      switch (highAttributes & NFileHeader::NAmigaAttribute::kIFMT)
      {
        case NFileHeader::NAmigaAttribute::kIFDIR: return true;
        case NFileHeader::NAmigaAttribute::kIFREG: return false;
        default: return false; // change it throw kUnknownAttributes;
      }
    case NFileHeader::NHostOS::kFAT:
    case NFileHeader::NHostOS::kNTFS:
    case NFileHeader::NHostOS::kHPFS:
    case NFileHeader::NHostOS::kVFAT:
      return ((ExternalAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
    case NFileHeader::NHostOS::kAtari:
    case NFileHeader::NHostOS::kMac:
    case NFileHeader::NHostOS::kVMS:
    case NFileHeader::NHostOS::kVM_CMS:
    case NFileHeader::NHostOS::kAcorn:
    case NFileHeader::NHostOS::kMVS:
      return false; // change it throw kUnknownAttributes;
    default:
      /*
      switch (highAttributes & NFileHeader::NUnixAttribute::kIFMT)
      {
        case NFileHeader::NUnixAttribute::kIFDIR:
          return true;
        default:
          return false;
      }
      */
      return false;
  }
}

#ifndef FILE_ATTRIBUTE_UNIX_EXTENSION
UInt32 CLocalItem::GetWinAttributes() const
{
  DWORD winAttributes = 0;
  if (IsDir())
    winAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  return winAttributes;
}
#endif

UInt32 CItem::GetWinAttributes() const
{
  DWORD winAttributes = 0;
  switch(MadeByVersion.HostOS)
  {
    case NFileHeader::NHostOS::kFAT:
    case NFileHeader::NHostOS::kNTFS:
      if (FromCentral)
        winAttributes = ExternalAttributes;
      break;
#ifdef FILE_ATTRIBUTE_UNIX_EXTENSION
    case NFileHeader::NHostOS::kUnix:
        winAttributes = (ExternalAttributes & 0xFFFF0000) | FILE_ATTRIBUTE_UNIX_EXTENSION; 
        if (winAttributes & (NFileHeader::NUnixAttribute::kIFDIR << 16))
		winAttributes |= FILE_ATTRIBUTE_DIRECTORY;
        return winAttributes;
#endif
    default:
      winAttributes = 0; // must be converted from unix value;
  }
  if (IsDir())       // test it;
    winAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  return winAttributes;
}

void CLocalItem::SetFlagBits(int startBitNumber, int numBits, int value)
{
  UInt16 mask = (UInt16)(((1 << numBits) - 1) << startBitNumber);
  Flags &= ~mask;
  Flags |= value << startBitNumber;
}

void CLocalItem::SetBitMask(int bitMask, bool enable)
{
  if(enable)
    Flags |= bitMask;
  else
    Flags &= ~bitMask;
}

void CLocalItem::SetEncrypted(bool encrypted)
  { SetBitMask(NFileHeader::NFlags::kEncrypted, encrypted); }
void CLocalItem::SetUtf8(bool isUtf8)
  { SetBitMask(NFileHeader::NFlags::kUtf8, isUtf8); }

}}
