fpcres.pas 12 KB

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