fpcres.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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(' riscv32, riscv64,');
  66. writeln(' sparc, alpha, ia64, mips, mipsel (elf)');
  67. writeln(' i386, x86_64, powerpc, powerpc64, arm, aarch64 (mach-o)');
  68. writeln(' bigendian, littleendian (external)');
  69. writeln(' --subarch, -s <name> Set object file sub-architecture. Supported values:');
  70. writeln(' arm: all, v4t, v6, v5tej, xscale, v7');
  71. writeln(' other architectures: all');
  72. writeln(' @<file> Read more options from file <file>');
  73. writeln('Default output target: '+TargetToStr(currenttarget));
  74. end;
  75. const
  76. SOutputFileAlreadySet = 'Output file name already set.';
  77. SUnknownParameter = 'Unknown parameter ''%s''';
  78. SArgumentMissing = 'Argument missing for option ''%s''';
  79. SUnknownObjFormat = 'Unknown file format ''%s''';
  80. SUnknownMachine = 'Unknown architecture ''%s''';
  81. SFormatArchMismatch = 'Architecture %s is not available for %s format';
  82. SNoInputFiles = 'No input files';
  83. SNoOutputFile = 'No output file name specified';
  84. SCannotReadConfFile ='Can''t read config file ''%s''';
  85. SCantOpenFile = 'Can''t open file ''%s''';
  86. SUnknownInputFormat = 'No known file format detected for file ''%s''';
  87. SCantCreateFile = 'Can''t create file ''%s''';
  88. function GetCurrentTimeMsec : longint;
  89. var h,m,s,ms : word;
  90. begin
  91. DecodeTime(Time,h,m,s,ms);
  92. Result:=h*3600*1000 + m*60*1000 + s*1000 + ms;
  93. end;
  94. procedure CheckTarget;
  95. begin
  96. //if user explicitally set a format, use it
  97. if params.Target.objformat<>ofNone then
  98. CurrentTarget.objformat:=params.Target.objformat;
  99. //if no machine was specified, check if current is ok for this format,
  100. //otherwise pick the default one for that format
  101. if params.Target.machine=mtNone then
  102. begin
  103. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  104. begin
  105. CurrentTarget.machine:=GetDefaultMachineForFormat(CurrentTarget.objformat);
  106. CurrentTarget.submachine:=GetDefaultSubMachineForMachine(currentTarget.machine);
  107. end
  108. end
  109. else
  110. begin
  111. CurrentTarget.machine:=params.Target.machine;
  112. CurrentTarget.submachine:=params.Target.submachine;
  113. end;
  114. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  115. begin
  116. Messages.DoError(Format(SFormatArchMismatch,[
  117. MachineToStr(CurrentTarget.machine),ObjFormatToStr(CurrentTarget.objformat)]));
  118. halt(halt_param_err);
  119. end;
  120. Messages.DoVerbose('target set to '+TargetToStr(CurrentTarget));
  121. end;
  122. procedure CheckInputFiles;
  123. begin
  124. if params.InputFiles.Count=0 then
  125. begin
  126. Messages.DoError(SNoInputFiles);
  127. halt(halt_param_err);
  128. end;
  129. end;
  130. procedure CheckOutputFile;
  131. var tmp : string;
  132. begin
  133. if params.OutputFile<>'' then exit;
  134. if params.InputFiles.Count>1 then
  135. begin
  136. Messages.DoError(SNoOutputFile);
  137. halt(halt_param_err);
  138. end;
  139. tmp:=ChangeFileExt(ExtractFileName(params.InputFiles[0]),
  140. ObjFormats[CurrentTarget.objformat].ext);
  141. if lowercase(tmp)=lowercase(params.InputFiles[0]) then
  142. tmp:=tmp+ObjFormats[CurrentTarget.objformat].ext;
  143. params.OutputFile:=tmp;
  144. end;
  145. procedure ParseParams;
  146. var msg : string;
  147. begin
  148. Messages.DoVerbose('parsing command line parameters');
  149. msg:='';
  150. if ParamCount = 0 then
  151. begin
  152. ShowHelp;
  153. halt(halt_no_err);
  154. end;
  155. params:=TParameters.Create;
  156. try
  157. params.Parse;
  158. except
  159. on e : EOutputFileAlreadySetException do msg:=SOutputFileAlreadySet;
  160. on e : EUnknownParameterException do msg:=Format(SUnknownParameter,[e.Message]);
  161. on e : EArgumentMissingException do msg:=Format(SArgumentMissing,[e.Message]);
  162. on e : EUnknownObjFormatException do msg:=Format(SUnknownObjFormat,[e.Message]);
  163. on e : EUnknownMachineException do msg:=Format(SUnknownMachine,[e.Message]);
  164. on e : ECannotReadConfFile do msg:=Format(SCannotReadConfFile,[e.Message]);
  165. end;
  166. Messages.Verbose:=params.Verbose;
  167. if msg<>'' then
  168. begin
  169. Messages.DoError(msg);
  170. halt(halt_param_err);
  171. end;
  172. if params.Version then
  173. begin
  174. ShowVersion;
  175. halt(halt_no_err);
  176. end;
  177. if params.Help then
  178. begin
  179. ShowHelp;
  180. halt(halt_no_err);
  181. end;
  182. CheckTarget;
  183. CheckInputFiles;
  184. CheckOutputFile;
  185. Messages.DoVerbose('finished parsing command line parameters');
  186. end;
  187. procedure LoadSourceFiles;
  188. var msg : string;
  189. begin
  190. msg:='';
  191. resources:=TResources.Create;
  192. sourcefiles:=TSourceFiles.Create;
  193. sourcefiles.FileList.AddStrings(params.InputFiles);
  194. sourcefiles.RCDefines.AddStrings(params.RCDefines);
  195. sourcefiles.RCIncludeDirs.AddStrings(params.RCIncludeDirs);
  196. sourcefiles.RCMode:=CurrentTarget.objformat=ofRes;
  197. try
  198. sourcefiles.Load(resources);
  199. except
  200. on e : ECantOpenFileException do msg:=Format(SCantOpenFile,[e.Message]);
  201. on e : EUnknownInputFormatException do msg:=Format(SUnknownInputFormat,[e.Message]);
  202. on e : Exception do
  203. begin
  204. if e.Message='' then msg:=e.ClassName
  205. else msg:=e.Message;
  206. end;
  207. end;
  208. if msg<>'' then
  209. begin
  210. Messages.DoError(msg);
  211. halt(halt_read_err);
  212. end;
  213. end;
  214. function SetUpResWriter : TResResourceWriter;
  215. begin
  216. Result:=TResResourceWriter.Create;
  217. end;
  218. function SetUpElfWriter : TElfResourceWriter;
  219. begin
  220. Result:=TElfResourceWriter.Create;
  221. case CurrentTarget.machine of
  222. // mtnone :
  223. mti386 : Result.MachineType:=emti386;
  224. mtx86_64 : Result.MachineType:=emtx86_64;
  225. mtppc : Result.MachineType:=emtppc;
  226. mtppc64 : Result.MachineType:=emtppc64;
  227. mtarm : Result.MachineType:=emtarm;
  228. mtarmeb : Result.MachineType:=emtarmeb;
  229. mtm68k : Result.MachineType:=emtm68k;
  230. mtsparc : Result.MachineType:=emtsparc;
  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. mtriscv32 : Result.MachineType:=emtriscv32;
  238. mtriscv64 : Result.MachineType:=emtriscv64;
  239. end;
  240. end;
  241. function SetUpCoffWriter : TCoffResourceWriter;
  242. begin
  243. Result:=TCoffResourceWriter.Create;
  244. case CurrentTarget.machine of
  245. // mtnone :
  246. mti386 : Result.MachineType:=cmti386;
  247. mtarm : Result.MachineType:=cmtarm;
  248. mtx86_64 : Result.MachineType:=cmtx8664;
  249. mtaarch64 : Result.MachineType:=cmtaarch64;
  250. end;
  251. end;
  252. function SetUpXCoffWriter : TCoffResourceWriter;
  253. begin
  254. Result:=TXCoffResourceWriter.Create;
  255. case CurrentTarget.machine of
  256. // mtnone :
  257. mtppc : Result.MachineType:=cmtppc32aix;
  258. // mtppc64 : Result.MachineType:=cmtppc64aix;
  259. end;
  260. end;
  261. function SetUpMachOWriter : TMachOResourceWriter;
  262. const
  263. ArmSubMachine2MachOSubMachine: array[TSubMachineTypeArm] of TMachOSubMachineTypeArm =
  264. (msmarm_all,msmarm_v4t,msmarm_v6,msmarm_v5tej,msmarm_xscale,msmarm_v7);
  265. var
  266. MachOSubMachineType: TMachoSubMachineType;
  267. begin
  268. Result:=TMachOResourceWriter.Create;
  269. case CurrentTarget.machine of
  270. // mtnone :
  271. mti386 :
  272. begin
  273. Result.MachineType:=mmti386;
  274. MachOSubMachineType.f386SubType:=msm386_all;
  275. end;
  276. mtx86_64 :
  277. begin
  278. Result.MachineType:=mmtx86_64;
  279. MachOSubMachineType.fX64SubType:=msmx64_all;
  280. end;
  281. mtppc :
  282. begin
  283. Result.MachineType:=mmtpowerpc;
  284. MachOSubMachineType.fPpcSubType:=msmppc_all;
  285. end;
  286. mtppc64 :
  287. begin
  288. Result.MachineType:=mmtpowerpc64;
  289. MachOSubMachineType.fPpc64SubType:=msmppc64_all;
  290. end;
  291. mtarm :
  292. begin
  293. Result.MachineType:=mmtarm;
  294. MachOSubMachineType.fArmSubType:=ArmSubMachine2MachOSubMachine[CurrentTarget.submachine.subarm];
  295. end;
  296. mtaarch64 :
  297. begin
  298. Result.MachineType:=mmtarm64;
  299. MachOSubMachineType.fArm64SubType:=msmaarch64_all;
  300. end;
  301. end;
  302. Result.SubMachineType:=MachOSubMachineType;
  303. end;
  304. function SetUpExternalWriter : TExternalResourceWriter;
  305. begin
  306. Result:=TExternalResourceWriter.Create;
  307. case CurrentTarget.machine of
  308. // mtnone :
  309. mtBigEndian : Result.Endianess:=EXT_ENDIAN_BIG;
  310. mtLittleEndian : Result.Endianess:=EXT_ENDIAN_LITTLE;
  311. end;
  312. end;
  313. procedure WriteOutputFile;
  314. var aStream : TClosableFileStream;
  315. aWriter : TAbstractResourceWriter;
  316. msg : string;
  317. begin
  318. Messages.DoVerbose(Format('Trying to create output file %s...',[params.OutputFile]));
  319. try
  320. aStream:=TClosableFileStream.Create(params.OutputFile,fmCreate or fmShareDenyWrite);
  321. except
  322. Messages.DoError(Format(SCantCreateFile,[params.OutputFile]));
  323. halt(halt_write_err);
  324. end;
  325. try
  326. Messages.DoVerbose('Setting up resource writer...');
  327. case CurrentTarget.objformat of
  328. ofRes : aWriter:=SetUpResWriter;
  329. ofElf : aWriter:=SetUpElfWriter;
  330. ofCoff : aWriter:=SetUpCoffWriter;
  331. ofXCoff : aWriter:=SetUpXCoffWriter;
  332. ofMachO : aWriter:=SetUpMachOWriter;
  333. ofExt : aWriter:=SetUpExternalWriter;
  334. end;
  335. try
  336. Messages.DoVerbose(Format('Writing output file %s...',[params.OutputFile]));
  337. try
  338. resources.WriteToStream(aStream,aWriter);
  339. except
  340. on e : Exception do
  341. begin
  342. if e.Message='' then msg:=e.ClassName
  343. else msg:=e.Message;
  344. Messages.DoError(msg);
  345. halt(halt_write_err);
  346. end;
  347. end;
  348. Messages.DoVerbose(Format('Output file %s written',[params.OutputFile]));
  349. finally
  350. aWriter.Free;
  351. end;
  352. finally
  353. aStream.Free;
  354. end;
  355. end;
  356. procedure Cleanup;
  357. begin
  358. Messages.DoVerbose('Cleaning up');
  359. if Resources<>nil then Resources.Free;
  360. if SourceFiles<>nil then SourceFiles.Free;
  361. if Params<>nil then Params.Free;
  362. end;
  363. var before, elapsed : longint;
  364. begin
  365. try
  366. before:=GetCurrentTimeMsec;
  367. ParseParams;
  368. LoadSourceFiles;
  369. WriteOutputFile;
  370. elapsed:=GetCurrentTimeMsec-before;
  371. if elapsed<0 then elapsed:=24*3600*1000 + elapsed;
  372. Messages.DoVerbose(Format('Time elapsed: %d.%d seconds',[elapsed div 1000,(elapsed mod 1000) div 10]));
  373. finally
  374. Cleanup;
  375. end;
  376. end.