Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Menu.cpp 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*!
  2. * \file src/Menu.cpp
  3. * \brief Menu 'overlay'
  4. *
  5. * \author xythobuz
  6. */
  7. #include <cctype>
  8. #include "global.h"
  9. #include "Console.h"
  10. #include "OpenRaider.h"
  11. #include "utils/strings.h"
  12. #include "TombRaider.h"
  13. #include "Window.h"
  14. #include "Menu.h"
  15. #if defined(HAVE_DIRENT_H) && defined(HAVE_OPENDIR) && defined(HAVE_READDIR_R) && defined(HAVE_CLOSEDIR) && defined(HAVE_DT_DIR)
  16. #include <dirent.h>
  17. #define USE_DIRENT
  18. #elif defined (_WIN32)
  19. #include <windows.h>
  20. #define USE_FINDFILE
  21. #else
  22. #error No support for recursive folder traversal
  23. #endif
  24. Menu::Menu() {
  25. mVisible = false;
  26. mCursor = 0;
  27. mMin = 0;
  28. mainText.text = bufferString(VERSION);
  29. mainText.color[0] = BLUE[0];
  30. mainText.color[1] = BLUE[1];
  31. mainText.color[2] = BLUE[2];
  32. mainText.color[3] = BLUE[3];
  33. mainText.scale = 1.2f;
  34. mainText.y = 10;
  35. mainText.w = 0;
  36. mainText.h = 0;
  37. mMapListFilled = false;
  38. mFirstPass = false;
  39. }
  40. Menu::~Menu() {
  41. delete [] mainText.text;
  42. mainText.text = NULL;
  43. while (mMapList.size() > 0) {
  44. delete [] mMapList.back();
  45. mMapList.pop_back();
  46. }
  47. }
  48. void Menu::setVisible(bool visible) {
  49. mVisible = visible;
  50. }
  51. bool Menu::isVisible() {
  52. return mVisible;
  53. }
  54. void Menu::loadPakFolderRecursive(const char *dir) {
  55. assert(dir != NULL);
  56. assert(dir[0] != '\0');
  57. #ifdef USE_DIRENT
  58. struct dirent entry;
  59. struct dirent *ep = NULL;
  60. DIR *pakDir;
  61. pakDir = opendir(dir);
  62. if (pakDir != NULL) {
  63. readdir_r(pakDir, &entry, &ep);
  64. while (ep != NULL) {
  65. if (ep->d_type == DT_DIR) {
  66. if ((strcmp(".", ep->d_name) != 0)
  67. && (strcmp("..", ep->d_name) != 0)) {
  68. char *tmp = bufferString("%s%s", dir, ep->d_name);
  69. char *next = fullPath(tmp, '/');
  70. loadPakFolderRecursive(next);
  71. delete next;
  72. delete tmp;
  73. }
  74. } else {
  75. char *fullPathMap = bufferString("%s%s", dir, ep->d_name);
  76. char *lowerPath = bufferString("%s", fullPathMap);
  77. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  78. // Check for valid extension
  79. if (stringEndsWith(lowerPath, ".phd")
  80. || stringEndsWith(lowerPath, ".tr2")
  81. || stringEndsWith(lowerPath, ".tr4")
  82. || stringEndsWith(lowerPath, ".trc")) {
  83. int error = TombRaider::checkMime(fullPathMap);
  84. if (error == 0) {
  85. // Just load relative filename
  86. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(getOpenRaider().mPakDir) + 1)));
  87. } else {
  88. getConsole().print("Error: pak file '%s' %s",
  89. fullPathMap, (error == -1) ? "not found" : "invalid");
  90. }
  91. }
  92. delete [] lowerPath;
  93. delete [] fullPathMap;
  94. }
  95. readdir_r(pakDir, &entry, &ep);
  96. }
  97. closedir(pakDir);
  98. } else {
  99. getConsole().print("Could not open PAK dir %s!", dir);
  100. }
  101. #elif defined(USE_FINDFILE)
  102. std::vector<char *> list;
  103. list.push_back(bufferString("%s", dir));
  104. do {
  105. WIN32_FIND_DATA fd;
  106. char *tmp = bufferString("%s\\*", list.at(0));
  107. HANDLE hFind = FindFirstFile(tmp, &fd);
  108. do {
  109. if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  110. list.push_back(bufferString("%s\\%s", list.at(0), fd.cFileName));
  111. } else {
  112. char *fullPathMap = bufferString("%s\\%s", list.at(0), fd.cFileName);
  113. char *lowerPath = bufferString("%s", fullPathMap);
  114. for (char *p = lowerPath; *p; ++p) *p = (char)tolower(*p);
  115. // Check for valid extension
  116. if (stringEndsWith(lowerPath, ".phd")
  117. || stringEndsWith(lowerPath, ".tr2")
  118. || stringEndsWith(lowerPath, ".tr4")
  119. || stringEndsWith(lowerPath, ".trc")) {
  120. int error = TombRaider::checkMime(fullPathMap);
  121. if (error == 0) {
  122. // Just load relative filename
  123. mMapList.push_back(bufferString("%s", (fullPathMap + strlen(getOpenRaider().mPakDir) + 1)));
  124. } else {
  125. getConsole().print("Error: pak file '%s' %s",
  126. fullPathMap, (error == -1) ? "not found" : "invalid");
  127. }
  128. }
  129. delete [] lowerPath;
  130. delete [] fullPathMap;
  131. }
  132. } while (FindNextFile(hFind, &fd) != 0);
  133. FindClose(hFind);
  134. delete [] tmp;
  135. delete [] list.at(0);
  136. list.erase(list.begin());
  137. } while (list.size() > 0);
  138. #endif
  139. }
  140. void Menu::fillMapList() {
  141. char *tmp = fullPath(getOpenRaider().mPakDir, '/');
  142. loadPakFolderRecursive(tmp);
  143. delete [] tmp;
  144. mMapListFilled = true;
  145. }
  146. void Menu::displayMapList() {
  147. // Estimate displayable number of items
  148. int items = (getWindow().getHeight() - 110) / 25;
  149. // Select which part of the list to show
  150. int min, max;
  151. if (((int)mCursor - (items / 2)) > 0)
  152. min = mCursor - (items / 2);
  153. else
  154. min = 0;
  155. if ((mCursor + (items / 2)) < mMapList.size())
  156. max = mCursor + (items / 2);
  157. else
  158. max = mMapList.size();
  159. while ((max - min) < items) {
  160. if (min > 0)
  161. min--;
  162. else if (max < ((int)mMapList.size()))
  163. max++;
  164. else
  165. break;
  166. }
  167. mMin = min;
  168. for (int i = 0; i < (max - min); i++) {
  169. char *map = mMapList[i + min];
  170. if ((i + min) == (int)mCursor)
  171. getFont().drawText(25, 100 + (25 * i), 0.75f, RED, "%s", map);
  172. else
  173. getFont().drawText(25, 100 + (25 * i), 0.75f, BLUE, "%s", map);
  174. }
  175. }
  176. void Menu::display() {
  177. if (mVisible) {
  178. // Draw half-transparent *overlay*
  179. glColor4f(0.0f, 0.0f, 0.0f, 0.75f);
  180. glDisable(GL_TEXTURE_2D);
  181. glRecti(0, 0, getWindow().getWidth(), getWindow().getHeight());
  182. glEnable(GL_TEXTURE_2D);
  183. // Draw heading text, using FontString so we can get the
  184. // width of the drawn text to center it
  185. mainText.x = (getWindow().getWidth() / 2) - (mainText.w / 2);
  186. getFont().writeString(mainText);
  187. if (!mMapListFilled) {
  188. getFont().drawText(25, (getWindow().getHeight() / 2) - 20, 0.75f, BLUE, "Generating map list...");
  189. } else {
  190. if (mMapList.size() == 0) {
  191. getFont().drawText(25, (getWindow().getHeight() / 2) - 20, 0.75f, RED, "No maps found! See README.md");
  192. } else {
  193. // draw *play button* above list
  194. glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  195. glDisable(GL_TEXTURE_2D);
  196. glRecti(25, 25, 100, 75);
  197. glEnable(GL_TEXTURE_2D);
  198. getFont().drawText(40, 35, 0.75f, BLACK, "Play");
  199. displayMapList();
  200. }
  201. }
  202. // Fill map list after first render pass,
  203. // so menu *loading screen* is visible
  204. if (mFirstPass) {
  205. if (!mMapListFilled) {
  206. fillMapList();
  207. }
  208. } else {
  209. mFirstPass = true;
  210. }
  211. }
  212. }
  213. void Menu::play() {
  214. char *tmp = bufferString("load %s", mMapList[mCursor]);
  215. if (getOpenRaider().command(tmp) == 0) {
  216. setVisible(false);
  217. } else {
  218. //! \todo Display something if an error occurs
  219. }
  220. delete [] tmp;
  221. }
  222. void Menu::handleKeyboard(KeyboardButton key, bool pressed) {
  223. if (!pressed)
  224. return;
  225. if (key == upKey) {
  226. if (mCursor > 0)
  227. mCursor--;
  228. else
  229. mCursor = mMapList.size() - 1;
  230. } else if (key == downKey) {
  231. if (mCursor < (mMapList.size() - 1))
  232. mCursor++;
  233. else
  234. mCursor = 0;
  235. } else if (key == rightKey) {
  236. int i = 10;
  237. if (mCursor > (mMapList.size() - 11))
  238. i = mMapList.size() - 1 - mCursor;
  239. while (i-- > 0)
  240. handleKeyboard(downKey, true);
  241. } else if (key == leftKey) {
  242. int i = 10;
  243. if (mCursor < 10)
  244. i = mCursor;
  245. while (i-- > 0)
  246. handleKeyboard(upKey, true);
  247. } else if (key == enterKey) {
  248. play();
  249. }
  250. }
  251. void Menu::handleMouseClick(unsigned int x, unsigned int y, KeyboardButton button, bool released) {
  252. int items = (getWindow().getHeight() - 110) / 25;
  253. if (released || (button != leftmouseKey))
  254. return;
  255. if ((y >= 100) && (y <= (unsigned int)(100 + (25 * items)))) {
  256. y -= 100;
  257. mCursor = mMin + (y / 25);
  258. } else if ((y >= 25) && (y <= 100) && (x >= 25) && (x <= 125)) {
  259. // Play button
  260. play();
  261. }
  262. }