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.5KB

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