1 #include <stdio.h>
2 #include <dirent.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <unistd.h>
6 #include <string>
7 #include <map>
8 #include <TCFoundation/TCUserDefaults.h>
9 #include <TCFoundation/mystring.h>
10 #include <LDLib/LDSnapshotTaker.h>
11 #include <LDLoader/LDLModel.h>
12 #include <TCFoundation/TCAutoreleasePool.h>
13 #include <TCFoundation/TCAlertManager.h>
14 #include <TCFoundation/TCProgressAlert.h>
15 #include <TCFoundation/TCLocalStrings.h>
16 #include <GL/osmesa.h>
17 #include <TRE/TREMainModel.h>
18 #include "StudLogo.h"
19 #include "LDViewMessages.h"
20 
21 typedef std::map<std::string, std::string> StringMap;
22 
23 #define DEPTH_BPP 24
24 // Note: buffer contains only color buffer, not depth and stencil.
25 #define BYTES_PER_PIXEL 4
26 
27 class ProgressHandler: public TCObject
28 {
29 public:
ProgressHandler(void)30 	ProgressHandler(void)
31 	{
32 		TCAlertManager::registerHandler(TCProgressAlert::alertClass(), this,
33 			(TCAlertCallback)&ProgressHandler::alertCallback);
34 	}
35 protected:
~ProgressHandler(void)36 	~ProgressHandler(void)
37 	{
38 	}
dealloc(void)39 	void dealloc(void)
40 	{
41 		TCAlertManager::unregisterHandler(this);
42 		TCObject::dealloc();
43 	}
alertCallback(TCProgressAlert * progress)44 	void alertCallback(TCProgressAlert *progress)
45 	{
46 		if (progress->getMessage() && strlen(progress->getMessage()))
47 		{
48 			printf("%s: %f%%\n", progress->getMessage(),
49 				progress->getProgress() * 100.0f);
50 		}
51 	}
52 };
53 
setupDefaults(char * argv[])54 void setupDefaults(char *argv[])
55 {
56 	TCUserDefaults::setCommandLine(argv);
57 	// IniFile can be specified on the command line; if so, don't load a
58 	// different one.
59 	if (!TCUserDefaults::isIniFileSet())
60 	{
61 		char *homeDir = getenv("HOME");
62 
63 		if (homeDir)
64 		{
65 			char *rcFilename = copyString(homeDir, 128);
66 
67 			strcat(rcFilename, "/.ldviewrc");
68 
69 			char *rcFilename2 = copyString(homeDir, 128);
70 
71 			strcat(rcFilename2, "/.config/LDView/ldviewrc");
72 			if (!TCUserDefaults::setIniFile(rcFilename) &&
73 				!TCUserDefaults::setIniFile(rcFilename2))
74 			{
75 				printf("Error setting INI File to %s or %s\n", rcFilename,
76 				    rcFilename2);
77 			}
78 			delete rcFilename;
79 			delete rcFilename2;
80 		}
81 		else
82 		{
83 			printf("HOME environment variable not defined: cannot use "
84 				"~/.ldviewrc.\n");
85 		}
86 	}
87 	setDebugLevel(TCUserDefaults::longForKey("DebugLevel", 0, false));
88 }
89 
setupContext(OSMesaContext & ctx)90 void *setupContext(OSMesaContext &ctx)
91 {
92 	void *buffer = NULL;
93 	int width = TCUserDefaults::longForKey("TileWidth", 1024, false);
94 	int height = TCUserDefaults::longForKey("TileHeight", 1024, false);
95 	int tileSize = TCUserDefaults::longForKey("TileSize", -1, false);
96 
97 	if (tileSize > 0)
98 	{
99 		width = height = tileSize;
100 	}
101 	ctx = OSMesaCreateContextExt(OSMESA_RGBA, DEPTH_BPP, 8, 0, NULL);
102 	if (!ctx)
103 	{
104 		printf("Error creating OSMesa context.\n");
105 		return NULL;
106 	}
107 	buffer = malloc(width * height * BYTES_PER_PIXEL * sizeof(GLubyte));
108 	if (OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, width, height))
109 	{
110 		GLint viewport[4] = {0};
111 		glGetIntegerv(GL_VIEWPORT, viewport);
112 		if (viewport[2] != width || viewport[3] != height)
113 		{
114 			printf("OSMesa not working!\n");
115 			printf("viewport: %d, %d, %d, %d\n", (int)viewport[0],
116 				(int)viewport[1], (int)viewport[2], (int)viewport[3]);
117 			free(buffer);
118 			buffer = NULL;
119 		}
120 	}
121 	else
122 	{
123 		printf("Error attaching buffer to context.\n");
124 		free(buffer);
125 		buffer = NULL;
126 	}
127 	return buffer;
128 }
129 
dirExists(const std::string & path)130 bool dirExists(const std::string &path)
131 {
132 	struct stat buf;
133 	if (stat(path.c_str(), &buf) != 0)
134 	{
135 		return false;
136 	}
137 	return S_ISDIR(buf.st_mode);
138 }
139 
fileOrDirExists(const std::string & path)140 bool fileOrDirExists(const std::string &path)
141 {
142 	struct stat buf;
143 	if (stat(path.c_str(), &buf) != 0)
144 	{
145 		return false;
146 	}
147 	return S_ISREG(buf.st_mode) || S_ISDIR(buf.st_mode);
148 }
149 
findDirEntry(std::string & path)150 bool findDirEntry(std::string &path)
151 {
152 	size_t lastSlash = path.rfind('/');
153 	if (lastSlash >= path.size())
154 	{
155 		return false;
156 	}
157 	std::string dirName = path.substr(0, lastSlash);
158 	std::string lowerFilename = lowerCaseString(path.substr(lastSlash + 1));
159 	DIR *dir = opendir(dirName.c_str());
160 	if (dir == NULL)
161 	{
162 		return false;
163 	}
164 	bool found = false;
165 	while (!found)
166 	{
167 		struct dirent *entry = readdir(dir);
168 		if (entry == NULL)
169 		{
170 			break;
171 		}
172 		std::string name = lowerCaseString(entry->d_name);
173 		if (name == lowerFilename)
174 		{
175 			path = dirName + '/' + entry->d_name;
176 			found = true;
177 		}
178 	}
179 	closedir(dir);
180 	return found;
181 }
182 
fileCaseCallback(char * filename)183 bool fileCaseCallback(char *filename)
184 {
185 	StringMap s_pathMap;
186 	int count;
187 	char **components = componentsSeparatedByString(filename, "/", count);
188 	std::string lowerFilename = lowerCaseString(filename);
189 
190 	StringMap::iterator it = s_pathMap.find(lowerFilename);
191 	if (it != s_pathMap.end())
192 	{
193 		strcpy(filename, it->second.c_str());
194 		return true;
195 	}
196 	if (count > 1)
197 	{
198 		bool ok = true;
199 		std::string builtPath = "/";
200 		for (int i = 1; i + 1 < count && ok; ++i)
201 		{
202 			builtPath += components[i];
203 
204 			it = s_pathMap.find(builtPath);
205 			if (it != s_pathMap.end())
206 			{
207 				// Do nothing
208 			}
209 			else if (dirExists(builtPath))
210 			{
211 				s_pathMap[lowerCaseString(builtPath)] = builtPath;
212 			}
213 			else if (findDirEntry(builtPath))
214 			{
215 				s_pathMap[lowerCaseString(builtPath)] = builtPath;
216 				if (!dirExists(builtPath))
217 				{
218 					ok = false;
219 				}
220 			}
221 			else
222 			{
223 				ok = false;
224 			}
225 			if (ok)
226 			{
227 				builtPath += '/';
228 			}
229 		}
230 		if (ok)
231 		{
232 			builtPath += components[count - 1];
233 			if (findDirEntry(builtPath))
234 			{
235 				s_pathMap[lowerCaseString(builtPath)] = builtPath;
236 				ok = fileOrDirExists(builtPath);
237 			}
238 			else
239 			{
240 				ok = false;
241 			}
242 		}
243 		if (ok)
244 		{
245 			strcpy(filename, builtPath.c_str());
246 		}
247 		deleteStringArray(components, count);
248 		return ok;
249 	}
250 	return false;
251 }
252 
main(int argc,char * argv[])253 int main(int argc, char *argv[])
254 {
255 	void *buffer;
256 	OSMesaContext ctx;
257 	int stringTableSize = sizeof(LDViewMessages_bytes);
258 	char *stringTable = new char[sizeof(LDViewMessages_bytes) + 1];
259 
260 	memcpy(stringTable, LDViewMessages_bytes, stringTableSize);
261 	stringTable[stringTableSize] = 0;
262 	TCLocalStrings::setStringTable(stringTable);
263 	setupDefaults(argv);
264 	if ((buffer = setupContext(ctx)) != NULL)
265 	{
266 		//ProgressHandler *progressHandler = new ProgressHandler;
267 
268 		TREMainModel::setStudTextureData(StudLogo_bytes,
269 			sizeof(StudLogo_bytes));
270 		LDLModel::setFileCaseCallback(fileCaseCallback);
271 		LDSnapshotTaker::doCommandLine();
272 		OSMesaDestroyContext(ctx);
273 		free(buffer);
274 		//TCObject::release(progressHandler);
275 	}
276 	TCAutoreleasePool::processReleases();
277 	return 0;
278 }
279