Inbox because ZipArchiveMember >> #extractInDirectory:overwrite: is a breaking change that *hypothetically* could break any of your automated extraction workflows, even though I do not really expect that. Please check. If no one objects, I will move this to the Trunk after one week or so. :-)<br>
<br>
Best,<br>
Christoph<br>
<br>
<font color="#808080">---<br>
</font><font color="#808080"><i>Sent from </i></font><font color="#808080"><i><a href="https://github.com/hpi-swa-lab/squeak-inbox-talk"><u><font color="#808080">Squeak Inbox Talk</font></u></a></i></font><br>
<br>
On 2022-01-03T18:07:40+00:00, commits@source.squeak.org wrote:<br>
<br>
> A new version of Compression was added to project The Inbox:<br>
> http://source.squeak.org/inbox/Compression-ct.56.mcz<br>
> <br>
> ==================== Summary ====================<br>
> <br>
> Name: Compression-ct.56<br>
> Author: ct<br>
> Time: 3 January 2022, 7:07:37.018703 pm<br>
> UUID: af841abb-44db-044e-b8d8-a6e8231c690e<br>
> Ancestors: Compression-dtl.55<br>
> <br>
> Makes the Compression package 100% multilingual.<br>
> Rewrites ZipArchiveMember >> #extractInDirectory:overwrite: to use new UIManager >> #chooseFromLabeledValues:title: and avoid infinite loops if the user closes the dialog window or #valueSuppressingAllMessages is used.<br>
> Also includes some recategorizations.<br>
> <br>
> =============== Diff against Compression-dtl.55 ===============<br>
> <br>
> Item was changed:<br>
>   ----- Method: Archive>>writeToFileNamed: (in category 'archive operations') -----<br>
>   writeToFileNamed: aFileName<br>
>       | stream |<br>
>       "Catch attempts to overwrite existing zip file"<br>
>       (self canWriteToFileNamed: aFileName)<br>
> +         ifFalse: [ ^self error: ('{1} is needed by one or more members in this archive' translated format: {aFileName}) ].<br>
> -         ifFalse: [ ^self error: (aFileName, ' is needed by one or more members in this archive') ].<br>
>       stream := StandardFileStream forceNewFileNamed: aFileName.<br>
>       self writeTo: stream.<br>
>       stream close.!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ArchiveMember class>>newDirectoryNamed: (in category 'instance creation') -----<br>
> - ----- Method: ArchiveMember class>>newDirectoryNamed: (in category 'as yet unclassified') -----<br>
>   newDirectoryNamed: aString<br>
>       self subclassResponsibility!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ArchiveMember class>>newFromFile: (in category 'instance creation') -----<br>
> - ----- Method: ArchiveMember class>>newFromFile: (in category 'as yet unclassified') -----<br>
>   newFromFile: aFileName<br>
>       self subclassResponsibility!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ArchiveMember class>>newFromString: (in category 'instance creation') -----<br>
> - ----- Method: ArchiveMember class>>newFromString: (in category 'as yet unclassified') -----<br>
>   newFromString: aString<br>
>       self subclassResponsibility!<br>
> <br>
> Item was changed:<br>
> + ----- Method: CRCError>>isResumable (in category 'priv handling') -----<br>
> - ----- Method: CRCError>>isResumable (in category 'as yet unclassified') -----<br>
>   isResumable<br>
>       ^true!<br>
> <br>
> Item was changed:<br>
> + ----- Method: CompressedSourceStream class>>on: (in category 'instance creation') -----<br>
> - ----- Method: CompressedSourceStream class>>on: (in category 'as yet unclassified') -----<br>
>   on: aFile<br>
>       ^ self basicNew openOn: aFile!<br>
> <br>
> Item was changed:<br>
>   ----- Method: CompressedSourceStream>>binary (in category 'open/close') -----<br>
>   binary<br>
> +     self error: 'Compressed source files are ascii to the user (though binary underneath)' translated!<br>
> -     self error: 'Compressed source files are ascii to the user (though binary underneath)'!<br>
> <br>
> Item was changed:<br>
>   ----- Method: CompressedSourceStream>>position: (in category 'access') -----<br>
>   position: newPosition<br>
>       | compressedBuffer newSegmentIndex |<br>
>       newPosition > endOfFile ifTrue:<br>
> +         [self error: 'Attempt to position beyond the end of file' translated].<br>
> -         [self error: 'Attempt to position beyond the end of file'].<br>
>       newSegmentIndex := (newPosition // segmentSize) + 1.<br>
>       newSegmentIndex ~= segmentIndex ifTrue:<br>
>           [self flush.<br>
>           segmentIndex := newSegmentIndex.<br>
>           newSegmentIndex > nSegments ifTrue:<br>
> +             [self error: 'file size limit exceeded' translated].<br>
> -             [self error: 'file size limit exceeded'].<br>
>           segmentFile position: (segmentTable at: segmentIndex).<br>
>           (segmentTable at: segmentIndex+1) = 0<br>
>               ifTrue:<br>
>               [newPosition ~= endOfFile ifTrue:<br>
> +                 [self error: 'Internal logic error' translated].<br>
> -                 [self error: 'Internal logic error'].<br>
>               collection size = segmentSize ifFalse:<br>
> +                 [self error: 'Internal logic error' translated].<br>
> -                 [self error: 'Internal logic error'].<br>
>               "just leave garbage beyond end of file"]<br>
>               ifFalse:<br>
>               [compressedBuffer := segmentFile next: ((segmentTable at: segmentIndex+1) - (segmentTable at: segmentIndex)).<br>
>               collection :=  (GZipReadStream on: compressedBuffer) upToEnd asString].<br>
>           readLimit := collection size min: endOfFile - self segmentOffset].<br>
> +     position := newPosition \\ segmentSize.!<br>
> -     position := newPosition \\ segmentSize.<br>
> -     !<br>
> <br>
> Item was changed:<br>
>   ----- Method: CompressedSourceStream>>readHeaderInfo (in category 'open/close') -----<br>
>   readHeaderInfo<br>
>       | valid a b |<br>
>       segmentFile position: 0.<br>
>       segmentSize := segmentFile nextNumber: 4.<br>
>       nSegments := segmentFile nextNumber: 4.<br>
>       endOfFile := segmentFile nextNumber: 4.<br>
>       segmentFile size < (nSegments+1 + 3 * 4) ifTrue: "Check for reasonable segment info"<br>
> +         [self error: 'This file is not in valid compressed source format' translated].<br>
> -         [self error: 'This file is not in valid compressed source format'].<br>
>       segmentTable := (1 to: nSegments+1) collect: [:x | segmentFile nextNumber: 4].<br>
>       segmentTable first ~= self firstSegmentLoc ifTrue:<br>
> +         [self error: 'This file is not in valid compressed source format' translated].<br>
> -         [self error: 'This file is not in valid compressed source format'].<br>
>       valid := true.<br>
>       1 to: nSegments do:  "Check that segment offsets are ascending"<br>
>           [:i | a := segmentTable at: i.  b := segmentTable at: i+1.<br>
>           (a = 0 and: [b ~= 0]) ifTrue: [valid := false].<br>
>           (a ~= 0 and: [b ~= 0]) ifTrue: [b <= a ifTrue: [valid := false]]].<br>
>       valid ifFalse:<br>
> +         [self error: 'This file is not in valid compressed source format' translated].<br>
> -         [self error: 'This file is not in valid compressed source format'].<br>
>       dirty := false.<br>
>       self position: 0.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: CompressedSourceStream>>segmentSize:maxSize: (in category 'private') -----<br>
>   segmentSize: segSize maxSize: maxSize<br>
>       "Note that this method can be called after the initial open, provided that no<br>
>       writing has yet taken place.  This is how to override the default segmentation."<br>
> +     self size = 0 ifFalse: [self error: 'Cannot set parameters after the first write' translated].<br>
> -     self size = 0 ifFalse: [self error: 'Cannot set parameters after the first write'].<br>
>       segmentFile position: 0.<br>
>       segmentFile nextNumber: 4 put: (segmentSize := segSize).<br>
>       segmentFile nextNumber: 4 put: (nSegments := maxSize // segSize + 2).<br>
>       segmentFile nextNumber: 4 put: (endOfFile := 0).<br>
>       segmentTable := Array new: nSegments+1 withAll: 0.<br>
>       segmentTable at: 1 put: self firstSegmentLoc.  "Loc of first segment, always."<br>
>       segmentTable do: [:i | segmentFile nextNumber: 4 put: i].<br>
>       segmentIndex := 1.<br>
>       collection := String new: segmentSize.<br>
>       writeLimit := segmentSize.<br>
>       readLimit := 0.<br>
>       position := 0.<br>
>       endOfFile := 0.<br>
> +     self writeSegment.!<br>
> -     self writeSegment.<br>
> - !<br>
> <br>
> Item was changed:<br>
>   ----- Method: DeflateStream>>validateMatchAt:from:to: (in category 'deflating') -----<br>
>   validateMatchAt: pos from: startPos to: endPos<br>
>       | here |<br>
>       here := pos.<br>
>       startPos+1 to: endPos+1 do:[:i|<br>
>           (collection at: i) = (collection at: (here := here + 1))<br>
> +             ifFalse:[^self error: 'Not a match' translated]].<br>
> -             ifFalse:[^self error:'Not a match']].<br>
>       ^true!<br>
> <br>
> Item was changed:<br>
>   ----- Method: GZipReadStream class>>saveContents: (in category 'fileIn/Out') -----<br>
>   saveContents: fullFileName<br>
>       "Save the contents of a gzipped file"<br>
>       | zipped buffer unzipped newName |<br>
>       newName := fullFileName copyUpToLast: FileDirectory extensionDelimiter.<br>
>       unzipped := FileStream newFileNamed: newName.<br>
>       unzipped binary.<br>
>       zipped := GZipReadStream on: (FileStream readOnlyFileNamed: fullFileName).<br>
>       buffer := ByteArray new: 50000.<br>
> +     ('Extracting {1}' translated format: {fullFileName})<br>
> -     'Extracting ' , fullFileName<br>
>           displayProgressFrom: 0<br>
>           to: zipped sourceStream size<br>
>           during: <br>
>               [:bar | <br>
>               [zipped atEnd]<br>
>                   whileFalse: <br>
>                       [bar value: zipped sourceStream position.<br>
>                       unzipped nextPutAll: (zipped nextInto: buffer)].<br>
>               zipped close.<br>
>               unzipped close].<br>
>       ^ newName!<br>
> <br>
> Item was changed:<br>
>   ----- Method: GZipReadStream>>on:from:to: (in category 'initialize') -----<br>
>   on: aCollection from: firstIndex to: lastIndex<br>
>       "Check the header of the GZIP stream."<br>
>       | method magic flags length |<br>
>       super on: aCollection from: firstIndex to: lastIndex.<br>
>       crc := 16rFFFFFFFF.<br>
>       magic := self nextBits: 16.<br>
>       (magic = GZipMagic) <br>
> +         ifFalse:[^self error: 'Not a GZipped stream' translated].<br>
> -         ifFalse:[^self error:'Not a GZipped stream'].<br>
>       method := self nextBits: 8.<br>
>       (method = GZipDeflated)<br>
> +         ifFalse:[^self error: 'Bad compression method' translated].<br>
> -         ifFalse:[^self error:'Bad compression method'].<br>
>       flags := self nextBits: 8.<br>
>       (flags anyMask: GZipEncryptFlag) <br>
> +         ifTrue:[^self error: 'Cannot decompress encrypted stream' translated].<br>
> -         ifTrue:[^self error:'Cannot decompress encrypted stream'].<br>
>       (flags anyMask: GZipReservedFlags)<br>
> +         ifTrue:[^self error: 'Cannot decompress stream with unknown flags' translated].<br>
> -         ifTrue:[^self error:'Cannot decompress stream with unknown flags'].<br>
>       "Ignore stamp, extra flags, OS type"<br>
>       self nextBits: 16; nextBits: 16. "stamp"<br>
>       self nextBits: 8. "extra flags"<br>
>       self nextBits: 8. "OS type"<br>
>       (flags anyMask: GZipContinueFlag) "Number of multi-part archive - ignored"<br>
>           ifTrue:[self nextBits: 16]. <br>
>       (flags anyMask: GZipExtraField) "Extra fields - ignored"<br>
>           ifTrue:[    length := self nextBits: 16.<br>
>                   1 to: length do:[:i| self nextBits: 8]].<br>
>       (flags anyMask: GZipNameFlag) "Original file name - ignored"<br>
>           ifTrue:[[(self nextBits: 8) = 0] whileFalse].<br>
>       (flags anyMask: GZipCommentFlag) "Comment - ignored"<br>
> +         ifTrue:[[(self nextBits: 8) = 0] whileFalse].!<br>
> -         ifTrue:[[(self nextBits: 8) = 0] whileFalse].<br>
> - !<br>
> <br>
> Item was changed:<br>
>   ----- Method: GZipReadStream>>verifyCrc (in category 'crc') -----<br>
>   verifyCrc<br>
>       | stored |<br>
>       stored := 0.<br>
>       0 to: 24 by: 8 do: [ :i |<br>
> +         sourcePos >= sourceLimit ifTrue: [ ^ self crcError: 'No checksum (proceed to ignore)' translated ].<br>
> -         sourcePos >= sourceLimit ifTrue: [ ^ self crcError: 'No checksum (proceed to ignore)' ].<br>
>           stored := stored + (self nextByte bitShift: i) ].<br>
>       stored := stored bitXor: 16rFFFFFFFF.<br>
> +     stored = crc ifFalse: [ ^ self crcError: 'Wrong checksum (proceed to ignore)' translated ].<br>
> -     stored = crc ifFalse: [ ^ self crcError: 'Wrong checksum (proceed to ignore)' ].<br>
>       ^stored!<br>
> <br>
> Item was changed:<br>
> + ----- Method: GZipSurrogateStream class>>newFileNamed:inDirectory: (in category 'instance creation') -----<br>
> - ----- Method: GZipSurrogateStream class>>newFileNamed:inDirectory: (in category 'as yet unclassified') -----<br>
>   newFileNamed: fName inDirectory: aDirectory<br>
>   <br>
>       ^self new newFileNamed: fName inDirectory: aDirectory!<br>
> <br>
> Item was changed:<br>
>   ----- Method: InflateStream>>decodeDynamicTable:from: (in category 'huffman trees') -----<br>
>   decodeDynamicTable: nItems from: aHuffmanTable<br>
>       "Decode the code length of the literal/length and distance table<br>
>       in a block compressed with dynamic huffman trees"<br>
>       | values index value repCount theValue |<br>
>       values := Array new: nItems.<br>
>       index := 1.<br>
>       theValue := 0.<br>
>       [index <= nItems] whileTrue:[<br>
>           value := self decodeValueFrom: aHuffmanTable.<br>
>           value < 16 ifTrue:[<br>
>               "Immediate values"<br>
>               theValue := value.<br>
>               values at: index put: value.<br>
>               index := index+1.<br>
>           ] ifFalse:[<br>
>               "Repeated values"<br>
>               value = 16 ifTrue:[<br>
>                   "Repeat last value"<br>
>                   repCount := (self nextBits: 2) + 3.<br>
>               ] ifFalse:[<br>
>                   "Repeat zero value"<br>
>                   theValue := 0.<br>
>                   value = 17 <br>
>                       ifTrue:[repCount := (self nextBits: 3) + 3]<br>
>                       ifFalse:[value = 18 <br>
>                                   ifTrue:[repCount := (self nextBits: 7) + 11]<br>
> +                                 ifFalse:[^self error: 'Invalid bits tree value' translated]]].<br>
> -                                 ifFalse:[^self error:'Invalid bits tree value']]].<br>
>               0 to: repCount-1 do:[:i| values at: index+i put: theValue].<br>
>               index := index + repCount].<br>
>       ].<br>
>       ^values!<br>
> <br>
> Item was changed:<br>
>   ----- Method: InflateStream>>decodeValueFrom: (in category 'inflating') -----<br>
>   decodeValueFrom: table<br>
>       "Decode the next value in the receiver using the given huffman table."<br>
>       | bits bitsNeeded tableIndex value |<br>
>       bitsNeeded := (table at: 1) bitShift: -24.    "Initial bits needed"<br>
>       tableIndex := 2.                            "First real table"<br>
>       [bits := self nextSingleBits: bitsNeeded.    "Get bits"<br>
>       value := table at: (tableIndex + bits).        "Lookup entry in table"<br>
>       (value bitAnd: 16r3F000000) = 0]             "Check if it is a non-leaf node"<br>
>           whileFalse:["Fetch sub table"<br>
>               tableIndex := value bitAnd: 16rFFFF.    "Table offset in low 16 bit"<br>
>               bitsNeeded := (value bitShift: -24) bitAnd: 255. "Additional bits in high 8 bit"<br>
> +             bitsNeeded > MaxBits ifTrue:[^self error: 'Invalid huffman table entry' translated]].<br>
> -             bitsNeeded > MaxBits ifTrue:[^self error:'Invalid huffman table entry']].<br>
>       ^value!<br>
> <br>
> Item was changed:<br>
>   ----- Method: InflateStream>>proceedStoredBlock (in category 'inflating') -----<br>
>   proceedStoredBlock<br>
>       "Proceed decompressing a stored (e.g., uncompressed) block"<br>
>       | length decoded |<br>
>       "Literal table must be nil for a stored block"<br>
> +     litTable == nil ifFalse:[^self error: 'Bad state' translated].<br>
> -     litTable == nil ifFalse:[^self error:'Bad state'].<br>
>       length := distTable.<br>
>       [length > 0 and:[readLimit < collection size and:[sourcePos < sourceLimit]]] <br>
>           whileTrue:[<br>
>               collection at: (readLimit := readLimit + 1) put: <br>
>                   (source at: (sourcePos := sourcePos + 1)).<br>
>               length := length - 1].<br>
>       length = 0 ifTrue:[state := state bitAnd: StateNoMoreData].<br>
>       decoded := length - distTable.<br>
>       distTable := length.<br>
>       ^decoded!<br>
> <br>
> Item was changed:<br>
>   ----- Method: InflateStream>>processStoredBlock (in category 'inflating') -----<br>
>   processStoredBlock<br>
>       | chkSum length |<br>
>       "Skip to byte boundary"<br>
>       self nextBits: (bitPos bitAnd: 7).<br>
>       length := self nextBits: 16.<br>
>       chkSum := self nextBits: 16.<br>
>       (chkSum bitXor: 16rFFFF) = length<br>
> +         ifFalse:[^self error: 'Bad block length' translated].<br>
> -         ifFalse:[^self error:'Bad block length'].<br>
>       litTable := nil.<br>
>       distTable := length.<br>
>       state := state bitOr: BlockProceedBit.<br>
>       ^self proceedStoredBlock!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZLibReadStream>>on:from:to: (in category 'initialize') -----<br>
>   on: aCollection from: firstIndex to: lastIndex<br>
>       "Check the header of the ZLib stream."<br>
>       | method byte |<br>
>       super on: aCollection from: firstIndex to: lastIndex.<br>
>       crc := 1.<br>
>       method := self nextBits: 8.<br>
> +     (method bitAnd: 15) = 8 ifFalse:[^self error: 'Unknown compression method' translated].<br>
> +     (method bitShift: -4) + 8 > 15 ifTrue:[^self error: 'Invalid window size' translated].<br>
> -     (method bitAnd: 15) = 8 ifFalse:[^self error:'Unknown compression method'].<br>
> -     (method bitShift: -4) + 8 > 15 ifTrue:[^self error:'Invalid window size'].<br>
>       byte := self nextBits: 8.<br>
> +     (method bitShift: 8) + byte \\ 31 = 0 ifFalse:[^self error: 'Incorrect header' translated].<br>
> +     (byte anyMask: 32) ifTrue:[^self error: 'Need preset dictionary' translated].!<br>
> -     (method bitShift: 8) + byte \\ 31 = 0 ifFalse:[^self error:'Incorrect header'].<br>
> -     (byte anyMask: 32) ifTrue:[^self error:'Need preset dictionary'].<br>
> - !<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZLibReadStream>>verifyCrc (in category 'crc') -----<br>
>   verifyCrc<br>
>       | stored |<br>
>       stored := 0.<br>
>       24 to: 0 by: -8 do: [ :i |<br>
> +         sourcePos >= sourceLimit ifTrue: [ ^ self crcError: 'No checksum (proceed to ignore)' translated ].<br>
> -         sourcePos >= sourceLimit ifTrue: [ ^ self crcError: 'No checksum (proceed to ignore)' ].<br>
>           stored := stored + (self nextByte bitShift: i) ].<br>
> +     stored = crc ifFalse: [ ^ self crcError: 'Wrong checksum (proceed to ignore)' translated ].<br>
> -     stored = crc ifFalse: [ ^ self crcError: 'Wrong checksum (proceed to ignore)' ].<br>
>       ^stored!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive class>>findEndOfCentralDirectoryFrom: (in category 'constants') -----<br>
>   findEndOfCentralDirectoryFrom: stream<br>
>       "Seek in the given stream to the end, then read backwards until we find the<br>
>       signature of the central directory record. Leave the file positioned right<br>
>       before the signature.<br>
>   <br>
>       Answers the file position of the EOCD, or 0 if not found."<br>
>   <br>
>       | data fileLength seekOffset pos maxOffset |<br>
>       stream setToEnd.<br>
>       fileLength := stream position.<br>
>       "If the file length is less than 18 for the EOCD length plus 4 for the signature, we have a problem"<br>
> +     fileLength < 22 ifTrue: [^ self error: ('file is too short: {1}' translated format: {stream name})].<br>
> -     fileLength < 22 ifTrue: [^ self error: 'file is too short: ', stream name].<br>
>       <br>
>       seekOffset := 0.<br>
>       pos := 0.<br>
>       data := ByteArray new: 4100.<br>
>       maxOffset := 40960 min: fileLength.    "limit search range to 40K"<br>
>   <br>
>       [<br>
>           seekOffset := (seekOffset + 4096) min: fileLength.<br>
>           stream position: fileLength - seekOffset.<br>
>           data := stream next: (4100 min: seekOffset) into: data startingAt: 1.<br>
>           pos := self lastIndexOfPKSignature: EndOfCentralDirectorySignature in: data.<br>
>           pos = 0 and: [seekOffset < maxOffset]<br>
>       ] whileTrue.<br>
>   <br>
>       ^ pos > 0<br>
>           ifTrue: [ | newPos | stream position: (newPos := (stream position + pos - seekOffset - 1)). newPos]<br>
>           ifFalse: [0]!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>extractAllTo:informing:overwrite: (in category 'archive operations') -----<br>
>   extractAllTo: aDirectory informing: bar overwrite: allOverwrite<br>
>       "Extract all elements to the given directory"<br>
>       | overwriteAll |<br>
>       overwriteAll := allOverwrite.<br>
>       self members do:[:entry| | dir |<br>
>           entry isDirectory ifTrue:[<br>
> +             bar ifNotNil: [bar value: ('Creating {1}' translated format: {entry fileName})].<br>
> -             bar ifNotNil:[bar value: 'Creating ', entry fileName].<br>
>               dir := (entry fileName findTokens:'/') <br>
>                       inject: aDirectory into:[:base :part| base directoryNamed: part].<br>
>               dir assureExistence.<br>
>           ].<br>
>       ].<br>
>       self members do:[:entry| | response |<br>
>           entry isDirectory ifFalse:[<br>
> +             bar ifNotNil: [bar value: ('Extracting {1}' translated format: {entry fileName})].<br>
> -             bar ifNotNil:[bar value: 'Extracting ', entry fileName].<br>
>               response := entry extractInDirectory: aDirectory overwrite: overwriteAll.<br>
>               response == #retryWithOverwrite ifTrue:[<br>
>                   overwriteAll := true.<br>
>                   response := entry extractInDirectory: aDirectory overwrite: overwriteAll.<br>
>               ].<br>
>               response == #abort ifTrue:[^self].<br>
>               response == #failed ifTrue:[<br>
> +                 (self confirm: ('Failed to extract {1}. Proceed?' translated format: {entry fileName})) ifFalse: [^self].<br>
> -                 (self confirm: 'Failed to extract ', entry fileName, '. Proceed?') ifFalse:[^self].<br>
>               ].<br>
>           ].<br>
> +     ].!<br>
> -     ].<br>
> - !<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>readEndOfCentralDirectoryFrom: (in category 'private') -----<br>
>   readEndOfCentralDirectoryFrom: aStream<br>
>       "Read EOCD, starting from position before signature."<br>
>       | signature zipFileCommentLength |<br>
>       signature := self readSignatureFrom: aStream.<br>
> +     signature = EndOfCentralDirectorySignature ifFalse: [ ^self error: ('bad signature at {1}' translated format: {aStream position}) ].<br>
> -     signature = EndOfCentralDirectorySignature ifFalse: [ ^self error: 'bad signature at ', aStream position printString ].<br>
>   <br>
>       aStream nextLittleEndianNumber: 2. "# of this disk"<br>
>       aStream nextLittleEndianNumber: 2. "# of disk with central dir start"<br>
>       aStream nextLittleEndianNumber: 2. "# of entries in central dir on this disk"<br>
>       aStream nextLittleEndianNumber: 2. "total # of entries in central dir"<br>
>       centralDirectorySize := aStream nextLittleEndianNumber: 4. "size of central directory"<br>
>       centralDirectoryOffsetWRTStartingDiskNumber := aStream nextLittleEndianNumber: 4. "offset of start of central directory"<br>
>       zipFileCommentLength := aStream nextLittleEndianNumber: 2. "zip file comment"<br>
> +     zipFileComment := aStream next: zipFileCommentLength.!<br>
> -     zipFileComment := aStream next: zipFileCommentLength.<br>
> - !<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>readFrom: (in category 'reading') -----<br>
>   readFrom: aStreamOrFileName<br>
>       | stream name eocdPosition |<br>
>       stream := aStreamOrFileName isStream<br>
>           ifTrue: [name := aStreamOrFileName name. aStreamOrFileName]<br>
>           ifFalse: [StandardFileStream readOnlyFileNamed: (name := aStreamOrFileName)].<br>
>       stream binary.<br>
>       eocdPosition := self class findEndOfCentralDirectoryFrom: stream.<br>
> +     eocdPosition <= 0 ifTrue: [self error: ('{1} cannot find EOCD position in {2}' translated format: {self class name. aStreamOrFileName name})].<br>
> -     eocdPosition <= 0 ifTrue: [self error: self class name, ' cannot find EOCD position in ', aStreamOrFileName name].<br>
>       self readEndOfCentralDirectoryFrom: stream.<br>
>       stream position: eocdPosition - centralDirectorySize.<br>
>       self readMembersFrom: stream named: name!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>readMembersFrom:named: (in category 'private') -----<br>
>   readMembersFrom: stream named: fileName<br>
>       [<br>
>           | newMember signature |<br>
>           newMember := self memberClass newFromZipFile: stream named: fileName.<br>
>           signature := self readSignatureFrom: stream.<br>
>           signature = EndOfCentralDirectorySignature ifTrue: [ ^self ].<br>
>           signature = CentralDirectoryFileHeaderSignature<br>
> +             ifFalse: [ self error: ('bad CD signature at {1}' translated format: {(stream position - 4) printStringHex}) ].<br>
> -             ifFalse: [ self error: 'bad CD signature at ', (stream position - 4) printStringHex ].<br>
>           newMember readFrom: stream.<br>
>           newMember looksLikeDirectory ifTrue: [ newMember := newMember asDirectory ].<br>
>           self addMember: newMember.<br>
>       ] repeat.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>readSignatureFrom: (in category 'private') -----<br>
>   readSignatureFrom: stream<br>
>       "Returns next signature from given stream, leaves stream positioned afterwards."<br>
>   <br>
>       | signatureData | <br>
>       signatureData := ByteArray new: 4.<br>
>       stream next: 4 into: signatureData.<br>
>       ({ CentralDirectoryFileHeaderSignature . LocalFileHeaderSignature . EndOfCentralDirectorySignature }<br>
>           includes: signatureData)<br>
> +             ifFalse: [ ^self error: ('bad signature {1} at position {2}' translated format: {signatureData asString asHex. stream position - 4}) ].<br>
> -             ifFalse: [ ^self error: 'bad signature ', signatureData asString asHex, ' at position ', (stream position - 4) asString ].<br>
>       ^signatureData<br>
>   !<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>writeToFileNamed:prepending: (in category 'writing') -----<br>
>   writeToFileNamed: aFileName prepending: aString<br>
>       | stream |<br>
>       "Catch attempts to overwrite existing zip file"<br>
>       (self canWriteToFileNamed: aFileName)<br>
> +         ifFalse: [ ^self error: ('{1} is needed by one or more members in this archive' translated format: {aFileName}) ].<br>
> -         ifFalse: [ ^self error: (aFileName, ' is needed by one or more members in this archive') ].<br>
>       stream := StandardFileStream forceNewFileNamed: aFileName.<br>
>       self writeTo: stream prepending: aString.<br>
>       stream close.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchive>>writeToFileNamed:prependingFileNamed: (in category 'writing') -----<br>
>   writeToFileNamed: aFileName prependingFileNamed: anotherFileName<br>
>       | stream |<br>
>       "Catch attempts to overwrite existing zip file"<br>
>       (self canWriteToFileNamed: aFileName)<br>
> +         ifFalse: [ ^self error: ('{1} is needed by one or more members in this archive' translated format: {aFileName}) ].<br>
> -         ifFalse: [ ^self error: (aFileName, ' is needed by one or more members in this archive') ].<br>
>       stream := StandardFileStream forceNewFileNamed: aFileName.<br>
>       self writeTo: stream prependingFileNamed: anotherFileName.<br>
>       stream close.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchiveMember>>extractInDirectory:overwrite: (in category 'extraction') -----<br>
>   extractInDirectory: aDirectory overwrite: overwriteAll<br>
>       "Extract this entry into the given directory. Answer #okay, #failed, #abort, or #retryWithOverwrite."<br>
> +     | path fileDir file localName |<br>
> +     path := fileName findTokens: '/'.<br>
> -     | path fileDir file index localName |<br>
> -     path := fileName findTokens:'/'.<br>
>       localName := path last.<br>
> +     fileDir := path allButLast inject: aDirectory into: [:base :part | base directoryNamed: part].<br>
> -     fileDir := path allButLast inject: aDirectory into:[:base :part| base directoryNamed: part].<br>
>       fileDir assureExistence.<br>
> +     <br>
> +     overwriteAll ifFalse: [<br>
> +         [file := fileDir newFileNamed: localName]<br>
> +             on: FileExistsException<br>
> +             do: [<br>
> +                 (Project uiManager<br>
> +                     chooseFromLabeledValues: (OrderedDictionary new<br>
> +                         at: 'Yes, overwrite' translated put: [#overwrite];<br>
> +                         at: 'No, don''t overwrite' translated put: [^ #okay];<br>
> +                         at: 'Overwrite ALL files' translated put: [^ #retryWithOverwrite];<br>
> +                         at: 'Cancel operation' translated put: [];<br>
> +                         yourself)<br>
> +                     title: ('{1} already exists. Overwrite?' translated format: {fileName})) value<br>
> +                         ifNil: [^ #abort]]].<br>
> +     file ifNil: [<br>
> +         file := ([fileDir forceNewFileNamed: localName]<br>
> +             on: Error do: [])<br>
> +                 ifNil: [^ #failed]].<br>
> +     <br>
> -     file := [fileDir newFileNamed: localName] on: FileExistsException do:[:ex| ex return: nil].<br>
> -     file ifNil:[<br>
> -         overwriteAll ifFalse:[<br>
> -             [index := UIManager default chooseFrom: {<br>
> -                         'Yes, overwrite'. <br>
> -                         'No, don''t overwrite'. <br>
> -                         'Overwrite ALL files'.<br>
> -                         'Cancel operation'<br>
> -                     } lines: #(2) title: fileName, ' already exists. Overwrite?'.<br>
> -             index == nil] whileTrue.<br>
> -             index = 4 ifTrue:[^#abort].<br>
> -             index = 3 ifTrue:[^#retryWithOverwrite].<br>
> -             index = 2 ifTrue:[^#okay].<br>
> -         ].<br>
> -         file := [fileDir forceNewFileNamed: localName] on: Error do:[:ex| ex return].<br>
> -         file ifNil:[^#failed].<br>
> -     ].<br>
>       self extractTo: file.<br>
>       file close.<br>
> +     ^ #okay!<br>
> -     ^#okay!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchiveMember>>extractTo: (in category 'extraction') -----<br>
>   extractTo: aStream<br>
>       | oldCompression |<br>
> +     self isEncrypted ifTrue: [ self error: 'encryption is unsupported' translated ].<br>
> -     self isEncrypted ifTrue: [ self error: 'encryption is unsupported' ].<br>
>       aStream binary.<br>
>       oldCompression := self desiredCompressionMethod: CompressionStored.<br>
>       self rewindData.<br>
>       self writeDataTo: aStream.<br>
>       self desiredCompressionMethod: oldCompression.<br>
>       self endRead.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchiveMember>>extractTo:from:to: (in category 'extraction') -----<br>
>   extractTo: aStream from: start to: finish<br>
>       | oldCompression |<br>
> +     self isEncrypted ifTrue: [ self error: 'encryption is unsupported' translated ].<br>
> -     self isEncrypted ifTrue: [ self error: 'encryption is unsupported' ].<br>
>       aStream binary.<br>
>       oldCompression := self desiredCompressionMethod: CompressionStored.<br>
>       self rewindData.<br>
>       self writeDataTo: aStream from: start to: finish.<br>
>       self desiredCompressionMethod: oldCompression.<br>
>       self endRead.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchiveMember>>extractToFileNamed:inDirectory: (in category 'accessing') -----<br>
>   extractToFileNamed: aLocalFileName inDirectory: dir<br>
>       | stream fullName fullDir |<br>
> +     self isEncrypted ifTrue: [ ^self error: 'encryption unsupported' translated ].<br>
> -     self isEncrypted ifTrue: [ ^self error: 'encryption unsupported' ].<br>
>       fullName := dir fullNameFor: aLocalFileName.<br>
>       fullDir := FileDirectory forFileName: fullName.<br>
>       fullDir assureExistence.<br>
>       self isDirectory ifFalse: [<br>
>           stream := fullDir forceNewFileNamed: (FileDirectory localNameFor: fullName).<br>
>           self extractTo: stream.<br>
>           stream close.<br>
>       ] ifTrue: [ fullDir assureExistence ]<br>
>   !<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipArchiveMember>>writeDataTo:from:to: (in category 'private-writing') -----<br>
>   writeDataTo: aStream from: start to: finish<br>
>       "Copy my (possibly inflated or deflated) data to the given stream.<br>
>       But only the specified byte range.<br>
>       This might do decompression, or straight copying, depending<br>
>       on the values of compressionMethod and desiredCompressionMethod"<br>
>   <br>
>       uncompressedSize = 0 ifTrue: [ ^self ].    "nothing to do because no data"<br>
>       start > finish ifTrue: [ ^self ].<br>
>       start > uncompressedSize ifTrue: [ ^self ].<br>
>   <br>
>       (compressionMethod = CompressionStored and: [ desiredCompressionMethod = CompressionDeflated ])<br>
> +         ifTrue: [ ^self error: 'only supports uncompression or copying right now' translated ].<br>
> -         ifTrue: [ ^self error: 'only supports uncompression or copying right now' ].<br>
>   <br>
>       (compressionMethod = CompressionDeflated and: [ desiredCompressionMethod = CompressionStored ])<br>
>           ifTrue: [ ^self uncompressDataTo: aStream from: start to: finish ].<br>
>   <br>
>       self copyRawDataTo: aStream from: start to: finish.!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ZipDirectoryMember class>>newNamed: (in category 'instance creation') -----<br>
> - ----- Method: ZipDirectoryMember class>>newNamed: (in category 'as yet unclassified') -----<br>
>   newNamed: aFileName<br>
>       ^(self new) localFileName: aFileName; yourself!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipEncoderNode>>encodeBitLength:from: (in category 'encoding') -----<br>
>   encodeBitLength: blCounts from: aTree<br>
>       | index |<br>
>       "Note: If bitLength is not nil then the tree must be broken"<br>
> +     bitLength ifNotNil: [self error: 'Huffman tree is broken' translated].<br>
> -     bitLength ifNotNil: [self error:'Huffman tree is broken'].<br>
>       parent  <br>
>           ifNil: [bitLength := 0]<br>
>           ifNotNil: [bitLength := parent bitLength + 1].<br>
>       self isLeaf ifTrue:[<br>
>           index := bitLength + 1.<br>
>           blCounts at: index put: (blCounts at: index) + 1.<br>
>       ] ifFalse:[<br>
>           left encodeBitLength: blCounts from: aTree.<br>
>           right encodeBitLength: blCounts from: aTree.<br>
>       ].!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipEncoderTree>>buildTree:maxDepth: (in category 'encoding') -----<br>
>   buildTree: nodeList maxDepth: depth<br>
>       "Build either the literal or the distance tree"<br>
>       | heap rootNode blCounts |<br>
>       heap := Heap new: nodeList size // 3.<br>
>       heap sortBlock: self nodeSortBlock.<br>
>       "Find all nodes with non-zero frequency and add to heap"<br>
>       maxCode := 0.<br>
>       nodeList do:[:dNode|<br>
>           dNode frequency = 0 ifFalse:[<br>
>               maxCode := dNode value.<br>
>               heap add: dNode]].<br>
>       "The pkzip format requires that at least one distance code exists,<br>
>       and that at least one bit should be sent even if there is only one<br>
>       possible code. So to avoid special checks later on we force at least<br>
>       two codes of non zero frequency."<br>
>       heap size = 0 ifTrue:[<br>
>           self assert:[maxCode = 0].<br>
>           heap add: nodeList first.<br>
>           heap add: nodeList second.<br>
>           maxCode := 1].<br>
>       heap size = 1 ifTrue:[<br>
>           nodeList first frequency = 0<br>
>               ifTrue:[heap add: nodeList first]<br>
>               ifFalse:[heap add: nodeList second].<br>
>           maxCode := maxCode max: 1].<br>
>       rootNode := self buildHierarchyFrom: heap.<br>
>       rootNode height > depth ifTrue:[<br>
>           rootNode := rootNode rotateToHeight: depth.<br>
> +         rootNode height > depth ifTrue: [self error: 'Cannot encode tree' translated]].<br>
> -         rootNode height > depth ifTrue:[self error:'Cannot encode tree']].<br>
>       blCounts := WordArray new: depth+1.<br>
>       rootNode encodeBitLength: blCounts from: self.<br>
>       self buildCodes: nodeList counts: blCounts maxDepth: depth.<br>
>       self setValuesFrom: nodeList.!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ZipFileMember class>>newFrom:named: (in category 'instance creation') -----<br>
> - ----- Method: ZipFileMember class>>newFrom:named: (in category 'as yet unclassified') -----<br>
>   newFrom: stream named: fileName<br>
>       ^(self new) stream: stream externalFileName: fileName!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipFileMember>>readLocalDirectoryFileHeaderFrom: (in category 'private-reading') -----<br>
>   readLocalDirectoryFileHeaderFrom: aStream <br>
>       "Positions stream as necessary. Will return stream to its original position"<br>
>   <br>
>       | fileNameLength extraFieldLength xcrc32 xcompressedSize xuncompressedSize sig oldPos |<br>
>   <br>
>       oldPos := aStream position.<br>
>   <br>
>       aStream position: localHeaderRelativeOffset.<br>
>   <br>
>       sig := aStream next: 4.<br>
>       sig = LocalFileHeaderSignature asByteArray<br>
>           ifFalse: [ aStream position: oldPos.<br>
> +                 ^self error: ('bad LH signature at {1}' translated format: {localHeaderRelativeOffset printStringHex}) ].<br>
> -                 ^self error: 'bad LH signature at ', localHeaderRelativeOffset printStringHex ].<br>
>   <br>
>       versionNeededToExtract := aStream nextLittleEndianNumber: 2.<br>
>       bitFlag := aStream nextLittleEndianNumber: 2.<br>
>       compressionMethod := aStream nextLittleEndianNumber: 2.<br>
>   <br>
>       lastModFileDateTime := aStream nextLittleEndianNumber: 4.<br>
>       xcrc32 := aStream nextLittleEndianNumber: 4.<br>
>       xcompressedSize := aStream nextLittleEndianNumber: 4.<br>
>       xuncompressedSize := aStream nextLittleEndianNumber: 4.<br>
>   <br>
>       fileNameLength := aStream nextLittleEndianNumber: 2.<br>
>       extraFieldLength := aStream nextLittleEndianNumber: 2.<br>
>   <br>
>       fileName := (aStream next: fileNameLength) asString asSqueakPathName.<br>
>       localExtraField := (aStream next: extraFieldLength) asByteArray.<br>
>   <br>
>       dataOffset := aStream position.<br>
>   <br>
>       "Don't trash these fields if we already got them from the central directory"<br>
>       self hasDataDescriptor ifFalse: [<br>
>           crc32 := xcrc32.<br>
>           compressedSize := xcompressedSize.<br>
>           uncompressedSize := xuncompressedSize.<br>
>       ].<br>
>   <br>
>       aStream position: oldPos.!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipFileMember>>rewindData (in category 'private-reading') -----<br>
>   rewindData<br>
>       super rewindData.<br>
>       (stream isNil or: [ stream closed ])<br>
> +         ifTrue: [ self error: 'stream missing or closed' translated ].<br>
> -         ifTrue: [ self error: 'stream missing or closed' ].<br>
>       stream position: (localHeaderRelativeOffset + 4).<br>
>       self skipLocalDirectoryFileHeaderFrom: stream.!<br>
> <br>
> Item was changed:<br>
> + ----- Method: ZipStringMember class>>newFrom:named: (in category 'instance creation') -----<br>
> - ----- Method: ZipStringMember class>>newFrom:named: (in category 'as yet unclassified') -----<br>
>   newFrom: aString named: aFileName<br>
>       ^(self new) contents: aString; localFileName: aFileName; yourself!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipWriteStream>>encodeMatch:distance: (in category 'encoding') -----<br>
>   encodeMatch: length distance: dist<br>
>       "Encode the given match of length length starting at dist bytes ahead"<br>
>       | literal distance |<br>
>       dist > 0 <br>
> +         ifFalse: [^self error: 'Distance must be positive' translated].<br>
> -         ifFalse:[^self error:'Distance must be positive'].<br>
>       length < MinMatch <br>
> +         ifTrue: [^self error: ('Match length must be at least {1}' translated format: {MinMatch})].<br>
> -         ifTrue:[^self error:'Match length must be at least ', MinMatch printString].<br>
>       litCount := litCount + 1.<br>
>       matchCount := matchCount + 1.<br>
>       literals at: litCount put: length - MinMatch.<br>
>       distances at: litCount put: dist.<br>
>       literal := (MatchLengthCodes at: length - MinMatch + 1).<br>
>       literalFreq at: literal+1 put: (literalFreq at: literal+1) + 1.<br>
>       dist < 257<br>
>           ifTrue:[distance := DistanceCodes at: dist]<br>
>           ifFalse:[distance := DistanceCodes at: 257 + (dist - 1 bitShift: -7)].<br>
>       distanceFreq at: distance+1 put: (distanceFreq at: distance+1) + 1.<br>
>       ^self shouldFlush!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipWriteStream>>flushBlock: (in category 'encoding') -----<br>
>   flushBlock: lastBlock<br>
>       "Send the current block"<br>
>       | lastFlag bitsRequired method bitsSent<br>
>       storedLength fixedLength dynamicLength <br>
>       blTree lTree dTree blBits blFreq |<br>
>   <br>
>       lastFlag := lastBlock ifTrue:[1] ifFalse:[0].<br>
>   <br>
>       "Compute the literal/length and distance tree"<br>
>       lTree := ZipEncoderTree buildTreeFrom: literalFreq maxDepth: MaxBits.<br>
>       dTree := ZipEncoderTree buildTreeFrom: distanceFreq maxDepth: MaxBits.<br>
>   <br>
>       "Compute the bit length tree"<br>
>       blBits := lTree bitLengths, dTree bitLengths.<br>
>       blFreq := WordArray new: MaxBitLengthCodes.<br>
>       self scanBitLengths: blBits into: blFreq.<br>
>       blTree := ZipEncoderTree buildTreeFrom: blFreq maxDepth: MaxBitLengthBits.<br>
>   <br>
>       "Compute the bit length for the current block.<br>
>       Note: Most of this could be computed on the fly but it's getting<br>
>       really ugly in this case so we do it afterwards."<br>
>       storedLength := self storedBlockSize.<br>
>       fixedLength := self fixedBlockSizeFor: lTree and: dTree.<br>
>       dynamicLength := self dynamicBlockSizeFor: lTree and: dTree <br>
>                               using: blTree and: blFreq.<br>
>       VerboseLevel > 1 ifTrue:[<br>
>           Transcript cr; show:'Block sizes (S/F/D):';<br>
>               space; print: storedLength // 8; <br>
>               nextPut:$/; print: fixedLength // 8; <br>
>               nextPut:$/; print: dynamicLength // 8; space; endEntry].<br>
>   <br>
>       "Check which method to use"<br>
>       method := self forcedMethod.<br>
>       method ifNil:[<br>
>           method := (storedLength < fixedLength and:[storedLength < dynamicLength]) <br>
>               ifTrue:[#stored]<br>
>               ifFalse:[fixedLength < dynamicLength ifTrue:[#fixed] ifFalse:[#dynamic]]].<br>
>       (method == #stored and:[blockStart < 0]) ifTrue:[<br>
>           "Cannot use #stored if the block is not available"<br>
>           method := fixedLength < dynamicLength ifTrue:[#fixed] ifFalse:[#dynamic]].<br>
>   <br>
>       bitsSent := encoder bitPosition. "# of bits sent before this block"<br>
>       bitsRequired := nil.<br>
>   <br>
>       (method == #stored) ifTrue:[<br>
>           VerboseLevel > 0 ifTrue:[Transcript show:'S'].<br>
>           bitsRequired := storedLength.<br>
>           encoder nextBits: 3 put: StoredBlock << 1 + lastFlag.<br>
>           self sendStoredBlock].<br>
>   <br>
>       (method == #fixed) ifTrue:[<br>
>           VerboseLevel > 0 ifTrue:[Transcript show:'F'].<br>
>           bitsRequired := fixedLength.<br>
>           encoder nextBits: 3 put: FixedBlock << 1 + lastFlag.<br>
>           self sendFixedBlock].<br>
>   <br>
>       (method == #dynamic) ifTrue:[<br>
>           VerboseLevel > 0 ifTrue:[Transcript show:'D'].<br>
>           bitsRequired := dynamicLength.<br>
>           encoder nextBits: 3 put: DynamicBlock << 1 + lastFlag.<br>
>           self sendDynamicBlock: blTree <br>
>               literalTree: lTree <br>
>               distanceTree: dTree <br>
>               bitLengths: blBits].<br>
>   <br>
>       bitsRequired = (encoder bitPosition - bitsSent)<br>
> +         ifFalse:[self error: 'Bits size mismatch' translated].<br>
> -         ifFalse:[self error:'Bits size mismatch'].<br>
>   <br>
>       lastBlock <br>
>           ifTrue:[self release]<br>
>           ifFalse:[self initializeNewBlock].!<br>
> <br>
> Item was changed:<br>
>   ----- Method: ZipWriteStream>>sendCompressedBlock:with: (in category 'dynamic blocks') -----<br>
>   sendCompressedBlock: litTree with: distTree<br>
>       "Send the current block using the encodings from the given literal/length and distance tree"<br>
>       | sum |<br>
>       sum := encoder<br>
>               sendBlock: (ReadStream on: literals from: 1 to: litCount)<br>
>               with: (ReadStream on: distances from: 1 to: litCount)<br>
>               with: litTree<br>
>               with: distTree.<br>
> +     sum = (blockPosition - blockStart) ifFalse:[self error: 'Wrong number of bytes' translated].!<br>
> -     sum = (blockPosition - blockStart) ifFalse:[self error:'Wrong number of bytes'].!<br>
> <br>