fpcres.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. {
  2. FPCRes - Free Pascal Resource Converter
  3. Part of the Free Pascal distribution
  4. Copyright (C) 2008 by Giulio Bernardi
  5. See the file COPYING, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. { Note: This program is not the old fpcres by Simon Kissel }
  12. program fpcres;
  13. {$MODE OBJFPC} {$H+}
  14. uses
  15. SysUtils, Classes, paramparser, target, msghandler, sourcehandler,
  16. closablefilestream, resource,
  17. //readers
  18. resreader, coffreader, winpeimagereader, elfreader, machoreader,
  19. externalreader, dfmreader, tlbreader, rcreader,
  20. //writers
  21. reswriter, coffwriter, xcoffwriter, elfwriter, machowriter, externalwriter,
  22. //misc
  23. elfconsts, cofftypes, machotypes, externaltypes;
  24. const
  25. halt_no_err = 0;
  26. halt_param_err = 1;
  27. halt_read_err = 2;
  28. halt_write_err = 3;
  29. progname = 'fpcres';
  30. progversion = '2.0'; //to distinguish from the old fpcres
  31. fpcversion = {$INCLUDE %FPCVERSION%};
  32. host_arch = {$INCLUDE %FPCTARGETCPU%};
  33. host_os = {$INCLUDE %FPCTARGETOS%};
  34. build_date = {$INCLUDE %DATE%};
  35. var
  36. params : TParameters = nil;
  37. resources : TResources = nil;
  38. sourcefiles : TSourceFiles = nil;
  39. procedure ShowVersion;
  40. begin
  41. writeln(progname+' - resource file converter, version '+progversion+' ['+build_date+'], FPC '+fpcversion);
  42. writeln('Host platform: '+host_os+' - '+host_arch);
  43. writeln('Copyright (c) 2008 by Giulio Bernardi.');
  44. end;
  45. procedure ShowHelp;
  46. begin
  47. ShowVersion;
  48. writeln('Syntax: '+progname+' [options] <inputfile> [<inputfile>...] [-o <outputfile>]');
  49. writeln;
  50. writeln('Options:');
  51. writeln(' --help, -h, -? Show this screen.');
  52. writeln(' --version, -V Show program version.');
  53. writeln(' --verbose, -v Be verbose.');
  54. writeln(' --input, -i <x> Ignored for compatibility.');
  55. writeln(' --include, -I <x> RC files: add a path for include searches');
  56. writeln(' --define, -D <sym>[=<val>]');
  57. writeln(' RC files: define a symbol (and value)');
  58. writeln(' --undefine, -U <sym> RC files: undefine a symbol');
  59. writeln(' --output, -o <x> Set the output file name.');
  60. writeln(' -of <format> Set the output file format. Supported formats:');
  61. writeln(' res, elf, coff, mach-o, external');
  62. writeln(' --arch, -a <name> Set object file architecture. Supported architectures:');
  63. writeln(' i386, x86_64, arm (coff)');
  64. writeln(' i386, x86_64, powerpc, powerpc64, arm, armeb, m68k,');
  65. writeln(' sparc, alpha, ia64, mips, mipsel (elf)');
  66. writeln(' i386, x86_64, powerpc, powerpc64, arm, aarch64 (mach-o)');
  67. writeln(' bigendian, littleendian (external)');
  68. writeln(' --subarch, -s <name> Set object file sub-architecture. Supported values:');
  69. writeln(' arm: all, v4t, v6, v5tej, xscale, v7');
  70. writeln(' other architectures: all');
  71. writeln(' @<file> Read more options from file <file>');
  72. writeln('Default output target: '+TargetToStr(currenttarget));
  73. end;
  74. const
  75. SOutputFileAlreadySet = 'Output file name already set.';
  76. SUnknownParameter = 'Unknown parameter ''%s''';
  77. SArgumentMissing = 'Argument missing for option ''%s''';
  78. SUnknownObjFormat = 'Unknown file format ''%s''';
  79. SUnknownMachine = 'Unknown architecture ''%s''';
  80. SFormatArchMismatch = 'Architecture %s is not available for %s format';
  81. SNoInputFiles = 'No input files';
  82. SNoOutputFile = 'No output file name specified';
  83. SCannotReadConfFile ='Can''t read config file ''%s''';
  84. SCantOpenFile = 'Can''t open file ''%s''';
  85. SUnknownInputFormat = 'No known file format detected for file ''%s''';
  86. SCantCreateFile = 'Can''t create file ''%s''';
  87. function GetCurrentTimeMsec : longint;
  88. var h,m,s,ms : word;
  89. begin
  90. DecodeTime(Time,h,m,s,ms);
  91. Result:=h*3600*1000 + m*60*1000 + s*1000 + ms;
  92. end;
  93. procedure CheckTarget;
  94. begin
  95. //if user explicitally set a format, use it
  96. if params.Target.objformat<>ofNone then
  97. CurrentTarget.objformat:=params.Target.objformat;
  98. //if no machine was specified, check if current is ok for this format,
  99. //otherwise pick the default one for that format
  100. if params.Target.machine=mtNone then
  101. begin
  102. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  103. begin
  104. CurrentTarget.machine:=GetDefaultMachineForFormat(CurrentTarget.objformat);
  105. CurrentTarget.submachine:=GetDefaultSubMachineForMachine(currentTarget.machine);
  106. end
  107. end
  108. else
  109. begin
  110. CurrentTarget.machine:=params.Target.machine;
  111. CurrentTarget.submachine:=params.Target.submachine;
  112. end;
  113. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  114. begin
  115. Messages.DoError(Format(SFormatArchMismatch,[
  116. MachineToStr(CurrentTarget.machine),ObjFormatToStr(CurrentTarget.objformat)]));
  117. halt(halt_param_err);
  118. end;
  119. Messages.DoVerbose('target set to '+TargetToStr(CurrentTarget));
  120. end;
  121. procedure CheckInputFiles;
  122. begin
  123. if params.InputFiles.Count=0 then
  124. begin
  125. Messages.DoError(SNoInputFiles);
  126. halt(halt_param_err);
  127. end;
  128. end;
  129. procedure CheckOutputFile;
  130. var tmp : string;
  131. begin
  132. if params.OutputFile<>'' then exit;
  133. if params.InputFiles.Count>1 then
  134. begin
  135. Messages.DoError(SNoOutputFile);
  136. halt(halt_param_err);
  137. end;
  138. tmp:=ChangeFileExt(ExtractFileName(params.InputFiles[0]),
  139. ObjFormats[CurrentTarget.objformat].ext);
  140. if lowercase(tmp)=lowercase(params.InputFiles[0]) then
  141. tmp:=tmp+ObjFormats[CurrentTarget.objformat].ext;
  142. params.OutputFile:=tmp;
  143. end;
  144. procedure ParseParams;
  145. var msg : string;
  146. begin
  147. Messages.DoVerbose('parsing command line parameters');
  148. msg:='';
  149. if ParamCount = 0 then
  150. begin
  151. ShowHelp;
  152. halt(halt_no_err);
  153. end;
  154. params:=TParameters.Create;
  155. try
  156. params.Parse;
  157. except
  158. on e : EOutputFileAlreadySetException do msg:=SOutputFileAlreadySet;
  159. on e : EUnknownParameterException do msg:=Format(SUnknownParameter,[e.Message]);
  160. on e : EArgumentMissingException do msg:=Format(SArgumentMissing,[e.Message]);
  161. on e : EUnknownObjFormatException do msg:=Format(SUnknownObjFormat,[e.Message]);
  162. on e : EUnknownMachineException do msg:=Format(SUnknownMachine,[e.Message]);
  163. on e : ECannotReadConfFile do msg:=Format(SCannotReadConfFile,[e.Message]);
  164. end;
  165. Messages.Verbose:=params.Verbose;
  166. if msg<>'' then
  167. begin
  168. Messages.DoError(msg);
  169. halt(halt_param_err);
  170. end;
  171. if params.Version then
  172. begin
  173. ShowVersion;
  174. halt(halt_no_err);
  175. end;
  176. if params.Help then
  177. begin
  178. ShowHelp;
  179. halt(halt_no_err);
  180. end;
  181. CheckTarget;
  182. CheckInputFiles;
  183. CheckOutputFile;
  184. Messages.DoVerbose('finished parsing command line parameters');
  185. end;
  186. procedure LoadSourceFiles;
  187. var msg : string;
  188. begin
  189. msg:='';
  190. resources:=TResources.Create;
  191. sourcefiles:=TSourceFiles.Create;
  192. sourcefiles.FileList.AddStrings(params.InputFiles);
  193. sourcefiles.RCDefines.AddStrings(params.RCDefines);
  194. sourcefiles.RCIncludeDirs.AddStrings(params.RCIncludeDirs);
  195. sourcefiles.RCMode:=CurrentTarget.objformat=ofRes;
  196. try
  197. sourcefiles.Load(resources);
  198. except
  199. on e : ECantOpenFileException do msg:=Format(SCantOpenFile,[e.Message]);
  200. on e : EUnknownInputFormatException do msg:=Format(SUnknownInputFormat,[e.Message]);
  201. on e : Exception do
  202. begin
  203. if e.Message='' then msg:=e.ClassName
  204. else msg:=e.Message;
  205. end;
  206. end;
  207. if msg<>'' then
  208. begin
  209. Messages.DoError(msg);
  210. halt(halt_read_err);
  211. end;
  212. end;
  213. function SetUpResWriter : TResResourceWriter;
  214. begin
  215. Result:=TResResourceWriter.Create;
  216. end;
  217. function SetUpElfWriter : TElfResourceWriter;
  218. begin
  219. Result:=TElfResourceWriter.Create;
  220. case CurrentTarget.machine of
  221. // mtnone :
  222. mti386 : Result.MachineType:=emti386;
  223. mtx86_64 : Result.MachineType:=emtx86_64;
  224. mtppc : Result.MachineType:=emtppc;
  225. mtppc64 : Result.MachineType:=emtppc64;
  226. mtarm : Result.MachineType:=emtarm;
  227. mtarmeb : Result.MachineType:=emtarmeb;
  228. mtm68k : Result.MachineType:=emtm68k;
  229. mtsparc : Result.MachineType:=emtsparc;
  230. mtalpha : Result.MachineType:=emtalpha;
  231. mtia64 : Result.MachineType:=emtia64;
  232. mtmips : Result.MachineType:=emtmips;
  233. mtmipsel : Result.MachineType:=emtmipsel;
  234. mtppc64le : Result.MachineType:=emtppc64le;
  235. mtaarch64 : Result.MachineType:=emtaarch64;
  236. end;
  237. end;
  238. function SetUpCoffWriter : TCoffResourceWriter;
  239. begin
  240. Result:=TCoffResourceWriter.Create;
  241. case CurrentTarget.machine of
  242. // mtnone :
  243. mti386 : Result.MachineType:=cmti386;
  244. mtarm : Result.MachineType:=cmtarm;
  245. mtx86_64 : Result.MachineType:=cmtx8664;
  246. end;
  247. end;
  248. function SetUpXCoffWriter : TCoffResourceWriter;
  249. begin
  250. Result:=TXCoffResourceWriter.Create;
  251. case CurrentTarget.machine of
  252. // mtnone :
  253. mtppc : Result.MachineType:=cmtppc32aix;
  254. // mtppc64 : Result.MachineType:=cmtppc64aix;
  255. end;
  256. end;
  257. function SetUpMachOWriter : TMachOResourceWriter;
  258. const
  259. ArmSubMachine2MachOSubMachine: array[TSubMachineTypeArm] of TMachOSubMachineTypeArm =
  260. (msmarm_all,msmarm_v4t,msmarm_v6,msmarm_v5tej,msmarm_xscale,msmarm_v7);
  261. var
  262. MachOSubMachineType: TMachoSubMachineType;
  263. begin
  264. Result:=TMachOResourceWriter.Create;
  265. case CurrentTarget.machine of
  266. // mtnone :
  267. mti386 :
  268. begin
  269. Result.MachineType:=mmti386;
  270. MachOSubMachineType.f386SubType:=msm386_all;
  271. end;
  272. mtx86_64 :
  273. begin
  274. Result.MachineType:=mmtx86_64;
  275. MachOSubMachineType.fX64SubType:=msmx64_all;
  276. end;
  277. mtppc :
  278. begin
  279. Result.MachineType:=mmtpowerpc;
  280. MachOSubMachineType.fPpcSubType:=msmppc_all;
  281. end;
  282. mtppc64 :
  283. begin
  284. Result.MachineType:=mmtpowerpc64;
  285. MachOSubMachineType.fPpc64SubType:=msmppc64_all;
  286. end;
  287. mtarm :
  288. begin
  289. Result.MachineType:=mmtarm;
  290. MachOSubMachineType.fArmSubType:=ArmSubMachine2MachOSubMachine[CurrentTarget.submachine.subarm];
  291. end;
  292. mtaarch64 :
  293. begin
  294. Result.MachineType:=mmtarm64;
  295. MachOSubMachineType.fArm64SubType:=msmaarch64_all;
  296. end;
  297. end;
  298. Result.SubMachineType:=MachOSubMachineType;
  299. end;
  300. function SetUpExternalWriter : TExternalResourceWriter;
  301. begin
  302. Result:=TExternalResourceWriter.Create;
  303. case CurrentTarget.machine of
  304. // mtnone :
  305. mtBigEndian : Result.Endianess:=EXT_ENDIAN_BIG;
  306. mtLittleEndian : Result.Endianess:=EXT_ENDIAN_LITTLE;
  307. end;
  308. end;
  309. procedure WriteOutputFile;
  310. var aStream : TClosableFileStream;
  311. aWriter : TAbstractResourceWriter;
  312. msg : string;
  313. begin
  314. Messages.DoVerbose(Format('Trying to create output file %s...',[params.OutputFile]));
  315. try
  316. aStream:=TClosableFileStream.Create(params.OutputFile,fmCreate or fmShareDenyWrite);
  317. except
  318. Messages.DoError(Format(SCantCreateFile,[params.OutputFile]));
  319. halt(halt_write_err);
  320. end;
  321. try
  322. Messages.DoVerbose('Setting up resource writer...');
  323. case CurrentTarget.objformat of
  324. ofRes : aWriter:=SetUpResWriter;
  325. ofElf : aWriter:=SetUpElfWriter;
  326. ofCoff : aWriter:=SetUpCoffWriter;
  327. ofXCoff : aWriter:=SetUpXCoffWriter;
  328. ofMachO : aWriter:=SetUpMachOWriter;
  329. ofExt : aWriter:=SetUpExternalWriter;
  330. end;
  331. try
  332. Messages.DoVerbose(Format('Writing output file %s...',[params.OutputFile]));
  333. try
  334. resources.WriteToStream(aStream,aWriter);
  335. except
  336. on e : Exception do
  337. begin
  338. if e.Message='' then msg:=e.ClassName
  339. else msg:=e.Message;
  340. Messages.DoError(msg);
  341. halt(halt_write_err);
  342. end;
  343. end;
  344. Messages.DoVerbose(Format('Output file %s written',[params.OutputFile]));
  345. finally
  346. aWriter.Free;
  347. end;
  348. finally
  349. aStream.Free;
  350. end;
  351. end;
  352. procedure Cleanup;
  353. begin
  354. Messages.DoVerbose('Cleaning up');
  355. if Resources<>nil then Resources.Free;
  356. if SourceFiles<>nil then SourceFiles.Free;
  357. if Params<>nil then Params.Free;
  358. end;
  359. var before, elapsed : longint;
  360. begin
  361. try
  362. before:=GetCurrentTimeMsec;
  363. ParseParams;
  364. LoadSourceFiles;
  365. WriteOutputFile;
  366. elapsed:=GetCurrentTimeMsec-before;
  367. if elapsed<0 then elapsed:=24*3600*1000 + elapsed;
  368. Messages.DoVerbose(Format('Time elapsed: %d.%d seconds',[elapsed div 1000,(elapsed mod 1000) div 10]));
  369. finally
  370. Cleanup;
  371. end;
  372. end.