fpcres.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. resource,
  17. //readers
  18. resreader, coffreader, winpeimagereader, elfreader, machoreader,
  19. externalreader, dfmreader,
  20. //writers
  21. reswriter, coffwriter, 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(' sparc, alpha, ia64 (elf)');
  63. writeln(' i386, x86_64, powerpc, powerpc64 (mach-o)');
  64. writeln(' bigendian, littleendian (external)');
  65. writeln(' @<file> Read more options from file <file>');
  66. writeln('Default output target: '+TargetToStr(currenttarget));
  67. end;
  68. const
  69. SOutputFileAlreadySet = 'Output file name already set.';
  70. SUnknownParameter = 'Unknown parameter ''%s''';
  71. SArgumentMissing = 'Argument missing for option ''%s''';
  72. SUnknownObjFormat = 'Unknown file format ''%s''';
  73. SUnknownMachine = 'Unknown architecture ''%s''';
  74. SFormatArchMismatch = 'Architecture %s is not available for %s format';
  75. SNoInputFiles = 'No input files';
  76. SNoOutputFile = 'No output file name specified';
  77. SCannotReadConfFile ='Can''t read config file ''%s''';
  78. SCantOpenFile = 'Can''t open file ''%s''';
  79. SUnknownInputFormat = 'No known file format detected for file ''%s''';
  80. SCantCreateFile = 'Can''t create file ''%s''';
  81. function GetCurrentTimeMsec : longint;
  82. var h,m,s,ms : word;
  83. begin
  84. DecodeTime(Time,h,m,s,ms);
  85. Result:=h*3600*1000 + m*60*1000 + s*1000 + ms;
  86. end;
  87. procedure CheckTarget;
  88. begin
  89. //if user explicitally set a format, use it
  90. if params.Target.objformat<>ofNone then
  91. CurrentTarget.objformat:=params.Target.objformat;
  92. //if no machine was specified, check if current is ok for this format,
  93. //otherwise pick the default one for that format
  94. if params.Target.machine=mtNone then
  95. begin
  96. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  97. CurrentTarget.machine:=GetDefaultMachineForFormat(CurrentTarget.objformat);
  98. end
  99. else CurrentTarget.machine:=params.Target.machine;
  100. if not (CurrentTarget.machine in ObjFormats[CurrentTarget.objformat].machines) then
  101. begin
  102. Messages.DoError(Format(SFormatArchMismatch,[
  103. MachineToStr(CurrentTarget.machine),ObjFormatToStr(CurrentTarget.objformat)]));
  104. halt(halt_param_err);
  105. end;
  106. Messages.DoVerbose('target set to '+TargetToStr(CurrentTarget));
  107. end;
  108. procedure CheckInputFiles;
  109. begin
  110. if params.InputFiles.Count=0 then
  111. begin
  112. Messages.DoError(SNoInputFiles);
  113. halt(halt_param_err);
  114. end;
  115. end;
  116. procedure CheckOutputFile;
  117. var tmp : string;
  118. begin
  119. if params.OutputFile<>'' then exit;
  120. if params.InputFiles.Count>1 then
  121. begin
  122. Messages.DoError(SNoOutputFile);
  123. halt(halt_param_err);
  124. end;
  125. tmp:=ChangeFileExt(ExtractFileName(params.InputFiles[0]),
  126. ObjFormats[CurrentTarget.objformat].ext);
  127. if lowercase(tmp)=lowercase(params.InputFiles[0]) then
  128. tmp:=tmp+ObjFormats[CurrentTarget.objformat].ext;
  129. params.OutputFile:=tmp;
  130. end;
  131. procedure ParseParams;
  132. var msg : string;
  133. begin
  134. Messages.DoVerbose('parsing command line parameters');
  135. msg:='';
  136. if ParamCount = 0 then
  137. begin
  138. ShowHelp;
  139. halt(halt_no_err);
  140. end;
  141. params:=TParameters.Create;
  142. try
  143. params.Parse;
  144. except
  145. on e : EOutputFileAlreadySetException do msg:=SOutputFileAlreadySet;
  146. on e : EUnknownParameterException do msg:=Format(SUnknownParameter,[e.Message]);
  147. on e : EArgumentMissingException do msg:=Format(SArgumentMissing,[e.Message]);
  148. on e : EUnknownObjFormatException do msg:=Format(SUnknownObjFormat,[e.Message]);
  149. on e : EUnknownMachineException do msg:=Format(SUnknownMachine,[e.Message]);
  150. on e : ECannotReadConfFile do msg:=Format(SCannotReadConfFile,[e.Message]);
  151. end;
  152. Messages.Verbose:=params.Verbose;
  153. if msg<>'' then
  154. begin
  155. Messages.DoError(msg);
  156. halt(halt_param_err);
  157. end;
  158. if params.Version then
  159. begin
  160. ShowVersion;
  161. halt(halt_no_err);
  162. end;
  163. if params.Help then
  164. begin
  165. ShowHelp;
  166. halt(halt_no_err);
  167. end;
  168. CheckTarget;
  169. CheckInputFiles;
  170. CheckOutputFile;
  171. Messages.DoVerbose('finished parsing command line parameters');
  172. end;
  173. procedure LoadSourceFiles;
  174. var msg : string;
  175. begin
  176. msg:='';
  177. resources:=TResources.Create;
  178. sourcefiles:=TSourceFiles.Create;
  179. sourcefiles.FileList.AddStrings(params.InputFiles);
  180. try
  181. sourcefiles.Load(resources);
  182. except
  183. on e : ECantOpenFileException do msg:=Format(SCantOpenFile,[e.Message]);
  184. on e : EUnknownInputFormatException do msg:=Format(SUnknownInputFormat,[e.Message]);
  185. on e : Exception do
  186. begin
  187. if e.Message='' then msg:=e.ClassName
  188. else msg:=e.Message;
  189. end;
  190. end;
  191. if msg<>'' then
  192. begin
  193. Messages.DoError(msg);
  194. halt(halt_read_err);
  195. end;
  196. end;
  197. function SetUpResWriter : TResResourceWriter;
  198. begin
  199. Result:=TResResourceWriter.Create;
  200. end;
  201. function SetUpElfWriter : TElfResourceWriter;
  202. begin
  203. Result:=TElfResourceWriter.Create;
  204. case CurrentTarget.machine of
  205. // mtnone :
  206. mti386 : Result.MachineType:=emti386;
  207. mtx86_64 : Result.MachineType:=emtx86_64;
  208. mtppc : Result.MachineType:=emtppc;
  209. mtppc64 : Result.MachineType:=emtppc64;
  210. mtarm : Result.MachineType:=emtarm;
  211. mtarmeb : Result.MachineType:=emtarmeb;
  212. mtm68k : Result.MachineType:=emtm68k;
  213. mtsparc : Result.MachineType:=emtsparc;
  214. mtalpha : Result.MachineType:=emtalpha;
  215. mtia64 : Result.MachineType:=emtia64;
  216. end;
  217. end;
  218. function SetUpCoffWriter : TCoffResourceWriter;
  219. begin
  220. Result:=TCoffResourceWriter.Create;
  221. case CurrentTarget.machine of
  222. // mtnone :
  223. mti386 : Result.MachineType:=cmti386;
  224. mtarm : Result.MachineType:=cmtarm;
  225. mtx86_64 : Result.MachineType:=cmtx8664;
  226. end;
  227. end;
  228. function SetUpMachOWriter : TMachOResourceWriter;
  229. begin
  230. Result:=TMachOResourceWriter.Create;
  231. case CurrentTarget.machine of
  232. // mtnone :
  233. mti386 : Result.MachineType:=mmti386;
  234. mtx86_64 : Result.MachineType:=mmtx86_64;
  235. mtppc : Result.MachineType:=mmtpowerpc;
  236. mtppc64 : Result.MachineType:=mmtpowerpc64;
  237. mtarm : Result.MachineType:=mmtarm;
  238. end;
  239. end;
  240. function SetUpExternalWriter : TExternalResourceWriter;
  241. begin
  242. Result:=TExternalResourceWriter.Create;
  243. case CurrentTarget.machine of
  244. // mtnone :
  245. mtBigEndian : Result.Endianess:=EXT_ENDIAN_BIG;
  246. mtLittleEndian : Result.Endianess:=EXT_ENDIAN_LITTLE;
  247. end;
  248. end;
  249. procedure WriteOutputFile;
  250. var aStream : TFileStream;
  251. aWriter : TAbstractResourceWriter;
  252. msg : string;
  253. begin
  254. Messages.DoVerbose(Format('Trying to create output file %s...',[params.OutputFile]));
  255. try
  256. aStream:=TFileStream.Create(params.OutputFile,fmCreate or fmShareDenyWrite);
  257. except
  258. Messages.DoError(Format(SCantCreateFile,[params.OutputFile]));
  259. halt(halt_write_err);
  260. end;
  261. try
  262. Messages.DoVerbose('Setting up resource writer...');
  263. case CurrentTarget.objformat of
  264. ofRes : aWriter:=SetUpResWriter;
  265. ofElf : aWriter:=SetUpElfWriter;
  266. ofCoff : aWriter:=SetUpCoffWriter;
  267. ofMachO : aWriter:=SetUpMachOWriter;
  268. ofExt : aWriter:=SetUpExternalWriter;
  269. end;
  270. try
  271. Messages.DoVerbose(Format('Writing output file %s...',[params.OutputFile]));
  272. try
  273. resources.WriteToStream(aStream,aWriter);
  274. except
  275. on e : Exception do
  276. begin
  277. if e.Message='' then msg:=e.ClassName
  278. else msg:=e.Message;
  279. Messages.DoError(msg);
  280. halt(halt_write_err);
  281. end;
  282. end;
  283. Messages.DoVerbose(Format('Output file %s written',[params.OutputFile]));
  284. finally
  285. aWriter.Free;
  286. end;
  287. finally
  288. aStream.Free;
  289. end;
  290. end;
  291. procedure Cleanup;
  292. begin
  293. Messages.DoVerbose('Cleaning up');
  294. if Resources<>nil then Resources.Free;
  295. if SourceFiles<>nil then SourceFiles.Free;
  296. if Params<>nil then Params.Free;
  297. end;
  298. var before, elapsed : longint;
  299. begin
  300. try
  301. before:=GetCurrentTimeMsec;
  302. ParseParams;
  303. LoadSourceFiles;
  304. WriteOutputFile;
  305. elapsed:=GetCurrentTimeMsec-before;
  306. if elapsed<0 then elapsed:=24*3600*1000 + elapsed;
  307. Messages.DoVerbose(Format('Time elapsed: %d.%d seconds',[elapsed div 1000,(elapsed mod 1000) div 10]));
  308. finally
  309. Cleanup;
  310. end;
  311. end.