ExternalTools.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (C) 2022 The V-Gears Team
  3. *
  4. * This file is part of V-Gears
  5. *
  6. * V-Gears is free software: you can redistribute it and/or modify it under
  7. * terms of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3.0 (GPLv3) of the License.
  9. *
  10. * V-Gears is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. */
  15. #pragma once
  16. #include <cstdint>
  17. #include <string>
  18. #include <vector>
  19. /**
  20. * Finding and running the external programs the installer uses to convert audio: FFmpeg (sounds and music) and
  21. * TiMidity (to render the MIDI music).
  22. *
  23. * Programs are searched in this order: the path the user chose, a list of directories (for example a "tools" folder
  24. * in the installation directory), and the system PATH. Every candidate is run to check that it really is the
  25. * program and that it can do what the installer needs, so a broken file doesn't hide a good one further down.
  26. *
  27. * Programs are run directly with a list of arguments, not through a shell: there is no quoting to get wrong, paths
  28. * can have spaces, and it works the same on Windows.
  29. */
  30. namespace ExternalTools{
  31. /**
  32. * What is needed to search for an executable.
  33. */
  34. struct Environment{
  35. /**
  36. * The PATH variable.
  37. */
  38. std::string path;
  39. /**
  40. * The PATHEXT variable (Windows): extensions that make a file executable, separated by ';'.
  41. */
  42. std::string pathext;
  43. /**
  44. * True to search with the rules of Windows (';' separates the PATH entries, a file is executable if it has
  45. * one of the PATHEXT extensions) and false for the rules of Unix (':' and the executable permission).
  46. */
  47. bool windows = false;
  48. /**
  49. * Gets the environment of the running system.
  50. */
  51. static Environment FromSystem();
  52. };
  53. /**
  54. * Where to look for the programs.
  55. */
  56. struct Locations{
  57. /**
  58. * Path of FFmpeg that the user chose. Empty if none.
  59. */
  60. std::string ffmpeg;
  61. /**
  62. * Path of TiMidity that the user chose. Empty if none.
  63. */
  64. std::string timidity;
  65. /**
  66. * Directories searched before the PATH.
  67. */
  68. std::vector<std::string> directories;
  69. };
  70. /**
  71. * Gets the locations used by the installer. Set them before an installation.
  72. */
  73. Locations& CurrentLocations();
  74. /**
  75. * Lists the executables that could be a program, in the order they are tried.
  76. *
  77. * @param[in] name Name of the program, without extension.
  78. * @param[in] saved_path Path chosen by the user. It is the first candidate if it is a file.
  79. * @param[in] directories Directories searched before the PATH.
  80. * @param[in] env The environment.
  81. */
  82. std::vector<std::string> Candidates(
  83. const std::string& name, const std::string& saved_path, const std::vector<std::string>& directories,
  84. const Environment& env
  85. );
  86. /**
  87. * A program found in the system.
  88. */
  89. struct Tool{
  90. /**
  91. * Path of the program. Empty if it wasn't found.
  92. */
  93. std::string path;
  94. /**
  95. * First line of the version of the program.
  96. */
  97. std::string version;
  98. /**
  99. * True if a working program was found.
  100. */
  101. bool usable = false;
  102. /**
  103. * Why it can't be used. Empty if it is usable. It's about the first candidate that failed if there were any,
  104. * and says it wasn't found if there were none.
  105. */
  106. std::string problem;
  107. };
  108. /**
  109. * Gets the version number of a tool for the window: "9.0.2-1" from "ffmpeg version 9.0.2-1 Copyright ...", and
  110. * "2.14.0" from "TiMidity++ version 2.14.0". If there is no "version" word it is the whole version line.
  111. * Empty if the tool has no version.
  112. */
  113. std::string ShortVersion(const Tool& tool);
  114. /**
  115. * Result of running a program.
  116. */
  117. struct RunResult{
  118. bool started = false;
  119. bool finished = false;
  120. int exit_code = -1;
  121. /**
  122. * Standard output and error.
  123. */
  124. std::string output;
  125. };
  126. /**
  127. * Runs a program and waits for it.
  128. *
  129. * @param[in] program Path of the program.
  130. * @param[in] arguments Arguments, without quoting.
  131. * @param[in] timeout_ms Milliseconds to wait before killing it. Negative to wait forever.
  132. */
  133. RunResult Run(const std::string& program, const std::vector<std::string>& arguments, int timeout_ms);
  134. /**
  135. * Checks that a file is FFmpeg and that it can write ogg files (it has the Vorbis encoder).
  136. */
  137. Tool ProbeFfmpeg(const std::string& path);
  138. /**
  139. * Checks that a file is TiMidity.
  140. */
  141. Tool ProbeTimidity(const std::string& path);
  142. /**
  143. * Finds a working FFmpeg.
  144. */
  145. Tool FindFfmpeg(const Locations& locations, const Environment& env = Environment::FromSystem());
  146. /**
  147. * Finds a working TiMidity.
  148. */
  149. Tool FindTimidity(const Locations& locations, const Environment& env = Environment::FromSystem());
  150. /**
  151. * Arguments to convert an audio file to ogg with FFmpeg.
  152. */
  153. std::vector<std::string> FfmpegArguments(const std::string& input, const std::string& output);
  154. /**
  155. * Arguments to convert the audio FFmpeg reads from its standard input to ogg.
  156. */
  157. std::vector<std::string> FfmpegStdinArguments(const std::string& output);
  158. /**
  159. * Arguments to render a MIDI file with TiMidity to a wav file written to the standard output.
  160. */
  161. std::vector<std::string> TimidityArguments(const std::string& midi);
  162. /**
  163. * Converts an audio file (wav) to ogg.
  164. *
  165. * @param[in] ffmpeg Path of FFmpeg.
  166. * @param[in] input File to convert.
  167. * @param[in] output File to create. It is removed if the conversion fails.
  168. * @param[out] error Reason of the failure. Optional.
  169. * @return True if the output was created.
  170. */
  171. bool ConvertToOgg(
  172. const std::string& ffmpeg, const std::string& input, const std::string& output, std::string* error = nullptr
  173. );
  174. /**
  175. * Converts a movie to what the game plays: a folder with its frames as JPEG files (00000.jpg,
  176. * 00001.jpg...), an ogg file with its sound, and movie.xml with the number of frames and the
  177. * frames per second.
  178. *
  179. * @param[in] ffmpeg Path of FFmpeg.
  180. * @param[in] input The movie.
  181. * @param[in] directory Folder to create. It is removed if the conversion fails.
  182. * @param[in] name Name of the movie, which is the name of the ogg file.
  183. * @param[in] camera Content of the camera file of the movie, which the game uses to draw the
  184. * models over it. It's written as camera.cam, and the first frame that has a camera in
  185. * movie.xml. Empty if the movie has no camera.
  186. * @param[out] error Reason of the failure. Optional.
  187. * @return True if the frames were created. A movie without sound is converted anyway.
  188. */
  189. bool ConvertMovie(
  190. const std::string& ffmpeg, const std::string& input, const std::string& directory,
  191. const std::string& name, const std::vector<uint8_t>& camera = {}, std::string* error = nullptr
  192. );
  193. /**
  194. * Converts a MIDI file to ogg: TiMidity renders it and its output is sent to FFmpeg.
  195. *
  196. * @param[in] timidity Path of TiMidity.
  197. * @param[in] ffmpeg Path of FFmpeg.
  198. * @param[in] midi File to convert.
  199. * @param[in] output File to create. It is removed if the conversion fails.
  200. * @param[out] error Reason of the failure. Optional.
  201. * @return True if the output was created.
  202. */
  203. bool ConvertMidiToOgg(
  204. const std::string& timidity, const std::string& ffmpeg, const std::string& midi, const std::string& output,
  205. std::string* error = nullptr
  206. );
  207. }