X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fsfs%2FsfsZipFS.pas;h=892dd636802cbf3e3e3edbb605a6401468064ce8;hb=03ec2f1d27fdcff9a5a8785806fcd8449f2537a9;hp=b7e9051523e952e7535549f684da3e840b14b1cb;hpb=2c514f6c21866e5434865d550238e62979c7add2;p=d2df-sdl.git diff --git a/src/sfs/sfsZipFS.pas b/src/sfs/sfsZipFS.pas index b7e9051..892dd63 100644 --- a/src/sfs/sfsZipFS.pas +++ b/src/sfs/sfsZipFS.pas @@ -10,8 +10,8 @@ // {.$DEFINE SFS_DEBUG_ZIPFS} {.$DEFINE SFS_ZIPFS_FULL} -{$MODE DELPHI} -{.$R-} +{$MODE OBJFPC} +{$R+} unit sfsZipFS; interface @@ -51,8 +51,8 @@ type TSFSZipVolumeFactory = class (TSFSVolumeFactory) public - function IsMyVolumePrefix (const prefix: TSFSString): Boolean; override; - function Produce (const prefix, fileName: TSFSString; st: TStream): TSFSVolume; override; + function IsMyVolumePrefix (const prefix: AnsiString): Boolean; override; + function Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; override; procedure Recycle (vol: TSFSVolume); override; end; @@ -61,7 +61,7 @@ type implementation uses - zstream, xstreams; + zstream, xstreams, utils; type @@ -153,7 +153,7 @@ begin for f := 1 to length(s0) do begin if f > length(s1) then begin result := f; exit; end; - if SFSUpCase(s0[f]) <> SFSUpCase(s1[f]) then begin result := f; exit; end; + if UpCase1251(s0[f]) <> UpCase1251(s1[f]) then begin result := f; exit; end; end; result := length(s0); end; @@ -215,6 +215,9 @@ var crc, psz, usz: LongWord; buf: packed array of Byte; bufPos, bufUsed: Integer; + efid, efsz: Word; + izver: Byte; + izcrc: LongWord; begin SetLength(buf, 0); // read local directory @@ -230,12 +233,54 @@ begin fi.fPackSz := 0; fi.fMethod := 0; + //fi.fOfs := fFileStream.Position; + fFileStream.ReadBuffer(lhdr, SizeOf(lhdr)); if lhdr.fnameSz > 255 then name[0] := #255 else name[0] := chr(lhdr.fnameSz); fFileStream.ReadBuffer(name[1], Length(name)); fFileStream.Seek(lhdr.fnameSz-Length(name), soCurrent); // rest of the name (if any) - fi.fName := name; - fFileStream.Seek(lhdr.localExtraSz, soCurrent); + fi.fName := utf8to1251(name); + //writeln(Format('0x%08x : %s', [Integer(fi.fOfs), name])); + + // here we should process extra field: it may contain utf8 filename + //fFileStream.Seek(lhdr.localExtraSz, soCurrent); + while lhdr.localExtraSz >= 4 do + begin + efid := 0; + efsz := 0; + fFileStream.ReadBuffer(efid, 2); + fFileStream.ReadBuffer(efsz, 2); + Dec(lhdr.localExtraSz, 4); + if efsz > lhdr.localExtraSz then break; + // Info-ZIP Unicode Path Extra Field? + if (efid = $7075) and (efsz <= 255+5) and (efsz > 5) then + begin + fFileStream.ReadBuffer(izver, 1); + if izver <> 1 then + begin + // skip it + Dec(efsz, 1); + end + else + begin + Dec(lhdr.localExtraSz, efsz); + fFileStream.ReadBuffer(izcrc, 4); // name crc, ignore it + Dec(efsz, 5); + name[0] := chr(efsz); + fFileStream.ReadBuffer(name[1], Length(name)); + fi.fName := utf8to1251(name); + break; + end; + end; + // skip it + if efsz > 0 then + begin + fFileStream.Seek(efsz, soCurrent); + Dec(lhdr.localExtraSz, efsz); + end; + end; + // skip the rest + if lhdr.localExtraSz > 0 then fFileStream.Seek(lhdr.localExtraSz, soCurrent); if (lhdr.flags and 1) <> 0 then begin @@ -284,8 +329,7 @@ begin begin bufPos := 0; // int64! - if fFileStream.Size-fFileStream.Position > Length(buf) then - bufUsed := Length(buf) + if fFileStream.Size-fFileStream.Position > Length(buf) then bufUsed := Length(buf) else bufUsed := fFileStream.Size-fFileStream.Position; if bufUsed = 0 then raise ESFSError.Create('invalid ZIP file'); fFileStream.ReadBuffer(buf[0], bufUsed); @@ -472,7 +516,7 @@ end; function TSFSZipVolume.OpenFileByIndex (const index: Integer): TStream; var zs: TZDecompressionStream; - fs: TStream; + fs, rs: TStream; gs: TSFSGuardStream; kill: Boolean; buf: packed array [0..1023] of Char; @@ -482,6 +526,7 @@ begin zs := nil; fs := nil; gs := nil; + rs := nil; if fFiles = nil then exit; if (index < 0) or (index >= fFiles.Count) or (fFiles[index] = nil) then exit; kill := false; @@ -506,37 +551,41 @@ begin fs.Seek(TSFSZipFileInfo(fFiles[index]).fOfs, soBeginning); if TSFSZipFileInfo(fFiles[index]).fMethod = 255 then begin - zs := TZDecompressionStream.Create(fs) + // sorry, pals, DFWAD is completely broken, so users of it should SUFFER + if TSFSZipFileInfo(fFiles[index]).fSize = -1 then + begin + TSFSZipFileInfo(fFiles[index]).fSize := 0; + //writeln('trying to determine file size for [', TSFSZipFileInfo(fFiles[index]).fPath, TSFSZipFileInfo(fFiles[index]).fName, ']'); + zs := TZDecompressionStream.Create(fs); + try + while true do + begin + rd := zs.read(buf, 1024); + //writeln(' got ', rd, ' bytes'); + if rd > 0 then Inc(TSFSZipFileInfo(fFiles[index]).fSize, rd); + if rd < 1024 then break; + end; + //writeln(' resulting size: ', TSFSZipFileInfo(fFiles[index]).fSize, ' bytes'); + // recreate stream + FreeAndNil(zs); + fs.Seek(TSFSZipFileInfo(fFiles[index]).fOfs, soBeginning); + except + //writeln('*** CAN''T determine file size for [', TSFSZipFileInfo(fFiles[index]).fPath, TSFSZipFileInfo(fFiles[index]).fName, ']'); + FreeAndNil(zs); + if kill then FreeAndNil(fs); + result := nil; + exit; + end; + end; + rs := TSFSPartialStream.Create(fs, TSFSZipFileInfo(fFiles[index]).fOfs, TSFSZipFileInfo(fFiles[index]).fPackSz, true); + zs := TZDecompressionStream.Create(rs); + rs := nil; end else begin - zs := TZDecompressionStream.Create(fs, true {-15}{MAX_WBITS}); - end; - // sorry, pals, DFWAD is completely broken, so users of it should SUFFER - if TSFSZipFileInfo(fFiles[index]).fSize = -1 then - begin - TSFSZipFileInfo(fFiles[index]).fSize := 0; - //writeln('trying to determine file size for [', TSFSZipFileInfo(fFiles[index]).fPath, TSFSZipFileInfo(fFiles[index]).fName, ']'); - try - while true do - begin - rd := zs.read(buf, 1024); - //writeln(' got ', rd, ' bytes'); - if rd > 0 then Inc(TSFSZipFileInfo(fFiles[index]).fSize, rd); - if rd < 1024 then break; - end; - //writeln(' resulting size: ', TSFSZipFileInfo(fFiles[index]).fSize, ' bytes'); - // recreate stream - FreeAndNil(zs); - fs.Seek(TSFSZipFileInfo(fFiles[index]).fOfs, soBeginning); - zs := TZDecompressionStream.Create(fs) - except - //writeln('*** CAN''T determine file size for [', TSFSZipFileInfo(fFiles[index]).fPath, TSFSZipFileInfo(fFiles[index]).fName, ']'); - FreeAndNil(zs); - if kill then FreeAndNil(fs); - result := nil; - exit; - end; + rs := TSFSPartialStream.Create(fs, TSFSZipFileInfo(fFiles[index]).fOfs, TSFSZipFileInfo(fFiles[index]).fPackSz, true); + zs := TZDecompressionStream.Create(rs, true {-15}{MAX_WBITS}); + rs := nil; end; gs := TSFSGuardStream.Create(zs, fs, true, kill, false); zs := nil; @@ -544,6 +593,7 @@ begin result := TSFSPartialStream.Create(gs, 0, TSFSZipFileInfo(fFiles[index]).fSize, true); end; except + FreeAndNil(rs); FreeAndNil(gs); FreeAndNil(zs); if kill then FreeAndNil(fs); @@ -554,16 +604,16 @@ end; { TSFSZipVolumeFactory } -function TSFSZipVolumeFactory.IsMyVolumePrefix (const prefix: TSFSString): Boolean; +function TSFSZipVolumeFactory.IsMyVolumePrefix (const prefix: AnsiString): Boolean; begin result := - SFSStrEqu(prefix, 'zip') or - SFSStrEqu(prefix, 'dfwad') + StrEquCI1251(prefix, 'zip') or + StrEquCI1251(prefix, 'dfwad') {$IFDEF SFS_ZIPFS_FULL} - or SFSStrEqu(prefix, 'jar') or - SFSStrEqu(prefix, 'fout2') or - SFSStrEqu(prefix, 'vtdb') or - SFSStrEqu(prefix, 'wad') + or StrEquCI1251(prefix, 'jar') or + StrEquCI1251(prefix, 'fout2') or + StrEquCI1251(prefix, 'vtdb') or + StrEquCI1251(prefix, 'wad') {$ENDIF} ; end; @@ -573,7 +623,7 @@ begin vol.Free(); end; -function TSFSZipVolumeFactory.Produce (const prefix, fileName: TSFSString; st: TStream): TSFSVolume; +function TSFSZipVolumeFactory.Produce (const prefix, fileName: AnsiString; st: TStream): TSFSVolume; var vt: TSFSZipVolumeType; begin @@ -612,6 +662,6 @@ var initialization zipf := TSFSZipVolumeFactory.Create(); SFSRegisterVolumeFactory(zipf); -finalization - SFSUnregisterVolumeFactory(zipf); +//finalization +// SFSUnregisterVolumeFactory(zipf); end.