KittyUtils.hpp
1 #pragma once 2 3 #include <sys/mman.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 7 #include <regex.h> 8 #include <stdio.h> 9 #include <fcntl.h> 10 #include <unistd.h> 11 #include <dirent.h> 12 13 #include <errno.h> 14 #include <inttypes.h> 15 16 #include <cstring> 17 #include <cstdint> 18 #include <cstdarg> 19 20 #include <string> 21 #include <sstream> 22 #include <iomanip> 23 #include <memory> 24 #include <algorithm> 25 #include <vector> 26 #include <utility> 27 #include <map> 28 #include <random> 29 #include <functional> 30 #include <mutex> 31 #include <cctype> 32 33 #ifdef __ANDROID__ 34 #include <sys/system_properties.h> 35 #endif 36 37 /** 38 * @brief Returns the memory page size. 39 */ 40 inline size_t KTGetPageSize() 41 { 42 static size_t pageSize = 0; 43 if (pageSize == 0) 44 pageSize = (sysconf(_SC_PAGE_SIZE)); 45 46 return pageSize; 47 } 48 49 #define KT_PAGE_SIZE (KTGetPageSize()) 50 #define KT_PAGE_START(x) (uintptr_t(x) & ~(KT_PAGE_SIZE - 1)) 51 #define KT_PAGE_END(x) (KT_PAGE_START(uintptr_t(x) + KT_PAGE_SIZE - 1)) 52 #define KT_PAGE_OFFSET(x) (uintptr_t(x) - KT_PAGE_START(x)) 53 #define KT_PAGE_LEN(x) (size_t(KT_PAGE_SIZE - KT_PAGE_OFFSET(x))) 54 55 #define KT_ALIGN_UP(ptr, align) (((uintptr_t)(ptr) + (align) - 1) & ~((align) - 1)) 56 #define KT_ALIGN_DOWN(ptr, align) (((uintptr_t)(ptr)) & ~((uintptr_t)(align) - 1)) 57 58 #if defined(__ANDROID__) && defined(kUSE_LOGCAT) 59 60 #include <android/log.h> 61 #define KITTY_LOG_TAG "KittyMemoryEx" 62 63 #ifdef kITTYMEMORY_DEBUG 64 #define KITTY_LOGD(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, KITTY_LOG_TAG, fmt, ##__VA_ARGS__)) 65 #else 66 #define KITTY_LOGD(fmt, ...) \ 67 do \ 68 { \ 69 } while (0) 70 #endif 71 72 #define KITTY_LOGI(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, KITTY_LOG_TAG, fmt, ##__VA_ARGS__)) 73 #define KITTY_LOGE(fmt, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, KITTY_LOG_TAG, fmt, ##__VA_ARGS__)) 74 #define KITTY_LOGW(fmt, ...) ((void)__android_log_print(ANDROID_LOG_WARN, KITTY_LOG_TAG, fmt, ##__VA_ARGS__)) 75 76 #else 77 78 #ifdef kITTYMEMORY_DEBUG 79 #define KITTY_LOGD(fmt, ...) printf("D: " fmt "\n", ##__VA_ARGS__) 80 #else 81 #define KITTY_LOGD(fmt, ...) \ 82 do \ 83 { \ 84 } while (0) 85 #endif 86 87 #define KITTY_LOGI(fmt, ...) printf("I: " fmt "\n", ##__VA_ARGS__) 88 #define KITTY_LOGE(fmt, ...) fprintf(stderr, "E: " fmt "\n", ##__VA_ARGS__) 89 #define KITTY_LOGW(fmt, ...) printf("W: " fmt "\n", ##__VA_ARGS__) 90 91 #endif 92 93 #define KT_EINTR_RETRY(exp) \ 94 ({ \ 95 __typeof__(exp) _rc; \ 96 do \ 97 { \ 98 _rc = (exp); \ 99 } while (_rc == -1 && errno == EINTR); \ 100 _rc; \ 101 }) 102 103 #include <elf.h> 104 #ifdef __LP64__ 105 #define KT_ELFCLASS_BITS 64 106 #define KT_ELF_EICLASS 2 107 #define KT_ElfW(x) Elf64_##x 108 #define KT_ELFW(x) ELF64_##x 109 #else 110 #define KT_ELFCLASS_BITS 32 111 #define KT_ELF_EICLASS 1 112 #define KT_ElfW(x) Elf32_##x 113 #define KT_ELFW(x) ELF32_##x 114 #endif 115 #define KT_ELF_ST_BIND(val) (((unsigned char)(val)) >> 4) 116 #define KT_ELF_ST_TYPE(val) ((val) & 0xf) 117 #define KT_ELF_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) 118 #define KT_ELF_ST_VISIBILITY(o) ((o) & 0x03) 119 120 /** 121 * @brief Provides general utility functions. 122 */ 123 namespace KittyUtils 124 { 125 126 #ifdef __ANDROID__ 127 /** 128 * @brief Provides utility functions for Android. 129 */ 130 namespace Android 131 { 132 inline int getUserId() 133 { 134 uid_t uid = getuid(); // Linux UID 135 return uid / 100000; // approximate Android user ID 136 } 137 138 /** 139 * @brief Get an Android system property as a typed value. 140 * 141 * Supports std::string, bool, integral, and floating-point types. 142 * Returns `defaultValue` if the property is missing or conversion fails. 143 * 144 * @tparam T Desired type 145 * @param key Property name (e.g., "ro.build.version.sdk") 146 * @param defaultValue Value returned if property not found or conversion fails 147 * @return Property value converted to T 148 */ 149 template <typename T> 150 inline T getSystemProperty(const std::string &key, T defaultValue) 151 { 152 static_assert( 153 std::is_same_v<T, std::string> || std::is_same_v<T, bool> || std::is_integral_v<T> || 154 std::is_floating_point_v<T>, 155 "getSystemProperty: unsupported type. Supported types: string, bool, integral, floating-point."); 156 157 char value[PROP_VALUE_MAX] = {0}; 158 int len = __system_property_get(key.c_str(), value); 159 if (len <= 0) 160 return defaultValue; 161 162 if constexpr (std::is_same_v<T, std::string>) 163 { 164 return std::string(value, len); 165 } 166 else if constexpr (std::is_same_v<T, bool>) 167 { 168 for (int i = 0; i < len; ++i) 169 value[i] = std::tolower(value[i]); 170 171 if (std::strcmp(value, "1") == 0 || std::strcmp(value, "true") == 0 || std::strcmp(value, "y") == 0 || 172 std::strcmp(value, "yes") == 0) 173 return true; 174 175 if (std::strcmp(value, "0") == 0 || std::strcmp(value, "false") == 0 || std::strcmp(value, "n") == 0 || 176 std::strcmp(value, "no") == 0) 177 return false; 178 179 return defaultValue; 180 } 181 else if constexpr (std::is_integral_v<T>) 182 { 183 char *end = nullptr; 184 long long result = std::strtoll(value, &end, 0); 185 if (end == value) 186 return defaultValue; 187 return static_cast<T>(result); 188 } 189 else if constexpr (std::is_floating_point_v<T>) 190 { 191 char *end = nullptr; 192 double result = std::strtod(value, &end); 193 if (end == value) 194 return defaultValue; 195 return static_cast<T>(result); 196 } 197 198 // unsupported type 199 return defaultValue; 200 } 201 202 /** 203 * @brief Returns the version of the Android operating system. 204 */ 205 int getVersion(); 206 207 /** 208 * @brief Returns the SDK version of the Android operating system. 209 */ 210 int getSDK(); 211 212 /** 213 * @brief Returns true if Android operating system supports 64bit. 214 */ 215 bool is64BitSupported(); 216 217 /** 218 * @brief Get the base external storage directory. 219 * 220 * Usually: 221 * /storage/emulated/0 222 * 223 * Uses the EXTERNAL_STORAGE environment variable. 224 * 225 * @return Absolute path to external storage root, falls back to "/sdcard" if not defined. 226 */ 227 inline std::string getExternalStorage() 228 { 229 const char *storage = std::getenv("EXTERNAL_STORAGE"); 230 return (storage && storage[0] != '\0') ? storage : "/sdcard"; 231 } 232 233 /** 234 * @brief Get the Android app-specific internal data directory. 235 * 236 * Equivalent to: 237 * Context.getDataDir() 238 * 239 * Example: 240 * /data/user/<user_id>/<package_name> 241 * 242 * @param packageName Android application package name. 243 * @return Absolute path to app internal data directory. 244 */ 245 std::string getAppInternalDataDir(const std::string &packageName); 246 247 /** 248 * @brief Get the Android app-specific internal files directory. 249 * 250 * Equivalent to: 251 * Context.getFilesDir() 252 * 253 * Example: 254 * /data/user/<user_id>/<package_name>/files 255 * 256 * @param packageName Android application package name. 257 * @return Absolute path to app internal files directory. 258 */ 259 std::string getAppInternalFilesDir(const std::string &packageName); 260 261 /** 262 * @brief Get the Android app-specific internal cache directory. 263 * the system usually sets the TMPDIR environment variable. 264 * If TMPDIR is not set, it falls back to `/data/user/<user_id>/<package_name>/cache` 265 * 266 * Equivalent to: 267 * Context.getCacheDir() 268 * 269 * Example: 270 * /data/user/<user_id>/<package_name>/cache 271 * 272 * @param packageName Android application package name. 273 * @return Absolute path to app internal cache directory. 274 */ 275 std::string getAppInternalCacheDir(const std::string &packageName); 276 277 /** 278 * @brief Get the Android app-specific external data directory. 279 * 280 * Example: 281 * /storage/emulated/0/Android/data/<package> 282 * 283 * @param packageName Android application package name. 284 * @return Absolute path to app external data. 285 */ 286 inline std::string getAppExternalDataDir(const std::string &packageName) 287 { 288 return getExternalStorage() + "/Android/data/" + packageName; 289 } 290 291 /** 292 * @brief Get the Android app-specific external files directory. 293 * 294 * Equivalent to: 295 * Context.getExternalFilesDir(null) 296 * 297 * Example: 298 * /storage/emulated/0/Android/data/<package>/files 299 * 300 * @param packageName Android application package name. 301 * @return Absolute path to external files directory. 302 */ 303 inline std::string getAppExternalFilesDir(const std::string &packageName) 304 { 305 return getAppExternalDataDir(packageName) + "/files"; 306 } 307 308 /** 309 * @brief Get the Android app-specific external cache directory. 310 * 311 * Equivalent to: 312 * Context.getExternalCacheDir() 313 * 314 * Example: 315 * /storage/emulated/0/Android/data/<package>/cache 316 * 317 * @param packageName Android application package name. 318 * @return Absolute path to external cache directory. 319 */ 320 inline std::string getAppExternalCacheDir(const std::string &packageName) 321 { 322 return getAppExternalDataDir(packageName) + "/cache"; 323 } 324 325 /** 326 * @brief Get the Android app-specific external media directory (Android 10+). 327 * 328 * Example: 329 * /storage/emulated/0/Android/media/<package> 330 * 331 * @param packageName Android application package name. 332 * @return Absolute path to external media directory. 333 */ 334 inline std::string getAppExternalMediaDir(const std::string &packageName) 335 { 336 return getExternalStorage() + "/Android/media/" + packageName; 337 } 338 339 /** 340 * @brief Get the Android app-specific OBB directory. 341 * 342 * Example: 343 * /storage/emulated/0/Android/obb/<package> 344 * 345 * @param packageName Android application package name. 346 * @return Absolute path to OBB directory. 347 */ 348 inline std::string getAppObbDir(const std::string &packageName) 349 { 350 return getExternalStorage() + "/Android/obb/" + packageName; 351 } 352 } // namespace Android 353 #endif 354 355 /** 356 * @brief Untags a heap pointer by removing the top byte (TBI). 357 * @note Currently only implemented for android 11+ arm64 358 * 359 * @param p The heap pointer to be untagged. 360 * @return The untagged pointer. 361 */ 362 inline uintptr_t untagHeepPtr(uintptr_t p) 363 { 364 #if defined(__LP64__) && defined(__ANDROID__) 365 return (p & ((static_cast<uintptr_t>(1) << 56) - 1)); 366 #else 367 return p; 368 #endif 369 } 370 371 inline void *untagHeepPtr(void *p) 372 { 373 return reinterpret_cast<void *>(untagHeepPtr(uintptr_t(p))); 374 } 375 376 inline const void *untagHeepPtr(const void *p) 377 { 378 return reinterpret_cast<const void *>(untagHeepPtr(uintptr_t(p))); 379 } 380 381 /** 382 * @brief Provides utility functions for paths. 383 */ 384 namespace Path 385 { 386 /** 387 * @brief Extracts the file name from a given file path. 388 * 389 * @param filePath The full path of the file. 390 * 391 * @return file name. 392 */ 393 std::string fileName(const std::string &filePath); 394 395 /** 396 * @brief Extracts the directory from a given file path. 397 * 398 * @param filePath The full path of the file. 399 * 400 * @return The directory path. 401 */ 402 std::string fileDirectory(const std::string &filePath); 403 404 /** 405 * @brief Extracts the file extension from a given file path. 406 * 407 * @param filePath The full path of the file. 408 * 409 * @return The file extension. 410 */ 411 std::string fileExtension(const std::string &filePath); 412 } // namespace Path 413 414 /** 415 * @brief Provides utility functions for strings. 416 */ 417 namespace String 418 { 419 /** 420 * @brief Helper to compare two characters case-insensitively. 421 */ 422 inline bool charEqualsIgnoreCase(char a, char b) 423 { 424 return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b)); 425 } 426 427 /** 428 * @brief Checks if a string starts with a given prefix. 429 * 430 * @param str The string to check. 431 * @param prefix The prefix to look for. 432 * @param sensitive Whether the comparison should be case-sensitive (default is true). 433 * 434 * @return true if str starts with prefix, false otherwise. 435 */ 436 bool startsWith(const std::string &str, const std::string &prefix, bool sensitive = true); 437 438 /** 439 * @brief Checks if a string starts with any of the given prefixes. 440 * 441 * @param str The string to check. 442 * @param prefixes A list of prefixes to look for. 443 * @param sensitive Whether the comparison should be case-sensitive (default is true). 444 * 445 * @return true if str starts with at least one prefix in prefixes, false otherwise. 446 */ 447 bool startsWith(const std::string &str, const std::vector<std::string> &prefixes, bool sensitive = true); 448 449 /** 450 * @brief Checks if a string contains a given substring. 451 * 452 * @param str The string to check. 453 * @param substring The substring to look for. 454 * @param sensitive Whether the comparison should be case-sensitive (default is true). 455 * 456 * @return true if str contains substring, false otherwise. 457 */ 458 bool contains(const std::string &str, const std::string &substring, bool sensitive = true); 459 460 /** 461 * @brief Checks if a string contains any of the given substrings. 462 * 463 * @param str The string to check. 464 * @param substrings A list of substrings to look for. 465 * @param sensitive Whether the comparison should be case-sensitive (default is true). 466 * 467 * @return true if str contains at least one substring in substrings, false otherwise. 468 */ 469 bool contains(const std::string &str, const std::vector<std::string> &substrings, bool sensitive = true); 470 471 /** 472 * @brief Checks if a string ends with a given suffix. 473 * 474 * @param str The string to check. 475 * @param suffix The suffix to look for. 476 * @param sensitive Whether the comparison should be case-sensitive (default is true). 477 * 478 * @return true if str ends with suffix, false otherwise. 479 */ 480 bool endsWith(const std::string &str, const std::string &suffix, bool sensitive = true); 481 482 /** 483 * @brief Checks if a string ends with any of the given suffixes. 484 * 485 * @param str The string to check. 486 * @param suffixes A list of suffixes to look for. 487 * @param sensitive Whether the comparison should be case-sensitive (default is true). 488 * 489 * @return true if str ends with at least one suffix in suffixes, false otherwise. 490 */ 491 bool endsWith(const std::string &str, const std::vector<std::string> &suffixes, bool sensitive = true); 492 493 /** 494 * @brief Trims whitespace from the beginning and end of a string. 495 * 496 * @param str The string to be trimmed. 497 */ 498 void trim(std::string &str); 499 500 /** 501 * @brief Checks if the provided string is a valid hexadecimal representation. 502 * 503 * This function validates if the given string is a valid hexadecimal number. 504 * A valid hexadecimal number can contain characters '0'-'9' and 'A-F' or 'a-f'. 505 * 506 * @param hex The string to validate as a hexadecimal number. 507 * @return true if the string is a valid hexadecimal number, false otherwise. 508 */ 509 bool isValidHex(const std::string &hex); 510 511 /** 512 * @brief Validates a hexadecimal string. 513 * 514 * @param hex The hexadecimal string to validate. 515 * @return True if the string was validated, false otherwise. 516 */ 517 bool validateHex(std::string &hex); 518 519 /** 520 * @brief Formats a string using a printf-style format. 521 * 522 * @param fmt The format string. 523 * @param ... Variable arguments to be formatted. 524 * @return The formatted string. 525 */ 526 std::string fmt(const char *fmt, ...); 527 } // namespace String 528 529 /** 530 * @brief Generates a random number of type T within a specified range. 531 * 532 * @tparam T The type of the number. 533 * @param min The minimum range. 534 * @param min The maximum range. 535 * @return A random number. 536 */ 537 template <typename T> 538 T randInt(T min, T max) 539 { 540 using param_type = typename std::uniform_int_distribution<T>::param_type; 541 542 static std::mutex mtx; 543 std::lock_guard<std::mutex> lock(mtx); 544 545 static std::mt19937 gen{std::random_device{}()}; 546 547 std::uniform_int_distribution<T> dist; 548 return dist(gen, param_type{min, max}); 549 } 550 551 /** 552 * @brief Generates a random bytes of a specified length. 553 * 554 * @param length The length of the random bytes to generate. 555 * @return Vector containing random byte values in the range [0, 255]. 556 */ 557 std::vector<uint8_t> randomBytes(std::size_t length); 558 559 /** 560 * @brief Generates a random string of a specified length. 561 * 562 * @param length The length of the random string to generate. 563 * @return A random string. 564 */ 565 std::string randomString(size_t length); 566 567 /** 568 * @brief Provides utility functions for data. 569 */ 570 namespace Data 571 { 572 /** 573 * @brief Converts a hexadecimal string to a binary data buffer. 574 * @note data buffer must be large enough to fit. 575 * 576 * @param in The hexadecimal string to convert. 577 * @param data Pointer to the destination buffer where the binary data will be stored. 578 * 579 * @return True if the conversion was successful, false otherwise. 580 */ 581 bool fromHex(std::string in, void *data); 582 583 /** 584 * @brief Converts binary data to a hexadecimal string. 585 * 586 * @param data Pointer to the source binary data. 587 * @param dataLength Length of the binary data. 588 * 589 * @return A hexadecimal string representation of the binary data. 590 */ 591 std::string toHex(const void *data, const size_t dataLength); 592 593 /** 594 * @brief Converts a binary representation of a type T to a hexadecimal string. 595 * 596 * @tparam T The type of the binary data. 597 * @param data The instance of type T to convert. 598 * 599 * @return A hexadecimal string representation of the binary data. 600 */ 601 template <typename T> 602 std::string toHex(const T &data) 603 { 604 return toHex(&data, sizeof(T)); 605 } 606 607 /** 608 * @brief Hex dumps the memory block at the specified address. 609 * 610 * @tparam rowSize The size of each row in the hex dump. Default is 8 bytes. 611 * @tparam showASCII Whether to include ASCII representation of the memory block. Defult is true. 612 * 613 * @param address Pointer to the start of the memory block to dump. 614 * @param len Length of the memory block to dump. 615 * 616 * @return A string containing the hex dump of the memory block. 617 * 618 * @details This function generates a human-readable hex dump of a memory block. 619 * It prints the address, hexadecimal values, and ASCII representation of the block. 620 * The dump is formatted into rows of specified size, and each row includes the offset, 621 * byte values, and ASCII characters. The ASCII representation only includes printable 622 * characters, and non-printable characters are represented by '.'. 623 */ 624 template <size_t rowSize = 8, bool showASCII = true> 625 std::string hexDump(const void *address, size_t len) 626 { 627 if (!address || len == 0 || rowSize == 0) 628 return ""; 629 630 const unsigned char *data = static_cast<const unsigned char *>(address); 631 632 std::stringstream ss; 633 ss << std::hex << std::uppercase << std::setfill('0'); 634 635 size_t i, j; 636 637 for (i = 0; i < len; i += rowSize) 638 { 639 // offset 640 ss << std::setw(8) << i << ": "; 641 642 // row bytes 643 for (j = 0; (j < rowSize) && ((i + j) < len); j++) 644 ss << std::setw(2) << static_cast<unsigned int>(data[i + j]) << " "; 645 646 // fill row empty space 647 for (; j < rowSize; j++) 648 ss << " "; 649 650 // ASCII 651 if (showASCII) 652 { 653 ss << " "; 654 655 for (j = 0; (j < rowSize) && ((i + j) < len); j++) 656 { 657 if (std::isprint(data[i + j])) 658 ss << data[i + j]; 659 else 660 ss << '.'; 661 } 662 } 663 664 ss << std::endl; 665 } 666 667 return ss.str(); 668 } 669 } // namespace Data 670 671 /** 672 * @brief Provides utility functions for handling ZIP files. 673 */ 674 namespace Zip 675 { 676 /** 677 * @brief Structure to hold ZIP entry info. 678 */ 679 struct ZipEntryInfo 680 { 681 std::string fileName; 682 uint64_t compressedSize = 0; 683 uint64_t uncompressedSize = 0; 684 uint16_t compressionMethod = 0; 685 uint32_t crc32 = 0; 686 uint16_t modTime = 0; 687 uint16_t modDate = 0; 688 uint64_t dataOffset = 0; 689 }; 690 691 /** 692 * @brief Structure to hold memory mapped ZIP entry info. 693 */ 694 struct ZipEntryMMap 695 { 696 void *mappingBase = nullptr; 697 size_t mappingSize = 0; 698 uint8_t *data = nullptr; 699 uint64_t size = 0; 700 }; 701 702 /** 703 * @brief Finds the central directory of a ZIP file. 704 * 705 * @param data Pointer to the ZIP file data. 706 * @param fileSize Size of the ZIP file in bytes. 707 * @param cdOffset Pointer to store the offset of the central directory. 708 * @param totalEntries Pointer to store the total number of entries in the ZIP file. 709 * 710 * @return True if the central directory is found, false otherwise. 711 */ 712 bool findCentralDirectory(const uint8_t *data, uint64_t fileSize, uint64_t *cdOffset, uint64_t *totalEntries); 713 714 /** 715 * @brief Lists all entries in a ZIP file. 716 * 717 * @param zipPath Path to the ZIP file. 718 * 719 * @return A vector of ZipEntryInfo objects containing information about each entry. 720 */ 721 std::vector<ZipEntryInfo> listEntriesInZip(const std::string &zipPath); 722 723 /** 724 * @brief Finds the ZipEntryInfo for an entry by its data offset. 725 * 726 * @param zipPath Path to the ZIP file. 727 * @param dataOffset Offset of the entry in the ZIP file. 728 * @param out Pointer to store the ZipEntryInfo object if found. 729 * 730 * @return True if the entry info is found, false otherwise. 731 */ 732 bool findEntryInfoByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryInfo *out); 733 734 /** 735 * @brief Maps an entry in a ZIP file by its data offset. 736 * 737 * @param zipPath Path to the ZIP file. 738 * @param dataOffset Offset of the entry in the ZIP file. 739 * @param out Pointer to store the ZipEntryMMap object if found. 740 * 741 * @return True if the entry is mapped, false otherwise. 742 */ 743 bool mmapEntryByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryMMap *out); 744 } // namespace Zip 745 746 } // namespace KittyUtils