size_t ZipFile::findCentralDirectoryEnd(SeekableReadStream &zip) { const size_t uSizeFile = zip.size(); const size_t uMaxBack = MIN<size_t>(0xFFFF, uSizeFile); // Maximum size of global comment byte buf[BUFREADCOMMENT + 4]; size_t uPosFound = 0; size_t uBackRead = 4; while ((uPosFound == 0) && (uBackRead < uMaxBack)) { uBackRead = MIN<size_t>(uMaxBack, uBackRead + BUFREADCOMMENT); const size_t uReadPos = uSizeFile - uBackRead; const size_t uReadSize = MIN<size_t>(BUFREADCOMMENT + 4, uSizeFile - uReadPos); try { zip.seek(uReadPos); } catch (...) { return 0; } if (zip.read(buf, uReadSize) != uReadSize) { uPosFound = 0; break; } for (size_t i = (uReadSize - 3); i-- > 0; ) if (READ_LE_UINT32(buf + i) == 0x06054B50) { uPosFound = uReadPos + i; break; } } return uPosFound; }
String NEResources::getResourceString(SeekableReadStream &exe, uint32 offset) { uint32 curPos = exe.pos(); if (!exe.seek(offset)) { exe.seek(curPos); return ""; } uint8 length = exe.readByte(); String string; for (uint16 i = 0; i < length; i++) string += (char)exe.readByte(); exe.seek(curPos); return string; }
void StreamTokenizer::nextChunk(SeekableReadStream &stream) { skipChunk(stream); uint32 c = stream.readChar(); if (c == ReadStream::kEOF) return; if (!isIn(c, _chunkEnds)) stream.seek(-1, SeekableReadStream::kOriginCurrent); }
bool StreamTokenizer::isChunkEnd(SeekableReadStream &stream) { if (stream.eos()) return true; bool chunkEnd = isIn(stream.readByte(), _chunkEnds); stream.seek(-1, SEEK_CUR); return chunkEnd; }
bool StreamTokenizer::isChunkEnd(SeekableReadStream &stream) { uint32 c = stream.readChar(); if (c == ReadStream::kEOF) return true; bool chunkEnd = isIn(c, _chunkEnds); stream.seek(-1, SeekableReadStream::kOriginCurrent); return chunkEnd; }
void StreamTokenizer::skipChunk(SeekableReadStream &stream) { assert(!_chunkEnds.empty()); uint32 c; while ((c = stream.readChar()) != ReadStream::kEOF) { if (isIn(c, _chunkEnds)) { stream.seek(-1, SeekableReadStream::kOriginCurrent); break; } } }
void printDataHex(SeekableReadStream &stream, size_t size) { size_t pos = stream.pos(); size = MIN<size_t>(stream.size() - pos, size); if (size == 0) return; uint32 offset = 0; byte rowData[16]; while (size > 0) { // At max 16 bytes printed per row uint32 n = MIN<size_t>(size, 16); if (stream.read(rowData, n) != n) throw Exception(kReadError); // Print an offset std::fprintf(stderr, "%08X ", offset); // 2 "blobs" of each 8 bytes per row for (uint32 i = 0; i < 2; i++) { for (uint32 j = 0; j < 8; j++) { uint32 m = i * 8 + j; if (m < n) // Print the data std::fprintf(stderr, "%02X ", rowData[m]); else // Last row, data count not aligned to 16 std::fprintf(stderr, " "); } // Separate the blobs by an extra space std::fprintf(stderr, " "); } std::fprintf(stderr, "|"); // If the data byte is a printable character, print it. If not, substitute a '.' for (uint32 i = 0; i < n; i++) std::fprintf(stderr, "%c", std::isprint(rowData[i]) ? rowData[i] : '.'); std::fprintf(stderr, "|\n"); size -= n; offset += n; } // Seek back stream.seek(pos); }
void StreamTokenizer::skipChunk(SeekableReadStream &stream) { assert(!_chunkEnds.empty()); while (!stream.eos() && !stream.err()) { if (isIn(stream.readByte(), _chunkEnds)) { stream.seek(-1, SEEK_CUR); break; } } if (stream.err()) throw Exception(kReadError); }
void StreamTokenizer::nextChunk(SeekableReadStream &stream) { skipChunk(stream); byte c = stream.readByte(); if (stream.eos() || stream.err()) return; if (!isIn(c, _chunkEnds)) stream.seek(-1, SEEK_CUR); else if (stream.pos() == stream.size()) // This actually the last character, read one more byte to properly set the EOS state stream.readByte(); }
size_t searchBackwards(SeekableReadStream &haystack, const byte *needle, size_t needleSize, size_t maxReadBack) { if (needleSize == 0 || maxReadBack == 0) return SIZE_MAX; assert(maxReadBack >= needleSize); static const size_t kReadBufferSize = 0x400; const size_t sizeFile = haystack.size(); const size_t maxBack = MIN<size_t>(maxReadBack, sizeFile); ScopedArray<byte> buf(new byte[kReadBufferSize + needleSize]); size_t backRead = needleSize; while (backRead < maxBack) { backRead = MIN<size_t>(maxBack, backRead + kReadBufferSize); const size_t readPos = sizeFile - backRead; const size_t readSize = MIN<size_t>(kReadBufferSize + needleSize, sizeFile - readPos); try { haystack.seek(readPos); } catch (...) { break; } if (haystack.read(buf.get(), readSize) != readSize) break; for (size_t i = (readSize - (needleSize - 1)); i-- > 0; ) if (!memcmp(buf.get() + i, needle, needleSize)) return readPos + i; } return SIZE_MAX; }
void FoxPro::loadFields(SeekableReadStream &dbf, uint32 recordSize) { // Read all field descriptions, 0x0D is the end marker uint32 fieldsLength = 0; while (!dbf.eos() && (dbf.readByte() != 0x0D)) { Field field; dbf.seek(-1, SeekableReadStream::kOriginCurrent); field.name = readStringFixed(dbf, kEncodingASCII, 11); field.type = (Type) dbf.readByte(); field.offset = dbf.readUint32LE(); field.size = dbf.readByte(); field.decimals = dbf.readByte(); field.flags = dbf.readByte(); field.autoIncNext = dbf.readUint32LE(); field.autoIncStep = dbf.readByte(); dbf.skip(8); // Reserved if (field.offset != (fieldsLength + 1)) throw Exception("Field offset makes no sense (%d != %d)", field.offset, fieldsLength + 1); if (field.type == kTypeMemo) _hasMemo = true; fieldsLength += field.size; _fields.push_back(field); } if (recordSize != (fieldsLength + 1)) throw Exception("Length of all fields does not equal the record size"); }
void ZipFile::getFileProperties(SeekableReadStream &zip, const IFile &file, uint16 &compMethod, uint32 &compSize, uint32 &realSize) const { zip.seek(file.offset); uint32 tag = zip.readUint32LE(); if (tag != 0x04034B50) throw Exception("Unknown ZIP record %08X", tag); zip.skip(4); compMethod = zip.readUint16LE(); zip.skip(8); compSize = zip.readUint32LE(); realSize = zip.readUint32LE(); uint16 nameLength = zip.readUint16LE(); uint16 extraLength = zip.readUint16LE(); zip.skip(nameLength); zip.skip(extraLength); }
void ZipFile::load(SeekableReadStream &zip) { size_t endPos = findCentralDirectoryEnd(zip); if (endPos == 0) throw Exception("End of central directory record not found"); zip.seek(endPos); zip.skip(4); // Header, already checked uint16 curDisk = zip.readUint16LE(); uint16 centralDirDisk = zip.readUint16LE(); uint16 curDiskDirs = zip.readUint16LE(); uint16 totalDirs = zip.readUint16LE(); if ((curDisk != 0) || (curDisk != centralDirDisk) || (curDiskDirs != totalDirs)) throw Exception("Unsupported multi-disk ZIP file"); zip.skip(4); // Size of central directory uint32 centralDirPos = zip.readUint32LE(); zip.seek(centralDirPos); uint32 tag = zip.readUint32LE(); if (tag != 0x02014B50) throw Exception("Unknown ZIP record %08X", tag); _iFiles.reserve(totalDirs); while (tag == 0x02014B50) { File file; IFile iFile; zip.skip(20); iFile.size = zip.readUint32LE(); uint16 nameLength = zip.readUint16LE(); uint16 extraLength = zip.readUint16LE(); uint16 commentLength = zip.readUint16LE(); uint16 diskNum = zip.readUint16LE(); if (diskNum != 0) throw Exception("Unsupported multi-disk ZIP file"); zip.skip(6); // File attributes iFile.offset = zip.readUint32LE(); file.name = readStringFixed(zip, kEncodingASCII, nameLength).toLower(); zip.skip(extraLength); zip.skip(commentLength); tag = zip.readUint32LE(); if ((tag != 0x02014B50) && (tag != 0x06054B50)) throw Exception("Unknown ZIP record %08X", tag); // Ignore empty file names if (!file.name.empty()) { // HACK: Skip any filename with a trailing slash because it's // a directory. The proper solution would be to interpret the // file attributes. if (*(--file.name.end()) != '/') { file.index = _iFiles.size(); _files.push_back(file); _iFiles.push_back(iFile); } } } }
UString StreamTokenizer::getToken(SeekableReadStream &stream) { // Init bool chunkEnd = false; bool inQuote = false; uint32 separator = 0xFFFFFFFF; UString token; // Run through the stream, character by character uint32 c; while ((c = stream.readChar()) != ReadStream::kEOF) { if (isIn(c, _chunkEnds)) { // This is a end character, seek back and break stream.seek(-1, SeekableReadStream::kOriginCurrent); chunkEnd = true; break; } if (isIn(c, _quotes)) { // This is a quote character, set state inQuote = !inQuote; continue; } if (!inQuote && isIn(c, _separators)) { // We're not in a quote and this is a separator if (!token.empty()) { // We have a token separator = c; break; } // We don't yet have a token, let the consecutive separator rule decide what to do if (_conSepRule == kRuleHeed) { // We heed every separator separator = c; break; } if ((_conSepRule == kRuleIgnoreSame) && (separator != 0xFFFFFFFF) && (separator != c)) { // We ignore only consecutive separators that are the same separator = c; break; } // We ignore all consecutive separators separator = c; continue; } if (isIn(c, _ignores)) // This is a character to be ignored, do so continue; // A normal character, add it to our token token += c; } // Is the string actually empty? if (!token.empty() && (*token.begin() == '\0')) token.clear(); if (!chunkEnd && (_conSepRule != kRuleHeed)) { // We have to look for consecutive separators while ((c = stream.readChar()) != ReadStream::kEOF) { // Use the rule to determine when we should abort skipping consecutive separators if (((_conSepRule == kRuleIgnoreSame) && (c != separator)) || ((_conSepRule == kRuleIgnoreAll ) && !isIn(c, _separators))) { stream.seek(-1, SeekableReadStream::kOriginCurrent); break; } } } // And return the token return token; }