GLS.PAKArchive.pas 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.PAKArchive;
  5. (* Methods of PAK Archiving for Archive Editor in utilities *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. System.Classes,
  10. System.SysUtils,
  11. GLS.ArchiveManager;
  12. const
  13. SIGN = 'PACK';
  14. type
  15. TPakHeader = record
  16. Signature: array [0 .. 3] of AnsiChar;
  17. DirOffset: integer;
  18. DirLength: integer;
  19. end;
  20. TFileSection = record
  21. FileName: array [0 .. 119] of AnsiChar;
  22. FilePos: integer;
  23. FileLength: integer;
  24. end;
  25. TPAKArchive = class(TGLBaseArchive)
  26. private
  27. FHeader: TPakHeader;
  28. FStream: TFileStream;
  29. function GetContentCount: integer;
  30. procedure MakeContentList;
  31. protected
  32. Procedure SetCompressionLevel(aValue: TCompressionLevel); override;
  33. public
  34. property ContentCount: integer Read GetContentCount;
  35. procedure LoadFromFile(const FileName: string); override;
  36. procedure Clear; override;
  37. function ContentExists(ContentName: string): boolean; override;
  38. function GetContent(Stream: TStream; index: integer): TStream; override;
  39. function GetContent(index: integer): TStream; override;
  40. function GetContent(ContentName: string): TStream; override;
  41. function GetContentSize(index: integer): integer; override;
  42. function GetContentSize(ContentName: string): integer; override;
  43. procedure AddFromStream(ContentName, Path: string; FS: TStream); override;
  44. procedure AddFromFile(FileName, Path: string); override;
  45. procedure RemoveContent(index: integer); override;
  46. procedure RemoveContent(ContentName: string); override;
  47. procedure Extract(index: integer; NewName: string); override;
  48. procedure Extract(ContentName, NewName: string); override;
  49. end;
  50. //=======================================================
  51. implementation
  52. //=======================================================
  53. var
  54. Dir: TFileSection;
  55. //---------------------------
  56. // TPAKArchive
  57. //---------------------------
  58. function TPAKArchive.GetContentCount: integer;
  59. begin
  60. Result := FHeader.DirLength div SizeOf(TFileSection);
  61. end;
  62. procedure TPAKArchive.MakeContentList;
  63. var
  64. I: integer;
  65. begin
  66. FStream.Seek(FHeader.DirOffset, soFromBeginning);
  67. FContentList.Clear;
  68. for I := 0 to ContentCount - 1 do
  69. begin
  70. FStream.ReadBuffer(Dir, SizeOf(TFileSection));
  71. FContentList.Add(string(Dir.FileName));
  72. end;
  73. end;
  74. procedure TPAKArchive.SetCompressionLevel(aValue: TCompressionLevel);
  75. begin
  76. aValue := clNone;
  77. inherited SetCompressionLevel(aValue);
  78. end;
  79. procedure TPAKArchive.LoadFromFile(const FileName: string);
  80. begin
  81. FFileName := FileName;
  82. FStream := TFileStream.Create(FileName, fmOpenReadWrite or fmShareDenyWrite);
  83. // ?????
  84. If (FStream = nil) then
  85. exit;
  86. if FStream.Size = 0 then
  87. begin
  88. FHeader.Signature := SIGN;
  89. FHeader.DirOffset := SizeOf(TPakHeader);
  90. FHeader.DirLength := 0;
  91. FStream.WriteBuffer(FHeader, SizeOf(TPakHeader));
  92. FStream.Position := 0;
  93. end;
  94. FStream.ReadBuffer(FHeader, SizeOf(TPakHeader));
  95. if (FHeader.Signature <> SIGN) then
  96. begin
  97. FStream.Free;
  98. raise Exception.Create(FileName + ' - This is not PAK file');
  99. exit;
  100. end;
  101. if ContentCount <> 0 then
  102. MakeContentList;
  103. end;
  104. procedure TPAKArchive.Clear;
  105. begin
  106. If FStream <> nil then
  107. FStream.Free;
  108. FContentList.Clear;
  109. end;
  110. function TPAKArchive.ContentExists(ContentName: string): boolean;
  111. begin
  112. Result := (FContentList.IndexOf(ContentName) > -1);
  113. end;
  114. function TPAKArchive.GetContent(Stream: TStream; index: integer): TStream;
  115. begin
  116. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  117. FStream.Read(Dir, SizeOf(TFileSection));
  118. FStream.Seek(Dir.FilePos, soFromBeginning);
  119. Result := Stream;
  120. Result.CopyFrom(FStream, Dir.FileLength);
  121. Result.Position := 0;
  122. end;
  123. function TPAKArchive.GetContent(index: integer): TStream;
  124. begin
  125. Result := GetContent(TMemoryStream.Create, index);
  126. end;
  127. function TPAKArchive.GetContent(ContentName: string): TStream;
  128. begin
  129. Result := nil;
  130. if ContentExists(ContentName) then
  131. Result := GetContent(FContentList.IndexOf(ContentName));
  132. end;
  133. function TPAKArchive.GetContentSize(index: integer): integer;
  134. begin
  135. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  136. FStream.Read(Dir, SizeOf(Dir));
  137. Result := Dir.FileLength;
  138. end;
  139. function TPAKArchive.GetContentSize(ContentName: string): integer;
  140. begin
  141. Result := -1;
  142. if ContentExists(ContentName) then
  143. Result := GetContentSize(FContentList.IndexOf(ContentName));
  144. end;
  145. procedure TPAKArchive.AddFromStream(ContentName, Path: string; FS: TStream);
  146. var
  147. Temp: TMemoryStream;
  148. begin
  149. // ?????
  150. If (FStream = nil) or ContentExists(ContentName) then
  151. exit;
  152. Temp := nil;
  153. FStream.Position := FHeader.DirOffset;
  154. if FHeader.DirLength > 0 then
  155. begin
  156. Temp := TMemoryStream.Create;
  157. Temp.CopyFrom(FStream, FHeader.DirLength);
  158. Temp.Position := 0;
  159. FStream.Position := FHeader.DirOffset;
  160. end;
  161. Dir.FilePos := FHeader.DirOffset;
  162. Dir.FileLength := FS.Size;
  163. FStream.CopyFrom(FS, 0);
  164. FHeader.DirOffset := FStream.Position;
  165. if FHeader.DirLength > 0 then
  166. begin
  167. FStream.CopyFrom(Temp, 0);
  168. Temp.Free;
  169. end;
  170. StrPCopy(Dir.FileName, AnsiString(Path + ExtractFileName(ContentName)));
  171. FStream.WriteBuffer(Dir, SizeOf(TFileSection));
  172. FHeader.DirLength := FHeader.DirLength + SizeOf(TFileSection);
  173. FStream.Position := 0;
  174. FStream.WriteBuffer(FHeader, SizeOf(TPakHeader));
  175. FContentList.Add(string(Dir.FileName));
  176. end;
  177. procedure TPAKArchive.AddFromFile(FileName, Path: string);
  178. var
  179. FS: TFileStream;
  180. begin
  181. if not FileExists(FileName) then
  182. exit;
  183. FS := TFileStream.Create(FileName, fmOpenRead);
  184. try
  185. AddFromStream(FileName, Path, FS);
  186. finally
  187. FS.Free;
  188. end;
  189. end;
  190. procedure TPAKArchive.RemoveContent(index: integer);
  191. var
  192. Temp: TMemoryStream;
  193. I: integer;
  194. f: TFileSection;
  195. begin
  196. Temp := TMemoryStream.Create;
  197. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * index, soFromBeginning);
  198. FStream.ReadBuffer(Dir, SizeOf(TFileSection));
  199. FStream.Seek(Dir.FilePos + Dir.FileLength, soFromBeginning);
  200. Temp.CopyFrom(FStream, FStream.Size - FStream.Position);
  201. FStream.Position := Dir.FilePos;
  202. FStream.CopyFrom(Temp, 0);
  203. FHeader.DirOffset := FHeader.DirOffset - Dir.FileLength;
  204. Temp.Clear;
  205. for I := 0 to ContentCount - 1 do
  206. if I > index then
  207. begin
  208. FStream.Seek(FHeader.DirOffset + SizeOf(TFileSection) * I, soFromBeginning);
  209. FStream.ReadBuffer(f, SizeOf(TFileSection));
  210. FStream.Position := FStream.Position - SizeOf(TFileSection);
  211. f.FilePos := f.FilePos - Dir.FileLength;
  212. FStream.WriteBuffer(f, SizeOf(TFileSection));
  213. end;
  214. I := FHeader.DirOffset + SizeOf(TFileSection) * index;
  215. FStream.Position := Cardinal(I + SizeOf(TFileSection));
  216. if FStream.Position < FStream.Size then
  217. begin
  218. Temp.CopyFrom(FStream, FStream.Size - FStream.Position);
  219. FStream.Position := I;
  220. FStream.CopyFrom(Temp, 0);
  221. end;
  222. Temp.Free;
  223. FHeader.DirLength := FHeader.DirLength - SizeOf(TFileSection);
  224. FStream.Position := 0;
  225. FStream.WriteBuffer(FHeader, SizeOf(TPakHeader));
  226. FStream.Size := FStream.Size - Dir.FileLength - SizeOf(TFileSection);
  227. MakeContentList;
  228. end;
  229. procedure TPAKArchive.RemoveContent(ContentName: string);
  230. begin
  231. if ContentExists(ContentName) then
  232. RemoveContent(FContentList.IndexOf(ContentName));
  233. end;
  234. procedure TPAKArchive.Extract(index: integer; NewName: string);
  235. var
  236. vExtractFileStream: TFileStream;
  237. vTmpStream: TStream;
  238. begin
  239. if NewName = '' then
  240. exit;
  241. if (index < 0) or (index >= ContentCount) then
  242. exit;
  243. vExtractFileStream := TFileStream.Create(NewName, fmCreate);
  244. vTmpStream := GetContent(index);
  245. vExtractFileStream.CopyFrom(vTmpStream, 0);
  246. vTmpStream.Free;
  247. vExtractFileStream.Free;
  248. end;
  249. procedure TPAKArchive.Extract(ContentName, NewName: string);
  250. begin
  251. if ContentExists(ContentName) then
  252. Extract(FContentList.IndexOf(ContentName), NewName);
  253. end;
  254. //----------------------------------------
  255. initialization
  256. //----------------------------------------
  257. RegisterArchiveFormat('pak', 'PAK File', TPAKArchive);
  258. end.