util.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // Copyright (c) 2011-2017, SDLPAL development team.
  5. // All rights reserved.
  6. //
  7. // This file is part of SDLPAL.
  8. //
  9. // SDLPAL is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. //
  22. #include "util.h"
  23. #include "input.h"
  24. #include "global.h"
  25. #include "palcfg.h"
  26. #include <errno.h>
  27. #include "midi.h"
  28. #if SDL_VERSION_ATLEAST(2, 0, 0)
  29. #include "SDL_video.h"
  30. #include "SDL_messagebox.h"
  31. #endif
  32. static char internal_buffer[PAL_MAX_GLOBAL_BUFFERS + 1][PAL_GLOBAL_BUFFER_SIZE];
  33. #ifdef _WIN32
  34. static const char * const path_separators = "/\\";
  35. # define isseparator(x) ((x) == '/' || (x) == '\\')
  36. #else
  37. static const char * const path_separators = "/";
  38. # define isseparator(x) ((x) == '/')
  39. #endif
  40. void UTIL_MsgBox(char *string)
  41. {
  42. #if SDL_VERSION_ATLEAST(2, 0, 0)
  43. extern SDL_Window *gpWindow;
  44. char buffer[300];
  45. SDL_MessageBoxButtonData buttons[] = { { 0, 0, "OK" } };
  46. SDL_MessageBoxData mbd = { SDL_MESSAGEBOX_WARNING, gpWindow, "Alert",buffer, 1, buttons, NULL };
  47. int btnid;
  48. sprintf(buffer, "%s\n", string);
  49. SDL_ShowMessageBox(&mbd, &btnid);
  50. #endif
  51. }
  52. long
  53. flength(
  54. FILE *fp
  55. )
  56. {
  57. long old_pos = ftell(fp), length;
  58. if (old_pos == -1) return -1;
  59. if (fseek(fp, 0, SEEK_END) == -1) return -1;
  60. length = ftell(fp); fseek(fp, old_pos, SEEK_SET);
  61. return length;
  62. }
  63. void
  64. trim(
  65. char *str
  66. )
  67. /*++
  68. Purpose:
  69. Remove the leading and trailing spaces in a string.
  70. Parameters:
  71. str - the string to proceed.
  72. Return value:
  73. None.
  74. --*/
  75. {
  76. int pos = 0;
  77. char *dest = str;
  78. //
  79. // skip leading blanks
  80. //
  81. while (str[pos] <= ' ' && str[pos] > 0)
  82. pos++;
  83. while (str[pos])
  84. {
  85. *(dest++) = str[pos];
  86. pos++;
  87. }
  88. *(dest--) = '\0'; // store the null
  89. //
  90. // remove trailing blanks
  91. //
  92. while (dest >= str && *dest <= ' ' && *dest > 0)
  93. *(dest--) = '\0';
  94. }
  95. char *
  96. UTIL_va(
  97. char *buffer,
  98. int buflen,
  99. const char *format,
  100. ...
  101. )
  102. {
  103. if (buflen > 0 && buffer)
  104. {
  105. va_list argptr;
  106. va_start(argptr, format);
  107. vsnprintf(buffer, buflen, format, argptr);
  108. va_end(argptr);
  109. return buffer;
  110. }
  111. else
  112. {
  113. return NULL;
  114. }
  115. }
  116. /*
  117. * RNG code based on RACC by Pierre-Marie Baty.
  118. * http://racc.bots-united.com
  119. *
  120. * Copyright (c) 2004, Pierre-Marie Baty
  121. * All rights reserved.
  122. *
  123. * Redistribution and use in source and binary forms, with or
  124. * without modification, are permitted provided that the following
  125. * conditions are met:
  126. *
  127. * Redistributions of source code must retain the above copyright
  128. * notice, this list of conditions and the following disclaimer.
  129. *
  130. * Redistributions in binary form must reproduce the above copyright
  131. * notice, this list of conditions and the following disclaimer in
  132. * the documentation and/or other materials provided with the
  133. * distribution.
  134. *
  135. * Neither the name of the RACC nor the names of its contributors
  136. * may be used to endorse or promote products derived from this
  137. * software without specific prior written permission.
  138. *
  139. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  140. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  141. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  142. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  143. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  144. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  145. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  146. * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  147. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  148. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  149. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  150. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  151. * POSSIBILITY OF SUCH DAMAGE.
  152. */
  153. //
  154. // Our random number generator's seed.
  155. //
  156. static int glSeed = 0;
  157. static void
  158. lsrand(
  159. unsigned int iInitialSeed
  160. )
  161. /*++
  162. Purpose:
  163. This function initializes the random seed based on the initial seed value passed in the
  164. iInitialSeed parameter.
  165. Parameters:
  166. [IN] iInitialSeed - The initial random seed.
  167. Return value:
  168. None.
  169. --*/
  170. {
  171. //
  172. // fill in the initial seed of the random number generator
  173. //
  174. glSeed = 1664525L * iInitialSeed + 1013904223L;
  175. }
  176. static int
  177. lrand(
  178. void
  179. )
  180. /*++
  181. Purpose:
  182. This function is the equivalent of the rand() standard C library function, except that
  183. whereas rand() works only with short integers (i.e. not above 32767), this function is
  184. able to generate 32-bit random numbers.
  185. Parameters:
  186. None.
  187. Return value:
  188. The generated random number.
  189. --*/
  190. {
  191. if (glSeed == 0) // if the random seed isn't initialized...
  192. lsrand((unsigned int)time(NULL)); // initialize it first
  193. glSeed = 1664525L * glSeed + 1013904223L; // do some twisted math (infinite suite)
  194. return ((glSeed >> 1) + 1073741824L); // and return the result.
  195. }
  196. int
  197. RandomLong(
  198. int from,
  199. int to
  200. )
  201. /*++
  202. Purpose:
  203. This function returns a random integer number between (and including) the starting and
  204. ending values passed by parameters from and to.
  205. Parameters:
  206. from - the starting value.
  207. to - the ending value.
  208. Return value:
  209. The generated random number.
  210. --*/
  211. {
  212. if (to <= from)
  213. return from;
  214. return from + lrand() / (INT_MAX / (to - from + 1));
  215. }
  216. float
  217. RandomFloat(
  218. float from,
  219. float to
  220. )
  221. /*++
  222. Purpose:
  223. This function returns a random floating-point number between (and including) the starting
  224. and ending values passed by parameters from and to.
  225. Parameters:
  226. from - the starting value.
  227. to - the ending value.
  228. Return value:
  229. The generated random number.
  230. --*/
  231. {
  232. if (to <= from)
  233. return from;
  234. return from + (float)lrand() / (INT_MAX / (to - from));
  235. }
  236. void
  237. UTIL_Delay(
  238. unsigned int ms
  239. )
  240. {
  241. unsigned int t = SDL_GetTicks() + ms;
  242. while (PAL_PollEvent(NULL));
  243. while (!SDL_TICKS_PASSED(SDL_GetTicks(), t))
  244. {
  245. SDL_Delay(1);
  246. while (PAL_PollEvent(NULL));
  247. }
  248. }
  249. void
  250. TerminateOnError(
  251. const char *fmt,
  252. ...
  253. )
  254. // This function terminates the game because of an error and
  255. // prints the message string pointed to by fmt both in the
  256. // console and in a messagebox.
  257. {
  258. va_list argptr;
  259. char string[256];
  260. extern VOID PAL_Shutdown(int);
  261. // concatenate all the arguments in one string
  262. va_start(argptr, fmt);
  263. vsnprintf(string, sizeof(string), fmt, argptr);
  264. va_end(argptr);
  265. fprintf(stderr, "\nFATAL ERROR: %s\n", string);
  266. #if SDL_VERSION_ATLEAST(2, 0, 0)
  267. {
  268. extern SDL_Window *gpWindow;
  269. char buffer[300];
  270. SDL_MessageBoxButtonData buttons[2] = { { 0, 0, "Yes" },{ 0, 1, "No" } };
  271. SDL_MessageBoxData mbd = { SDL_MESSAGEBOX_ERROR, gpWindow, "FATAL ERROR", buffer, 2, buttons, NULL };
  272. int btnid;
  273. sprintf(buffer, "%sLaunch setting dialog on next start?\n", string);
  274. if (SDL_ShowMessageBox(&mbd, &btnid) == 0 && btnid == 0)
  275. {
  276. gConfig.fLaunchSetting = TRUE;
  277. PAL_SaveConfig();
  278. }
  279. PAL_Shutdown(255);
  280. }
  281. #else
  282. PAL_FATAL_OUTPUT(string);
  283. #endif
  284. #ifdef _DEBUG
  285. assert(!"TerminateOnError()"); // allows jumping to debugger
  286. #endif
  287. PAL_Shutdown(255);
  288. }
  289. void *
  290. UTIL_malloc(
  291. size_t buffer_size
  292. )
  293. {
  294. // handy wrapper for operations we always forget, like checking malloc's returned pointer.
  295. void *buffer;
  296. // first off, check if buffer size is valid
  297. if (buffer_size == 0)
  298. TerminateOnError("UTIL_malloc() called with invalid buffer size: %d\n", buffer_size);
  299. buffer = malloc(buffer_size); // allocate real memory space
  300. // last check, check if malloc call succeeded
  301. if (buffer == NULL)
  302. TerminateOnError("UTIL_malloc() failure for %d bytes (out of memory?)\n", buffer_size);
  303. return buffer; // nothing went wrong, so return buffer pointer
  304. }
  305. void *
  306. UTIL_calloc(
  307. size_t n,
  308. size_t size
  309. )
  310. {
  311. // handy wrapper for operations we always forget, like checking calloc's returned pointer.
  312. void *buffer;
  313. // first off, check if buffer size is valid
  314. if (n == 0 || size == 0)
  315. TerminateOnError ("UTIL_calloc() called with invalid parameters\n");
  316. buffer = calloc(n, size); // allocate real memory space
  317. // last check, check if malloc call succeeded
  318. if (buffer == NULL)
  319. TerminateOnError("UTIL_calloc() failure for %d bytes (out of memory?)\n", size * n);
  320. return buffer; // nothing went wrong, so return buffer pointer
  321. }
  322. FILE *
  323. UTIL_OpenRequiredFile(
  324. LPCSTR lpszFileName
  325. )
  326. /*++
  327. Purpose:
  328. Open a required file. If fails, quit the program.
  329. Parameters:
  330. [IN] lpszFileName - file name to open.
  331. Return value:
  332. Pointer to the file.
  333. --*/
  334. {
  335. return UTIL_OpenRequiredFileForMode(lpszFileName, "rb");
  336. }
  337. FILE *
  338. UTIL_OpenRequiredFileForMode(
  339. LPCSTR lpszFileName,
  340. LPCSTR szMode
  341. )
  342. /*++
  343. Purpose:
  344. Open a required file. If fails, quit the program.
  345. Parameters:
  346. [IN] lpszFileName - file name to open.
  347. [IN] szMode - file open mode.
  348. Return value:
  349. Pointer to the file.
  350. --*/
  351. {
  352. FILE *fp = UTIL_OpenFileForMode(lpszFileName, szMode);
  353. if (fp == NULL)
  354. {
  355. TerminateOnError("File open error(%d): %s!\n", errno, lpszFileName);
  356. }
  357. return fp;
  358. }
  359. FILE *
  360. UTIL_OpenFile(
  361. LPCSTR lpszFileName
  362. )
  363. /*++
  364. Purpose:
  365. Open a file. If fails, return NULL.
  366. Parameters:
  367. [IN] lpszFileName - file name to open.
  368. Return value:
  369. Pointer to the file.
  370. --*/
  371. {
  372. return UTIL_OpenFileForMode(lpszFileName, "rb");
  373. }
  374. FILE *
  375. UTIL_OpenFileForMode(
  376. LPCSTR lpszFileName,
  377. LPCSTR szMode
  378. )
  379. /*++
  380. Purpose:
  381. Open a file. If fails, return NULL.
  382. Parameters:
  383. [IN] lpszFileName - file name to open.
  384. [IN] szMode - file open mode.
  385. Return value:
  386. Pointer to the file.
  387. --*/
  388. {
  389. //
  390. // If lpszFileName is an absolute path, use its last element as filename
  391. //
  392. if (UTIL_IsAbsolutePath(lpszFileName))
  393. {
  394. char *temp = strdup(lpszFileName), *filename = temp;
  395. FILE *fp = NULL;
  396. for (char *next = strpbrk(filename, path_separators); next; next = strpbrk(filename = next + 1, path_separators));
  397. if (*filename)
  398. {
  399. filename[-1] = '\0';
  400. fp = UTIL_OpenFileAtPathForMode(*temp ? temp : "/", filename, szMode);
  401. }
  402. free(temp);
  403. return fp;
  404. }
  405. return UTIL_OpenFileAtPathForMode(gConfig.pszGamePath, lpszFileName, szMode);
  406. }
  407. FILE *
  408. UTIL_OpenFileAtPath(
  409. LPCSTR lpszPath,
  410. LPCSTR lpszFileName
  411. )
  412. {
  413. return UTIL_OpenFileAtPathForMode(lpszPath, lpszFileName, "rb");
  414. }
  415. FILE *
  416. UTIL_OpenFileAtPathForMode(
  417. LPCSTR lpszPath,
  418. LPCSTR lpszFileName,
  419. LPCSTR szMode
  420. )
  421. {
  422. //
  423. // Construct full path according to lpszPath and lpszFileName
  424. //
  425. const char *path = UTIL_GetFullPathName(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, lpszPath, lpszFileName);
  426. return path ? fopen(path, szMode) : NULL;
  427. }
  428. VOID
  429. UTIL_CloseFile(
  430. FILE *fp
  431. )
  432. /*++
  433. Purpose:
  434. Close a file.
  435. Parameters:
  436. [IN] fp - file handle to be closed.
  437. Return value:
  438. None.
  439. --*/
  440. {
  441. if (fp != NULL)
  442. {
  443. fclose(fp);
  444. }
  445. }
  446. const char *
  447. UTIL_GetFullPathName(
  448. char *buffer,
  449. size_t buflen,
  450. const char *basepath,
  451. const char *subpath
  452. )
  453. {
  454. if (!buffer || !basepath || !subpath || buflen == 0) return NULL;
  455. int baselen = strlen(basepath), sublen = strlen(subpath);
  456. if (sublen == 0) return NULL;
  457. char *_base = strdup(basepath), *_sub = strdup(subpath);
  458. const char *result = NULL;
  459. if (access(UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, _base, _sub), 0) == 0)
  460. {
  461. result = internal_buffer[PAL_MAX_GLOBAL_BUFFERS];
  462. }
  463. #if !defined(PAL_FILESYSTEM_IGNORE_CASE) || !PAL_FILESYSTEM_IGNORE_CASE
  464. if (result == NULL)
  465. {
  466. size_t pos = strspn(_sub, path_separators);
  467. if (pos < sublen)
  468. {
  469. char *start = _sub + pos;
  470. char *end = strpbrk(start, path_separators);
  471. if (end) *end = '\0';
  472. //
  473. // try to find the matching file in the directory.
  474. //
  475. struct dirent **list;
  476. int n = scandir(_base, &list, 0, alphasort);
  477. while (n-- > 0)
  478. {
  479. if (!result && strcasecmp(list[n]->d_name, start) == 0)
  480. {
  481. result = UTIL_CombinePath(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, 2, _base, list[n]->d_name);
  482. if (end)
  483. result = UTIL_GetFullPathName(internal_buffer[PAL_MAX_GLOBAL_BUFFERS], PAL_GLOBAL_BUFFER_SIZE, result, end + 1);
  484. else if (access(result, 0) != 0)
  485. result = NULL;
  486. }
  487. free(list[n]);
  488. }
  489. free(list);
  490. }
  491. }
  492. #endif
  493. if (result != NULL)
  494. {
  495. result = (char *)memmove(buffer, result, min(buflen, strlen(result)));
  496. }
  497. free(_base);
  498. free(_sub);
  499. return result;
  500. }
  501. const char *
  502. UTIL_CombinePath(
  503. char *buffer,
  504. size_t buflen,
  505. int numentry,
  506. ...
  507. )
  508. {
  509. if (buffer && buflen > 0 && numentry > 0)
  510. {
  511. const char *retval = buffer;
  512. va_list argptr;
  513. va_start(argptr, numentry);
  514. for (int i = 0; i < numentry && buflen > 1; i++)
  515. {
  516. const char *path = va_arg(argptr, const char *);
  517. int path_len = path ? strlen(path) : 0;
  518. int append_delim = (i < numentry - 1 && path_len > 0 && !isseparator(path[path_len - 1]));
  519. for (int is_sep = 0, j = 0; j < path_len && buflen > (size_t)append_delim + 1; j++)
  520. {
  521. //
  522. // Skip continuous path separators
  523. //
  524. if (isseparator(path[j]))
  525. {
  526. if (is_sep)
  527. continue;
  528. else
  529. is_sep = 1;
  530. }
  531. else
  532. {
  533. is_sep = 0;
  534. }
  535. *buffer++ = path[j];
  536. buflen--;
  537. }
  538. //
  539. // Make sure a path delimeter is append to the destination if this is not the last entry
  540. //
  541. if (append_delim)
  542. {
  543. *buffer++ = '/';
  544. buflen--;
  545. }
  546. }
  547. va_end(argptr);
  548. *buffer = '\0';
  549. return retval;
  550. }
  551. else
  552. {
  553. return NULL;
  554. }
  555. }
  556. char *
  557. UTIL_GlobalBuffer(
  558. int index
  559. )
  560. {
  561. return (index >= 0 && index < PAL_MAX_GLOBAL_BUFFERS) ? internal_buffer[index] : NULL;
  562. }
  563. #if !defined(PAL_HAS_PLATFORM_SPECIFIC_UTILS)
  564. BOOL
  565. UTIL_GetScreenSize(
  566. DWORD *pdwScreenWidth,
  567. DWORD *pdwScreenHeight
  568. )
  569. {
  570. return FALSE;
  571. }
  572. BOOL
  573. UTIL_IsAbsolutePath(
  574. LPCSTR lpszFileName
  575. )
  576. {
  577. return FALSE;
  578. }
  579. INT
  580. UTIL_Platform_Init(
  581. int argc,
  582. char* argv[]
  583. )
  584. {
  585. gConfig.fLaunchSetting = FALSE;
  586. return 0;
  587. }
  588. VOID
  589. UTIL_Platform_Quit(
  590. VOID
  591. )
  592. {
  593. }
  594. #endif
  595. /*
  596. * Logging utilities
  597. */
  598. #ifndef PAL_LOG_BUFFER_SIZE
  599. # define PAL_LOG_BUFFER_SIZE 4096
  600. #endif
  601. #define PAL_LOG_BUFFER_EXTRA_SIZE 32
  602. static LOGCALLBACK _log_callbacks[PAL_LOG_MAX_OUTPUTS];
  603. static LOGLEVEL _log_callback_levels[PAL_LOG_MAX_OUTPUTS];
  604. static char _log_buffer[PAL_LOG_BUFFER_SIZE + PAL_LOG_BUFFER_EXTRA_SIZE];
  605. static const char * const _loglevel_str[] = {
  606. "[VERBOSE]",
  607. " [DEBUG]",
  608. " [INFO]",
  609. "[WARNING]",
  610. " [ERROR]",
  611. " [FATAL]",
  612. };
  613. int
  614. UTIL_LogAddOutputCallback(
  615. LOGCALLBACK callback,
  616. LOGLEVEL loglevel
  617. )
  618. {
  619. if (!callback) return -1;
  620. // De-duplication
  621. for (int i = 0; i < PAL_LOG_MAX_OUTPUTS; i++)
  622. {
  623. if (!_log_callbacks[i])
  624. {
  625. _log_callbacks[i] = callback;
  626. }
  627. if (_log_callbacks[i] == callback)
  628. {
  629. _log_callback_levels[i] = loglevel;
  630. return i;
  631. }
  632. }
  633. return -1;
  634. }
  635. void
  636. UTIL_LogRemoveOutputCallback(
  637. int id
  638. )
  639. {
  640. if (id < 0 || id >= PAL_LOG_MAX_OUTPUTS) return;
  641. while (id < PAL_LOG_MAX_OUTPUTS - 1)
  642. {
  643. _log_callbacks[id] = _log_callbacks[id + 1];
  644. _log_callback_levels[id] = _log_callback_levels[id + 1];
  645. id++;
  646. }
  647. _log_callbacks[id] = NULL;
  648. _log_callback_levels[id] = LOGLEVEL_MIN;
  649. }
  650. void
  651. UTIL_LogOutput(
  652. LOGLEVEL level,
  653. const char *fmt,
  654. ...
  655. )
  656. {
  657. va_list va;
  658. time_t tv = time(NULL);
  659. struct tm *tmval = localtime(&tv);
  660. int id, n;
  661. if (level < gConfig.iLogLevel || !_log_callbacks[0]) return;
  662. if (level > LOGLEVEL_MAX) level = LOGLEVEL_MAX;
  663. snprintf(_log_buffer, PAL_LOG_BUFFER_EXTRA_SIZE,
  664. "%04d-%02d-%02d %02d:%02d:%02d %s: ",
  665. tmval->tm_year + 1900, tmval->tm_mon, tmval->tm_mday,
  666. tmval->tm_hour, tmval->tm_min, tmval->tm_sec,
  667. _loglevel_str[level]);
  668. va_start(va, fmt);
  669. n = vsnprintf(_log_buffer + PAL_LOG_BUFFER_EXTRA_SIZE - 1, PAL_LOG_BUFFER_SIZE, fmt, va);
  670. va_end(va);
  671. n = (n == -1) ? PAL_LOG_BUFFER_EXTRA_SIZE + PAL_LOG_BUFFER_SIZE - 1 : n + PAL_LOG_BUFFER_EXTRA_SIZE;
  672. _log_buffer[n--] = '\0';
  673. if (_log_buffer[n] != '\n') _log_buffer[n] = '\n';
  674. for(id = 0; id < PAL_LOG_MAX_OUTPUTS && _log_callbacks[id]; id++)
  675. {
  676. if (level >= _log_callback_levels[id])
  677. {
  678. _log_callbacks[id](level, _log_buffer, _log_buffer + PAL_LOG_BUFFER_EXTRA_SIZE - 1);
  679. }
  680. }
  681. }
  682. void
  683. UTIL_LogSetLevel(
  684. LOGLEVEL minlevel
  685. )
  686. {
  687. if (minlevel < LOGLEVEL_MIN)
  688. gConfig.iLogLevel = LOGLEVEL_MIN;
  689. else if (minlevel > LOGLEVEL_MAX)
  690. gConfig.iLogLevel = LOGLEVEL_MAX;
  691. else
  692. gConfig.iLogLevel = minlevel;
  693. }
  694. void
  695. UTIL_LogToFile(
  696. LOGLEVEL _,
  697. const char *string,
  698. const char *__
  699. )
  700. {
  701. FILE *fp = fopen(gConfig.pszLogFile, "a");
  702. if (fp)
  703. {
  704. fputs(string, fp);
  705. fclose(fp);
  706. }
  707. }