Skip to content

Commit e49720e

Browse files
Mischa Spiegelmockmilkdropper
andauthored
Preset subdirs (#385)
Scanning textures/presets dirs for textures and scanning preset dir for presets. Scanning is now done recursively, so presets and textures can be organized into subdirectories instead of needing to be flattened into a single directory. On POSIX systems makes use of [ftw](https://linux.die.net/man/3/ftw) which should be relatively efficient. Otherwise falls back to recursing with `dirent` (for windows). Probably should have made an autoconf check for `ftw` instead of doing `#ifdef WIN32`. * Scan subdirectories in presets directory * remove preset subdir config * Recursively scan for textures too, add c++-17 compatibility * Refactor directory scanning code so it's reused by texture loader and preset loader. Make cross-platform (maybe) * filescanner in makefile * extension filter for file loader * make extensions match up' * need string.h * Add FileScanner.cpp to win build (maybe) * scan all dirs * #ifndef #ifdef is def fun * bogus comment * bleh * bleh * itunes plugin with c++17 * EyeTunes needs to know about the FileScanner Co-authored-by: milkdropper.com <[email protected]> Co-authored-by: milkdropper <[email protected]>
1 parent e51b2c7 commit e49720e

27 files changed

+379
-330
lines changed

Makefile.am

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,15 @@ CLEANFILES=dist
1111
# aka /usr/local/share/projectM
1212
pm_data_dir = $(pkgdatadir)
1313
pm_font_dir = $(pm_data_dir)/fonts
14-
pm_presets_dir = $(pm_data_dir)/presets
1514

1615
# files to install
1716
pm_data__DATA = src/libprojectM/config.inp
1817
pm_font__DATA = fonts/Vera.ttf fonts/VeraMono.ttf
1918

2019
# find and install all preset files
2120
install-data-local:
22-
if ENABLE_PRESET_SUBDIRS
2321
find "$(PRESETSDIR)" -type d -exec $(MKDIR_P) "$(DESTDIR)/$(pm_data_dir)/{}" \;
2422
find "$(PRESETSDIR)" -type f -exec $(INSTALL_DATA) "{}" "$(DESTDIR)/$(pm_data_dir)/{}" \;
25-
else
26-
test -z $(DESTDIR)$(pkgdatadir) || $(MKDIR_P) $(DESTDIR)$(pm_presets_dir)
27-
find "$(PRESETSDIR)" -type f -print0 | LC_ALL=C sort -z | xargs -0 '-I{}' $(INSTALL_DATA) '{}' $(DESTDIR)$(pm_presets_dir)
28-
endif
2923

3024
# from https://stackoverflow.com/questions/30897170/ac-subst-does-not-expand-variable answer: https://stackoverflow.com/a/30960268
3125
# ptomato https://stackoverflow.com/users/172999/ptomato

configure.ac

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ AS_IF([test "x$enable_emscripten" = "xyes" || test "x$EMSCRIPTEN" = "xyes"], [
3232
enable_threading=no
3333
enable_gles=yes
3434
enable_sdl=yes
35-
enable_preset_subdirs=no
3635
], [
3736
dnl Running in a normal OS (not emscripten)
3837
AX_CHECK_GL
@@ -127,13 +126,6 @@ AC_ARG_ENABLE([gles],
127126
AC_DEFINE([USE_GLES], [1], [Define USE_GLES])
128127
])
129128

130-
AC_ARG_ENABLE([preset_subdirs],
131-
AS_HELP_STRING([--enable-preset-subdirs], [Organize presets into subdirectories.]),
132-
[], [enable_preset_subdirs=no])
133-
AS_IF([test "x$enable_preset_subdirs" = "xyes"], [
134-
AC_DEFINE([ENABLE_PRESET_SUBDIRS], [1], [Define ENABLE_PRESET_SUBDIRS])
135-
])
136-
137129
dnl LLVM
138130
dnl unfortuately AX_LLVM macro seems to be out of date, so we're going to rely on the user to make sure LLVM is installed correctly
139131
AC_ARG_ENABLE([llvm],
@@ -228,7 +220,6 @@ AM_CONDITIONAL([ENABLE_QT], [test "x$enable_qt" = "xyes"])
228220
AM_CONDITIONAL([ENABLE_JACK], [test "x$enable_jack" = "xyes"])
229221
AM_CONDITIONAL([ENABLE_PULSEAUDIO], [test "x$enable_pulseaudio" = "xyes"])
230222
AM_CONDITIONAL([ENABLE_EMSCRIPTEN], [test "x$enable_emscripten" = "xyes"])
231-
AM_CONDITIONAL([ENABLE_PRESET_SUBDIRS], [test "x$enable_preset_subdirs" = "xyes"])
232223

233224

234225
my_CFLAGS="-Wall -Wchar-subscripts -Wformat-security -Wpointer-arith -Wshadow -Wsign-compare -Wtype-limits"
@@ -277,5 +268,4 @@ Jack: ${enable_jack}
277268
OpenGLES: ${enable_gles}
278269
Emscripten: ${enable_emscripten}
279270
llvm: ${enable_llvm}
280-
Preset subdirs: ${enable_preset_subdirs}
281271
])

msvc/projectM.vcxproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
<ClCompile Include="$(MSBuildThisFileDirectory)../src\libprojectM\TimeKeeper.cpp" />
133133
<ClCompile Include="$(MSBuildThisFileDirectory)../src\libprojectM\timer.cpp" />
134134
<ClCompile Include="$(MSBuildThisFileDirectory)../src\libprojectM\wipemalloc.cpp" />
135+
<ClCompile Include="$(MSBuildThisFileDirectory)../src\libprojectM\FileScanner.cpp" />
135136
<ClInclude Include="$(MSBuildThisFileDirectory)../msvc\glew.h" />
136137
<ClCompile Include="$(MSBuildThisFileDirectory)../msvc\glew.c">
137138
<CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">CompileAsC</CompileAs>
@@ -166,4 +167,4 @@
166167
<None Include="packages.config" />
167168
</ItemGroup>
168169
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
169-
</Project>
170+
</Project>

src/EyeTune/EyeTune.Shared/EyeTune.Shared.vcxitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\MilkdropPresetFactory\PresetFrameIO.cpp" />
3737
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\PresetFactory.cpp" />
3838
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\ConfigFile.cpp" />
39+
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\FileScanner.cpp" />
3940
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\fftsg.cpp" />
4041
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\KeyHandler.cpp" />
4142
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\libprojectM\PCM.cpp" />

src/libprojectM/Common.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ inline std::string parseExtension(const std::string & filename) {
213213
const std::size_t start = filename.find_last_of('.');
214214

215215
if (start == std::string::npos || start >= (filename.length()-1))
216-
return "";
216+
return {};
217217
std::string ext = filename.substr(start+1, filename.length());
218218
std::transform(ext.begin(), ext.end(), ext.begin(), tolower);
219219
return ext;

src/libprojectM/FileScanner.cpp

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//
2+
// FileScanner.cpp
3+
// libprojectM
4+
//
5+
//
6+
7+
#include "FileScanner.hpp"
8+
9+
FileScanner::FileScanner() {}
10+
11+
FileScanner::FileScanner(std::vector<std::string> &rootDirs, std::vector<std::string> &extensions) : _rootDirs(rootDirs), _extensions(extensions) {}
12+
13+
void FileScanner::scan(ScanCallback cb) {
14+
#ifdef WIN32
15+
for (auto dir : _rootDirs)
16+
scanGeneric(cb, dir.c_str());
17+
#else
18+
scanPosix(cb);
19+
#endif
20+
}
21+
22+
void FileScanner::handleDirectoryError(std::string dir) {
23+
#ifdef WIN32
24+
std::cerr << "[PresetLoader] warning: errno unsupported on win32 platforms. fix me" << std::endl;
25+
#else
26+
27+
std::cerr << dir << " scan error: ";
28+
29+
switch ( errno )
30+
{
31+
case ENOENT:
32+
std::cerr << "ENOENT error. The path \"" << dir << "\" probably does not exist. \"man open\" for more info." << std::endl;
33+
break;
34+
case ENOMEM:
35+
std::cerr << "out of memory!" << std::endl;
36+
abort();
37+
case ENOTDIR:
38+
std::cerr << "directory specified is not a directory! Trying to continue..." << std::endl;
39+
break;
40+
case ENFILE:
41+
std::cerr << "Your system has reached its open file limit. Trying to continue..." << std::endl;
42+
break;
43+
case EMFILE:
44+
std::cerr << "too many files in use by projectM! Bailing!" << std::endl;
45+
break;
46+
case EACCES:
47+
std::cerr << "permissions issue reading the specified preset directory." << std::endl;
48+
break;
49+
default:
50+
break;
51+
}
52+
#endif
53+
}
54+
55+
std::string FileScanner::extensionMatches(std::string &filename) {
56+
// returns file name without extension
57+
// TODO: optimize me
58+
59+
std::string lowerCaseFileName(filename);
60+
std::transform(lowerCaseFileName.begin(), lowerCaseFileName.end(), lowerCaseFileName.begin(), tolower);
61+
62+
// Remove extension
63+
for (auto ext : _extensions)
64+
{
65+
size_t found = lowerCaseFileName.find(ext);
66+
if (found != std::string::npos)
67+
{
68+
std::string name = filename;
69+
name.replace(int(found), ext.size(), "");
70+
return name;
71+
}
72+
}
73+
74+
return {};
75+
}
76+
77+
78+
// generic implementation using dirent
79+
void FileScanner::scanGeneric(ScanCallback cb, const char *currentDir) {
80+
DIR * m_dir;
81+
82+
// Allocate a new a stream given the current directory name
83+
if ((m_dir = opendir(currentDir)) == NULL)
84+
{
85+
return; // no files found in here
86+
}
87+
88+
struct dirent * dir_entry;
89+
90+
while ((dir_entry = readdir(m_dir)) != NULL)
91+
{
92+
// Convert char * to friendly string
93+
std::string filename(dir_entry->d_name);
94+
95+
if (filename.length() > 0 && filename[0] == '.')
96+
continue;
97+
98+
std::string fullPath = std::string(currentDir) + PATH_SEPARATOR + filename;
99+
100+
if (dir_entry->d_type == DT_DIR) {
101+
// recurse into dir
102+
scanGeneric(cb, fullPath.c_str());
103+
continue;
104+
} else if (dir_entry->d_type != DT_REG && dir_entry->d_type != DT_LNK) {
105+
// not regular file/link
106+
continue;
107+
}
108+
109+
auto nameMatched = extensionMatches(filename);
110+
if (! nameMatched.empty())
111+
cb(fullPath, nameMatched);
112+
}
113+
114+
if (m_dir)
115+
{
116+
closedir(m_dir);
117+
m_dir = 0;
118+
}
119+
}
120+
121+
#ifndef WIN32
122+
// more optimized posix "fts" directory traversal
123+
int fts_compare(const FTSENT** one, const FTSENT** two) {
124+
return (strcmp((*one)->fts_name, (*two)->fts_name));
125+
}
126+
#endif
127+
128+
void FileScanner::scanPosix(ScanCallback cb) {
129+
#ifndef WIN32
130+
131+
// efficient directory traversal
132+
FTS* fileSystem = NULL;
133+
FTSENT *node = NULL;
134+
135+
// list of directories to scan
136+
auto rootDirCount = _rootDirs.size();
137+
char **dirList = (char **)malloc(sizeof(char*) * (rootDirCount + 1));
138+
for (unsigned long i = 0; i < rootDirCount; i++) {
139+
dirList[i] = (char *) _rootDirs[i].c_str();
140+
}
141+
dirList[rootDirCount] = NULL;
142+
143+
// initialize file hierarchy traversal
144+
fileSystem = fts_open(dirList, FTS_LOGICAL|FTS_NOCHDIR|FTS_NOSTAT, &fts_compare);
145+
if (fileSystem == NULL) {
146+
std::string s;
147+
for (int i = 0; i < _rootDirs.size(); i++)
148+
s += _rootDirs[i] + ' ';
149+
handleDirectoryError(s);
150+
151+
free(dirList);
152+
return;
153+
}
154+
155+
std::string path, name, nameMatched;
156+
157+
// traverse dirList
158+
while( (node = fts_read(fileSystem)) != NULL) {
159+
switch (node->fts_info) {
160+
case FTS_F:
161+
case FTS_SL:
162+
case FTS_NSOK:
163+
// found a file
164+
path = std::string(node->fts_path);
165+
name = std::string(node->fts_name);
166+
167+
// check extension
168+
nameMatched = extensionMatches(name);
169+
if (! nameMatched.empty())
170+
cb(path, nameMatched);
171+
break;
172+
default:
173+
break;
174+
}
175+
}
176+
fts_close(fileSystem);
177+
free(dirList);
178+
179+
#endif
180+
}

src/libprojectM/FileScanner.hpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// FileScanner.hpp
3+
// libprojectM
4+
//
5+
// Cross-platform directory traversal with filtering by extension
6+
7+
#ifndef FileScanner_hpp
8+
#define FileScanner_hpp
9+
10+
#include <string>
11+
#include <vector>
12+
#include <iostream>
13+
#include <functional>
14+
#include "Common.hpp"
15+
#include <string.h>
16+
17+
#ifdef WIN32
18+
#include "dirent.h"
19+
#else
20+
#include <fts.h>
21+
#endif
22+
23+
#ifdef __unix__
24+
extern "C"
25+
{
26+
#include <errno.h>
27+
#include <dirent.h>
28+
}
29+
#endif
30+
31+
#ifdef __APPLE__
32+
extern "C"
33+
{
34+
#include <errno.h>
35+
#include <dirent.h>
36+
}
37+
#endif
38+
39+
40+
typedef std::function<void (std::string &path, std::string &name)> ScanCallback;
41+
42+
class FileScanner {
43+
public:
44+
FileScanner();
45+
FileScanner(std::vector<std::string> &rootDirs, std::vector<std::string> &extensions);
46+
47+
void scan(ScanCallback cb);
48+
std::string extensionMatches(std::string &filename);
49+
50+
private:
51+
std::vector<std::string> _rootDirs;
52+
std::vector<std::string> _extensions;
53+
54+
void scanGeneric(ScanCallback cb, const char *dir);
55+
void scanPosix(ScanCallback cb);
56+
void handleDirectoryError(std::string dir);
57+
};
58+
59+
60+
#endif /* FileScanner_hpp */

src/libprojectM/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ libprojectM_la_LIBADD = \
2424
libprojectM_la_SOURCES = ConfigFile.cpp Preset.cpp PresetLoader.cpp timer.cpp \
2525
KeyHandler.cpp PresetChooser.cpp TimeKeeper.cpp PCM.cpp PresetFactory.cpp \
2626
fftsg.cpp wipemalloc.cpp PipelineMerger.cpp PresetFactoryManager.cpp projectM.cpp \
27-
TestRunner.cpp TestRunner.hpp \
27+
TestRunner.cpp TestRunner.hpp FileScanner.cpp FileScanner.hpp\
2828
Common.hpp PipelineMerger.hpp PresetLoader.hpp\
2929
HungarianMethod.hpp Preset.hpp RandomNumberGenerators.hpp\
3030
IdleTextures.hpp PresetChooser.hpp TimeKeeper.hpp\
3131
KeyHandler.hpp PresetFactory.hpp projectM.hpp\
32-
BackgroundWorker.h \
32+
BackgroundWorker.h \
3333
PCM.hpp PresetFactoryManager.hpp\
3434
projectM.hpp projectM-opengl.h \
3535
ConfigFile.h \

src/libprojectM/MilkdropPresetFactory/InitCond.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ void InitCond::evaluate(bool evalUser)
7171
/* WIP */
7272
void InitCond::init_cond_to_string()
7373
{
74-
int string_length;
74+
unsigned long string_length;
7575
char string[MAX_TOKEN_SIZE];
7676

7777
/* Create a string "param_name=val" */

src/libprojectM/MilkdropPresetFactory/MilkdropPreset.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,7 @@ MilkdropPreset::~MilkdropPreset()
115115

116116
int MilkdropPreset::add_per_pixel_eqn(char * name, Expr * gen_expr)
117117
{
118-
119118
PerPixelEqn * per_pixel_eqn = NULL;
120-
int index;
121119
Param * param = NULL;
122120

123121
assert(gen_expr);
@@ -134,7 +132,7 @@ int MilkdropPreset::add_per_pixel_eqn(char * name, Expr * gen_expr)
134132
return PROJECTM_FAILURE;
135133
}
136134

137-
index = per_pixel_eqn_tree.size();
135+
auto index = per_pixel_eqn_tree.size();
138136

139137
/* Create the per pixel equation given the index, parameter, and general expression */
140138
if ((per_pixel_eqn = new PerPixelEqn(index, param, gen_expr)) == NULL)
@@ -143,8 +141,6 @@ int MilkdropPreset::add_per_pixel_eqn(char * name, Expr * gen_expr)
143141
return PROJECTM_FAILURE;
144142
}
145143

146-
147-
148144
/* Insert the per pixel equation into the preset per pixel database */
149145
std::pair<std::map<int, PerPixelEqn*>::iterator, bool> inserteeOption = per_pixel_eqn_tree.insert
150146
(std::make_pair(per_pixel_eqn->index, per_pixel_eqn));

0 commit comments

Comments
 (0)