PageRenderTime 705ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/tools/upgrader/main.cpp

#
C++ | 917 lines | 622 code | 163 blank | 132 comment | 82 complexity | 4b90e7abb38057fea0d9d86adff31697 MD5 | raw file
Possible License(s): GPL-2.0
  1. //----------------------------------------------------------------------------
  2. // EDGE Upgrader Tool
  3. //----------------------------------------------------------------------------
  4. //
  5. // Copyright (c) 2004 The EDGE Team.
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. //
  12. // This program is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. //----------------------------------------------------------------------------
  18. #include "epi.h"
  19. #include "epiarray.h"
  20. #include "epicrc.h"
  21. #include "epiendian.h"
  22. #include "epierror.h"
  23. #include "epifile.h"
  24. #include "epifilesystem.h"
  25. #include "epistring.h"
  26. #include "epitype.h"
  27. #include <iostream>
  28. // Versioning
  29. #define VERSION_FILE "version.dat"
  30. #define VERSION_ID 0x00000001
  31. // Debugging stuff...
  32. #define DEBUG
  33. // Parameter keys...
  34. #define KEY_CFGDIR "--config-dir"
  35. #define KEY_EDGEDIR "--edge-dir"
  36. #define KEY_HELP "--help"
  37. /* --> For future use...
  38. #define KEY_EXISTINGVERSION "--existing-version"
  39. #define KEY_MODE "--mode"
  40. */
  41. // Edge settings
  42. #define EDGE_BUG_ADDR "<http://edge.sourceforge.net>"
  43. #ifdef WIN32
  44. #define EDGE_CFG_SUBDIR "Application Data\\Edge"
  45. #else
  46. #define EDGE_CFG_SUBDIR ".edge"
  47. #endif
  48. #define EDGE_CFG_NAME "edge.cfg"
  49. #define EDGE_SG_MASK "*.esg"
  50. #define EDGE_SGSUBDIR "savegame"
  51. // Error Codes
  52. enum error_e
  53. {
  54. ERROR_GENERICUSER,
  55. ERROR_INTERNAL,
  56. ERROR_NEEDHELP,
  57. ERROR_NOCONFIGDIR,
  58. ERROR_NOEDGEDIR,
  59. ERROR_UNABLETOCREATEDIR,
  60. ERROR_UPGRADEDALREADY,
  61. ERROR_NUMTYPES
  62. };
  63. // Upgrade Modes (THIS IS SAVED OFF - PLEASE DO NOT MODIFY THE ORDER OF THE ENUM!)
  64. enum upgrademode_e
  65. {
  66. UPGRADEMODE_UNKNOWN,
  67. UPGRADEMODE_127_128, // Version 1.27 to 1.28
  68. UPGRADEMODE_NUMMODES
  69. };
  70. // Helper class for managing the parameter list
  71. struct param_s
  72. {
  73. param_s()
  74. {
  75. name = NULL;
  76. value = NULL;
  77. }
  78. epi::string_c *name;
  79. epi::string_c *value;
  80. };
  81. class paramlist_c : public epi::array_c
  82. {
  83. public:
  84. paramlist_c() : epi::array_c(sizeof(param_s))
  85. {
  86. }
  87. ~paramlist_c()
  88. {
  89. Clear();
  90. }
  91. private:
  92. void CleanupObject(void *obj)
  93. {
  94. param_s *p = (param_s*)obj;
  95. if (p->name) { delete p->name; }
  96. if (p->value) { delete p->value; }
  97. }
  98. public:
  99. //
  100. // Find the parameter by given key
  101. //
  102. int FindParamByKey(char *k)
  103. {
  104. if (!k)
  105. return -1;
  106. epi::array_iterator_c it;
  107. int i;
  108. param_s* p;
  109. i = 0;
  110. it = GetBaseIterator();
  111. while (it.IsValid())
  112. {
  113. p = (param_s*)((void*)it);
  114. if (!p->name->Compare(k))
  115. {
  116. return i; // Got a matching key - return the index
  117. }
  118. i++;
  119. it++;
  120. }
  121. return -1;
  122. }
  123. //
  124. // GetEntry
  125. //
  126. param_s* GetEntry(int idx)
  127. {
  128. return (param_s*)FetchObject(idx);
  129. }
  130. //
  131. // GetSize()
  132. //
  133. int GetSize()
  134. {
  135. return array_entries;
  136. }
  137. //
  138. // Load parameter list for std main() arguments
  139. //
  140. bool Load(int argc, char **argv)
  141. {
  142. bool read;
  143. param_s tmp_param;
  144. epi::string_c s;
  145. int i;
  146. i=0;
  147. while(i<argc)
  148. {
  149. read = false;
  150. tmp_param.name = NULL;
  151. tmp_param.value = NULL;
  152. // Check for key value in the form : --<key>=<value>
  153. s = argv[i];
  154. if (s.GetLength() > 2 && s.GetAt(0) == '-' && s.GetAt(1) == '-')
  155. {
  156. int pos = s.Find('=');
  157. if (pos>3)
  158. {
  159. epi::string_c s2;
  160. s2 = s;
  161. s.RemoveRight(s.GetLength()-pos);
  162. s2.RemoveLeft(pos+1);
  163. tmp_param.name = new epi::string_c(s);
  164. tmp_param.value = new epi::string_c(s2);
  165. if (tmp_param.name == NULL || tmp_param.value == NULL)
  166. {
  167. if (tmp_param.name)
  168. delete tmp_param.name;
  169. return false;
  170. }
  171. read = true;
  172. }
  173. }
  174. // if we haven't read in the value yet, take the whole thing as a parameter
  175. if (!read)
  176. {
  177. tmp_param.name = new epi::string_c(s);
  178. if (!tmp_param.name)
  179. return false;
  180. }
  181. // Add to self..
  182. if (InsertObject(&tmp_param)<0)
  183. return false;
  184. i++;
  185. }
  186. return true;
  187. }
  188. };
  189. class version_file_c
  190. {
  191. public:
  192. version_file_c() {};
  193. ~version_file_c() {};
  194. private:
  195. u32_t version;
  196. u32_t umode;
  197. public:
  198. u32_t GetVersion() { return version; }
  199. u32_t GetUpgradeMode() { return umode; }
  200. void SetVersion(u32_t _version) { version = _version; }
  201. void SetUpgradeMode(u32_t _umode) { umode = _umode; }
  202. bool Read(const char *name)
  203. {
  204. epi::crc32_c crc1;
  205. epi::file_c *f;
  206. u32_t tmp_version;
  207. u32_t tmp_umode;
  208. u32_t tmp_crcval;
  209. f = epi::the_filesystem->Open(name,
  210. epi::file_c::ACCESS_READ | epi::file_c::ACCESS_BINARY);
  211. if (!f)
  212. return false;
  213. f->Read32BitInt(&tmp_version);
  214. tmp_version = EPI_LE_U32(tmp_version);
  215. f->Read32BitInt(&tmp_umode);
  216. tmp_umode = EPI_LE_U32(tmp_umode);
  217. f->Read32BitInt(&tmp_crcval);
  218. tmp_crcval = EPI_LE_U32(tmp_crcval);
  219. crc1 += tmp_version;
  220. crc1 += tmp_umode;
  221. if (crc1.crc != tmp_crcval)
  222. return false;
  223. version = tmp_version;
  224. umode = tmp_umode;
  225. return true;
  226. };
  227. bool Write(const char *name)
  228. {
  229. epi::file_c *f;
  230. bool ok;
  231. f = epi::the_filesystem->Open(name,
  232. epi::file_c::ACCESS_WRITE | epi::file_c::ACCESS_BINARY);
  233. if (!f)
  234. return false;
  235. ok = true;
  236. try
  237. {
  238. epi::crc32_c crc1;
  239. u32_t tmp;
  240. crc1 += version;
  241. crc1 += umode;
  242. tmp = EPI_LE_U32(version);
  243. if (!f->Write32BitInt(&tmp))
  244. {
  245. epi::error_c err(ERROR_INTERNAL);
  246. throw err;
  247. }
  248. tmp = EPI_LE_U32(umode);
  249. if (!f->Write32BitInt(&tmp))
  250. {
  251. epi::error_c err(ERROR_INTERNAL);
  252. throw err;
  253. }
  254. tmp = EPI_LE_U32(crc1.crc);
  255. if (!f->Write32BitInt(&tmp))
  256. {
  257. epi::error_c err(ERROR_INTERNAL);
  258. throw err;
  259. }
  260. epi::the_filesystem->Close(f);
  261. }
  262. catch (epi::error_c)
  263. {
  264. epi::the_filesystem->Close(f);
  265. epi::the_filesystem->Delete(name);
  266. ok = false;
  267. }
  268. return ok;
  269. };
  270. };
  271. // ============================= PROTOTYPES =============================
  272. void Pause(void);
  273. // =========================== INIT/SHUTDOWN ============================
  274. #ifdef WIN32
  275. epi::string_c *oldworkingdir = NULL;
  276. #endif
  277. //
  278. // Init
  279. //
  280. bool Init(char **argv)
  281. {
  282. if (!epi::Init())
  283. return false;
  284. #ifdef WIN32
  285. // Windows users will most likely double click, so we assume that the
  286. // tool is located in the edge directory and that this is our current
  287. // directory. We do restore the directory afterwards since our win32
  288. // user may be a commandline junkie.
  289. epi::string_c *workingdir = NULL;
  290. char p[MAX_PATH];
  291. int idx;
  292. try
  293. {
  294. if (!epi::the_filesystem->GetCurrDir(p, MAX_PATH))
  295. return false;
  296. oldworkingdir = new epi::string_c(p);
  297. if (!oldworkingdir)
  298. {
  299. epi::error_c err(ERROR_INTERNAL);
  300. throw err;
  301. }
  302. workingdir = new epi::string_c(argv[0]);
  303. if (!workingdir)
  304. {
  305. epi::error_c err(ERROR_INTERNAL);
  306. throw err;
  307. }
  308. idx = workingdir->ReverseFind(DIRSEPARATOR);
  309. if (idx<0)
  310. {
  311. epi::error_c err(ERROR_INTERNAL);
  312. throw err;
  313. }
  314. workingdir->RemoveRight(workingdir->GetLength()-idx);
  315. if (!epi::the_filesystem->SetCurrDir(*workingdir))
  316. {
  317. epi::error_c err(ERROR_INTERNAL);
  318. throw err;
  319. }
  320. delete workingdir;
  321. workingdir = NULL;
  322. }
  323. catch (epi::error_c)
  324. {
  325. delete oldworkingdir;
  326. oldworkingdir = NULL;
  327. delete workingdir;
  328. workingdir = NULL;
  329. return false;
  330. }
  331. #endif
  332. return true;
  333. }
  334. //
  335. // Shutdown
  336. //
  337. void Shutdown(void)
  338. {
  339. #ifdef WIN32
  340. if (oldworkingdir)
  341. epi::the_filesystem->SetCurrDir(*oldworkingdir);
  342. Pause();
  343. #endif
  344. epi::Shutdown();
  345. return;
  346. }
  347. // ============================= MISC UTIL ==============================
  348. //
  349. // ReportInternalError
  350. //
  351. void ReportInternalError(const char *err)
  352. {
  353. std::cerr << "===========================================" << '\n';
  354. std::cerr << " Upgrader tool has failed with an internal " << '\n';
  355. std::cerr << " error. Should the problem continue than " << '\n';
  356. std::cerr << " please post a bug on the EDGE website at " << '\n';
  357. std::cerr << " http://edge.sourceforge.net. " << '\n';
  358. if (err != NULL)
  359. {
  360. std::cerr << '\n';
  361. std::cerr << " Error Description:" << '\n';
  362. std::cerr << " " << err << '\n';
  363. }
  364. std::cerr << "===========================================";
  365. }
  366. //
  367. // ReportNothingTodo
  368. //
  369. void ReportNothingToDo(void)
  370. {
  371. std::cout << "Nothing to do!" << "\n";
  372. }
  373. //
  374. // ReportUserError
  375. //
  376. void ReportUserError(const char *err)
  377. {
  378. // No error specified here is an internal error
  379. if (!err)
  380. {
  381. ReportInternalError(NULL);
  382. return;
  383. }
  384. std::cerr << "Unable to complete upgrade process due to: " << '\n';
  385. std::cerr << " " << err << "\n";
  386. return;
  387. }
  388. //
  389. // Usage
  390. //
  391. void Usage(void)
  392. {
  393. std::cout << "upgrader [OPTION]" << '\n';
  394. std::cout << "Upgrade tool for the Enhanced Doom Gaming Engine (EDGE)." << '\n';
  395. std::cout << '\n';
  396. std::cout << KEY_CFGDIR << "=CFGPATH\twhere CFGPATH is a path to the EDGE config files" << '\n';
  397. std::cout << KEY_EDGEDIR << "=BINPATH\twhere BINPATH is a path to the EDGE binaries" << '\n';
  398. std::cout << KEY_HELP << "\t\t\tdisplay this help and exit" << '\n';
  399. std::cout << '\n';
  400. std::cout << "CFGPATH will default to the home directory should one exist. BINPATH will" << '\n';
  401. std::cout << "default to ";
  402. #ifndef WIN32
  403. std::cout << "the current directory." << "\n";
  404. #else
  405. std::cout << "the directory in which the upgrade tool is located." << "\n";
  406. #endif
  407. std::cout << '\n';
  408. std::cout << "Report bugs at " << EDGE_BUG_ADDR << '\n';
  409. }
  410. //
  411. // VersionStream
  412. //
  413. bool VersionStream(const char *cfgdir, bool update, version_file_c& vf)
  414. {
  415. epi::string_c s;
  416. s = cfgdir;
  417. if (s.GetLastChar() != DIRSEPARATOR)
  418. s += DIRSEPARATOR;
  419. s += VERSION_FILE;
  420. // Are we reading one in...?
  421. if (!update)
  422. return vf.Read(s);
  423. // Must be writing one out...
  424. return vf.Write(s);
  425. }
  426. //
  427. // Pause
  428. //
  429. // Emulates the DOS pause functionality
  430. //
  431. void Pause(void)
  432. {
  433. char c;
  434. std::cout << '\n' << "Press return to continue." << '\n';
  435. std::cin.sync();
  436. std::cin.read(&c,1);
  437. }
  438. // ========================= PARAMETER HANDLING =========================
  439. //
  440. // CopyConfigDirectory
  441. //
  442. void CopyConfigDirectory(epi::string_c *destdir, epi::string_c *srcdir) throw (epi::error_c)
  443. {
  444. epi::filesystem_dir_c fsd;
  445. epi::string_c dest, src;
  446. version_file_c vf;
  447. int len;
  448. if (!destdir || !srcdir)
  449. {
  450. epi::error_c err(ERROR_INTERNAL, "Bad Params in CopyConfigDirectory()");
  451. throw err;
  452. }
  453. if (!epi::the_filesystem->IsDir(*srcdir))
  454. {
  455. epi::error_c err(ERROR_NOEDGEDIR, *srcdir);
  456. throw err;
  457. }
  458. // Add the directory seperator if we need one
  459. src = *srcdir;
  460. dest = *destdir;
  461. if (!epi::the_filesystem->IsDir(dest))
  462. {
  463. if (!epi::the_filesystem->MakeDir(dest))
  464. {
  465. epi::error_c err(ERROR_UNABLETOCREATEDIR, dest);
  466. throw err;
  467. }
  468. }
  469. else
  470. {
  471. //
  472. // Attempt to load a version file; if it succeeds than
  473. // we've done a upgrade already. At this point we know
  474. // this the only version and therefore no action is
  475. // needed.
  476. //
  477. if (VersionStream(*destdir, false, vf))
  478. {
  479. epi::error_c err(ERROR_UPGRADEDALREADY, dest);
  480. throw err;
  481. }
  482. }
  483. if (src.GetLastChar() != DIRSEPARATOR)
  484. src += DIRSEPARATOR;
  485. if (dest.GetLastChar() != DIRSEPARATOR)
  486. dest += DIRSEPARATOR;
  487. src += EDGE_CFG_NAME;
  488. dest += EDGE_CFG_NAME;
  489. if (!epi::the_filesystem->Copy(dest, src))
  490. {
  491. epi::error_c err(ERROR_GENERICUSER, "Config file copy failed");
  492. throw err;
  493. }
  494. // Strip the config name from the src & dest strings
  495. len = (int)strlen(EDGE_CFG_NAME);
  496. src.RemoveRight(len);
  497. dest.RemoveRight(len);
  498. // Add the savegame sub dir
  499. src += EDGE_SGSUBDIR;
  500. dest += EDGE_SGSUBDIR;
  501. if (epi::the_filesystem->ReadDir(&fsd, src, EDGE_SG_MASK))
  502. {
  503. epi::string_c dest_file, src_file;
  504. int i, max;
  505. if (!epi::the_filesystem->IsDir(dest))
  506. {
  507. if (!epi::the_filesystem->MakeDir(dest))
  508. {
  509. epi::error_c err(ERROR_UNABLETOCREATEDIR, dest);
  510. throw err;
  511. }
  512. }
  513. dest_file = dest;
  514. dest_file += DIRSEPARATOR;
  515. src_file = src;
  516. src_file += DIRSEPARATOR;
  517. max = fsd.GetSize();
  518. for (i=0; i<max; i++)
  519. {
  520. // Add the filename
  521. dest_file += fsd[i]->name->GetString();
  522. src_file += fsd[i]->name->GetString();
  523. // Attempt the copy
  524. if (!epi::the_filesystem->Copy(dest_file, src_file))
  525. {
  526. epi::error_c err(ERROR_GENERICUSER, "Savegame file copy failed");
  527. throw err;
  528. }
  529. // Strip out the filename
  530. len = fsd[i]->name->GetLength();
  531. dest_file.RemoveRight(len);
  532. src_file.RemoveRight(len);
  533. }
  534. }
  535. // Update the version data
  536. vf.SetUpgradeMode(UPGRADEMODE_127_128);
  537. vf.SetVersion(VERSION_ID);
  538. if (!VersionStream(*destdir, true, vf))
  539. {
  540. epi::error_c err(ERROR_GENERICUSER, "Write the version data file failed");
  541. throw err;
  542. }
  543. return;
  544. }
  545. //
  546. // GetConfigDirectory
  547. //
  548. // Returns:
  549. // >0 - Internal Error
  550. // 0 - OK
  551. // <0 - Config directory could not be determined
  552. //
  553. int GetConfigDirectory(paramlist_c *pl, epi::string_c &cfgdir)
  554. {
  555. const char *s;
  556. int idx;
  557. if (!pl)
  558. return 1; // Internal Error
  559. cfgdir.Empty();
  560. // Check for a parameter setting the value
  561. idx = pl->FindParamByKey(KEY_CFGDIR);
  562. if (idx >= 0)
  563. {
  564. if (pl->GetEntry(idx)->value)
  565. cfgdir = pl->GetEntry(idx)->value->GetString();
  566. }
  567. if (!cfgdir.GetLength())
  568. {
  569. s = getenv("HOME");
  570. if (!s)
  571. return -1;
  572. cfgdir = s;
  573. if (cfgdir.GetLastChar() != DIRSEPARATOR)
  574. cfgdir += DIRSEPARATOR;
  575. cfgdir += EDGE_CFG_SUBDIR;
  576. }
  577. return 0;
  578. }
  579. //
  580. // GetEdgeDirectory
  581. //
  582. // Returns:
  583. // >0 - Internal Error
  584. // 0 - OK
  585. //
  586. int GetEdgeDirectory(paramlist_c *pl, epi::string_c &edgedir)
  587. {
  588. const char *s;
  589. int idx;
  590. if (!pl)
  591. return 1; // Internal Error
  592. // Check for a parameter setting the value
  593. idx = pl->FindParamByKey(KEY_EDGEDIR);
  594. if (idx >= 0)
  595. s = pl->GetEntry(idx)->value->GetString();
  596. else
  597. s = NULL;
  598. if (s)
  599. {
  600. edgedir = s;
  601. }
  602. else
  603. {
  604. edgedir = ".";
  605. edgedir += DIRSEPARATOR;
  606. }
  607. return 0;
  608. }
  609. //
  610. // GetUpgradeMode
  611. //
  612. // Returns:
  613. // >0 - Internal Error
  614. // 0 - OK
  615. //
  616. int GetUpgradeMode(paramlist_c *pl, int &mode)
  617. {
  618. if (!pl)
  619. return 1;
  620. // TODO: Logic behind this!
  621. mode = UPGRADEMODE_UNKNOWN;
  622. return 0;
  623. }
  624. // =============================== MAIN =================================
  625. //
  626. // DogsBollox
  627. //
  628. void DogsBollox(paramlist_c *pl) throw (epi::error_c)
  629. {
  630. int mode;
  631. if (pl->FindParamByKey(KEY_HELP) > 0)
  632. {
  633. epi::error_c err(ERROR_NEEDHELP, NULL);
  634. throw err;
  635. }
  636. if (GetUpgradeMode(pl, mode))
  637. {
  638. epi::error_c err(ERROR_INTERNAL, "Blank parameter list given for GetUpgradeMode()");
  639. throw err;
  640. }
  641. if (mode == UPGRADEMODE_UNKNOWN) { mode = UPGRADEMODE_127_128; } // default mode
  642. switch (mode)
  643. {
  644. case UPGRADEMODE_127_128:
  645. {
  646. epi::string_c cfgdir;
  647. epi::string_c edgedir;
  648. int error;
  649. // Get the config directory
  650. error = GetConfigDirectory(pl, cfgdir);
  651. if (error != 0)
  652. {
  653. if (error < 0)
  654. {
  655. epi::error_c err(ERROR_NOCONFIGDIR);
  656. throw err;
  657. }
  658. else
  659. {
  660. epi::error_c err(ERROR_INTERNAL);
  661. throw err;
  662. }
  663. }
  664. // Get the edge directory
  665. if (GetEdgeDirectory(pl, edgedir) != 0)
  666. {
  667. epi::error_c err(ERROR_NOEDGEDIR);
  668. throw err;
  669. }
  670. CopyConfigDirectory(&cfgdir, &edgedir);
  671. break;
  672. }
  673. default:
  674. break;
  675. }
  676. return;
  677. }
  678. //
  679. // main
  680. //
  681. // Life starts here
  682. //
  683. int main(int argc, char *argv[])
  684. {
  685. paramlist_c paramlist;
  686. try
  687. {
  688. if (!Init(argv))
  689. {
  690. epi::error_c err(ERROR_INTERNAL, "Failed to initialise");
  691. throw err;
  692. }
  693. if (!paramlist.Load(argc, argv))
  694. {
  695. epi::error_c err(ERROR_INTERNAL, "Couldn't load the parameter list");
  696. throw err;
  697. }
  698. DogsBollox(&paramlist);
  699. std::cout << "Upgrade completed OK!" << "\n";
  700. }
  701. catch (epi::error_c err)
  702. {
  703. if (!err.IsEPIGenerated())
  704. {
  705. switch(err.GetCode())
  706. {
  707. case ERROR_GENERICUSER:
  708. ReportUserError(err.GetInfo());
  709. break;
  710. case ERROR_INTERNAL:
  711. ReportInternalError(err.GetInfo());
  712. break;
  713. case ERROR_NEEDHELP:
  714. Usage();
  715. break;
  716. case ERROR_NOCONFIGDIR:
  717. ReportNothingToDo();
  718. break;
  719. case ERROR_NOEDGEDIR:
  720. {
  721. epi::string_c s;
  722. s = "Unable to find the specified edge directory";
  723. if (err.GetInfo())
  724. {
  725. s += ": ";
  726. s += err.GetInfo();
  727. }
  728. ReportUserError(s);
  729. break;
  730. }
  731. case ERROR_UNABLETOCREATEDIR:
  732. {
  733. epi::string_c s;
  734. s = "Unable to create directory";
  735. if (err.GetInfo())
  736. {
  737. s += ": ";
  738. s += err.GetInfo();
  739. }
  740. ReportUserError(s);
  741. break;
  742. }
  743. case ERROR_UPGRADEDALREADY:
  744. ReportNothingToDo();
  745. break;
  746. default:
  747. ReportInternalError(NULL);
  748. break;
  749. }
  750. }
  751. }
  752. Shutdown();
  753. return 0;
  754. }