fpcres.pas 13 KB

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