5 #include "Common/IntToString.h"
\r
6 #include "Common/UTFConvert.h"
\r
8 #include "../../Common/LimitedStreams.h"
\r
12 namespace NArchive {
\r
15 // define CHM_LOW, if you want to see low level items
\r
18 static const GUID kChmLzxGuid = { 0x7FC28940, 0x9D31, 0x11D0, { 0x9B, 0x27, 0x00, 0xA0, 0xC9, 0x1E, 0x9C, 0x7C } };
\r
19 static const GUID kHelp2LzxGuid = { 0x0A9007C6, 0x4076, 0x11D3, { 0x87, 0x89, 0x00, 0x00, 0xF8, 0x10, 0x57, 0x54 } };
\r
20 static const GUID kDesGuid = { 0x67F6E4A2, 0x60BF, 0x11D3, { 0x85, 0x40, 0x00, 0xC0, 0x4F, 0x58, 0xC3, 0xCF } };
\r
22 static bool AreGuidsEqual(REFGUID g1, REFGUID g2)
\r
24 if (g1.Data1 != g2.Data1 ||
\r
25 g1.Data2 != g2.Data2 ||
\r
26 g1.Data3 != g2.Data3)
\r
28 for (int i = 0; i < 8; i++)
\r
29 if (g1.Data4[i] != g2.Data4[i])
\r
34 static char GetHex(Byte value)
\r
36 return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
\r
39 static void PrintByte(Byte b, AString &s)
\r
41 s += GetHex(b >> 4);
\r
42 s += GetHex(b & 0xF);
\r
45 static void PrintUInt16(UInt16 v, AString &s)
\r
47 PrintByte((Byte)(v >> 8), s);
\r
48 PrintByte((Byte)v, s);
\r
51 static void PrintUInt32(UInt32 v, AString &s)
\r
53 PrintUInt16((UInt16)(v >> 16), s);
\r
54 PrintUInt16((UInt16)v, s);
\r
57 AString CMethodInfo::GetGuidString() const
\r
61 PrintUInt32(Guid.Data1, s);
\r
63 PrintUInt16(Guid.Data2, s);
\r
65 PrintUInt16(Guid.Data3, s);
\r
67 PrintByte(Guid.Data4[0], s);
\r
68 PrintByte(Guid.Data4[1], s);
\r
70 for (int i = 2; i < 8; i++)
\r
71 PrintByte(Guid.Data4[i], s);
\r
76 bool CMethodInfo::IsLzx() const
\r
78 if (AreGuidsEqual(Guid, kChmLzxGuid))
\r
80 return AreGuidsEqual(Guid, kHelp2LzxGuid);
\r
83 bool CMethodInfo::IsDes() const
\r
85 return AreGuidsEqual(Guid, kDesGuid);
\r
88 UString CMethodInfo::GetName() const
\r
95 ConvertUInt32ToString(LzxInfo.GetNumDictBits(), temp);
\r
105 s2 = GetGuidString();
\r
106 if (ControlData.GetCapacity() > 0)
\r
109 for (size_t i = 0; i < ControlData.GetCapacity(); i++)
\r
110 PrintByte(ControlData[i], s2);
\r
113 ConvertUTF8ToUnicode(s2, s);
\r
118 bool CSectionInfo::IsLzx() const
\r
120 if (Methods.Size() != 1)
\r
122 return Methods[0].IsLzx();
\r
125 UString CSectionInfo::GetMethodName() const
\r
131 if (ConvertUTF8ToUnicode(Name, temp))
\r
135 for (int i = 0; i < Methods.Size(); i++)
\r
139 s += Methods[i].GetName();
\r
144 Byte CInArchive::ReadByte()
\r
147 if (!_inBuffer.ReadByte(b))
\r
152 void CInArchive::Skip(size_t size)
\r
154 while (size-- != 0)
\r
158 void CInArchive::ReadBytes(Byte *data, UInt32 size)
\r
160 for (UInt32 i = 0; i < size; i++)
\r
161 data[i] = ReadByte();
\r
164 UInt16 CInArchive::ReadUInt16()
\r
167 for (int i = 0; i < 2; i++)
\r
168 value |= ((UInt16)(ReadByte()) << (8 * i));
\r
172 UInt32 CInArchive::ReadUInt32()
\r
175 for (int i = 0; i < 4; i++)
\r
176 value |= ((UInt32)(ReadByte()) << (8 * i));
\r
180 UInt64 CInArchive::ReadUInt64()
\r
183 for (int i = 0; i < 8; i++)
\r
184 value |= ((UInt64)(ReadByte()) << (8 * i));
\r
188 UInt64 CInArchive::ReadEncInt()
\r
191 for (int i = 0; i < 10; i++)
\r
193 Byte b = ReadByte();
\r
202 void CInArchive::ReadGUID(GUID &g)
\r
204 g.Data1 = ReadUInt32();
\r
205 g.Data2 = ReadUInt16();
\r
206 g.Data3 = ReadUInt16();
\r
207 ReadBytes(g.Data4, 8);
\r
210 void CInArchive::ReadString(int size, AString &s)
\r
215 char c = (char)ReadByte();
\r
225 void CInArchive::ReadUString(int size, UString &s)
\r
230 wchar_t c = ReadUInt16();
\r
240 HRESULT CInArchive::ReadChunk(IInStream *inStream, UInt64 pos, UInt64 size)
\r
242 RINOK(inStream->Seek(pos, STREAM_SEEK_SET, NULL));
\r
243 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
\r
244 CMyComPtr<ISequentialInStream> limitedStream(streamSpec);
\r
245 streamSpec->SetStream(inStream);
\r
246 streamSpec->Init(size);
\r
247 _inBuffer.SetStream(limitedStream);
\r
252 HRESULT CInArchive::ReadDirEntry(CDatabase &database)
\r
255 UInt64 nameLength = ReadEncInt();
\r
256 if (nameLength == 0 || nameLength >= 0x10000000)
\r
258 ReadString((int)nameLength, item.Name);
\r
259 item.Section = ReadEncInt();
\r
260 item.Offset = ReadEncInt();
\r
261 item.Size = ReadEncInt();
\r
262 database.Items.Add(item);
\r
266 HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database)
\r
268 UInt32 headerSize = ReadUInt32();
\r
269 if (headerSize != 0x60)
\r
271 UInt32 unknown1 = ReadUInt32();
\r
272 if (unknown1 != 0 && unknown1 != 1) // it's 0 in one .sll file
\r
274 /* UInt32 timeStamp = */ ReadUInt32();
\r
275 // Considered as a big-endian DWORD, it appears to contain seconds (MSB) and
\r
276 // fractional seconds (second byte).
\r
277 // The third and fourth bytes may contain even more fractional bits.
\r
278 // The 4 least significant bits in the last byte are constant.
\r
279 /* UInt32 lang = */ ReadUInt32();
\r
281 ReadGUID(g); // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC}
\r
282 ReadGUID(g); // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC}
\r
283 const int kNumSections = 2;
\r
284 UInt64 sectionOffsets[kNumSections];
\r
285 UInt64 sectionSizes[kNumSections];
\r
287 for (i = 0; i < kNumSections; i++)
\r
289 sectionOffsets[i] = ReadUInt64();
\r
290 sectionSizes[i] = ReadUInt64();
\r
292 // if (chmVersion == 3)
\r
293 database.ContentOffset = ReadUInt64();
\r
296 database.ContentOffset = _startPosition + 0x58
\r
301 ReadChunk(inStream, sectionOffsets[0], sectionSizes[0]);
\r
302 if (sectionSizes[0] != 0x18)
\r
304 ReadUInt32(); // unknown: 01FE
\r
305 ReadUInt32(); // unknown: 0
\r
306 UInt64 fileSize = ReadUInt64();
\r
307 ReadUInt32(); // unknown: 0
\r
308 ReadUInt32(); // unknown: 0
\r
311 // Section 1: The Directory Listing
\r
312 ReadChunk(inStream, sectionOffsets[1], sectionSizes[1]);
\r
313 if (ReadUInt32() != NHeader::kItspSignature)
\r
315 if (ReadUInt32() != 1) // version
\r
317 /* UInt32 dirHeaderSize = */ ReadUInt32();
\r
318 ReadUInt32(); // 0x0A (unknown)
\r
319 UInt32 dirChunkSize = ReadUInt32(); // $1000
\r
320 if (dirChunkSize < 32)
\r
322 /* UInt32 density = */ ReadUInt32(); // "Density" of quickref section, usually 2.
\r
323 /* UInt32 depth = */ ReadUInt32(); // Depth of the index tree: 1 there is no index,
\r
324 // 2 if there is one level of PMGI chunks.
\r
326 /* UInt32 chunkNumber = */ ReadUInt32(); // Chunk number of root index chunk, -1 if there is none
\r
327 // (though at least one file has 0 despite there being no
\r
328 // index chunk, probably a bug.)
\r
329 /* UInt32 firstPmglChunkNumber = */ ReadUInt32(); // Chunk number of first PMGL (listing) chunk
\r
330 /* UInt32 lastPmglChunkNumber = */ ReadUInt32(); // Chunk number of last PMGL (listing) chunk
\r
331 ReadUInt32(); // -1 (unknown)
\r
332 UInt32 numDirChunks = ReadUInt32(); // Number of directory chunks (total)
\r
333 /* UInt32 windowsLangId = */ ReadUInt32();
\r
334 ReadGUID(g); // {5D02926A-212E-11D0-9DF9-00A0C922E6EC}
\r
335 ReadUInt32(); // 0x54 (This is the length again)
\r
336 ReadUInt32(); // -1 (unknown)
\r
337 ReadUInt32(); // -1 (unknown)
\r
338 ReadUInt32(); // -1 (unknown)
\r
340 for (UInt32 ci = 0; ci < numDirChunks; ci++)
\r
342 UInt64 chunkPos = _inBuffer.GetProcessedSize();
\r
343 if (ReadUInt32() == NHeader::kPmglSignature)
\r
345 // The quickref area is written backwards from the end of the chunk.
\r
346 // One quickref entry exists for every n entries in the file, where n
\r
347 // is calculated as 1 + (1 << quickref density). So for density = 2, n = 5.
\r
349 UInt32 quickrefLength = ReadUInt32(); // Length of free space and/or quickref area at end of directory chunk
\r
350 if (quickrefLength > dirChunkSize || quickrefLength < 2)
\r
352 ReadUInt32(); // Always 0
\r
353 ReadUInt32(); // Chunk number of previous listing chunk when reading
\r
354 // directory in sequence (-1 if this is the first listing chunk)
\r
355 ReadUInt32(); // Chunk number of next listing chunk when reading
\r
356 // directory in sequence (-1 if this is the last listing chunk)
\r
360 UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos;
\r
361 UInt32 offsetLimit = dirChunkSize - quickrefLength;
\r
362 if (offset > offsetLimit)
\r
364 if (offset == offsetLimit)
\r
366 RINOK(ReadDirEntry(database));
\r
369 Skip(quickrefLength - 2);
\r
370 if (ReadUInt16() != numItems)
\r
374 Skip(dirChunkSize - 4);
\r
379 HRESULT CInArchive::OpenHelp2(IInStream *inStream, CDatabase &database)
\r
381 if (ReadUInt32() != 1) // version
\r
383 if (ReadUInt32() != 0x28) // Location of header section table
\r
385 UInt32 numHeaderSections = ReadUInt32();
\r
386 const int kNumHeaderSectionsMax = 5;
\r
387 if (numHeaderSections != kNumHeaderSectionsMax)
\r
389 ReadUInt32(); // Length of post-header table
\r
391 ReadGUID(g); // {0A9007C1-4076-11D3-8789-0000F8105754}
\r
393 // header section table
\r
394 UInt64 sectionOffsets[kNumHeaderSectionsMax];
\r
395 UInt64 sectionSizes[kNumHeaderSectionsMax];
\r
397 for (i = 0; i < numHeaderSections; i++)
\r
399 sectionOffsets[i] = ReadUInt64();
\r
400 sectionSizes[i] = ReadUInt64();
\r
405 ReadUInt32(); // 0x98: offset to CAOL from beginning of post-header)
\r
406 // ----- Directory information
\r
407 ReadUInt64(); // Chunk number of top-level AOLI chunk in directory, or -1
\r
408 ReadUInt64(); // Chunk number of first AOLL chunk in directory
\r
409 ReadUInt64(); // Chunk number of last AOLL chunk in directory
\r
410 ReadUInt64(); // 0 (unknown)
\r
411 ReadUInt32(); // $2000 (Directory chunk size of directory)
\r
412 ReadUInt32(); // Quickref density for main directory, usually 2
\r
413 ReadUInt32(); // 0 (unknown)
\r
414 ReadUInt32(); // Depth of main directory index tree
\r
415 // 1 there is no index, 2 if there is one level of AOLI chunks.
\r
416 ReadUInt64(); // 0 (unknown)
\r
417 UInt64 numDirEntries = ReadUInt64(); // Number of directory entries
\r
418 // ----- Directory Index Information
\r
419 ReadUInt64(); // -1 (unknown, probably chunk number of top-level AOLI in directory index)
\r
420 ReadUInt64(); // Chunk number of first AOLL chunk in directory index
\r
421 ReadUInt64(); // Chunk number of last AOLL chunk in directory index
\r
422 ReadUInt64(); // 0 (unknown)
\r
423 ReadUInt32(); // $200 (Directory chunk size of directory index)
\r
424 ReadUInt32(); // Quickref density for directory index, usually 2
\r
425 ReadUInt32(); // 0 (unknown)
\r
426 ReadUInt32(); // Depth of directory index index tree.
\r
427 ReadUInt64(); // Possibly flags -- sometimes 1, sometimes 0.
\r
428 ReadUInt64(); // Number of directory index entries (same as number of AOLL
\r
429 // chunks in main directory)
\r
431 // (The obvious guess for the following two fields, which recur in a number
\r
432 // of places, is they are maximum sizes for the directory and directory index.
\r
433 // However, I have seen no direct evidence that this is the case.)
\r
435 ReadUInt32(); // $100000 (Same as field following chunk size in directory)
\r
436 ReadUInt32(); // $20000 (Same as field following chunk size in directory index)
\r
438 ReadUInt64(); // 0 (unknown)
\r
439 if (ReadUInt32() != NHeader::kCaolSignature)
\r
441 if (ReadUInt32() != 2) // (Most likely a version number)
\r
443 UInt32 caolLength = ReadUInt32(); // $50 (Length of the CAOL section, which includes the ITSF section)
\r
444 if (caolLength >= 0x2C)
\r
446 /* UInt32 c7 = */ ReadUInt16(); // Unknown. Remains the same when identical files are built.
\r
447 // Does not appear to be a checksum. Many files have
\r
448 // 'HH' (HTML Help?) here, indicating this may be a compiler ID
\r
449 // field. But at least one ITOL/ITLS compiler does not set this
\r
450 // field to a constant value.
\r
451 ReadUInt16(); // 0 (Unknown. Possibly part of 00A4 field)
\r
452 ReadUInt32(); // Unknown. Two values have been seen -- $43ED, and 0.
\r
453 ReadUInt32(); // $2000 (Directory chunk size of directory)
\r
454 ReadUInt32(); // $200 (Directory chunk size of directory index)
\r
455 ReadUInt32(); // $100000 (Same as field following chunk size in directory)
\r
456 ReadUInt32(); // $20000 (Same as field following chunk size in directory index)
\r
457 ReadUInt32(); // 0 (unknown)
\r
458 ReadUInt32(); // 0 (Unknown)
\r
459 if (caolLength == 0x2C)
\r
461 database.ContentOffset = 0;
\r
462 database.NewFormat = true;
\r
464 else if (caolLength == 0x50)
\r
466 ReadUInt32(); // 0 (Unknown)
\r
467 if (ReadUInt32() != NHeader::kItsfSignature)
\r
469 if (ReadUInt32() != 4) // $4 (Version number -- CHM uses 3)
\r
471 if (ReadUInt32() != 0x20) // $20 (length of ITSF)
\r
473 UInt32 unknown = ReadUInt32();
\r
474 if (unknown != 0 && unknown != 1) // = 0 for some HxW files, 1 in other cases;
\r
476 database.ContentOffset = _startPosition + ReadUInt64();
\r
477 /* UInt32 timeStamp = */ ReadUInt32();
\r
478 // A timestamp of some sort.
\r
479 // Considered as a big-endian DWORD, it appears to contain
\r
480 // seconds (MSB) and fractional seconds (second byte).
\r
481 // The third and fourth bytes may contain even more fractional
\r
482 // bits. The 4 least significant bits in the last byte are constant.
\r
483 /* UInt32 lang = */ ReadUInt32(); // BE?
\r
491 ReadChunk(inStream, _startPosition + sectionOffsets[0], sectionSizes[0]);
\r
492 if (sectionSizes[0] != 0x18)
\r
494 ReadUInt32(); // unknown: 01FE
\r
495 ReadUInt32(); // unknown: 0
\r
496 UInt64 fileSize = ReadUInt64();
\r
497 ReadUInt32(); // unknown: 0
\r
498 ReadUInt32(); // unknown: 0
\r
501 // Section 1: The Directory Listing
\r
502 ReadChunk(inStream, _startPosition + sectionOffsets[1], sectionSizes[1]);
\r
503 if (ReadUInt32() != NHeader::kIfcmSignature)
\r
505 if (ReadUInt32() != 1) // (probably a version number)
\r
507 UInt32 dirChunkSize = ReadUInt32(); // $2000
\r
508 if (dirChunkSize < 64)
\r
510 ReadUInt32(); // $100000 (unknown)
\r
511 ReadUInt32(); // -1 (unknown)
\r
512 ReadUInt32(); // -1 (unknown)
\r
513 UInt32 numDirChunks = ReadUInt32();
\r
514 ReadUInt32(); // 0 (unknown, probably high word of above)
\r
516 for (UInt32 ci = 0; ci < numDirChunks; ci++)
\r
518 UInt64 chunkPos = _inBuffer.GetProcessedSize();
\r
519 if (ReadUInt32() == NHeader::kAollSignature)
\r
521 UInt32 quickrefLength = ReadUInt32(); // Length of quickref area at end of directory chunk
\r
522 if (quickrefLength > dirChunkSize || quickrefLength < 2)
\r
524 ReadUInt64(); // Directory chunk number
\r
525 // This must match physical position in file, that is
\r
526 // the chunk size times the chunk number must be the
\r
527 // offset from the end of the directory header.
\r
528 ReadUInt64(); // Chunk number of previous listing chunk when reading
\r
529 // directory in sequence (-1 if first listing chunk)
\r
530 ReadUInt64(); // Chunk number of next listing chunk when reading
\r
531 // directory in sequence (-1 if last listing chunk)
\r
532 ReadUInt64(); // Number of first listing entry in this chunk
\r
533 ReadUInt32(); // 1 (unknown -- other values have also been seen here)
\r
534 ReadUInt32(); // 0 (unknown)
\r
539 UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos;
\r
540 UInt32 offsetLimit = dirChunkSize - quickrefLength;
\r
541 if (offset > offsetLimit)
\r
543 if (offset == offsetLimit)
\r
545 if (database.NewFormat)
\r
547 UInt16 nameLength = ReadUInt16();
\r
548 if (nameLength == 0)
\r
551 ReadUString((int)nameLength, name);
\r
553 ConvertUnicodeToUTF8(name, s);
\r
554 Byte b = ReadByte();
\r
558 UInt64 len = ReadEncInt();
\r
559 // then number of items ?
\r
561 // then some data (binary encoding?)
\r
567 database.NewFormatString += s;
\r
568 database.NewFormatString += "\r\n";
\r
572 RINOK(ReadDirEntry(database));
\r
576 Skip(quickrefLength - 2);
\r
577 if (ReadUInt16() != numItems)
\r
579 if (numItems > numDirEntries)
\r
581 numDirEntries -= numItems;
\r
584 Skip(dirChunkSize - 4);
\r
586 return numDirEntries == 0 ? S_OK : S_FALSE;
\r
589 HRESULT CInArchive::DecompressStream(IInStream *inStream, const CDatabase &database, const AString &name)
\r
591 int index = database.FindItem(name);
\r
594 const CItem &item = database.Items[index];
\r
595 _chunkSize = item.Size;
\r
596 return ReadChunk(inStream, database.ContentOffset + item.Offset, item.Size);
\r
600 #define DATA_SPACE "::DataSpace/"
\r
601 static const char *kNameList = DATA_SPACE "NameList";
\r
602 static const char *kStorage = DATA_SPACE "Storage/";
\r
603 static const char *kContent = "Content";
\r
604 static const char *kControlData = "ControlData";
\r
605 static const char *kSpanInfo = "SpanInfo";
\r
606 static const char *kTransform = "Transform/";
\r
607 static const char *kResetTable = "/InstanceData/ResetTable";
\r
608 static const char *kTransformList = "List";
\r
610 static AString GetSectionPrefix(const AString &name)
\r
612 return AString(kStorage) + name + AString("/");
\r
615 #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
\r
617 static int CompareFiles(const int *p1, const int *p2, void *param)
\r
619 const CObjectVector<CItem> &items = *(const CObjectVector<CItem> *)param;
\r
620 const CItem &item1 = items[*p1];
\r
621 const CItem &item2 = items[*p2];
\r
622 bool isDir1 = item1.IsDir();
\r
623 bool isDir2 = item2.IsDir();
\r
624 if (isDir1 && !isDir2)
\r
629 return MyCompare(*p1, *p2);
\r
632 RINOZ(MyCompare(item1.Section, item2.Section));
\r
633 RINOZ(MyCompare(item1.Offset, item2.Offset));
\r
634 RINOZ(MyCompare(item1.Size, item2.Size));
\r
635 return MyCompare(*p1, *p2);
\r
638 void CFilesDatabase::SetIndices()
\r
640 for (int i = 0; i < Items.Size(); i++)
\r
642 const CItem &item = Items[i];
\r
643 if (item.IsUserItem() && item.Name.Length() != 1)
\r
648 void CFilesDatabase::Sort()
\r
650 Indices.Sort(CompareFiles, (void *)&Items);
\r
653 bool CFilesDatabase::Check()
\r
656 UInt64 prevSection = 0;
\r
657 for(int i = 0; i < Indices.Size(); i++)
\r
659 const CItem &item = Items[Indices[i]];
\r
660 if (item.Section == 0 || item.IsDir())
\r
662 if (item.Section != prevSection)
\r
664 prevSection = item.Section;
\r
668 if (item.Offset < maxPos)
\r
670 maxPos = item.Offset + item.Size;
\r
671 if (maxPos < item.Offset)
\r
677 HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database)
\r
680 // The NameList file
\r
681 RINOK(DecompressStream(inStream, database, kNameList));
\r
682 /* UInt16 length = */ ReadUInt16();
\r
683 UInt16 numSections = ReadUInt16();
\r
684 for (int i = 0; i < numSections; i++)
\r
686 CSectionInfo section;
\r
687 UInt16 nameLength = ReadUInt16();
\r
689 ReadUString(nameLength, name);
\r
690 if (ReadUInt16() != 0)
\r
692 if (!ConvertUnicodeToUTF8(name, section.Name))
\r
694 database.Sections.Add(section);
\r
699 for (i = 1; i < database.Sections.Size(); i++)
\r
701 CSectionInfo §ion = database.Sections[i];
\r
702 AString sectionPrefix = GetSectionPrefix(section.Name);
\r
705 int index = database.FindItem(sectionPrefix + kContent);
\r
708 const CItem &item = database.Items[index];
\r
709 section.Offset = item.Offset;
\r
710 section.CompressedSize = item.Size;
\r
712 AString transformPrefix = sectionPrefix + kTransform;
\r
713 if (database.Help2Format)
\r
716 RINOK(DecompressStream(inStream, database, transformPrefix + kTransformList));
\r
717 if ((_chunkSize & 0xF) != 0)
\r
719 int numGuids = (int)(_chunkSize / 0x10);
\r
722 for (int i = 0; i < numGuids; i++)
\r
724 CMethodInfo method;
\r
725 ReadGUID(method.Guid);
\r
726 section.Methods.Add(method);
\r
731 CMethodInfo method;
\r
732 method.Guid = kChmLzxGuid;
\r
733 section.Methods.Add(method);
\r
738 RINOK(DecompressStream(inStream, database, sectionPrefix + kControlData));
\r
739 for (int mi = 0; mi < section.Methods.Size(); mi++)
\r
741 CMethodInfo &method = section.Methods[mi];
\r
742 UInt32 numDWORDS = ReadUInt32();
\r
743 if (method.IsLzx())
\r
747 if (ReadUInt32() != NHeader::kLzxcSignature)
\r
749 CLzxInfo &li = method.LzxInfo;
\r
750 li.Version = ReadUInt32();
\r
751 if (li.Version != 2 && li.Version != 3)
\r
753 li.ResetInterval = ReadUInt32();
\r
754 li.WindowSize = ReadUInt32();
\r
755 li.CacheSize = ReadUInt32();
\r
757 li.ResetInterval != 1 &&
\r
758 li.ResetInterval != 2 &&
\r
759 li.ResetInterval != 4 &&
\r
760 li.ResetInterval != 8 &&
\r
761 li.ResetInterval != 16 &&
\r
762 li.ResetInterval != 32 &&
\r
763 li.ResetInterval != 64)
\r
766 li.WindowSize != 1 &&
\r
767 li.WindowSize != 2 &&
\r
768 li.WindowSize != 4 &&
\r
769 li.WindowSize != 8 &&
\r
770 li.WindowSize != 16 &&
\r
771 li.WindowSize != 32 &&
\r
772 li.WindowSize != 64)
\r
775 while (numDWORDS-- != 0)
\r
780 UInt32 numBytes = numDWORDS * 4;
\r
781 method.ControlData.SetCapacity(numBytes);
\r
782 ReadBytes(method.ControlData, numBytes);
\r
789 RINOK(DecompressStream(inStream, database, sectionPrefix + kSpanInfo));
\r
790 section.UncompressedSize = ReadUInt64();
\r
793 // read ResetTable for LZX
\r
794 for (int mi = 0; mi < section.Methods.Size(); mi++)
\r
796 CMethodInfo &method = section.Methods[mi];
\r
797 if (method.IsLzx())
\r
800 RINOK(DecompressStream(inStream, database, transformPrefix +
\r
801 method.GetGuidString() + kResetTable));
\r
802 CResetTable &rt = method.LzxInfo.ResetTable;
\r
803 if (_chunkSize < 4)
\r
805 if (_chunkSize != 0)
\r
807 // ResetTable is empty in .chw files
\r
808 if (section.UncompressedSize != 0)
\r
810 rt.UncompressedSize = 0;
\r
811 rt.CompressedSize = 0;
\r
816 UInt32 ver = ReadUInt32(); // 2 unknown (possibly a version number)
\r
817 if (ver != 2 && ver != 3)
\r
819 UInt32 numEntries = ReadUInt32();
\r
820 if (ReadUInt32() != 8) // Size of table entry (bytes)
\r
822 if (ReadUInt32() != 0x28) // Length of table header
\r
824 rt.UncompressedSize = ReadUInt64();
\r
825 rt.CompressedSize = ReadUInt64();
\r
826 rt.BlockSize = ReadUInt64(); // 0x8000 block size for locations below
\r
827 if (rt.BlockSize != 0x8000)
\r
829 rt.ResetOffsets.Reserve(numEntries);
\r
830 for (UInt32 i = 0; i < numEntries; i++)
\r
831 rt.ResetOffsets.Add(ReadUInt64());
\r
837 database.SetIndices();
\r
839 return database.Check() ? S_OK : S_FALSE;
\r
842 HRESULT CInArchive::Open2(IInStream *inStream,
\r
843 const UInt64 *searchHeaderSizeLimit,
\r
844 CFilesDatabase &database)
\r
848 RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &_startPosition));
\r
850 database.Help2Format = false;
\r
851 const UInt32 chmVersion = 3;
\r
853 if (!_inBuffer.Create(1 << 14))
\r
854 return E_OUTOFMEMORY;
\r
855 _inBuffer.SetStream(inStream);
\r
858 const int kSignatureSize = 8;
\r
859 UInt64 hxsSignature = NHeader::GetHxsSignature();
\r
860 UInt64 chmSignature = ((UInt64)chmVersion << 32)| NHeader::kItsfSignature;
\r
861 UInt64 limit = 1 << 18;
\r
862 if (searchHeaderSizeLimit)
\r
863 if (limit > *searchHeaderSizeLimit)
\r
864 limit = *searchHeaderSizeLimit;
\r
869 if (!_inBuffer.ReadByte(b))
\r
872 value |= ((UInt64)b) << ((kSignatureSize - 1) * 8);
\r
873 if (_inBuffer.GetProcessedSize() >= kSignatureSize)
\r
875 if (value == chmSignature)
\r
877 if (value == hxsSignature)
\r
879 database.Help2Format = true;
\r
882 if (_inBuffer.GetProcessedSize() > limit)
\r
886 _startPosition += _inBuffer.GetProcessedSize() - kSignatureSize;
\r
889 if (database.Help2Format)
\r
891 RINOK(OpenHelp2(inStream, database));
\r
892 if (database.NewFormat)
\r
897 RINOK(OpenChm(inStream, database));
\r
903 HRESULT res = OpenHighLevel(inStream, database);
\r
904 if (res == S_FALSE)
\r
906 database.HighLevelClear();
\r
910 database.LowLevel = false;
\r
920 HRESULT CInArchive::Open(IInStream *inStream,
\r
921 const UInt64 *searchHeaderSizeLimit,
\r
922 CFilesDatabase &database)
\r
926 HRESULT res = Open2(inStream, searchHeaderSizeLimit, database);
\r
927 _inBuffer.ReleaseStream();
\r
932 _inBuffer.ReleaseStream();
\r