Example #1
0
HRESULT CInArchive::OpenHelp2(IInStream *inStream, CDatabase &database)
{
  if (ReadUInt32() != 1) // version
    return S_FALSE;
  if (ReadUInt32() != 0x28) // Location of header section table
    return S_FALSE;
  UInt32 numHeaderSections = ReadUInt32();
  const unsigned kNumHeaderSectionsMax = 5;
  if (numHeaderSections != kNumHeaderSectionsMax)
    return S_FALSE;

  IsArc = true;

  ReadUInt32(); // Len of post-header table
  Byte g[16];
  ReadGUID(g);  // {0A9007C1-4076-11D3-8789-0000F8105754}

  // header section table
  UInt64 sectionOffsets[kNumHeaderSectionsMax];
  UInt64 sectionSizes[kNumHeaderSectionsMax];
  UInt32 i;
  for (i = 0; i < numHeaderSections; i++)
  {
    sectionOffsets[i] = ReadUInt64();
    sectionSizes[i] = ReadUInt64();
    UInt64 end = sectionOffsets[i] + sectionSizes[i];
    database.UpdatePhySize(end);
  }
  
  // Post-Header
  ReadUInt32(); // 2
  ReadUInt32(); // 0x98: offset to CAOL from beginning of post-header)
  // ----- Directory information
  ReadUInt64(); // Chunk number of top-level AOLI chunk in directory, or -1
  ReadUInt64(); // Chunk number of first AOLL chunk in directory
  ReadUInt64(); // Chunk number of last AOLL chunk in directory
  ReadUInt64(); // 0 (unknown)
  ReadUInt32(); // $2000 (Directory chunk size of directory)
  ReadUInt32(); // Quickref density for main directory, usually 2
  ReadUInt32(); // 0 (unknown)
  ReadUInt32(); // Depth of main directory index tree
                // 1 there is no index, 2 if there is one level of AOLI chunks.
  ReadUInt64(); // 0 (unknown)
  UInt64 numDirEntries = ReadUInt64(); // Number of directory entries
  // ----- Directory Index Information
  ReadUInt64(); // -1 (unknown, probably chunk number of top-level AOLI in directory index)
  ReadUInt64(); // Chunk number of first AOLL chunk in directory index
  ReadUInt64(); // Chunk number of last AOLL chunk in directory index
  ReadUInt64(); // 0 (unknown)
  ReadUInt32(); // $200 (Directory chunk size of directory index)
  ReadUInt32(); // Quickref density for directory index, usually 2
  ReadUInt32(); // 0 (unknown)
  ReadUInt32(); // Depth of directory index index tree.
  ReadUInt64(); // Possibly flags -- sometimes 1, sometimes 0.
  ReadUInt64(); // Number of directory index entries (same as number of AOLL
               // chunks in main directory)
  
  // (The obvious guess for the following two fields, which recur in a number
  // of places, is they are maximum sizes for the directory and directory index.
  // However, I have seen no direct evidence that this is the case.)

  ReadUInt32(); // $100000 (Same as field following chunk size in directory)
  ReadUInt32(); // $20000 (Same as field following chunk size in directory index)

  ReadUInt64(); // 0 (unknown)
  if (ReadUInt32() != kSignature_CAOL)
    return S_FALSE;
  if (ReadUInt32() != 2) // (Most likely a version number)
    return S_FALSE;
  UInt32 caolLength = ReadUInt32(); // $50 (Len of the CAOL section, which includes the ITSF section)
  if (caolLength >= 0x2C)
  {
    /* UInt32 c7 = */ ReadUInt16(); // Unknown.  Remains the same when identical files are built.
              // Does not appear to be a checksum.  Many files have
              // 'HH' (HTML Help?) here, indicating this may be a compiler ID
              //  field.  But at least one ITOL/ITLS compiler does not set this
              // field to a constant value.
    ReadUInt16(); // 0 (Unknown.  Possibly part of 00A4 field)
    ReadUInt32(); // Unknown.  Two values have been seen -- $43ED, and 0.
    ReadUInt32(); // $2000 (Directory chunk size of directory)
    ReadUInt32(); // $200 (Directory chunk size of directory index)
    ReadUInt32(); // $100000 (Same as field following chunk size in directory)
    ReadUInt32(); // $20000 (Same as field following chunk size in directory index)
    ReadUInt32(); // 0 (unknown)
    ReadUInt32(); // 0 (Unknown)
    if (caolLength == 0x2C)
    {
      // fprintf(stdout, "\n !!!NewFormat\n");
      // fflush(stdout);
      database.ContentOffset = 0; // maybe we must add database.StartPosition here?
      database.NewFormat = true;
    }
    else if (caolLength == 0x50)
    {
      ReadUInt32(); // 0 (Unknown)
      if (ReadUInt32() != kSignature_ITSF)
        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 = database.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, database.StartPosition + sectionOffsets[0], sectionSizes[0]);
  if (sectionSizes[0] < 0x18)
    return S_FALSE;
  if (ReadUInt32() != 0x01FE)
    return S_FALSE;
  ReadUInt32(); // unknown:  0
  UInt64 fileSize = ReadUInt64();
  database.UpdatePhySize(fileSize);
  ReadUInt32(); // unknown:  0
  ReadUInt32(); // unknown:  0

  // Section 1: The Directory Listing
  ReadChunk(inStream, database.StartPosition + sectionOffsets[1], sectionSizes[1]);
  if (ReadUInt32() != kSignature_IFCM)
    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() == kSignature_AOLL)
    {
      UInt32 quickrefLength = ReadUInt32(); // Len 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)
      
      unsigned 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 nameLen = ReadUInt16();
          if (nameLen == 0)
            return S_FALSE;
          UString name;
          ReadUString((unsigned)nameLen, name);
          AString s;
          ConvertUnicodeToUTF8(name, s);
          Byte b = ReadByte();
          s.Add_Space();
          PrintByte(b, s);
          s.Add_Space();
          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++;
      }
      Skip(quickrefLength - 2);
      if (ReadUInt16() != numItems)
        return S_FALSE;
      if (numItems > numDirEntries)
        return S_FALSE;
      numDirEntries -= numItems;
    }
    else
      Skip(dirChunkSize - 4);
  }
  return numDirEntries == 0 ? S_OK : S_FALSE;
}
Example #2
0
HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database)
{
  UInt32 headerSize = ReadUInt32();
  if (headerSize != 0x60)
    return S_FALSE;
  database.PhySize = headerSize;

  UInt32 unknown1 = ReadUInt32();
  if (unknown1 != 0 && unknown1 != 1) // it's 0 in one .sll file
    return S_FALSE;

  IsArc = true;

  /* UInt32 timeStamp = */ ReadUInt32();
      // 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();
  Byte g[16];
  ReadGUID(g); // {7C01FD10-7BAA-11D0-9E0C-00A0-C922-E6EC}
  ReadGUID(g); // {7C01FD11-7BAA-11D0-9E0C-00A0-C922-E6EC}
  const unsigned kNumSections = 2;
  UInt64 sectionOffsets[kNumSections];
  UInt64 sectionSizes[kNumSections];
  unsigned i;
  for (i = 0; i < kNumSections; i++)
  {
    sectionOffsets[i] = ReadUInt64();
    sectionSizes[i] = ReadUInt64();
    UInt64 end = sectionOffsets[i] + sectionSizes[i];
    database.UpdatePhySize(end);
  }
  // if (chmVersion == 3)
    database.ContentOffset = ReadUInt64();
  /*
  else
    database.ContentOffset = database.StartPosition + 0x58
  */

  // Section 0
  ReadChunk(inStream, sectionOffsets[0], sectionSizes[0]);
  if (sectionSizes[0] < 0x18)
    return S_FALSE;
  if (ReadUInt32() != 0x01FE)
    return S_FALSE;
  ReadUInt32(); // unknown:  0
  UInt64 fileSize = ReadUInt64();
  database.UpdatePhySize(fileSize);
  ReadUInt32(); // unknown:  0
  ReadUInt32(); // unknown:  0

  // Section 1: The Directory Listing
  ReadChunk(inStream, sectionOffsets[1], sectionSizes[1]);
  if (ReadUInt32() != kSignature_ITSP)
    return S_FALSE;
  if (ReadUInt32() != 1) // version
    return S_FALSE;
  /* UInt32 dirHeaderSize = */ ReadUInt32();
  ReadUInt32(); // 0x0A (unknown)
  UInt32 dirChunkSize = ReadUInt32(); // $1000
  if (dirChunkSize < 32)
    return S_FALSE;
  /* UInt32 density = */ ReadUInt32(); //  "Density" of quickref section, usually 2.
  /* UInt32 depth = */ ReadUInt32(); //  Depth of the index tree: 1 there is no index,
                               // 2 if there is one level of PMGI chunks.

  /* UInt32 chunkNumber = */ ReadUInt32(); //  Chunk number of root index chunk, -1 if there is none
                                     // (though at least one file has 0 despite there being no
                                     // index chunk, probably a bug.)
  /* UInt32 firstPmglChunkNumber = */ ReadUInt32(); // Chunk number of first PMGL (listing) chunk
  /* UInt32 lastPmglChunkNumber = */ ReadUInt32();  // Chunk number of last PMGL (listing) chunk
  ReadUInt32(); // -1 (unknown)
  UInt32 numDirChunks = ReadUInt32(); // Number of directory chunks (total)
  /* UInt32 windowsLangId = */ ReadUInt32();
  ReadGUID(g);  // {5D02926A-212E-11D0-9DF9-00A0C922E6EC}
  ReadUInt32(); // 0x54 (This is the length again)
  ReadUInt32(); // -1 (unknown)
  ReadUInt32(); // -1 (unknown)
  ReadUInt32(); // -1 (unknown)

  for (UInt32 ci = 0; ci < numDirChunks; ci++)
  {
    UInt64 chunkPos = _inBuffer.GetProcessedSize();
    if (ReadUInt32() == kSignature_PMGL)
    {
      // The quickref area is written backwards from the end of the chunk.
      // One quickref entry exists for every n entries in the file, where n
      // is calculated as 1 + (1 << quickref density). So for density = 2, n = 5.

      UInt32 quickrefLength = ReadUInt32(); // Len of free space and/or quickref area at end of directory chunk
      if (quickrefLength > dirChunkSize || quickrefLength < 2)
        return S_FALSE;
      ReadUInt32(); // Always 0
      ReadUInt32(); // Chunk number of previous listing chunk when reading
                    // directory in sequence (-1 if this is the first listing chunk)
      ReadUInt32(); // Chunk number of next  listing chunk when reading
                    // directory in sequence (-1 if this is the last listing chunk)
      unsigned numItems = 0;
      
      for (;;)
      {
        UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos;
        UInt32 offsetLimit = dirChunkSize - quickrefLength;
        if (offset > offsetLimit)
          return S_FALSE;
        if (offset == offsetLimit)
          break;
        RINOK(ReadDirEntry(database));
        numItems++;
      }
      
      Skip(quickrefLength - 2);
      
      unsigned rrr = ReadUInt16();
      if (rrr != numItems)
      {
        // Lazarus 9-26-2 chm contains 0 here.
        if (rrr != 0)
          return S_FALSE;
      }
    }
    else
      Skip(dirChunkSize - 4);
  }
  return S_OK;
}