fpcres.pas 13 KB

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