X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fshared%2Fxstreams.pas;h=81a104cc9e33be77409318c25766fcdc5b0382b3;hb=b744498c3f6d4d30731c74ba376e15e71de9b610;hp=f62582b1bd0854acf19a77242f37788fb80732b9;hpb=03ec2f1d27fdcff9a5a8785806fcd8449f2537a9;p=d2df-sdl.git diff --git a/src/shared/xstreams.pas b/src/shared/xstreams.pas index f62582b..81a104c 100644 --- a/src/shared/xstreams.pas +++ b/src/shared/xstreams.pas @@ -1,6 +1,21 @@ +(* Copyright (C) DooM 2D:Forever Developers + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *) // special stream classes -{$MODE OBJFPC} -{$R+} +{$INCLUDE a_modes.inc} +{.$R+} unit xstreams; interface @@ -95,6 +110,7 @@ type fSize: Int64; // can be -1 fSrcStPos: Int64; fSkipToPos: Int64; // >0: skip to this position + fKillSrc: Boolean; procedure reset (); function readBuf (var buffer; count: LongInt): LongInt; @@ -103,7 +119,7 @@ type public // `aSize` can be -1 if stream size is unknown - constructor create (asrc: TStream; aSize: Int64; aSkipHeader: boolean=false); + constructor create (asrc: TStream; aSize: Int64; aKillSrc: Boolean; aSkipHeader: boolean=false); destructor destroy (); override; function read (var buffer; count: LongInt): LongInt; override; function write (const buffer; count: LongInt): LongInt; override; @@ -288,121 +304,153 @@ 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); + +constructor TUnZStream.create (asrc: TStream; aSize: Int64; aKillSrc: Boolean; aSkipHeader: boolean=false); var err: Integer; begin + fKillSrc := aKillSrc; fPos := 0; fSkipToPos := -1; fSrcSt := asrc; fSize := aSize; GetMem(fBuffer, ZBufSize); fSkipHeader := aSkipHeader; + fSrcStPos := fSrcSt.position; + FillChar(fZlibSt, sizeof(fZlibSt), 0); 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; + if fKillSrc then fSrcSt.Free(); + inherited Destroy(); end; + function TUnZStream.readBuf (var buffer; count: LongInt): LongInt; var err: Integer; - lastavail: LongInt; + sz: LongInt; begin - fZlibSt.next_out := @buffer; - fZlibSt.avail_out := count; - lastavail := count; - while fZlibSt.avail_out <> 0 do + result := 0; + if (fSize >= 0) and (fPos >= fSize) then exit; + if count > 0 then begin - if fZlibSt.avail_in = 0 then + fZlibSt.next_out := @buffer; + fZlibSt.avail_out := count; + sz := fZlibSt.avail_out; + while fZlibSt.avail_out > 0 do 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; + if fZlibSt.avail_in = 0 then + begin + // refill the buffer + fZlibSt.next_in := fBuffer; + fZlibSt.avail_in := fSrcSt.read(Fbuffer^, ZBufSize); + end; + err := inflate(fZlibSt, Z_NO_FLUSH); + if (err <> Z_OK) and (err <> Z_STREAM_END) then raise XStreamError.Create(zerror(err)); + Inc(result, sz-fZlibSt.avail_out); + Inc(fPos, sz-fZlibSt.avail_out); + sz := fZlibSt.avail_out; + if err = Z_STREAM_END then begin fSize := fPos; break; end; 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(); + //writeln('fixing pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos); + if fSkipToPos < fPos then reset(); while fPos < fSkipToPos do begin if fSkipToPos-fPos > 4096 then rd := 4096 else rd := LongInt(fSkipToPos-fPos); + //writeln(' reading ', rd, ' bytes...'); rr := readBuf(buf, rd); - if rd <> rr then raise XStreamError.Create('seek error'); + //writeln(' got ', rr, ' bytes; fPos=', fPos, '; fSkipToPos=', fSkipToPos); + if rr <= 0 then raise XStreamError.Create('seek error'); end; + //writeln(' pos: fPos=', fPos, '; fSkipToPos=', fSkipToPos); fSkipToPos := -1; end; + procedure TUnZStream.determineSize (); var buf: array [0..4095] of Byte; rd: LongInt; + opos: Int64; begin if fSize >= 0 then exit; - while true do - begin - rd := readBuf(buf, 4096); - if rd <> 4096 then break; + opos := fPos; + try + //writeln('determining unzstream size...'); + while true do + begin + rd := readBuf(buf, 4096); + if rd = 0 then break; + end; + fSize := fPos; + //writeln(' unzstream size is ', fSize); + finally + if fSkipToPos < 0 then fSkipToPos := opos; 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 + //writeln('doing RESET'); fSrcSt.position := fSrcStPos; fPos := 0; inflateEnd(fZlibSt); + FillChar(fZlibSt, sizeof(fZlibSt), 0); 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; +var + cpos: Int64; begin + cpos := fPos; + if fSkipToPos >= 0 then cpos := fSkipToPos; case origin of soBeginning: result := offset; - soCurrent: result := offset+fPos; - soEnd: begin if fSize = -1 then determineSize(); result := fSize+offset; end; + soCurrent: result := offset+cpos; + soEnd: begin determineSize(); result := fSize+offset; end; else raise XStreamError.Create('invalid Seek() call'); // äðóãèõ íå áûâàåò. à ó êîãî áûâàåò, òîìó ÿ íå äîêòîð. end; if result < 0 then result := 0; fSkipToPos := result; + //writeln('seek: ofs=', offset, '; origin=', origin, '; result=', result); end;