⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xarhandler.cpp

📁 7-Zip 是一款号称有着现今最高压缩比的压缩软件
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// XarHandler.cpp

#include "StdAfx.h"

#include "../../../C/CpuArch.h"

#include "Common/ComTry.h"
#include "Common/MyXml.h"
#include "Common/StringToInt.h"
#include "Common/UTFConvert.h"

#include "Windows/PropVariant.h"
#include "Windows/Time.h"

#include "../Common/LimitedStreams.h"
#include "../Common/ProgressUtils.h"
#include "../Common/RegisterArc.h"
#include "../Common/StreamObjects.h"
#include "../Common/StreamUtils.h"

#include "../Compress/BZip2/BZip2Decoder.h"
#include "../Compress/Copy/CopyCoder.h"
#include "../Compress/Deflate/ZlibDecoder.h"

#include "Common/OutStreamWithSha1.h"

#define XAR_SHOW_RAW

#define Get16(p) GetBe16(p)
#define Get32(p) GetBe32(p)
#define Get64(p) GetBe64(p)

namespace NArchive {
namespace NXar {

struct CFile
{
  AString Name;
  AString Method;
  UInt64 Size;
  UInt64 PackSize;
  UInt64 Offset;
  
  // UInt32 mode;
  UInt64 CTime;
  UInt64 MTime;
  UInt64 ATime;
  
  bool IsDir;
  bool HasData;

  bool Sha1IsDefined;
  Byte Sha1[20];
  // bool packSha1IsDefined;
  // Byte packSha1[20];

  int Parent;

  CFile(): IsDir(false), HasData(false), Sha1IsDefined(false),
    /* packSha1IsDefined(false), */
    Parent(-1), Size(0), PackSize(0), CTime(0), MTime(0), ATime(0) {}
};

class CHandler:
  public IInArchive,
  public CMyUnknownImp
{
  UInt64 _dataStartPos;
  CMyComPtr<IInStream> _inStream;
  AString _xml;
  CObjectVector<CFile> _files;

  HRESULT Open2(IInStream *stream);
  HRESULT Extract(IInStream *stream);
public:
  MY_UNKNOWN_IMP1(IInArchive)
  INTERFACE_IInArchive(;)
};

const UInt32 kXmlSizeMax = ((UInt32)1 << 30) - (1 << 14);

STATPROPSTG kProps[] =
{
  { NULL, kpidPath, VT_BSTR},
  { NULL, kpidSize, VT_UI8},
  { NULL, kpidPackSize, VT_UI8},
  { NULL, kpidMTime, VT_FILETIME},
  { NULL, kpidCTime, VT_FILETIME},
  { NULL, kpidATime, VT_FILETIME},
  { NULL, kpidMethod, VT_BSTR}
};

IMP_IInArchive_Props
IMP_IInArchive_ArcProps_NO

static bool ParseNumber(const char *s, int size, UInt32 &res)
{
  const char *end;
  res = (UInt32)ConvertStringToUInt64(s, &end);
  return (end - s == size);
}

static bool ParseUInt64(const CXmlItem &item, const char *name, UInt64 &res)
{
  AString s = item.GetSubStringForTag(name);
  const char *end;
  res = ConvertStringToUInt64(s, &end);
  return (end - (const char *)s == s.Length());
}

static UInt64 ParseTime(const CXmlItem &item, const char *name)
{
  AString s = item.GetSubStringForTag(name);
  if (s.Length() < 20)
    return 0;
  const char *p = s;
  if (p[ 4] != '-' || p[ 7] != '-' || p[10] != 'T' ||
      p[13] != ':' || p[16] != ':' || p[19] != 'Z')
    return 0;
  UInt32 year, month, day, hour, min, sec;
  if (!ParseNumber(p,      4, year )) return 0;
  if (!ParseNumber(p + 5,  2, month)) return 0;
  if (!ParseNumber(p + 8,  2, day  )) return 0;
  if (!ParseNumber(p + 11, 2, hour )) return 0;
  if (!ParseNumber(p + 14, 2, min  )) return 0;
  if (!ParseNumber(p + 17, 2, sec  )) return 0;
  
  UInt64 numSecs;
  if (!NWindows::NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs))
    return 0;
  return numSecs * 10000000;
}

static bool HexToByte(char c, Byte &res)
{
  if      (c >= '0' && c <= '9') res = c - '0';
  else if (c >= 'A' && c <= 'F') res = c - 'A' + 10;
  else if (c >= 'a' && c <= 'f') res = c - 'a' + 10;
  else return false;
  return true;
}

#define METHOD_NAME_ZLIB "zlib"

static bool ParseSha1(const CXmlItem &item, const char *name, Byte *digest)
{
  int index = item.FindSubTag(name);
  if (index  < 0)
    return false;
  const CXmlItem &checkItem = item.SubItems[index];
  AString style = checkItem.GetPropertyValue("style");
  if (style == "SHA1")
  {
    AString s = checkItem.GetSubString();
    if (s.Length() != 40)
      return false;
    for (int i = 0; i < s.Length(); i += 2)
    {
      Byte b0, b1;
      if (!HexToByte(s[i], b0) || !HexToByte(s[i + 1], b1))
        return false;
      digest[i / 2] = (b0 << 4) | b1;
    }
    return true;
  }
  return false;
}

static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int parent)
{
  if (!item.IsTag)
    return true;
  if (item.Name == "file")
  {
    CFile file;
    file.Parent = parent;
    parent = files.Size();
    file.Name = item.GetSubStringForTag("name");
    AString type = item.GetSubStringForTag("type");
    if (type == "directory")
      file.IsDir = true;
    else if (type == "file")
      file.IsDir = false;
    else
      return false;

    int dataIndex = item.FindSubTag("data");
    if (dataIndex >= 0 && !file.IsDir)
    {
      file.HasData = true;
      const CXmlItem &dataItem = item.SubItems[dataIndex];
      if (!ParseUInt64(dataItem, "size", file.Size))
        return false;
      if (!ParseUInt64(dataItem, "length", file.PackSize))
        return false;
      if (!ParseUInt64(dataItem, "offset", file.Offset))
        return false;
      file.Sha1IsDefined = ParseSha1(dataItem, "extracted-checksum", file.Sha1);
      // file.packSha1IsDefined = ParseSha1(dataItem, "archived-checksum",  file.packSha1);
      int encodingIndex = dataItem.FindSubTag("encoding");
      const CXmlItem &encodingItem = dataItem.SubItems[encodingIndex];
      if (encodingItem.IsTag)
      {
        AString s = encodingItem.GetPropertyValue("style");
        if (s.Length() >= 0)
        {
          AString appl = "application/";
          if (s.Left(appl.Length()) == appl)
          {
            s = s.Mid(appl.Length());
            AString xx = "x-";
            if (s.Left(xx.Length()) == xx)
            {
              s = s.Mid(xx.Length());
              if (s == "gzip")
                s = METHOD_NAME_ZLIB;
            }
          }
          file.Method = s;
        }
      }
    }

    file.CTime = ParseTime(item, "ctime");
    file.MTime = ParseTime(item, "mtime");
    file.ATime = ParseTime(item, "atime");
    files.Add(file);
  }
  for (int i = 0; i < item.SubItems.Size(); i++)
    if (!AddItem(item.SubItems[i], files, parent))
      return false;
  return true;
}

