📄 chmin.cpp
字号:
if (ReadUInt32() != NHeader::kItsfSignature)
return S_FALSE;
if (ReadUInt32() != 4) // $4 (Version number -- CHM uses 3)
return S_FALSE;
if (ReadUInt32() != 0x20) // $20 (length of ITSF)
return S_FALSE;
UInt32 unknown = ReadUInt32();
if (unknown != 0 && unknown != 1) // = 0 for some HxW files, 1 in other cases;
return S_FALSE;
database.ContentOffset = _startPosition + ReadUInt64();
/* UInt32 timeStamp = */ ReadUInt32();
// A timestamp of some sort.
// Considered as a big-endian DWORD, it appears to contain
// seconds (MSB) and fractional seconds (second byte).
// The third and fourth bytes may contain even more fractional
// bits. The 4 least significant bits in the last byte are constant.
/* UInt32 lang = */ ReadUInt32(); // BE?
}
else
return S_FALSE;
}
/*
// Section 0
ReadChunk(inStream, _startPosition + sectionOffsets[0], sectionSizes[0]);
if (sectionSizes[0] != 0x18)
return S_FALSE;
ReadUInt32(); // unknown: 01FE
ReadUInt32(); // unknown: 0
UInt64 fileSize = ReadUInt64();
ReadUInt32(); // unknown: 0
ReadUInt32(); // unknown: 0
*/
// Section 1: The Directory Listing
ReadChunk(inStream, _startPosition + sectionOffsets[1], sectionSizes[1]);
if (ReadUInt32() != NHeader::kIfcmSignature)
return S_FALSE;
if (ReadUInt32() != 1) // (probably a version number)
return S_FALSE;
UInt32 dirChunkSize = ReadUInt32(); // $2000
if (dirChunkSize < 64)
return S_FALSE;
ReadUInt32(); // $100000 (unknown)
ReadUInt32(); // -1 (unknown)
ReadUInt32(); // -1 (unknown)
UInt32 numDirChunks = ReadUInt32();
ReadUInt32(); // 0 (unknown, probably high word of above)
for (UInt32 ci = 0; ci < numDirChunks; ci++)
{
UInt64 chunkPos = _inBuffer.GetProcessedSize();
if (ReadUInt32() == NHeader::kAollSignature)
{
UInt32 quickrefLength = ReadUInt32(); // Length of quickref area at end of directory chunk
if (quickrefLength > dirChunkSize || quickrefLength < 2)
return S_FALSE;
ReadUInt64(); // Directory chunk number
// This must match physical position in file, that is
// the chunk size times the chunk number must be the
// offset from the end of the directory header.
ReadUInt64(); // Chunk number of previous listing chunk when reading
// directory in sequence (-1 if first listing chunk)
ReadUInt64(); // Chunk number of next listing chunk when reading
// directory in sequence (-1 if last listing chunk)
ReadUInt64(); // Number of first listing entry in this chunk
ReadUInt32(); // 1 (unknown -- other values have also been seen here)
ReadUInt32(); // 0 (unknown)
int numItems = 0;
for (;;)
{
UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos;
UInt32 offsetLimit = dirChunkSize - quickrefLength;
if (offset > offsetLimit)
return S_FALSE;
if (offset == offsetLimit)
break;
if (database.NewFormat)
{
UInt16 nameLength = ReadUInt16();
if (nameLength == 0)
return S_FALSE;
UString name;
ReadUString((int)nameLength, name);
AString s;
ConvertUnicodeToUTF8(name, s);
Byte b = ReadByte();
s += ' ';
PrintByte(b, s);
s += ' ';
UInt64 len = ReadEncInt();
// then number of items ?
// then length ?
// then some data (binary encoding?)
while (len-- != 0)
{
b = ReadByte();
PrintByte(b, s);
}
database.NewFormatString += s;
database.NewFormatString += "\r\n";
}
else
{
RINOK(ReadDirEntry(database));
}
numItems++;
}
Skeep(quickrefLength - 2);
if (ReadUInt16() != numItems)
return S_FALSE;
if (numItems > numDirEntries)
return S_FALSE;
numDirEntries -= numItems;
}
else
Skeep(dirChunkSize - 4);
}
return numDirEntries == 0 ? S_OK : S_FALSE;
}
HRESULT CInArchive::DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name)
{
int index = database.FindItem(name);
if (index < 0)
return S_FALSE;
const CItem &item = database.Items[index];
_chunkSize = item.Size;
return ReadChunk(inStream, database.ContentOffset + item.Offset, item.Size);
}
#define DATA_SPACE "::DataSpace/"
static const char *kNameList = DATA_SPACE "NameList";
static const char *kStorage = DATA_SPACE "Storage/";
static const char *kContent = "Content";
static const char *kControlData = "ControlData";
static const char *kSpanInfo = "SpanInfo";
static const char *kTransform = "Transform/";
static const char *kResetTable = "/InstanceData/ResetTable";
static const char *kTransformList = "List";
static AString GetSectionPrefix(const AString &name)
{
return AString(kStorage) + name + AString("/");
}
#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
static int CompareFiles(const int *p1, const int *p2, void *param)
{
const CObjectVector<CItem> &items = *(const CObjectVector<CItem> *)param;
const CItem &item1 = items[*p1];
const CItem &item2 = items[*p2];
bool isDir1 = item1.IsDir();
bool isDir2 = item2.IsDir();
if (isDir1 && !isDir2)
return -1;
if (isDir2)
{
if (isDir1)
return MyCompare(*p1, *p2);
return 1;
}
RINOZ(MyCompare(item1.Section, item2.Section));
RINOZ(MyCompare(item1.Offset, item2.Offset));
RINOZ(MyCompare(item1.Size, item2.Size));
return MyCompare(*p1, *p2);
}
void CFilesDatabase::SetIndices()
{
for (int i = 0; i < Items.Size(); i++)
{
const CItem &item = Items[i];
if (item.IsUserItem() && item.Name.Length() != 1)
Indices.Add(i);
}
}
void CFilesDatabase::Sort()
{
Indices.Sort(CompareFiles, (void *)&Items);
}
bool CFilesDatabase::Check()
{
UInt64 maxPos = 0;
UInt64 prevSection = 0;
for(int i = 0; i < Indices.Size(); i++)
{
const CItem &item = Items[Indices[i]];
if (item.Section == 0 || item.IsDir())
continue;
if (item.Section != prevSection)
{
prevSection = item.Section;
maxPos = 0;
continue;
}
if (item.Offset < maxPos)
return false;
maxPos = item.Offset + item.Size;
if (maxPos < item.Offset)
return false;
}
return true;
}
HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database)
{
{
// The NameList file
RINOK(DecompressStream(inStream, database, kNameList));
/* UInt16 length = */ ReadUInt16();
UInt16 numSections = ReadUInt16();
for (int i = 0; i < numSections; i++)
{
CSectionInfo section;
UInt16 nameLength = ReadUInt16();
UString name;
ReadUString(nameLength, name);
if (ReadUInt16() != 0)
return S_FALSE;
if (!ConvertUnicodeToUTF8(name, section.Name))
return S_FALSE;
database.Sections.Add(section);
}
}
int i;
for (i = 1; i < database.Sections.Size(); i++)
{
CSectionInfo §ion = database.Sections[i];
AString sectionPrefix = GetSectionPrefix(section.Name);
{
// Content
int index = database.FindItem(sectionPrefix + kContent);
if (index < 0)
return S_FALSE;
const CItem &item = database.Items[index];
section.Offset = item.Offset;
section.CompressedSize = item.Size;
}
AString transformPrefix = sectionPrefix + kTransform;
if (database.Help2Format)
{
// Transform List
RINOK(DecompressStream(inStream, database, transformPrefix + kTransformList));
if ((_chunkSize & 0xF) != 0)
return S_FALSE;
int numGuids = (int)(_chunkSize / 0x10);
if (numGuids < 1)
return S_FALSE;
for (int i = 0; i < numGuids; i++)
{
CMethodInfo method;
ReadGUID(method.Guid);
section.Methods.Add(method);
}
}
else
{
CMethodInfo method;
method.Guid = kChmLzxGuid;
section.Methods.Add(method);
}
{
// Control Data
RINOK(DecompressStream(inStream, database, sectionPrefix + kControlData));
for (int mi = 0; mi < section.Methods.Size(); mi++)
{
CMethodInfo &method = section.Methods[mi];
UInt32 numDWORDS = ReadUInt32();
if (method.IsLzx())
{
if (numDWORDS < 5)
return S_FALSE;
if (ReadUInt32() != NHeader::kLzxcSignature)
return S_FALSE;
CLzxInfo &li = method.LzxInfo;
li.Version = ReadUInt32();
if (li.Version != 2 && li.Version != 3)
return S_FALSE;
li.ResetInterval = ReadUInt32();
li.WindowSize = ReadUInt32();
li.CacheSize = ReadUInt32();
if (
li.ResetInterval != 1 &&
li.ResetInterval != 2 &&
li.ResetInterval != 4 &&
li.ResetInterval != 8 &&
li.ResetInterval != 16 &&
li.ResetInterval != 32 &&
li.ResetInterval != 64)
return S_FALSE;
if (
li.WindowSize != 1 &&
li.WindowSize != 2 &&
li.WindowSize != 4 &&
li.WindowSize != 8 &&
li.WindowSize != 16 &&
li.WindowSize != 32 &&
li.WindowSize != 64)
return S_FALSE;
numDWORDS -= 5;
while (numDWORDS-- != 0)
ReadUInt32();
}
else
{
UInt32 numBytes = numDWORDS * 4;
method.ControlData.SetCapacity(numBytes);
ReadBytes(method.ControlData, numBytes);
}
}
}
{
// SpanInfo
RINOK(DecompressStream(inStream, database, sectionPrefix + kSpanInfo));
section.UncompressedSize = ReadUInt64();
}
// read ResetTable for LZX
for (int mi = 0; mi < section.Methods.Size(); mi++)
{
CMethodInfo &method = section.Methods[mi];
if (method.IsLzx())
{
// ResetTable;
RINOK(DecompressStream(inStream, database, transformPrefix +
method.GetGuidString() + kResetTable));
CResetTable &rt = method.LzxInfo.ResetTable;
if (_chunkSize < 4)
{
if (_chunkSize != 0)
return S_FALSE;
// ResetTable is empty in .chw files
if (section.UncompressedSize != 0)
return S_FALSE;
rt.UncompressedSize = 0;
rt.CompressedSize = 0;
rt.BlockSize = 0;
}
else
{
UInt32 ver = ReadUInt32(); // 2 unknown (possibly a version number)
if (ver != 2 && ver != 3)
return S_FALSE;
UInt32 numEntries = ReadUInt32();
if (ReadUInt32() != 8) // Size of table entry (bytes)
return S_FALSE;
if (ReadUInt32() != 0x28) // Length of table header
return S_FALSE;
rt.UncompressedSize = ReadUInt64();
rt.CompressedSize = ReadUInt64();
rt.BlockSize = ReadUInt64(); // 0x8000 block size for locations below
if (rt.BlockSize != 0x8000)
return S_FALSE;
rt.ResetOffsets.Reserve(numEntries);
for (UInt32 i = 0; i < numEntries; i++)
rt.ResetOffsets.Add(ReadUInt64());
}
}
}
}
database.SetIndices();
database.Sort();
return database.Check() ? S_OK : S_FALSE;
}
HRESULT CInArchive::Open2(IInStream *inStream,
const UInt64 *searchHeaderSizeLimit,
CFilesDatabase &database)
{
database.Clear();
RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition));
database.Help2Format = false;
const UInt32 chmVersion = 3;
{
if (!_inBuffer.Create(1 << 14))
return E_OUTOFMEMORY;
_inBuffer.SetStream(inStream);
_inBuffer.Init();
UInt64 value = 0;
const int kSignatureSize = 8;
UInt64 hxsSignature = NHeader::GetHxsSignature();
UInt64 chmSignature = ((UInt64)chmVersion << 32)| NHeader::kItsfSignature;
UInt64 limit = 1 << 18;
if (searchHeaderSizeLimit)
if (limit > *searchHeaderSizeLimit)
limit = *searchHeaderSizeLimit;
for (;;)
{
Byte b;
if (!_inBuffer.ReadByte(b))
return S_FALSE;
value >>= 8;
value |= ((UInt64)b) << ((kSignatureSize - 1) * 8);
if (_inBuffer.GetProcessedSize() >= kSignatureSize)
{
if (value == chmSignature)
break;
if (value == hxsSignature)
{
database.Help2Format = true;
break;
}
if (_inBuffer.GetProcessedSize() > limit)
return S_FALSE;
}
}
_startPosition += _inBuffer.GetProcessedSize() - kSignatureSize;
}
if (database.Help2Format)
{
RINOK(OpenHelp2(inStream, database));
if (database.NewFormat)
return S_OK;
}
else
{
RINOK(OpenChm(inStream, database));
}
#ifndef CHM_LOW
try
{
HRESULT res = OpenHighLevel(inStream, database);
if (res == S_FALSE)
{
database.HighLevelClear();
return S_OK;
}
RINOK(res);
database.LowLevel = false;
}
catch(...)
{
return S_OK;
}
#endif
return S_OK;
}
HRESULT CInArchive::Open(IInStream *inStream,
const UInt64 *searchHeaderSizeLimit,
CFilesDatabase &database)
{
try
{
HRESULT res = Open2(inStream, searchHeaderSizeLimit, database);
_inBuffer.ReleaseStream();
return res;
}
catch(...)
{
_inBuffer.ReleaseStream();
throw;
}
}
}}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -