Unpack5(bool Solid,bool SuspendAfterInit)1 void Unpack::Unpack5(bool Solid,bool SuspendAfterInit)
2 {
3 FileExtracted=true;
4
5 if (!Suspended)
6 {
7 UnpInitData(Solid);
8 if (!UnpReadBuf())
9 return;
10
11 // Check TablesRead5 to be sure that we read tables at least once
12 // regardless of current block header TablePresent flag.
13 // So we can safefly use these tables below.
14 if (!ReadBlockHeader(Inp,BlockHeader) ||
15 !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5)
16 return;
17 }
18
19 if (SuspendAfterInit) {
20 Suspended = true;
21 }
22
23 while (true)
24 {
25 UnpPtr&=MaxWinMask;
26
27 if (Inp.InAddr>=ReadBorder)
28 {
29 bool FileDone=false;
30
31 // We use 'while', because for empty block containing only Huffman table,
32 // we'll be on the block border once again just after reading the table.
33 while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 ||
34 Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 &&
35 Inp.InBit>=BlockHeader.BlockBitSize)
36 {
37 if (BlockHeader.LastBlockInFile)
38 {
39 FileDone=true;
40 break;
41 }
42 if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables))
43 return;
44 }
45 if (FileDone || !UnpReadBuf())
46 break;
47 }
48
49 if (((WriteBorder-UnpPtr) & MaxWinMask)<MAX_INC_LZ_MATCH && WriteBorder!=UnpPtr)
50 {
51 UnpWriteBuf();
52 if (WrittenFileSize>DestUnpSize)
53 return;
54 if (Suspended)
55 {
56 FileExtracted=false;
57 return;
58 }
59 }
60
61 uint MainSlot=DecodeNumber(Inp,&BlockTables.LD);
62 if (MainSlot<256)
63 {
64 if (Fragmented)
65 FragWindow[UnpPtr++]=(byte)MainSlot;
66 else
67 Window[UnpPtr++]=(byte)MainSlot;
68 continue;
69 }
70 if (MainSlot>=262)
71 {
72 uint Length=SlotToLength(Inp,MainSlot-262);
73
74 uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD);
75 if (DistSlot<4)
76 {
77 DBits=0;
78 Distance+=DistSlot;
79 }
80 else
81 {
82 DBits=DistSlot/2 - 1;
83 Distance+=(2 | (DistSlot & 1)) << DBits;
84 }
85
86 if (DBits>0)
87 {
88 if (DBits>=4)
89 {
90 if (DBits>4)
91 {
92 Distance+=((Inp.getbits32()>>(36-DBits))<<4);
93 Inp.addbits(DBits-4);
94 }
95 uint LowDist=DecodeNumber(Inp,&BlockTables.LDD);
96 Distance+=LowDist;
97 }
98 else
99 {
100 Distance+=Inp.getbits32()>>(32-DBits);
101 Inp.addbits(DBits);
102 }
103 }
104
105 if (Distance>0x100)
106 {
107 Length++;
108 if (Distance>0x2000)
109 {
110 Length++;
111 if (Distance>0x40000)
112 Length++;
113 }
114 }
115
116 InsertOldDist(Distance);
117 LastLength=Length;
118 if (Fragmented)
119 FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
120 else
121 CopyString(Length,Distance);
122 continue;
123 }
124 if (MainSlot==256)
125 {
126 UnpackFilter Filter;
127 if (!ReadFilter(Inp,Filter) || !AddFilter(Filter))
128 break;
129 continue;
130 }
131 if (MainSlot==257)
132 {
133 if (LastLength!=0)
134 if (Fragmented)
135 FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask);
136 else
137 CopyString(LastLength,OldDist[0]);
138 continue;
139 }
140 if (MainSlot<262)
141 {
142 uint DistNum=MainSlot-258;
143 uint Distance=OldDist[DistNum];
144 for (uint I=DistNum;I>0;I--)
145 OldDist[I]=OldDist[I-1];
146 OldDist[0]=Distance;
147
148 uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD);
149 uint Length=SlotToLength(Inp,LengthSlot);
150 LastLength=Length;
151 if (Fragmented)
152 FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask);
153 else
154 CopyString(Length,Distance);
155 continue;
156 }
157 }
158 UnpWriteBuf();
159 }
160
161
ReadFilterData(BitInput & Inp)162 uint Unpack::ReadFilterData(BitInput &Inp)
163 {
164 uint ByteCount=(Inp.fgetbits()>>14)+1;
165 Inp.addbits(2);
166
167 uint Data=0;
168 for (uint I=0;I<ByteCount;I++)
169 {
170 Data+=(Inp.fgetbits()>>8)<<(I*8);
171 Inp.addbits(8);
172 }
173 return Data;
174 }
175
176
ReadFilter(BitInput & Inp,UnpackFilter & Filter)177 bool Unpack::ReadFilter(BitInput &Inp,UnpackFilter &Filter)
178 {
179 if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16)
180 if (!UnpReadBuf())
181 return false;
182
183 Filter.BlockStart=ReadFilterData(Inp);
184 Filter.BlockLength=ReadFilterData(Inp);
185 if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE)
186 Filter.BlockLength=0;
187
188 Filter.Type=Inp.fgetbits()>>13;
189 Inp.faddbits(3);
190
191 if (Filter.Type==FILTER_DELTA)
192 {
193 Filter.Channels=(Inp.fgetbits()>>11)+1;
194 Inp.faddbits(5);
195 }
196
197 return true;
198 }
199
200
AddFilter(UnpackFilter & Filter)201 bool Unpack::AddFilter(UnpackFilter &Filter)
202 {
203 if (Filters.Size()>=MAX_UNPACK_FILTERS)
204 {
205 UnpWriteBuf(); // Write data, apply and flush filters.
206 if (Filters.Size()>=MAX_UNPACK_FILTERS)
207 InitFilters(); // Still too many filters, prevent excessive memory use.
208 }
209
210 // If distance to filter start is that large that due to circular dictionary
211 // mode now it points to old not written yet data, then we set 'NextWindow'
212 // flag and process this filter only after processing that older data.
213 Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart;
214
215 Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask);
216 Filters.Push(Filter);
217 return true;
218 }
219
220
UnpReadBuf()221 bool Unpack::UnpReadBuf()
222 {
223 int DataSize=ReadTop-Inp.InAddr; // Data left to process.
224 if (DataSize<0)
225 return false;
226 BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart;
227 if (Inp.InAddr>BitInput::MAX_SIZE/2)
228 {
229 // If we already processed more than half of buffer, let's move
230 // remaining data into beginning to free more space for new data
231 // and ensure that calling function does not cross the buffer border
232 // even if we did not read anything here. Also it ensures that read size
233 // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk
234 // to make it zero.
235 if (DataSize>0)
236 memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize);
237 Inp.InAddr=0;
238 ReadTop=DataSize;
239 }
240 else
241 DataSize=ReadTop;
242 int ReadCode=0;
243 if (BitInput::MAX_SIZE!=DataSize)
244 ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize);
245 if (ReadCode>0) // Can be also -1.
246 ReadTop+=ReadCode;
247 ReadBorder=ReadTop-30;
248 BlockHeader.BlockStart=Inp.InAddr;
249 if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet.
250 {
251 // We may need to quit from main extraction loop and read new block header
252 // and trees earlier than data in input buffer ends.
253 ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1);
254 }
255 return ReadCode!=-1;
256 }
257
258
UnpWriteBuf()259 void Unpack::UnpWriteBuf()
260 {
261 size_t WrittenBorder=WrPtr;
262 size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask;
263 size_t WriteSizeLeft=FullWriteSize;
264 bool NotAllFiltersProcessed=false;
265 for (size_t I=0;I<Filters.Size();I++)
266 {
267 // Here we apply filters to data which we need to write.
268 // We always copy data to another memory block before processing.
269 // We cannot process them just in place in Window buffer, because
270 // these data can be used for future string matches, so we must
271 // preserve them in original form.
272
273 UnpackFilter *flt=&Filters[I];
274 if (flt->Type==FILTER_NONE)
275 continue;
276 if (flt->NextWindow)
277 {
278 // Here we skip filters which have block start in current data range
279 // due to address wrap around in circular dictionary, but actually
280 // belong to next dictionary block. If such filter start position
281 // is included to current write range, then we reset 'NextWindow' flag.
282 // In fact we can reset it even without such check, because current
283 // implementation seems to guarantee 'NextWindow' flag reset after
284 // buffer writing for all existing filters. But let's keep this check
285 // just in case. Compressor guarantees that distance between
286 // filter block start and filter storing position cannot exceed
287 // the dictionary size. So if we covered the filter block start with
288 // our write here, we can safely assume that filter is applicable
289 // to next block on no further wrap arounds is possible.
290 if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize)
291 flt->NextWindow=false;
292 continue;
293 }
294 uint BlockStart=flt->BlockStart;
295 uint BlockLength=flt->BlockLength;
296 if (((BlockStart-WrittenBorder)&MaxWinMask)<WriteSizeLeft)
297 {
298 if (WrittenBorder!=BlockStart)
299 {
300 UnpWriteArea(WrittenBorder,BlockStart);
301 WrittenBorder=BlockStart;
302 WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
303 }
304 if (BlockLength<=WriteSizeLeft)
305 {
306 if (BlockLength>0) // We set it to 0 also for invalid filters.
307 {
308 uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask;
309
310 FilterSrcMemory.Alloc(BlockLength);
311 byte *Mem=&FilterSrcMemory[0];
312 if (BlockStart<BlockEnd || BlockEnd==0)
313 {
314 if (Fragmented)
315 FragWindow.CopyData(Mem,BlockStart,BlockLength);
316 else
317 memcpy(Mem,Window+BlockStart,BlockLength);
318 }
319 else
320 {
321 size_t FirstPartLength=size_t(MaxWinSize-BlockStart);
322 if (Fragmented)
323 {
324 FragWindow.CopyData(Mem,BlockStart,FirstPartLength);
325 FragWindow.CopyData(Mem+FirstPartLength,0,BlockEnd);
326 }
327 else
328 {
329 memcpy(Mem,Window+BlockStart,FirstPartLength);
330 memcpy(Mem+FirstPartLength,Window,BlockEnd);
331 }
332 }
333
334 byte *OutMem=ApplyFilter(Mem,BlockLength,flt);
335
336 Filters[I].Type=FILTER_NONE;
337
338 if (OutMem!=NULL)
339 UnpIO->UnpWrite(OutMem,BlockLength);
340
341 UnpSomeRead=true;
342 WrittenFileSize+=BlockLength;
343 WrittenBorder=BlockEnd;
344 WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask;
345 }
346 }
347 else
348 {
349 // Current filter intersects the window write border, so we adjust
350 // the window border to process this filter next time, not now.
351 WrPtr=WrittenBorder;
352
353 // Since Filter start position can only increase, we quit processing
354 // all following filters for this data block and reset 'NextWindow'
355 // flag for them.
356 for (size_t J=I;J<Filters.Size();J++)
357 {
358 UnpackFilter *flt=&Filters[J];
359 if (flt->Type!=FILTER_NONE)
360 flt->NextWindow=false;
361 }
362
363 // Do not write data left after current filter now.
364 NotAllFiltersProcessed=true;
365 break;
366 }
367 }
368 }
369
370 // Remove processed filters from queue.
371 size_t EmptyCount=0;
372 for (size_t I=0;I<Filters.Size();I++)
373 {
374 if (EmptyCount>0)
375 Filters[I-EmptyCount]=Filters[I];
376 if (Filters[I].Type==FILTER_NONE)
377 EmptyCount++;
378 }
379 if (EmptyCount>0)
380 Filters.Alloc(Filters.Size()-EmptyCount);
381
382 if (!NotAllFiltersProcessed) // Only if all filters are processed.
383 {
384 // Write data left after last filter.
385 UnpWriteArea(WrittenBorder,UnpPtr);
386 WrPtr=UnpPtr;
387 }
388
389 // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE
390 // instead of potentially huge MaxWinSize blocks. It also allows us
391 // to keep the size of Filters array reasonable.
392 WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask;
393
394 // Choose the nearest among WriteBorder and WrPtr actual written border.
395 // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead.
396 if (WriteBorder==UnpPtr ||
397 WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask))
398 WriteBorder=WrPtr;
399 }
400
401
ApplyFilter(byte * Data,uint DataSize,UnpackFilter * Flt)402 byte* Unpack::ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt)
403 {
404 byte *SrcData=Data;
405 switch(Flt->Type)
406 {
407 case FILTER_E8:
408 case FILTER_E8E9:
409 {
410 uint FileOffset=(uint)WrittenFileSize;
411
412 const uint FileSize=0x1000000;
413 byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8;
414 // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4"
415 // to avoid overflow for DataSize<4.
416 for (uint CurPos=0;CurPos+4<DataSize;)
417 {
418 byte CurByte=*(Data++);
419 CurPos++;
420 if (CurByte==0xe8 || CurByte==CmpByte2)
421 {
422 uint Offset=(CurPos+FileOffset)%FileSize;
423 uint Addr=RawGet4(Data);
424
425 // We check 0x80000000 bit instead of '< 0' comparison
426 // not assuming int32 presence or uint size and endianness.
427 if ((Addr & 0x80000000)!=0) // Addr<0
428 {
429 if (((Addr+Offset) & 0x80000000)==0) // Addr+Offset>=0
430 RawPut4(Addr+FileSize,Data);
431 }
432 else
433 if (((Addr-FileSize) & 0x80000000)!=0) // Addr<FileSize
434 RawPut4(Addr-Offset,Data);
435
436 Data+=4;
437 CurPos+=4;
438 }
439 }
440 }
441 return SrcData;
442 case FILTER_ARM:
443 // 2019-11-15: we turned off ARM filter by default when compressing,
444 // mostly because it is inefficient for modern 64 bit ARM binaries.
445 // It was turned on by default in 5.0 - 5.80b3 , so we still need it
446 // here for compatibility with some of previously created archives.
447 {
448 uint FileOffset=(uint)WrittenFileSize;
449 // DataSize is unsigned, so we use "CurPos+3" and not "DataSize-3"
450 // to avoid overflow for DataSize<3.
451 for (uint CurPos=0;CurPos+3<DataSize;CurPos+=4)
452 {
453 byte *D=Data+CurPos;
454 if (D[3]==0xeb) // BL command with '1110' (Always) condition.
455 {
456 uint Offset=D[0]+uint(D[1])*0x100+uint(D[2])*0x10000;
457 Offset-=(FileOffset+CurPos)/4;
458 D[0]=(byte)Offset;
459 D[1]=(byte)(Offset>>8);
460 D[2]=(byte)(Offset>>16);
461 }
462 }
463 }
464 return SrcData;
465 case FILTER_DELTA:
466 {
467 // Unlike RAR3, we do not need to reject excessive channel
468 // values here, since RAR5 uses only 5 bits to store channel.
469 uint Channels=Flt->Channels,SrcPos=0;
470
471 FilterDstMemory.Alloc(DataSize);
472 byte *DstData=&FilterDstMemory[0];
473
474 // Bytes from same channels are grouped to continual data blocks,
475 // so we need to place them back to their interleaving positions.
476 for (uint CurChannel=0;CurChannel<Channels;CurChannel++)
477 {
478 byte PrevByte=0;
479 for (uint DestPos=CurChannel;DestPos<DataSize;DestPos+=Channels)
480 DstData[DestPos]=(PrevByte-=Data[SrcPos++]);
481 }
482 return DstData;
483 }
484
485 }
486 return NULL;
487 }
488
489
UnpWriteArea(size_t StartPtr,size_t EndPtr)490 void Unpack::UnpWriteArea(size_t StartPtr,size_t EndPtr)
491 {
492 if (EndPtr!=StartPtr)
493 UnpSomeRead=true;
494 if (EndPtr<StartPtr)
495 UnpAllBuf=true;
496
497 if (Fragmented)
498 {
499 size_t SizeToWrite=(EndPtr-StartPtr) & MaxWinMask;
500 while (SizeToWrite>0)
501 {
502 size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite);
503 UnpWriteData(&FragWindow[StartPtr],BlockSize);
504 SizeToWrite-=BlockSize;
505 StartPtr=(StartPtr+BlockSize) & MaxWinMask;
506 }
507 }
508 else
509 if (EndPtr<StartPtr)
510 {
511 UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr);
512 UnpWriteData(Window,EndPtr);
513 }
514 else
515 UnpWriteData(Window+StartPtr,EndPtr-StartPtr);
516 }
517
518
UnpWriteData(byte * Data,size_t Size)519 void Unpack::UnpWriteData(byte *Data,size_t Size)
520 {
521 if (WrittenFileSize>=DestUnpSize)
522 return;
523 size_t WriteSize=Size;
524 int64 LeftToWrite=DestUnpSize-WrittenFileSize;
525 if ((int64)WriteSize>LeftToWrite)
526 WriteSize=(size_t)LeftToWrite;
527 UnpIO->UnpWrite(Data,WriteSize);
528 WrittenFileSize+=Size;
529 }
530
531
UnpInitData50(bool Solid)532 void Unpack::UnpInitData50(bool Solid)
533 {
534 if (!Solid)
535 TablesRead5=false;
536 }
537
538
ReadBlockHeader(BitInput & Inp,UnpackBlockHeader & Header)539 bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header)
540 {
541 Header.HeaderSize=0;
542
543 if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7)
544 if (!UnpReadBuf())
545 return false;
546 Inp.faddbits((8-Inp.InBit)&7);
547
548 byte BlockFlags=Inp.fgetbits()>>8;
549 Inp.faddbits(8);
550 uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count.
551
552 if (ByteCount==4)
553 return false;
554
555 Header.HeaderSize=2+ByteCount;
556
557 Header.BlockBitSize=(BlockFlags&7)+1;
558
559 byte SavedCheckSum=Inp.fgetbits()>>8;
560 Inp.faddbits(8);
561
562 int BlockSize=0;
563 for (uint I=0;I<ByteCount;I++)
564 {
565 BlockSize+=(Inp.fgetbits()>>8)<<(I*8);
566 Inp.addbits(8);
567 }
568
569 Header.BlockSize=BlockSize;
570 byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16));
571 if (CheckSum!=SavedCheckSum)
572 return false;
573
574 Header.BlockStart=Inp.InAddr;
575 ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1);
576
577 Header.LastBlockInFile=(BlockFlags & 0x40)!=0;
578 Header.TablePresent=(BlockFlags & 0x80)!=0;
579 return true;
580 }
581
582
ReadTables(BitInput & Inp,UnpackBlockHeader & Header,UnpackBlockTables & Tables)583 bool Unpack::ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables)
584 {
585 if (!Header.TablePresent)
586 return true;
587
588 if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25)
589 if (!UnpReadBuf())
590 return false;
591
592 byte BitLength[BC];
593 for (uint I=0;I<BC;I++)
594 {
595 uint Length=(byte)(Inp.fgetbits() >> 12);
596 Inp.faddbits(4);
597 if (Length==15)
598 {
599 uint ZeroCount=(byte)(Inp.fgetbits() >> 12);
600 Inp.faddbits(4);
601 if (ZeroCount==0)
602 BitLength[I]=15;
603 else
604 {
605 ZeroCount+=2;
606 while (ZeroCount-- > 0 && I<ASIZE(BitLength))
607 BitLength[I++]=0;
608 I--;
609 }
610 }
611 else
612 BitLength[I]=Length;
613 }
614
615 MakeDecodeTables(BitLength,&Tables.BD,BC);
616
617 byte Table[HUFF_TABLE_SIZE];
618 const uint TableSize=HUFF_TABLE_SIZE;
619 for (uint I=0;I<TableSize;)
620 {
621 if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-5)
622 if (!UnpReadBuf())
623 return false;
624 uint Number=DecodeNumber(Inp,&Tables.BD);
625 if (Number<16)
626 {
627 Table[I]=Number;
628 I++;
629 }
630 else
631 if (Number<18)
632 {
633 uint N;
634 if (Number==16)
635 {
636 N=(Inp.fgetbits() >> 13)+3;
637 Inp.faddbits(3);
638 }
639 else
640 {
641 N=(Inp.fgetbits() >> 9)+11;
642 Inp.faddbits(7);
643 }
644 if (I==0)
645 {
646 // We cannot have "repeat previous" code at the first position.
647 // Multiple such codes would shift Inp position without changing I,
648 // which can lead to reading beyond of Inp boundary in mutithreading
649 // mode, where Inp.ExternalBuffer disables bounds check and we just
650 // reserve a lot of buffer space to not need such check normally.
651 return false;
652 }
653 else
654 while (N-- > 0 && I<TableSize)
655 {
656 Table[I]=Table[I-1];
657 I++;
658 }
659 }
660 else
661 {
662 uint N;
663 if (Number==18)
664 {
665 N=(Inp.fgetbits() >> 13)+3;
666 Inp.faddbits(3);
667 }
668 else
669 {
670 N=(Inp.fgetbits() >> 9)+11;
671 Inp.faddbits(7);
672 }
673 while (N-- > 0 && I<TableSize)
674 Table[I++]=0;
675 }
676 }
677 TablesRead5=true;
678 if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop)
679 return false;
680 MakeDecodeTables(&Table[0],&Tables.LD,NC);
681 MakeDecodeTables(&Table[NC],&Tables.DD,DC);
682 MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC);
683 MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC);
684 return true;
685 }
686
687
InitFilters()688 void Unpack::InitFilters()
689 {
690 Filters.SoftReset();
691 }
692