// http://www.nongnu.org/chmspec/latest/Internal.html#WINDOWS void ChmDoc::ParseWindowsData() { size_t windowsLen, stringsLen; ScopedMem<unsigned char> windowsData(GetData("/#WINDOWS", &windowsLen)); ScopedMem<unsigned char> stringsData(GetData("/#STRINGS", &stringsLen)); if (!windowsData || !stringsData) return; if (windowsLen <= 8) return; ByteReader rw(windowsData, windowsLen); DWORD entries = rw.DWordLE(0); DWORD entrySize = rw.DWordLE(4); if (entrySize < 188) return; for (DWORD i = 0; i < entries && (i + 1) * entrySize <= windowsLen; i++) { DWORD off = 8 + i * entrySize; if (!title) { DWORD strOff = rw.DWordLE(off + 0x14); title.Set(GetCharZ(stringsData, stringsLen, strOff)); } if (!tocPath) { DWORD strOff = rw.DWordLE(off + 0x60); tocPath.Set(GetCharZ(stringsData, stringsLen, strOff)); } if (!indexPath) { DWORD strOff = rw.DWordLE(off + 0x64); indexPath.Set(GetCharZ(stringsData, stringsLen, strOff)); } if (!homePath) { DWORD strOff = rw.DWordLE(off + 0x68); homePath.Set(GetCharZ(stringsData, stringsLen, strOff)); } } }
// http://www.nongnu.org/chmspec/latest/Internal.html#SYSTEM bool ChmDoc::ParseSystemData() { size_t dataLen; ScopedMem<unsigned char> data(GetData("/#SYSTEM", &dataLen)); if (!data) return false; ByteReader r(data, dataLen); DWORD len = 0; // Note: skipping DWORD version at offset 0. It's supposed to be 2 or 3. for (size_t off = 4; off + 4 < dataLen; off += len + 4) { // Note: at some point we seem to get off-sync i.e. I'm seeing // many entries with type == 0 and len == 0. Seems harmless. len = r.WordLE(off + 2); if (len == 0) continue; WORD type = r.WordLE(off); switch (type) { case 0: if (!tocPath) tocPath.Set(GetCharZ(data, dataLen, off + 4)); break; case 1: if (!indexPath) indexPath.Set(GetCharZ(data, dataLen, off + 4)); break; case 2: if (!homePath) homePath.Set(GetCharZ(data, dataLen, off + 4)); break; case 3: if (!title) title.Set(GetCharZ(data, dataLen, off + 4)); break; case 4: if (!codepage && len >= 4) codepage = LcidToCodepage(r.DWordLE(off + 4)); break; case 6: // compiled file - ignore break; case 9: if (!creator) creator.Set(GetCharZ(data, dataLen, off + 4)); break; case 16: // default font - ignore break; } } return true; }
char* ChmDoc::ResolveTopicID(unsigned int id) { size_t ivbLen = 0; ScopedMem<unsigned char> ivbData(GetData("/#IVB", &ivbLen)); ByteReader br(ivbData, ivbLen); if ((ivbLen % 8) != 4 || ivbLen - 4 != br.DWordLE(0)) return nullptr; for (size_t off = 4; off < ivbLen; off += 8) { if (br.DWordLE(off) == id) { size_t stringsLen = 0; ScopedMem<unsigned char> stringsData(GetData("/#STRINGS", &stringsLen)); return GetCharZ(stringsData, stringsLen, br.DWordLE(off + 4)); } } return nullptr; }