Selaa lähdekoodia

Menu using Folder API. WIP, does not work well!

[ci skip]
Thomas Buck 10 vuotta sitten
vanhempi
commit
4263f71122

+ 4
- 0
ChangeLog.md Näytä tiedosto

@@ -2,6 +2,10 @@
2 2
 
3 3
 ## OpenRaider (0.1.3) xythobuz <xythobuz@xythobuz.de>
4 4
 
5
+    [ 20140808 ]
6
+    * Added unit test for file-system utils
7
+    * Moving Menu to Folder/File API
8
+
5 9
     [ 20140807 ]
6 10
     * Script parser successfully loading level scripts
7 11
     * Can also read TR3 script now

+ 29
- 0
include/Exception.h Näytä tiedosto

@@ -0,0 +1,29 @@
1
+/*!
2
+ * \file include/Exception.h
3
+ * \brief Custom global Exception
4
+ *
5
+ * \author xythobuz
6
+ */
7
+
8
+#ifndef _EXCEPTION_H_
9
+#define _EXCEPTION_H_
10
+
11
+#include <memory>
12
+#include <stdexcept>
13
+#include <string>
14
+
15
+class Exception : std::runtime_error {
16
+public:
17
+    Exception(const char *what);
18
+    Exception(const std::string &what);
19
+
20
+    static std::string getLastException();
21
+
22
+private:
23
+    static std::string lastException;
24
+
25
+    virtual void foo(); //!< We don't want to emit a vtable in every translation unit
26
+};
27
+
28
+#endif
29
+

+ 6
- 9
include/Menu.h Näytä tiedosto

@@ -8,7 +8,10 @@
8 8
 #ifndef _MENU_H_
9 9
 #define _MENU_H_
10 10
 
11
+#include <memory>
12
+
11 13
 #include "Font.h"
14
+#include "utils/Folder.h"
12 15
 
13 16
 /*!
14 17
  * \brief Menu 'overlay'
@@ -26,6 +29,8 @@ public:
26 29
      */
27 30
     ~Menu();
28 31
 
32
+    int initialize();
33
+
29 34
     void setVisible(bool visible);
30 35
 
31 36
     bool isVisible();
@@ -38,12 +43,6 @@ public:
38 43
 
39 44
 private:
40 45
 
41
-    void loadPakFolderRecursive(const char *dir);
42
-
43
-    void fillMapList();
44
-
45
-    void displayMapList();
46
-
47 46
     void play();
48 47
 
49 48
     bool mVisible;
@@ -52,9 +51,7 @@ private:
52 51
 
53 52
     FontString mainText;
54 53
 
55
-    bool mMapListFilled;
56
-    bool mFirstPass;
57
-    std::vector<char *> mMapList;
54
+    Folder *mapFolder;
58 55
 };
59 56
 
60 57
 Menu &getMenu();

+ 1
- 1
include/TombRaider.h Näytä tiedosto

@@ -125,7 +125,7 @@ public:
125 125
      * \param filename file to check
126 126
      * \returns 0 if it is a TombRaider pak
127 127
      */
128
-    static int checkMime(char *filename);
128
+    static int checkMime(const char *filename);
129 129
 
