dosbox_wrapper.pas 6.6 KB

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