porcupine/demo/c/dr_libs/old/dr.h
Go to the documentation of this file.
1 // Public Domain. See "unlicense" statement at the end of this file.
2 
3 // USAGE
4 //
5 // This is a single-file library. To use it, do something like the following in one .c file.
6 // #define DR_IMPLEMENTATION
7 // #include "dr.h"
8 //
9 // You can then #include dr.h in other parts of the program as you would with any other header file.
10 //
11 //
12 //
13 // OPTIONS
14 //
15 // #define DR_UTIL_WIN32_USE_CRITICAL_SECTION_MUTEX
16 // - Use the Win32 CRITICAL_SECTION API for mutex objects.
17 
18 #ifndef dr_util_h
19 #define dr_util_h
20 
21 #ifdef __cplusplus
22 extern "C" {
23 #endif
24 
25 
26 // Disable MSVC compatibility if we're compiling with it.
27 #if defined(_MSC_VER) || defined(__MINGW32__)
28  #define DR_NO_MSVC_COMPAT
29 #endif
30 
31 #if defined(_MSC_VER)
32 #define DR_INLINE static __inline
33 #else
34 #define DR_INLINE static inline
35 #endif
36 
37 
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <stdio.h>
42 
43 #ifndef DR_NO_MSVC_COMPAT
44 #include <errno.h>
45 #endif
46 
47 #ifndef DR_SIZED_TYPES_DEFINED
48 #define DR_SIZED_TYPES_DEFINED
49 #if defined(_MSC_VER) && _MSC_VER < 1600
50 typedef signed char dr_int8;
51 typedef unsigned char dr_uint8;
52 typedef signed short dr_int16;
53 typedef unsigned short dr_uint16;
54 typedef signed int dr_int32;
55 typedef unsigned int dr_uint32;
56 typedef signed __int64 dr_int64;
57 typedef unsigned __int64 dr_uint64;
58 #else
59 #include <stdint.h>
60 typedef int8_t dr_int8;
61 typedef uint8_t dr_uint8;
62 typedef int16_t dr_int16;
63 typedef uint16_t dr_uint16;
64 typedef int32_t dr_int32;
65 typedef uint32_t dr_uint32;
66 typedef int64_t dr_int64;
67 typedef uint64_t dr_uint64;
68 #endif
71 #define DR_TRUE 1
72 #define DR_FALSE 0
73 #endif
74 
75 
76 #define STRINGIFY(x) #x
77 #define TOSTRING(x) STRINGIFY(x)
78 
79 
81 // Annotations
82 
83 #ifndef IN
84 #define IN
85 #endif
86 
87 #ifndef OUT
88 #define OUT
89 #endif
90 
91 #ifndef UNUSED
92 #define UNUSED(x) ((void)(x))
93 #endif
94 
95 
97 // min/max/clamp
98 
99 #ifndef dr_min
100 #define dr_min(x, y) (((x) < (y)) ? (x) : (y))
101 #endif
102 
103 #ifndef dr_max
104 #define dr_max(x, y) (((x) > (y)) ? (x) : (y))
105 #endif
106 
107 #ifndef dr_clamp
108 #define dr_clamp(x, low, high) (dr_max(low, dr_min(x, high)))
109 #endif
110 
111 #ifndef dr_round_up
112 #define dr_round_up(x, multiple) ((((x) + ((multiple) - 1)) / (multiple)) * (multiple))
113 #endif
114 
115 #ifndef dr_round_up_signed
116 #define dr_round_up_signed(x, multiple) ((((x) + (((x) >= 0)*((multiple) - 1))) / (multiple)) * (multiple))
117 #endif
118 
120 {
121  --value;
122  value = (value >> 1) | value;
123  value = (value >> 2) | value;
124  value = (value >> 4) | value;
125  value = (value >> 8) | value;
126  value = (value >> 16) | value;
127  return value + 1;
128 }
129 
130 
131 #define dr_abs(x) (((x) < 0) ? (-(x)) : (x))
132 
133 
135 // MSVC Compatibility
136 
137 // A basic implementation of MSVC's strcpy_s().
138 int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src);
139 
140 // A basic implementation of MSVC's strncpy_s().
141 int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count);
142 
143 // A basic implementation of MSVC's strcat_s().
144 int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src);
145 
146 // A basic implementation of MSVC's strncat_s()
147 int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count);
148 
149 // A basic implementation of MSVC's _atoi_s()
150 int dr_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix);
151 
152 #ifndef DR_NO_MSVC_COMPAT
153 #ifndef _TRUNCATE
154 #define _TRUNCATE ((size_t)-1)
155 #endif
156 
157 DR_INLINE int strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
158 {
159  return dr_strcpy_s(dst, dstSizeInBytes, src);
160 }
161 
162 DR_INLINE int strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
163 {
164  return dr_strncpy_s(dst, dstSizeInBytes, src, count);
165 }
166 
167 DR_INLINE int strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
168 {
169  return dr_strcat_s(dst, dstSizeInBytes, src);
170 }
171 
172 DR_INLINE int strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
173 {
174  return dr_strncat_s(dst, dstSizeInBytes, src, count);
175 }
176 
177 #ifndef __MINGW32__
178 DR_INLINE int _stricmp(const char* string1, const char* string2)
179 {
180  return strcasecmp(string1, string2);
181 }
182 #endif
183 
184 DR_INLINE int _itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
185 {
186  return dr_itoa_s(value, dst, dstSizeInBytes, radix);
187 }
188 #endif
189 
190 
192 // String Helpers
193 
194 // Determines if the given character is whitespace.
196 
198 void dr_strrmchar(char* str, char c);
199 
201 const char* dr_first_non_whitespace(const char* str);
202 
203 static inline const char* dr_ltrim(const char* str) { return dr_first_non_whitespace(str); }
204 static const char* dr_rtrim(const char* str);
205 
207 void dr_trim(char* str);
208 
210 const char* dr_first_whitespace(const char* str);
211 
213 const char* dr_next_line(const char* str);
214 
216 size_t dr_copy_line(const char* str, char* lineOut, size_t lineOutSize);
217 
218 // A slow string replace function. Free the returned string with free().
219 char* dr_string_replace(const char* src, const char* query, const char* replacement);
220 
221 // Replaces an ASCII character with another in the given string.
222 void dr_string_replace_ascii(char* src, char c, char replacement);
223 
224 
226 // Unicode Utilities
227 
237 DR_INLINE int dr_utf32_to_utf16_ch(unsigned int utf32, unsigned short utf16[2])
238 {
239  if (utf16 == NULL) {
240  return 0;
241  }
242 
243  if (utf32 < 0xD800 || (utf32 >= 0xE000 && utf32 <= 0xFFFF))
244  {
245  utf16[0] = (unsigned short)utf32;
246  utf16[1] = 0;
247  return 1;
248  }
249  else
250  {
251  if (utf32 >= 0x10000 && utf32 <= 0x10FFFF)
252  {
253  utf16[0] = (unsigned short)(0xD7C0 + (unsigned short)(utf32 >> 10));
254  utf16[1] = (unsigned short)(0xDC00 + (unsigned short)(utf32 & 0x3FF));
255  return 2;
256  }
257  else
258  {
259  // Invalid.
260  utf16[0] = 0;
261  utf16[1] = 0;
262  return 0;
263  }
264  }
265 }
266 
268 DR_INLINE unsigned int dr_utf16_to_utf32_ch(unsigned short utf16[2])
269 {
270  if (utf16 == NULL) {
271  return 0;
272  }
273 
274  if (utf16[0] < 0xD800 || utf16[0] > 0xDFFF)
275  {
276  return utf16[0];
277  }
278  else
279  {
280  if ((utf16[0] & 0xFC00) == 0xD800 && (utf16[1] & 0xFC00) == 0xDC00)
281  {
282  return ((unsigned int)utf16[0] << 10) + utf16[1] - 0x35FDC00;
283  }
284  else
285  {
286  // Invalid.
287  return 0;
288  }
289  }
290 }
291 
293 DR_INLINE unsigned int dr_utf16pair_to_utf32_ch(unsigned short utf160, unsigned short utf161)
294 {
295  unsigned short utf16[2];
296  utf16[0] = utf160;
297  utf16[1] = utf161;
298  return dr_utf16_to_utf32_ch(utf16);
299 }
300 
301 
303 // Aligned Allocations
304 
305 #ifndef DRUTIL_NO_ALIGNED_MALLOC
306 DR_INLINE void* dr_aligned_malloc(size_t alignment, size_t size)
307 {
308 #if defined(_WIN32) || defined(_WIN64)
309  return _aligned_malloc(size, alignment);
310 #else
311  void* pResult;
312  if (posix_memalign(&pResult, alignment, size) == 0) {
313  return pResult;
314  }
315 
316  return 0;
317 #endif
318 }
319 
320 DR_INLINE void dr_aligned_free(void* ptr)
321 {
322 #if defined(_WIN32) || defined(_WIN64)
323  _aligned_free(ptr);
324 #else
325  free(ptr);
326 #endif
327 }
328 #endif // !DRUTIL_NO_ALIGNED_MALLOC
329 
330 
331 
333 // Key/Value Pair Parsing
334 
335 typedef size_t (* dr_key_value_read_proc) (void* pUserData, void* pDataOut, size_t bytesToRead);
336 typedef void (* dr_key_value_pair_proc) (void* pUserData, const char* key, const char* value);
337 typedef void (* dr_key_value_error_proc)(void* pUserData, const char* message, unsigned int line);
338 
356 
357 // This will only return DR_FALSE if the file fails to open. It will still return DR_TRUE even if there are syntax error or whatnot.
358 dr_bool32 dr_parse_key_value_pairs_from_file(const char* filePath, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData);
359 
360 
361 
363 // Basic Tokenizer
364 
379 const char* dr_next_token(const char* tokens, char* tokenOut, size_t tokenOutSize);
380 
381 
382 
384 // Known Folders
385 
390 dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize);
391 
396 dr_bool32 dr_get_executable_directory_path(char* pathOut, size_t pathOutSize);
397 
402 dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize);
403 
408 dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize);
409 
411 const char* dr_get_current_directory(char* pathOut, size_t pathOutSize);
412 
414 dr_bool32 dr_set_current_directory(const char* path);
415 
416 
417 
419 // Basic File Management
420 
421 // Callback function for file iteration.
422 typedef dr_bool32 (* dr_iterate_files_proc)(const char* filePath, void* pUserData);
423 
424 
425 // Helper for opening a stdio FILE.
426 FILE* dr_fopen(const char* fileName, const char* openMode);
427 
428 // Helper for creating an empty file.
429 dr_bool32 dr_create_empty_file(const char* fileName, dr_bool32 failIfExists);
430 
431 // Retrieves the file data of the given file. Free the returned pointer with dr_free_file_data().
432 void* dr_open_and_read_file(const char* filePath, size_t* pFileSizeOut);
433 
434 // Retrieves the file data of the given file as a null terminated string. Free the returned pointer with dr_free_file_data(). The
435 // returned file size is the length of the string not including the null terminator.
436 char* dr_open_and_read_text_file(const char* filePath, size_t* pFileSizeOut);
437 
438 // Creates a new file with the given data.
439 dr_bool32 dr_open_and_write_file(const char* filePath, const void* pData, size_t dataSize);
440 
441 // Creates a new file with the given string.
442 dr_bool32 dr_open_and_write_text_file(const char* filePath, const char* text);
443 
444 // Frees the file data returned by dr_open_and_read_file().
445 void dr_free_file_data(void* valueReturnedByOpenAndReadFile);
446 
447 // Determines whether or not the given file path is to a file.
448 //
449 // This will return DR_FALSE if the path points to a directory.
450 dr_bool32 dr_file_exists(const char* filePath);
451 
452 // Determines whether or not the given file path points to a directory.
453 //
454 // This will return DR_FALSE if the path points to a file.
455 dr_bool32 dr_directory_exists(const char* directoryPath);
456 static inline dr_bool32 dr_is_directory(const char* directoryPath) { return dr_directory_exists(directoryPath); }
457 
458 // Moves a file.
459 //
460 // This uses rename() on POSIX platforms and MoveFileEx(oldPath, newPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) on windows platforms.
461 dr_bool32 dr_move_file(const char* oldPath, const char* newPath);
462 
463 // Copies a file.
464 dr_bool32 dr_copy_file(const char* srcPath, const char* dstPath, dr_bool32 failIfExists);
465 
466 // Determines if the given file is read only.
467 dr_bool32 dr_is_file_read_only(const char* filePath);
468 
469 // Retrieves the last modified time of the file at the given path.
470 dr_uint64 dr_get_file_modified_time(const char* filePath);
471 
472 // Deletes the file at the given path.
473 //
474 // This uses remove() on POSIX platforms and DeleteFile() on Windows platforms.
475 dr_bool32 dr_delete_file(const char* filePath);
476 
477 // Cross-platform wrapper for creating a directory.
478 dr_bool32 dr_mkdir(const char* directoryPath);
479 
480 // Recursively creates a directory.
481 dr_bool32 dr_mkdir_recursive(const char* directoryPath);
482 
483 // Iterates over every file and folder of the given directory.
484 dr_bool32 dr_iterate_files(const char* directory, dr_bool32 recursive, dr_iterate_files_proc proc, void* pUserData);
485 
486 
488 // DPI Awareness
489 
490 #if defined(_WIN32)
491 void dr_win32_make_dpi_aware();
493 
495 void dr_win32_get_base_dpi(int* pDPIXOut, int* pDPIYOut);
496 
498 void dr_win32_get_system_dpi(int* pDPIXOut, int* pDPIYOut);
499 
506 void dr_win32_get_monitor_dpi(int monitor, int* pDPIXOut, int* pDPIYOut);
507 
512 int dr_win32_get_monitor_count();
513 #endif
514 
515 
516 
518 // Date / Time
519 
521 time_t dr_now();
522 
524 void dr_datetime_short(time_t t, char* strOut, unsigned int strOutSize);
525 
526 // Returns a date string in YYYYMMDD format.
527 void dr_date_YYYYMMDD(time_t t, char* strOut, unsigned int strOutSize);
528 
529 
530 
532 // Command Line
533 //
534 // The command line functions below are just simple iteration functions. This command line system is good for
535 // simple command lines, but probably not the best for programs requiring complex command line work.
536 //
537 // For argv style command lines, parse_cmdline() will run without any heap allocations. With a Win32 style
538 // command line there will be one malloc() per call fo parse_cmdline(). This is the only function that will do
539 // a malloc().
540 //
541 // Below is an example:
542 //
543 // dr_cmdline cmdline;
544 // if (dr_init_cmdline(&cmdline, argc, argv)) {
545 // dr_parse_cmdline(&cmdline, my_cmdline_handler, pMyUserData);
546 // }
547 //
548 // void my_cmdline_handler(const char* key, const char* value, void* pUserData)
549 // {
550 // // Do something...
551 // }
552 //
553 //
554 // When parsing the command line, the first iteration will be the program path and the key will be "[path]".
555 //
556 // For segments such as "-abcd", the callback will be called for "a", "b", "c", "d" individually, with the
557 // value set to NULL.
558 //
559 // For segments such as "--server", the callback will be called for "server", with the value set to NULL.
560 //
561 // For segments such as "-f file.txt", the callback will be called with the key set to "f" and the value set
562 // to "file.txt".
563 //
564 // For segments such as "-f file1.txt file2.txt", the callback will be called twice, once for file1.txt and
565 // again for file2.txt, with with the key set to "f" in both cases.
566 //
567 // For segments where there is no leading key, the values will be posted as annonymous (key set to NULL). An example
568 // is "my_program.exe file1.txt file2.txt", in which case the first iteration will be the program path, the second iteration
569 // will be "file1.txt", with the key set to NULL. The third iteration will be "file2.txt" with the key set to NULL.
570 //
571 // For segments such as "-abcd file.txt", "a", "b", "c", "d" will be sent with NULL values, and "file.txt" will be
572 // posted with a NULL key.
573 
574 typedef struct dr_cmdline dr_cmdline;
576 {
577  // argv style.
578  int argc;
579  char** argv;
580 
581  // Win32 style
582  const char* win32;
583 };
584 
585 typedef dr_bool32 dr_cmdline_parse_proc(const char* key, const char* value, void* pUserData);
586 
587 
589 dr_bool32 dr_init_cmdline(dr_cmdline* pCmdLine, int argc, char** argv);
590 
592 dr_bool32 dr_init_cmdline_win32(dr_cmdline* pCmdLine, const char* args);
593 
595 void dr_parse_cmdline(dr_cmdline* pCmdLine, dr_cmdline_parse_proc callback, void* pUserData);
596 
598 dr_bool32 dr_cmdline_key_exists(dr_cmdline* pCmdLine, const char* key);
599 
600 // Convers the given command line object to argc/argv style.
601 //
602 // Returns the argument count. Returns 0 if an error occurs. Free "argvOut" with dr_free_argv().
603 int dr_cmdline_to_argv(dr_cmdline* pCmdLine, char*** argvOut);
604 
605 // Converts a WinMain style command line to argc/argv.
606 //
607 // Returns the argument count. Returns 0 if an error occurs. Free "argvOut" with dr_free_argv().
608 int dr_winmain_to_argv(const char* cmdlineWinMain, char*** argvOut);
609 
610 // Frees the argc/argv command line that was generated by dr.h
611 void dr_free_argv(char** argv);
612 
613 
614 
615 
617 // Threading
618 
623 void dr_sleep(unsigned int milliseconds);
624 void dr_yield();
625 
627 unsigned int dr_get_logical_processor_count();
628 
629 
631 typedef void* dr_thread;
632 typedef int (* dr_thread_entry_proc)(void* pData);
633 
641 dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData);
642 
654 void dr_delete_thread(dr_thread thread);
655 
657 void dr_wait_thread(dr_thread thread);
658 
661 
662 
663 
665 typedef void* dr_mutex;
666 
672 
674 void dr_delete_mutex(dr_mutex mutex);
675 
677 void dr_lock_mutex(dr_mutex mutex);
678 
680 void dr_unlock_mutex(dr_mutex mutex);
681 
682 
683 
685 typedef void* dr_semaphore;
686 
691 dr_semaphore dr_create_semaphore(int initialValue);
692 
694 void dr_delete_semaphore(dr_semaphore semaphore);
695 
698 
701 
702 
703 
705 // Timing
706 
707 typedef struct
708 {
710 } dr_timer;
711 
712 // Initializes a high-resolution timer.
713 void dr_timer_init(dr_timer* pTimer);
714 
715 // Ticks the timer and returns the number of seconds since the previous tick.
716 //
717 // The maximum return value is about 140 years or so.
718 double dr_timer_tick(dr_timer* pTimer);
719 
720 
721 
722 
724 // Random
725 
726 // Generates a random double between 0 and 1. This is bassed of C standard rand().
727 double dr_randd();
728 
729 // Generates a random float between 0 and 1. This is based off C standard rand().
730 float dr_randf();
731 
732 
733 
735 // User Accounts and Process Management
736 
737 // Retrieves the user name of the user running the application.
738 size_t dr_get_username(char* usernameOut, size_t usernameOutSize);
739 
740 // Retrieves the ID of the current process.
741 unsigned int dr_get_process_id();
742 
743 
744 
745 
747 // Miscellaneous Stuff.
748 
749 // Helper for clearing the given object to 0.
750 #define dr_zero_object(pObject) memset(pObject, 0, sizeof(*pObject));
751 
752 // Converts an ASCII hex character to it's integral equivalent. Returns DR_FALSE if it's not a valid hex character.
753 dr_bool32 dr_hex_char_to_uint(char ascii, unsigned int* out);
754 
755 
757 // C++ Specific
758 
759 #ifdef __cplusplus
760 
761 // Use this to prevent objects of the given class or struct from being copied. This is also useful for eliminating some
762 // compiler warnings.
763 //
764 // Note for structs - this sets the access mode to private, so place this at the end of the declaration.
765 #define NO_COPY(classname) \
766  private: \
767  classname(const classname &); \
768  classname & operator=(const classname &);
769 
770 
771 #ifndef DR_NO_MSVC_COMPAT
772 extern "C++"
773 {
774 
775 template <size_t dstSizeInBytes>
776 int strcpy_s(char (&dst)[dstSizeInBytes], const char* src)
777 {
778  return strcpy_s(dst, dstSizeInBytes, src);
779 }
780 
781 template <size_t dstSizeInBytes>
782 int strncpy_s(char (&dst)[dstSizeInBytes], const char* src, size_t count)
783 {
784  return strncpy_s(dst, dstSizeInBytes, src, count);
785 }
786 
787 template <size_t dstSizeInBytes>
788 int strcat_s(char (&dst)[dstSizeInBytes], const char* src)
789 {
790  return strcat_s(dst, dstSizeInBytes, src);
791 }
792 
793 template <size_t dstSizeInBytes>
794 int strncat_s(char (&dst)[dstSizeInBytes], const char* src, size_t count)
795 {
796  return strncat_s(dst, dstSizeInBytes, src, count);
797 }
798 
799 }
800 #endif
801 
802 #endif
803 
804 
805 
806 #ifdef __cplusplus
807 }
808 #endif
809 
810 #endif //dr_util_h
811 
812 
813 
815 //
816 // IMPLEMENTATION
817 //
819 
820 #ifdef DR_IMPLEMENTATION
821 #include <assert.h>
822 #include <stdarg.h>
823 #include <stdio.h>
824 #include <string.h> // For memmove()
825 #include <errno.h>
826 
827 #ifdef _WIN32
828 #include <windows.h>
829 #else
830 #include <unistd.h>
831 #include <sys/syscall.h>
832 #include <sys/types.h>
833 #include <pthread.h>
834 #include <fcntl.h>
835 #include <semaphore.h>
836 #include <dirent.h>
837 #endif
838 
839 int dr_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
840 {
841  if (dst == 0) {
842  return EINVAL;
843  }
844  if (dstSizeInBytes == 0) {
845  return ERANGE;
846  }
847  if (src == 0) {
848  dst[0] = '\0';
849  return EINVAL;
850  }
851 
852  size_t i;
853  for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
854  dst[i] = src[i];
855  }
856 
857  if (i < dstSizeInBytes) {
858  dst[i] = '\0';
859  return 0;
860  }
861 
862  dst[0] = '\0';
863  return ERANGE;
864 }
865 
866 int dr_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
867 {
868  if (dst == 0) {
869  return EINVAL;
870  }
871  if (dstSizeInBytes == 0) {
872  return ERANGE;
873  }
874  if (src == 0) {
875  dst[0] = '\0';
876  return EINVAL;
877  }
878 
879  size_t maxcount = count;
880  if (count == ((size_t)-1) || count >= dstSizeInBytes) { // -1 = _TRUNCATE
881  maxcount = dstSizeInBytes - 1;
882  }
883 
884  size_t i;
885  for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
886  dst[i] = src[i];
887  }
888 
889  if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
890  dst[i] = '\0';
891  return 0;
892  }
893 
894  dst[0] = '\0';
895  return ERANGE;
896 }
897 
898 int dr_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
899 {
900  if (dst == 0) {
901  return EINVAL;
902  }
903  if (dstSizeInBytes == 0) {
904  return ERANGE;
905  }
906  if (src == 0) {
907  dst[0] = '\0';
908  return EINVAL;
909  }
910 
911  char* dstorig = dst;
912 
913  while (dstSizeInBytes > 0 && dst[0] != '\0') {
914  dst += 1;
915  dstSizeInBytes -= 1;
916  }
917 
918  if (dstSizeInBytes == 0) {
919  return EINVAL; // Unterminated.
920  }
921 
922 
923  while (dstSizeInBytes > 0 && src[0] != '\0') {
924  *dst++ = *src++;
925  dstSizeInBytes -= 1;
926  }
927 
928  if (dstSizeInBytes > 0) {
929  dst[0] = '\0';
930  } else {
931  dstorig[0] = '\0';
932  return ERANGE;
933  }
934 
935  return 0;
936 }
937 
938 int dr_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
939 {
940  if (dst == 0) {
941  return EINVAL;
942  }
943  if (dstSizeInBytes == 0) {
944  return ERANGE;
945  }
946  if (src == 0) {
947  return EINVAL;
948  }
949 
950  char* dstorig = dst;
951 
952  while (dstSizeInBytes > 0 && dst[0] != '\0') {
953  dst += 1;
954  dstSizeInBytes -= 1;
955  }
956 
957  if (dstSizeInBytes == 0) {
958  return EINVAL; // Unterminated.
959  }
960 
961 
962  if (count == ((size_t)-1)) { // _TRUNCATE
963  count = dstSizeInBytes - 1;
964  }
965 
966  while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0)
967  {
968  *dst++ = *src++;
969  dstSizeInBytes -= 1;
970  count -= 1;
971  }
972 
973  if (dstSizeInBytes > 0) {
974  dst[0] = '\0';
975  } else {
976  dstorig[0] = '\0';
977  return ERANGE;
978  }
979 
980  return 0;
981 }
982 
983 int dr_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
984 {
985  if (dst == NULL || dstSizeInBytes == 0) {
986  return EINVAL;
987  }
988  if (radix < 2 || radix > 36) {
989  dst[0] = '\0';
990  return EINVAL;
991  }
992 
993  int sign = (value < 0 && radix == 10) ? -1 : 1; // The negative sign is only used when the base is 10.
994 
995  unsigned int valueU;
996  if (value < 0) {
997  valueU = -value;
998  } else {
999  valueU = value;
1000  }
1001 
1002  char* dstEnd = dst;
1003  do
1004  {
1005  int remainder = valueU % radix;
1006  if (remainder > 9) {
1007  *dstEnd = (char)((remainder - 10) + 'a');
1008  } else {
1009  *dstEnd = (char)(remainder + '0');
1010  }
1011 
1012  dstEnd += 1;
1013  dstSizeInBytes -= 1;
1014  valueU /= radix;
1015  } while (dstSizeInBytes > 0 && valueU > 0);
1016 
1017  if (dstSizeInBytes == 0) {
1018  dst[0] = '\0';
1019  return EINVAL; // Ran out of room in the output buffer.
1020  }
1021 
1022  if (sign < 0) {
1023  *dstEnd++ = '-';
1024  dstSizeInBytes -= 1;
1025  }
1026 
1027  if (dstSizeInBytes == 0) {
1028  dst[0] = '\0';
1029  return EINVAL; // Ran out of room in the output buffer.
1030  }
1031 
1032  *dstEnd = '\0';
1033 
1034 
1035  // At this point the string will be reversed.
1036  dstEnd -= 1;
1037  while (dst < dstEnd) {
1038  char temp = *dst;
1039  *dst = *dstEnd;
1040  *dstEnd = temp;
1041 
1042  dst += 1;
1043  dstEnd -= 1;
1044  }
1045 
1046  return 0;
1047 }
1048 
1050 // String Helpers
1051 
1053 {
1054  return utf32 == ' ' || utf32 == '\t' || utf32 == '\n' || utf32 == '\v' || utf32 == '\f' || utf32 == '\r';
1055 }
1056 
1057 void dr_strrmchar(char* str, char c)
1058 {
1059  char* src = str;
1060  char* dst = str;
1061 
1062  while (src[0] != '\0')
1063  {
1064  dst[0] = src[0];
1065 
1066  if (dst[0] != c) {
1067  dst += 1;
1068  }
1069 
1070  src += 1;
1071  }
1072 
1073  dst[0] = '\0';
1074 }
1075 
1076 const char* dr_first_non_whitespace(const char* str)
1077 {
1078  if (str == NULL) {
1079  return NULL;
1080  }
1081 
1082  while (str[0] != '\0' && !(str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != '\v' && str[0] != '\f' && str[0] != '\r')) {
1083  str += 1;
1084  }
1085 
1086  return str;
1087 }
1088 
1089 const char* dr_first_whitespace(const char* str)
1090 {
1091  if (str == NULL) {
1092  return NULL;
1093  }
1094 
1095  while (str[0] != '\0' && (str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != '\v' && str[0] != '\f' && str[0] != '\r')) {
1096  str += 1;
1097  }
1098 
1099  return str;
1100 }
1101 
1102 const char* dr_rtrim(const char* str)
1103 {
1104  if (str == NULL) {
1105  return NULL;
1106  }
1107 
1108  const char* rstr = str;
1109  while (str[0] != '\0') {
1110  if (dr_is_whitespace(str[0])) {
1111  str += 1;
1112  continue;
1113  }
1114 
1115  str += 1;
1116  rstr = str;
1117  }
1118 
1119  return rstr;
1120 }
1121 
1122 void dr_trim(char* str)
1123 {
1124  if (str == NULL) {
1125  return;
1126  }
1127 
1128  const char* lstr = dr_ltrim(str);
1129  const char* rstr = dr_rtrim(lstr);
1130 
1131  if (lstr > str) {
1132  memmove(str, lstr, rstr-lstr);
1133  }
1134 
1135  str[rstr-lstr] = '\0';
1136 }
1137 
1138 const char* dr_next_line(const char* str)
1139 {
1140  if (str == NULL) {
1141  return NULL;
1142  }
1143 
1144  while (str[0] != '\0' && (str[0] != '\n' && !(str[0] == '\r' && str[1] == '\n'))) {
1145  str += 1;
1146  }
1147 
1148  if (str[0] == '\0') {
1149  return NULL;
1150  }
1151 
1152  if (str[0] == '\r') {
1153  return str + 2;
1154  }
1155 
1156  return str + 1;
1157 }
1158 
1159 size_t dr_copy_line(const char* str, char* lineOut, size_t lineOutSize)
1160 {
1161  if (str == NULL) {
1162  return 0;
1163  }
1164 
1165  if (str == NULL) {
1166  return 0;
1167  }
1168 
1169  size_t length = 0;
1170  while (lineOutSize > 0 && str[0] != '\0' && (str[0] != '\n' && !(str[0] == '\r' && str[1] == '\n'))) {
1171  *lineOut++ = *str++;
1172  lineOutSize -= 1;
1173  length += 1;
1174  }
1175 
1176  if (lineOutSize == 0) {
1177  return 0;
1178  }
1179 
1180  *lineOut = '\0';
1181  return length;
1182 }
1183 
1184 char* dr_string_replace(const char* src, const char* query, const char* replacement)
1185 {
1186  // This function could be improved, but it's good enough for now.
1187 
1188  if (src == NULL || query == NULL) {
1189  return NULL;
1190  }
1191 
1192  if (replacement == NULL) {
1193  replacement = "";
1194  }
1195 
1196  int queryLen = (int)strlen(query);
1197  int replacementLen = (int)strlen(replacement);
1198 
1199  size_t replacementCount = 0;
1200 
1201  const char* temp = src;
1202  for (;;) {
1203  temp = strstr(temp, query);
1204  if (temp == NULL) {
1205  break;
1206  }
1207 
1208  temp += queryLen;
1209  replacementCount += 1;
1210  }
1211 
1212 
1213  char* result = (char*)malloc(strlen(src) + (replacementLen - queryLen)*replacementCount + 1); // +1 for null terminator.
1214  if (result == NULL) {
1215  return NULL;
1216  }
1217 
1218  char* runningResult = result;
1219  for (size_t i = 0; i < replacementCount; ++i) {
1220  size_t len = strstr(src, query) - src;
1221  for (size_t j = 0; j < len; ++j) {
1222  runningResult[j] = src[j];
1223  }
1224 
1225  runningResult += len;
1226  for (int j = 0; j < replacementLen; ++j) {
1227  runningResult[j] = replacement[j];
1228  }
1229 
1230  runningResult += replacementLen;
1231  src += len + queryLen;
1232  }
1233 
1234  // The trailing part.
1235  strcpy_s(runningResult, strlen(src)+1, src);
1236  return result;
1237 }
1238 
1239 void dr_string_replace_ascii(char* src, char c, char replacement)
1240 {
1241  for (;;) {
1242  if (*src == '\0') {
1243  break;
1244  }
1245 
1246  if (*src == c) {
1247  *src = replacement;
1248  }
1249 
1250  src += 1;
1251  }
1252 }
1253 
1254 
1256 // Key/Value Pair Parsing
1257 
1259 {
1260  if (onRead == NULL) {
1261  return;
1262  }
1263 
1264  char pChunk[4096];
1265  size_t chunkSize = 0;
1266 
1267  unsigned int currentLine = 1;
1268 
1269  dr_bool32 moveToNextLineBeforeProcessing = DR_FALSE;
1270  dr_bool32 skipWhitespaceBeforeProcessing = DR_FALSE;
1271 
1272  // Just keep looping. We'll break from this loop when we have run out of data.
1273  for (;;)
1274  {
1275  // Start the iteration by reading as much data as we can.
1276  chunkSize = onRead(pUserData, pChunk, sizeof(pChunk));
1277  if (chunkSize == 0) {
1278  // No more data available.
1279  return;
1280  }
1281 
1282  char* pChunkEnd = pChunk + chunkSize;
1283  char* pC = pChunk; // Chunk pointer. This is as the chunk is processed.
1284 
1285  if (moveToNextLineBeforeProcessing)
1286  {
1287  move_to_next_line:
1288  while (pC < pChunkEnd && pC[0] != '\n') {
1289  pC += 1;
1290  }
1291 
1292  if (pC == pChunkEnd) {
1293  // Ran out of data. Load the next chunk and keep going.
1294  moveToNextLineBeforeProcessing = DR_TRUE;
1295  continue;
1296  }
1297 
1298  pC += 1; // pC[0] == '\n' - skip past the new line character.
1299  currentLine += 1;
1300  moveToNextLineBeforeProcessing = DR_FALSE;
1301  }
1302 
1303  if (skipWhitespaceBeforeProcessing)
1304  {
1305  while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
1306  pC += 1;
1307  }
1308 
1309  if (pC == pChunkEnd) {
1310  // Ran out of data.
1311  skipWhitespaceBeforeProcessing = DR_TRUE;
1312  continue;
1313  }
1314 
1315  skipWhitespaceBeforeProcessing = DR_FALSE;
1316  }
1317 
1318 
1319  // We loop character by character. When we run out of data, we start again.
1320  while (pC < pChunkEnd)
1321  {
1323 
1324  // Skip whitespace.
1325  while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
1326  pC += 1;
1327  }
1328 
1329  if (pC == pChunkEnd) {
1330  // Ran out of data.
1331  skipWhitespaceBeforeProcessing = DR_TRUE;
1332  continue;
1333  }
1334 
1335  if (pC[0] == '\n') {
1336  // Found the end of the line.
1337  pC += 1;
1338  currentLine += 1;
1339  continue;
1340  }
1341 
1342  if (pC[0] == '#') {
1343  // Found a comment. Move to the end of the line and continue.
1344  goto move_to_next_line;
1345  }
1346 
1347  char* pK = pC;
1348  while (pC < pChunkEnd && pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r' && pC[0] != '\n' && pC[0] != '#') {
1349  pC += 1;
1350  }
1351 
1352  if (pC == pChunkEnd)
1353  {
1354  // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
1355  if (chunkSize == sizeof(pChunk))
1356  {
1357  size_t lineSizeSoFar = pC - pK;
1358  memmove(pChunk, pK, lineSizeSoFar);
1359 
1360  chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
1361  pChunkEnd = pChunk + chunkSize;
1362 
1363  pK = pChunk;
1364  pC = pChunk + lineSizeSoFar;
1365  while (pC < pChunkEnd && pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r' && pC[0] != '\n' && pC[0] != '#') {
1366  pC += 1;
1367  }
1368  }
1369 
1370  if (pC == pChunkEnd) {
1371  if (chunkSize == sizeof(pChunk)) {
1372  if (onError) {
1373  onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
1374  }
1375 
1376  goto move_to_next_line;
1377  } else {
1378  // No more data. Just treat this one as a value-less key and return.
1379  if (onPair) {
1380  pC[0] = '\0';
1381  onPair(pUserData, pK, NULL);
1382  }
1383 
1384  return;
1385  }
1386  }
1387  }
1388 
1389  char* pKEnd = pC;
1390 
1392 
1393  // Skip whitespace.
1394  while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
1395  pC += 1;
1396  }
1397 
1398  if (pC == pChunkEnd)
1399  {
1400  // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
1401  if (chunkSize == sizeof(pChunk))
1402  {
1403  size_t lineSizeSoFar = pC - pK;
1404  memmove(pChunk, pK, lineSizeSoFar);
1405 
1406  chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
1407  pChunkEnd = pChunk + chunkSize;
1408 
1409  pKEnd = pChunk + (pKEnd - pK);
1410  pK = pChunk;
1411  pC = pChunk + lineSizeSoFar;
1412  while (pC < pChunkEnd && (pC[0] == ' ' || pC[0] == '\t' || pC[0] == '\r')) {
1413  pC += 1;
1414  }
1415  }
1416 
1417  if (pC == pChunkEnd) {
1418  if (chunkSize == sizeof(pChunk)) {
1419  if (onError) {
1420  onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
1421  }
1422 
1423  goto move_to_next_line;
1424  } else {
1425  // No more data. Just treat this one as a value-less key and return.
1426  if (onPair) {
1427  pKEnd[0] = '\0';
1428  onPair(pUserData, pK, NULL);
1429  }
1430 
1431  return;
1432  }
1433  }
1434  }
1435 
1436  if (pC[0] == '\n') {
1437  // Found the end of the line. Treat it as a value-less key.
1438  pKEnd[0] = '\0';
1439  if (onPair) {
1440  onPair(pUserData, pK, NULL);
1441  }
1442 
1443  pC += 1;
1444  currentLine += 1;
1445  continue;
1446  }
1447 
1448  if (pC[0] == '#') {
1449  // Found a comment. Treat is as a value-less key and move to the end of the line.
1450  pKEnd[0] = '\0';
1451  if (onPair) {
1452  onPair(pUserData, pK, NULL);
1453  }
1454 
1455  goto move_to_next_line;
1456  }
1457 
1458  char* pV = pC;
1459 
1460  // Find the last non-whitespace character.
1461  char* pVEnd = pC;
1462  while (pC < pChunkEnd && pC[0] != '\n' && pC[0] != '#') {
1463  if (pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r') {
1464  pVEnd = pC;
1465  }
1466 
1467  pC += 1;
1468  }
1469 
1470  if (pC == pChunkEnd)
1471  {
1472  // Ran out of data. We need to move what we have of the key to the start of the chunk buffer, and then read more data.
1473  if (chunkSize == sizeof(pChunk))
1474  {
1475  size_t lineSizeSoFar = pC - pK;
1476  memmove(pChunk, pK, lineSizeSoFar);
1477 
1478  chunkSize = lineSizeSoFar + onRead(pUserData, pChunk + lineSizeSoFar, sizeof(pChunk) - lineSizeSoFar);
1479  pChunkEnd = pChunk + chunkSize;
1480 
1481  pVEnd = pChunk + (pVEnd - pK);
1482  pKEnd = pChunk + (pKEnd - pK);
1483  pV = pChunk + (pV - pK);
1484  pK = pChunk;
1485  pC = pChunk + lineSizeSoFar;
1486  while (pC < pChunkEnd && pC[0] != '\n' && pC[0] != '#') {
1487  if (pC[0] != ' ' && pC[0] != '\t' && pC[0] != '\r') {
1488  pVEnd = pC;
1489  }
1490 
1491  pC += 1;
1492  }
1493  }
1494 
1495  if (pC == pChunkEnd) {
1496  if (chunkSize == sizeof(pChunk)) {
1497  if (onError) {
1498  onError(pUserData, "Line is too long. A single line cannot exceed 4KB.", currentLine);
1499  }
1500 
1501  goto move_to_next_line;
1502  }
1503  }
1504  }
1505 
1506 
1507  // Before null-terminating the value we first need to determine how we'll proceed after posting onPair.
1508  dr_bool32 wasOnNL = pVEnd[1] == '\n';
1509 
1510  pKEnd[0] = '\0';
1511  pVEnd[1] = '\0';
1512  if (onPair) {
1513  onPair(pUserData, pK, pV);
1514  }
1515 
1516  if (wasOnNL)
1517  {
1518  // Was sitting on a new-line character.
1519  pC += 1;
1520  currentLine += 1;
1521  continue;
1522  }
1523  else
1524  {
1525  // Was sitting on a comment - just to the next line.
1526  goto move_to_next_line;
1527  }
1528  }
1529  }
1530 }
1531 
1532 
1533 typedef struct
1534 {
1535  FILE* pFile;
1536  dr_key_value_pair_proc onPair;
1537  dr_key_value_error_proc onError;
1538  void* pOriginalUserData;
1539 } dr_parse_key_value_pairs_from_file_data;
1540 
1541 size_t dr_parse_key_value_pairs_from_file__on_read(void* pUserData, void* pDataOut, size_t bytesToRead)
1542 {
1543  dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1544  assert(pData != NULL);
1545 
1546  return fread(pDataOut, 1, bytesToRead, pData->pFile);
1547 }
1548 
1549 void dr_parse_key_value_pairs_from_file__on_pair(void* pUserData, const char* key, const char* value)
1550 {
1551  dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1552  assert(pData != NULL);
1553 
1554  pData->onPair(pData->pOriginalUserData, key, value);
1555 }
1556 
1557 void dr_parse_key_value_pairs_from_file__on_error(void* pUserData, const char* message, unsigned int line)
1558 {
1559  dr_parse_key_value_pairs_from_file_data* pData = (dr_parse_key_value_pairs_from_file_data*)pUserData;
1560  assert(pData != NULL);
1561 
1562  pData->onError(pData->pOriginalUserData, message, line);
1563 }
1564 
1565 dr_bool32 dr_parse_key_value_pairs_from_file(const char* filePath, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void* pUserData)
1566 {
1567  dr_parse_key_value_pairs_from_file_data data;
1568  data.pFile = dr_fopen(filePath, "rb");
1569  if (data.pFile == NULL) {
1570  if (onError) onError(pUserData, "Could not open file.", 0);
1571  return DR_FALSE;
1572  }
1573 
1574  data.onPair = onPair;
1575  data.onError = onError;
1576  data.pOriginalUserData = pUserData;
1577  dr_parse_key_value_pairs(dr_parse_key_value_pairs_from_file__on_read, dr_parse_key_value_pairs_from_file__on_pair, dr_parse_key_value_pairs_from_file__on_error, &data);
1578 
1579  fclose(data.pFile);
1580  return DR_TRUE;
1581 }
1582 
1583 
1585 // Basic Tokenizer
1586 
1587 const char* dr_next_token(const char* tokens, char* tokenOut, size_t tokenOutSize)
1588 {
1589  if (tokenOut) tokenOut[0] = '\0';
1590 
1591  if (tokens == NULL) {
1592  return NULL;
1593  }
1594 
1595  // Skip past leading whitespace.
1596  while (tokens[0] != '\0' && !(tokens[0] != ' ' && tokens[0] != '\t' && tokens[0] != '\n' && tokens[0] != '\v' && tokens[0] != '\f' && tokens[0] != '\r')) {
1597  tokens += 1;
1598  }
1599 
1600  if (tokens[0] == '\0') {
1601  return NULL;
1602  }
1603 
1604 
1605  const char* strBeg = tokens;
1606  const char* strEnd = strBeg;
1607 
1608  if (strEnd[0] == '\"')
1609  {
1610  // It's double-quoted - loop until the next unescaped quote character.
1611 
1612  // Skip past the first double-quote character.
1613  strBeg += 1;
1614  strEnd += 1;
1615 
1616  // Keep looping until the next unescaped double-quote character.
1617  char prevChar = '\0';
1618  while (strEnd[0] != '\0' && (strEnd[0] != '\"' || prevChar == '\\'))
1619  {
1620  prevChar = strEnd[0];
1621  strEnd += 1;
1622  }
1623  }
1624  else
1625  {
1626  // It's not double-quoted - just loop until the first whitespace.
1627  while (strEnd[0] != '\0' && (strEnd[0] != ' ' && strEnd[0] != '\t' && strEnd[0] != '\n' && strEnd[0] != '\v' && strEnd[0] != '\f' && strEnd[0] != '\r')) {
1628  strEnd += 1;
1629  }
1630  }
1631 
1632 
1633  // If the output buffer is large enough to hold the token, copy the token into it. When we copy the token we need to
1634  // ensure we don't include the escape character.
1635  //assert(strEnd >= strBeg);
1636 
1637  while (tokenOutSize > 1 && strBeg < strEnd)
1638  {
1639  if (strBeg[0] == '\\' && strBeg[1] == '\"' && strBeg < strEnd) {
1640  strBeg += 1;
1641  }
1642 
1643  *tokenOut++ = *strBeg++;
1644  tokenOutSize -= 1;
1645  }
1646 
1647  // Null-terminate.
1648  if (tokenOutSize > 0) {
1649  *tokenOut = '\0';
1650  }
1651 
1652 
1653  // Skip past the double-quote character before returning.
1654  if (strEnd[0] == '\"') {
1655  strEnd += 1;
1656  }
1657 
1658  return strEnd;
1659 }
1660 
1661 
1662 
1663 
1665 // Known Folders
1666 
1667 #if defined(_WIN32)
1668 #if defined(_MSC_VER)
1669  #pragma warning(push)
1670  #pragma warning(disable:4091) // 'typedef ': ignored on left of 'tagGPFIDL_FLAGS' when no variable is declared
1671 #endif
1672 #include <shlobj.h>
1673 #if defined(_MSC_VER)
1674  #pragma warning(pop)
1675 #endif
1676 
1677 
1678 dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize)
1679 {
1680  if (pathOut == NULL || pathOutSize == 0) {
1681  return 0;
1682  }
1683 
1684  DWORD length = GetModuleFileNameA(NULL, pathOut, (DWORD)pathOutSize);
1685  if (length == 0) {
1686  pathOut[0] = '\0';
1687  return DR_FALSE;
1688  }
1689 
1690  // Force null termination.
1691  if (length == pathOutSize) {
1692  pathOut[length - 1] = '\0';
1693  }
1694 
1695  // Back slashes need to be normalized to forward.
1696  while (pathOut[0] != '\0') {
1697  if (pathOut[0] == '\\') {
1698  pathOut[0] = '/';
1699  }
1700 
1701  pathOut += 1;
1702  }
1703 
1704  return DR_TRUE;
1705 }
1706 
1707 dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize)
1708 {
1709  // The documentation for SHGetFolderPathA() says that the output path should be the size of MAX_PATH. We'll enforce
1710  // that just to be safe.
1711  if (pathOutSize >= MAX_PATH)
1712  {
1713  SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pathOut);
1714  }
1715  else
1716  {
1717  char pathOutTemp[MAX_PATH];
1718  SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, pathOutTemp);
1719 
1720  if (strcpy_s(pathOut, pathOutSize, pathOutTemp) != 0) {
1721  return 0;
1722  }
1723  }
1724 
1725 
1726  // Back slashes need to be normalized to forward.
1727  while (pathOut[0] != '\0') {
1728  if (pathOut[0] == '\\') {
1729  pathOut[0] = '/';
1730  }
1731 
1732  pathOut += 1;
1733  }
1734 
1735  return 1;
1736 }
1737 
1738 dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize)
1739 {
1740  return dr_get_config_folder_path(pathOut, pathOutSize);
1741 }
1742 
1743 const char* dr_get_current_directory(char* pathOut, size_t pathOutSize)
1744 {
1745  DWORD result = GetCurrentDirectoryA((DWORD)pathOutSize, pathOut);
1746  if (result == 0) {
1747  return NULL;
1748  }
1749 
1750  return pathOut;
1751 }
1752 
1753 dr_bool32 dr_set_current_directory(const char* path)
1754 {
1755  return SetCurrentDirectoryA(path) != 0;
1756 }
1757 #else
1758 #include <unistd.h>
1759 #include <sys/types.h>
1760 #include <pwd.h>
1761 
1762 dr_bool32 dr_get_executable_path(char* pathOut, size_t pathOutSize)
1763 {
1764  if (pathOut == NULL || pathOutSize == 0) {
1765  return 0;
1766  }
1767 
1768  ssize_t length = readlink("/proc/self/exe", pathOut, pathOutSize);
1769  if (length == -1) {
1770  pathOut[0] = '\0';
1771  return DR_FALSE;
1772  }
1773 
1774  if ((size_t)length == pathOutSize) {
1775  pathOut[length - 1] = '\0';
1776  } else {
1777  pathOut[length] = '\0';
1778  }
1779 
1780  return DR_TRUE;
1781 }
1782 
1783 dr_bool32 dr_get_config_folder_path(char* pathOut, size_t pathOutSize)
1784 {
1785  const char* configdir = getenv("XDG_CONFIG_HOME");
1786  if (configdir != NULL)
1787  {
1788  return strcpy_s(pathOut, pathOutSize, configdir) == 0;
1789  }
1790  else
1791  {
1792  const char* homedir = getenv("HOME");
1793  if (homedir == NULL) {
1794  homedir = getpwuid(getuid())->pw_dir;
1795  }
1796 
1797  if (homedir != NULL)
1798  {
1799  if (strcpy_s(pathOut, pathOutSize, homedir) == 0)
1800  {
1801  size_t homedirLength = strlen(homedir);
1802  pathOut += homedirLength;
1803  pathOutSize -= homedirLength;
1804 
1805  if (pathOutSize > 0)
1806  {
1807  pathOut[0] = '/';
1808  pathOut += 1;
1809  pathOutSize -= 1;
1810 
1811  return strcpy_s(pathOut, pathOutSize, ".config") == 0;
1812  }
1813  }
1814  }
1815  }
1816 
1817  return 0;
1818 }
1819 
1820 dr_bool32 dr_get_log_folder_path(char* pathOut, size_t pathOutSize)
1821 {
1822  return strcpy_s(pathOut, pathOutSize, "var/log") == 0;
1823 }
1824 
1825 const char* dr_get_current_directory(char* pathOut, size_t pathOutSize)
1826 {
1827  return getcwd(pathOut, pathOutSize);
1828 }
1829 
1830 dr_bool32 dr_set_current_directory(const char* path)
1831 {
1832  return chdir(path) == 0;
1833 }
1834 #endif
1835 
1836 dr_bool32 dr_get_executable_directory_path(char* pathOut, size_t pathOutSize)
1837 {
1838  if (!dr_get_executable_path(pathOut, pathOutSize)) {
1839  return DR_FALSE;
1840  }
1841 
1842  // A null terminator needs to be placed at the last slash.
1843  char* lastSlash = pathOut;
1844  while (pathOut[0] != '\0') {
1845  if (pathOut[0] == '/' || pathOut[0] == '\\') {
1846  lastSlash = pathOut;
1847  }
1848  pathOut += 1;
1849  }
1850 
1851  lastSlash[0] = '\0';
1852  return DR_TRUE;
1853 }
1854 
1855 
1856 
1858 // Basic File Management
1859 
1860 #ifndef _WIN32
1861 #include <sys/types.h>
1862 #include <sys/stat.h>
1863 #include <fcntl.h>
1864 #endif
1865 
1866 FILE* dr_fopen(const char* filePath, const char* openMode)
1867 {
1868  FILE* pFile;
1869 #ifdef _MSC_VER
1870  if (fopen_s(&pFile, filePath, openMode) != 0) {
1871  return NULL;
1872  }
1873 #else
1874  pFile = fopen(filePath, openMode);
1875  if (pFile == NULL) {
1876  return NULL;
1877  }
1878 #endif
1879 
1880  return pFile;
1881 }
1882 
1883 dr_bool32 dr_create_empty_file(const char* fileName, dr_bool32 failIfExists)
1884 {
1885  if (fileName == NULL) {
1886  return DR_FALSE;
1887  }
1888 #ifdef _WIN32
1889  DWORD dwCreationDisposition;
1890  if (failIfExists) {
1891  dwCreationDisposition = CREATE_NEW;
1892  } else {
1893  dwCreationDisposition = CREATE_ALWAYS;
1894  }
1895 
1896  HANDLE hFile = CreateFileA(fileName, FILE_GENERIC_WRITE, 0, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
1897  if (hFile == INVALID_HANDLE_VALUE) {
1898  return DR_FALSE;
1899  }
1900 
1901  CloseHandle(hFile);
1902  return DR_TRUE;
1903 #else
1904  int flags = O_WRONLY | O_CREAT;
1905  if (failIfExists) {
1906  flags |= O_EXCL;
1907  } else {
1908  flags |= O_TRUNC;
1909  }
1910  int fd = open(fileName, flags, 0666);
1911  if (fd == -1) {
1912  return DR_FALSE;
1913  }
1914 
1915  close(fd);
1916  return DR_TRUE;
1917 #endif
1918 }
1919 
1920 static void* dr_open_and_read_file_with_extra_data(const char* filePath, size_t* pFileSizeOut, size_t extraBytes)
1921 {
1922  if (pFileSizeOut) *pFileSizeOut = 0; // For safety.
1923 
1924  if (filePath == NULL) {
1925  return NULL;
1926  }
1927 
1928  // TODO: Use 64-bit versions of the FILE APIs.
1929 
1930  FILE* pFile = dr_fopen(filePath, "rb");
1931  if (pFile == NULL) {
1932  return NULL;
1933  }
1934 
1935  fseek(pFile, 0, SEEK_END);
1936  dr_uint64 fileSize = ftell(pFile);
1937  fseek(pFile, 0, SEEK_SET);
1938 
1939  if (fileSize + extraBytes > SIZE_MAX) {
1940  fclose(pFile);
1941  return NULL; // File is too big.
1942  }
1943 
1944  void* pFileData = malloc((size_t)fileSize + extraBytes); // <-- Safe cast due to the check above.
1945  if (pFileData == NULL) {
1946  fclose(pFile);
1947  return NULL; // Failed to allocate memory for the file. Good chance the file is too big.
1948  }
1949 
1950  size_t bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile);
1951  if (bytesRead != fileSize) {
1952  free(pFileData);
1953  fclose(pFile);
1954  return NULL; // Failed to read every byte from the file.
1955  }
1956 
1957  fclose(pFile);
1958 
1959  if (pFileSizeOut) *pFileSizeOut = (size_t)fileSize;
1960  return pFileData;
1961 }
1962 
1963 void* dr_open_and_read_file(const char* filePath, size_t* pFileSizeOut)
1964 {
1965  return dr_open_and_read_file_with_extra_data(filePath, pFileSizeOut, 0);
1966 }
1967 
1968 char* dr_open_and_read_text_file(const char* filePath, size_t* pFileSizeOut)
1969 {
1970  if (pFileSizeOut) *pFileSizeOut = 0; // For safety.
1971 
1972  size_t fileSize;
1973  char* pFileData = (char*)dr_open_and_read_file_with_extra_data(filePath, &fileSize, 1); // <-- 1 extra byte for the null terminator.
1974  if (pFileData == NULL) {
1975  return NULL;
1976  }
1977 
1978  pFileData[fileSize] = '\0';
1979 
1980  if (pFileSizeOut) *pFileSizeOut = fileSize;
1981  return pFileData;
1982 }
1983 
1984 dr_bool32 dr_open_and_write_file(const char* filePath, const void* pData, size_t dataSize)
1985 {
1986  if (filePath == NULL) {
1987  return DR_FALSE;
1988  }
1989 
1990  // TODO: Use 64-bit versions of the FILE APIs.
1991 
1992  FILE* pFile = dr_fopen(filePath, "wb");
1993  if (pFile == NULL) {
1994  return DR_FALSE;
1995  }
1996 
1997  if (pData != NULL && dataSize > 0) {
1998  fwrite(pData, 1, dataSize, pFile);
1999  }
2000 
2001  fclose(pFile);
2002  return DR_TRUE;
2003 }
2004 
2005 dr_bool32 dr_open_and_write_text_file(const char* filePath, const char* text)
2006 {
2007  if (text == NULL) {
2008  text = "";
2009  }
2010 
2011  return dr_open_and_write_file(filePath, text, strlen(text));
2012 }
2013 
2014 void dr_free_file_data(void* valueReturnedByOpenAndReadFile)
2015 {
2016  free(valueReturnedByOpenAndReadFile);
2017 }
2018 
2019 dr_bool32 dr_file_exists(const char* filePath)
2020 {
2021  if (filePath == NULL) {
2022  return DR_FALSE;
2023  }
2024 
2025 #if _WIN32
2026  DWORD attributes = GetFileAttributesA(filePath);
2027  return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
2028 #else
2029  struct stat info;
2030  if (stat(filePath, &info) != 0) {
2031  return DR_FALSE; // Likely the folder doesn't exist.
2032  }
2033 
2034  return (info.st_mode & S_IFDIR) == 0;
2035 #endif
2036 }
2037 
2038 dr_bool32 dr_directory_exists(const char* directoryPath)
2039 {
2040  if (directoryPath == NULL) {
2041  return DR_FALSE;
2042  }
2043 
2044 #if _WIN32
2045  DWORD attributes = GetFileAttributesA(directoryPath);
2046  return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2047 #else
2048  struct stat info;
2049  if (stat(directoryPath, &info) != 0) {
2050  return DR_FALSE; // Likely the folder doesn't exist.
2051  }
2052 
2053  return (info.st_mode & S_IFDIR) != 0;
2054 #endif
2055 }
2056 
2057 dr_bool32 dr_move_file(const char* oldPath, const char* newPath)
2058 {
2059  if (oldPath == NULL || newPath == NULL) {
2060  return DR_FALSE;
2061  }
2062 
2063 #if _WIN32
2064  return MoveFileExA(oldPath, newPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH) != 0;
2065 #else
2066  return rename(oldPath, newPath) == 0;
2067 #endif
2068 }
2069 
2070 dr_bool32 dr_copy_file(const char* srcPath, const char* dstPath, dr_bool32 failIfExists)
2071 {
2072  if (srcPath == NULL || dstPath == NULL) {
2073  return DR_FALSE;
2074  }
2075 
2076 #if _WIN32
2077  return CopyFileA(srcPath, dstPath, failIfExists) != 0;
2078 #else
2079  int fdSrc = open(srcPath, O_RDONLY, 0666);
2080  if (fdSrc == -1) {
2081  return DR_FALSE;
2082  }
2083 
2084  int fdDst = open(dstPath, O_WRONLY | O_TRUNC | O_CREAT | ((failIfExists) ? O_EXCL : 0), 0666);
2085  if (fdDst == -1) {
2086  close(fdSrc);
2087  return DR_FALSE;
2088  }
2089 
2090  dr_bool32 result = DR_TRUE;
2091  struct stat info;
2092  if (fstat(fdSrc, &info) == 0) {
2093  char buffer[BUFSIZ];
2094  int bytesRead;
2095  while ((bytesRead = read(fdSrc, buffer, sizeof(buffer))) > 0) {
2096  if (write(fdDst, buffer, bytesRead) != bytesRead) {
2097  result = DR_FALSE;
2098  break;
2099  }
2100  }
2101  } else {
2102  result = DR_FALSE;
2103  }
2104 
2105  close(fdDst);
2106  close(fdSrc);
2107 
2108  // Permissions.
2109  chmod(dstPath, info.st_mode & 07777);
2110 
2111  return result;
2112 #endif
2113 }
2114 
2115 dr_bool32 dr_is_file_read_only(const char* filePath)
2116 {
2117  if (filePath == NULL || filePath[0] == '\0') {
2118  return DR_FALSE;
2119  }
2120 
2121 #if _WIN32
2122  DWORD attributes = GetFileAttributesA(filePath);
2123  return attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_READONLY) != 0;
2124 #else
2125  return access(filePath, W_OK) == -1;
2126 #endif
2127 }
2128 
2129 dr_uint64 dr_get_file_modified_time(const char* filePath)
2130 {
2131  if (filePath == NULL || filePath[0] == '\0') {
2132  return 0;
2133  }
2134 
2135 #if _WIN32
2136  HANDLE hFile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
2137  if (hFile == INVALID_HANDLE_VALUE) {
2138  return 0;
2139  }
2140 
2141  FILETIME fileTime;
2142  BOOL wasSuccessful = GetFileTime(hFile, NULL, NULL, &fileTime);
2143  CloseHandle(hFile);
2144 
2145  if (!wasSuccessful) {
2146  return 0;
2147  }
2148 
2149  ULARGE_INTEGER result;
2150  result.HighPart = fileTime.dwHighDateTime;
2151  result.LowPart = fileTime.dwLowDateTime;
2152  return result.QuadPart;
2153 #else
2154  struct stat info;
2155  if (stat(filePath, &info) != 0) {
2156  return 0;
2157  }
2158 
2159  return info.st_mtime;
2160 #endif
2161 }
2162 
2163 dr_bool32 dr_delete_file(const char* filePath)
2164 {
2165  if (filePath == NULL) {
2166  return DR_FALSE;
2167  }
2168 
2169 #if _WIN32
2170  return DeleteFileA(filePath) != 0;
2171 #else
2172  return remove(filePath) == 0;
2173 #endif
2174 }
2175 
2176 dr_bool32 dr_mkdir(const char* directoryPath)
2177 {
2178  if (directoryPath == NULL) {
2179  return DR_FALSE;
2180  }
2181 
2182 #if _WIN32
2183  return CreateDirectoryA(directoryPath, NULL) != 0;
2184 #else
2185  return mkdir(directoryPath, 0777) == 0;
2186 #endif
2187 }
2188 
2189 dr_bool32 dr_mkdir_recursive(const char* directoryPath)
2190 {
2191  if (directoryPath == NULL || directoryPath[0] == '\0') {
2192  return DR_FALSE;
2193  }
2194 
2195  // All we need to do is iterate over every segment in the path and try creating the directory.
2196  char runningPath[4096];
2197  memset(runningPath, 0, sizeof(runningPath));
2198 
2199  size_t i = 0;
2200  for (;;) {
2201  if (i >= sizeof(runningPath)-1) {
2202  return DR_FALSE; // Path is too long.
2203  }
2204 
2205  if (directoryPath[0] == '\0' || directoryPath[0] == '/' || directoryPath[0] == '\\') {
2206  if (runningPath[0] != '\0' && !(runningPath[1] == ':' && runningPath[2] == '\0')) { // <-- If the running path is empty, it means we're trying to create the root directory.
2207  if (!dr_directory_exists(runningPath)) {
2208  if (!dr_mkdir(runningPath)) {
2209  return DR_FALSE;
2210  }
2211  }
2212  }
2213 
2214  //printf("%s\n", runningPath);
2215  runningPath[i++] = '/';
2216  runningPath[i] = '\0';
2217 
2218  if (directoryPath[0] == '\0') {
2219  break;
2220  }
2221  } else {
2222  runningPath[i++] = directoryPath[0];
2223  }
2224 
2225  directoryPath += 1;
2226  }
2227 
2228  return DR_TRUE;
2229 }
2230 
2231 dr_bool32 dr_iterate_files(const char* directory, dr_bool32 recursive, dr_iterate_files_proc proc, void* pUserData)
2232 {
2233 #ifdef _WIN32
2234  char searchQuery[MAX_PATH];
2235  strcpy_s(searchQuery, sizeof(searchQuery), directory);
2236 
2237  unsigned int searchQueryLength = (unsigned int)strlen(searchQuery);
2238  if (searchQueryLength >= MAX_PATH - 3) {
2239  return DR_FALSE; // Path is too long.
2240  }
2241 
2242  searchQuery[searchQueryLength + 0] = '\\';
2243  searchQuery[searchQueryLength + 1] = '*';
2244  searchQuery[searchQueryLength + 2] = '\0';
2245 
2246  WIN32_FIND_DATAA ffd;
2247  HANDLE hFind = FindFirstFileA(searchQuery, &ffd);
2248  if (hFind == INVALID_HANDLE_VALUE) {
2249  return DR_FALSE; // Failed to begin search.
2250  }
2251 
2252  do
2253  {
2254  // Skip past "." and ".." directories.
2255  if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) {
2256  continue;
2257  }
2258 
2259  char filePath[MAX_PATH];
2260  strcpy_s(filePath, sizeof(filePath), directory);
2261  strcat_s(filePath, sizeof(filePath), "/");
2262  strcat_s(filePath, sizeof(filePath), ffd.cFileName);
2263 
2264  if (!proc(filePath, pUserData)) {
2265  return DR_FALSE;
2266  }
2267 
2268  if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
2269  if (recursive) {
2270  if (!dr_iterate_files(filePath, recursive, proc, pUserData)) {
2271  return DR_FALSE;
2272  }
2273  }
2274  }
2275 
2276  } while (FindNextFileA(hFind, &ffd));
2277 
2278  FindClose(hFind);
2279 #else
2280  DIR* dir = opendir(directory);
2281  if (dir == NULL) {
2282  return DR_FALSE;
2283  }
2284 
2285  struct dirent* info = NULL;
2286  while ((info = readdir(dir)) != NULL)
2287  {
2288  // Skip past "." and ".." directories.
2289  if (strcmp(info->d_name, ".") == 0 || strcmp(info->d_name, "..") == 0) {
2290  continue;
2291  }
2292 
2293  char filePath[4096];
2294  strcpy_s(filePath, sizeof(filePath), directory);
2295  strcat_s(filePath, sizeof(filePath), "/");
2296  strcat_s(filePath, sizeof(filePath), info->d_name);
2297 
2298  struct stat fileinfo;
2299  if (stat(filePath, &fileinfo) != 0) {
2300  continue;
2301  }
2302 
2303  if (!proc(filePath, pUserData)) {
2304  return DR_FALSE;
2305  }
2306 
2307  if (fileinfo.st_mode & S_IFDIR) {
2308  if (recursive) {
2309  if (!dr_iterate_files(filePath, recursive, proc, pUserData)) {
2310  return DR_FALSE;
2311  }
2312  }
2313  }
2314  }
2315 
2316  closedir(dir);
2317 #endif
2318 
2319  return DR_TRUE;
2320 }
2321 
2322 
2323 
2325 // DPI Awareness
2326 
2327 #if defined(_WIN32) || defined(_WIN64)
2328 
2329 typedef enum PROCESS_DPI_AWARENESS {
2330  PROCESS_DPI_UNAWARE = 0,
2331  PROCESS_SYSTEM_DPI_AWARE = 1,
2332  PROCESS_PER_MONITOR_DPI_AWARE = 2
2333 } PROCESS_DPI_AWARENESS;
2334 
2335 typedef enum MONITOR_DPI_TYPE {
2336  MDT_EFFECTIVE_DPI = 0,
2337  MDT_ANGULAR_DPI = 1,
2338  MDT_RAW_DPI = 2,
2339  MDT_DEFAULT = MDT_EFFECTIVE_DPI
2340 } MONITOR_DPI_TYPE;
2341 
2342 typedef BOOL (__stdcall * PFN_SetProcessDPIAware) (void);
2343 typedef HRESULT (__stdcall * PFN_SetProcessDpiAwareness) (PROCESS_DPI_AWARENESS);
2344 typedef HRESULT (__stdcall * PFN_GetDpiForMonitor) (HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY);
2345 
2346 void dr_win32_make_dpi_aware()
2347 {
2348  dr_bool32 fallBackToDiscouragedAPI = DR_FALSE;
2349 
2350  // We can't call SetProcessDpiAwareness() directly because otherwise on versions of Windows < 8.1 we'll get an error at load time about
2351  // a missing DLL.
2352  HMODULE hSHCoreDLL = LoadLibraryW(L"shcore.dll");
2353  if (hSHCoreDLL != NULL)
2354  {
2355  PFN_SetProcessDpiAwareness _SetProcessDpiAwareness = (PFN_SetProcessDpiAwareness)GetProcAddress(hSHCoreDLL, "SetProcessDpiAwareness");
2356  if (_SetProcessDpiAwareness != NULL)
2357  {
2358  if (_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK)
2359  {
2360  fallBackToDiscouragedAPI = DR_FALSE;
2361  }
2362  }
2363  else
2364  {
2365  fallBackToDiscouragedAPI = DR_FALSE;
2366  }
2367 
2368  FreeLibrary(hSHCoreDLL);
2369  }
2370  else
2371  {
2372  fallBackToDiscouragedAPI = DR_FALSE;
2373  }
2374 
2375 
2376  if (fallBackToDiscouragedAPI)
2377  {
2378  HMODULE hUser32DLL = LoadLibraryW(L"user32.dll");
2379  if (hUser32DLL != NULL)
2380  {
2381  PFN_SetProcessDPIAware _SetProcessDPIAware = (PFN_SetProcessDPIAware)GetProcAddress(hUser32DLL, "SetProcessDPIAware");
2382  if (_SetProcessDPIAware != NULL) {
2383  _SetProcessDPIAware();
2384  }
2385 
2386  FreeLibrary(hUser32DLL);
2387  }
2388  }
2389 }
2390 
2391 void dr_win32_get_base_dpi(int* pDPIXOut, int* pDPIYOut)
2392 {
2393  if (pDPIXOut != NULL) {
2394  *pDPIXOut = 96;
2395  }
2396 
2397  if (pDPIYOut != NULL) {
2398  *pDPIYOut = 96;
2399  }
2400 }
2401 
2402 void dr_win32_get_system_dpi(int* pDPIXOut, int* pDPIYOut)
2403 {
2404  if (pDPIXOut != NULL) {
2405  *pDPIXOut = GetDeviceCaps(GetDC(NULL), LOGPIXELSX);
2406  }
2407 
2408  if (pDPIYOut != NULL) {
2409  *pDPIYOut = GetDeviceCaps(GetDC(NULL), LOGPIXELSY);
2410  }
2411 }
2412 
2413 
2414 typedef struct
2415 {
2416  int monitorIndex;
2417  int i;
2418  int dpiX;
2419  int dpiY;
2420  PFN_GetDpiForMonitor _GetDpiForMonitor;
2421 
2422 } win32_get_monitor_dpi_data;
2423 
2424 static BOOL CALLBACK win32_get_monitor_dpi_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
2425 {
2426  (void)hdcMonitor;
2427  (void)lprcMonitor;
2428 
2429  win32_get_monitor_dpi_data* pData = (win32_get_monitor_dpi_data*)dwData;
2430  if (pData->monitorIndex == pData->i)
2431  {
2432  UINT dpiX;
2433  UINT dpiY;
2434  if (pData->_GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK)
2435  {
2436  pData->dpiX = (int)dpiX;
2437  pData->dpiY = (int)dpiY;
2438  }
2439  else
2440  {
2441  dr_win32_get_system_dpi(&pData->dpiX, &pData->dpiY);
2442  }
2443 
2444  return FALSE; // Return DR_FALSE to terminate the enumerator.
2445  }
2446 
2447  pData->i += 1;
2448  return TRUE;
2449 }
2450 
2451 void dr_win32_get_monitor_dpi(int monitor, int* pDPIXOut, int* pDPIYOut)
2452 {
2453  // If multi-monitor DPI awareness is not supported we will need to fall back to system DPI.
2454  HMODULE hSHCoreDLL = LoadLibraryW(L"shcore.dll");
2455  if (hSHCoreDLL == NULL) {
2456  dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
2457  return;
2458  }
2459 
2460  PFN_GetDpiForMonitor _GetDpiForMonitor = (PFN_GetDpiForMonitor)GetProcAddress(hSHCoreDLL, "GetDpiForMonitor");
2461  if (_GetDpiForMonitor == NULL) {
2462  dr_win32_get_system_dpi(pDPIXOut, pDPIYOut);
2463  FreeLibrary(hSHCoreDLL);
2464  return;
2465  }
2466 
2467 
2468  win32_get_monitor_dpi_data data;
2469  data.monitorIndex = monitor;
2470  data.i = 0;
2471  data.dpiX = 0;
2472  data.dpiY = 0;
2473  data._GetDpiForMonitor = _GetDpiForMonitor;
2474  EnumDisplayMonitors(NULL, NULL, win32_get_monitor_dpi_callback, (LPARAM)&data);
2475 
2476  if (pDPIXOut) {
2477  *pDPIXOut = data.dpiX;
2478  }
2479 
2480  if (pDPIYOut) {
2481  *pDPIYOut = data.dpiY;
2482  }
2483 
2484 
2485  FreeLibrary(hSHCoreDLL);
2486 }
2487 
2488 
2489 static BOOL CALLBACK win32_get_monitor_count_callback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
2490 {
2491  (void)hMonitor;
2492  (void)hdcMonitor;
2493  (void)lprcMonitor;
2494 
2495  int *count = (int*)dwData;
2496  (*count)++;
2497 
2498  return TRUE;
2499 }
2500 
2501 int dr_win32_get_monitor_count()
2502 {
2503  int count = 0;
2504  if (EnumDisplayMonitors(NULL, NULL, win32_get_monitor_count_callback, (LPARAM)&count)) {
2505  return count;
2506  }
2507 
2508  return 0;
2509 }
2510 #endif
2511 
2512 
2514 // Date / Time
2515 
2516 time_t dr_now()
2517 {
2518  return time(NULL);
2519 }
2520 
2521 void dr_datetime_short(time_t t, char* strOut, unsigned int strOutSize)
2522 {
2523 #if defined(_MSC_VER)
2524  struct tm local;
2525  localtime_s(&local, &t);
2526  strftime(strOut, strOutSize, "%x %H:%M:%S", &local);
2527 #else
2528  struct tm *local = localtime(&t);
2529  strftime(strOut, strOutSize, "%x %H:%M:%S", local);
2530 #endif
2531 }
2532 
2533 void dr_date_YYYYMMDD(time_t t, char* strOut, unsigned int strOutSize)
2534 {
2535 #if defined(_MSC_VER)
2536  struct tm local;
2537  localtime_s(&local, &t);
2538  strftime(strOut, strOutSize, "%Y%m%d", &local);
2539 #else
2540  struct tm *local = localtime(&t);
2541  strftime(strOut, strOutSize, "%Y%m%d", local);
2542 #endif
2543 }
2544 
2545 
2546 
2548 // Command Line
2549 
2550 typedef struct
2551 {
2552  dr_cmdline* pCmdLine;
2553  char* value;
2554 
2555  // Win32 style data.
2556  char* win32_payload;
2557  char* valueEnd;
2558 
2559  // argv style data.
2560  int iarg; // <-- This starts at -1 so that the first call to next() increments it to 0.
2561 
2562 } dr_cmdline_iterator;
2563 
2564 dr_cmdline_iterator dr_cmdline_begin(dr_cmdline* pCmdLine)
2565 {
2566  dr_cmdline_iterator i;
2567  i.pCmdLine = pCmdLine;
2568  i.value = NULL;
2569  i.win32_payload = NULL;
2570  i.valueEnd = NULL;
2571  i.iarg = -1;
2572 
2573  if (pCmdLine != NULL && pCmdLine->win32 != NULL) {
2574  // Win32 style
2575  size_t length = strlen(pCmdLine->win32);
2576  i.win32_payload = (char*)malloc(length + 2); // +2 for a double null terminator.
2577  strcpy_s(i.win32_payload, length + 2, pCmdLine->win32);
2578  i.win32_payload[length + 1] = '\0';
2579 
2580  i.valueEnd = i.win32_payload;
2581  }
2582 
2583  return i;
2584 }
2585 
2586 dr_bool32 dr_cmdline_next(dr_cmdline_iterator* i)
2587 {
2588  if (i != NULL && i->pCmdLine != NULL)
2589  {
2590  if (i->pCmdLine->win32 != NULL)
2591  {
2592  // Win32 style
2593  if (i->value == NULL) {
2594  i->value = i->win32_payload;
2595  i->valueEnd = i->value;
2596  } else {
2597  i->value = i->valueEnd + 1;
2598  }
2599 
2600 
2601  // Move to the start of the next argument.
2602  while (i->value[0] == ' ') {
2603  i->value += 1;
2604  }
2605 
2606 
2607  // If at this point we are sitting on the null terminator it means we have finished iterating.
2608  if (i->value[0] == '\0')
2609  {
2610  free(i->win32_payload);
2611  i->win32_payload = NULL;
2612  i->pCmdLine = NULL;
2613  i->value = NULL;
2614  i->valueEnd = NULL;
2615 
2616  return DR_FALSE;
2617  }
2618 
2619 
2620  // Move to the end of the token. If the argument begins with a double quote, we iterate until we find
2621  // the next unescaped double-quote.
2622  if (i->value[0] == '\"')
2623  {
2624  // Go to the last unescaped double-quote.
2625  i->value += 1;
2626  i->valueEnd = i->value + 1;
2627 
2628  while (i->valueEnd[0] != '\0' && i->valueEnd[0] != '\"')
2629  {
2630  if (i->valueEnd[0] == '\\') {
2631  i->valueEnd += 1;
2632 
2633  if (i->valueEnd[0] == '\0') {
2634  break;
2635  }
2636  }
2637 
2638  i->valueEnd += 1;
2639  }
2640  i->valueEnd[0] = '\0';
2641  }
2642  else
2643  {
2644  // Go to the next space.
2645  i->valueEnd = i->value + 1;
2646 
2647  while (i->valueEnd[0] != '\0' && i->valueEnd[0] != ' ')
2648  {
2649  i->valueEnd += 1;
2650  }
2651  i->valueEnd[0] = '\0';
2652  }
2653 
2654  return DR_TRUE;
2655  }
2656  else
2657  {
2658  // argv style
2659  i->iarg += 1;
2660  if (i->iarg < i->pCmdLine->argc)
2661  {
2662  i->value = i->pCmdLine->argv[i->iarg];
2663  return DR_TRUE;
2664  }
2665  else
2666  {
2667  i->value = NULL;
2668  return DR_FALSE;
2669  }
2670  }
2671  }
2672 
2673  return DR_FALSE;
2674 }
2675 
2676 
2677 dr_bool32 dr_init_cmdline(dr_cmdline* pCmdLine, int argc, char** argv)
2678 {
2679  if (pCmdLine == NULL) {
2680  return DR_FALSE;
2681  }
2682 
2683  pCmdLine->argc = argc;
2684  pCmdLine->argv = argv;
2685  pCmdLine->win32 = NULL;
2686 
2687  return DR_TRUE;
2688 }
2689 
2690 dr_bool32 dr_init_cmdline_win32(dr_cmdline* pCmdLine, const char* args)
2691 {
2692  if (pCmdLine == NULL) {
2693  return DR_FALSE;
2694  }
2695 
2696  pCmdLine->argc = 0;
2697  pCmdLine->argv = NULL;
2698  pCmdLine->win32 = args;
2699 
2700  return DR_TRUE;
2701 }
2702 
2703 void dr_parse_cmdline(dr_cmdline* pCmdLine, dr_cmdline_parse_proc callback, void* pUserData)
2704 {
2705  if (pCmdLine == NULL || callback == NULL) {
2706  return;
2707  }
2708 
2709 
2710  char pTemp[2] = {0};
2711 
2712  char* pKey = NULL;
2713  char* pVal = NULL;
2714 
2715  dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
2716  if (dr_cmdline_next(&arg))
2717  {
2718  if (!callback("[path]", arg.value, pUserData)) {
2719  return;
2720  }
2721  }
2722 
2723  while (dr_cmdline_next(&arg))
2724  {
2725  if (arg.value[0] == '-')
2726  {
2727  // key
2728 
2729  // If the key is non-null, but the value IS null, it means we hit a key with no value in which case it will not yet have been posted.
2730  if (pKey != NULL && pVal == NULL)
2731  {
2732  if (!callback(pKey, pVal, pUserData)) {
2733  return;
2734  }
2735 
2736  pKey = NULL;
2737  }
2738  else
2739  {
2740  // Need to ensure the key and value are reset before doing any further processing.
2741  pKey = NULL;
2742  pVal = NULL;
2743  }
2744 
2745 
2746 
2747  if (arg.value[1] == '-')
2748  {
2749  // --argument style
2750  pKey = arg.value + 2;
2751  }
2752  else
2753  {
2754  // -a -b -c -d or -abcd style
2755  if (arg.value[1] != '\0')
2756  {
2757  if (arg.value[2] == '\0')
2758  {
2759  // -a -b -c -d style
2760  pTemp[0] = arg.value[1];
2761  pKey = pTemp;
2762  pVal = NULL;
2763  }
2764  else
2765  {
2766  // -abcd style.
2767  int i = 1;
2768  while (arg.value[i] != '\0')
2769  {
2770  pTemp[0] = arg.value[i];
2771 
2772  if (!callback(pTemp, NULL, pUserData)) {
2773  return;
2774  }
2775 
2776  pKey = NULL;
2777  pVal = NULL;
2778 
2779  i += 1;
2780  }
2781  }
2782  }
2783  }
2784  }
2785  else
2786  {
2787  // value
2788 
2789  pVal = arg.value;
2790  if (!callback(pKey, pVal, pUserData)) {
2791  return;
2792  }
2793  }
2794  }
2795 
2796 
2797  // There may be a key without a value that needs posting.
2798  if (pKey != NULL && pVal == NULL) {
2799  callback(pKey, pVal, pUserData);
2800  }
2801 }
2802 
2803 typedef struct
2804 {
2805  dr_bool32 exists;
2806  const char* key;
2807 } dr_cmdline_key_exists_data;
2808 
2809 dr_bool32 dr_cmdline_key_exists_callback(const char* key, const char* value, void* pUserData)
2810 {
2811  (void)value;
2812 
2813  dr_cmdline_key_exists_data* pData = (dr_cmdline_key_exists_data*)pUserData;
2814  assert(pData != NULL);
2815 
2816  if (key != NULL && strcmp(pData->key, key) == 0) {
2817  pData->exists = DR_TRUE;
2818  return DR_FALSE;
2819  }
2820 
2821  return DR_TRUE;
2822 }
2823 
2824 dr_bool32 dr_cmdline_key_exists(dr_cmdline* pCmdLine, const char* key)
2825 {
2826  dr_cmdline_key_exists_data data;
2827  data.exists = DR_FALSE;
2828  data.key = key;
2829  dr_parse_cmdline(pCmdLine, dr_cmdline_key_exists_callback, &data);
2830 
2831  return data.exists;
2832 }
2833 
2834 int dr_cmdline_to_argv(dr_cmdline* pCmdLine, char*** argvOut)
2835 {
2836  if (argvOut == NULL) return 0;
2837  *argvOut = NULL; // Safety.
2838 
2839  int argc = 0;
2840  char** argv = NULL;
2841  size_t cmdlineLen = 0;
2842 
2843  // The command line is parsed in 2 passes. The first pass simple calculates the required sizes of each buffer. The second
2844  // pass fills those buffers with actual data.
2845 
2846  // First pass.
2847  dr_cmdline_iterator arg = dr_cmdline_begin(pCmdLine);
2848  while (dr_cmdline_next(&arg)) {
2849  cmdlineLen += strlen(arg.value) + 1; // +1 for null terminator.
2850  argc += 1;
2851  }
2852 
2853  if (argc == 0) {
2854  return 0;
2855  }
2856 
2857 
2858  // The entire data for the command line is stored in a single buffer.
2859  char* data = (char*)malloc((argc * sizeof(char**)) + (cmdlineLen * sizeof(char)));
2860  if (data == NULL) {
2861  return 0; // Ran out of memory.
2862  }
2863 
2864  argv = (char**)data;
2865  char* cmdlineStr = data + (argc * sizeof(char**));
2866 
2867 
2868 
2869  // Second pass.
2870  argc = 0;
2871  cmdlineLen = 0;
2872 
2873  arg = dr_cmdline_begin(pCmdLine);
2874  while (dr_cmdline_next(&arg)) {
2875  argv[argc] = cmdlineStr + cmdlineLen;
2876 
2877  int i = 0;
2878  while (arg.value[i] != '\0') {
2879  argv[argc][i] = arg.value[i];
2880  i += 1;
2881  }
2882  argv[argc][i] = '\0';
2883 
2884 
2885  cmdlineLen += strlen(arg.value) + 1; // +1 for null terminator.
2886  argc += 1;
2887  }
2888 
2889 
2890  *argvOut = argv;
2891  return argc;
2892 }
2893 
2894 int dr_winmain_to_argv(const char* cmdlineWinMain, char*** argvOut)
2895 {
2896  dr_cmdline cmdline;
2897  if (!dr_init_cmdline_win32(&cmdline, cmdlineWinMain)) {
2898  return 0;
2899  }
2900 
2901  return dr_cmdline_to_argv(&cmdline, argvOut);
2902 }
2903 
2904 void dr_free_argv(char** argv)
2905 {
2906  if (argv == NULL) {
2907  return;
2908  }
2909 
2910  free(argv);
2911 }
2912 
2913 
2914 
2916 // Threading
2917 
2918 #if defined(_WIN32)
2919 void dr_sleep(unsigned int milliseconds)
2920 {
2921  Sleep((DWORD)milliseconds);
2922 }
2923 
2924 void dr_yield()
2925 {
2926  SwitchToThread();
2927 }
2928 
2929 unsigned int dr_get_logical_processor_count()
2930 {
2931  SYSTEM_INFO sysinfo;
2932  GetSystemInfo(&sysinfo);
2933 
2934  return (unsigned int)sysinfo.dwNumberOfProcessors;
2935 }
2936 
2937 
2938 typedef struct
2939 {
2941  HANDLE hThread;
2942 
2944  dr_thread_entry_proc entryProc;
2945 
2947  void* pData;
2948 
2950  dr_bool32 isInEntryProc;
2951 
2952 } dr_thread_win32;
2953 
2954 static DWORD WINAPI dr_thread_entry_proc_win32(LPVOID pUserData)
2955 {
2956  dr_thread_win32* pThreadWin32 = (dr_thread_win32*)pUserData;
2957  assert(pThreadWin32 != NULL);
2958 
2959  void* pEntryProcData = pThreadWin32->pData;
2960  dr_thread_entry_proc entryProc = pThreadWin32->entryProc;
2961  assert(entryProc != NULL);
2962 
2963  pThreadWin32->isInEntryProc = DR_TRUE;
2964 
2965  return (DWORD)entryProc(pEntryProcData);
2966 }
2967 
2968 dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData)
2969 {
2970  if (entryProc == NULL) {
2971  return NULL;
2972  }
2973 
2974  dr_thread_win32* pThreadWin32 = (dr_thread_win32*)malloc(sizeof(*pThreadWin32));
2975  if (pThreadWin32 != NULL)
2976  {
2977  pThreadWin32->entryProc = entryProc;
2978  pThreadWin32->pData = pData;
2979  pThreadWin32->isInEntryProc = DR_FALSE;
2980 
2981  pThreadWin32->hThread = CreateThread(NULL, 0, dr_thread_entry_proc_win32, pThreadWin32, 0, NULL);
2982  if (pThreadWin32 == NULL) {
2983  free(pThreadWin32);
2984  return NULL;
2985  }
2986 
2987  // Wait for the new thread to enter into it's entry point before returning. We need to do this so we can safely
2988  // support something like dr_delete_thread(dr_create_thread(my_thread_proc, pData)).
2989  //
2990  // On Win32 there are times when this can get stuck in an infinite loop - I expect it's something to do with some
2991  // bad scheduling by the OS. This can be "fixed" by sleeping for a bit.
2992  while (!pThreadWin32->isInEntryProc) { dr_sleep(0); }
2993  }
2994 
2995  return (dr_thread)pThreadWin32;
2996 }
2997 
2998 void dr_delete_thread(dr_thread thread)
2999 {
3000  dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
3001  if (pThreadWin32 != NULL)
3002  {
3003  CloseHandle(pThreadWin32->hThread);
3004  }
3005 
3006  free(pThreadWin32);
3007 }
3008 
3009 void dr_wait_thread(dr_thread thread)
3010 {
3011  dr_thread_win32* pThreadWin32 = (dr_thread_win32*)thread;
3012  if (pThreadWin32 != NULL)
3013  {
3014  WaitForSingleObject(pThreadWin32->hThread, INFINITE);
3015  }
3016 }
3017 
3019 {
3020  dr_wait_thread(thread);
3021  dr_delete_thread(thread);
3022 }
3023 
3024 
3025 #ifdef DR_UTIL_WIN32_USE_CRITICAL_SECTION_MUTEX
3027 {
3028  dr_mutex mutex = malloc(sizeof(CRITICAL_SECTION));
3029  if (mutex != NULL)
3030  {
3031  InitializeCriticalSection(mutex);
3032  }
3033 
3034  return mutex;
3035 }
3036 
3037 void dr_delete_mutex(dr_mutex mutex)
3038 {
3039  DeleteCriticalSection(mutex);
3040  free(mutex);
3041 }
3042 
3043 void dr_lock_mutex(dr_mutex mutex)
3044 {
3045  EnterCriticalSection(mutex);
3046 }
3047 
3048 void dr_unlock_mutex(dr_mutex mutex)
3049 {
3050  LeaveCriticalSection(mutex);
3051 }
3052 #else
3054 {
3055  return CreateEventA(NULL, FALSE, TRUE, NULL);
3056 }
3057 
3058 void dr_delete_mutex(dr_mutex mutex)
3059 {
3060  CloseHandle((HANDLE)mutex);
3061 }
3062 
3063 void dr_lock_mutex(dr_mutex mutex)
3064 {
3065  WaitForSingleObject((HANDLE)mutex, INFINITE);
3066 }
3067 
3068 void dr_unlock_mutex(dr_mutex mutex)
3069 {
3070  SetEvent((HANDLE)mutex);
3071 }
3072 #endif
3073 
3074 
3075 dr_semaphore dr_create_semaphore(int initialValue)
3076 {
3077  return (void*)CreateSemaphoreA(NULL, initialValue, LONG_MAX, NULL);
3078 }
3079 
3080 void dr_delete_semaphore(dr_semaphore semaphore)
3081 {
3082  CloseHandle(semaphore);
3083 }
3084 
3086 {
3087  return WaitForSingleObject((HANDLE)semaphore, INFINITE) == WAIT_OBJECT_0;
3088 }
3089 
3091 {
3092  return ReleaseSemaphore((HANDLE)semaphore, 1, NULL) != 0;
3093 }
3094 #else
3095 void dr_sleep(unsigned int milliseconds)
3096 {
3097  usleep(milliseconds * 1000); // <-- usleep is in microseconds.
3098 }
3099 
3100 void dr_yield()
3101 {
3102  sched_yield();
3103 }
3104 
3105 unsigned int dr_get_logical_processor_count()
3106 {
3107  return (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
3108 }
3109 
3110 
3111 typedef struct
3112 {
3114  pthread_t pthread;
3115 
3117  dr_thread_entry_proc entryProc;
3118 
3120  void* pData;
3121 
3123  dr_bool32 isInEntryProc;
3124 
3125 } dr_thread_posix;
3126 
3127 static void* dr_thread_entry_proc_posix(void* pDataIn)
3128 {
3129  dr_thread_posix* pThreadPosix = (dr_thread_posix*)pDataIn;
3130  assert(pThreadPosix != NULL);
3131 
3132  void* pEntryProcData = pThreadPosix->pData;
3133  dr_thread_entry_proc entryProc = pThreadPosix->entryProc;
3134  assert(entryProc != NULL);
3135 
3136  pThreadPosix->isInEntryProc = DR_TRUE;
3137 
3138  return (void*)(size_t)entryProc(pEntryProcData);
3139 }
3140 
3141 dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void* pData)
3142 {
3143  if (entryProc == NULL) {
3144  return NULL;
3145  }
3146 
3147  dr_thread_posix* pThreadPosix = (dr_thread_posix*)malloc(sizeof(*pThreadPosix));
3148  if (pThreadPosix != NULL)
3149  {
3150  pThreadPosix->entryProc = entryProc;
3151  pThreadPosix->pData = pData;
3152  pThreadPosix->isInEntryProc = DR_FALSE;
3153 
3154  if (pthread_create(&pThreadPosix->pthread, NULL, dr_thread_entry_proc_posix, pThreadPosix) != 0) {
3155  free(pThreadPosix);
3156  return NULL;
3157  }
3158 
3159  // Wait for the new thread to enter into it's entry point before returning. We need to do this so we can safely
3160  // support something like dr_delete_thread(dr_create_thread(my_thread_proc, pData)).
3161  while (!pThreadPosix->isInEntryProc) {}
3162  }
3163 
3164  return (dr_thread)pThreadPosix;
3165 }
3166 
3167 void dr_delete_thread(dr_thread thread)
3168 {
3169  free(thread);
3170 }
3171 
3172 void dr_wait_thread(dr_thread thread)
3173 {
3174  dr_thread_posix* pThreadPosix = (dr_thread_posix*)thread;
3175  if (pThreadPosix != NULL)
3176  {
3177  pthread_join(pThreadPosix->pthread, NULL);
3178  }
3179 }
3180 
3181 
3182 
3184 {
3185  pthread_mutex_t* mutex = (pthread_mutex_t*)malloc(sizeof(*mutex));
3186  if (pthread_mutex_init(mutex, NULL) != 0) {
3187  free(mutex);
3188  mutex = NULL;
3189  }
3190 
3191  return mutex;
3192 }
3193 
3194 void dr_delete_mutex(dr_mutex mutex)
3195 {
3196  pthread_mutex_destroy((pthread_mutex_t*)mutex);
3197 }
3198 
3199 void dr_lock_mutex(dr_mutex mutex)
3200 {
3201  pthread_mutex_lock((pthread_mutex_t*)mutex);
3202 }
3203 
3204 void dr_unlock_mutex(dr_mutex mutex)
3205 {
3206  pthread_mutex_unlock((pthread_mutex_t*)mutex);
3207 }
3208 
3209 
3210 
3211 dr_semaphore dr_create_semaphore(int initialValue)
3212 {
3213  sem_t* semaphore = (sem_t*)malloc(sizeof(*semaphore));
3214  if (sem_init(semaphore, 0, (unsigned int)initialValue) == -1) {
3215  free(semaphore);
3216  semaphore = NULL;
3217  }
3218 
3219  return semaphore;
3220 }
3221 
3222 void dr_delete_semaphore(dr_semaphore semaphore)
3223 {
3224  sem_close((sem_t*)semaphore);
3225 }
3226 
3228 {
3229  return sem_wait((sem_t*)semaphore) != -1;
3230 }
3231 
3233 {
3234  return sem_post((sem_t*)semaphore) != -1;
3235 }
3236 #endif
3237 
3238 
3239 
3241 // Timing
3242 
3243 // macOS does not have clock_gettime on OS X < 10.12
3244 #ifdef __MACH__
3245 #include <AvailabilityMacros.h>
3246 #ifndef MAC_OS_X_VERSION_10_12
3247 #define MAC_OS_X_VERSION_10_12 101200
3248 #endif
3249 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
3250 #include <mach/mach_time.h>
3251 #define CLOCK_REALTIME 0
3252 #define CLOCK_MONOTONIC 0
3253 int clock_gettime(int clk_id, struct timespec* t)
3254 {
3255  mach_timebase_info_data_t timebase;
3256  mach_timebase_info(&timebase);
3257  dr_uint64 time;
3258  time = mach_absolute_time();
3259  double nseconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom);
3260  double seconds = ((double)time * (double)timebase.numer) / ((double)timebase.denom * 1e9);
3261  t->tv_sec = seconds;
3262  t->tv_nsec = nseconds;
3263  return 0;
3264 }
3265 #endif
3266 #endif
3267 
3268 #ifdef _WIN32
3269 static LARGE_INTEGER g_DRTimerFrequency = {{0}};
3270 void dr_timer_init(dr_timer* pTimer)
3271 {
3272  if (g_DRTimerFrequency.QuadPart == 0) {
3273  QueryPerformanceFrequency(&g_DRTimerFrequency);
3274  }
3275 
3276  LARGE_INTEGER counter;
3277  QueryPerformanceCounter(&counter);
3278  pTimer->counter = (dr_uint64)counter.QuadPart;
3279 }
3280 
3281 double dr_timer_tick(dr_timer* pTimer)
3282 {
3283  LARGE_INTEGER counter;
3284  if (!QueryPerformanceCounter(&counter)) {
3285  return 0;
3286  }
3287 
3288  dr_uint64 newTimeCounter = counter.QuadPart;
3289  dr_uint64 oldTimeCounter = pTimer->counter;
3290 
3291  pTimer->counter = newTimeCounter;
3292 
3293  return (newTimeCounter - oldTimeCounter) / (double)g_DRTimerFrequency.QuadPart;
3294 }
3295 #else
3296 void dr_timer_init(dr_timer* pTimer)
3297 {
3298  struct timespec newTime;
3299  clock_gettime(CLOCK_MONOTONIC, &newTime);
3300 
3301  pTimer->counter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
3302 }
3303 
3304 double dr_timer_tick(dr_timer* pTimer)
3305 {
3306  struct timespec newTime;
3307  clock_gettime(CLOCK_MONOTONIC, &newTime);
3308 
3309  dr_uint64 newTimeCounter = (newTime.tv_sec * 1000000000LL) + newTime.tv_nsec;
3310  dr_uint64 oldTimeCounter = pTimer->counter;
3311 
3312  pTimer->counter = newTimeCounter;
3313 
3314  return (newTimeCounter - oldTimeCounter) / 1000000000.0;
3315 }
3316 #endif
3317 
3318 
3320 // Random
3321 
3322 double dr_randd()
3323 {
3324  return (double)rand() / (double)RAND_MAX;
3325 }
3326 
3327 float dr_randf()
3328 {
3329  return (float)dr_randd();
3330 }
3331 
3332 
3334 // User Accounts and Process Management
3335 
3336 size_t dr_get_username(char* usernameOut, size_t usernameOutSize)
3337 {
3338  if (usernameOut != NULL && usernameOutSize > 0) {
3339  usernameOut[0] = '\0';
3340  }
3341 
3342 #ifdef _WIN32
3343  DWORD dwSize = (DWORD)usernameOutSize;
3344  if (!GetUserNameA(usernameOut, &dwSize)) {
3345  return 0;
3346  }
3347 
3348  return dwSize;
3349 #else
3350  struct passwd *pw = getpwuid(geteuid());
3351  if (pw == NULL) {
3352  return 0;
3353  }
3354 
3355  if (usernameOut != NULL) {
3356  strcpy_s(usernameOut, usernameOutSize, pw->pw_name);
3357  }
3358 
3359  return strlen(pw->pw_name);
3360 #endif
3361 }
3362 
3363 unsigned int dr_get_process_id()
3364 {
3365 #ifdef _WIN32
3366  return GetProcessId(GetCurrentProcess());
3367 #else
3368  return (unsigned int)getpid();
3369 #endif
3370 }
3371 
3372 
3373 
3375 // Miscellaneous Stuff.
3376 
3377 dr_bool32 dr_hex_char_to_uint(char ascii, unsigned int* out)
3378 {
3379  if (ascii >= '0' && ascii <= '9') {
3380  if (out) *out = ascii - '0';
3381  return DR_TRUE;
3382  }
3383 
3384  if (ascii >= 'A' && ascii <= 'F') {
3385  if (out) *out = 10 + (ascii - 'A');
3386  return DR_TRUE;
3387  }
3388 
3389  if (ascii >= 'a' && ascii <= 'f') {
3390  if (out) *out = 10 + (ascii - 'a');
3391  return DR_TRUE;
3392  }
3393 
3394  if (out) *out = 0;
3395  return DR_FALSE;
3396 }
3397 
3398 #endif //DR_IMPLEMENTATION
3399 
3400 
3401 
3402 
3405 //
3406 // dr_path
3407 //
3410 
3411 #ifndef dr_path_h
3412 #define dr_path_h
3413 
3414 #include <stddef.h>
3415 
3416 #ifdef __cplusplus
3417 extern "C" {
3418 #endif
3419 
3420 
3421 // Structure representing a section of a path.
3422 typedef struct
3423 {
3424  size_t offset;
3425  size_t length;
3426 
3427 } drpath_segment;
3428 
3429 // Structure used for iterating over a path while at the same time providing useful and easy-to-use information about the iteration.
3430 typedef struct drpath_iterator
3431 {
3432  const char* path;
3434 
3435 } drpath_iterator;
3436 
3437 
3438 
3447 dr_bool32 drpath_segments_equal(const char* s0Path, const drpath_segment s0, const char* s1Path, const drpath_segment s1);
3448 
3449 
3455 dr_bool32 drpath_first(const char* path, drpath_iterator* i);
3456 
3458 dr_bool32 drpath_last(const char* path, drpath_iterator* i);
3459 
3466 
3473 
3478 
3483 
3491 
3492 
3495 
3498 
3501 
3502 
3506 void drpath_to_forward_slashes(char* path);
3507 
3511 void drpath_to_backslashes(char* path);
3512 
3513 
3523 dr_bool32 drpath_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath);
3524 
3534 dr_bool32 drpath_is_child(const char* childAbsolutePath, const char* parentAbsolutePath);
3535 
3536 
3540 char* drpath_base_path(char* path);
3541 
3552 void drpath_copy_base_path(const char* path, char* baseOut, size_t baseSizeInBytes);
3553 
3564 const char* drpath_file_name(const char* path);
3565 
3567 const char* drpath_copy_file_name(const char* path, char* fileNameOut, size_t fileNameSizeInBytes);
3568 
3581 const char* drpath_extension(const char* path);
3582 
3583 
3596 dr_bool32 drpath_equal(const char* path1, const char* path2);
3597 
3602 dr_bool32 drpath_extension_equal(const char* path, const char* extension);
3603 
3604 
3608 dr_bool32 drpath_is_relative(const char* path);
3609 
3613 dr_bool32 drpath_is_absolute(const char* path);
3614 
3615 
3624 dr_bool32 drpath_append(char* base, size_t baseBufferSizeInBytes, const char* other);
3625 
3627 dr_bool32 drpath_append_iterator(char* base, size_t baseBufferSizeInBytes, drpath_iterator i);
3628 
3630 dr_bool32 drpath_append_extension(char* base, size_t baseBufferSizeInBytes, const char* extension);
3631 
3643 dr_bool32 drpath_copy_and_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other);
3644 
3656 dr_bool32 drpath_copy_and_append_iterator(char* dst, size_t dstSizeInBytes, const char* base, drpath_iterator i);
3657 
3665 dr_bool32 drpath_copy_and_append_extension(char* dst, size_t dstSizeInBytes, const char* base, const char* extension);
3666 
3667 
3686 size_t drpath_clean(const char* path, char* pathOut, size_t pathOutSizeInBytes);
3687 
3689 size_t drpath_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other);
3690 
3691 
3697 
3699 dr_bool32 drpath_copy_and_remove_extension(char* dst, size_t dstSizeInBytes, const char* path);
3700 
3701 
3704 
3706 dr_bool32 drpath_copy_and_remove_file_name(char* dst, size_t dstSizeInBytes, const char* path);
3707 
3708 
3715 dr_bool32 drpath_to_relative(const char* absolutePathToMakeRelative, const char* absolutePathToMakeRelativeTo, char* relativePathOut, size_t relativePathOutSizeInBytes);
3716 
3723 dr_bool32 drpath_to_absolute(const char* relativePathToMakeAbsolute, const char* basePath, char* absolutePathOut, size_t absolutePathOutSizeInBytes);
3724 
3725 
3726 #ifdef __cplusplus
3727 }
3728 #endif
3729 #endif //dr_path_h
3730 
3731 
3732 
3734 //
3735 // IMPLEMENTATION
3736 //
3738 #ifdef DR_IMPLEMENTATION
3739 #include <string.h>
3740 #include <ctype.h>
3741 #include <errno.h>
3742 
3743 dr_bool32 drpath_first(const char* path, drpath_iterator* i)
3744 {
3745  if (i == 0) return DR_FALSE;
3746  i->path = path;
3747  i->segment.offset = 0;
3748  i->segment.length = 0;
3749 
3750  if (path == 0 || path[0] == '\0') {
3751  return DR_FALSE;
3752  }
3753 
3754  while (i->path[i->segment.length] != '\0' && (i->path[i->segment.length] != '/' && i->path[i->segment.length] != '\\')) {
3755  i->segment.length += 1;
3756  }
3757 
3758  return DR_TRUE;
3759 }
3760 
3761 dr_bool32 drpath_last(const char* path, drpath_iterator* i)
3762 {
3763  if (i == 0) return DR_FALSE;
3764  i->path = path;
3765  i->segment.offset = 0;
3766  i->segment.length = 0;
3767 
3768  if (path == 0 || path[0] == '\0') {
3769  return DR_FALSE;
3770  }
3771 
3772  i->path = path;
3773  i->segment.offset = strlen(path);
3774  i->segment.length = 0;
3775 
3776  return drpath_prev(i);
3777 }
3778 
3780 {
3781  if (i == NULL || i->path == NULL) {
3782  return DR_FALSE;
3783  }
3784 
3785  i->segment.offset = i->segment.offset + i->segment.length;
3786  i->segment.length = 0;
3787 
3788  while (i->path[i->segment.offset] != '\0' && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\')) {
3789  i->segment.offset += 1;
3790  }
3791 
3792  if (i->path[i->segment.offset] == '\0') {
3793  return DR_FALSE;
3794  }
3795 
3796 
3797  while (i->path[i->segment.offset + i->segment.length] != '\0' && (i->path[i->segment.offset + i->segment.length] != '/' && i->path[i->segment.offset + i->segment.length] != '\\')) {
3798  i->segment.length += 1;
3799  }
3800 
3801  return DR_TRUE;
3802 }
3803 
3805 {
3806  if (i == NULL || i->path == NULL || i->segment.offset == 0) {
3807  return DR_FALSE;
3808  }
3809 
3810  i->segment.length = 0;
3811 
3812  do
3813  {
3814  i->segment.offset -= 1;
3815  } while (i->segment.offset > 0 && (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\'));
3816 
3817  if (i->segment.offset == 0) {
3818  if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
3819  i->segment.length = 0;
3820  return DR_TRUE;
3821  }
3822 
3823  return DR_FALSE;
3824  }
3825 
3826 
3827  size_t offsetEnd = i->segment.offset + 1;
3828  while (i->segment.offset > 0 && (i->path[i->segment.offset] != '/' && i->path[i->segment.offset] != '\\')) {
3829  i->segment.offset -= 1;
3830  }
3831 
3832  if (i->path[i->segment.offset] == '/' || i->path[i->segment.offset] == '\\') {
3833  i->segment.offset += 1;
3834  }
3835 
3836 
3837  i->segment.length = offsetEnd - i->segment.offset;
3838 
3839  return DR_TRUE;
3840 }
3841 
3843 {
3844  return i.path == 0 || i.path[i.segment.offset] == '\0';
3845 }
3846 
3848 {
3849  return i.path != 0 && i.segment.offset == 0;
3850 }
3851 
3853 {
3854  return drpath_segments_equal(i0.path, i0.segment, i1.path, i1.segment);
3855 }
3856 
3857 dr_bool32 drpath_segments_equal(const char* s0Path, const drpath_segment s0, const char* s1Path, const drpath_segment s1)
3858 {
3859  if (s0Path == NULL || s1Path == NULL) {
3860  return DR_FALSE;
3861  }
3862 
3863  if (s0.length != s1.length) {
3864  return DR_FALSE;
3865  }
3866 
3867  return strncmp(s0Path + s0.offset, s1Path + s1.offset, s0.length) == 0;
3868 }
3869 
3870 
3872 {
3874 }
3875 
3877 {
3878  if (i.path == NULL) {
3879  return DR_FALSE;
3880  }
3881 
3882  if (i.segment.offset == 0 && i.segment.length == 0) {
3883  return DR_TRUE; // "/" style root.
3884  }
3885 
3886  return DR_FALSE;
3887 }
3888 
3890 {
3891  if (i.path == NULL) {
3892  return DR_FALSE;
3893  }
3894 
3895  if (i.segment.offset == 0 && i.segment.length == 2) {
3896  if (((i.path[0] >= 'a' && i.path[0] <= 'z') || (i.path[0] >= 'A' && i.path[0] <= 'Z')) && i.path[1] == ':') {
3897  return DR_TRUE;
3898  }
3899  }
3900 
3901  return DR_FALSE;
3902 }
3903 
3904 
3905 void drpath_to_forward_slashes(char* path)
3906 {
3907  if (path == NULL) {
3908  return;
3909  }
3910 
3911  while (path[0] != '\0') {
3912  if (path[0] == '\\') {
3913  path[0] = '/';
3914  }
3915 
3916  path += 1;
3917  }
3918 }
3919 
3920 void drpath_to_backslashes(char* path)
3921 {
3922  if (path == NULL) {
3923  return;
3924  }
3925 
3926  while (path[0] != '\0') {
3927  if (path[0] == '/') {
3928  path[0] = '\\';
3929  }
3930 
3931  path += 1;
3932  }
3933 }
3934 
3935 
3936 dr_bool32 drpath_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath)
3937 {
3938  drpath_iterator iChild;
3939  if (!drpath_first(descendantAbsolutePath, &iChild)) {
3940  return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
3941  }
3942 
3943  drpath_iterator iParent;
3944  if (drpath_first(parentAbsolutePath, &iParent))
3945  {
3946  do
3947  {
3948  // If the segment is different, the paths are different and thus it is not a descendant.
3949  if (!drpath_iterators_equal(iParent, iChild)) {
3950  return DR_FALSE;
3951  }
3952 
3953  if (!drpath_next(&iChild)) {
3954  return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
3955  }
3956 
3957  } while (drpath_next(&iParent));
3958  }
3959 
3960  return DR_TRUE;
3961 }
3962 
3963 dr_bool32 drpath_is_child(const char* childAbsolutePath, const char* parentAbsolutePath)
3964 {
3965  drpath_iterator iChild;
3966  if (!drpath_first(childAbsolutePath, &iChild)) {
3967  return DR_FALSE; // The descendant is an empty string which makes it impossible for it to be a descendant.
3968  }
3969 
3970  drpath_iterator iParent;
3971  if (drpath_first(parentAbsolutePath, &iParent))
3972  {
3973  do
3974  {
3975  // If the segment is different, the paths are different and thus it is not a descendant.
3976  if (!drpath_iterators_equal(iParent, iChild)) {
3977  return DR_FALSE;
3978  }
3979 
3980  if (!drpath_next(&iChild)) {
3981  return DR_FALSE; // The descendant is shorter which means it's impossible for it to be a descendant.
3982  }
3983 
3984  } while (drpath_next(&iParent));
3985  }
3986 
3987  // At this point we have finished iteration of the parent, which should be shorter one. We now do one more iterations of
3988  // the child to ensure it is indeed a direct child and not a deeper descendant.
3989  return !drpath_next(&iChild);
3990 }
3991 
3992 char* drpath_base_path(char* path)
3993 {
3994  if (path == NULL) {
3995  return NULL;
3996  }
3997 
3998  char* pathorig = path;
3999  char* baseend = path;
4000 
4001  // We just loop through the path until we find the last slash.
4002  while (path[0] != '\0') {
4003  if (path[0] == '/' || path[0] == '\\') {
4004  baseend = path;
4005  }
4006 
4007  path += 1;
4008  }
4009 
4010 
4011  // Now we just loop backwards until we hit the first non-slash.
4012  while (baseend > path) {
4013  if (baseend[0] != '/' && baseend[0] != '\\') {
4014  break;
4015  }
4016 
4017  baseend -= 1;
4018  }
4019 
4020 
4021  // We just put a null terminator on the end.
4022  baseend[0] = '\0';
4023 
4024  return pathorig;
4025 }
4026 
4027 void drpath_copy_base_path(const char* path, char* baseOut, size_t baseSizeInBytes)
4028 {
4029  if (path == NULL || baseOut == NULL || baseSizeInBytes == 0) {
4030  return;
4031  }
4032 
4033  strcpy_s(baseOut, baseSizeInBytes, path);
4034  drpath_base_path(baseOut);
4035 }
4036 
4037 const char* drpath_file_name(const char* path)
4038 {
4039  if (path == NULL) {
4040  return NULL;
4041  }
4042 
4043  const char* fileName = path;
4044 
4045  // We just loop through the path until we find the last slash.
4046  while (path[0] != '\0') {
4047  if (path[0] == '/' || path[0] == '\\') {
4048  fileName = path;
4049  }
4050 
4051  path += 1;
4052  }
4053 
4054  // At this point the file name is sitting on a slash, so just move forward.
4055  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
4056  fileName += 1;
4057  }
4058 
4059  return fileName;
4060 }
4061 
4062 const char* drpath_copy_file_name(const char* path, char* fileNameOut, size_t fileNameSizeInBytes)
4063 {
4064  const char* fileName = drpath_file_name(path);
4065  if (fileName != NULL) {
4066  strcpy_s(fileNameOut, fileNameSizeInBytes, fileName);
4067  }
4068 
4069  return fileName;
4070 }
4071 
4072 const char* drpath_extension(const char* path)
4073 {
4074  if (path == NULL) {
4075  return NULL;
4076  }
4077 
4078  const char* extension = drpath_file_name(path);
4079  const char* lastoccurance = 0;
4080 
4081  // Just find the last '.' and return.
4082  while (extension[0] != '\0')
4083  {
4084  if (extension[0] == '.') {
4085  extension += 1;
4086  lastoccurance = extension;
4087  }
4088 
4089  extension += 1;
4090  }
4091 
4092  return (lastoccurance != 0) ? lastoccurance : extension;
4093 }
4094 
4095 
4096 dr_bool32 drpath_equal(const char* path1, const char* path2)
4097 {
4098  if (path1 == NULL || path2 == NULL) {
4099  return DR_FALSE;
4100  }
4101 
4102  if (path1 == path2 || (path1[0] == '\0' && path2[0] == '\0')) {
4103  return DR_TRUE; // Two empty paths are treated as the same.
4104  }
4105 
4106  drpath_iterator iPath1;
4107  drpath_iterator iPath2;
4108  if (drpath_first(path1, &iPath1) && drpath_first(path2, &iPath2))
4109  {
4110  dr_bool32 isPath1Valid;
4111  dr_bool32 isPath2Valid;
4112 
4113  do
4114  {
4115  if (!drpath_iterators_equal(iPath1, iPath2)) {
4116  return DR_FALSE;
4117  }
4118 
4119  isPath1Valid = drpath_next(&iPath1);
4120  isPath2Valid = drpath_next(&iPath2);
4121 
4122  } while (isPath1Valid && isPath2Valid);
4123 
4124  // At this point either iPath1 and/or iPath2 have finished iterating. If both of them are at the end, the two paths are equal.
4125  return isPath1Valid == isPath2Valid && iPath1.path[iPath1.segment.offset] == '\0' && iPath2.path[iPath2.segment.offset] == '\0';
4126  }
4127 
4128  return DR_FALSE;
4129 }
4130 
4131 dr_bool32 drpath_extension_equal(const char* path, const char* extension)
4132 {
4133  if (path == NULL || extension == NULL) {
4134  return DR_FALSE;
4135  }
4136 
4137  const char* ext1 = extension;
4138  const char* ext2 = drpath_extension(path);
4139 
4140 #ifdef _MSC_VER
4141  return _stricmp(ext1, ext2) == 0;
4142 #else
4143  return strcasecmp(ext1, ext2) == 0;
4144 #endif
4145 }
4146 
4147 
4148 
4149 dr_bool32 drpath_is_relative(const char* path)
4150 {
4151  if (path == NULL) {
4152  return DR_FALSE;
4153  }
4154 
4155  drpath_iterator seg;
4156  if (drpath_first(path, &seg)) {
4157  return !drpath_is_root_segment(seg);
4158  }
4159 
4160  // We'll get here if the path is empty. We consider this to be a relative path.
4161  return DR_TRUE;
4162 }
4163 
4164 dr_bool32 drpath_is_absolute(const char* path)
4165 {
4166  return !drpath_is_relative(path);
4167 }
4168 
4169 
4170 dr_bool32 drpath_append(char* base, size_t baseBufferSizeInBytes, const char* other)
4171 {
4172  if (base == NULL || other == NULL) {
4173  return DR_FALSE;
4174  }
4175 
4176  size_t path1Length = strlen(base);
4177  size_t path2Length = strlen(other);
4178 
4179  if (path1Length >= baseBufferSizeInBytes) {
4180  return DR_FALSE;
4181  }
4182 
4183 
4184  // Slash.
4185  if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
4186  base[path1Length] = '/';
4187  path1Length += 1;
4188  }
4189 
4190  // Other part.
4191  if (path1Length + path2Length >= baseBufferSizeInBytes) {
4192  path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
4193  }
4194 
4195  strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, other, path2Length);
4196 
4197  return DR_TRUE;
4198 }
4199 
4200 dr_bool32 drpath_append_iterator(char* base, size_t baseBufferSizeInBytes, drpath_iterator i)
4201 {
4202  if (base == NULL) {
4203  return DR_FALSE;
4204  }
4205 
4206  size_t path1Length = strlen(base);
4207  size_t path2Length = i.segment.length;
4208 
4209  if (path1Length >= baseBufferSizeInBytes) {
4210  return DR_FALSE;
4211  }
4212 
4214  if (baseBufferSizeInBytes > 1) {
4215  base[0] = '/';
4216  base[1] = '\0';
4217  return DR_TRUE;
4218  }
4219  }
4220 
4221  // Slash.
4222  if (path1Length > 0 && base[path1Length - 1] != '/' && base[path1Length - 1] != '\\') {
4223  base[path1Length] = '/';
4224  path1Length += 1;
4225  }
4226 
4227  // Other part.
4228  if (path1Length + path2Length >= baseBufferSizeInBytes) {
4229  path2Length = baseBufferSizeInBytes - path1Length - 1; // -1 for the null terminator.
4230  }
4231 
4232  strncpy_s(base + path1Length, baseBufferSizeInBytes - path1Length, i.path + i.segment.offset, path2Length);
4233 
4234  return DR_TRUE;
4235 }
4236 
4237 dr_bool32 drpath_append_extension(char* base, size_t baseBufferSizeInBytes, const char* extension)
4238 {
4239  if (base == NULL || extension == NULL) {
4240  return DR_FALSE;
4241  }
4242 
4243  size_t baseLength = strlen(base);
4244  size_t extLength = strlen(extension);
4245 
4246  if (baseLength >= baseBufferSizeInBytes) {
4247  return DR_FALSE;
4248  }
4249 
4250  base[baseLength] = '.';
4251  baseLength += 1;
4252 
4253  if (baseLength + extLength >= baseBufferSizeInBytes) {
4254  extLength = baseBufferSizeInBytes - baseLength - 1; // -1 for the null terminator.
4255  }
4256 
4257  strncpy_s(base + baseLength, baseBufferSizeInBytes - baseLength, extension, extLength);
4258 
4259  return DR_TRUE;
4260 }
4261 
4262 dr_bool32 drpath_copy_and_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
4263 {
4264  if (dst == NULL || dstSizeInBytes == 0) {
4265  return DR_FALSE;
4266  }
4267 
4268  strcpy_s(dst, dstSizeInBytes, base);
4269  return drpath_append(dst, dstSizeInBytes, other);
4270 }
4271 
4272 dr_bool32 drpath_copy_and_append_iterator(char* dst, size_t dstSizeInBytes, const char* base, drpath_iterator i)
4273 {
4274  if (dst == NULL || dstSizeInBytes == 0) {
4275  return DR_FALSE;
4276  }
4277 
4278  strcpy_s(dst, dstSizeInBytes, base);
4279  return drpath_append_iterator(dst, dstSizeInBytes, i);
4280 }
4281 
4282 dr_bool32 drpath_copy_and_append_extension(char* dst, size_t dstSizeInBytes, const char* base, const char* extension)
4283 {
4284  if (dst == NULL || dstSizeInBytes == 0) {
4285  return DR_FALSE;
4286  }
4287 
4288  strcpy_s(dst, dstSizeInBytes, base);
4289  return drpath_append_extension(dst, dstSizeInBytes, extension);
4290 }
4291 
4292 
4293 
4294 // This function recursively cleans a path which is defined as a chain of iterators. This function works backwards, which means at the time of calling this
4295 // function, each iterator in the chain should be placed at the end of the path.
4296 //
4297 // This does not write the null terminator, nor a leading slash for absolute paths.
4298 size_t _drpath_clean_trywrite(drpath_iterator* iterators, unsigned int iteratorCount, char* pathOut, size_t pathOutSizeInBytes, unsigned int ignoreCounter)
4299 {
4300  if (iteratorCount == 0) {
4301  return 0;
4302  }
4303 
4304  drpath_iterator isegment = iterators[iteratorCount - 1];
4305 
4306 
4307  // If this segment is a ".", we ignore it. If it is a ".." we ignore it and increment "ignoreCount".
4308  int ignoreThisSegment = ignoreCounter > 0 && isegment.segment.length > 0;
4309 
4310  if (isegment.segment.length == 1 && isegment.path[isegment.segment.offset] == '.')
4311  {
4312  // "."
4313  ignoreThisSegment = 1;
4314  }
4315  else
4316  {
4317  if (isegment.segment.length == 2 && isegment.path[isegment.segment.offset] == '.' && isegment.path[isegment.segment.offset + 1] == '.')
4318  {
4319  // ".."
4320  ignoreThisSegment = 1;
4321  ignoreCounter += 1;
4322  }
4323  else
4324  {
4325  // It's a regular segment, so decrement the ignore counter.
4326  if (ignoreCounter > 0) {
4327  ignoreCounter -= 1;
4328  }
4329  }
4330  }
4331 
4332 
4333  // The previous segment needs to be written before we can write this one.
4334  size_t bytesWritten = 0;
4335 
4336  drpath_iterator prev = isegment;
4337  if (!drpath_prev(&prev))
4338  {
4339  if (iteratorCount > 1)
4340  {
4341  iteratorCount -= 1;
4342  prev = iterators[iteratorCount - 1];
4343  }
4344  else
4345  {
4346  prev.path = NULL;
4347  prev.segment.offset = 0;
4348  prev.segment.length = 0;
4349  }
4350  }
4351 
4352  if (prev.segment.length > 0)
4353  {
4354  iterators[iteratorCount - 1] = prev;
4355  bytesWritten = _drpath_clean_trywrite(iterators, iteratorCount, pathOut, pathOutSizeInBytes, ignoreCounter);
4356  }
4357 
4358 
4359  if (!ignoreThisSegment)
4360  {
4361  if (pathOutSizeInBytes > 0)
4362  {
4363  pathOut += bytesWritten;
4364  pathOutSizeInBytes -= bytesWritten;
4365 
4366  if (bytesWritten > 0)
4367  {
4368  pathOut[0] = '/';
4369  bytesWritten += 1;
4370 
4371  pathOut += 1;
4372  pathOutSizeInBytes -= 1;
4373  }
4374 
4375  if (pathOutSizeInBytes >= isegment.segment.length)
4376  {
4377  strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, isegment.segment.length);
4378  bytesWritten += isegment.segment.length;
4379  }
4380  else
4381  {
4382  strncpy_s(pathOut, pathOutSizeInBytes, isegment.path + isegment.segment.offset, pathOutSizeInBytes);
4383  bytesWritten += pathOutSizeInBytes;
4384  }
4385  }
4386  }
4387 
4388  return bytesWritten;
4389 }
4390 
4391 size_t drpath_clean(const char* path, char* pathOut, size_t pathOutSizeInBytes)
4392 {
4393  if (path == NULL) {
4394  return 0;
4395  }
4396 
4397  drpath_iterator last;
4398  if (drpath_last(path, &last))
4399  {
4400  size_t bytesWritten = 0;
4401  if (path[0] == '/') {
4402  if (pathOut != NULL && pathOutSizeInBytes > 1) {
4403  pathOut[0] = '/';
4404  bytesWritten = 1;
4405  }
4406  }
4407 
4408  bytesWritten += _drpath_clean_trywrite(&last, 1, pathOut + bytesWritten, pathOutSizeInBytes - bytesWritten - 1, 0); // -1 to ensure there is enough room for a null terminator later on.
4409  if (pathOutSizeInBytes > bytesWritten) {
4410  pathOut[bytesWritten] = '\0';
4411  }
4412 
4413  return bytesWritten + 1;
4414  }
4415 
4416  return 0;
4417 }
4418 
4419 size_t drpath_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other)
4420 {
4421  if (base == NULL || other == NULL) {
4422  return 0;
4423  }
4424 
4425  drpath_iterator last[2] = {
4426  {NULL, {0, 0}},
4427  {NULL, {0, 0}}
4428  };
4429 
4430  dr_bool32 isPathEmpty0 = !drpath_last(base, last + 0);
4431  dr_bool32 isPathEmpty1 = !drpath_last(other, last + 1);
4432 
4433  int iteratorCount = !isPathEmpty0 + !isPathEmpty1;
4434  if (iteratorCount == 0) {
4435  return 0; // Both input strings are empty.
4436  }
4437 
4438  size_t bytesWritten = 0;
4439  if (base[0] == '/') {
4440  if (dst != NULL && dstSizeInBytes > 1) {
4441  dst[0] = '/';
4442  bytesWritten = 1;
4443  }
4444  }
4445 
4446  bytesWritten += _drpath_clean_trywrite(last, 2, dst + bytesWritten, dstSizeInBytes - bytesWritten - 1, 0); // -1 to ensure there is enough room for a null terminator later on.
4447  if (dstSizeInBytes > bytesWritten) {
4448  dst[bytesWritten] = '\0';
4449  }
4450 
4451  return bytesWritten + 1;
4452 }
4453 
4454 
4456 {
4457  if (path == NULL) {
4458  return DR_FALSE;
4459  }
4460 
4461  const char* extension = drpath_extension(path);
4462  if (extension != NULL) {
4463  extension -= 1;
4464  path[(extension - path)] = '\0';
4465  }
4466 
4467  return DR_TRUE;
4468 }
4469 
4470 dr_bool32 drpath_copy_and_remove_extension(char* dst, size_t dstSizeInBytes, const char* path)
4471 {
4472  if (dst == NULL || dstSizeInBytes == 0 || path == NULL) {
4473  return DR_FALSE;
4474  }
4475 
4476  const char* extension = drpath_extension(path);
4477  if (extension != NULL) {
4478  extension -= 1;
4479  }
4480 
4481  strncpy_s(dst, dstSizeInBytes, path, (size_t)(extension - path));
4482  return DR_TRUE;
4483 }
4484 
4485 
4487 {
4488  if (path == NULL) {
4489  return DR_FALSE;
4490  }
4491 
4492 
4493  // We just create an iterator that starts at the last segment. We then move back one and place a null terminator at the end of
4494  // that segment. That will ensure the resulting path is not left with a slash.
4495  drpath_iterator iLast;
4496  if (!drpath_last(path, &iLast)) {
4497  return DR_FALSE; // The path is empty.
4498  }
4499 
4500  if (drpath_is_root_segment(iLast)) {
4501  return DR_FALSE;
4502  }
4503 
4504 
4505  drpath_iterator iSecondLast = iLast;
4506  if (drpath_prev(&iSecondLast))
4507  {
4508  // There is a segment before it so we just place a null terminator at the end, but only if it's not the root of a Linux-style absolute path.
4509  if (drpath_is_linux_style_root_segment(iSecondLast)) {
4510  path[iLast.segment.offset] = '\0';
4511  } else {
4512  path[iSecondLast.segment.offset + iSecondLast.segment.length] = '\0';
4513  }
4514  }
4515  else
4516  {
4517  // This is already the last segment, so just place a null terminator at the beginning of the string.
4518  path[0] = '\0';
4519  }
4520 
4521  return DR_TRUE;
4522 }
4523 
4524 dr_bool32 drpath_copy_and_remove_file_name(char* dst, size_t dstSizeInBytes, const char* path)
4525 {
4526  if (dst == NULL) {
4527  return DR_FALSE;
4528  }
4529  if (dstSizeInBytes == 0) {
4530  return DR_FALSE;
4531  }
4532  if (path == NULL) {
4533  dst[0] = '\0';
4534  return DR_FALSE;
4535  }
4536 
4537 
4538  // We just create an iterator that starts at the last segment. We then move back one and place a null terminator at the end of
4539  // that segment. That will ensure the resulting path is not left with a slash.
4540  drpath_iterator iLast;
4541  if (!drpath_last(path, &iLast)) {
4542  dst[0] = '\0';
4543  return DR_FALSE; // The path is empty.
4544  }
4545 
4547  if (dstSizeInBytes > 1) {
4548  dst[0] = '/'; dst[1] = '\0';
4549  return DR_FALSE;
4550  } else {
4551  dst[0] = '\0';
4552  return DR_FALSE;
4553  }
4554  }
4555 
4557  return strncpy_s(dst, dstSizeInBytes, path + iLast.segment.offset, iLast.segment.length) == 0;
4558  }
4559 
4560 
4561  drpath_iterator iSecondLast = iLast;
4562  if (drpath_prev(&iSecondLast))
4563  {
4564  // There is a segment before it so we just place a null terminator at the end, but only if it's not the root of a Linux-style absolute path.
4565  if (drpath_is_linux_style_root_segment(iSecondLast)) {
4566  return strcpy_s(dst, dstSizeInBytes, "/") == 0;
4567  } else {
4568  return strncpy_s(dst, dstSizeInBytes, path, iSecondLast.segment.offset + iSecondLast.segment.length) == 0;
4569  }
4570  }
4571  else
4572  {
4573  // This is already the last segment, so just place a null terminator at the beginning of the string.
4574  dst[0] = '\0';
4575  return DR_TRUE;
4576  }
4577 }
4578 
4579 
4580 dr_bool32 drpath_to_relative(const char* absolutePathToMakeRelative, const char* absolutePathToMakeRelativeTo, char* relativePathOut, size_t relativePathOutSizeInBytes)
4581 {
4582  // We do this in two phases. The first phase just iterates past each segment of both the path to convert and the
4583  // base path until we find two that are not equal. The second phase just adds the appropriate ".." segments.
4584 
4585  if (relativePathOut == 0) {
4586  return DR_FALSE;
4587  }
4588  if (relativePathOutSizeInBytes == 0) {
4589  return DR_FALSE;
4590  }
4591 
4592  if (!drpath_is_absolute(absolutePathToMakeRelative) || !drpath_is_absolute(absolutePathToMakeRelativeTo)) {
4593  return DR_FALSE;
4594  }
4595 
4596 
4597  drpath_iterator iPath;
4598  drpath_iterator iBase;
4599  dr_bool32 isPathEmpty = !drpath_first(absolutePathToMakeRelative, &iPath);
4600  dr_bool32 isBaseEmpty = !drpath_first(absolutePathToMakeRelativeTo, &iBase);
4601 
4602  if (isPathEmpty && isBaseEmpty)
4603  {
4604  // Looks like both paths are empty.
4605  relativePathOut[0] = '\0';
4606  return DR_FALSE;
4607  }
4608 
4609 
4610  // Phase 1: Get past the common section.
4611  int isPathAtEnd = 0;
4612  int isBaseAtEnd = 0;
4613  while (!isPathAtEnd && !isBaseAtEnd && drpath_iterators_equal(iPath, iBase))
4614  {
4615  isPathAtEnd = !drpath_next(&iPath);
4616  isBaseAtEnd = !drpath_next(&iBase);
4617  }
4618 
4619  if (iPath.segment.offset == 0)
4620  {
4621  // The path is not relative to the base path.
4622  relativePathOut[0] = '\0';
4623  return DR_FALSE;
4624  }
4625 
4626 
4627  // Phase 2: Append ".." segments - one for each remaining segment in the base path.
4628  char* pDst = relativePathOut;
4629  size_t bytesAvailable = relativePathOutSizeInBytes;
4630 
4631  if (!drpath_at_end(iBase))
4632  {
4633  do
4634  {
4635  if (pDst != relativePathOut)
4636  {
4637  if (bytesAvailable <= 3)
4638  {
4639  // Ran out of room.
4640  relativePathOut[0] = '\0';
4641  return DR_FALSE;
4642  }
4643 
4644  pDst[0] = '/';
4645  pDst[1] = '.';
4646  pDst[2] = '.';
4647 
4648  pDst += 3;
4649  bytesAvailable -= 3;
4650  }
4651  else
4652  {
4653  // It's the first segment, so we need to ensure we don't lead with a slash.
4654  if (bytesAvailable <= 2)
4655  {
4656  // Ran out of room.
4657  relativePathOut[0] = '\0';
4658  return DR_FALSE;
4659  }
4660 
4661  pDst[0] = '.';
4662  pDst[1] = '.';
4663 
4664  pDst += 2;
4665  bytesAvailable -= 2;
4666  }
4667  } while (drpath_next(&iBase));
4668  }
4669 
4670 
4671  // Now we just append whatever is left of the main path. We want the path to be clean, so we append segment-by-segment.
4672  if (!drpath_at_end(iPath))
4673  {
4674  do
4675  {
4676  // Leading slash, if required.
4677  if (pDst != relativePathOut)
4678  {
4679  if (bytesAvailable <= 1)
4680  {
4681  // Ran out of room.
4682  relativePathOut[0] = '\0';
4683  return DR_FALSE;
4684  }
4685 
4686  pDst[0] = '/';
4687 
4688  pDst += 1;
4689  bytesAvailable -= 1;
4690  }
4691 
4692 
4693  if (bytesAvailable <= iPath.segment.length)
4694  {
4695  // Ran out of room.
4696  relativePathOut[0] = '\0';
4697  return DR_FALSE;
4698  }
4699 
4700  if (strncpy_s(pDst, bytesAvailable, iPath.path + iPath.segment.offset, iPath.segment.length) != 0)
4701  {
4702  // Error copying the string. Probably ran out of room in the output buffer.
4703  relativePathOut[0] = '\0';
4704  return DR_FALSE;
4705  }
4706 
4707  pDst += iPath.segment.length;
4708  bytesAvailable -= iPath.segment.length;
4709 
4710 
4711  } while (drpath_next(&iPath));
4712  }
4713 
4714 
4715  // Always null terminate.
4716  //assert(bytesAvailable > 0);
4717  pDst[0] = '\0';
4718 
4719  return DR_TRUE;
4720 }
4721 
4722 dr_bool32 drpath_to_absolute(const char* relativePathToMakeAbsolute, const char* basePath, char* absolutePathOut, size_t absolutePathOutSizeInBytes)
4723 {
4724  return drpath_append_and_clean(absolutePathOut, absolutePathOutSizeInBytes, basePath, relativePathToMakeAbsolute) != 0;
4725 }
4726 #endif //DR_IMPLEMENTATION
4727 
4728 
4729 
4730 /*
4731 This is free and unencumbered software released into the public domain.
4732 
4733 Anyone is free to copy, modify, publish, use, compile, sell, or
4734 distribute this software, either in source code form or as a compiled
4735 binary, for any purpose, commercial or non-commercial, and by any
4736 means.
4737 
4738 In jurisdictions that recognize copyright laws, the author or authors
4739 of this software dedicate any and all copyright interest in the
4740 software to the public domain. We make this dedication for the benefit
4741 of the public at large and to the detriment of our heirs and
4742 successors. We intend this dedication to be an overt act of
4743 relinquishment in perpetuity of all present and future rights to this
4744 software under copyright law.
4745 
4746 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
4747 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
4748 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
4749 IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
4750 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
4751 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
4752 OTHER DEALINGS IN THE SOFTWARE.
4753 
4754 For more information, please refer to <http://unlicense.org/>
4755 */
drpath_is_root_segment
dr_bool32 drpath_is_root_segment(const drpath_iterator i)
Determines whether or not the given iterator refers to the root segment of a path.
DR_FALSE
#define DR_FALSE
Definition: porcupine/demo/c/dr_libs/old/dr.h:72
drpath_iterator::path
const char * path
Definition: porcupine/demo/c/dr_libs/old/dr.h:3432
strncat_s
DR_INLINE int strncat_s(char *dst, size_t dstSizeInBytes, const char *src, size_t count)
Definition: porcupine/demo/c/dr_libs/old/dr.h:172
drpath_copy_base_path
void drpath_copy_base_path(const char *path, char *baseOut, size_t baseSizeInBytes)
dr_date_YYYYMMDD
void dr_date_YYYYMMDD(time_t t, char *strOut, unsigned int strOutSize)
drpath_to_backslashes
void drpath_to_backslashes(char *path)
drpath_copy_and_remove_extension
dr_bool32 drpath_copy_and_remove_extension(char *dst, size_t dstSizeInBytes, const char *path)
Creates a copy of the given string and removes the extension.
exists
ROSCPP_DECL bool exists(const std::string &service_name, bool print_failure_reason)
dr_thread_entry_proc
int(* dr_thread_entry_proc)(void *pData)
Definition: porcupine/demo/c/dr_libs/old/dr.h:632
drpath_is_absolute
dr_bool32 drpath_is_absolute(const char *path)
dr_uint32
uint32_t dr_uint32
Definition: porcupine/demo/c/dr_libs/old/dr.h:65
dr_delete_mutex
void dr_delete_mutex(dr_mutex mutex)
Deletes a mutex object.
drpath_last
dr_bool32 drpath_last(const char *path, drpath_iterator *i)
Creates an iterator beginning at the last segment.
dr_get_log_folder_path
dr_bool32 dr_get_log_folder_path(char *pathOut, size_t pathOutSize)
NULL
#define NULL
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/speex_resampler/thirdparty/resample.c:92
dr_randd
double dr_randd()
dr_get_executable_path
dr_bool32 dr_get_executable_path(char *pathOut, size_t pathOutSize)
dr_is_whitespace
dr_bool32 dr_is_whitespace(dr_uint32 utf32)
drpath_at_start
dr_bool32 drpath_at_start(drpath_iterator i)
dr_open_and_read_text_file
char * dr_open_and_read_text_file(const char *filePath, size_t *pFileSizeOut)
dr_int8
int8_t dr_int8
Definition: porcupine/demo/c/dr_libs/old/dr.h:60
drpath_first
dr_bool32 drpath_first(const char *path, drpath_iterator *i)
drpath_iterators_equal
dr_bool32 drpath_iterators_equal(const drpath_iterator i0, const drpath_iterator i1)
drpath_segment
Definition: porcupine/demo/c/dr_libs/old/dr.h:3422
dr_first_non_whitespace
const char * dr_first_non_whitespace(const char *str)
Finds the first non-whitespace character in the given string.
dr_next_token
const char * dr_next_token(const char *tokens, char *tokenOut, size_t tokenOutSize)
_stricmp
DR_INLINE int _stricmp(const char *string1, const char *string2)
Definition: porcupine/demo/c/dr_libs/old/dr.h:178
drpath_at_end
dr_bool32 drpath_at_end(drpath_iterator i)
drpath_next
dr_bool32 drpath_next(drpath_iterator *i)
dr_get_process_id
unsigned int dr_get_process_id()
drpath_iterator
struct drpath_iterator drpath_iterator
dr_hex_char_to_uint
dr_bool32 dr_hex_char_to_uint(char ascii, unsigned int *out)
dr_next_line
const char * dr_next_line(const char *str)
Finds the beginning of the next line.
time.h
FALSE
#define FALSE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:642
dr_create_thread
dr_thread dr_create_thread(dr_thread_entry_proc entryProc, void *pData)
dr_yield
void dr_yield()
dr_int16
int16_t dr_int16
Definition: porcupine/demo/c/dr_libs/old/dr.h:62
drpath_append
dr_bool32 drpath_append(char *base, size_t baseBufferSizeInBytes, const char *other)
drpath_base_path
char * drpath_base_path(char *path)
dr_parse_key_value_pairs_from_file
dr_bool32 dr_parse_key_value_pairs_from_file(const char *filePath, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void *pUserData)
dr_cmdline
Definition: porcupine/demo/c/dr_libs/old/dr.h:575
dr_mutex
void * dr_mutex
Mutex.
Definition: porcupine/demo/c/dr_libs/old/dr.h:665
dr_mkdir_recursive
dr_bool32 dr_mkdir_recursive(const char *directoryPath)
dr_release_semaphore
dr_bool32 dr_release_semaphore(dr_semaphore semaphore)
Releases the given semaphore and increments it's counter by one upon returning.
dr_next_power_of_2
DR_INLINE dr_uint32 dr_next_power_of_2(dr_uint32 value)
Definition: porcupine/demo/c/dr_libs/old/dr.h:119
drpath_is_descendant
dr_bool32 drpath_is_descendant(const char *descendantAbsolutePath, const char *parentAbsolutePath)
dr_trim
void dr_trim(char *str)
Trims both the leading and trailing whitespace from the given string.
dr_timer_init
void dr_timer_init(dr_timer *pTimer)
strncpy_s
DR_INLINE int strncpy_s(char *dst, size_t dstSizeInBytes, const char *src, size_t count)
Definition: porcupine/demo/c/dr_libs/old/dr.h:162
dr_open_and_read_file_with_extra_data
void * dr_open_and_read_file_with_extra_data(const char *pFilePath, size_t *pFileSizeOut, size_t extraBytes)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:621
dr_get_executable_directory_path
dr_bool32 dr_get_executable_directory_path(char *pathOut, size_t pathOutSize)
dr_get_username
size_t dr_get_username(char *usernameOut, size_t usernameOutSize)
drpath_clean
size_t drpath_clean(const char *path, char *pathOut, size_t pathOutSizeInBytes)
dr_init_cmdline_win32
dr_bool32 dr_init_cmdline_win32(dr_cmdline *pCmdLine, const char *args)
Initializes a command line object using a Win32 style command line.
dr_bool8
dr_uint8 dr_bool8
Definition: porcupine/demo/c/dr_libs/old/dr.h:69
dr_open_and_read_file
void * dr_open_and_read_file(const char *filePath, size_t *pFileSizeOut)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:675
strcat_s
DR_INLINE int strcat_s(char *dst, size_t dstSizeInBytes, const char *src)
Definition: porcupine/demo/c/dr_libs/old/dr.h:167
dr_timer_tick
double dr_timer_tick(dr_timer *pTimer)
drpath_segment::length
size_t length
Definition: porcupine/demo/c/dr_libs/old/dr.h:3425
dr_open_and_write_text_file
dr_bool32 dr_open_and_write_text_file(const char *filePath, const char *text)
drpath_iterator
Definition: porcupine/demo/c/dr_libs/old/dr.h:3430
dr_create_empty_file
dr_bool32 dr_create_empty_file(const char *fileName, dr_bool32 failIfExists)
dr_strncat_s
int dr_strncat_s(char *dst, size_t dstSizeInBytes, const char *src, size_t count)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:205
dr_create_mutex
dr_mutex dr_create_mutex()
dr_string_replace_ascii
void dr_string_replace_ascii(char *src, char c, char replacement)
dr_copy_file
dr_bool32 dr_copy_file(const char *srcPath, const char *dstPath, dr_bool32 failIfExists)
dr_iterate_files_proc
dr_bool32(* dr_iterate_files_proc)(const char *filePath, void *pUserData)
Definition: porcupine/demo/c/dr_libs/old/dr.h:422
dr_rtrim
static const char * dr_rtrim(const char *str)
dr_get_file_modified_time
dr_uint64 dr_get_file_modified_time(const char *filePath)
dr_datetime_short
void dr_datetime_short(time_t t, char *strOut, unsigned int strOutSize)
Formats a data/time string.
dr_lock_mutex
void dr_lock_mutex(dr_mutex mutex)
Locks the given mutex.
dr_get_current_directory
const char * dr_get_current_directory(char *pathOut, size_t pathOutSize)
Retrieves the current directory.
dr_wait_and_delete_thread
void dr_wait_and_delete_thread(dr_thread thread)
Helper function for waiting for a thread and then deleting the handle after it has terminated.
drpath_prev
dr_bool32 drpath_prev(drpath_iterator *i)
dr_parse_key_value_pairs
void dr_parse_key_value_pairs(dr_key_value_read_proc onRead, dr_key_value_pair_proc onPair, dr_key_value_error_proc onError, void *pUserData)
drpath_extension
const char * drpath_extension(const char *path)
dr_strcpy_s
int dr_strcpy_s(char *dst, size_t dstSizeInBytes, const char *src)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:101
TRUE
#define TRUE
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:641
drpath_extension_equal
dr_bool32 drpath_extension_equal(const char *path, const char *extension)
dr_cmdline::argc
int argc
Definition: porcupine/demo/c/dr_libs/old/dr.h:578
drpath_copy_and_append_extension
dr_bool32 drpath_copy_and_append_extension(char *dst, size_t dstSizeInBytes, const char *base, const char *extension)
strcpy_s
DR_INLINE int strcpy_s(char *dst, size_t dstSizeInBytes, const char *src)
Definition: porcupine/demo/c/dr_libs/old/dr.h:157
dr_parse_cmdline
void dr_parse_cmdline(dr_cmdline *pCmdLine, dr_cmdline_parse_proc callback, void *pUserData)
Parses the given command line.
dr_thread
void * dr_thread
Thread.
Definition: porcupine/demo/c/dr_libs/old/dr.h:631
dr_aligned_free
DR_INLINE void dr_aligned_free(void *ptr)
Definition: porcupine/demo/c/dr_libs/old/dr.h:320
dr_bool32
dr_uint32 dr_bool32
Definition: porcupine/demo/c/dr_libs/old/dr.h:70
dr_strncpy_s
int dr_strncpy_s(char *dst, size_t dstSizeInBytes, const char *src, size_t count)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:129
_itoa_s
DR_INLINE int _itoa_s(int value, char *dst, size_t dstSizeInBytes, int radix)
Definition: porcupine/demo/c/dr_libs/old/dr.h:184
dr_first_whitespace
const char * dr_first_whitespace(const char *str)
Finds the first occurance of a whitespace character in the given string.
count
size_t count
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/tests/test_common/ma_test_common.c:31
dr_string_replace
char * dr_string_replace(const char *src, const char *query, const char *replacement)
dr_uint64
uint64_t dr_uint64
Definition: porcupine/demo/c/dr_libs/old/dr.h:67
dr_delete_file
dr_bool32 dr_delete_file(const char *filePath)
dr_uint8
uint8_t dr_uint8
Definition: porcupine/demo/c/dr_libs/old/dr.h:61
drpath_to_absolute
dr_bool32 drpath_to_absolute(const char *relativePathToMakeAbsolute, const char *basePath, char *absolutePathOut, size_t absolutePathOutSizeInBytes)
dr_int64
int64_t dr_int64
Definition: porcupine/demo/c/dr_libs/old/dr.h:66
dr_randf
float dr_randf()
dr_strcat_s
int dr_strcat_s(char *dst, size_t dstSizeInBytes, const char *src)
Definition: porcupine/demo/c/dr_libs/tests/common/dr_common.c:163
dr_cmdline_key_exists
dr_bool32 dr_cmdline_key_exists(dr_cmdline *pCmdLine, const char *key)
Helper for determining whether or not the given key exists.
DR_TRUE
#define DR_TRUE
Definition: porcupine/demo/c/dr_libs/old/dr.h:71
dr_key_value_pair_proc
void(* dr_key_value_pair_proc)(void *pUserData, const char *key, const char *value)
Definition: porcupine/demo/c/dr_libs/old/dr.h:336
drpath_copy_and_append_iterator
dr_bool32 drpath_copy_and_append_iterator(char *dst, size_t dstSizeInBytes, const char *base, drpath_iterator i)
dr_now
time_t dr_now()
Retrieves a time_t as of the time the function was called.
DR_INLINE
#define DR_INLINE
Definition: porcupine/demo/c/dr_libs/old/dr.h:34
dr_mkdir
dr_bool32 dr_mkdir(const char *directoryPath)
dr_move_file
dr_bool32 dr_move_file(const char *oldPath, const char *newPath)
dr_create_semaphore
dr_semaphore dr_create_semaphore(int initialValue)
drpath_iterator::segment
drpath_segment segment
Definition: porcupine/demo/c/dr_libs/old/dr.h:3433
dr_timer
Definition: porcupine/demo/c/dr_libs/old/dr.h:707
L
#define L
Definition: porcupine/demo/c/dr_libs/tests/external/miniaudio/extras/stb_vorbis.c:5102
dr_is_directory
static dr_bool32 dr_is_directory(const char *directoryPath)
Definition: porcupine/demo/c/dr_libs/old/dr.h:456
drpath_copy_and_remove_file_name
dr_bool32 drpath_copy_and_remove_file_name(char *dst, size_t dstSizeInBytes, const char *path)
Creates a copy of the given string and removes the extension.
dr_utf32_to_utf16_ch
DR_INLINE int dr_utf32_to_utf16_ch(unsigned int utf32, unsigned short utf16[2])
Definition: porcupine/demo/c/dr_libs/old/dr.h:237
python.pvrecorder.CALLBACK
CALLBACK
Definition: porcupine/demo/c/pvrecorder/sdk/python/pvrecorder.py:17
dr_cmdline::win32
const char * win32
Definition: porcupine/demo/c/dr_libs/old/dr.h:582
drpath_is_relative
dr_bool32 drpath_is_relative(const char *path)
dr_cmdline_parse_proc
dr_bool32 dr_cmdline_parse_proc(const char *key, const char *value, void *pUserData)
Definition: porcupine/demo/c/dr_libs/old/dr.h:585
dr_delete_semaphore
void dr_delete_semaphore(dr_semaphore semaphore)
Deletes the given semaphore.
dr_free_argv
void dr_free_argv(char **argv)
dr_itoa_s
int dr_itoa_s(int value, char *dst, size_t dstSizeInBytes, int radix)
dr_sleep
void dr_sleep(unsigned int milliseconds)
dr_utf16pair_to_utf32_ch
DR_INLINE unsigned int dr_utf16pair_to_utf32_ch(unsigned short utf160, unsigned short utf161)
Converts a UTF-16 surrogate pair to UTF-32.
Definition: porcupine/demo/c/dr_libs/old/dr.h:293
dr_file_exists
dr_bool32 dr_file_exists(const char *filePath)
dr_winmain_to_argv
int dr_winmain_to_argv(const char *cmdlineWinMain, char ***argvOut)
dr_get_logical_processor_count
unsigned int dr_get_logical_processor_count()
Retrieves the number of logical cores on system.
dr_fopen
FILE * dr_fopen(const char *fileName, const char *openMode)
dr_wait_semaphore
dr_bool32 dr_wait_semaphore(dr_semaphore semaphore)
Waits on the given semaphore object and decrements it's counter by one upon returning.
dr_int32
int32_t dr_int32
Definition: porcupine/demo/c/dr_libs/old/dr.h:64
drpath_append_iterator
dr_bool32 drpath_append_iterator(char *base, size_t baseBufferSizeInBytes, drpath_iterator i)
Appends an iterator object to the given base path.
dr_directory_exists
dr_bool32 dr_directory_exists(const char *directoryPath)
dr_open_and_write_file
dr_bool32 dr_open_and_write_file(const char *filePath, const void *pData, size_t dataSize)
drpath_is_child
dr_bool32 drpath_is_child(const char *childAbsolutePath, const char *parentAbsolutePath)
drpath_append_and_clean
size_t drpath_append_and_clean(char *dst, size_t dstSizeInBytes, const char *base, const char *other)
Appends one path to the other and then cleans it.
dr_timer::counter
dr_int64 counter
Definition: porcupine/demo/c/dr_libs/old/dr.h:709
drpath_file_name
const char * drpath_file_name(const char *path)
dr_iterate_files
dr_bool32 dr_iterate_files(const char *directory, dr_bool32 recursive, dr_iterate_files_proc proc, void *pUserData)
dr_ltrim
static const char * dr_ltrim(const char *str)
Definition: porcupine/demo/c/dr_libs/old/dr.h:203
drpath_segment::offset
size_t offset
Definition: porcupine/demo/c/dr_libs/old/dr.h:3424
dr_unlock_mutex
void dr_unlock_mutex(dr_mutex mutex)
Unlocks the given mutex.
dr_key_value_read_proc
size_t(* dr_key_value_read_proc)(void *pUserData, void *pDataOut, size_t bytesToRead)
Definition: porcupine/demo/c/dr_libs/old/dr.h:335
dr_copy_line
size_t dr_copy_line(const char *str, char *lineOut, size_t lineOutSize)
Makes a copy of the first line of the given string.
python.test_porcupine.argv
argv
Definition: test_porcupine.py:158
dr_key_value_error_proc
void(* dr_key_value_error_proc)(void *pUserData, const char *message, unsigned int line)
Definition: porcupine/demo/c/dr_libs/old/dr.h:337
dr_set_current_directory
dr_bool32 dr_set_current_directory(const char *path)
Sets the current directory.
dr_semaphore
void * dr_semaphore
Semaphore.
Definition: porcupine/demo/c/dr_libs/old/dr.h:685
dr_aligned_malloc
DR_INLINE void * dr_aligned_malloc(size_t alignment, size_t size)
Definition: porcupine/demo/c/dr_libs/old/dr.h:306
dr_init_cmdline
dr_bool32 dr_init_cmdline(dr_cmdline *pCmdLine, int argc, char **argv)
Initializes a command line object.
dr_cmdline::argv
char ** argv
Definition: porcupine/demo/c/dr_libs/old/dr.h:579
dr_strrmchar
void dr_strrmchar(char *str, char c)
Removes every occurance of the given character from the given string.
drpath_remove_extension
dr_bool32 drpath_remove_extension(char *path)
drpath_to_relative
dr_bool32 drpath_to_relative(const char *absolutePathToMakeRelative, const char *absolutePathToMakeRelativeTo, char *relativePathOut, size_t relativePathOutSizeInBytes)
args
drpath_to_forward_slashes
void drpath_to_forward_slashes(char *path)
drpath_append_extension
dr_bool32 drpath_append_extension(char *base, size_t baseBufferSizeInBytes, const char *extension)
Appends an extension to the given path.
drpath_equal
dr_bool32 drpath_equal(const char *path1, const char *path2)
assert.h
dr_uint16
uint16_t dr_uint16
Definition: porcupine/demo/c/dr_libs/old/dr.h:63
drpath_copy_file_name
const char * drpath_copy_file_name(const char *path, char *fileNameOut, size_t fileNameSizeInBytes)
Copies the file name into the given buffer.
dr_get_config_folder_path
dr_bool32 dr_get_config_folder_path(char *pathOut, size_t pathOutSize)
dr_cmdline_to_argv
int dr_cmdline_to_argv(dr_cmdline *pCmdLine, char ***argvOut)
drpath_remove_file_name
dr_bool32 drpath_remove_file_name(char *path)
Removes the last segment from the given path.
dr_free_file_data
void dr_free_file_data(void *valueReturnedByOpenAndReadFile)
drpath_is_linux_style_root_segment
dr_bool32 drpath_is_linux_style_root_segment(const drpath_iterator i)
Determines whether or not the given iterator refers to a Linux style root directory ("/")
dr_is_file_read_only
dr_bool32 dr_is_file_read_only(const char *filePath)
drpath_segments_equal
dr_bool32 drpath_segments_equal(const char *s0Path, const drpath_segment s0, const char *s1Path, const drpath_segment s1)
drpath_copy_and_append
dr_bool32 drpath_copy_and_append(char *dst, size_t dstSizeInBytes, const char *base, const char *other)
dr_delete_thread
void dr_delete_thread(dr_thread thread)
dr_utf16_to_utf32_ch
DR_INLINE unsigned int dr_utf16_to_utf32_ch(unsigned short utf16[2])
Converts a UTF-16 character to UTF-32.
Definition: porcupine/demo/c/dr_libs/old/dr.h:268
drpath_is_win32_style_root_segment
dr_bool32 drpath_is_win32_style_root_segment(const drpath_iterator i)
Determines whether or not the given iterator refers to a Windows style root directory.
dr_wait_thread
void dr_wait_thread(dr_thread thread)
Waits for the given thread to terminate.


picovoice_driver
Author(s):
autogenerated on Fri Apr 1 2022 02:13:48