Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
8.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Handles running the OS commands for map compilation.
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include <afxtempl.h>
  8. #include "GameConfig.h"
  9. #include "RunCommands.h"
  10. #include "Options.h"
  11. #include <process.h>
  12. #include "ProcessWnd.h"
  13. #include <io.h>
  14. #include <direct.h>
  15. #include "GlobalFunctions.h"
  16. #include "hammer.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include <tier0/memdbgon.h>
  19. static bool s_bRunsCommands = false;
  20. bool IsRunningCommands() { return s_bRunsCommands; }
  21. static char *pszDocPath, *pszDocName, *pszDocExt;
  22. CProcessWnd procWnd;
  23. void FixGameVars(char *pszSrc, char *pszDst, BOOL bUseQuotes)
  24. {
  25. // run through the parms list and substitute $variable strings for
  26. // the real thing
  27. char *pSrc = pszSrc, *pDst = pszDst;
  28. BOOL bInQuote = FALSE;
  29. while(pSrc[0])
  30. {
  31. if(pSrc[0] == '$') // found a parm
  32. {
  33. if(pSrc[1] == '$') // nope, it's a single symbol
  34. {
  35. *pDst++ = '$';
  36. ++pSrc;
  37. }
  38. else
  39. {
  40. // figure out which parm it is ..
  41. ++pSrc;
  42. if (!bInQuote && bUseQuotes)
  43. {
  44. // not in quote, and subbing a variable.. start quote
  45. *pDst++ = '\"';
  46. bInQuote = TRUE;
  47. }
  48. if(!strnicmp(pSrc, "file", 4))
  49. {
  50. pSrc += 4;
  51. strcpy(pDst, pszDocName);
  52. pDst += strlen(pDst);
  53. }
  54. else if(!strnicmp(pSrc, "ext", 3))
  55. {
  56. pSrc += 3;
  57. strcpy(pDst, pszDocExt);
  58. pDst += strlen(pDst);
  59. }
  60. else if(!strnicmp(pSrc, "path", 4))
  61. {
  62. pSrc += 4;
  63. strcpy(pDst, pszDocPath);
  64. pDst += strlen(pDst);
  65. }
  66. else if(!strnicmp(pSrc, "exedir", 6))
  67. {
  68. pSrc += 6;
  69. strcpy(pDst, g_pGameConfig->m_szGameExeDir);
  70. pDst += strlen(pDst);
  71. }
  72. else if(!strnicmp(pSrc, "bspdir", 6))
  73. {
  74. pSrc += 6;
  75. strcpy(pDst, g_pGameConfig->szBSPDir);
  76. pDst += strlen(pDst);
  77. }
  78. else if(!strnicmp(pSrc, "bsp_exe", 7))
  79. {
  80. pSrc += 7;
  81. strcpy(pDst, g_pGameConfig->szBSP);
  82. pDst += strlen(pDst);
  83. }
  84. else if(!strnicmp(pSrc, "vis_exe", 7))
  85. {
  86. pSrc += 7;
  87. strcpy(pDst, g_pGameConfig->szVIS);
  88. pDst += strlen(pDst);
  89. }
  90. else if(!strnicmp(pSrc, "light_exe", 9))
  91. {
  92. pSrc += 9;
  93. strcpy(pDst, g_pGameConfig->szLIGHT);
  94. pDst += strlen(pDst);
  95. }
  96. else if(!strnicmp(pSrc, "game_exe", 8))
  97. {
  98. pSrc += 8;
  99. strcpy(pDst, g_pGameConfig->szExecutable);
  100. pDst += strlen(pDst);
  101. }
  102. else if (!strnicmp(pSrc, "gamedir", 7))
  103. {
  104. pSrc += 7;
  105. strcpy(pDst, g_pGameConfig->m_szModDir);
  106. pDst += strlen(pDst);
  107. }
  108. }
  109. }
  110. else
  111. {
  112. if(*pSrc == ' ' && bInQuote)
  113. {
  114. bInQuote = FALSE;
  115. *pDst++ = '\"'; // close quotes
  116. }
  117. // just copy the char into the destination buffer
  118. *pDst++ = *pSrc++;
  119. }
  120. }
  121. if(bInQuote)
  122. {
  123. bInQuote = FALSE;
  124. *pDst++ = '\"'; // close quotes
  125. }
  126. pDst[0] = 0;
  127. }
  128. static void RemoveQuotes(char *pBuf)
  129. {
  130. if(pBuf[0] == '\"')
  131. strcpy(pBuf, pBuf+1);
  132. if(pBuf[strlen(pBuf)-1] == '\"')
  133. pBuf[strlen(pBuf)-1] = 0;
  134. }
  135. LPCTSTR GetErrorString()
  136. {
  137. static char szBuf[200];
  138. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
  139. szBuf, 200, NULL);
  140. char *p = strchr(szBuf, '\r'); // get rid of \r\n
  141. if(p) p[0] = 0;
  142. return szBuf;
  143. }
  144. bool RunCommands(CCommandArray& Commands, LPCTSTR pszOrigDocName)
  145. {
  146. s_bRunsCommands = true;
  147. char szCurDir[MAX_PATH];
  148. _getcwd(szCurDir, MAX_PATH);
  149. procWnd.GetReady();
  150. // cut up document name into file and extension components.
  151. // create two sets of buffers - one set with the long filename
  152. // and one set with the 8.3 format.
  153. char szDocLongPath[MAX_PATH] = {0}, szDocLongName[MAX_PATH] = {0},
  154. szDocLongExt[MAX_PATH] = {0};
  155. char szDocShortPath[MAX_PATH] = {0}, szDocShortName[MAX_PATH] = {0},
  156. szDocShortExt[MAX_PATH] = {0};
  157. GetFullPathName(pszOrigDocName, MAX_PATH, szDocLongPath, NULL);
  158. GetShortPathName(pszOrigDocName, szDocShortPath, MAX_PATH);
  159. // split them up
  160. char *p = strrchr(szDocLongPath, '.');
  161. if(p && strrchr(szDocLongPath, '\\') < p && strrchr(szDocLongPath, '/') < p)
  162. {
  163. // got the extension
  164. strcpy(szDocLongExt, p+1);
  165. p[0] = 0;
  166. }
  167. p = strrchr(szDocLongPath, '\\');
  168. if(!p)
  169. p = strrchr(szDocLongPath, '/');
  170. if(p)
  171. {
  172. // got the filepart
  173. strcpy(szDocLongName, p+1);
  174. p[0] = 0;
  175. }
  176. // split the short part up
  177. p = strrchr(szDocShortPath, '.');
  178. if(p && strrchr(szDocShortPath, '\\') < p && strrchr(szDocShortPath, '/') < p)
  179. {
  180. // got the extension
  181. strcpy(szDocShortExt, p+1);
  182. p[0] = 0;
  183. }
  184. p = strrchr(szDocShortPath, '\\');
  185. if(!p)
  186. p = strrchr(szDocShortPath, '/');
  187. if(p)
  188. {
  189. // got the filepart
  190. strcpy(szDocShortName, p+1);
  191. p[0] = 0;
  192. }
  193. int iSize = Commands.GetSize(), i = 0;
  194. char *ppParms[32];
  195. while(iSize--)
  196. {
  197. CCOMMAND &cmd = Commands[i++];
  198. // anything there?
  199. if((!cmd.szRun[0] && !cmd.iSpecialCmd) || !cmd.bEnable)
  200. continue;
  201. // set name pointers for long filenames
  202. pszDocExt = szDocLongExt;
  203. pszDocName = szDocLongName;
  204. pszDocPath = szDocLongPath;
  205. char szNewParms[MAX_PATH*5], szNewRun[MAX_PATH*5];
  206. // HACK: force the spawnv call for launching the game
  207. if (!Q_stricmp(cmd.szRun, "$game_exe"))
  208. {
  209. cmd.bUseProcessWnd = FALSE;
  210. }
  211. FixGameVars(cmd.szRun, szNewRun, TRUE);
  212. FixGameVars(cmd.szParms, szNewParms, TRUE);
  213. CString strTmp;
  214. strTmp.Format("\r\n"
  215. "** Executing...\r\n"
  216. "** Command: %s\r\n"
  217. "** Parameters: %s\r\n\r\n", szNewRun, szNewParms);
  218. procWnd.Append(strTmp);
  219. // create a parameter list (not always required)
  220. if(!cmd.bUseProcessWnd || cmd.iSpecialCmd)
  221. {
  222. p = szNewParms;
  223. ppParms[0] = szNewRun;
  224. int iArg = 1;
  225. BOOL bDone = FALSE;
  226. while(p[0])
  227. {
  228. ppParms[iArg++] = p;
  229. while(p[0])
  230. {
  231. if(p[0] == ' ')
  232. {
  233. // found a space-separator
  234. p[0] = 0;
  235. p++;
  236. // skip remaining white space
  237. while (*p == ' ')
  238. p++;
  239. break;
  240. }
  241. // found the beginning of a quoted parameters
  242. if(p[0] == '\"')
  243. {
  244. while(1)
  245. {
  246. p++;
  247. if(p[0] == '\"')
  248. {
  249. // found the end
  250. if(p[1] == 0)
  251. bDone = TRUE;
  252. p[1] = 0; // kick its ass
  253. p += 2;
  254. // skip remaining white space
  255. while (*p == ' ')
  256. p++;
  257. break;
  258. }
  259. }
  260. break;
  261. }
  262. // else advance p
  263. ++p;
  264. }
  265. if(!p[0] || bDone)
  266. break; // done.
  267. }
  268. ppParms[iArg] = NULL;
  269. if(cmd.iSpecialCmd)
  270. {
  271. BOOL bError = FALSE;
  272. LPCTSTR pszError = "";
  273. if(cmd.iSpecialCmd == CCCopyFile && iArg == 3)
  274. {
  275. RemoveQuotes(ppParms[1]);
  276. RemoveQuotes(ppParms[2]);
  277. // don't copy if we're already there
  278. if (stricmp(ppParms[1], ppParms[2]) &&
  279. (!CopyFile(ppParms[1], ppParms[2], FALSE)))
  280. {
  281. bError = TRUE;
  282. pszError = GetErrorString();
  283. }
  284. }
  285. else if(cmd.iSpecialCmd == CCDelFile && iArg == 2)
  286. {
  287. RemoveQuotes(ppParms[1]);
  288. if(!DeleteFile(ppParms[1]))
  289. {
  290. bError = TRUE;
  291. pszError = GetErrorString();
  292. }
  293. }
  294. else if(cmd.iSpecialCmd == CCRenameFile && iArg == 3)
  295. {
  296. RemoveQuotes(ppParms[1]);
  297. RemoveQuotes(ppParms[2]);
  298. if(rename(ppParms[1], ppParms[2]))
  299. {
  300. bError = TRUE;
  301. pszError = strerror(errno);
  302. }
  303. }
  304. else if(cmd.iSpecialCmd == CCChangeDir && iArg == 2)
  305. {
  306. RemoveQuotes(ppParms[1]);
  307. if(mychdir(ppParms[1]) == -1)
  308. {
  309. bError = TRUE;
  310. pszError = strerror(errno);
  311. }
  312. }
  313. if(bError)
  314. {
  315. CString str;
  316. str.Format("The command failed. Windows reported the error:\r\n"
  317. " \"%s\"\r\n", pszError);
  318. procWnd.Append(str);
  319. procWnd.SetForegroundWindow();
  320. str += "\r\nDo you want to continue?";
  321. if(AfxMessageBox(str, MB_YESNO) == IDNO)
  322. break;
  323. }
  324. }
  325. else
  326. {
  327. // Change to the game exe folder before spawning the engine.
  328. // This is necessary for Steam to find the correct Steam DLL (it
  329. // uses the current working directory to search).
  330. char szDir[MAX_PATH];
  331. Q_strncpy(szDir, szNewRun, sizeof(szDir));
  332. Q_StripFilename(szDir);
  333. mychdir(szDir);
  334. // YWB Force asynchronous operation so that engine doesn't hang on
  335. // exit??? Seems to work.
  336. // spawnv doesn't like quotes
  337. RemoveQuotes(szNewRun);
  338. _spawnv(/*cmd.bNoWait ?*/ _P_NOWAIT /*: P_WAIT*/, szNewRun,
  339. (const char *const *)ppParms);
  340. }
  341. }
  342. else
  343. {
  344. procWnd.Execute(szNewRun, szNewParms);
  345. }
  346. // check for existence?
  347. if(cmd.bEnsureCheck)
  348. {
  349. char szFile[MAX_PATH];
  350. FixGameVars(cmd.szEnsureFn, szFile, FALSE);
  351. if(GetFileAttributes(szFile) == 0xFFFFFFFF)
  352. {
  353. // not there!
  354. CString str;
  355. str.Format("The file '%s' was not built.\n"
  356. "Do you want to continue?", szFile);
  357. procWnd.SetForegroundWindow();
  358. if(AfxMessageBox(str, MB_YESNO) == IDNO)
  359. break; // outta here
  360. }
  361. }
  362. }
  363. mychdir(szCurDir);
  364. s_bRunsCommands = false;
  365. return TRUE;
  366. }