1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // special stream classes
29 XStreamError
= class(Exception
);
31 // read-only ïîòîê äëÿ èçâëå÷åíèÿ èç èñõîäíîãî òîëüêî êóñî÷êà
32 TSFSPartialStream
= class(TStream
)
34 fSource
: TStream
; // èñõîäíûé ïîòîê
35 fKillSource
: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè?
36 fLastReadPos
: Int64; // ïîñëåäíèé Read() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos)
37 fCurrentPos
: Int64; // ïîñëåäíèé Seek() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos)
38 fStartPos
: Int64; // íà÷àëî êóñî÷êà
39 fSize
: Int64; // äëèíà êóñî÷êà
40 fPreBuf
: packed array of Byte; // ýòîò áóôåð áóäåò ïåðåä ôàéëîì
42 procedure CheckPos ();
45 // aSrc: ïîòîê-èñõîäíèê.
46 // aPos: íà÷àëüíàÿ ïîçèöèÿ â ïîòîêå. -1 -- ñ òåêóùåé.
47 // åñëè aPos < òåêóùåé ïîçèöèè, òî èñõîäíûé ïîòîê äîëæåí
48 // íîðìàëüíî ïîääåðæèâàòü Seek()!
49 // aSize: êîëè÷åñòâî áàéòèêîâ, êîòîðîå ìîæíî ïðî÷åñòü èç ïîòîêà.
50 // åñëè ìåíüøå íóëÿ -- òî äî êîíöà.
51 // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì?
52 // òàêæå ìîæåò ïðèøïàíäîðèòü ê íà÷àëó ôàéëà áóôåð. bufSz áóäåò äîáàâëåíî ê
54 constructor Create (aSrc
: TStream
; aPos
, aSize
: Int64; aKillSrc
: Boolean; preBuf
: Pointer=nil; bufSz
: Integer=0);
55 destructor Destroy (); override;
57 // íîðìàëèçóåò count è ÷èòàåò.
58 function Read (var buffer
; count
: LongInt): LongInt; override;
59 // Write() ïðîñòî ãðîìêî ïàäàåò.
60 function Write (const buffer
; count
: LongInt): LongInt; override;
61 // Seek() ðåàëèçîâàíî, ÷òîáû ìîãëà ðàáîòàòü ïðîïåðòÿ Size.
62 // âîîáùå-òî ìîæíî ïåðåêðûòü ìåòîä GetSize(), íî âäðóã êàêîé
63 // áîëüíîé íà ãîëîâó êîäåð áóäåò ïîëó÷àòü ðàçìåð ïðè ïîìîùè
65 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
68 // this stream can kill both `proxied` and `guarded` streams on closing
69 TSFSGuardStream
= class(TStream
)
71 fSource
: TStream
; // èñõîäíûé ïîòîê
72 fGuardedStream
: TStream
; // ïîòîê, êîòîðûé çàâàëèì ïðè ïîìèðàíèè
73 fKillSource
: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè?
74 fKillGuarded
: Boolean; // óáèâàòü îõðàíÿåìûé ïðè ïîìèðàíèè?
75 fGuardedFirst
: Boolean; // ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî?
78 // aSrc: ïîòîê-èñõîäíèê (íà êîòîðûé çàìàïåíû îïåðàöèè ÷òåíèÿ/çàïèñè).
79 // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì?
80 // aKillGuarded: óáèâàòü ëè îõðàíÿåìûé ïîòîê, êîãäà ñàìè óìèðàåì?
81 // aGuardedFirst: true: ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî.
82 constructor Create (aSrc
, aGuarded
: TStream
; aKillSrc
, aKillGuarded
: Boolean; aGuardedFirst
: Boolean=true);
83 destructor Destroy (); override;
85 // íèæåñëåäóþùåå çàìàïëåíî íà fSource
86 function Read (var buffer
; count
: LongInt): LongInt; override;
87 function Write (const buffer
; count
: LongInt): LongInt; override;
88 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
91 TSFSMemoryStreamRO
= class(TCustomMemoryStream
)
97 constructor Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
98 destructor Destroy (); override;
100 function Write (const buffer
; count
: LongInt): LongInt; override;
103 TUnZStream
= class(TStream
)
109 fSkipHeader
: Boolean;
110 fSize
: Int64; // can be -1
112 fSkipToPos
: Int64; // >0: skip to this position
116 function readBuf (var buffer
; count
: LongInt): LongInt;
118 procedure determineSize ();
121 // `aSize` can be -1 if stream size is unknown
122 constructor create (asrc
: TStream
; aSize
: Int64; aKillSrc
: Boolean; aSkipHeader
: boolean=false);
123 destructor destroy (); override;
124 function read (var buffer
; count
: LongInt): LongInt; override;
125 function write (const buffer
; count
: LongInt): LongInt; override;
126 function seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
136 { TSFSPartialStream }
137 constructor TSFSPartialStream
.Create (aSrc
: TStream
; aPos
, aSize
: Int64; aKillSrc
: Boolean; preBuf
: Pointer=nil; bufSz
: Integer=0);
141 if aPos
< 0 then aPos
:= aSrc
.Position
;
142 if aSize
< 0 then aSize
:= 0;
144 fKillSource
:= aKillSrc
;
151 SetLength(fPreBuf
, bufSz
);
152 Move(preBuf
^, fPreBuf
[0], bufSz
);
161 destructor TSFSPartialStream
.Destroy ();
163 if fKillSource
then FreeAndNil(fSource
);
167 procedure TSFSPartialStream
.CheckPos ();
170 if fSource.Position <> fStartPos+fCurrentPos-Length(fPreBuf) then
172 fSource.Position := fStartPos+fCurrentPos-Length(fPreBuf);
175 if fCurrentPos
>= length(fPreBuf
) then
177 //writeln('seeking at ', fCurrentPos, ' (real: ', fStartPos+fCurrentPos-Length(fPreBuf), ')');
178 fSource
.Position
:= fStartPos
+fCurrentPos
-Length(fPreBuf
);
180 fLastReadPos
:= fCurrentPos
;
183 function TSFSPartialStream
.Write (const buffer
; count
: LongInt): LongInt;
186 raise XStreamError
.Create('can''t write to read-only stream');
187 // à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü!
190 function TSFSPartialStream
.Read (var buffer
; count
: LongInt): LongInt;
196 if count
< 0 then raise XStreamError
.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á...
197 if count
= 0 then begin result
:= 0; exit
; end;
200 if (Length(fPreBuf
) > 0) and (fCurrentPos
< Length(fPreBuf
)) then
202 fLastReadPos
:= fCurrentPos
;
203 left
:= Length(fPreBuf
)-fCurrentPos
;
204 if left
> count
then left
:= count
;
207 Move(fPreBuf
[fCurrentPos
], pc
^, left
);
208 Inc(PChar(pc
), left
);
209 Inc(fCurrentPos
, left
);
210 fLastReadPos
:= fCurrentPos
;
213 if count
= 0 then exit
;
217 left
:= fSize
-fCurrentPos
;
218 if left
< count
then count
:= left
; // è òàê ñëó÷àåòñÿ...
221 rd
:= fSource
.Read(pc
^, count
);
223 Inc(fCurrentPos
, rd
);
224 fLastReadPos
:= fCurrentPos
;
232 function TSFSPartialStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
235 soBeginning
: result
:= offset
;
236 soCurrent
: result
:= offset
+fCurrentPos
;
237 soEnd
: result
:= fSize
+offset
;
238 else raise XStreamError
.Create('invalid Seek() call');
239 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
241 if result
< 0 then result
:= 0
242 else if result
> fSize
then result
:= fSize
;
243 fCurrentPos
:= result
;
248 constructor TSFSGuardStream
.Create (aSrc
, aGuarded
: TStream
; aKillSrc
, aKillGuarded
: Boolean; aGuardedFirst
: Boolean=true);
251 fSource
:= aSrc
; fGuardedStream
:= aGuarded
;
252 fKillSource
:= aKillSrc
; fKillGuarded
:= aKillGuarded
;
253 fGuardedFirst
:= aGuardedFirst
;
256 destructor TSFSGuardStream
.Destroy ();
258 if fKillGuarded
and fGuardedFirst
then FreeAndNil(fGuardedStream
);
259 if fKillSource
then FreeAndNil(fSource
);
260 if fKillGuarded
and not fGuardedFirst
then FreeAndNil(fGuardedStream
);
264 function TSFSGuardStream
.Read (var buffer
; count
: LongInt): LongInt;
266 result
:= fSource
.Read(buffer
, count
);
269 function TSFSGuardStream
.Write (const buffer
; count
: LongInt): LongInt;
271 result
:= fSource
.Write(buffer
, count
);
274 function TSFSGuardStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
276 result
:= fSource
.Seek(offset
, origin
);
280 { TSFSMemoryStreamRO }
281 constructor TSFSMemoryStreamRO
.Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
283 fFreeMem
:= aFreeMem
;
286 SetPointer(pMem
, pSize
);
290 destructor TSFSMemoryStreamRO
.Destroy ();
292 if fFreeMem
and (fMem
<> nil) then FreeMem(fMem
);
295 function TSFSMemoryStreamRO
.Write (const buffer
; count
: LongInt): LongInt;
298 raise XStreamError
.Create('can''t write to read-only stream');
299 // ñîâñåì ñáðåíäèë...
303 // ////////////////////////////////////////////////////////////////////////// //
305 const ZBufSize
= 32768; // size of the buffer used for temporarily storing data from the child stream
308 constructor TUnZStream
.create (asrc
: TStream
; aSize
: Int64; aKillSrc
: Boolean; aSkipHeader
: boolean=false);
312 fKillSrc
:= aKillSrc
;
317 GetMem(fBuffer
, ZBufSize
);
318 fSkipHeader
:= aSkipHeader
;
319 fSrcStPos
:= fSrcSt
.position
;
320 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
321 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
322 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
326 destructor TUnZStream
.destroy ();
330 if fKillSrc
then fSrcSt
.Free();
335 function TUnZStream
.readBuf (var buffer
; count
: LongInt): LongInt;
341 if (fSize
>= 0) and (fPos
>= fSize
) then exit
;
344 fZlibSt
.next_out
:= @buffer
;
345 fZlibSt
.avail_out
:= count
;
346 sz
:= fZlibSt
.avail_out
;
347 while fZlibSt
.avail_out
> 0 do
349 if fZlibSt
.avail_in
= 0 then
352 fZlibSt
.next_in
:= fBuffer
;
353 fZlibSt
.avail_in
:= fSrcSt
.read(Fbuffer
^, ZBufSize
);
355 err
:= inflate(fZlibSt
, Z_NO_FLUSH
);
356 if (err
<> Z_OK
) and (err
<> Z_STREAM_END
) then raise XStreamError
.Create(zerror(err
));
357 Inc(result
, sz
-fZlibSt
.avail_out
);
358 Inc(fPos
, sz
-fZlibSt
.avail_out
);
359 sz
:= fZlibSt
.avail_out
;
360 if err
= Z_STREAM_END
then begin fSize
:= fPos
; break
; end;
366 procedure TUnZStream
.fixPos ();
368 buf
: array [0..4095] of Byte;
371 if fSkipToPos
< 0 then exit
;
372 //writeln('fixing pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
373 if fSkipToPos
< fPos
then reset();
374 while fPos
< fSkipToPos
do
376 if fSkipToPos
-fPos
> 4096 then rd
:= 4096 else rd
:= LongInt(fSkipToPos
-fPos
);
377 //writeln(' reading ', rd, ' bytes...');
378 rr
:= readBuf(buf
, rd
);
379 //writeln(' got ', rr, ' bytes; fPos=', fPos, '; fSkipToPos=', fSkipToPos);
380 if rr
<= 0 then raise XStreamError
.Create('seek error');
382 //writeln(' pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
387 procedure TUnZStream
.determineSize ();
389 buf
: array [0..4095] of Byte;
393 if fSize
>= 0 then exit
;
396 //writeln('determining unzstream size...');
399 rd
:= readBuf(buf
, 4096);
400 if rd
= 0 then break
;
403 //writeln(' unzstream size is ', fSize);
405 if fSkipToPos
< 0 then fSkipToPos
:= opos
;
410 function TUnZStream
.read (var buffer
; count
: LongInt): LongInt;
412 if fSkipToPos
>= 0 then fixPos();
413 result
:= readBuf(buffer
, count
);
417 function TUnZStream
.write (const buffer
; count
: LongInt): LongInt;
420 raise XStreamError
.Create('can''t write to read-only stream');
424 procedure TUnZStream
.reset ();
428 //writeln('doing RESET');
429 fSrcSt
.position
:= fSrcStPos
;
432 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
433 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
434 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
438 function TUnZStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
443 if fSkipToPos
>= 0 then cpos
:= fSkipToPos
;
445 soBeginning
: result
:= offset
;
446 soCurrent
: result
:= offset
+cpos
;
447 soEnd
: begin determineSize(); result
:= fSize
+offset
; end;
448 else raise XStreamError
.Create('invalid Seek() call');
449 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
451 if result
< 0 then result
:= 0;
452 fSkipToPos
:= result
;
453 //writeln('seek: ofs=', offset, '; origin=', origin, '; result=', result);