fpcres.pas 12 KB

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