fpcres.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. mtaarch64 : Result.MachineType:=cmtaarch64;
  247. end;
  248. end;
  249. function SetUpXCoffWriter : TCoffResourceWriter;
  250. begin
  251. Result:=TXCoffResourceWriter.Create;
  252. case CurrentTarget.machine of
  253. // mtnone :
  254. mtppc : Result.MachineType:=cmtppc32aix;
  255. // mtppc64 : Result.MachineType:=cmtppc64aix;
  256. end;
  257. end;
  258. function SetUpMachOWriter : TMachOResourceWriter;
  259. const
  260. ArmSubMachine2MachOSubMachine: array[TSubMachineTypeArm] of TMachOSubMachineTypeArm =
  261. (msmarm_all,msmarm_v4t,msmarm_v6,msmarm_v5tej,msmarm_xscale,msmarm_v7);
  262. var
  263. MachOSubMachineType: TMachoSubMachineType;
  264. begin
  265. Result:=TMachOResourceWriter.Create;
  266. case CurrentTarget.machine of
  267. // mtnone :
  268. mti386 :
  269. begin
  270. Result.MachineType:=mmti386;
  271. MachOSubMachineType.f386SubType:=msm386_all;
  272. end;
  273. mtx86_64 :
  274. begin
  275. Result.MachineType:=mmtx86_64;
  276. MachOSubMachineType.fX64SubType:=msmx64_all;
  277. end;
  278. mtppc :
  279. begin
  280. Result.MachineType:=mmtpowerpc;
  281. MachOSubMachineType.fPpcSubType:=msmppc_all;
  282. end;
  283. mtppc64 :
  284. begin
  285. Result.MachineType:=mmtpowerpc64;
  286. MachOSubMachineType.fPpc64SubType:=msmppc64_all;
  287. end;
  288. mtarm :
  289. begin
  290. Result.MachineType:=mmtarm;
  291. MachOSubMachineType.fArmSubType:=ArmSubMachine2MachOSubMachine[CurrentTarget.submachine.subarm];
  292. end;
  293. mtaarch64 :
  294. begin
  295. Result.MachineType:=mmtarm64;
  296. MachOSubMachineType.fArm64SubType:=msmaarch64_all;
  297. end;
  298. end;
  299. Result.SubMachineType:=MachOSubMachineType;
  300. end;
  301. function SetUpExternalWriter : TExternalResourceWriter;
  302. begin
  303. Result:=TExternalResourceWriter.Create;
  304. case CurrentTarget.machine of
  305. // mtnone :
  306. mtBigEndian : Result.Endianess:=EXT_ENDIAN_BIG;
  307. mtLittleEndian : Result.Endianess:=EXT_ENDIAN_LITTLE;
  308. end;
  309. end;
  310. procedure WriteOutputFile;
  311. var aStream : TClosableFileStream;
  312. aWriter : TAbstractResourceWriter;
  313. msg : string;
  314. begin
  315. Messages.DoVerbose(Format('Trying to create output file %s...',[params.OutputFile]));
  316. try
  317. aStream:=TClosableFileStream.Create(params.OutputFile,fmCreate or fmShareDenyWrite);
  318. except
  319. Messages.DoError(Format(SCantCreateFile,[params.OutputFile]));
  320. halt(halt_write_err);
  321. end;
  322. try
  323. Messages.DoVerbose('Setting up resource writer...');
  324. case CurrentTarget.objformat of
  325. ofRes : aWriter:=SetUpResWriter;
  326. ofElf : aWriter:=SetUpElfWriter;
  327. ofCoff : aWriter:=SetUpCoffWriter;
  328. ofXCoff : aWriter:=SetUpXCoffWriter;
  329. ofMachO : aWriter:=SetUpMachOWriter;
  330. ofExt : aWriter:=SetUpExternalWriter;
  331. end;
  332. try
  333. Messages.DoVerbose(Format('Writing output file %s...',[params.OutputFile]));
  334. try
  335. resources.WriteToStream(aStream,aWriter);
  336. except
  337. on e : Exception do
  338. begin
  339. if e.Message='' then msg:=e.ClassName
  340. else msg:=e.Message;
  341. Messages.DoError(msg);
  342. halt(halt_write_err);
  343. end;
  344. end;
  345. Messages.DoVerbose(Format('Output file %s written',[params.OutputFile]));
  346. finally
  347. aWriter.Free;
  348. end;
  349. finally
  350. aStream.Free;
  351. end;
  352. end;
  353. procedure Cleanup;
  354. begin
  355. Messages.DoVerbose('Cleaning up');
  356. if Resources<>nil then Resources.Free;
  357. if SourceFiles<>nil then SourceFiles.Free;
  358. if Params<>nil then Params.Free;
  359. end;
  360. var before, elapsed : longint;
  361. begin
  362. try
  363. before:=GetCurrentTimeMsec;
  364. ParseParams;
  365. LoadSourceFiles;
  366. WriteOutputFile;
  367. elapsed:=GetCurrentTimeMsec-before;
  368. if elapsed<0 then elapsed:=24*3600*1000 + elapsed;
  369. Messages.DoVerbose(Format('Time elapsed: %d.%d seconds',[elapsed div 1000,(elapsed mod 1000) div 10]));
  370. finally
  371. Cleanup;
  372. end;
  373. end.