diff --git a/src/sfs/xstreams.pas b/src/shared/xstreams.pas
similarity index 63%
rename from src/sfs/xstreams.pas
rename to src/shared/xstreams.pas
index 1861c616d0972578f2fcb967dc051dc4e9fc9443..f62582b1bd0854acf19a77242f37788fb80732b9 100644 (file)
rename from src/sfs/xstreams.pas
rename to src/shared/xstreams.pas
index 1861c616d0972578f2fcb967dc051dc4e9fc9443..f62582b1bd0854acf19a77242f37788fb80732b9 100644 (file)
--- a/src/sfs/xstreams.pas
+++ b/src/shared/xstreams.pas
// special stream classes
// special stream classes
-{$MODE DELPHI}
-{.$R-}
+{$MODE OBJFPC}
+{$R+}
unit xstreams;
interface
uses
unit xstreams;
interface
uses
- SysUtils, Classes, SDL2;
+ SysUtils, Classes,
+ zbase{z_stream};
type
type
- // ïîòîê-îá¸ðòêà äëÿ SDL_RWops
- TSFSSDLStream = class(TStream)
- protected
- fRW: PSDL_RWops; // SDL-íàÿ ïðîêëàäêà
- fFreeSource: Boolean; // óáèâàòü èñõîäíèê ïðè ïîìèðàíèè?
-
- public
- constructor Create (aSrc: PSDL_RWops; aFreeSource: Boolean=true);
- destructor Destroy (); override;
-
- function Read (var buffer; count: LongInt): LongInt; override;
- function Write (const buffer; count: LongInt): LongInt; override;
- function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override;
- end;
+ XStreamError = class(Exception);
// read-only ïîòîê äëÿ èçâëå÷åíèÿ èç èñõîäíîãî òîëüêî êóñî÷êà
TSFSPartialStream = class(TStream)
// read-only ïîòîê äëÿ èçâëå÷åíèÿ èç èñõîäíîãî òîëüêî êóñî÷êà
TSFSPartialStream = class(TStream)
function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override;
end;
function Seek (const offset: Int64; origin: TSeekOrigin): Int64; override;
end;
+ // this stream can kill both `proxied` and `guarded` streams on closing
TSFSGuardStream = class(TStream)
protected
fSource: TStream; // èñõîäíûé ïîòîê
TSFSGuardStream = class(TStream)
protected
fSource: TStream; // èñõîäíûé ïîòîê
function Write (const buffer; count: LongInt): LongInt; override;
end;
function Write (const buffer; count: LongInt): LongInt; override;
end;
+ TUnZStream = class(TStream)
+ protected
+ fSrcSt: TStream;
+ fZlibSt: z_stream;
+ fBuffer: PByte;
+ fPos: Int64;
+ fSkipHeader: Boolean;
+ fSize: Int64; // can be -1
+ fSrcStPos: Int64;
+ fSkipToPos: Int64; // >0: skip to this position
+
+ procedure reset ();
+ function readBuf (var buffer; count: LongInt): LongInt;
+ procedure fixPos ();
+ procedure determineSize ();
-implementation
-
-uses
- sfs; // for ESFSError
-
-{ TSFSSDLStream }
-constructor TSFSSDLStream.Create (aSrc: PSDL_RWops; aFreeSource: Boolean=true);
-begin
- inherited Create();
- //ASSERT(aSrc <> nil);
- fRW := aSrc;
- fFreeSource := aFreeSource;
-end;
-
-destructor TSFSSDLStream.Destroy ();
-begin
- if fFreeSource and (fRW <> nil) then SDL_FreeRW(fRW);
- inherited Destroy();
-end;
+ public
+ // `aSize` can be -1 if stream size is unknown
+ constructor create (asrc: TStream; aSize: Int64; aSkipHeader: boolean=false);
+ destructor destroy (); override;
+ function read (var buffer; count: LongInt): LongInt; override;
+ function write (const buffer; count: LongInt): LongInt; override;
+ function seek (const offset: Int64; origin: TSeekOrigin): Int64; override;
+ end;
-function TSFSSDLStream.Read (var buffer; count: LongInt): LongInt;
-begin
- if (fRW = nil) or (count <= 0) then begin result := 0; exit; end;
- result := SDL_RWread(fRW, @buffer, 1, count);
-end;
-function TSFSSDLStream.Write (const buffer; count: LongInt): LongInt;
-begin
- if (fRW = nil) or (count <= 0) then begin result := 0; exit; end;
- result := SDL_RWwrite(fRW, @buffer, 1, count);
-end;
+implementation
-function TSFSSDLStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64;
-var
- ss: Integer;
-begin
- if fRW = nil then begin result := 0; exit; end;
- case origin of
- soBeginning: ss := RW_SEEK_SET;
- soCurrent: ss := RW_SEEK_CUR;
- soEnd: ss := RW_SEEK_END;
- else raise ESFSError.Create('invalid Seek() call');
- // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
- end;
- result := SDL_RWseek(fRW, offset, ss);
- if result = -1 then raise ESFSError.Create('Seek() error');
-end;
+uses
+ zinflate;
{ TSFSPartialStream }
{ TSFSPartialStream }
function TSFSPartialStream.Write (const buffer; count: LongInt): LongInt;
begin
result := 0;
function TSFSPartialStream.Write (const buffer; count: LongInt): LongInt;
begin
result := 0;
- raise ESFSError.Create('can''t write to read-only stream');
+ raise XStreamError.Create('can''t write to read-only stream');
// à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü!
end;
// à íå õîäè, íåõîðîøèé, â íàø ñàäèê ãóëÿòü!
end;
pc: Pointer;
rd: LongInt;
begin
pc: Pointer;
rd: LongInt;
begin
- if count < 0 then raise ESFSError.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á...
+ if count < 0 then raise XStreamError.Create('invalid Read() call'); // ñêàçî÷íûé äîëáî¸á...
if count = 0 then begin result := 0; exit; end;
pc := @buffer;
result := 0;
if count = 0 then begin result := 0; exit; end;
pc := @buffer;
result := 0;
soBeginning: result := offset;
soCurrent: result := offset+fCurrentPos;
soEnd: result := fSize+offset;
soBeginning: result := offset;
soCurrent: result := offset+fCurrentPos;
soEnd: result := fSize+offset;
- else raise ESFSError.Create('invalid Seek() call');
+ else raise XStreamError.Create('invalid Seek() call');
// äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
end;
if result < 0 then result := 0
// äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
end;
if result < 0 then result := 0
function TSFSMemoryStreamRO.Write (const buffer; count: LongInt): LongInt;
begin
result := 0;
function TSFSMemoryStreamRO.Write (const buffer; count: LongInt): LongInt;
begin
result := 0;
- raise ESFSError.Create('can''t write to read-only stream');
+ raise XStreamError.Create('can''t write to read-only stream');
// ñîâñåì ñáðåíäèë...
end;
// ñîâñåì ñáðåíäèë...
end;
+// ////////////////////////////////////////////////////////////////////////// //
+{ TUnZStream }
+const ZBufSize = 32768; // size of the buffer used for temporarily storing data from the child stream
+
+constructor TUnZStream.create (asrc: TStream; aSize: Int64; aSkipHeader: boolean=false);
+var
+ err: Integer;
+begin
+ fPos := 0;
+ fSkipToPos := -1;
+ fSrcSt := asrc;
+ fSize := aSize;
+ GetMem(fBuffer, ZBufSize);
+ fSkipHeader := aSkipHeader;
+ if fSkipHeader then err := inflateInit2(fZlibSt, -MAX_WBITS) else err := inflateInit(fZlibSt);
+ if err <> Z_OK then raise XStreamError.Create(zerror(err));
+ fSrcStPos := fSrcSt.position;
+end;
+
+destructor TUnZStream.destroy ();
+begin
+ inflateEnd(fZlibSt);
+ FreeMem(fBuffer);
+ fSrcSt.Free;
+ inherited destroy;
+end;
+
+function TUnZStream.readBuf (var buffer; count: LongInt): LongInt;
+var
+ err: Integer;
+ lastavail: LongInt;
+begin
+ fZlibSt.next_out := @buffer;
+ fZlibSt.avail_out := count;
+ lastavail := count;
+ while fZlibSt.avail_out <> 0 do
+ begin
+ if fZlibSt.avail_in = 0 then
+ begin
+ // refill the buffer
+ fZlibSt.next_in := fBuffer;
+ fZlibSt.avail_in := fSrcSt.read(Fbuffer^, ZBufSize);
+ //Inc(compressed_read, fZlibSt.avail_in);
+ Inc(fPos, lastavail-fZlibSt.avail_out);
+ lastavail := fZlibSt.avail_out;
+ end;
+ err := inflate(fZlibSt, Z_NO_FLUSH);
+ if err = Z_STREAM_END then fSize := fPos; break;
+ if err <> Z_OK then raise XStreamError.Create(zerror(err));
+ end;
+ //if err = Z_STREAM_END then Dec(compressed_read, fZlibSt.avail_in);
+ Inc(fPos, lastavail-fZlibSt.avail_out);
+ result := count-fZlibSt.avail_out;
+end;
+
+procedure TUnZStream.fixPos ();
+var
+ buf: array [0..4095] of Byte;
+ rd, rr: LongInt;
+begin
+ if fSkipToPos < 0 then exit;
+ if fSkipToPos > fPos then reset();
+ while fPos < fSkipToPos do
+ begin
+ if fSkipToPos-fPos > 4096 then rd := 4096 else rd := LongInt(fSkipToPos-fPos);
+ rr := readBuf(buf, rd);
+ if rd <> rr then raise XStreamError.Create('seek error');
+ end;
+ fSkipToPos := -1;
+end;
+
+procedure TUnZStream.determineSize ();
+var
+ buf: array [0..4095] of Byte;
+ rd: LongInt;
+begin
+ if fSize >= 0 then exit;
+ while true do
+ begin
+ rd := readBuf(buf, 4096);
+ if rd <> 4096 then break;
+ end;
+ fSize := fPos;
+end;
+
+function TUnZStream.read (var buffer; count: LongInt): LongInt;
+begin
+ if fSkipToPos >= 0 then fixPos();
+ result := readBuf(buffer, count);
+end;
+
+function TUnZStream.write (const buffer; count: LongInt): LongInt;
+begin
+ result := 0;
+ raise XStreamError.Create('can''t write to read-only stream');
+end;
+
+procedure TUnZStream.reset ();
+var
+ err: Integer;
+begin
+ fSrcSt.position := fSrcStPos;
+ fPos := 0;
+ inflateEnd(fZlibSt);
+ if fSkipHeader then err := inflateInit2(fZlibSt, -MAX_WBITS) else err := inflateInit(fZlibSt);
+ if err <> Z_OK then raise XStreamError.Create(zerror(err));
+end;
+
+function TUnZStream.Seek (const offset: Int64; origin: TSeekOrigin): Int64;
+begin
+ case origin of
+ soBeginning: result := offset;
+ soCurrent: result := offset+fPos;
+ soEnd: begin if fSize = -1 then determineSize(); result := fSize+offset; end;
+ else raise XStreamError.Create('invalid Seek() call');
+ // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð.
+ end;
+ if result < 0 then result := 0;
+ fSkipToPos := result;
+end;
+
+
end.
end.