mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-11 04:07:08 -06:00
4.20
This commit is contained in:
committed by
Kornel Lesiński
parent
8c1b5c7b7e
commit
3c510ba80b
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
#include <initguid.h>
|
||||
|
||||
#include "Common/MyInitGuid.h"
|
||||
#include "Common/ComTry.h"
|
||||
#include "Windows/PropVariant.h"
|
||||
#include "../../ICoder.h"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
// StdAfx.cpp
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "StdAfx.h"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// stdafx.h
|
||||
// StdAfx.h
|
||||
|
||||
#ifndef __STDAFX_H
|
||||
#define __STDAFX_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <time.h>
|
||||
#include "../../../Common/MyWindows.h"
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -126,6 +126,22 @@ SOURCE=.\Tar.ico
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\..\Common\Alloc.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\..\Common\Alloc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\..\Common\IntToString.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\..\Common\IntToString.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\..\Common\NewHandler.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
@@ -42,13 +42,13 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetNumberOfProperties(UINT32 *numProperties)
|
||||
STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties)
|
||||
{
|
||||
*numProperties = sizeof(kProperties) / sizeof(kProperties[0]);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetPropertyInfo(UINT32 index,
|
||||
STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index,
|
||||
BSTR *name, PROPID *propID, VARTYPE *varType)
|
||||
{
|
||||
if(index >= sizeof(kProperties) / sizeof(kProperties[0]))
|
||||
@@ -60,20 +60,20 @@ STDMETHODIMP CHandler::GetPropertyInfo(UINT32 index,
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UINT32 *numProperties)
|
||||
STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties)
|
||||
{
|
||||
*numProperties = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetArchivePropertyInfo(UINT32 index,
|
||||
STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32 index,
|
||||
BSTR *name, PROPID *propID, VARTYPE *varType)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Open(IInStream *stream,
|
||||
const UINT64 *maxCheckStartPosition,
|
||||
const UInt64 *maxCheckStartPosition,
|
||||
IArchiveOpenCallback *openArchiveCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
@@ -90,7 +90,7 @@ STDMETHODIMP CHandler::Open(IInStream *stream,
|
||||
if (openArchiveCallback != NULL)
|
||||
{
|
||||
RINOK(openArchiveCallback->SetTotal(NULL, NULL));
|
||||
UINT64 numFiles = _items.Size();
|
||||
UInt64 numFiles = _items.Size();
|
||||
RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ STDMETHODIMP CHandler::Open(IInStream *stream,
|
||||
archive.SkeepDataRecords(item.Size);
|
||||
if (openArchiveCallback != NULL)
|
||||
{
|
||||
UINT64 numFiles = _items.Size();
|
||||
UInt64 numFiles = _items.Size();
|
||||
RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
|
||||
}
|
||||
}
|
||||
@@ -134,13 +134,13 @@ STDMETHODIMP CHandler::Close()
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetNumberOfItems(UINT32 *numItems)
|
||||
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
|
||||
{
|
||||
*numItems = _items.Size();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *value)
|
||||
STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
NWindows::NCOM::CPropVariant propVariant;
|
||||
@@ -157,13 +157,13 @@ STDMETHODIMP CHandler::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *val
|
||||
break;
|
||||
case kpidSize:
|
||||
case kpidPackedSize:
|
||||
propVariant = (UINT64)item.Size;
|
||||
propVariant = (UInt64)item.Size;
|
||||
break;
|
||||
case kpidLastWriteTime:
|
||||
{
|
||||
FILETIME utcFileTime;
|
||||
if (item.ModificationTime != 0)
|
||||
NTime::UnixTimeToFileTime(item.ModificationTime, utcFileTime);
|
||||
NTime::UnixTimeToFileTime((UInt32)item.ModificationTime, utcFileTime);
|
||||
else
|
||||
{
|
||||
utcFileTime.dwLowDateTime = 0;
|
||||
@@ -186,24 +186,24 @@ STDMETHODIMP CHandler::GetProperty(UINT32 index, PROPID propID, PROPVARIANT *val
|
||||
COM_TRY_END
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::Extract(const UINT32* indices, UINT32 numItems,
|
||||
INT32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
||||
STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
|
||||
Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
bool testMode = (_aTestMode != 0);
|
||||
bool allFilesMode = (numItems == UINT32(-1));
|
||||
bool allFilesMode = (numItems == UInt32(-1));
|
||||
if (allFilesMode)
|
||||
numItems = _items.Size();
|
||||
UINT64 totalSize = 0;
|
||||
UInt64 totalSize = 0;
|
||||
if(numItems == 0)
|
||||
return S_OK;
|
||||
UINT32 i;
|
||||
UInt32 i;
|
||||
for(i = 0; i < numItems; i++)
|
||||
totalSize += _items[allFilesMode ? i : indices[i]].Size;
|
||||
extractCallback->SetTotal(totalSize);
|
||||
|
||||
UINT64 currentTotalSize = 0;
|
||||
UINT64 currentItemSize;
|
||||
UInt64 currentTotalSize = 0;
|
||||
UInt64 currentItemSize;
|
||||
|
||||
CMyComPtr<ICompressCoder> copyCoder;
|
||||
|
||||
@@ -211,10 +211,10 @@ STDMETHODIMP CHandler::Extract(const UINT32* indices, UINT32 numItems,
|
||||
{
|
||||
RINOK(extractCallback->SetCompleted(¤tTotalSize));
|
||||
CMyComPtr<ISequentialOutStream> realOutStream;
|
||||
INT32 askMode;
|
||||
Int32 askMode;
|
||||
askMode = testMode ? NArchive::NExtract::NAskMode::kTest :
|
||||
NArchive::NExtract::NAskMode::kExtract;
|
||||
INT32 index = allFilesMode ? i : indices[i];
|
||||
Int32 index = allFilesMode ? i : indices[i];
|
||||
const CItemEx &item = _items[index];
|
||||
|
||||
RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Tar/Handler.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __TAR_HANDLER_H
|
||||
#define __TAR_HANDLER_H
|
||||
|
||||
@@ -25,28 +23,28 @@ public:
|
||||
)
|
||||
|
||||
STDMETHOD(Open)(IInStream *stream,
|
||||
const UINT64 *maxCheckStartPosition,
|
||||
const UInt64 *maxCheckStartPosition,
|
||||
IArchiveOpenCallback *openArchiveCallback);
|
||||
STDMETHOD(Close)();
|
||||
STDMETHOD(GetNumberOfItems)(UINT32 *numItems);
|
||||
STDMETHOD(GetProperty)(UINT32 index, PROPID propID, PROPVARIANT *value);
|
||||
STDMETHOD(Extract)(const UINT32* indices, UINT32 numItems,
|
||||
INT32 testMode, IArchiveExtractCallback *extractCallback);
|
||||
STDMETHOD(GetNumberOfItems)(UInt32 *numItems);
|
||||
STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
|
||||
STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems,
|
||||
Int32 testMode, IArchiveExtractCallback *extractCallback);
|
||||
|
||||
STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value);
|
||||
|
||||
STDMETHOD(GetNumberOfProperties)(UINT32 *numProperties);
|
||||
STDMETHOD(GetPropertyInfo)(UINT32 index,
|
||||
STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties);
|
||||
STDMETHOD(GetPropertyInfo)(UInt32 index,
|
||||
BSTR *name, PROPID *propID, VARTYPE *varType);
|
||||
|
||||
STDMETHOD(GetNumberOfArchiveProperties)(UINT32 *numProperties);
|
||||
STDMETHOD(GetArchivePropertyInfo)(UINT32 index,
|
||||
STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties);
|
||||
STDMETHOD(GetArchivePropertyInfo)(UInt32 index,
|
||||
BSTR *name, PROPID *propID, VARTYPE *varType);
|
||||
|
||||
// IOutArchive
|
||||
STDMETHOD(UpdateItems)(IOutStream *outStream, UINT32 numItems,
|
||||
STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems,
|
||||
IArchiveUpdateCallback *updateCallback);
|
||||
STDMETHOD(GetFileTimeType)(UINT32 *type);
|
||||
STDMETHOD(GetFileTimeType)(UInt32 *type);
|
||||
|
||||
private:
|
||||
CObjectVector<CItemEx> _items;
|
||||
|
||||
@@ -20,23 +20,23 @@ using namespace NTime;
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
STDMETHODIMP CHandler::GetFileTimeType(UINT32 *type)
|
||||
STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
|
||||
{
|
||||
*type = NFileTimeType::kUnix;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CHandler::UpdateItems(IOutStream *outStream, UINT32 numItems,
|
||||
STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
|
||||
IArchiveUpdateCallback *updateCallback)
|
||||
{
|
||||
COM_TRY_BEGIN
|
||||
CObjectVector<CUpdateItemInfo> updateItems;
|
||||
for(UINT32 i = 0; i < numItems; i++)
|
||||
for(UInt32 i = 0; i < numItems; i++)
|
||||
{
|
||||
CUpdateItemInfo updateItem;
|
||||
INT32 newData;
|
||||
INT32 newProperties;
|
||||
UINT32 indexInArchive;
|
||||
Int32 newData;
|
||||
Int32 newProperties;
|
||||
UInt32 indexInArchive;
|
||||
if (!updateCallback)
|
||||
return E_FAIL;
|
||||
RINOK(updateCallback->GetUpdateItemInfo(i,
|
||||
@@ -51,7 +51,7 @@ STDMETHODIMP CHandler::UpdateItems(IOutStream *outStream, UINT32 numItems,
|
||||
FILETIME utcTime;
|
||||
UString name;
|
||||
bool isDirectoryStatusDefined;
|
||||
UINT32 attributes;
|
||||
UInt32 attributes;
|
||||
{
|
||||
NCOM::CPropVariant propVariant;
|
||||
RINOK(updateCallback->GetProperty(i, kpidAttributes, &propVariant));
|
||||
@@ -98,18 +98,19 @@ STDMETHODIMP CHandler::UpdateItems(IOutStream *outStream, UINT32 numItems,
|
||||
updateItem.IsDirectory = ((attributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
||||
if (updateItem.IsDirectory)
|
||||
updateItem.Name += '/';
|
||||
|
||||
if(!FileTimeToUnixTime(utcTime, updateItem.Time))
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
if (IntToBool(newData))
|
||||
{
|
||||
UINT64 size;
|
||||
UInt64 size;
|
||||
{
|
||||
NCOM::CPropVariant propVariant;
|
||||
RINOK(updateCallback->GetProperty(i, kpidSize, &propVariant));
|
||||
if (propVariant.vt != VT_UI8)
|
||||
return E_INVALIDARG;
|
||||
size = *(UINT64 *)(&propVariant.uhVal);
|
||||
size = propVariant.uhVal.QuadPart;
|
||||
}
|
||||
updateItem.Size = size;
|
||||
}
|
||||
|
||||
@@ -22,4 +22,3 @@ namespace NFileHeader {
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Archive/Tar/Header.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __ARCHIVE_TAR_HEADER_H
|
||||
#define __ARCHIVE_TAR_HEADER_H
|
||||
|
||||
@@ -10,9 +8,6 @@
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
#pragma pack( push, PragmaTarHeaders)
|
||||
#pragma pack( push, 1)
|
||||
|
||||
namespace NFileHeader
|
||||
{
|
||||
const int kRecordSize = 512;
|
||||
@@ -20,6 +15,7 @@ namespace NFileHeader
|
||||
const int kUserNameSize = 32;
|
||||
const int kGroupNameSize = 32;
|
||||
|
||||
/*
|
||||
struct CHeader
|
||||
{
|
||||
char Name[kNameSize];
|
||||
@@ -36,17 +32,13 @@ namespace NFileHeader
|
||||
char GroupName[kGroupNameSize];
|
||||
char DeviceMajor[8];
|
||||
char DeviceMinor[8];
|
||||
/*
|
||||
BYTE Padding[kRecordSize - (kNameSize + 8 + 8 + 12 + 12 + 8 + 1 + kNameSize + 8 +
|
||||
kUserNameSize + kGroupNameSize + 8 + 8)];
|
||||
*/
|
||||
};
|
||||
union CRecord
|
||||
{
|
||||
CHeader Header;
|
||||
BYTE Padding[kRecordSize];
|
||||
Byte Padding[kRecordSize];
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
namespace NMode
|
||||
{
|
||||
@@ -99,9 +91,6 @@ namespace NFileHeader
|
||||
|
||||
}
|
||||
|
||||
#pragma pack(pop)
|
||||
#pragma pack(pop, PragmaTarHeaders)
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
HRESULT CInArchive::ReadBytes(void *data, UINT32 size, UINT32 &processedSize)
|
||||
HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 &processedSize)
|
||||
{
|
||||
RINOK(m_Stream->Read(data, size, &processedSize));
|
||||
m_Position += processedSize;
|
||||
@@ -24,7 +24,7 @@ HRESULT CInArchive::Open(IInStream *inStream)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static UINT32 OctalToNumber(const char *srcString)
|
||||
static UInt32 OctalToNumber(const char *srcString)
|
||||
{
|
||||
char *endPtr;
|
||||
return(strtoul(srcString, &endPtr, 8));
|
||||
@@ -47,86 +47,131 @@ static bool CheckOctalString(const char *srcString, int numChars)
|
||||
|
||||
#define ReturnIfBadOctal(x, y) { if (!CheckOctalString((x), (y))) return S_FALSE; }
|
||||
|
||||
static bool IsRecordLast(const NFileHeader::CRecord &record)
|
||||
static bool IsRecordLast(const char *record)
|
||||
{
|
||||
for (int i = 0; i < sizeof(record); i++)
|
||||
if (record.Padding[i] != 0)
|
||||
for (int i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
if (record[i] != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ReadString(const char *s, int size, AString &result)
|
||||
{
|
||||
if (size > NFileHeader::kNameSize)
|
||||
size = NFileHeader::kNameSize;
|
||||
char tempString[NFileHeader::kNameSize + 1];
|
||||
strncpy(tempString, s, size);
|
||||
tempString[size] = '\0';
|
||||
result = tempString;
|
||||
}
|
||||
|
||||
static char GetHex(Byte value)
|
||||
{
|
||||
return (value < 10) ? ('0' + value) : ('A' + (value - 10));
|
||||
}
|
||||
|
||||
HRESULT CInArchive::GetNextItemReal(bool &filled, CItemEx &item)
|
||||
{
|
||||
item.LongLinkSize = 0;
|
||||
NFileHeader::CRecord record;
|
||||
// NFileHeader::CRecord record;
|
||||
char record[NFileHeader::kRecordSize];
|
||||
char *cur = record;
|
||||
|
||||
filled = false;
|
||||
|
||||
UINT32 processedSize;
|
||||
UInt32 processedSize;
|
||||
item.HeaderPosition = m_Position;
|
||||
RINOK(ReadBytes(&record, sizeof(record), processedSize));
|
||||
RINOK(ReadBytes(record, NFileHeader::kRecordSize, processedSize));
|
||||
if (processedSize == 0 ||
|
||||
(processedSize == sizeof(record) && IsRecordLast(record)))
|
||||
(processedSize == NFileHeader::kRecordSize && IsRecordLast(record)))
|
||||
return S_OK;
|
||||
if (processedSize < sizeof(record))
|
||||
if (processedSize < NFileHeader::kRecordSize)
|
||||
return S_FALSE;
|
||||
|
||||
NFileHeader::CHeader &header = record.Header;
|
||||
// NFileHeader::CHeader &header = record.Header;
|
||||
|
||||
char tempString[NFileHeader::kNameSize + 1];
|
||||
strncpy(tempString, header.Name, NFileHeader::kNameSize);
|
||||
tempString[NFileHeader::kNameSize] = '\0';
|
||||
item.Name = tempString;
|
||||
AString name;
|
||||
ReadString(cur, NFileHeader::kNameSize, name);
|
||||
cur += NFileHeader::kNameSize;
|
||||
|
||||
item.Name.Empty();
|
||||
int i;
|
||||
for (i = 0; i < item.Name.Length(); i++)
|
||||
if (((BYTE)item.Name[i]) < 0x20)
|
||||
for (i = 0; i < name.Length(); i++)
|
||||
{
|
||||
char c = name[i];
|
||||
if (((Byte)c) < 0x08)
|
||||
{
|
||||
return S_FALSE;
|
||||
item.LinkFlag = header.LinkFlag;
|
||||
}
|
||||
if (((Byte)c) < 0x20)
|
||||
{
|
||||
item.Name += '[';
|
||||
item.Name += GetHex(((Byte)c) >> 4);
|
||||
item.Name += GetHex(((Byte)c) & 0xF);
|
||||
item.Name += ']';
|
||||
}
|
||||
else
|
||||
item.Name += c;
|
||||
}
|
||||
|
||||
BYTE linkFlag = item.LinkFlag;
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
item.Mode = OctalToNumber(cur);
|
||||
cur += 8;
|
||||
|
||||
ReturnIfBadOctal(header.Mode, 8);
|
||||
ReturnIfBadOctal(header.UID, 8);
|
||||
ReturnIfBadOctal(header.GID, 8);
|
||||
ReturnIfBadOctal(header.Size, 12);
|
||||
ReturnIfBadOctal(header.ModificationTime, 12);
|
||||
ReturnIfBadOctal(header.CheckSum, 8);
|
||||
ReturnIfBadOctal(header.DeviceMajor, 8);
|
||||
ReturnIfBadOctal(header.DeviceMinor, 8);
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
item.UID = OctalToNumber(cur);
|
||||
cur += 8;
|
||||
|
||||
item.Mode = OctalToNumber(header.Mode);
|
||||
item.UID = OctalToNumber(header.UID);
|
||||
item.GID = OctalToNumber(header.GID);
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
item.GID = OctalToNumber(cur);
|
||||
cur += 8;
|
||||
|
||||
ReturnIfBadOctal(cur, 12);
|
||||
item.Size = OctalToNumber(cur);
|
||||
cur += 12;
|
||||
|
||||
ReturnIfBadOctal(cur, 12);
|
||||
item.ModificationTime = OctalToNumber(cur);
|
||||
cur += 12;
|
||||
|
||||
item.Size = OctalToNumber(header.Size);
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
UInt32 checkSum = OctalToNumber(cur);
|
||||
memmove(cur, NFileHeader::kCheckSumBlanks, 8);
|
||||
cur += 8;
|
||||
|
||||
item.LinkFlag = *cur++;
|
||||
Byte linkFlag = item.LinkFlag;
|
||||
|
||||
ReadString(cur, NFileHeader::kNameSize, item.LinkName);
|
||||
cur += NFileHeader::kNameSize;
|
||||
|
||||
memmove(item.Magic, cur, 8);
|
||||
cur += 8;
|
||||
|
||||
ReadString(cur, NFileHeader::kUserNameSize, item.UserName);
|
||||
cur += NFileHeader::kUserNameSize;
|
||||
ReadString(cur, NFileHeader::kUserNameSize, item.GroupName);
|
||||
cur += NFileHeader::kUserNameSize;
|
||||
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
item.DeviceMajorDefined = (cur[0] != 0);
|
||||
if (item.DeviceMajorDefined)
|
||||
item.DeviceMajor = OctalToNumber(cur);
|
||||
|
||||
ReturnIfBadOctal(cur, 8);
|
||||
item.DeviceMinorDefined = (cur[0] != 0);
|
||||
if (item.DeviceMinorDefined)
|
||||
item.DeviceMinor = OctalToNumber(cur);
|
||||
cur += 8;
|
||||
|
||||
|
||||
if (item.LinkFlag == NFileHeader::NLinkFlag::kLink)
|
||||
item.Size = 0;
|
||||
|
||||
item.ModificationTime = OctalToNumber(header.ModificationTime);
|
||||
|
||||
|
||||
|
||||
item.LinkName = header.LinkName;
|
||||
memmove(item.Magic, header.Magic, 8);
|
||||
|
||||
item.UserName = header.UserName;
|
||||
item.GroupName = header.GroupName;
|
||||
|
||||
|
||||
item.DeviceMajorDefined = (header.DeviceMajor[0] != 0);
|
||||
if (item.DeviceMajorDefined)
|
||||
item.DeviceMajor = OctalToNumber(header.DeviceMajor);
|
||||
|
||||
item.DeviceMinorDefined = (header.DeviceMinor[0] != 0);
|
||||
if (item.DeviceMinorDefined)
|
||||
item.DeviceMinor = OctalToNumber(header.DeviceMinor);
|
||||
|
||||
UINT32 checkSum = OctalToNumber(header.CheckSum);
|
||||
|
||||
memmove(header.CheckSum, NFileHeader::kCheckSumBlanks, 8);
|
||||
|
||||
UINT32 checkSumReal = 0;
|
||||
UInt32 checkSumReal = 0;
|
||||
for(i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
checkSumReal += BYTE(record.Padding[i]);
|
||||
checkSumReal += Byte(record[i]);
|
||||
|
||||
if (checkSumReal != checkSum)
|
||||
return S_FALSE;
|
||||
@@ -145,12 +190,12 @@ HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item)
|
||||
{
|
||||
if (item.Name.Compare(NFileHeader::kLongLink) != 0)
|
||||
return S_FALSE;
|
||||
UINT64 headerPosition = item.HeaderPosition;
|
||||
UInt64 headerPosition = item.HeaderPosition;
|
||||
|
||||
UINT32 processedSize;
|
||||
UInt32 processedSize;
|
||||
AString fullName;
|
||||
char *buffer = fullName.GetBuffer((UINT32)item.Size + 1);
|
||||
RINOK(ReadBytes(buffer, (UINT32)item.Size, processedSize));
|
||||
char *buffer = fullName.GetBuffer((UInt32)item.Size + 1);
|
||||
RINOK(ReadBytes(buffer, (UInt32)item.Size, processedSize));
|
||||
buffer[item.Size] = '\0';
|
||||
fullName.ReleaseBuffer();
|
||||
if (processedSize != item.Size)
|
||||
@@ -166,9 +211,9 @@ HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CInArchive::Skeep(UINT64 numBytes)
|
||||
HRESULT CInArchive::Skeep(UInt64 numBytes)
|
||||
{
|
||||
UINT64 newPostion;
|
||||
UInt64 newPostion;
|
||||
RINOK(m_Stream->Seek(numBytes, STREAM_SEEK_CUR, &newPostion));
|
||||
m_Position += numBytes;
|
||||
if (m_Position != newPostion)
|
||||
@@ -177,15 +222,9 @@ HRESULT CInArchive::Skeep(UINT64 numBytes)
|
||||
}
|
||||
|
||||
|
||||
HRESULT CInArchive::SkeepDataRecords(UINT64 dataSize)
|
||||
HRESULT CInArchive::SkeepDataRecords(UInt64 dataSize)
|
||||
{
|
||||
return Skeep((dataSize + 511) &
|
||||
#if ( __GNUC__)
|
||||
0xFFFFFFFFFFFFFE00LL
|
||||
#else
|
||||
0xFFFFFFFFFFFFFE00
|
||||
#endif
|
||||
);
|
||||
return Skeep((dataSize + 0x1FF) & (~((UInt64)0x1FF)));
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Archive/TarIn.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __ARCHIVE_TAR_IN_H
|
||||
#define __ARCHIVE_TAR_IN_H
|
||||
|
||||
@@ -16,15 +14,15 @@ namespace NTar {
|
||||
class CInArchive
|
||||
{
|
||||
CMyComPtr<IInStream> m_Stream;
|
||||
UINT64 m_Position;
|
||||
UInt64 m_Position;
|
||||
|
||||
HRESULT ReadBytes(void *data, UINT32 size, UINT32 &processedSize);
|
||||
HRESULT ReadBytes(void *data, UInt32 size, UInt32 &processedSize);
|
||||
public:
|
||||
HRESULT Open(IInStream *inStream);
|
||||
HRESULT GetNextItemReal(bool &filled, CItemEx &itemInfo);
|
||||
HRESULT GetNextItem(bool &filled, CItemEx &itemInfo);
|
||||
HRESULT Skeep(UINT64 numBytes);
|
||||
HRESULT SkeepDataRecords(UINT64 dataSize);
|
||||
HRESULT Skeep(UInt64 numBytes);
|
||||
HRESULT SkeepDataRecords(UInt64 dataSize);
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Archive/Tar/Item.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __ARCHIVE_TAR_ITEM_H
|
||||
#define __ARCHIVE_TAR_ITEM_H
|
||||
|
||||
@@ -9,6 +7,8 @@
|
||||
|
||||
#include "Common/Types.h"
|
||||
#include "Common/String.h"
|
||||
|
||||
#include "../Common/ItemNameUtils.h"
|
||||
#include "TarHeader.h"
|
||||
|
||||
namespace NArchive {
|
||||
@@ -18,11 +18,11 @@ class CItem
|
||||
{
|
||||
public:
|
||||
AString Name;
|
||||
UINT32 Mode;
|
||||
UINT32 UID;
|
||||
UINT32 GID;
|
||||
UINT64 Size;
|
||||
time_t ModificationTime;
|
||||
UInt32 Mode;
|
||||
UInt32 UID;
|
||||
UInt32 GID;
|
||||
UInt64 Size;
|
||||
UInt32 ModificationTime;
|
||||
char LinkFlag;
|
||||
AString LinkName;
|
||||
char Magic[8];
|
||||
@@ -30,9 +30,9 @@ public:
|
||||
AString GroupName;
|
||||
|
||||
bool DeviceMajorDefined;
|
||||
UINT32 DeviceMajor;
|
||||
UInt32 DeviceMajor;
|
||||
bool DeviceMinorDefined;
|
||||
UINT32 DeviceMinor;
|
||||
UInt32 DeviceMinor;
|
||||
|
||||
bool IsDirectory() const
|
||||
{
|
||||
@@ -41,13 +41,7 @@ public:
|
||||
if (LinkFlag == NFileHeader::NLinkFlag::kOldNormal ||
|
||||
LinkFlag == NFileHeader::NLinkFlag::kNormal)
|
||||
{
|
||||
if (Name.IsEmpty())
|
||||
return false;
|
||||
#ifdef WIN32
|
||||
return (*CharPrevExA(CP_OEMCP, Name, &Name[Name.Length()], 0) == '/');
|
||||
#else
|
||||
return (Name[Name.Length() - 1) == '/');
|
||||
#endif
|
||||
return NItemName::HasTailSlash(Name, CP_OEMCP);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -56,10 +50,10 @@ public:
|
||||
class CItemEx: public CItem
|
||||
{
|
||||
public:
|
||||
UINT64 HeaderPosition;
|
||||
UINT64 LongLinkSize;
|
||||
UINT64 GetDataPosition() const { return HeaderPosition + LongLinkSize + NFileHeader::kRecordSize; };
|
||||
UINT64 GetFullSize() const { return LongLinkSize + NFileHeader::kRecordSize + Size; };
|
||||
UInt64 HeaderPosition;
|
||||
UInt64 LongLinkSize;
|
||||
UInt64 GetDataPosition() const { return HeaderPosition + LongLinkSize + NFileHeader::kRecordSize; };
|
||||
UInt64 GetFullSize() const { return LongLinkSize + NFileHeader::kRecordSize + Size; };
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
#include "TarOut.h"
|
||||
#include "TarHeader.h"
|
||||
|
||||
#include "Common/IntToString.h"
|
||||
#include "Windows/Defs.h"
|
||||
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
HRESULT COutArchive::WriteBytes(const void *buffer, UINT32 size)
|
||||
HRESULT COutArchive::WriteBytes(const void *buffer, UInt32 size)
|
||||
{
|
||||
UINT32 processedSize;
|
||||
UInt32 processedSize;
|
||||
RINOK(m_Stream->Write(buffer, size, &processedSize));
|
||||
if(processedSize != size)
|
||||
return E_FAIL;
|
||||
@@ -24,14 +25,14 @@ void COutArchive::Create(ISequentialOutStream *outStream)
|
||||
m_Stream = outStream;
|
||||
}
|
||||
|
||||
static AString MakeOctalString(UINT64 value)
|
||||
static AString MakeOctalString(UInt64 value)
|
||||
{
|
||||
char s[32];
|
||||
_ui64toa(value, s, 8);
|
||||
ConvertUInt64ToString(value, s, 8);
|
||||
return AString(s) + ' ';
|
||||
}
|
||||
|
||||
static bool MakeOctalString8(char *s, UINT32 value)
|
||||
static bool MakeOctalString8(char *s, UInt32 value)
|
||||
{
|
||||
AString tempString = MakeOctalString(value);
|
||||
|
||||
@@ -45,7 +46,7 @@ static bool MakeOctalString8(char *s, UINT32 value)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MakeOctalString12(char *s, UINT64 value)
|
||||
static bool MakeOctalString12(char *s, UInt64 value)
|
||||
{
|
||||
AString tempString = MakeOctalString(value);
|
||||
const int kMaxSize = 12;
|
||||
@@ -70,45 +71,63 @@ static bool CopyString(char *dest, const AString &src, int maxSize)
|
||||
|
||||
HRESULT COutArchive::WriteHeaderReal(const CItem &item)
|
||||
{
|
||||
NFileHeader::CRecord record;
|
||||
char record[NFileHeader::kRecordSize];
|
||||
char *cur = record;
|
||||
int i;
|
||||
for (i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
record.Padding[i] = 0;
|
||||
|
||||
NFileHeader::CHeader &header = record.Header;
|
||||
record[i] = 0;
|
||||
|
||||
// RETURN_IF_NOT_TRUE(CopyString(header.Name, item.Name, NFileHeader::kNameSize));
|
||||
if (item.Name.Length() > NFileHeader::kNameSize)
|
||||
return E_FAIL;
|
||||
strncpy(header.Name, item.Name, NFileHeader::kNameSize);
|
||||
strncpy(cur, item.Name, NFileHeader::kNameSize);
|
||||
cur += NFileHeader::kNameSize;
|
||||
|
||||
RETURN_IF_NOT_TRUE(CopyString(header.LinkName, item.LinkName, NFileHeader::kNameSize));
|
||||
RETURN_IF_NOT_TRUE(CopyString(header.UserName, item.UserName, NFileHeader::kUserNameSize));
|
||||
RETURN_IF_NOT_TRUE(CopyString(header.GroupName, item.GroupName, NFileHeader::kGroupNameSize));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.Mode));
|
||||
cur += 8;
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.UID));
|
||||
cur += 8;
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.GID));
|
||||
cur += 8;
|
||||
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.Mode, item.Mode));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.UID, item.UID));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.GID, item.GID));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString12(cur, item.Size));
|
||||
cur += 12;
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString12(cur, item.ModificationTime));
|
||||
cur += 12;
|
||||
|
||||
memmove(cur, NFileHeader::kCheckSumBlanks, 8);
|
||||
cur += 8;
|
||||
|
||||
*cur++ = item.LinkFlag;
|
||||
|
||||
RETURN_IF_NOT_TRUE(CopyString(cur, item.LinkName, NFileHeader::kNameSize));
|
||||
cur += NFileHeader::kNameSize;
|
||||
|
||||
memmove(cur, item.Magic, 8);
|
||||
cur += 8;
|
||||
|
||||
RETURN_IF_NOT_TRUE(CopyString(cur, item.UserName, NFileHeader::kUserNameSize));
|
||||
cur += NFileHeader::kUserNameSize;
|
||||
RETURN_IF_NOT_TRUE(CopyString(cur, item.GroupName, NFileHeader::kGroupNameSize));
|
||||
cur += NFileHeader::kUserNameSize;
|
||||
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString12(header.Size, item.Size));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString12(header.ModificationTime, item.ModificationTime));
|
||||
header.LinkFlag = item.LinkFlag;
|
||||
memmove(header.Magic, item.Magic, 8);
|
||||
|
||||
if (item.DeviceMajorDefined)
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.DeviceMajor, item.DeviceMajor));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.DeviceMajor));
|
||||
cur += 8;
|
||||
|
||||
if (item.DeviceMinorDefined)
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.DeviceMinor, item.DeviceMinor));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(cur, item.DeviceMinor));
|
||||
cur += 8;
|
||||
|
||||
memmove(header.CheckSum, NFileHeader::kCheckSumBlanks, 8);
|
||||
|
||||
UINT32 checkSumReal = 0;
|
||||
UInt32 checkSumReal = 0;
|
||||
for(i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
checkSumReal += BYTE(record.Padding[i]);
|
||||
checkSumReal += Byte(record[i]);
|
||||
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(header.CheckSum, checkSumReal));
|
||||
RETURN_IF_NOT_TRUE(MakeOctalString8(record + 148, checkSumReal));
|
||||
|
||||
return WriteBytes(&record, sizeof(record));
|
||||
return WriteBytes(record, NFileHeader::kRecordSize);
|
||||
}
|
||||
|
||||
HRESULT COutArchive::WriteHeader(const CItem &item)
|
||||
@@ -132,24 +151,24 @@ HRESULT COutArchive::WriteHeader(const CItem &item)
|
||||
return WriteHeaderReal(modifiedItem);
|
||||
}
|
||||
|
||||
HRESULT COutArchive::FillDataResidual(UINT64 dataSize)
|
||||
HRESULT COutArchive::FillDataResidual(UInt64 dataSize)
|
||||
{
|
||||
UINT32 lastRecordSize = UINT32(dataSize & (NFileHeader::kRecordSize - 1));
|
||||
UInt32 lastRecordSize = UInt32(dataSize & (NFileHeader::kRecordSize - 1));
|
||||
if (lastRecordSize == 0)
|
||||
return S_OK;
|
||||
UINT32 residualSize = NFileHeader::kRecordSize - lastRecordSize;
|
||||
BYTE residualBytes[NFileHeader::kRecordSize];
|
||||
for (UINT32 i = 0; i < residualSize; i++)
|
||||
UInt32 residualSize = NFileHeader::kRecordSize - lastRecordSize;
|
||||
Byte residualBytes[NFileHeader::kRecordSize];
|
||||
for (UInt32 i = 0; i < residualSize; i++)
|
||||
residualBytes[i] = 0;
|
||||
return WriteBytes(residualBytes, residualSize);
|
||||
}
|
||||
|
||||
HRESULT COutArchive::WriteFinishHeader()
|
||||
{
|
||||
NFileHeader::CRecord record;
|
||||
char record[NFileHeader::kRecordSize];
|
||||
for (int i = 0; i < NFileHeader::kRecordSize; i++)
|
||||
record.Padding[i] = 0;
|
||||
return WriteBytes(&record, sizeof(record));
|
||||
record[i] = 0;
|
||||
return WriteBytes(record, NFileHeader::kRecordSize);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Archive/TarOut.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __ARCHIVE_TAR_OUT_H
|
||||
#define __ARCHIVE_TAR_OUT_H
|
||||
|
||||
@@ -16,12 +14,12 @@ namespace NTar {
|
||||
class COutArchive
|
||||
{
|
||||
CMyComPtr<ISequentialOutStream> m_Stream;
|
||||
HRESULT WriteBytes(const void *buffer, UINT32 size);
|
||||
HRESULT WriteBytes(const void *buffer, UInt32 size);
|
||||
public:
|
||||
void Create(ISequentialOutStream *outStream);
|
||||
HRESULT WriteHeaderReal(const CItem &item);
|
||||
HRESULT WriteHeader(const CItem &item);
|
||||
HRESULT FillDataResidual(UINT64 dataSize);
|
||||
HRESULT FillDataResidual(UInt64 dataSize);
|
||||
HRESULT WriteFinishHeader();
|
||||
};
|
||||
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
#include "TarOut.h"
|
||||
#include "TarUpdate.h"
|
||||
|
||||
static const UINT64 kOneItemComplexity = 512;
|
||||
static const UInt64 kOneItemComplexity = 512;
|
||||
|
||||
namespace NArchive {
|
||||
namespace NTar {
|
||||
|
||||
static HRESULT CopyBlock(ISequentialInStream *inStream,
|
||||
ISequentialOutStream *outStream, ICompressProgressInfo *progress,
|
||||
UINT64 *totalSize = NULL)
|
||||
UInt64 *totalSize = NULL)
|
||||
{
|
||||
NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
|
||||
CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
|
||||
@@ -38,7 +38,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
COutArchive outArchive;
|
||||
outArchive.Create(outStream);
|
||||
|
||||
UINT64 complexity = 0;
|
||||
UInt64 complexity = 0;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < updateItems.Size(); i++)
|
||||
@@ -99,28 +99,31 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
if (updateItem.NewData)
|
||||
{
|
||||
item.Size = updateItem.Size;
|
||||
if (item.Size == UInt64(Int64(-1)))
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
else
|
||||
{
|
||||
const CItemEx &existItemInfo = inputItems[updateItem.IndexInArchive];
|
||||
item.Size = existItemInfo.Size;
|
||||
}
|
||||
if (updateItem.NewData || updateItem.NewProperties)
|
||||
{
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
}
|
||||
|
||||
|
||||
if (updateItem.NewData)
|
||||
{
|
||||
CMyComPtr<IInStream> fileInStream;
|
||||
RINOK(updateCallback->GetStream(updateItem.IndexInClient, &fileInStream));
|
||||
if (!updateItem.IsDirectory)
|
||||
CMyComPtr<ISequentialInStream> fileInStream;
|
||||
HRESULT res = updateCallback->GetStream(updateItem.IndexInClient, &fileInStream);
|
||||
if (res != S_FALSE)
|
||||
{
|
||||
UINT64 totalSize;
|
||||
RINOK(CopyBlock(fileInStream, outStream, compressProgress, &totalSize));
|
||||
if (totalSize != item.Size)
|
||||
return E_FAIL;
|
||||
RINOK(outArchive.FillDataResidual(item.Size));
|
||||
RINOK(res);
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
if (!updateItem.IsDirectory)
|
||||
{
|
||||
UInt64 totalSize;
|
||||
RINOK(CopyBlock(fileInStream, outStream, compressProgress, &totalSize));
|
||||
if (totalSize != item.Size)
|
||||
return E_FAIL;
|
||||
RINOK(outArchive.FillDataResidual(item.Size));
|
||||
}
|
||||
}
|
||||
complexity += updateItem.Size;
|
||||
RINOK(updateCallback->SetOperationResult(
|
||||
@@ -133,6 +136,7 @@ HRESULT UpdateArchive(IInStream *inStream, ISequentialOutStream *outStream,
|
||||
const CItemEx &existItemInfo = inputItems[updateItem.IndexInArchive];
|
||||
if (updateItem.NewProperties)
|
||||
{
|
||||
RINOK(outArchive.WriteHeader(item));
|
||||
RINOK(inStream->Seek(existItemInfo.GetDataPosition(),
|
||||
STREAM_SEEK_SET, NULL));
|
||||
streamSpec->Init(inStream, existItemInfo.Size);
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Tar/Update.h
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef __TAR_UPDATE_H
|
||||
#define __TAR_UPDATE_H
|
||||
|
||||
@@ -22,8 +20,8 @@ struct CUpdateItemInfo
|
||||
int IndexInArchive;
|
||||
int IndexInClient;
|
||||
|
||||
time_t Time;
|
||||
UINT64 Size;
|
||||
UInt32 Time;
|
||||
UInt64 Size;
|
||||
AString Name;
|
||||
bool IsDirectory;
|
||||
};
|
||||
|
||||
@@ -75,8 +75,8 @@ IDI_ICON1 ICON DISCARDABLE "Tar.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,9,2,0
|
||||
PRODUCTVERSION 3,9,2,0
|
||||
FILEVERSION 4,13,0,0
|
||||
PRODUCTVERSION 4,13,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -94,14 +94,14 @@ BEGIN
|
||||
VALUE "Comments", "\0"
|
||||
VALUE "CompanyName", "Igor Pavlov\0"
|
||||
VALUE "FileDescription", "Tar Plugin for 7-Zip\0"
|
||||
VALUE "FileVersion", "3, 9, 2, 0\0"
|
||||
VALUE "FileVersion", "4, 13, 0, 0\0"
|
||||
VALUE "InternalName", "tar\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 1999-2003 Igor Pavlov\0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 1999-2004 Igor Pavlov\0"
|
||||
VALUE "LegalTrademarks", "\0"
|
||||
VALUE "OriginalFilename", "tar.dll\0"
|
||||
VALUE "PrivateBuild", "\0"
|
||||
VALUE "ProductName", "7-Zip\0"
|
||||
VALUE "ProductVersion", "3, 9, 2, 0\0"
|
||||
VALUE "ProductVersion", "4, 13, 0, 0\0"
|
||||
VALUE "SpecialBuild", "\0"
|
||||
END
|
||||
END
|
||||
|
||||
Reference in New Issue
Block a user