130 130
     /*!
131 131
      * \brief Loads TombRaider 1-5 pak into memory

+ 2
- 4
include/utils/File.h Näytä tiedosto

@@ -8,7 +8,6 @@
8 8
 #ifndef _UTILS_FILE_H_
9 9
 #define _UTILS_FILE_H_
10 10
 
11
-#include <memory>
12 11
 #include <string>
13 12
 
14 13
 class Folder;
@@ -16,14 +15,13 @@ class Folder;
16 15
 class File {
17 16
 public:
18 17
     File(std::string file);
19
-    ~File();
20 18
 
21
-    std::shared_ptr<Folder> getParent();
19
+    std::string &getName();
20
+    std::string &getPath();
22 21
 
23 22
 private:
24 23
     std::string name;
25 24
     std::string path;
26
-    std::weak_ptr<Folder> parent;
27 25
 };
28 26
 
29 27
 #endif

+ 31
- 7
include/utils/Folder.h Näytä tiedosto

@@ -8,21 +8,45 @@
8 8
 #ifndef _UTILS_FOLDER_H_
9 9
 #define _UTILS_FOLDER_H_
10 10
 
11
+#include "Exception.h"
12
+#include "utils/File.h"
13
+
14
+#include <functional>
11 15
 #include <memory>
12 16
 #include <string>
13 17
 #include <vector>
14 18
 
15
-class File;
16
-
17 19
 class Folder {
18 20
 public:
19
-    Folder(std::string folder);
20
-    ~Folder();
21
+    Folder(std::string folder, bool listDotFiles = false);
22
+
23
+    std::string &getName();
24
+    std::string &getPath();
25
+
26
+    unsigned long fileCount();
27
+    File &getFile(unsigned long i);
28
+
29
+    unsigned long folderCount();
30
+    Folder &getFolder(unsigned long i);
31
+
32
+    unsigned long countRecursiveItems();
33
+    void executeRemoveRecursiveItems(std::function<bool (File &f)> func);
34
+    std::string getRecursiveItemName(unsigned long i);
35
+    //File &getRecursiveItem(unsigned long i);
21 36
 
22 37
 private:
23
-    std::string name;
24
-    std::string path;
25
-    std::vector<std::shared_ptr<File>> files;
38
+
39
+    void createFolderItems();
40
+    int readFolderItems(std::vector<std::string> &foundFiles, std::vector<std::string> &foundFolders);
41
+
42
+    std::string name; //!< Only last part of path
43
+    std::string path; //!< Full path, with name and '/' at end
44
+
45
+    bool hasListed;
46
+    bool listDot;
47
+
48
+    std::vector<File> files;
49
+    std::vector<Folder> folders;
26 50
 };
27 51
 
28 52
 #endif

+ 1
- 0
src/CMakeLists.txt Näytä tiedosto

@@ -51,6 +51,7 @@ set (SRCS ${SRCS} "Camera.cpp")
51 51
 set (SRCS ${SRCS} "Command.cpp")
52 52
 set (SRCS ${SRCS} "Console.cpp")
53 53
 set (SRCS ${SRCS} "Entity.cpp")
54
+set (SRCS ${SRCS} "Exception.cpp")
54 55
 set (SRCS ${SRCS} "Font.cpp")
55 56
 set (SRCS ${SRCS} "FontManager.cpp")
56 57
 set (SRCS ${SRCS} "FontTRLE.cpp")

+ 26
- 0
src/Exception.cpp Näytä tiedosto

@@ -0,0 +1,26 @@
1
+/*!
2
+ * \file src/Exception.cpp
3
+ * \brief Custom global Exception
4
+ *
5
+ * \author xythobuz
6
+ */
7
+
8
+#include "global.h"
9
+#include "Exception.h"
10
+
11
+std::string Exception::lastException("No custom exception since start!");
12
+
13
+Exception::Exception(const char *what) : runtime_error(what) {
14
+    lastException = what;
15
+}
16
+
17
+Exception::Exception(const std::string &what) : runtime_error(what) {
18
+    lastException = what;
19
+}
20
+
21
+std::string Exception::getLastException() {
22
+    return lastException;
23
+}
24
+
25
+void Exception::foo() { }
26
+

+ 79
- 188
src/Menu.cpp Näytä tiedosto

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

+ 7
- 0
src/OpenRaider.cpp Näytä tiedosto

@@ -90,6 +90,13 @@ int OpenRaider::initialize() {
90 90
         return -6;
91 91
     }
92 92
 
93
+    // Initialize main menu
94
+    error = getMenu().initialize();
95
+    if (error != 0) {
96
+        printf("Could not initialize Menu (%d)!\n", error);
97
+        return -7;
98
+    }
99
+
93 100
 #ifdef DEBUG
94 101
     mFPS = true;
95 102
 #endif

+ 1
- 1
src/TombRaider.cpp Näytä tiedosto

@@ -366,7 +366,7 @@ unsigned char *TombRaider::Palette8()
366 366
 }
367 367
 
368 368
 