HRESULT CHandler::Open2(IInStream *stream)
{
  UInt64 archiveStartPos;
  RINOK(stream->Seek(0, STREAM_SEEK_SET, &archiveStartPos));

  const UInt32 kHeaderSize = 0x1C;
  Byte buf[kHeaderSize];
  RINOK(ReadStream_FALSE(stream, buf, kHeaderSize));

  UInt32 size = Get16(buf + 4);
  // UInt32 ver = Get16(buf + 6); // == 0
  if (Get32(buf) != 0x78617221 || size != kHeaderSize)
    return S_FALSE;

  UInt64 packSize = Get64(buf + 8);
  UInt64 unpackSize = Get64(buf + 0x10);
  // UInt32 checkSumAlogo = Get32(buf + 0x18);

  if (unpackSize >= kXmlSizeMax)
    return S_FALSE;

  _dataStartPos = archiveStartPos + kHeaderSize + packSize;

  char *ss = _xml.GetBuffer((int)unpackSize + 1);

  NCompress::NZlib::CDecoder *zlibCoderSpec = new NCompress::NZlib::CDecoder();
  CMyComPtr<ICompressCoder> zlibCoder = zlibCoderSpec;

  CLimitedSequentialInStream *inStreamLimSpec = new CLimitedSequentialInStream;
  CMyComPtr<ISequentialInStream> inStreamLim(inStreamLimSpec);
  inStreamLimSpec->SetStream(stream);
  inStreamLimSpec->Init(packSize);

  CSequentialOutStreamImp2 *outStreamLimSpec = new CSequentialOutStreamImp2;
  CMyComPtr<ISequentialOutStream> outStreamLim(outStreamLimSpec);
  outStreamLimSpec->Init((Byte *)ss, (size_t)unpackSize);

  RINOK(zlibCoder->Code(inStreamLim, outStreamLim, NULL, NULL, NULL));

  if (outStreamLimSpec->GetPos() != (size_t)unpackSize)
    return S_FALSE;

  ss[(size_t)unpackSize] = 0;
  _xml.ReleaseBuffer();

  CXml xml;
  if (!xml.Parse(_xml))
    return S_FALSE;
  
  if (!xml.Root.IsTagged("xar") || xml.Root.SubItems.Size() != 1)
    return S_FALSE;
  const CXmlItem &toc = xml.Root.SubItems[0];
  if (!toc.IsTagged("toc"))
    return S_FALSE;
  if (!AddItem(toc, _files, -1))
    return S_FALSE;
  return S_OK;
}

STDMETHODIMP CHandler::Open(IInStream *stream,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -