bool PatchedFile::seek(int32 offset, int whence) { int32 totJump, relOffset; uint32 skipDiff, skipExtra, skipSize; relOffset = 0; skipDiff = 0; skipExtra = 0; totJump = 0; switch (whence) { case SEEK_SET: relOffset = offset - pos(); break; case SEEK_CUR: relOffset = offset; break; case SEEK_END: relOffset = (size() + offset) - pos(); break; default: error("%s: Invalid seek instruction", _patchName.c_str()); } if (relOffset == 0) return true; if (relOffset < 0) { Debug::debug(Debug::Patchr, "Seeking back to start %s", _patchName.c_str()); _file->seek(0, SEEK_SET); _ctrl->seek(0, SEEK_SET); _extra->seek(0, SEEK_SET); instrLeft = _ctrl->size() / (3 * sizeof(uint32)); readNextInst(); int p = pos() + relOffset; _pos = 0; return seek(p, SEEK_SET); } while (relOffset > 0) { if (diffCopy > 0) { skipSize = MIN(diffCopy, (uint32)relOffset); diffCopy -= skipSize; relOffset -= skipSize; skipDiff += skipSize; totJump += skipSize; } if (relOffset == 0) break; if (extraCopy > 0) { skipSize = MIN(extraCopy, (uint32)relOffset); extraCopy -= skipSize; relOffset -= skipSize; skipExtra += skipSize; } if (diffCopy == 0 && extraCopy == 0) { totJump += jump; readNextInst(); } } _diff->seek(skipDiff, SEEK_CUR); _extra->seek(skipExtra, SEEK_CUR); _file->seek(totJump, SEEK_CUR); return true; }
bool PatchedFile::load(Common::SeekableReadStream *file, const Common::String &patchName) { uint8 md5_p[16], md5_f[16]; uint32 zctrllen, zdatalen, zextralen; Common::File patch; _patchName = patchName; // Open the patch if (!patch.open(_patchName)) { error("Unable to open patchfile %s", _patchName.c_str()); return false; } // Check for appropriate signature if (patch.readUint32BE() != MKTAG('P','A','T','R')) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } // Check the version number if (patch.readUint16LE() != _kVersionMajor || patch.readUint16LE() > _kVersionMinor) { error("%s has a wrong version number (must be major = %d, minor <= %d)", _patchName.c_str(), _kVersionMajor, _kVersionMinor); return false; } _flags = patch.readUint32LE(); // Check if the file to patch match Common::computeStreamMD5(*file, md5_f, _kMd5size); file->seek(0, SEEK_SET); patch.read(md5_p, 16); if (memcmp(md5_p, md5_f, 16) != 0 || (uint32)file->size() != patch.readUint32LE()) { Debug::debug(Debug::Patchr,"%s targets a different file", _patchName.c_str()); return false; } // Read lengths from header _newSize = patch.readUint32LE(); zctrllen = patch.readUint32LE(); zdatalen = patch.readUint32LE(); zextralen = patch.readUint32LE(); patch.close(); // Opens ctrl, diff and extra substreams Common::File *tmp; tmp = new Common::File; tmp->open(_patchName); _ctrl = new Common::SeekableSubReadStream(tmp, _kHeaderSize, _kHeaderSize + zctrllen, DisposeAfterUse::YES); if (_flags & FLAG_COMPRESS_CTRL) _ctrl = Common::wrapCompressedReadStream(_ctrl); //ctrl stream sanity checks if (_ctrl->size() % (3 * sizeof(uint32)) != 0) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } instrLeft = _ctrl->size() / (3 * sizeof(uint32)); tmp = new Common::File; tmp->open(_patchName); _diff = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen, _kHeaderSize + zctrllen + zdatalen, DisposeAfterUse::YES); _diff = Common::wrapCompressedReadStream(_diff); if (_flags & FLAG_MIX_DIFF_EXTRA) _extra = _diff; else { tmp = new Common::File; tmp->open(_patchName); _extra = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen + zdatalen, _kHeaderSize + zctrllen + zdatalen + zextralen, DisposeAfterUse::YES); _extra = Common::wrapCompressedReadStream(_extra); } _file = file; readNextInst(); return true; }
uint32 PatchedFile::read(void *dataPtr, uint32 dataSize) { uint32 readSize, diffRead, toRead, rd; byte *data = (byte*)dataPtr; toRead = dataSize; while (toRead > 0) { // Read data from original file and apply the differences if (diffCopy > 0) { readSize = MIN(toRead, diffCopy); rd = _file->read(data, readSize); if (_file->err() || rd != readSize) error("%s: Corrupted patchfile", _patchName.c_str()); toRead -= readSize; diffCopy -= readSize; //Read data from diff as blocks of size _kDiffBufferSize, // then xor original data with them in groups of 4 or 8 // bytes, depending on the architecture while (readSize > 0) { diffRead = MIN(readSize, _kDiffBufferSize); rd = _diff->read(diffBuffer, diffRead); if (_diff->err() || rd != diffRead) error("%s: Corrupted patchfile", _patchName.c_str()); #ifdef SCUMM_64BITS for (uint32 i = 0; i < diffRead/8; ++i) *((uint64*)data + i) ^= *((uint64*)diffBuffer + i); for (uint32 i = diffRead - diffRead % 8; i < diffRead; ++i) data[i] ^= diffBuffer[i]; #else for (uint32 i = 0; i < diffRead/4; ++i) *((uint32*)data + i) ^= *((uint32*)diffBuffer + i); for (uint32 i = diffRead - diffRead % 4; i < diffRead; ++i) data[i] ^= diffBuffer[i]; #endif readSize -= diffRead; data += diffRead; } } if (toRead == 0) break; // Read data from extra if (extraCopy > 0) { readSize = MIN(toRead, extraCopy); rd = _extra->read(data, readSize); if (_extra->err() || rd != readSize) error("%s: Corrupted patchfile", _patchName.c_str()); data += readSize; toRead -= readSize; extraCopy -= readSize; } // Jump and read next instructions if (diffCopy == 0 && extraCopy == 0) { if (jump != 0) _file->seek(jump, SEEK_CUR); //If there aren't new instructions, breaks here if (!readNextInst()) break; } } _pos += dataSize - toRead; return (dataSize - toRead); }
uint32 PatchedFile::read(void *dataPtr, uint32 dataSize) { uint32 readSize, diffRead, toRead, rd; byte *data = (byte*)dataPtr; toRead = dataSize; while (toRead > 0) { // Read data from original file and apply the differences if (_diffCopy > 0) { readSize = MIN(toRead, _diffCopy); rd = _file->read(data, readSize); if (_file->err() || rd != readSize) error("%s: Corrupted patchfile", _patchName.c_str()); toRead -= readSize; _diffCopy -= readSize; //Read data from diff as blocks of size _kDiffBufferSize, // then xor original data with them in groups of 4 bytes while (readSize > 0) { diffRead = MIN(readSize, _kDiffBufferSize); rd = _diff->read(_diffBuffer, diffRead); if (_diff->err() || rd != diffRead) error("%s: Corrupted patchfile", _patchName.c_str()); for (uint32 i = 0; i < diffRead / 4; ++i) WRITE_UINT32((uint32 *)data + i, READ_UINT32((uint32 *)data + i) ^ READ_UINT32((uint32 *)_diffBuffer + i)); for (uint32 i = diffRead - diffRead % 4; i < diffRead; ++i) data[i] ^= _diffBuffer[i]; readSize -= diffRead; data += diffRead; } } if (toRead == 0) break; // Read data from extra if (_extraCopy > 0) { readSize = MIN(toRead, _extraCopy); rd = _extra->read(data, readSize); if (_extra->err() || rd != readSize) error("%s: Corrupted patchfile", _patchName.c_str()); data += readSize; toRead -= readSize; _extraCopy -= readSize; } // Jump and read next instructions if (_diffCopy == 0 && _extraCopy == 0) { if (_jump != 0) _file->seek(_jump, SEEK_CUR); //If there aren't new instructions, breaks here if (!readNextInst()) break; } } _pos += dataSize - toRead; return (dataSize - toRead); }