Open Source Tomb Raider Engine
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.

Menu.cpp 8.3KB

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