void ModifyEntry(HfsFile workFile, HfsUpdate update) { workFile.WriteLocalEntryHeader(update); long dataStart = workFile.baseStream_.Position; // TODO: This is slow if the changes don't effect the data!! if (update.Entry.IsFile && (update.Filename != null)) { using (Stream output = workFile.GetOutputStream(update.OutEntry)) { using (Stream source = this.GetInputStream(update.Entry)) { CopyBytes(update, output, source, source.Length, true); } } } long dataEnd = workFile.baseStream_.Position; update.Entry.CompressedSize = dataEnd - dataStart; }
void WriteLocalEntryHeader(HfsUpdate update) { HfsEntry entry = update.OutEntry; // TODO: Local offset will require adjusting for multi-disk Hfs files. entry.Offset = baseStream_.Position; // TODO: Need to clear any entry flags that dont make sense or throw an exception here. if (update.Command != UpdateCommand.Copy) { } // Write the local file header WriteLEInt(HfsConstants.LocalHeaderSignature); WriteLEShort(entry.Version); WriteLEShort(entry.Flags); WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); if (!entry.HasCrc) { // Note patch address for updating CRC later. update.CrcPatchOffset = baseStream_.Position; WriteLEInt((int)0); } else { WriteLEInt(unchecked((int)entry.Crc)); } if ((entry.CompressedSize < 0) || (entry.Size < 0)) { update.SizePatchOffset = baseStream_.Position; } WriteLEInt((int)entry.CompressedSize); WriteLEInt((int)entry.Size); byte[] name = HfsConstants.ConvertToArray(entry.Flags, entry.Name); if (name.Length > 0xFFFF) { throw new HfsException("Entry name too long."); } entry.ExtraData = new byte[0]; WriteLEShort(name.Length); WriteLEShort(entry.ExtraData.Length); HfsXorCipher.XorBlockWithKey(name, HfsXorCipher.XorTruths, (int)baseStream_.Position); if (name.Length > 0) { baseStream_.Write(name, 0, name.Length); } if (entry.ExtraData.Length > 0) { baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length); } }
void CopyEntryDirect(HfsFile workFile, HfsUpdate update, ref long destinationPosition) { bool skipOver = false; if (update.Entry.Offset == destinationPosition) { skipOver = true; } if (!skipOver) { baseStream_.Position = destinationPosition; workFile.WriteLocalEntryHeader(update); destinationPosition = baseStream_.Position; } long sourcePosition = 0; const int NameLengthOffset = 26; // TODO: Add base for SFX friendly handling long entryDataOffset = update.Entry.Offset + NameLengthOffset; baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); // Clumsy way of handling retrieving the original name and extra data length for now. // TODO: Stop re-reading name and data length in CopyEntryDirect. uint nameLength = ReadLEUshort(); uint extraLength = ReadLEUshort(); sourcePosition = baseStream_.Position + nameLength + extraLength; if (skipOver) { if (update.OffsetBasedSize != -1) destinationPosition += update.OffsetBasedSize; else // TODO: Find out why this calculation comes up 4 bytes short on some entries in ODT (Office Document Text) archives. // WinHfs produces a warning on these entries: // "caution: value of lrec.csize (compressed size) changed from ..." destinationPosition += (sourcePosition - entryDataOffset) + NameLengthOffset + // Header size update.Entry.CompressedSize + GetDescriptorSize(update); } else { if (update.Entry.CompressedSize > 0) { CopyEntryDataDirect(update, baseStream_, false, ref destinationPosition, ref sourcePosition); } CopyDescriptorBytesDirect(update, baseStream_, ref destinationPosition, sourcePosition); } }
/// <summary> /// Get the size of the source descriptor for a <see cref="HfsUpdate"/>. /// </summary> /// <param name="update">The update to get the size for.</param> /// <returns>The descriptor size, zero if there isnt one.</returns> int GetDescriptorSize(HfsUpdate update) { int result = 0; return result; }
void CopyEntry(HfsFile workFile, HfsUpdate update) { workFile.WriteLocalEntryHeader(update); if (update.Entry.CompressedSize > 0) { const int NameLengthOffset = 26; long entryDataOffset = update.Entry.Offset + NameLengthOffset; // TODO: This wont work for SFX files! baseStream_.Seek(entryDataOffset, SeekOrigin.Begin); uint nameLength = ReadLEUshort(); uint extraLength = ReadLEUshort(); baseStream_.Seek(nameLength + extraLength, SeekOrigin.Current); CopyBytes(update, workFile.baseStream_, baseStream_, update.Entry.CompressedSize, false); } CopyDescriptorBytes(update, workFile.baseStream_, baseStream_); }
void CopyEntryDataDirect(HfsUpdate update, Stream stream, bool updateCrc, ref long destinationPosition, ref long sourcePosition) { long bytesToCopy = update.Entry.CompressedSize; // NOTE: Compressed size is updated elsewhere. Crc32 crc = new Crc32(); byte[] buffer = GetBuffer(); long targetBytes = bytesToCopy; long totalBytesRead = 0; int bytesRead; do { int readSize = buffer.Length; if (bytesToCopy < readSize) { readSize = (int)bytesToCopy; } stream.Position = sourcePosition; bytesRead = stream.Read(buffer, 0, readSize); if (bytesRead > 0) { if (updateCrc) { crc.Update(buffer, 0, bytesRead); } stream.Position = destinationPosition; stream.Write(buffer, 0, bytesRead); destinationPosition += bytesRead; sourcePosition += bytesRead; bytesToCopy -= bytesRead; totalBytesRead += bytesRead; } } while ((bytesRead > 0) && (bytesToCopy > 0)); if (totalBytesRead != targetBytes) { throw new HfsException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); } if (updateCrc) { update.OutEntry.Crc = crc.Value; } }
void CopyDescriptorBytesDirect(HfsUpdate update, Stream stream, ref long destinationPosition, long sourcePosition) { int bytesToCopy = GetDescriptorSize(update); while (bytesToCopy > 0) { int readSize = (int)bytesToCopy; byte[] buffer = GetBuffer(); stream.Position = sourcePosition; int bytesRead = stream.Read(buffer, 0, readSize); if (bytesRead > 0) { stream.Position = destinationPosition; stream.Write(buffer, 0, bytesRead); bytesToCopy -= bytesRead; destinationPosition += bytesRead; sourcePosition += bytesRead; } else { throw new HfsException("Unxpected end of stream"); } } }
void CopyDescriptorBytes(HfsUpdate update, Stream dest, Stream source) { int bytesToCopy = GetDescriptorSize(update); if (bytesToCopy > 0) { byte[] buffer = GetBuffer(); while (bytesToCopy > 0) { int readSize = Math.Min(buffer.Length, bytesToCopy); int bytesRead = source.Read(buffer, 0, readSize); if (bytesRead > 0) { dest.Write(buffer, 0, bytesRead); bytesToCopy -= bytesRead; } else { throw new HfsException("Unxpected end of stream"); } } } }
void CopyBytes(HfsUpdate update, Stream destination, Stream source, long bytesToCopy, bool updateCrc) { if (destination == source) { throw new InvalidOperationException("Destination and source are the same"); } // NOTE: Compressed size is updated elsewhere. Crc32 crc = new Crc32(); byte[] buffer = GetBuffer(); long targetBytes = bytesToCopy; long totalBytesRead = 0; int bytesRead; do { int readSize = buffer.Length; if (bytesToCopy < readSize) { readSize = (int)bytesToCopy; } bytesRead = source.Read(buffer, 0, readSize); if (bytesRead > 0) { if (updateCrc) { crc.Update(buffer, 0, bytesRead); } destination.Write(buffer, 0, bytesRead); bytesToCopy -= bytesRead; totalBytesRead += bytesRead; } } while ((bytesRead > 0) && (bytesToCopy > 0)); if (totalBytesRead != targetBytes) { throw new HfsException(string.Format("Failed to copy bytes expected {0} read {1}", targetBytes, totalBytesRead)); } if (updateCrc) { update.OutEntry.Crc = crc.Value; } }
void AddUpdate(HfsUpdate update) { contentsEdited_ = true; int index = FindExistingUpdate(update.Entry.Name); if (index >= 0) { if (updates_[index] == null) { updateCount_ += 1; } // Direct replacement is faster than delete and add. updates_[index] = update; } else { index = updates_.Add(update); updateCount_ += 1; updateIndex_.Add(update.Entry.Name, index); } }
void AddEntry(HfsFile workFile, HfsUpdate update) { Stream source = null; if (update.Entry.IsFile) { source = update.GetSource(); if (source == null) { source = updateDataSource_.GetSource(update.Entry, update.Filename); } } if (source != null) { using (source) { long sourceStreamLength = source.Length; if (update.OutEntry.Size < 0) { update.OutEntry.Size = sourceStreamLength; } else { // Check for errant entries. if (update.OutEntry.Size != sourceStreamLength) { throw new HfsException("Entry size/stream size mismatch"); } } workFile.WriteLocalEntryHeader(update); long dataStart = workFile.baseStream_.Position; update.OutEntry.DataOffset = dataStart; using (Stream output = workFile.GetOutputStream(update.OutEntry)) { CopyBytes(update, output, source, sourceStreamLength, true); } long dataEnd = workFile.baseStream_.Position; update.OutEntry.CompressedSize = update.OutEntry.Size = dataEnd - dataStart; } } else { workFile.WriteLocalEntryHeader(update); update.OutEntry.CompressedSize = 0; } }