| 108 | } |
| 109 | |
| 110 | bool ZipFile::loadFileStatSlim(const char* filename, FileStatSlim* fileStat) { |
| 111 | if (!fileStatSlimCache.empty()) { |
| 112 | const auto it = fileStatSlimCache.find(filename); |
| 113 | if (it != fileStatSlimCache.end()) { |
| 114 | *fileStat = it->second; |
| 115 | return true; |
| 116 | } |
| 117 | return false; |
| 118 | } |
| 119 | |
| 120 | const ScopedOpenClose zip{*this}; |
| 121 | if (!zip) return false; |
| 122 | |
| 123 | if (!loadZipDetails()) return false; |
| 124 | |
| 125 | // Phase 1: Try scanning from cursor position first |
| 126 | uint32_t startPos = lastCentralDirPosValid ? lastCentralDirPos : zipDetails.centralDirOffset; |
| 127 | bool wrapped = false; |
| 128 | bool found = false; |
| 129 | |
| 130 | file.seek(startPos); |
| 131 | |
| 132 | uint32_t sig; |
| 133 | char itemName[256]; |
| 134 | |
| 135 | while (true) { |
| 136 | uint32_t entryStart = file.position(); |
| 137 | |
| 138 | if (file.read(&sig, 4) != 4 || sig != 0x02014b50) { |
| 139 | // End of central directory |
| 140 | if (!wrapped && lastCentralDirPosValid && startPos != zipDetails.centralDirOffset) { |
| 141 | // Wrap around to beginning |
| 142 | file.seek(zipDetails.centralDirOffset); |
| 143 | wrapped = true; |
| 144 | continue; |
| 145 | } |
| 146 | break; |
| 147 | } |
| 148 | |
| 149 | // If we've wrapped and reached our start position, stop |
| 150 | if (wrapped && entryStart >= startPos) { |
| 151 | break; |
| 152 | } |
| 153 | |
| 154 | file.seekCur(6); |
| 155 | file.read(&fileStat->method, 2); |
| 156 | file.seekCur(8); |
| 157 | file.read(&fileStat->compressedSize, 4); |
| 158 | file.read(&fileStat->uncompressedSize, 4); |
| 159 | uint16_t nameLen, m, k; |
| 160 | file.read(&nameLen, 2); |
| 161 | file.read(&m, 2); |
| 162 | file.read(&k, 2); |
| 163 | file.seekCur(8); |
| 164 | file.read(&fileStat->localHeaderOffset, 4); |
| 165 | |
| 166 | if (nameLen < 256) { |
| 167 | file.read(itemName, nameLen); |