1 /*
2 * The Doomsday Engine Project -- libcore
3 *
4 * Copyright © 2011-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * LGPL: http://www.gnu.org/licenses/lgpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
15 * General Public License for more details. You should have received a copy of
16 * the GNU Lesser General Public License along with this program; if not, see:
17 * http://www.gnu.org/licenses</small>
18 */
19
20 #include "de/c_wrapper.h"
21 #include "de/Error"
22 #include "de/App"
23 #include "de/CommandLine"
24 #include "de/Loop"
25 #include "de/Address"
26 #include "de/ByteRefArray"
27 #include "de/Block"
28 #include "de/LogBuffer"
29 #include "de/ByteOrder"
30 #include "de/Info"
31 #include "de/UnixInfo"
32 #include <QFile>
33 #include <cstring>
34 #include <stdarg.h>
35
36 #define DENG2_COMMANDLINE() DENG2_APP->commandLine()
37
checkLogEntryMetadata(unsigned int & metadata)38 static bool checkLogEntryMetadata(unsigned int &metadata)
39 {
40 // Automatically apply the generic domain if not specified.
41 if (!(metadata & de::LogEntry::DomainMask))
42 {
43 metadata |= de::LogEntry::Generic;
44 }
45
46 // Validate the level.
47 de::LogEntry::Level logLevel = de::LogEntry::Level(metadata & de::LogEntry::LevelMask);
48 if (logLevel < de::LogEntry::XVerbose || logLevel > de::LogEntry::Critical)
49 {
50 metadata &= ~de::LogEntry::LevelMask;
51 metadata |= de::LogEntry::Message;
52 }
53
54 // If this level is not enabled, just ignore.
55 return de::LogBuffer::get().isEnabled(metadata);
56 }
57
logFragmentPrinter(duint32 metadata,char const * fragment)58 static void logFragmentPrinter(duint32 metadata, char const *fragment)
59 {
60 static std::string currentLogLine;
61
62 currentLogLine += fragment;
63
64 std::string::size_type pos;
65 while ((pos = currentLogLine.find('\n')) != std::string::npos)
66 {
67 LOG().enter(metadata, currentLogLine.substr(0, pos).c_str());
68 currentLogLine.erase(0, pos + 1);
69 }
70 }
71
App_Log(unsigned int metadata,char const * format,...)72 void App_Log(unsigned int metadata, char const *format, ...)
73 {
74 if (!checkLogEntryMetadata(metadata)) return;
75
76 char buffer[0x2000];
77 va_list args;
78 va_start(args, format);
79 size_t nc = vsprintf(buffer, format, args); /// @todo unsafe
80 va_end(args);
81 DENG2_ASSERT(nc < sizeof(buffer) - 2);
82 if (!nc) return;
83
84 LOG().enter(metadata, buffer);
85
86 // Make sure there's a newline in the end.
87 /*if (buffer[nc - 1] != '\n')
88 {
89 buffer[nc++] = '\n';
90 buffer[nc] = 0;
91 }
92 logFragmentPrinter(metadata, buffer);*/
93 }
94
App_Timer(unsigned int milliseconds,void (* callback)(void))95 void App_Timer(unsigned int milliseconds, void (*callback)(void))
96 {
97 de::Loop::timer(de::TimeSpan::fromMilliSeconds(milliseconds), callback);
98 }
99
App_FatalError(char const * msgFormat,...)100 void App_FatalError(char const *msgFormat, ...)
101 {
102 char buffer[4096];
103 de::zap(buffer);
104
105 va_list args;
106 va_start(args, msgFormat);
107 qvsnprintf(buffer, sizeof(buffer) - 1, msgFormat, args);
108 va_end(args);
109
110 DENG2_APP->handleUncaughtException(buffer);
111
112 // Let's make sure this is the end.
113 exit(-1);
114 }
115
CommandLine_Alias(char const * longname,char const * shortname)116 void CommandLine_Alias(char const *longname, char const *shortname)
117 {
118 DENG2_COMMANDLINE().alias(longname, shortname);
119 }
120
CommandLine_Count(void)121 int CommandLine_Count(void)
122 {
123 return DENG2_COMMANDLINE().count();
124 }
125
CommandLine_At(int i)126 char const *CommandLine_At(int i)
127 {
128 DENG2_ASSERT(i >= 0);
129 DENG2_ASSERT(i < DENG2_COMMANDLINE().count());
130 return *(DENG2_COMMANDLINE().argv() + i);
131 }
132
CommandLine_PathAt(int i)133 char const *CommandLine_PathAt(int i)
134 {
135 DENG2_COMMANDLINE().makeAbsolutePath(i);
136 return CommandLine_At(i);
137 }
138
139 static int argLastMatch = 0; // used only in ArgCheck/ArgNext (not thread-safe)
140
CommandLine_Next(void)141 char const *CommandLine_Next(void)
142 {
143 if (!argLastMatch || argLastMatch >= CommandLine_Count() - 1)
144 {
145 // No more arguments following the last match.
146 return 0;
147 }
148 return CommandLine_At(++argLastMatch);
149 }
150
CommandLine_NextAsPath(void)151 char const *CommandLine_NextAsPath(void)
152 {
153 if (!argLastMatch || argLastMatch >= CommandLine_Count() - 1)
154 {
155 // No more arguments following the last match.
156 return 0;
157 }
158 DENG2_COMMANDLINE().makeAbsolutePath(argLastMatch + 1);
159 return CommandLine_Next();
160 }
161
CommandLine_Check(char const * check)162 int CommandLine_Check(char const *check)
163 {
164 return argLastMatch = DENG2_COMMANDLINE().check(check);
165 }
166
CommandLine_CheckWith(char const * check,int num)167 int CommandLine_CheckWith(char const *check, int num)
168 {
169 return argLastMatch = DENG2_COMMANDLINE().check(check, num);
170 }
171
CommandLine_Exists(char const * check)172 int CommandLine_Exists(char const *check)
173 {
174 return DENG2_COMMANDLINE().has(check);
175 }
176
CommandLine_IsOption(int i)177 int CommandLine_IsOption(int i)
178 {
179 return DENG2_COMMANDLINE().isOption(i);
180 }
181
CommandLine_IsMatchingAlias(char const * original,char const * originalOrAlias)182 int CommandLine_IsMatchingAlias(char const *original, char const *originalOrAlias)
183 {
184 return DENG2_COMMANDLINE().matches(original, originalOrAlias);
185 }
186
LogBuffer_Flush(void)187 void LogBuffer_Flush(void)
188 {
189 if (de::LogBuffer::appBufferExists())
190 {
191 de::LogBuffer::get().flush();
192 }
193 }
194
LogBuffer_Clear(void)195 void LogBuffer_Clear(void)
196 {
197 de::LogBuffer::get().clear();
198 }
199
LogBuffer_EnableStandardOutput(int enable)200 void LogBuffer_EnableStandardOutput(int enable)
201 {
202 de::LogBuffer::get().enableStandardOutput(enable != 0);
203 }
204
LogBuffer_Printf(unsigned int metadata,char const * format,...)205 void LogBuffer_Printf(unsigned int metadata, char const *format, ...)
206 {
207 if (!checkLogEntryMetadata(metadata)) return;
208
209 char buffer[0x2000];
210 va_list args;
211 va_start(args, format);
212 size_t nc = vsprintf(buffer, format, args); /// @todo unsafe
213 va_end(args);
214 DENG2_ASSERT(nc < sizeof(buffer) - 1);
215 DENG2_UNUSED(nc);
216
217 logFragmentPrinter(metadata, buffer);
218 }
219
Info_NewFromString(char const * utf8text)220 de_Info *Info_NewFromString(char const *utf8text)
221 {
222 try
223 {
224 return reinterpret_cast<de_Info *>(new de::Info(QString::fromUtf8(utf8text)));
225 }
226 catch (de::Error const &er)
227 {
228 LOG_WARNING(er.asText());
229 return 0;
230 }
231 }
232
Info_NewFromFile(char const * nativePath)233 de_Info *Info_NewFromFile(char const *nativePath)
234 {
235 try
236 {
237 QScopedPointer<de::Info> info(new de::Info);
238 info->parseNativeFile(nativePath);
239 return reinterpret_cast<de_Info *>(info.take());
240 }
241 catch (de::Error const &er)
242 {
243 LOG_WARNING(er.asText());
244 return 0;
245 }
246 }
247
Info_Delete(de_Info * info)248 void Info_Delete(de_Info *info)
249 {
250 if (info)
251 {
252 DENG2_SELF(Info, info);
253 delete self;
254 }
255 }
256
Info_FindValue(de_Info * info,char const * path,char * buffer,size_t bufSize)257 int Info_FindValue(de_Info *info, char const *path, char *buffer, size_t bufSize)
258 {
259 if (!info) return false;
260
261 DENG2_SELF(Info, info);
262 de::Info::Element const *element = self->findByPath(path);
263 if (!element || !element->isKey()) return false;
264 QString value = static_cast<de::Info::KeyElement const *>(element)->value();
265 if (buffer)
266 {
267 qstrncpy(buffer, value.toUtf8().constData(), uint(bufSize));
268 return true;
269 }
270 else
271 {
272 // Just return the size of the value.
273 return value.size();
274 }
275 }
276
UnixInfo_GetConfigValue(char const * configFile,char const * key)277 char *UnixInfo_GetConfigValue(char const *configFile, char const *key)
278 {
279 de::UnixInfo &info = de::App::unixInfo();
280
281 // "paths" is the only config file currently being used.
282 if (!qstrcmp(configFile, "paths"))
283 {
284 de::NativePath foundValue;
285 if (info.path(key, foundValue))
286 {
287 return qstrdup(foundValue.toString().toUtf8().constData());
288 }
289 }
290 else if (!qstrcmp(configFile, "defaults"))
291 {
292 de::String foundValue;
293 if (info.defaults(key, foundValue))
294 {
295 return qstrdup(foundValue.toUtf8().constData());
296 }
297 }
298 return nullptr;
299 }
300
LittleEndianByteOrder_ToForeignInt16(dint16 value)301 dint16 LittleEndianByteOrder_ToForeignInt16(dint16 value)
302 {
303 DENG2_ASSERT(sizeof(dint16) == sizeof(de::dint16));
304 return de::littleEndianByteOrder.toNetwork(de::dint16(value));
305 }
306
LittleEndianByteOrder_ToForeignInt32(dint32 value)307 dint32 LittleEndianByteOrder_ToForeignInt32(dint32 value)
308 {
309 DENG2_ASSERT(sizeof(dint32) == sizeof(de::dint32));
310 return de::littleEndianByteOrder.toNetwork(de::dint32(value));
311 }
312
LittleEndianByteOrder_ToForeignInt64(dint64 value)313 dint64 LittleEndianByteOrder_ToForeignInt64(dint64 value)
314 {
315 DENG2_ASSERT(sizeof(dint64) == sizeof(de::dint64));
316 return de::littleEndianByteOrder.toNetwork(de::dint64(value));
317 }
318
LittleEndianByteOrder_ToForeignUInt16(duint16 value)319 duint16 LittleEndianByteOrder_ToForeignUInt16(duint16 value)
320 {
321 DENG2_ASSERT(sizeof(duint16) == sizeof(de::duint16));
322 return de::littleEndianByteOrder.toNetwork(de::duint16(value));
323 }
324
LittleEndianByteOrder_ToForeignUInt32(duint32 value)325 duint32 LittleEndianByteOrder_ToForeignUInt32(duint32 value)
326 {
327 DENG2_ASSERT(sizeof(duint32) == sizeof(de::duint32));
328 return de::littleEndianByteOrder.toNetwork(de::duint32(value));
329 }
330
LittleEndianByteOrder_ToForeignUInt64(duint64 value)331 duint64 LittleEndianByteOrder_ToForeignUInt64(duint64 value)
332 {
333 DENG2_ASSERT(sizeof(duint64) == sizeof(de::duint64));
334 return de::littleEndianByteOrder.toNetwork(de::duint64(value));
335 }
336
LittleEndianByteOrder_ToForeignFloat(dfloat value)337 dfloat LittleEndianByteOrder_ToForeignFloat(dfloat value)
338 {
339 DENG2_ASSERT(sizeof(dfloat) == sizeof(de::dfloat));
340 return de::littleEndianByteOrder.toNetwork(de::dfloat(value));
341 }
342
LittleEndianByteOrder_ToForeignDouble(ddouble value)343 ddouble LittleEndianByteOrder_ToForeignDouble(ddouble value)
344 {
345 DENG2_ASSERT(sizeof(ddouble) == sizeof(de::ddouble));
346 return de::littleEndianByteOrder.toNetwork(de::ddouble(value));
347 }
348
LittleEndianByteOrder_ToNativeInt16(dint16 value)349 dint16 LittleEndianByteOrder_ToNativeInt16(dint16 value)
350 {
351 DENG2_ASSERT(sizeof(dint16) == sizeof(de::dint16));
352 return de::littleEndianByteOrder.toHost(de::dint16(value));
353 }
354
LittleEndianByteOrder_ToNativeInt32(dint32 value)355 dint32 LittleEndianByteOrder_ToNativeInt32(dint32 value)
356 {
357 DENG2_ASSERT(sizeof(dint32) == sizeof(de::dint32));
358 return de::littleEndianByteOrder.toHost(de::dint32(value));
359 }
360
LittleEndianByteOrder_ToNativeInt64(dint64 value)361 dint64 LittleEndianByteOrder_ToNativeInt64(dint64 value)
362 {
363 DENG2_ASSERT(sizeof(dint64) == sizeof(de::dint64));
364 return de::littleEndianByteOrder.toHost(de::dint64(value));
365 }
366
LittleEndianByteOrder_ToNativeUInt16(duint16 value)367 duint16 LittleEndianByteOrder_ToNativeUInt16(duint16 value)
368 {
369 DENG2_ASSERT(sizeof(duint16) == sizeof(de::duint16));
370 return de::littleEndianByteOrder.toHost(de::duint16(value));
371 }
372
LittleEndianByteOrder_ToNativeUInt32(duint32 value)373 duint32 LittleEndianByteOrder_ToNativeUInt32(duint32 value)
374 {
375 DENG2_ASSERT(sizeof(duint32) == sizeof(de::duint32));
376 return de::littleEndianByteOrder.toHost(de::duint32(value));
377 }
378
LittleEndianByteOrder_ToNativeUInt64(duint64 value)379 duint64 LittleEndianByteOrder_ToNativeUInt64(duint64 value)
380 {
381 DENG2_ASSERT(sizeof(duint64) == sizeof(de::duint64));
382 return de::littleEndianByteOrder.toHost(de::duint64(value));
383 }
384
LittleEndianByteOrder_ToNativeFloat(dfloat value)385 dfloat LittleEndianByteOrder_ToNativeFloat(dfloat value)
386 {
387 DENG2_ASSERT(sizeof(dfloat) == sizeof(de::dfloat));
388 return de::littleEndianByteOrder.toHost(de::dfloat(value));
389 }
390
LittleEndianByteOrder_ToNativeDouble(ddouble value)391 ddouble LittleEndianByteOrder_ToNativeDouble(ddouble value)
392 {
393 DENG2_ASSERT(sizeof(ddouble) == sizeof(de::ddouble));
394 return de::littleEndianByteOrder.toHost(de::ddouble(value));
395 }
396