dosbox_wrapper.pas 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. {$MODE objfpc}{$H+}
  2. uses
  3. SysUtils, StrUtils, Process;
  4. const
  5. use_temp_dir : boolean = true;
  6. hide_execution : boolean = true;
  7. do_exit : boolean =true;
  8. dosbox_timeout : integer = 100; { default timeout in seconds }
  9. var
  10. OutputFileName : String;
  11. function GenerateTempDir: string;
  12. var
  13. FileName: string;
  14. TempDir: string;
  15. Done: Boolean = False;
  16. begin
  17. TempDir := GetTempDir(False);
  18. repeat
  19. try
  20. FileName := TempDir + 'dosboxwrappertmp_' + IntToStr(Random(100000));
  21. MkDir(FileName);
  22. Done := True;
  23. except
  24. on E: EInOutError do
  25. begin
  26. { 5 = Access Denied, returned when a file is duplicated }
  27. if E.ErrorCode <> 5 then
  28. raise;
  29. end;
  30. end;
  31. until Done;
  32. Result := FileName + DirectorySeparator;
  33. end;
  34. procedure GenerateDosBoxConf(const ADosBoxDir: string);
  35. var
  36. SourceConfFileName, TargetConfFileName: string;
  37. SourceFile, TargetFile: TextFile;
  38. S: string;
  39. begin
  40. SourceConfFileName := ExtractFilePath(ParamStr(0)) + 'dosbox.conf';
  41. TargetConfFileName := ADosBoxDir + 'dosbox.conf';
  42. OutputFileName := ADosBoxDir + 'dosbox.out';
  43. Writeln('Using target dosbox.conf ',TargetConfFileName);
  44. AssignFile(SourceFile, SourceConfFileName);
  45. AssignFile(TargetFile, TargetConfFileName);
  46. Reset(SourceFile);
  47. try
  48. Rewrite(TargetFile);
  49. try
  50. while not EoF(SourceFile) do
  51. begin
  52. Readln(SourceFile, S);
  53. S := AnsiReplaceStr(S, '$DosBoxDir', ADosBoxDir);
  54. S := AnsiReplaceStr(S, '$wrapper_output', OutputFileName);
  55. if do_exit then
  56. S := AnsiReplaceStr(S, '$exit', 'exit')
  57. else
  58. S := AnsiReplaceStr(S, '$exit', '');
  59. Writeln(TargetFile, S);
  60. end;
  61. finally
  62. CloseFile(TargetFile);
  63. end;
  64. finally
  65. CloseFile(SourceFile);
  66. end;
  67. end;
  68. procedure CopyFile(ASrcFileName, ADestFileName: string);
  69. var
  70. SrcF, DestF: File;
  71. OldFileMode: Integer;
  72. Buf: array [0..4095] of Byte;
  73. BytesRead: Integer;
  74. begin
  75. Writeln('CopyFile ', ASrcFileName, '->', ADestFileName);
  76. if not AnsiEndsText('.exe', ASrcFileName) then
  77. ASrcFileName := ASrcFileName + '.exe';
  78. OldFileMode := FileMode;
  79. try
  80. AssignFile(SrcF, ASrcFileName);
  81. AssignFile(DestF, ADestFileName);
  82. FileMode := fmOpenRead;
  83. Reset(SrcF, 1);
  84. try
  85. FileMode := fmOpenWrite;
  86. try
  87. Rewrite(DestF, 1);
  88. repeat
  89. BlockRead(SrcF, Buf, SizeOf(Buf), BytesRead);
  90. BlockWrite(DestF, Buf, BytesRead);
  91. until BytesRead < SizeOf(Buf);
  92. finally
  93. CloseFile(DestF);
  94. end;
  95. finally
  96. CloseFile(SrcF);
  97. end;
  98. finally
  99. FileMode := OldFileMode;
  100. end;
  101. end;
  102. { On modified dosbox executable it is possible to get
  103. a copy of all output to CON into a file, simply write it
  104. back to output, so it ends up into testname.elg file }
  105. procedure EchoOutput;
  106. var
  107. StdText : TextFile;
  108. st : string;
  109. line : longint;
  110. begin
  111. if FileExists(OutputFileName) then
  112. begin
  113. Writeln('Trying to open ',OutputFileName);
  114. try
  115. AssignFile(StdText, OutputFileName);
  116. Reset(StdText);
  117. Writeln('Successfully opened ',OutputFileName,', copying content to output');
  118. try
  119. line:=0;
  120. while not eof(StdText) do
  121. begin
  122. Readln(StdText,st);
  123. inc(line);
  124. Writeln(line,': ',st);
  125. end;
  126. finally
  127. CloseFile(StdText);
  128. end;
  129. finally
  130. if use_temp_dir then
  131. DeleteFile(OutputFileName);
  132. end;
  133. end;
  134. end;
  135. function ReadExitCode(const ADosBoxDir: string): Integer;
  136. var
  137. F: TextFile;
  138. begin
  139. AssignFile(F, ADosBoxDir + 'EXITCODE.TXT');
  140. try
  141. Reset(F);
  142. Readln(F, Result);
  143. if Result <> 0 then
  144. Writeln('ExitCode=',Result);
  145. except
  146. Writeln('Unable to read exitcode value');
  147. ReadExitCode:=127*256;
  148. end;
  149. CloseFile(F);
  150. end;
  151. procedure ExecuteDosBox(const ADosBoxBinaryPath, ADosBoxDir: string);
  152. var
  153. Process: TProcess;
  154. Time: Integer = 0;
  155. begin
  156. Process := TProcess.Create(nil);
  157. try
  158. Process.Executable := ADosBoxBinaryPath;
  159. Process.Parameters.Add('-conf');
  160. Process.Parameters.Add(ADosBoxDir + 'dosbox.conf');
  161. if hide_execution then
  162. Process.ShowWindow := swoHIDE;
  163. Process.Execute;
  164. repeat
  165. Inc(Time);
  166. if (Time > 10*dosbox_timeout) and do_exit then
  167. break;
  168. Sleep(100);
  169. until not Process.Running;
  170. if Process.Running then
  171. begin
  172. Writeln('Timeout exceeded. Killing dosbox...');
  173. Process.Terminate(254);
  174. end;
  175. finally
  176. Process.Free;
  177. EchoOutput;
  178. end;
  179. end;
  180. procedure Cleanup(const ADosBoxDir: string);
  181. procedure DeleteIfExists(const AFileName: string);
  182. begin
  183. if FileExists(AFileName) then
  184. DeleteFile(AFileName);
  185. end;
  186. begin
  187. DeleteIfExists(ADosBoxDir + 'dosbox.conf');
  188. DeleteIfExists(ADosBoxDir + 'EXITCODE.TXT');
  189. DeleteIfExists(ADosBoxDir + 'EXITCODE.EXE');
  190. DeleteIfExists(ADosBoxDir + 'TEST.EXE');
  191. RmDir(ADosBoxDir);
  192. end;
  193. var
  194. DosBoxDir: string;
  195. ExitCode: Integer = 255;
  196. DosBoxBinaryPath: string;
  197. begin
  198. Randomize;
  199. if GetEnvironmentVariable('DOSBOX_NO_TEMPDIR')<>'' then
  200. begin
  201. use_temp_dir:=false;
  202. Writeln('use_temp_dir set to false');
  203. end;
  204. if GetEnvironmentVariable('DOSBOX_NO_HIDE')<>'' then
  205. begin
  206. hide_execution:=false;
  207. Writeln('hide_execution set to false');
  208. end;
  209. if GetEnvironmentVariable('DOSBOX_NO_EXIT')<>'' then
  210. begin
  211. do_exit:=false;
  212. Writeln('do_exit set to false');
  213. end;
  214. if GetEnvironmentVariable('DOSBOX_TIMEOUT')<>'' then
  215. begin
  216. dosbox_timeout:=StrToInt(GetEnvironmentVariable('DOSBOX_TIMEOUT'));
  217. Writeln('dosbox_timeout set to ', dosbox_timeout, ' seconds');
  218. end;
  219. if ParamCount = 0 then
  220. begin
  221. Writeln('Usage: ' + ParamStr(0) + ' <executable>');
  222. Writeln('Set DOSBOX_NO_TEMPDIR env variable to 1 to avoid using a temporary directory');
  223. Writeln('Set DOSBOX_NO_HIDE to avoid running dosbox in an hidden window');
  224. Writeln('Set DOSBOX_NO_EXIT to avoid exiting dosbox after test has been run');
  225. Writeln('Set DOSBOX_TIMEOUT to set the timeout in seconds before killing the dosbox process, assuming the test has hanged');
  226. halt(1);
  227. end;
  228. DosBoxBinaryPath := GetEnvironmentVariable('DOSBOX');
  229. if DosBoxBinaryPath = '' then
  230. begin
  231. Writeln('Please set the DOSBOX environment variable to the dosbox executable');
  232. halt(1);
  233. end
  234. else
  235. begin
  236. Writeln('Using DOSBOX executable: ',DosBoxBinaryPath);
  237. end;
  238. { DosBoxDir is used inside dosbox.conf as a MOUNT parameter }
  239. if use_temp_dir then
  240. DosBoxDir := GenerateTempDir
  241. else
  242. begin
  243. Writeln('Using ',ParamStr(1));
  244. DosBoxDir:=ExtractFilePath(ParamStr(1));
  245. if DosBoxDir='' then
  246. DosBoxDir:=GetCurrentDir+DirectorySeparator;
  247. Writeln('Using DosBoxDir=',DosBoxDir);
  248. end;
  249. try
  250. GenerateDosBoxConf(DosBoxDir);
  251. CopyFile(ExtractFilePath(ParamStr(0)) + 'exitcode.exe', DosBoxDir + 'EXITCODE.EXE');
  252. CopyFile(ParamStr(1), DosBoxDir + 'TEST.EXE');
  253. ExecuteDosBox(DosBoxBinaryPath, DosBoxDir);
  254. ExitCode := ReadExitCode(DosBoxDir);
  255. finally
  256. if use_temp_dir then
  257. Cleanup(DosBoxDir);
  258. end;
  259. halt(ExitCode);
  260. end.