GLFileZLIB.pas 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. {
  5. Methods for archiving using ZLib
  6. .
  7. }
  8. unit GLFileZLIB;
  9. {$I GLScene.inc}
  10. interface
  11. uses
  12. System.Classes,
  13. System.SysUtils,
  14. System.ZLib,
  15. GLSArchiveManager;
  16. const
  17. SIGN = 'ZLIB'; //Signature for compressed zlib.
  18. Type
  19. TZLibHeader = record
  20. Signature: array[0..3] of AnsiChar;
  21. DirOffset: integer;
  22. DirLength: integer;
  23. end;
  24. TFileSection = record
  25. FileName: array[0..119] of AnsiChar;
  26. FilePos: integer;
  27. FileLength: integer;
  28. CbrMode: TCompressionLevel;
  29. end;
  30. { TZLibArchive }
  31. TZLibArchive=class(TGLBaseArchive)
  32. private
  33. FHeader: TZLibHeader;
  34. FStream: TFileStream;
  35. function GetContentCount: integer;
  36. procedure MakeContentList;
  37. public
  38. property ContentCount: integer Read GetContentCount;
  39. destructor Destroy; override;
  40. procedure LoadFromFile(const FileName: string); override;
  41. procedure Clear; override;
  42. function ContentExists(ContentName: string): boolean;override;
  43. function GetContent(aStream: TStream; index: integer): TStream; override;
  44. function GetContent(index: integer): TStream; override;
  45. function GetContent(ContentName: string): TStream; override;
  46. function GetContentSize(index: integer): integer; override;
  47. function GetContentSize(ContentName: string): integer; override;
  48. procedure AddFromStream(ContentName, Path: string; FS: TStream);override;
  49. procedure AddFromFile(FileName, Path: string); override;
  50. procedure RemoveContent(index: integer); overload; override;
  51. procedure RemoveContent(ContentName: string); overload;override;
  52. procedure Extract(index: integer; NewName: string); override;
  53. procedure Extract(ContentName, NewName: string); override;
  54. end;
  55. //-------------------------------------------------------------
  56. implementation
  57. //-------------------------------------------------------------
  58. var
  59. Dir: TFileSection;
  60. { TZLibArchive }
  61. function TZLibArchive.GetContentCount: integer;
  62. begin
  63. Result := FHeader.DirLength div SizeOf(TFileSection);
  64. end;
  65. procedure TZLibArchive.MakeContentList;
  66. var
  67. I: integer;
  68. begin
  69. FStream.Seek(FHeader.DirOffset, soFromBeginning);
  70. FContentList.Clear;
  71. for i := 0 to ContentCount - 1 do
  72. begin
  73. FStream.ReadBuffer(Dir, SizeOf(TFileSection));
  74. FContentList.Add(string(Dir.FileName));
  75. end;
  76. end;
  77. destructor TZLibArchive.Destroy;
  78. begin
  79. inherited Destroy;
  80. end;
  81. procedure TZLibArchive.LoadFromFile(const FileName: string);
  82. begin
  83. FFileName := FileName;
  84. FStream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyWrite);
  85. if FStream.Size = 0 then
  86. begin
  87. FHeader.Signature := SIGN;
  88. FHeader.DirOffset := SizeOf(TZLibHeader);
  89. FHeader.DirLength := 0;
  90. FStream.WriteBuffer(FHeader, SizeOf(TZlibHeader));
  91. FStream.Position := 0;
  92. end;
  93. FStream.ReadBuffer(FHeader, SizeOf(TZlibHeader));
  94. if FHeader.Signature <> SIGN then
  95. begin
  96. FreeAndNil(FStream); // nil it too to avoid own Clear() giving AV
  97. raise Exception.Create(FileName+' - This is not ZLIB file');
  98. Exit;
  99. end;
  100. if ContentCount <> 0 then
  101. MakeContentList;
  102. end;
  103. procedure TZLibArchive.Clear;
  104. begin
  105. FContentList.Clear;
  106. If FStream <> nil then FStream.Free;
  107. end;
  108. function TZLibArchive.ContentExists(ContentName: string): boolean;
  109. begin
  110. Result := (FContentList.IndexOf(ContentName) > -1);
  111. end;
  112. function TZLibArchive.GetContent(aStream: TStream; index: integer): TStream;
  113. var
  114. tempStream: TMemoryStream;
  115. decompr : TZDecompressionStream;
  116. begin
  117. Result := nil;
  118. If FStream = nil then exit;
  119. Result := aStream;
  120. //Find file
  121. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  122. FStream.Read(Dir, SizeOf(TFileSection));
  123. FStream.Seek(Dir.FilePos, soFromBeginning);
  124. //Copy file to temp stream
  125. tempStream := TMemoryStream.Create;
  126. tempStream.CopyFrom(FStream, Dir.FileLength);
  127. tempStream.Position := 0;
  128. //decompress
  129. decompr := TZDecompressionStream.Create(tempStream);
  130. try
  131. Result.CopyFrom(decompr, 0);
  132. finally
  133. decompr.Free;
  134. tempStream.Free;
  135. end;
  136. Result.Position := 0;
  137. end;
  138. function TZLibArchive.GetContent(index: integer): TStream;
  139. begin
  140. Result:=GetContent(TMemoryStream.Create,index);
  141. end;
  142. function TZLibArchive.GetContent(ContentName: string): TStream;
  143. begin
  144. Result := nil;
  145. if ContentExists(ContentName) then
  146. Result := GetContent(FContentList.IndexOf(ContentName));
  147. end;
  148. function TZLibArchive.GetContentSize(index: integer): integer;
  149. begin
  150. Result := -1;
  151. If FStream = nil then exit;
  152. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  153. FStream.Read(Dir, SizeOf(Dir));
  154. Result := Dir.FileLength;
  155. end;
  156. function TZLibArchive.GetContentSize(ContentName: string): integer;
  157. begin
  158. Result := -1;
  159. if ContentExists(ContentName) then
  160. Result := GetContentSize(FContentList.IndexOf(ContentName));
  161. end;
  162. procedure TZLibArchive.AddFromStream(ContentName, Path: string; FS: TStream);
  163. var
  164. Temp, compressed: TMemoryStream;
  165. FCompressor: TZCompressionStream;
  166. LCompLevel : TZCompressionLevel;
  167. begin
  168. if (FStream = nil) or ContentExists(ContentName) then exit;
  169. FStream.Position := FHeader.DirOffset;
  170. //???
  171. if FHeader.DirLength > 0 then
  172. begin
  173. Temp := TMemoryStream.Create;
  174. Temp.CopyFrom(FStream, FHeader.DirLength);
  175. Temp.Position := 0;
  176. FStream.Position := FHeader.DirOffset;
  177. end
  178. else
  179. Temp := nil;
  180. Dir.FilePos := FHeader.DirOffset;
  181. Dir.CbrMode := compressionLevel;
  182. compressed := TMemoryStream.Create;
  183. // Archive data to stream
  184. case CompressionLevel of
  185. clNone : LCompLevel := zcNone;
  186. clFastest : LCompLevel := zcFastest;
  187. clMax : LCompLevel := zcMax;
  188. else
  189. LCompLevel := zcDefault;
  190. end;
  191. FCompressor := TZCompressionStream.Create(compressed, LCompLevel, 15);
  192. FCompressor.CopyFrom(FS, FS.Size);
  193. FCompressor.Free;
  194. // Copy results
  195. FStream.CopyFrom(compressed, 0);
  196. Dir.FileLength := compressed.Size;
  197. Compressed .Free;
  198. // ???
  199. FHeader.DirOffset := FStream.Position;
  200. if FHeader.DirLength > 0 then
  201. begin
  202. FStream.CopyFrom(Temp, 0);
  203. Temp.Free;
  204. end;
  205. StrPCopy(Dir.FileName, AnsiString(Path + ExtractFileName(ContentName)));
  206. FStream.WriteBuffer(Dir, SizeOf(TFileSection));
  207. FHeader.DirLength := FHeader.DirLength + SizeOf(TFileSection);
  208. FStream.Position := 0;
  209. FStream.WriteBuffer(FHeader, SizeOf(TZLibHeader));
  210. FContentList.Add(string(Dir.FileName));
  211. end;
  212. procedure TZLibArchive.AddFromFile(FileName, Path: string);
  213. var
  214. FS: TFileStream;
  215. begin
  216. if not FileExists(FileName) then
  217. exit;
  218. FS := TFileStream.Create(FileName, fmOpenRead);
  219. try
  220. AddFromStream(FileName, Path, FS);
  221. finally
  222. FS.Free;
  223. end;
  224. end;
  225. procedure TZLibArchive.RemoveContent(index: integer);
  226. var
  227. Temp: TMemoryStream;
  228. i: integer;
  229. f: TFileSection;
  230. begin
  231. Temp := TMemoryStream.Create;
  232. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  233. FStream.ReadBuffer(Dir, SizeOf(TFileSection));
  234. FStream.Seek(Dir.FilePos + Dir.FileLength, soFromBeginning);
  235. Temp.CopyFrom(FStream, FStream.Size - FStream.Position);
  236. FStream.Position := Dir.FilePos;
  237. FStream.CopyFrom(Temp, 0);
  238. FHeader.DirOffset := FHeader.DirOffset - dir.FileLength;
  239. Temp.Clear;
  240. for i := 0 to ContentCount - 1 do
  241. if i > index then
  242. begin
  243. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * i, soFromBeginning);
  244. FStream.ReadBuffer(f, SizeOf(TFileSection));
  245. FStream.Position := FStream.Position - SizeOf(TFileSection);
  246. f.FilePos := f.FilePos - dir.FileLength;
  247. FStream.WriteBuffer(f, SizeOf(TFileSection));
  248. end;
  249. i := FHeader.DirOffset + SizeOf(TFileSection) * index;
  250. FStream.Position := Cardinal(i + SizeOf(TFileSection));
  251. if FStream.Position < FStream.Size then
  252. begin
  253. Temp.CopyFrom(FStream, FStream.Size - FStream.Position);
  254. FStream.Position := i;
  255. FStream.CopyFrom(Temp, 0);
  256. end;
  257. Temp.Free;
  258. FHeader.DirLength := FHeader.DirLength - SizeOf(TFileSection);
  259. FStream.Position := 0;
  260. FStream.WriteBuffer(FHeader, SizeOf(TZLibHeader));
  261. FStream.Size := FStream.Size - dir.FileLength - SizeOf(TFileSection);
  262. MakeContentList;
  263. end;
  264. procedure TZLibArchive.RemoveContent(ContentName: string);
  265. begin
  266. if ContentExists(ContentName) then
  267. RemoveContent(FContentList.IndexOf(ContentName));
  268. end;
  269. procedure TZLibArchive.Extract(index: integer; NewName: string);
  270. var
  271. vExtractFileStream: TFileStream;
  272. vTmpStream: Tstream;
  273. begin
  274. if NewName = '' then
  275. Exit;
  276. if (index < 0) or (index >= ContentCount) then
  277. exit;
  278. vExtractFileStream := TFileStream.Create(NewName, fmCreate);
  279. vTmpStream := GetContent(index);
  280. vExtractFileStream.CopyFrom(vTmpStream, 0);
  281. vTmpStream.Free;
  282. vExtractFileStream.Free;
  283. end;
  284. procedure TZLibArchive.Extract(ContentName, NewName: string);
  285. begin
  286. if ContentExists(ContentName) then
  287. Extract(FContentList.IndexOf(ContentName), NewName);
  288. end;
  289. initialization
  290. RegisterArchiveFormat('zlib', 'GLScene file uses the zlib compression algorithm', TZLibArchive);
  291. end.