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, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 // special stream classes
16 {$INCLUDE a_modes.inc}
28 XStreamError
= class(Exception
);
30 // read-only ïîòîê äëÿ èçâëå÷åíèÿ èç èñõîäíîãî òîëüêî êóñî÷êà
31 TSFSPartialStream
= class(TStream
)
33 fSource
: TStream
; // èñõîäíûé ïîòîê
34 fKillSource
: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè?
35 fLastReadPos
: Int64; // ïîñëåäíèé Read() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos)
36 fCurrentPos
: Int64; // ïîñëåäíèé Seek() îñòàíîâèëñÿ çäåñü (îòíîñ. fStartPos)
37 fStartPos
: Int64; // íà÷àëî êóñî÷êà
38 fSize
: Int64; // äëèíà êóñî÷êà
39 fPreBuf
: packed array of Byte; // ýòîò áóôåð áóäåò ïåðåä ôàéëîì
41 procedure CheckPos ();
44 // aSrc: ïîòîê-èñõîäíèê.
45 // aPos: íà÷àëüíàÿ ïîçèöèÿ â ïîòîêå. -1 -- ñ òåêóùåé.
46 // åñëè aPos < òåêóùåé ïîçèöèè, òî èñõîäíûé ïîòîê äîëæåí
47 // íîðìàëüíî ïîääåðæèâàòü Seek()!
48 // aSize: êîëè÷åñòâî áàéòèêîâ, êîòîðîå ìîæíî ïðî÷åñòü èç ïîòîêà.
49 // åñëè ìåíüøå íóëÿ -- òî äî êîíöà.
50 // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì?
51 // òàêæå ìîæåò ïðèøïàíäîðèòü ê íà÷àëó ôàéëà áóôåð. bufSz áóäåò äîáàâëåíî ê
53 constructor Create (aSrc
: TStream
; aPos
, aSize
: Int64; aKillSrc
: Boolean; preBuf
: Pointer=nil; bufSz
: Integer=0);
54 destructor Destroy (); override;
56 // íîðìàëèçóåò count è ÷èòàåò.
57 function Read (var buffer
; count
: LongInt): LongInt; override;
58 // Write() ïðîñòî ãðîìêî ïàäàåò.
59 function Write (const buffer
; count
: LongInt): LongInt; override;
60 // Seek() ðåàëèçîâàíî, ÷òîáû ìîãëà ðàáîòàòü ïðîïåðòÿ Size.
61 // âîîáùå-òî ìîæíî ïåðåêðûòü ìåòîä GetSize(), íî âäðóã êàêîé
62 // áîëüíîé íà ãîëîâó êîäåð áóäåò ïîëó÷àòü ðàçìåð ïðè ïîìîùè
64 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
67 // this stream can kill both `proxied` and `guarded` streams on closing
68 TSFSGuardStream
= class(TStream
)
70 fSource
: TStream
; // èñõîäíûé ïîòîê
71 fGuardedStream
: TStream
; // ïîòîê, êîòîðûé çàâàëèì ïðè ïîìèðàíèè
72 fKillSource
: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè?
73 fKillGuarded
: Boolean; // óáèâàòü îõðàíÿåìûé ïðè ïîìèðàíèè?
74 fGuardedFirst
: Boolean; // ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî?
77 // aSrc: ïîòîê-èñõîäíèê (íà êîòîðûé çàìàïåíû îïåðàöèè ÷òåíèÿ/çàïèñè).
78 // aKillSrc: óáèâàòü ëè èñõîäíûé ïîòîê, êîãäà ñàìè óìèðàåì?
79 // aKillGuarded: óáèâàòü ëè îõðàíÿåìûé ïîòîê, êîãäà ñàìè óìèðàåì?
80 // aGuardedFirst: true: ïðè ñìåðòè ïåðâûì ïðèøèáàåì îõðàíÿåìîãî.
81 constructor Create (aSrc
, aGuarded
: TStream
; aKillSrc
, aKillGuarded
: Boolean; aGuardedFirst
: Boolean=true);
82 destructor Destroy (); override;
84 // íèæåñëåäóþùåå çàìàïëåíî íà fSource
85 function Read (var buffer
; count
: LongInt): LongInt; override;
86 function Write (const buffer
; count
: LongInt): LongInt; override;
87 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
90 TSFSMemoryStreamRO
= class(TCustomMemoryStream
)
96 constructor Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
97 destructor Destroy (); override;
99 function Write (const buffer
; count
: LongInt): LongInt; override;
102 TUnZStream
= class(TStream
)
108 fSkipHeader
: Boolean;
109 fSize
: Int64; // can be -1
111 fSkipToPos
: Int64; // >0: skip to this position
115 function readBuf (var buffer
; count
: LongInt): LongInt;
117 procedure determineSize ();
120 // `aSize` can be -1 if stream size is unknown
121 constructor create (asrc
: TStream
; aSize
: Int64; aKillSrc
: Boolean; aSkipHeader
: boolean=false);
122 destructor destroy (); override;
123 function read (var buffer
; count
: LongInt): LongInt; override;
124 function write (const buffer
; count
: LongInt): LongInt; override;
125 function seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
128 // fixed memory chunk
129 TSFSMemoryChunkStream
= class(TStream
)
137 // if `pMem` is `nil`, stream will allocate it
138 constructor Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
139 destructor Destroy (); override;
141 procedure setup (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
143 function Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64; override;
144 function Read (var buffer
; count
: LongInt): LongInt; override;
145 function Write (const buffer
; count
: LongInt): LongInt; override;
147 property chunkSize
: Integer read fMemSize
;
148 property chunkData
: PByte read fMemBuf
;
158 { TSFSPartialStream }
159 constructor TSFSPartialStream
.Create (aSrc
: TStream
; aPos
, aSize
: Int64; aKillSrc
: Boolean; preBuf
: Pointer=nil; bufSz
: Integer=0);
163 if aPos
< 0 then aPos
:= aSrc
.Position
;
164 if aSize
< 0 then aSize
:= 0;
166 fKillSource
:= aKillSrc
;
173 SetLength(fPreBuf
, bufSz
);
174 Move(preBuf
^, fPreBuf
[0], bufSz
);
183 destructor TSFSPartialStream
.Destroy ();
185 if fKillSource
then FreeAndNil(fSource
);
189 procedure TSFSPartialStream
.CheckPos ();
192 if fSource.Position <> fStartPos+fCurrentPos-Length(fPreBuf) then
194 fSource.Position := fStartPos+fCurrentPos-Length(fPreBuf);
197 if fCurrentPos
>= length(fPreBuf
) then
199 //writeln('seeking at ', fCurrentPos, ' (real: ', fStartPos+fCurrentPos-Length(fPreBuf), ')');
200 fSource
.Position
:= fStartPos
+fCurrentPos
-Length(fPreBuf
);
202 fLastReadPos
:= fCurrentPos
;
205 function TSFSPartialStream
.Write (const buffer
; count
: LongInt): LongInt;
208 raise XStreamError
.Create('can''t write to read-only stream');
209 // à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü!
212 function TSFSPartialStream
.Read (var buffer
; count
: LongInt): LongInt;
218 if count
< 0 then raise XStreamError
.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á...
219 if count
= 0 then begin result
:= 0; exit
; end;
222 if (Length(fPreBuf
) > 0) and (fCurrentPos
< Length(fPreBuf
)) then
224 fLastReadPos
:= fCurrentPos
;
225 left
:= Length(fPreBuf
)-fCurrentPos
;
226 if left
> count
then left
:= count
;
229 Move(fPreBuf
[fCurrentPos
], pc
^, left
);
230 Inc(PChar(pc
), left
);
231 Inc(fCurrentPos
, left
);
232 fLastReadPos
:= fCurrentPos
;
235 if count
= 0 then exit
;
239 left
:= fSize
-fCurrentPos
;
240 if left
< count
then count
:= left
; // è òàê ñëó÷àåòñÿ...
243 rd
:= fSource
.Read(pc
^, count
);
245 Inc(fCurrentPos
, rd
);
246 fLastReadPos
:= fCurrentPos
;
254 function TSFSPartialStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
257 soBeginning
: result
:= offset
;
258 soCurrent
: result
:= offset
+fCurrentPos
;
259 soEnd
: result
:= fSize
+offset
;
260 else raise XStreamError
.Create('invalid Seek() call');
261 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
263 if result
< 0 then result
:= 0
264 else if result
> fSize
then result
:= fSize
;
265 fCurrentPos
:= result
;
270 constructor TSFSGuardStream
.Create (aSrc
, aGuarded
: TStream
; aKillSrc
, aKillGuarded
: Boolean; aGuardedFirst
: Boolean=true);
273 fSource
:= aSrc
; fGuardedStream
:= aGuarded
;
274 fKillSource
:= aKillSrc
; fKillGuarded
:= aKillGuarded
;
275 fGuardedFirst
:= aGuardedFirst
;
278 destructor TSFSGuardStream
.Destroy ();
280 if fKillGuarded
and fGuardedFirst
then FreeAndNil(fGuardedStream
);
281 if fKillSource
then FreeAndNil(fSource
);
282 if fKillGuarded
and not fGuardedFirst
then FreeAndNil(fGuardedStream
);
286 function TSFSGuardStream
.Read (var buffer
; count
: LongInt): LongInt;
288 result
:= fSource
.Read(buffer
, count
);
291 function TSFSGuardStream
.Write (const buffer
; count
: LongInt): LongInt;
293 result
:= fSource
.Write(buffer
, count
);
296 function TSFSGuardStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
298 result
:= fSource
.Seek(offset
, origin
);
302 { TSFSMemoryStreamRO }
303 constructor TSFSMemoryStreamRO
.Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
305 fFreeMem
:= aFreeMem
;
308 SetPointer(pMem
, pSize
);
312 destructor TSFSMemoryStreamRO
.Destroy ();
314 if fFreeMem
and (fMem
<> nil) then FreeMem(fMem
);
317 function TSFSMemoryStreamRO
.Write (const buffer
; count
: LongInt): LongInt;
320 raise XStreamError
.Create('can''t write to read-only stream');
321 // ñîâñåì ñáðåíäèë...
325 // ////////////////////////////////////////////////////////////////////////// //
327 const ZBufSize
= 32768; // size of the buffer used for temporarily storing data from the child stream
330 constructor TUnZStream
.create (asrc
: TStream
; aSize
: Int64; aKillSrc
: Boolean; aSkipHeader
: boolean=false);
334 fKillSrc
:= aKillSrc
;
339 GetMem(fBuffer
, ZBufSize
);
340 fSkipHeader
:= aSkipHeader
;
341 fSrcStPos
:= fSrcSt
.position
;
342 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
343 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
344 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
348 destructor TUnZStream
.destroy ();
352 if fKillSrc
then fSrcSt
.Free();
357 function TUnZStream
.readBuf (var buffer
; count
: LongInt): LongInt;
363 if (fSize
>= 0) and (fPos
>= fSize
) then exit
;
366 fZlibSt
.next_out
:= @buffer
;
367 fZlibSt
.avail_out
:= count
;
368 sz
:= fZlibSt
.avail_out
;
369 while fZlibSt
.avail_out
> 0 do
371 if fZlibSt
.avail_in
= 0 then
374 fZlibSt
.next_in
:= fBuffer
;
375 fZlibSt
.avail_in
:= fSrcSt
.read(Fbuffer
^, ZBufSize
);
377 err
:= inflate(fZlibSt
, Z_NO_FLUSH
);
378 if (err
<> Z_OK
) and (err
<> Z_STREAM_END
) then raise XStreamError
.Create(zerror(err
));
379 Inc(result
, sz
-fZlibSt
.avail_out
);
380 Inc(fPos
, sz
-fZlibSt
.avail_out
);
381 sz
:= fZlibSt
.avail_out
;
382 if err
= Z_STREAM_END
then begin fSize
:= fPos
; break
; end;
388 procedure TUnZStream
.fixPos ();
390 buf
: array [0..4095] of Byte;
393 if fSkipToPos
< 0 then exit
;
394 //writeln('fixing pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
395 if fSkipToPos
< fPos
then reset();
396 while fPos
< fSkipToPos
do
398 if fSkipToPos
-fPos
> 4096 then rd
:= 4096 else rd
:= LongInt(fSkipToPos
-fPos
);
399 //writeln(' reading ', rd, ' bytes...');
400 rr
:= readBuf(buf
, rd
);
401 //writeln(' got ', rr, ' bytes; fPos=', fPos, '; fSkipToPos=', fSkipToPos);
402 if rr
<= 0 then raise XStreamError
.Create('seek error');
404 //writeln(' pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos);
409 procedure TUnZStream
.determineSize ();
411 buf
: array [0..4095] of Byte;
415 if fSize
>= 0 then exit
;
418 //writeln('determining unzstream size...');
421 rd
:= readBuf(buf
, 4096);
422 if rd
= 0 then break
;
425 //writeln(' unzstream size is ', fSize);
427 if fSkipToPos
< 0 then fSkipToPos
:= opos
;
432 function TUnZStream
.read (var buffer
; count
: LongInt): LongInt;
434 if fSkipToPos
>= 0 then fixPos();
435 result
:= readBuf(buffer
, count
);
439 function TUnZStream
.write (const buffer
; count
: LongInt): LongInt;
442 raise XStreamError
.Create('can''t write to read-only stream');
446 procedure TUnZStream
.reset ();
450 //writeln('doing RESET');
451 fSrcSt
.position
:= fSrcStPos
;
454 FillChar(fZlibSt
, sizeof(fZlibSt
), 0);
455 if fSkipHeader
then err
:= inflateInit2(fZlibSt
, -MAX_WBITS
) else err
:= inflateInit(fZlibSt
);
456 if err
<> Z_OK
then raise XStreamError
.Create(zerror(err
));
460 function TUnZStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
465 if fSkipToPos
>= 0 then cpos
:= fSkipToPos
;
467 soBeginning
: result
:= offset
;
468 soCurrent
: result
:= offset
+cpos
;
469 soEnd
: begin determineSize(); result
:= fSize
+offset
; end;
470 else raise XStreamError
.Create('invalid Seek() call');
471 // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
473 if result
< 0 then result
:= 0;
474 fSkipToPos
:= result
;
475 //writeln('seek: ofs=', offset, '; origin=', origin, '; result=', result);
479 // ////////////////////////////////////////////////////////////////////////// //
480 constructor TSFSMemoryChunkStream
.Create (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
486 setup(pMem
, pSize
, aFreeMem
);
490 procedure TSFSMemoryChunkStream
.setup (pMem
: Pointer; pSize
: Integer; aFreeMem
: Boolean=false);
492 if fFreeMem
then FreeMem(fMemBuf
);
497 if (pSize
< 0) then raise XStreamError
.Create('invalid chunk size');
503 if (pMem
= nil) then raise XStreamError
.Create('out of memory for chunk');
511 fFreeMem
:= aFreeMem
;
512 fMemBuf
:= PByte(pMem
);
517 destructor TSFSMemoryChunkStream
.Destroy ();
519 if fFreeMem
then FreeMem(fMemBuf
);
524 function TSFSMemoryChunkStream
.Seek (const offset
: Int64; origin
: TSeekOrigin
): Int64;
527 soBeginning
: result
:= offset
;
528 soCurrent
: result
:= offset
+fCurPos
;
529 soEnd
: result
:= fMemSize
+offset
;
530 else raise XStreamError
.Create('invalid Seek() call');
532 if (result
< 0) then raise XStreamError
.Create('invalid Seek() call');
533 if (result
> fMemSize
) then result
:= fMemSize
;
538 function TSFSMemoryChunkStream
.Read (var buffer
; count
: LongInt): LongInt;
542 if (count
< 0) then raise XStreamError
.Create('negative read');
543 left
:= fMemSize
-fCurPos
;
544 if (left
< 0) then raise XStreamError
.Create('internal error in TSFSMemoryChunkStream (read)');
545 if (count
> left
) then count
:= left
;
546 if (count
> 0) then Move((fMemBuf
+fCurPos
)^, buffer
, count
);
552 function TSFSMemoryChunkStream
.Write (const buffer
; count
: LongInt): LongInt;
556 if (count
< 0) then raise XStreamError
.Create('negative write');
557 left
:= fMemSize
-fCurPos
;
558 if (left
< 0) then raise XStreamError
.Create('internal error in TSFSMemoryChunkStream (write)');
559 if (count
> left
) then count
:= left
;
560 if (count
> 0) then Move(buffer
, (fMemBuf
+fCurPos
)^, count
);