sourcehandler.pp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. {
  2. FPCResLipo - Free Pascal External Resource Thinner
  3. Part of the Free Pascal distribution
  4. Copyright (C) 2008 by Giulio Bernardi
  5. Source files handling
  6. See the file COPYING, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. }
  12. unit sourcehandler;
  13. {$MODE OBJFPC} {$H+}
  14. interface
  15. uses
  16. Classes, SysUtils, resource, externalreader, externalwriter;
  17. type
  18. ESourceFilesException = class(Exception);
  19. ECantOpenFileException = class(ESourceFilesException);
  20. EUnknownInputFormatException = class(ESourceFilesException);
  21. ECantCreateFileException = class(ESourceFilesException);
  22. type
  23. { TSourceFile }
  24. TSourceFile = class
  25. private
  26. fFname : string;
  27. fStream : TStream;
  28. fResources : TResources;
  29. fProcessed : TResources;
  30. fEndianess : byte;
  31. fModified : boolean;
  32. function Delete : boolean;
  33. protected
  34. public
  35. constructor Create(aFileName : string);
  36. destructor Destroy; override;
  37. procedure Update;
  38. property FileName : string read fFname;
  39. property Resources : TResources read fResources;
  40. property Processed : TResources read fProcessed;
  41. property Endianess : byte read fEndianess;
  42. property Modified : boolean read fModified write fModified;
  43. end;
  44. { TSourceFiles }
  45. TSourceFiles = class
  46. private
  47. similarities, simcount : array of integer;
  48. fList : TFPList;
  49. function GetItem(index : integer) : TSourceFile;
  50. function GetCount : integer;
  51. procedure ResetSimArrays;
  52. function GetMostCommon : integer;
  53. procedure CheckSimilarities(idx : integer; aType,aName : TResourceDesc; aLangID : TLangID);
  54. procedure ExtractCommon(idx : integer; outRes : TResources; aType,aName : TResourceDesc; aLangID : TLangID);
  55. protected
  56. public
  57. constructor Create;
  58. destructor Destroy; override;
  59. procedure NewSourceFile(aFileName : string);
  60. procedure Process(outRes : TResources);
  61. procedure Update;
  62. property Items[index : integer] : TSourceFile read GetItem;
  63. property Count : integer read GetCount;
  64. end;
  65. implementation
  66. uses msghandler;
  67. { TSourceFile }
  68. function TSourceFile.Delete : boolean;
  69. begin
  70. FreeAndNil(fResources);
  71. FreeAndNil(fStream);
  72. Result:=DeleteFile(fFname);
  73. if not Result then
  74. Messages.DoError(Format('Can''t delete file %s.',[fFname]))
  75. end;
  76. constructor TSourceFile.Create(aFileName: string);
  77. var aReader : TExternalResourceReader;
  78. begin
  79. fModified:=false;
  80. fFName:=aFileName;
  81. Messages.DoVerbose(Format('Trying to open file %s...',[fFName]));
  82. try
  83. fStream:=TFileStream.Create(fFName,fmOpenRead or fmShareDenyWrite);
  84. except
  85. raise ECantOpenFileException.Create(fFName);
  86. end;
  87. aReader:=TExternalResourceReader.Create;
  88. fResources:=TResources.Create;
  89. try
  90. try
  91. try
  92. Messages.DoVerbose('Reading resource information...');
  93. fResources.LoadFromStream(fStream,aReader);
  94. Messages.DoVerbose(Format('%d resources read.',[fResources.Count]));
  95. fEndianess:=aReader.Endianess;
  96. except
  97. on e : EResourceReaderWrongFormatException do
  98. raise EUnknownInputFormatException.Create(fFname);
  99. end;
  100. except
  101. FreeAndNil(fResources);
  102. FreeAndNil(fStream);
  103. end;
  104. finally
  105. aReader.Free;
  106. end;
  107. fProcessed:=TResources.Create;
  108. end;
  109. destructor TSourceFile.Destroy;
  110. begin
  111. if fResources<>nil then fResources.Free;
  112. if fProcessed<>nil then fProcessed.Free;
  113. if fStream<>nil then fStream.Free;
  114. end;
  115. procedure TSourceFile.Update;
  116. var tmp : string;
  117. aWriter : TExternalResourceWriter;
  118. aStream : TFileStream;
  119. begin
  120. if not fModified then
  121. begin
  122. Messages.DoVerbose(Format('File %s is unchanged.',[fFname]));
  123. exit;
  124. end;
  125. if Resources.Count=0 then
  126. begin
  127. if Delete then
  128. Messages.DoVerbose(Format('No more resources in file %s, deleted',[fFname]));
  129. exit;
  130. end;
  131. tmp:=ExtractFileDir(fFname);
  132. if tmp='' then tmp:='.';
  133. tmp:=GetTempFileName(tmp,'tmp');
  134. Messages.DoVerbose(Format('Updating file %s...',[fFname]));
  135. try
  136. aStream:=TFileStream.Create(tmp,fmCreate or fmShareDenyWrite);
  137. except
  138. raise ECantCreateFileException.Create(tmp);
  139. end;
  140. try
  141. aWriter:=TExternalResourceWriter.Create;
  142. aWriter.Endianess:=Endianess;
  143. try
  144. Resources.WriteToStream(aStream,aWriter);
  145. Messages.DoVerbose(Format('%d resources written.',[Resources.Count]));
  146. finally
  147. aWriter.Free;
  148. end;
  149. finally
  150. aStream.Free;
  151. end;
  152. if not Delete then exit;
  153. if not RenameFile(tmp,fFname) then
  154. Messages.DoError(Format('Can''t rename file %s to %s.',[tmp,fFname]))
  155. else
  156. Messages.DoVerbose(Format('File %s updated',[fFname]));
  157. end;
  158. { TSourceFiles }
  159. function TSourceFiles.GetItem(index : integer) : TSourceFile;
  160. begin
  161. Result:=TSourceFile(fList[index]);
  162. end;
  163. function TSourceFiles.GetCount: integer;
  164. begin
  165. Result:=fList.Count;
  166. end;
  167. procedure TSourceFiles.ResetSimArrays;
  168. var i : integer;
  169. begin
  170. for i:=0 to Count-1 do
  171. begin
  172. similarities[i]:=i;
  173. simcount[i]:=1;
  174. end;
  175. end;
  176. function TSourceFiles.GetMostCommon: integer;
  177. var i : integer;
  178. max, maxidx : integer;
  179. begin
  180. max:=0;
  181. maxidx:=0;
  182. for i:=0 to Count-1 do
  183. if simcount[i]>max then
  184. begin
  185. max:=simcount[i];
  186. maxidx:=i;
  187. end;
  188. Result:=maxidx;
  189. end;
  190. procedure TSourceFiles.CheckSimilarities(idx: integer; aType,
  191. aName: TResourceDesc; aLangID: TLangID);
  192. var i,j : integer;
  193. res1, res2 : TAbstractResource;
  194. begin
  195. for i:=idx to Count-1 do
  196. begin
  197. if similarities[i]<>i then continue;
  198. try
  199. res1:=Items[i].Resources.Find(aType,aName,aLangID);
  200. except
  201. on e : EResourceNotFoundException do continue;
  202. end;
  203. for j:=idx+1 to Count-1 do
  204. begin
  205. try
  206. res2:=Items[j].Resources.Find(aType,aName,aLangID);
  207. except
  208. on e : EResourceNotFoundException do continue;
  209. end;
  210. if res1.CompareContents(res2) then
  211. begin
  212. dec(simcount[similarities[j]]);
  213. inc(simcount[similarities[i]]);
  214. similarities[j]:=similarities[i];
  215. end;
  216. end;
  217. end;
  218. end;
  219. procedure TSourceFiles.ExtractCommon(idx: integer; outRes: TResources; aType,
  220. aName: TResourceDesc; aLangID: TLangID);
  221. var maxidx,i : integer;
  222. res : TAbstractResource;
  223. begin
  224. maxidx:=GetMostCommon;
  225. if simcount[maxidx]<=1 then
  226. begin
  227. for i:=idx to Count-1 do
  228. begin
  229. try
  230. res:=Items[i].Resources.Remove(aType,aName,aLangID);
  231. except
  232. on e : EResourceNotFoundException do continue;
  233. end;
  234. Items[i].Processed.Add(res);
  235. end;
  236. exit;
  237. end;
  238. res:=Items[maxidx].Resources.Remove(aType,aName,aLangID);
  239. Items[maxidx].Modified:=true;
  240. outRes.Add(res);
  241. for i:=idx to Count-1 do
  242. begin
  243. if i=maxidx then continue;
  244. try
  245. res:=Items[i].Resources.Remove(aType,aName,aLangID);
  246. except
  247. on e : EResourceNotFoundException do continue;
  248. end;
  249. if similarities[i]=similarities[maxidx] then
  250. begin
  251. res.Free;
  252. Items[i].Modified:=true;
  253. end
  254. else
  255. Items[i].Processed.Add(res);
  256. end;
  257. end;
  258. constructor TSourceFiles.Create;
  259. begin
  260. fList:=TFPList.Create;
  261. end;
  262. destructor TSourceFiles.Destroy;
  263. var i : integer;
  264. begin
  265. for i:=0 to fList.Count-1 do
  266. TSourceFile(fList[i]).Free;
  267. fList.Free;
  268. end;
  269. procedure TSourceFiles.NewSourceFile(aFileName : string);
  270. var aFile : TSourceFile;
  271. begin
  272. aFile:=TSourceFile.Create(aFileName);
  273. fList.Add(aFile);
  274. end;
  275. procedure TSourceFiles.Process(outRes: TResources);
  276. var i : integer;
  277. res : TAbstractResource;
  278. begin
  279. setlength(similarities,Count);
  280. setlength(simcount,Count);
  281. for i:=0 to Count-1 do
  282. begin
  283. while Items[i].Resources.Count>0 do
  284. begin
  285. ResetSimArrays;
  286. res:=Items[i].Resources[Items[i].Resources.Count-1];
  287. if res.Owner<>nil then
  288. res:=res.Owner;
  289. CheckSimilarities(i,res._Type,res.Name,res.LangID);
  290. ExtractCommon(i,outRes,res._Type,res.Name,res.LangID);
  291. end;
  292. Items[i].Resources.MoveFrom(Items[i].Processed);
  293. end;
  294. end;
  295. procedure TSourceFiles.Update;
  296. var i : integer;
  297. begin
  298. for i:=0 to Count-1 do
  299. Items[i].Update;
  300. end;
  301. end.