369
-int TombRaider::checkMime(char *filename) {
369
+int TombRaider::checkMime(const char *filename) {
370 370
     FILE *f;
371 371
     unsigned int version;
372 372
 

+ 39
- 0
src/main.cpp Näytä tiedosto

@@ -12,6 +12,7 @@
12 12
 #include "global.h"
13 13
 #include "Camera.h"
14 14
 #include "Console.h"
15
+#include "Exception.h"
15 16
 #include "FontManager.h"
16 17
 #include "Game.h"
17 18
 #include "Menu.h"
@@ -23,6 +24,8 @@
23 24
 #include "utils/strings.h"
24 25
 #include "utils/time.h"
25 26
 
27
+#ifndef UNIT_TEST
28
+
26 29
 #ifdef USING_AL
27 30
 #include "SoundAL.h"
28 31
 #else
@@ -362,3 +365,39 @@ KeyboardButton stringToKeyboardButton(const char *key) {
362 365
     return unknownKey;
363 366
 }
364 367
 
368
+#endif // UNIT_TEST
369
+
370
+#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS)
371
+#ifndef NDEBUG
372
+
373
+#include <exception>
374
+#include <execinfo.h>
375
+
376
+namespace {
377
+    extern std::terminate_handler oldTerminateHandler;
378
+
379
+    [[noreturn]] void terminateHandler() {
380
+        const unsigned int maxSize = 128;
381
+        void *callstack[maxSize];
382
+        int frames = backtrace(callstack, maxSize);
383
+        char **strs = backtrace_symbols(callstack, frames);
384
+
385
+        std::cout << std::endl;
386
+        for (int i = 0; i < frames; i++)
387
+            std::cout << strs[i] << std::endl;
388
+
389
+        delete [] strs;
390
+
391
+        std::cout << std::endl << "Last custom Exception:" << std::endl;
392
+        std::cout << "    " << Exception::getLastException() << std::endl << std::endl;
393
+
394
+        oldTerminateHandler();
395
+        abort();
396
+    }
397
+
398
+    std::terminate_handler oldTerminateHandler = std::set_terminate(terminateHandler);
399
+}
400
+
401
+#endif // NDEBUG
402
+#endif // HAVE_EXECINFO_H && HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
403
+

+ 11
- 1
src/utils/File.cpp Näytä tiedosto

@@ -5,15 +5,25 @@
5 5
  * \author xythobuz
6 6
  */
7 7
 
8
+#include <algorithm>
9
+
8 10
 #include "global.h"
9 11
 #include "utils/File.h"
10 12
 #include "utils/Folder.h"
11 13
 
12 14
 File::File(std::string file) {
15
+    path = file;
13 16
 
17
+    size_t pos = path.rfind('/', path.length() - 2);
18
+    name = path.substr(pos + 1);
19
+    std::transform(name.begin(), name.end(), name.begin(), ::tolower);
14 20
 }
15 21
 
16
-File::~File() {
22
+std::string &File::getName() {
23
+    return name;
24
+}
17 25
 
26
+std::string &File::getPath() {
27
+    return path;
18 28
 }
19 29
 

+ 237
- 2
src/utils/Folder.cpp Näytä tiedosto

@@ -5,15 +5,250 @@
5 5
  * \author xythobuz
6 6
  */
7 7
 
8
+#include <algorithm>
9
+#include <iostream>
10
+#include <sstream>
11
+
8 12
 #include "global.h"
13
+#include "utils/filesystem.h"
9 14
 #include "utils/File.h"
10 15
 #include "utils/Folder.h"
11 16
 
12
-Folder::Folder(std::string folder) {
17
+#if defined(HAVE_DIRENT_H) && defined(HAVE_OPENDIR) && defined(HAVE_READDIR_R) && defined(HAVE_CLOSEDIR) && defined(HAVE_DT_DIR)
18
+#include <dirent.h>
19
+#define USE_DIRENT
20
+#elif defined (_WIN32)
21
+#include <windows.h>
22
+#define USE_FINDFILE
23
+#else
24
+#error No support for recursive folder traversal
25
+#endif
26
+
27
+Folder::Folder(std::string folder, bool listDotFiles) {
28
+    if (((folder.length() == 0) || (folder.compare(".") == 0))
29
+#ifdef _WIN32
30
+            || ((folder.compare(1, 2, std::string(":\\")) != 0) && (folder.at(0) != '~'))
31
+#else
32
+            || ((folder.at(0) != '/') && (folder.at(0) != '~'))
33
+#endif
34
+            ) {
35
+        // Prepend current working directory
36
+        path = getCurrentWorkingDirectory();
37
+        if (folder.length() > 1)
38
+            path += folder.substr(1);
39
+    } else if (folder.at(0) == '~') {
40
+        // Prepend home directory
41
+        path = getHomeDirectory();
42
+        std::cout << "Home: " << path << std::endl;
43
+        if (folder.length() > 1)
44
+            path += folder.substr(1);
45
+    } else {
46
+        path = folder;
47
+    }
48
+
49
+    // Find all '\\' replace with '/'
50
+    size_t pos = path.find('\\');
51
+    while (pos != std::string::npos) {
52
+        path.at(pos) = '/';
53
+        pos = path.find('\\');
54
+    }
55
+
56
+    size_t last = path.rfind('/', path.length() - 2);
57
+    name = path.substr(last + 1);
58
+    if (name.back() == '/')
59
+        name.erase(name.length() - 1);
60
+
61
+    std::transform(name.begin(), name.end(), name.begin(), ::tolower);
62
+
63
+    if (path.back() != '/')
64
+        path.append(1, '/');
65
+
66
+    hasListed = false;
67
+    listDot = listDotFiles;
68
+}
69
+
70
+std::string &Folder::getName() {
71
+    return name;
72
+}
73
+
74
+std::string &Folder::getPath() {
75
+    return path;
76
+}
77
+
78
+unsigned long Folder::fileCount() {
79
+    createFolderItems();
80
+    return files.size();
81
+}
82
+
83
+File &Folder::getFile(unsigned long i) {
84
+    createFolderItems();
85
+    assert(i < files.size());
86
+    return files.at(i);
87
+}
88
+
89
+unsigned long Folder::folderCount() {
90
+    createFolderItems();
91
+    return folders.size();
92
+}
93
+
94
+Folder &Folder::getFolder(unsigned long i) {
95
+    createFolderItems();
96
+    assert(i < folders.size());
97
+    return folders.at(i);
98
+}
99
+
100
+unsigned long Folder::countRecursiveItems() {
101
+    unsigned long count = fileCount();
102
+
103
+    for (unsigned long i = 0; i < folderCount(); i++) {
104
+        count += getFolder(i).countRecursiveItems();
105
+    }
106
+
107
+    return count;
108
+}
109
+
110
+void Folder::executeRemoveRecursiveItems(std::function<bool (File &f)> func) {
111
+    for (unsigned long i = 0; i < fileCount(); i++) {
112
+        if (func(getFile(i))) {
113
+            files.erase(files.begin() + (long)i);
114
+        }
115
+    }
116
+
117
+    for (unsigned long i = 0; i < folderCount(); i++) {
118
+        getFolder(i).executeRemoveRecursiveItems(func);
119
+    }
120
+}
121
+
122
+std::string Folder::getRecursiveItemName(unsigned long i) {
123
+    assert(i < countRecursiveItems());
124
+    if (i < fileCount()) {
125
+        return getFile(i).getName();
126
+    } else {
127
+        for (unsigned long n = 0; n < folderCount(); n++) {
128
+            if ((i - fileCount()) < getFolder(n).countRecursiveItems()) {
129
+                return getFolder(n).getName() + '/'
130
+                    + getFolder(n).getRecursiveItemName(i - fileCount());
131
+            }
132
+        }
133
+    }
134
+
135
+    assert(false);
136
+    return "";
137
+}
138
+
139
+/*
140
+File &Folder::getRecursiveItem(unsigned long i) {
141
+    assert(i < countRecursiveItems());
142
+    if (i < fileCount()) {
143
+        return getFile(i);
144
+    } else {
145
+        for (unsigned long n = 0; n < folderCount(); n++) {
146
+            if ((i - fileCount()) < getFolder(n).countRecursiveItems()) {
147
+                return getFolder(n).getRecursiveItem(i - fileCount());
148
+            }
149
+        }
150
+    }
151
+
152
+    assert(false);
153
+    return files.at(0);
154
+}
155
+*/
156
+
157
+void Folder::createFolderItems() {
158
+    if (hasListed)
159
+        return;
160
+
161
+    std::vector<std::string> foundFiles, foundFolders;
162
+    if (readFolderItems(foundFiles, foundFolders) != 0)
163
+        throw Exception(std::string("Could not open folder ") + name);
164
+
165
+    if (!listDot) {
166
+        std::vector<std::string>::iterator it = foundFiles.begin();
167
+        while (it != foundFiles.end()) {
168
+            size_t pos = it->rfind('/', it->length() - 2);
169
+            if (it->at(pos + 1) == '.')
170
+                it = foundFiles.erase(it);
171
+            else
172
+                ++it;
173
+        }
13 174
 
175
+        it = foundFolders.begin();
176
+        while (it != foundFolders.end()) {
177
+            size_t pos = it->rfind('/', it->length() - 2);
178
+            if (it->at(pos + 1) == '.')
179
+                it = foundFolders.erase(it);
180
+            else
181
+                ++it;
182
+        }
183
+    }
184
+
185
+    for (unsigned long i = 0; i < foundFiles.size(); i++)
186
+        files.emplace_back(File(foundFiles.at(i)));
187
+
188
+    for (unsigned long i = 0; i < foundFolders.size(); i++)
189
+        folders.emplace_back(Folder(foundFolders.at(i)));
190
+
191
+    hasListed = true;
192
+}
193
+
194
+#ifdef USE_DIRENT
195
+
196
+int Folder::readFolderItems(std::vector<std::string> &foundFiles, std::vector<std::string> &foundFolders) {
197
+    struct dirent entry;
198
+    struct dirent *ep = nullptr;
199
+    DIR *pakDir;
200
+
201
+    pakDir = opendir(path.c_str());
202
+    if (pakDir != nullptr) {
203
+        readdir_r(pakDir, &entry, &ep);
204
+        while (ep != nullptr) {
205
+            if ((strcmp(".", ep->d_name) != 0)
206
+                     && (strcmp("..", ep->d_name) != 0)) {
207
+                std::string tmp(path);
208
+                if (tmp.back() != '/')
209
+                    tmp += '/';
210
+                tmp += ep->d_name;
211
+                if (ep->d_type == DT_DIR) {
212
+                    if (tmp.back() != '/')
213
+                        tmp += '/';
214
+                    foundFolders.push_back(std::string(tmp));
215
+                } else {
216
+                    foundFiles.push_back(std::string(tmp));
217
+                }
218
+            }
219
+            readdir_r(pakDir, &entry, &ep);
220
+        }
221
+        closedir(pakDir);
222
+    } else {
223
+        return 1;
224
+    }
225
+
226
+    return 0;
14 227
 }
15 228
 
16
-Folder::~Folder() {
229
+#elif defined(USE_FINDFILE)
230
+
231
+int Folder::readFolderItems(std::vector<std::string> &foundFiles, std::vector<std::string> &foundFolders) {
232
+    std::string tmp(path);
233
+    tmp += "/*";
17 234
 
235
+    WIN32_FIND_DATA fd;
236
+    HANDLE hFind = FindFirstFile(tmp.c_str(), &fd);
237
+    if (hFind == nullptr)
238
+        return 1;
239
+
240
+    do {
241
+        std::string s(path);
242
+        s = s + "/" + fd.cFileName;
243
+        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
244
+            foundFolders.push_back(s);
245
+        else
246
+            foundFiles.push_back(s);
247
+    } while (FindNextFile(hFind, &fd) != 0);
248
+
249
+    FindClose(hFind);
250
+    return 0;
18 251
 }
19 252
 
253
+#endif
254
+

+ 13
- 3
test/CMakeLists.txt Näytä tiedosto

@@ -7,19 +7,29 @@ add_custom_target (check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure)
7 7
 #################################################################
8 8
 
9 9
 add_executable (tester_binary EXCLUDE_FROM_ALL
10
-    "binary.cpp" "../src/utils/binary.cpp"
10
+    "binary.cpp" "../src/utils/binary.cpp" "../src/main.cpp"
11 11
 )
12 12
 add_dependencies (check tester_binary)
13 13
 add_test (NAME test_binary COMMAND tester_binary)
14 14
 
15 15
 #################################################################
16 16
 
17
+add_executable (tester_folder EXCLUDE_FROM_ALL
18
+    "Folder.cpp" "../src/utils/filesystem.cpp"
19
+    "../src/utils/Folder.cpp" "../src/utils/File.cpp"
20
+    "../src/Exception.cpp" "../src/main.cpp"
21
+)
22
+
23
+#add_dependencies (check tester_folder)
24
+#add_test (NAME test_folder COMMAND tester_folder)
25
+
26
+#################################################################
27
+
17 28
 add_executable (tester_script EXCLUDE_FROM_ALL
18
-    "Script.cpp" "../src/Script.cpp"
29
+    "Script.cpp" "../src/Script.cpp" "../src/main.cpp"
19 30
     "../src/utils/binary.cpp" "../src/utils/strings.cpp"
20 31
 )
21 32
 
22
-# Currently not running Script test on make check (for Travis)
23 33
 #add_dependencies (check tester_script)
24 34
 #add_test (NAME test_script COMMAND tester_script)
25 35
 

+ 27
- 0
test/Folder.cpp Näytä tiedosto

@@ -0,0 +1,27 @@
1
+/*!
2
+ * \file test/Folder.cpp
3
+ * \brief File system utils unit test
4
+ *
5
+ * \author xythobuz
6
+ */
7
+
8
+#include <iostream>
9
+
10
+#include "global.h"
11
+#include "utils/File.h"
12
+#include "utils/Folder.h"
13
+
14
+int main() {
15
+    Folder f(".");
16
+
17
+    std::cout << f.folderCount() << " folders:" << std::endl;
18
+    for (unsigned long i = 0; i < f.folderCount(); i++)
19
+        std::cout << "\t" << f.getFolder(i).getName() << std::endl;
20
+
21
+    std::cout << f.fileCount() << " files:" << std::endl;
22
+    for (unsigned long i = 0; i < f.fileCount(); i++)
23
+        std::cout << "\t" << f.getFile(i).getName() << std::endl;
24
+
25
+    return 0;
26
+}
27
+

Loading…
Peruuta
Tallenna