diff --git a/src/sfs/sfsZipFS.pas b/src/sfs/sfsZipFS.pas
index b7e9051523e952e7535549f684da3e840b14b1cb..892dd636802cbf3e3e3edbb605a6401468064ce8 100644 (file)
--- a/src/sfs/sfsZipFS.pas
+++ b/src/sfs/sfsZipFS.pas
//
{.$DEFINE SFS_DEBUG_ZIPFS}
{.$DEFINE SFS_ZIPFS_FULL}
-{$MODE DELPHI}
-{.$R-}
+{$MODE OBJFPC}
+{$R+}
unit sfsZipFS;
interface
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;
implementation
uses
- zstream, xstreams;
+ zstream, xstreams, utils;
type
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;
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
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
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);
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;
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;
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;
result := TSFSPartialStream.Create(gs, 0, TSFSZipFileInfo(fFiles[index]).fSize, true);
end;
except
+ FreeAndNil(rs);
FreeAndNil(gs);
FreeAndNil(zs);
if kill then FreeAndNil(fs);
{ 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;
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
initialization
zipf := TSFSZipVolumeFactory.Create();
SFSRegisterVolumeFactory(zipf);
-finalization
- SFSUnregisterVolumeFactory(zipf);
+//finalization
+// SFSUnregisterVolumeFactory(zipf);
end.