ShaderCompiler.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Exception.h"
  24. #include "File.h"
  25. #include "StringUtils.h"
  26. #include "XMLFile.h"
  27. #include <cstdio>
  28. #include <cstdlib>
  29. #include <cstring>
  30. #include <fstream>
  31. #include <iostream>
  32. #include <string>
  33. #include <vector>
  34. #include <map>
  35. #include "DebugNew.h"
  36. enum ShaderType
  37. {
  38. VS,
  39. PS,
  40. Both
  41. };
  42. struct Variation
  43. {
  44. Variation(const std::string& name, bool isOption) :
  45. mName(name),
  46. mOption(isOption)
  47. {
  48. }
  49. void addDefine(const std::string& def)
  50. {
  51. mDefines.push_back(def);
  52. }
  53. void addExclude(const std::string& excl)
  54. {
  55. mExcludes.push_back(excl);
  56. }
  57. void addInclude(const std::string& incl)
  58. {
  59. mIncludes.push_back(incl);
  60. }
  61. void addRequire(const std::string& req)
  62. {
  63. mRequires.push_back(req);
  64. }
  65. std::string mName;
  66. std::vector<std::string> mDefines;
  67. std::vector<std::string> mExcludes;
  68. std::vector<std::string> mIncludes;
  69. std::vector<std::string> mRequires;
  70. bool mOption;
  71. };
  72. struct Shader
  73. {
  74. Shader(const std::string& name, ShaderType type) :
  75. mName(name),
  76. mType(type)
  77. {
  78. }
  79. void addVariation(const Variation& var)
  80. {
  81. mVariations.push_back(var);
  82. }
  83. std::string mName;
  84. ShaderType mType;
  85. std::vector<Variation> mVariations;
  86. };
  87. struct Parameter
  88. {
  89. std::string mName;
  90. unsigned mIndex;
  91. };
  92. struct Parameters
  93. {
  94. void addVSParam(const std::string& name, unsigned index)
  95. {
  96. Parameter newParam;
  97. newParam.mName = name;
  98. newParam.mIndex = index;
  99. mVSParams.push_back(newParam);
  100. }
  101. void addPSParam(const std::string& name, unsigned index)
  102. {
  103. Parameter newParam;
  104. newParam.mName = name;
  105. newParam.mIndex = index;
  106. mPSParams.push_back(newParam);
  107. }
  108. void addTextureUnit(const std::string& name, unsigned index)
  109. {
  110. Parameter newParam;
  111. newParam.mName = name;
  112. newParam.mIndex = index;
  113. mTextureUnits.push_back(newParam);
  114. }
  115. std::vector<Parameter> mVSParams;
  116. std::vector<Parameter> mPSParams;
  117. std::vector<Parameter> mTextureUnits;
  118. };
  119. std::string gInDir;
  120. std::string gInFile;
  121. std::string gOutDir;
  122. std::map<std::string, unsigned> gVSParams;
  123. std::map<std::string, unsigned> gPSParams;
  124. std::map<std::string, unsigned> gTextureUnits;
  125. bool gUseSM3 = false;
  126. std::vector<std::string> gDefines;
  127. int main(int argc, char** argv);
  128. void run(const std::vector<std::string>& arguments);
  129. void compileVariations(const Shader& baseShader, XMLElement& shaders);
  130. bool compile(ShaderType type, const std::string& input, const std::string& output, const std::vector<std::string>& defines, Parameters& params);
  131. void errorExit(const std::string& error);
  132. int main(int argc, char** argv)
  133. {
  134. std::vector<std::string> arguments;
  135. for (int i = 1; i < argc; ++i)
  136. arguments.push_back(replace(std::string(argv[i]), '/', '\\'));
  137. try
  138. {
  139. run(arguments);
  140. }
  141. catch (Exception& e)
  142. {
  143. std::cout << e.whatStr() << std::endl;
  144. return 1;
  145. }
  146. return 0;
  147. }
  148. void run(const std::vector<std::string>& arguments)
  149. {
  150. if (arguments.size() < 2)
  151. {
  152. errorExit(
  153. "Usage: ShaderCompiler <definitionfile> <outputpath> [SM3] [define1] [define2]\n"
  154. "Note: .hlsl files will be loaded from definition file directory, and binary\n"
  155. "code will be output to same directory as the output file."
  156. );
  157. }
  158. size_t pos = arguments[0].rfind('\\');
  159. if (pos != std::string::npos)
  160. {
  161. gInDir = arguments[0].substr(0, pos);
  162. gInFile = arguments[0].substr(pos + 1);
  163. }
  164. else
  165. {
  166. gInFile = arguments[0];
  167. }
  168. gOutDir = arguments[1];
  169. if (gInDir.empty())
  170. gInDir = ".\\";
  171. if (gOutDir.empty())
  172. gOutDir = ".\\";
  173. if (gInDir[gInDir.length()-1] != '\\')
  174. gInDir = gInDir + "\\";
  175. if (gOutDir[gOutDir.length()-1] != '\\')
  176. gOutDir = gOutDir + "\\";
  177. for (unsigned i = 2; i < arguments.size(); ++i)
  178. {
  179. std::string arg = toUpper(arguments[i]);
  180. if (arg == "SM3")
  181. gUseSM3 = true;
  182. else if (arg == "SM2")
  183. gUseSM3 = false;
  184. gDefines.push_back(arg);
  185. }
  186. XMLFile doc;
  187. File source(arguments[0]);
  188. doc.load(source);
  189. XMLElement shaders = doc.getRootElement("shaders");
  190. XMLFile outDoc;
  191. XMLElement outShaders = outDoc.createRootElement("shaders");
  192. XMLElement shader = shaders.getChildElement("shader");
  193. while (shader)
  194. {
  195. bool writeOutput = false;
  196. std::string source = shader.getString("name");
  197. ShaderType compileType = Both;
  198. std::string type = shader.getString("type");
  199. if ((type == "VS") || (type == "vs"))
  200. compileType = VS;
  201. if ((type == "PS") || (type == "ps"))
  202. compileType = PS;
  203. if ((compileType == Both) || (compileType == PS))
  204. writeOutput = true;
  205. Shader baseShader(source, compileType);
  206. XMLElement variation = shader.getChildElement("");
  207. while (variation)
  208. {
  209. std::string value = variation.getName();
  210. if ((value == "variation") || (value == "option"))
  211. {
  212. std::string name = variation.getString("name");
  213. Variation newVar(name, value == "option");
  214. std::string simpleDefine = variation.getString("define");
  215. if (!simpleDefine.empty())
  216. newVar.addDefine(simpleDefine);
  217. std::string simpleExclude = variation.getString("exclude");
  218. if (!simpleExclude.empty())
  219. newVar.addExclude(simpleExclude);
  220. std::string simpleInclude = variation.getString("include");
  221. if (!simpleInclude.empty())
  222. newVar.addInclude(simpleInclude);
  223. std::string simpleRequire = variation.getString("require");
  224. if (!simpleRequire.empty())
  225. newVar.addRequire(simpleRequire);
  226. XMLElement define = variation.getChildElement("define");
  227. while (define)
  228. {
  229. std::string name = define.getString("name");
  230. newVar.addDefine(name);
  231. define = define.getNextElement("define");
  232. }
  233. XMLElement exclude = variation.getChildElement("exclude");
  234. while (exclude)
  235. {
  236. std::string name = exclude.getString("name");
  237. newVar.addExclude(name);
  238. exclude = exclude.getNextElement("exclude");
  239. }
  240. XMLElement include = variation.getChildElement("include");
  241. while (include)
  242. {
  243. std::string name = include.getString("name");
  244. newVar.addInclude(name);
  245. include = include.getNextElement("include");
  246. }
  247. XMLElement require = variation.getChildElement("require");
  248. while (require)
  249. {
  250. std::string name = require.getString("name");
  251. newVar.addRequire(name);
  252. require = require.getNextElement("require");
  253. }
  254. baseShader.addVariation(newVar);
  255. }
  256. variation = variation.getNextElement();
  257. }
  258. if (baseShader.mType != Both)
  259. compileVariations(baseShader, outShaders);
  260. else
  261. {
  262. baseShader.mType = VS;
  263. compileVariations(baseShader, outShaders);
  264. baseShader.mType = PS;
  265. compileVariations(baseShader, outShaders);
  266. }
  267. if (writeOutput)
  268. {
  269. std::string outFileName = gOutDir + gInDir + source + ".xml";
  270. remove(outFileName.c_str());
  271. // Add global parameter & texture sampler definitions
  272. {
  273. XMLElement parameters = outShaders.createChildElement("vsparameters");
  274. std::multimap<unsigned, std::string> sorted;
  275. for (std::map<std::string, unsigned>::const_iterator i = gVSParams.begin(); i != gVSParams.end(); ++i)
  276. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  277. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  278. {
  279. XMLElement param = parameters.createChildElement("parameter");
  280. param.setString("name", i->second);
  281. param.setString("index", toString(i->first));
  282. }
  283. }
  284. {
  285. XMLElement parameters = outShaders.createChildElement("psparameters");
  286. std::multimap<unsigned, std::string> sorted;
  287. for (std::map<std::string, unsigned>::const_iterator i = gPSParams.begin(); i != gPSParams.end(); ++i)
  288. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  289. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  290. {
  291. XMLElement param = parameters.createChildElement("parameter");
  292. param.setString("name", i->second);
  293. param.setString("index", toString(i->first));
  294. }
  295. }
  296. {
  297. XMLElement parameters = outShaders.createChildElement("textureunits");
  298. std::multimap<unsigned, std::string> sorted;
  299. for (std::map<std::string, unsigned>::const_iterator i = gTextureUnits.begin(); i != gTextureUnits.end(); ++i)
  300. sorted.insert(std::pair<unsigned, std::string>(i->second, i->first));
  301. for (std::multimap<unsigned, std::string>::const_iterator i = sorted.begin(); i != sorted.end(); ++i)
  302. {
  303. XMLElement param = parameters.createChildElement("textureunit");
  304. param.setString("name", i->second);
  305. param.setString("index", toString(i->first));
  306. }
  307. }
  308. File outFile(outFileName, FILE_WRITE);
  309. outDoc.save(outFile);
  310. }
  311. shader = shader.getNextElement("shader");
  312. }
  313. }
  314. void compileVariations(const Shader& baseShader, XMLElement& shaders)
  315. {
  316. unsigned combinations = 1;
  317. std::vector<unsigned> compiled;
  318. bool hasVariations = false;
  319. const std::vector<Variation>& variations = baseShader.mVariations;
  320. std::map<std::string, unsigned> nameToIndex;
  321. if (variations.size() > 32)
  322. errorExit("Maximum amount of variations exceeded");
  323. for (unsigned i = 0; i < variations.size(); ++i)
  324. {
  325. combinations *= 2;
  326. nameToIndex[variations[i].mName] = i;
  327. if (!variations[i].mOption)
  328. hasVariations = true;
  329. }
  330. for (unsigned i = 0; i < combinations; ++i)
  331. {
  332. unsigned active = i; // Variations/options active on this particular combination
  333. bool variationActive = false;
  334. bool skipThis = false;
  335. // Check for excludes/includes/requires
  336. for (unsigned j = 0; j < variations.size(); ++j)
  337. {
  338. if ((active >> j) & 1)
  339. {
  340. for (unsigned k = 0; k < variations[j].mIncludes.size(); ++k)
  341. {
  342. if (nameToIndex.find(variations[j].mIncludes[k]) != nameToIndex.end())
  343. active |= (1 << nameToIndex[variations[j].mIncludes[k]]);
  344. }
  345. for (unsigned k = 0; k < variations[j].mExcludes.size(); ++k)
  346. {
  347. if (nameToIndex.find(variations[j].mExcludes[k]) != nameToIndex.end())
  348. active &= ~(1 << nameToIndex[variations[j].mExcludes[k]]);
  349. }
  350. // If it's a variation, exclude all other variations
  351. if (!variations[j].mOption)
  352. {
  353. for (unsigned k = 0; k < variations.size(); ++k)
  354. {
  355. if ((k != j) && (!variations[k].mOption))
  356. active &= ~(1 << k);
  357. }
  358. variationActive = true;
  359. }
  360. for (unsigned k = 0; k < variations[j].mRequires.size(); ++k)
  361. {
  362. bool requireFound = false;
  363. for (unsigned l = 0; l < gDefines.size(); ++l)
  364. {
  365. if (gDefines[l] == variations[j].mRequires[k])
  366. {
  367. requireFound = true;
  368. break;
  369. }
  370. }
  371. for (unsigned l = 0; l < variations.size(); ++l)
  372. {
  373. if (((active >> l) & 1) && (l != j))
  374. {
  375. if (variations[l].mName == variations[j].mRequires[k])
  376. {
  377. requireFound = true;
  378. break;
  379. }
  380. for (unsigned m = 0; m < variations[l].mDefines.size(); ++m)
  381. {
  382. if (variations[l].mDefines[m] == variations[j].mRequires[k])
  383. {
  384. requireFound = true;
  385. break;
  386. }
  387. }
  388. }
  389. if (requireFound)
  390. break;
  391. }
  392. if (!requireFound)
  393. skipThis = true;
  394. }
  395. }
  396. }
  397. // If variations are included, check that one of them is active
  398. if ((hasVariations) && (!variationActive))
  399. continue;
  400. if (skipThis)
  401. continue;
  402. // Check that this combination is unique
  403. bool unique = true;
  404. for (unsigned j = 0; j < compiled.size(); ++j)
  405. {
  406. if (compiled[j] == active)
  407. {
  408. unique = false;
  409. break;
  410. }
  411. }
  412. if (unique)
  413. {
  414. bool firstSuffix = true;
  415. // Build output shader filename & defines from active variations
  416. std::string outName = baseShader.mName;
  417. std::vector<std::string> defines;
  418. for (unsigned j = 0; j < variations.size(); ++j)
  419. {
  420. if (active & (1 << j))
  421. {
  422. if (variations[j].mName.length())
  423. {
  424. if (firstSuffix)
  425. {
  426. outName = outName + "_" + variations[j].mName;
  427. firstSuffix = false;
  428. }
  429. else
  430. outName = outName + variations[j].mName;
  431. }
  432. for (unsigned k = 0; k < variations[j].mDefines.size(); ++k)
  433. defines.push_back(variations[j].mDefines[k]);
  434. }
  435. }
  436. Parameters params;
  437. bool ok = compile(baseShader.mType, baseShader.mName, outName, defines, params);
  438. // If shader was unnecessary (for example SM2 does not support HQ variations)
  439. // no output may have been produced. Skip in that case.
  440. if (ok)
  441. {
  442. XMLElement shader = shaders.createChildElement("shader");
  443. shader.setString("name", outName);
  444. switch (baseShader.mType)
  445. {
  446. case VS:
  447. shader.setString("type", "vs");
  448. for (unsigned j = 0; j < params.mVSParams.size(); ++j)
  449. {
  450. XMLElement vsParam = shader.createChildElement("parameter");
  451. vsParam.setString("name", params.mVSParams[j].mName);
  452. }
  453. break;
  454. case PS:
  455. shader.setString("type", "ps");
  456. for (unsigned j = 0; j < params.mPSParams.size(); ++j)
  457. {
  458. XMLElement psParam = shader.createChildElement("parameter");
  459. psParam.setString("name", params.mPSParams[j].mName);
  460. }
  461. for (unsigned j = 0; j < params.mTextureUnits.size(); ++j)
  462. {
  463. XMLElement texture = shader.createChildElement("textureunit");
  464. texture.setString("name", params.mTextureUnits[j].mName);
  465. }
  466. break;
  467. }
  468. compiled.push_back(active);
  469. }
  470. }
  471. }
  472. }
  473. bool compile(ShaderType type, const std::string& input, const std::string& output, const std::vector<std::string>& defines,
  474. Parameters& params)
  475. {
  476. bool compiled = false;
  477. if (type == VS)
  478. {
  479. if (!gUseSM3)
  480. {
  481. std::string outFile = output + ".vs2";
  482. std::string command = "fxc /Tvs_2_0 /O3 /Evs /Fo" + gOutDir + gInDir + outFile + " /Fcoutput.txt ";
  483. for (unsigned i = 0; i < defines.size(); ++i)
  484. {
  485. command += "/D" + defines[i] + " ";
  486. }
  487. for (unsigned i = 0; i < gDefines.size(); ++i)
  488. {
  489. command += "/D" + gDefines[i] + " ";
  490. }
  491. command += gInDir + input + ".hlsl";
  492. if (system(command.c_str()))
  493. errorExit("Failed to compile shader " + outFile);
  494. compiled = true;
  495. }
  496. else
  497. {
  498. std::string outFile = output + ".vs3";
  499. std::string command = "fxc /Tvs_3_0 /O3 /Evs /Fo" + gOutDir + gInDir + outFile + " /Fcoutput.txt ";
  500. for (unsigned i = 0; i < defines.size(); ++i)
  501. {
  502. command += "/D" + defines[i] + " ";
  503. }
  504. for (unsigned i = 0; i < gDefines.size(); ++i)
  505. {
  506. command += "/D" + gDefines[i] + " ";
  507. }
  508. command += gInDir + input + ".hlsl";
  509. if (system(command.c_str()))
  510. errorExit("Failed to compile shader " + outFile);
  511. compiled = true;
  512. }
  513. }
  514. if (type == PS)
  515. {
  516. if (!gUseSM3)
  517. {
  518. std::string outFile = output + ".ps2";
  519. std::string command = "fxc /Tps_2_0 /O3 /Eps /Fo" + gOutDir + gInDir + outFile + " /Fcoutput.txt ";
  520. for (unsigned i = 0; i < defines.size(); ++i)
  521. {
  522. command += "/D" + defines[i] + " ";
  523. }
  524. for (unsigned i = 0; i < gDefines.size(); ++i)
  525. {
  526. command += "/D" + gDefines[i] + " ";
  527. }
  528. command += gInDir + input + ".hlsl";
  529. std::cout << command << std::endl;
  530. if (system(command.c_str()))
  531. errorExit("Failed to compile shader " + outFile);
  532. compiled = true;
  533. }
  534. else
  535. {
  536. std::string outFile = output + ".ps3";
  537. std::string command = "fxc /Tps_3_0 /O3 /Gfp /Eps /Fo" + gOutDir + gInDir + outFile + " /Fcoutput.txt ";
  538. for (unsigned i = 0; i < defines.size(); ++i)
  539. {
  540. command += "/D" + defines[i] + " ";
  541. }
  542. for (unsigned i = 0; i < gDefines.size(); ++i)
  543. {
  544. command += "/D" + gDefines[i] + " ";
  545. }
  546. command += gInDir + input + ".hlsl";
  547. std::cout << command << std::endl;
  548. if (system(command.c_str()))
  549. errorExit("Failed to compile shader" + outFile);
  550. compiled = true;
  551. }
  552. }
  553. if (!compiled)
  554. return false;
  555. std::ifstream dump("output.txt");
  556. if (!dump.good())
  557. errorExit("Could not open dump file");
  558. bool paramsStarted = false;
  559. while (!dump.eof())
  560. {
  561. char line[256];
  562. dump.getline(line, 256);
  563. std::string lineStr(line);
  564. std::vector<std::string> elements = split(lineStr, ' ');
  565. if (paramsStarted)
  566. {
  567. if ((!elements.size()) || (elements[0] != "//"))
  568. break;
  569. if ((elements.size() == 4) && (elements[0] == "//") && (elements[1][0] != '-'))
  570. {
  571. std::string name = elements[1];
  572. std::string reg = elements[2];
  573. bool isSampler = false;
  574. if (reg[0] == 's')
  575. isSampler = true;
  576. if ((name[0] == 'c') || (name[0] == 's'))
  577. name = name.substr(1, name.size() - 1);
  578. unsigned index = toInt(reg.substr(1, reg.size() - 1));
  579. if (isSampler)
  580. {
  581. // Skip if it's a G-buffer sampler
  582. if (name.find("Buffer") == std::string::npos)
  583. {
  584. params.addTextureUnit(name, index);
  585. if (gTextureUnits.find(name) != gTextureUnits.end())
  586. {
  587. unsigned oldIndex = gTextureUnits[name];
  588. if (oldIndex != index)
  589. errorExit("Texture " + name + " bound to several sampler registers");
  590. }
  591. gTextureUnits[name] = index;
  592. }
  593. }
  594. else
  595. {
  596. if (type == VS)
  597. {
  598. params.addVSParam(name, index);
  599. if (gVSParams.find(name) != gVSParams.end())
  600. {
  601. unsigned oldIndex = gVSParams[name];
  602. if (oldIndex != index)
  603. errorExit("Parameter " + name + " bound to several constant registers");
  604. }
  605. gVSParams[name] = index;
  606. }
  607. else
  608. {
  609. params.addPSParam(name, index);
  610. if (gPSParams.find(name) != gPSParams.end())
  611. {
  612. unsigned oldIndex = gPSParams[name];
  613. if (oldIndex != index)
  614. errorExit("Parameter " + name + " bound to several constant registers");
  615. }
  616. gPSParams[name] = index;
  617. }
  618. }
  619. }
  620. }
  621. else
  622. {
  623. if ((elements.size() == 4) && (elements[0] == "//") && (elements[1] == "Name"))
  624. paramsStarted = true;
  625. }
  626. }
  627. dump.close();
  628. remove("output.txt");
  629. return true;
  630. }
  631. void errorExit(const std::string& error)
  632. {
  633. throw Exception(error);
  634. }