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
17 {$INCLUDE a_modes.inc}
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;
129 // fixed memory chunk
130 TSFSMemoryChunkStream
= class(TCustomMemoryStream
)
138 // if `pMem` is `nil`, stream will allocate it
139 constructor Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
140 destructor Destroy (); override;
142 procedure setup (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
144 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
145 function Read (var buffer
; count
: LongInt): LongInt; override;
146 function Write (const buffer
; count
: LongInt): LongInt; override;
148 property chunkSize
: Integer read fMemSize
;
149 property chunkData
: PByte read fMemBuf
;
159 { TSFSPartialStream }
160 constructor TSFSPartialStream
.Create (aSrc
: TStream
; aPos
, aSize
: Int64; aKillSrc
: Boolean; preBuf
: Pointer=nil; bufSz
: Integer=0);
164 if aPos
< 0 then aPos
:= aSrc
.Position
;
165 if aSize
< 0 then aSize
:= 0;
167 fKillSource
:= aKillSrc
;
174 SetLength(fPreBuf
, bufSz
);
175 Move(preBuf
^, fPreBuf
[0], bufSz
);
184 destructor TSFSPartialStream
.Destroy ();
186 if fKillSource
then FreeAndNil(fSource
);
190 procedure TSFSPartialStream
.CheckPos ();
193 if fSource.Position <> fStartPos+fCurrentPos-Length(fPreBuf) then
195 fSource.Position := fStartPos+fCurrentPos-Length(fPreBuf);
198 if fCurrentPos
>= length(fPreBuf
) then
200 //writeln('seeking at ', fCurrentPos, ' (real: ', fStartPos+fCurrentPos-Length(fPreBuf), ')');
201 fSource
.Position
:= fStartPos
+fCurrentPos
-Length(fPreBuf
);
203 fLastReadPos
:= fCurrentPos
;
206 function TSFSPartialStream
.Write (const buffer
; count
: LongInt): LongInt;
209 raise XStreamError
.Create('can''t write to read-only stream');
210 // à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü!
213 function TSFSPartialStream
.Read (var buffer
; count
: LongInt): LongInt;
219 if count
< 0 then raise XStreamError
.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á...
220 if count
= 0 then begin result
:= 0; exit
; end;
223 if (Length(fPreBuf
) > 0) and (fCurrentPos
< Length(fPreBuf
)) then
225 fLastReadPos
:= fCurrentPos
;
226 left
:= Length(fPreBuf
)-fCurrentPos
;
227 if left
> count
then left
:= count
;
230 Move(fPreBuf
[fCurrentPos
], pc
^, left
);
231 Inc(PChar(pc
), left
);
232 Inc(fCurrentPos
, left
);
233 fLastReadPos
:= fCurrentPos
;
236 if count
= 0 then exit
;
240 left
:= fSize
-fCurrentPos
;
241 if left
< count
then count
:= left
; // è òàê ñëó÷àåòñÿ...
244 rd
:= fSource
.Read(pc
^, count
);
246 Inc(fCurrentPos
, rd
);
247 fLastReadPos
:= fCurrentPos
;
255 function TSFSPartialStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
258 soBeginning
: result
:= offset
;
259 soCurrent
: result
:= offset
+fCurrentPos
;
260 soEnd
: result
:= fSize
+offset
;
261 else raise XStreamError
.Create('invalid Seek() call');
262 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
264 if result
< 0 then result
:= 0
265 else if result
> fSize
then result
:= fSize
;
266 fCurrentPos
:= result
;
271 constructor TSFSGuardStream
.Create (aSrc
, aGuarded
: TStream
; aKillSrc
, aKillGuarded
: Boolean; aGuardedFirst
: Boolean=true);
274 fSource
:= aSrc
; fGuardedStream
:= aGuarded
;
275 fKillSource
:= aKillSrc
; fKillGuarded
:= aKillGuarded
;
276 fGuardedFirst
:= aGuardedFirst
;
279 destructor TSFSGuardStream
.Destroy ();
281 if fKillGuarded
and fGuardedFirst
then FreeAndNil(fGuardedStream
);
282 if fKillSource
then FreeAndNil(fSource
);
283 if fKillGuarded
and not fGuardedFirst
then FreeAndNil(fGuardedStream
);
287 function TSFSGuardStream
.Read (var buffer
; count
: LongInt): LongInt;
289 result
:= fSource
.Read(buffer
, count
);
292 function TSFSGuardStream
.Write (const buffer
; count
: LongInt): LongInt;
294 result
:= fSource
.Write(buffer
, count
);
297 function TSFSGuardStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
299 result
:= fSource
.Seek(offset
, origin
);
303 { TSFSMemoryStreamRO }
304 constructor TSFSMemoryStreamRO
.Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
306 fFreeMem
:= aFreeMem
;
309 SetPointer(pMem
, pSize
);
313 destructor TSFSMemoryStreamRO
.Destroy ();
315 if fFreeMem
and (fMem
<> nil) then FreeMem(fMem
);
318 function TSFSMemoryStreamRO
.Write (const buffer
; count
: LongInt): LongInt;
321 raise XStreamError
.Create('can''t write to read-only stream');
322 // ñîâñåì ñáðåíäèë...
326 // ////////////////////////////////////////////////////////////////////////// //
328 const ZBufSize
= 32768; // size of the buffer used for temporarily storing data from the child stream
331 constructor TUnZStream
.create (asrc
: TStream
; aSize
: Int64; aKillSrc
: Boolean; aSkipHeader
: boolean=false);
335 fKillSrc
:= aKillSrc
;
340 GetMem(fBuffer
, ZBufSize
);
341 fSkipHeader
:= aSkipHeader
;
342 fSrcStPos
:= fSrcSt
.position
;
343 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
344 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
345 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
349 destructor TUnZStream
.destroy ();
353 if fKillSrc
then fSrcSt
.Free();
358 function TUnZStream
.readBuf (var buffer
; count
: LongInt): LongInt;
364 if (fSize
>= 0) and (fPos
>= fSize
) then exit
;
367 fZlibSt
.next_out
:= @buffer
;
368 fZlibSt
.avail_out
:= count
;
369 sz
:= fZlibSt
.avail_out
;
370 while fZlibSt
.avail_out
> 0 do
372 if fZlibSt
.avail_in
= 0 then
375 fZlibSt
.next_in
:= fBuffer
;
376 fZlibSt
.avail_in
:= fSrcSt
.read(Fbuffer
^, ZBufSize
);
378 err
:= inflate(fZlibSt
, Z_NO_FLUSH
);
379 if (err
<> Z_OK
) and (err
<> Z_STREAM_END
) then raise XStreamError
.Create(zerror(err
));
380 Inc(result
, sz
-fZlibSt
.avail_out
);
381 Inc(fPos
, sz
-fZlibSt
.avail_out
);
382 sz
:= fZlibSt
.avail_out
;
383 if err
= Z_STREAM_END
then begin fSize
:= fPos
; break
; end;
389 procedure TUnZStream
.fixPos ();
391 buf
: array [0..4095] of Byte;
394 if fSkipToPos
< 0 then exit
;
395 //writeln('fixing pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
396 if fSkipToPos
< fPos
then reset();
397 while fPos
< fSkipToPos
do
399 if fSkipToPos
-fPos
> 4096 then rd
:= 4096 else rd
:= LongInt(fSkipToPos
-fPos
);
400 //writeln(' reading ', rd, ' bytes...');
401 rr
:= readBuf(buf
, rd
);
402 //writeln(' got ', rr, ' bytes; fPos=', fPos, '; fSkipToPos=', fSkipToPos);
403 if rr
<= 0 then raise XStreamError
.Create('seek error');
405 //writeln(' pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
410 procedure TUnZStream
.determineSize ();
412 buf
: array [0..4095] of Byte;
416 if fSize
>= 0 then exit
;
419 //writeln('determining unzstream size...');
422 rd
:= readBuf(buf
, 4096);
423 if rd
= 0 then break
;
426 //writeln(' unzstream size is ', fSize);
428 if fSkipToPos
< 0 then fSkipToPos
:= opos
;
433 function TUnZStream
.read (var buffer
; count
: LongInt): LongInt;
435 if fSkipToPos
>= 0 then fixPos();
436 result
:= readBuf(buffer
, count
);
440 function TUnZStream
.write (const buffer
; count
: LongInt): LongInt;
443 raise XStreamError
.Create('can''t write to read-only stream');
447 procedure TUnZStream
.reset ();
451 //writeln('doing RESET');
452 fSrcSt
.position
:= fSrcStPos
;
455 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
456 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
457 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
461 function TUnZStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
466 if fSkipToPos
>= 0 then cpos
:= fSkipToPos
;
468 soBeginning
: result
:= offset
;
469 soCurrent
: result
:= offset
+cpos
;
470 soEnd
: begin determineSize(); result
:= fSize
+offset
; end;
471 else raise XStreamError
.Create('invalid Seek() call');
472 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
474 if result
< 0 then result
:= 0;
475 fSkipToPos
:= result
;
476 //writeln('seek: ofs=', offset, '; origin=', origin, '; result=', result);
480 // ////////////////////////////////////////////////////////////////////////// //
481 constructor TSFSMemoryChunkStream
.Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
487 setup(pMem
, pSize
, aFreeMem
);
491 procedure TSFSMemoryChunkStream
.setup (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
493 if fFreeMem
then FreeMem(fMemBuf
);
498 if (pSize
< 0) then raise XStreamError
.Create('invalid chunk size');
504 if (pMem
= nil) then raise XStreamError
.Create('out of memory for chunk');
512 fFreeMem
:= aFreeMem
;
513 fMemBuf
:= PByte(pMem
);
518 destructor TSFSMemoryChunkStream
.Destroy ();
520 if fFreeMem
then FreeMem(fMemBuf
);
525 function TSFSMemoryChunkStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
528 soBeginning
: result
:= offset
;
529 soCurrent
: result
:= offset
+fCurPos
;
530 soEnd
: result
:= fMemSize
+offset
;
531 else raise XStreamError
.Create('invalid Seek() call');
533 if (result
< 0) then raise XStreamError
.Create('invalid Seek() call');
534 if (result
> fMemSize
) then result
:= fMemSize
;
539 function TSFSMemoryChunkStream
.Read (var buffer
; count
: LongInt): LongInt;
543 if (count
< 0) then raise XStreamError
.Create('negative read');
544 left
:= fMemSize
-fCurPos
;
545 if (left
< 0) then raise XStreamError
.Create('internal error in TSFSMemoryChunkStream (read)');
546 if (count
> left
) then count
:= left
;
547 if (count
> 0) then Move((fMemBuf
+fCurPos
)^, buffer
, count
);
553 function TSFSMemoryChunkStream
.Write (const buffer
; count
: LongInt): LongInt;
557 if (count
< 0) then raise XStreamError
.Create('negative write');
558 left
:= fMemSize
-fCurPos
;
559 if (left
< 0) then raise XStreamError
.Create('internal error in TSFSMemoryChunkStream (write)');
560 if (count
> left
) then count
:= left
;
561 //writeln('mcs: writing ', count, ' bytes at ofs ', fCurPos, ' (total size is ', fMemSize, ')');
562 if (count
> 0) then Move(buffer
, (fMemBuf
+fCurPos
)^, count
);