#include #include #include #include #include #include #include #include "utils.h" #include #include #include extern "C"{ #include "mp3/dr_mp3.h" #include #include "ini.h" #include "log.h" #include "net.h" #include "defines.h" #include "dialog.h" #include "zip/zip.h" } #include #include "installpkg.h" #include #include #include #include #include #include #include #include #include /*================== MISC GOLBAL VAR ============*/ static uint32_t sdkVersion = -1; static std::string checkedsize; static const char* sizes[] = { "EiB", "PiB", "TiB", "GiB", "MiB", "KiB", "B" }; static const uint64_t exbibytes = 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL; void* __stack_chk_guard = (void*)0xdeadbeef; size_t _orbisFile_lastopenFile_size; /* =========================================== */ /* ================== IF GLOBAL VARS========== */ ItemzSettings set, * get; extern std::string ipc_msg; extern std::atomic_int inserted_disc; extern std::atomic_bool is_disc_inserted; extern struct retry_t* cf_tex; extern int total_pages; extern ThreadSafeVector all_apps; // Installed_Apps bool sceAppInst_done = false; using namespace multi_select_options; bool (*sceStoreApiLaunchStore)(const char* query) = NULL; update_ret (*sceStoreApiCheckUpdate)(const char* tid) = NULL; /* =========================================== */ /* ================== MP3 VARS =====================-*/ #include #include #include ThreadSafeVector mp3_playlist; current_song_t current_song; static uint64_t off2 = 0; //drmp3_uint8 pData[DRMP3_DATA_CHUNK_SIZE]; // reading buffer from opened file, 16k static drmp3 mp3; // the main dr_mp3 object static drmp3_config Config; // and its config static std::atomic_int dataSize = 0; static std::vector mp3data; extern int g_idx; static std::atomic_int main_ch = 0; static std::atomic_int fill = 0; static std::atomic_bool m_bPlaying = false; static int numframe = 1; int retry_mp3_count = 0; static short play_buf[(1024 * 2) * 4], // small fixed buffer to append decoded pcm s16le samples snd[1152 * 2]; // small fixed buffer to store a single audio frame of decoded samples #define MP3_BUFFER_SIZE (1152) // max decoded s16le sample, for a single channel /*===============================================*/ /*============= USB VARS ========================*/ std::mutex usb_lock; std::atomic_int usb_number = ATOMIC_VAR_INIT(false); /*=============================================*/ /*-===================== THEME =========================*/ typedef struct { std::string name, version, date, author; bool is_image, has_font, using_sb; } theme; const char *required_theme_files[] = { "theme/btn_o.png", "theme/btn_sq.png", "theme/btn_tri.png", "theme/btn_x.png", "theme/btn_up.png", "theme/btn_down.png", "theme/btn_left.png", "theme/btn_right.png", "theme/btn_l1.png", "theme/btn_l2.png", "theme/btn_r1.png" }; const char *if_d_zip[] = { "ONLY WORKS IN ITEMZFLOW" }; bool reboot_app = false; #include "shaders.h" double CalcFreeGigs(const char* path) { struct statfs st; if (statfs(path, &st) != 0) { return 0; } return (double)st.f_bfree * st.f_bsize / (1024 * 1024 * 1024); } #if __cplusplus extern "C" { #endif int64_t sys_dynlib_load_prx(char* prxPath, int* moduleID) { return (int64_t)syscall4(594, prxPath, 0, moduleID, 0); } int64_t sys_dynlib_unload_prx(int64_t prxID) { return (int64_t)syscall1(595, (void*)prxID); } int64_t sys_dynlib_dlsym(int64_t moduleHandle, const char* functionName, void* destFuncOffset) { return (int64_t)syscall3(591, (void*)moduleHandle, (void*)functionName, destFuncOffset); } unsigned char* orbisFileGetFileContent(const char* filename) { _orbisFile_lastopenFile_size = -1; FILE* file = fopen(filename, "rb"); if (!file) { log_error("Unable to open file \"%s\".", filename); return NULL; } fseek(file, 0, SEEK_END); size_t size = ftell(file); fseek(file, 0, SEEK_SET); unsigned char* buffer = (unsigned char*)malloc((size + 1) * sizeof(char)); fread(buffer, sizeof(char), size, file); buffer[size] = 0; _orbisFile_lastopenFile_size = size; fclose(file); return buffer; } int sceSystemServiceKillApp(uint32_t appid, int opt,int method, int reason); int sceShellUIUtilInitialize(); int sceShellUIUtilLaunchByUri(const char* uri, SceShellUIUtilLaunchByUriParam *Param); int sceAppInstUtilAppUnInstallAddcont(const char* title_id, const char* content_id); int sceAppInstUtilAppUnInstallPat(const char* tid); int malloc_stats_fast(LibcMallocManagedSize *ManagedSize); #ifdef __cplusplus } #endif /*=====================================================*/ /* * Creates all the directories in the provided path. (can include a filename) * (directory must end with '/') */ void* prx_func_loader(std::string prx_path, std::string symbol) { void* addrp = NULL; log_info("Loading %s", prx_path.c_str()); int libcmi = sceKernelLoadStartModule(prx_path.c_str(), 0, nullptr, 0, 0, 0); if(libcmi < 0){ log_error("Error loading prx: 0x%X", libcmi); return addrp; } else log_debug("%s loaded successfully", prx_path.c_str()); if(sceKernelDlsym(libcmi, symbol.c_str(), &addrp) < 0){ log_error("Symbol %s NOT Found", symbol.c_str()); } else log_debug("Function %s | addr %p loaded successfully", symbol.c_str(), addrp); return addrp; } // folder that sees if a path is a folder bool is_folder(const char* path) { struct stat st; if (stat(path, &st) == 0) return S_ISDIR(st.st_mode); return false; } static int theme_info(void* user, const char* section, const char* name, const char* value, int unused) { static char prev_section[50] = ""; if (strcmp("THEME", prev_section)) { log_debug("%s [%s]", (prev_section[0] ? "" : ""), section); strncpy(prev_section, section, sizeof(prev_section)); prev_section[sizeof(prev_section) - 1] = '\0'; } theme* set = (theme*)user; #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 if (MATCH("THEME", "Author")) { set->author = value; } if (MATCH("THEME", "Date")) { set->date = value; } if (MATCH("THEME", "Name")) { set->name = value; } if (MATCH("THEME", "Version")) { set->version = value; } if (MATCH("THEME", "Image")) { set->is_image = atol(value); } if (MATCH("THEME", "Font")) { set->has_font = atol(value); } if (MATCH("THEME", "Shader") && !set->is_image) { set->using_sb = atol(value); } return 0; } void __stack_chk_fail(void) { log_info("Stack smashing detected."); msgok(MSG_DIALOG::FATAL, "Stack Smashing has been Detected"); } bool IS_ERROR(uint32_t ret) { return ret & 0x80000000; } std::string calculateSize(uint64_t size) { std::string result; uint64_t multiplier = exbibytes; int i; for (i = 0; i < DIM(sizes); i++, multiplier /= 1024) { if (size < multiplier) continue; if (size % multiplier == 0) result = fmt::format("{0:} {1:}", size / multiplier, sizes[i]); else result = fmt::format("{0:.1f} {1:}", (float)(size / multiplier), sizes[i]); return result; } result = "0"; return result; } size_t CalcAppsize(const char* filename) { struct stat st; if(stat(filename, &st) != 0) { return 0; } return st.st_size; } int ItemzLocalKillApp(uint32_t appid) { log_info("ItemzLocalKillApp(%x, -1, 0, 0)", appid); if ((appid & ~0xFFFFFF) == 0x60000000) return sceSystemServiceKillApp(appid, -1, 0, 0); else return ITEMZCORE_EINVAL; } int ItemzLaunchByUri(const char* uri) { int libcmi = -1; if (!uri) return ITEMZCORE_EINVAL; if (sys_dynlib_load_prx((char*)"/system/common/lib/libSceShellUIUtil.sprx", &libcmi) < 0 || libcmi < 0) return ITEMZCORE_PRX_RESOLVE_ERROR; SceShellUIUtilLaunchByUriParam Param; Param.size = sizeof(SceShellUIUtilLaunchByUriParam); sceUserServiceGetForegroundUser(&Param.userId); // DONT CARE log_info("ItemzLaunchByUri(%s)", uri); log_info("sceShellUIUtilInitialize returned %x", sceShellUIUtilInitialize()); return sceShellUIUtilLaunchByUri(uri, &Param); } bool copyFile(std::string source, std::string dest, bool show_progress) { std::string buf; int src = sceKernelOpen(source.c_str(), 0x0000, 0); if (src > 0) { int out = sceKernelOpen(dest.c_str(), 0x0001 | 0x0200 | 0x0400, 0777); if (out > 0) { if(show_progress) progstart("Starting..."); size_t bytes ,total = 0; size_t len = CalcAppsize(source.c_str()); std::string size = calculateSize(len); std::vector buffer(MB(10)); if (buffer.size() > 0) { while (0 < (bytes = sceKernelRead(src, &buffer[0], MB(10)))){ sceKernelWrite(out, &buffer[0], bytes); total += bytes; if(show_progress){ uint32_t g_progress = (uint32_t)(((float)total / len) * 100.f); int status = sceMsgDialogUpdateStatus(); if (ORBIS_COMMON_DIALOG_STATUS_RUNNING == status) { buf = fmt::format("{0:}...\n Size: {1:}", getLangSTR(LANG_STR::COPYING_FILE), size); sceMsgDialogProgressBarSetValue(0, g_progress); sceMsgDialogProgressBarSetMsg(0, buf.c_str()); } } } buffer.clear(); } } else { fmt::print("Could copy file: {} Reason: {x}", src, out); sceKernelClose(src); if(show_progress) sceMsgDialogTerminate(); return false; } sceKernelClose(src); sceKernelClose(out); sceMsgDialogTerminate(); return true; } else { fmt::print("Could copy file: {} Reason: {x}", dest, src); return false; } } void ProgSetMessagewText(int prog, const char* fmt, ...) { char buff[300]; memset(&buff[0], 0, sizeof(buff)); va_list args; va_start(args, fmt); vsnprintf(&buff[0], 299, fmt, args); va_end(args); sceMsgDialogProgressBarSetValue(0, prog); sceMsgDialogProgressBarSetMsg(0, buff); } /// timing float get_time_ms(std::chrono::time_point &last_time) { auto now = std::chrono::system_clock::now(); auto duration = now - last_time; auto millis = std::chrono::duration_cast(duration).count(); last_time = now; return (float)(millis / 1000.f); } bool touch_file(const char* destfile) { int fd = open(destfile, O_WRONLY | O_CREAT | O_TRUNC, 0777); if (fd > 0) { unlink(destfile); close(fd); return true; } else return false; } #define SCE_KERNEL_EVF_WAITMODE_OR 0x02 unsigned char _bittest64(uint64_t *Base, uint64_t Offset) { int old = 0; __asm__ __volatile__("btq %2,%1\n\tsbbl %0,%0 " :"=r" (old),"=m" ((*(volatile long long *) Base)) :"Ir" (Offset)); return (old != 0); } // both real and _func functions are the same unsigned int sceShellCoreUtilIsUsbMassStorageMounted(unsigned int usb_index); //interchangable unsigned int sceShellCoreUtilIsUsbMassStorageMounted_func(unsigned int usb_index) { uint64_t res; void* ef; if ( (unsigned int)sceKernelOpenEventFlag(&ef, "SceAutoMountUsbMass") ) return 0; sceKernelPollEventFlag(ef, 0xFFFFFFF, SCE_KERNEL_EVF_WAITMODE_OR, &res); sceKernelCloseEventFlag(ef); return _bittest64(&res, usb_index); } unsigned int usbpath() { // std::lock_guard lock(disc_lock); unsigned int usb_index = -1; for (int i = 0; i < 8; i++){ if (sceShellCoreUtilIsUsbMassStorageMounted_func((unsigned int)i)){ usb_index = i; //log_info("[UTIL] USB %i is mounted, SceAutoMountUsbMass: %i", i, usb_number); break; } } return usb_index; } bool MD5_file_compare(const char* file1, const char* file2) { unsigned char c[MD5_HASH_LENGTH]; unsigned char c2[MD5_HASH_LENGTH]; int i; FILE* f1 = fopen(file1, "rb"); FILE* f2 = fopen(file2, "rb"); if(!f1) { log_error("Could not open both files"); return false; } if(!f2) { fclose(f1); log_error("Could not open both files"); return false; } MD5_CTX mdContext; MD5_CTX mdContext2; int bytes2 = 0; unsigned char data2[1024]; int bytes = 0; unsigned char data[1024]; MD5_Init(&mdContext); while ((bytes = fread(data, 1, 1024, f1)) != 0) MD5_Update(&mdContext, data, bytes); MD5_Final(c, &mdContext); MD5_Init(&mdContext2); while ((bytes2 = fread(data2, 1, 1024, f2)) != 0) MD5_Update(&mdContext2, data2, bytes2); MD5_Final(c2, &mdContext2); for (i = 0; i < 16; i++) { if (c[i] != c2[i]){ log_error("MD5 compare failed: %s != %s HASH %s | %s", file1, file2, c, c2); fclose(f1); fclose(f2); return false; } } fclose(f1); fclose(f2); log_info("MD5s match for %s and %s", file1, file2); return true; } bool MD5_hash_compare(const char* file1, const char* hash) { unsigned char c[MD5_HASH_LENGTH]; FILE* f1 = fopen(file1, "rb"); if(!f1) return false; MD5_CTX mdContext; int bytes = 0; unsigned char data[1024]; MD5_Init(&mdContext); while ((bytes = fread(data, 1, 1024, f1)) != 0) MD5_Update(&mdContext, data, bytes); MD5_Final(c, &mdContext); char md5_string[33] = {0}; for(int i = 0; i < 16; ++i) { snprintf(&md5_string[i*2], 32, "%02x", (unsigned int)c[i]); } log_info( "[MD5] FILE HASH: %s", md5_string); md5_string[32] = 0; if (strcmp(md5_string, hash) != 0) { fclose(f1); return DIFFERENT_HASH; } log_info( "[MD5] Input HASH: %s", hash); fclose(f1); return SAME_HASH; } bool is_apputil_init() { int ret = sceAppInstUtilInitialize(); if (ret) { log_error( " sceAppInstUtilInitialize failed: 0x%08X", ret); return false; } else sceAppInst_done = true; return true; } bool app_inst_util_uninstall_patch(const char* title_id, int* error) { int ret; *error = 0; if(!sceAppInst_done) { bool res = is_apputil_init(); if (!res) { *error = 0xDEADBEEF; return false; } } if (!title_id) { ret = 0x80020016; if (error) { *error = ret; } goto err; } ret = sceAppInstUtilAppUnInstallPat(title_id); if (ret) { if (error) { *error = ret; } log_error( " sceAppInstUtilAppUnInstallPat failed: 0x%08X", ret); goto err; } return true; err: log_error("app_inst_util_uninstall_patch: 0x%X", error); return false; } bool app_inst_util_uninstall_game(const char *title_id, int *error) { int ret; *error = 0; if (strlen(title_id) != 9) { *error = 0xBADBAD; return false; } if(!sceAppInst_done) { bool res = is_apputil_init(); if (!res) { *error = 0xDEADBEEF; return false; } } if (!title_id) { ret = 0x80020016; if (error) { *error = ret; } goto err; } ret = sceAppInstUtilAppUnInstall(title_id); if (ret) { if (error) { *error = ret; } log_error( " sceAppInstUtilAppUnInstall failed: 0x%08X", ret); goto err; } return true; err: log_error("app_inst_util_uninstall_game: 0x%X", error); return false; } bool app_inst_util_uninstall_ac(const char* content_id, const char* title_id, int* error) { *error = 0; if (!sceAppInst_done) { bool res = is_apputil_init(); if (!res) { *error = 0xDEADBEEF; return false; } } int ret = sceAppInstUtilAppUnInstallAddcont(title_id, content_id); if (ret) { if (error) { *error = ret; } log_error("sceAppInstUtilAppUnInstallAddcont failed: 0x%08X", ret); return false; } return true; } int app_inst_util_get_size(const char* title_id) { uint64_t size = 0; if (!sceAppInst_done) { log_debug(" Starting app_inst_util_init.."); if (!app_inst_util_init()) { log_error("app_inst_util_init has failed..."); return ITEMZCORE_GENERAL_ERROR; } } int ret = sceAppInstUtilAppGetSize(title_id, &size); if (ret) { log_error("sceAppInstUtilAppGetSize failed: 0x%08X", ret); return ret; } else log_info("Size: %s", calculateSize(size).c_str()); return B2GB(size); } bool Fnt_setting_enabled = false; static int print_ini_info(void* user, const char* section, const char* name, const char* value, int unused) { static char prev_section[50] = ""; if (strcmp(section, prev_section)) { log_debug("%s[%s]", (prev_section[0] ? "" : ""), section); strncpy(prev_section, section, sizeof(prev_section)); prev_section[sizeof(prev_section) - 1] = '\0'; } ItemzSettings* set = (ItemzSettings*)user; #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 //log_info("Settings: %s = %s", name, value); if (MATCH("Settings", "Daemon_on_start")) { set->setting_bools[Daemon_on_start] = atoi(value); } else if (MATCH("Settings", "TTF_Font")) { set->setting_strings[FNT_PATH] = value; if (set->setting_strings[FNT_PATH].find("SCE-PS3-RD-R-LATIN.TTF") == std::string::npos){ log_debug("Font is not default, enabling font settings"); Fnt_setting_enabled = true; } } else if (MATCH("Settings", "HomeMenu_Redirection")) { set->setting_bools[HomeMenu_Redirection] = atoi(value); } else if (MATCH("Settings", "Show_install_prog")) { set->setting_bools[Show_install_prog] = atoi(value); } else if (MATCH("Settings", "Dumper_option")) { set->Dump_opt = (Dump_Multi_Sels)atoi(value); } else if (MATCH("Settings", "Sort_By")) { set->sort_by = (Sort_Multi_Sel)atoi(value); } else if (MATCH("Settings", "Sort_Cat")) { set->sort_cat = (Sort_Category)atoi(value); } else if (MATCH("Settings", "cover_message")) { set->setting_bools[cover_message] = atoi(value); } else if (MATCH("Settings", "Dumper_Path")) { set->setting_strings[DUMPER_PATH] = value; } else if (MATCH("Settings", "MP3_Path")) { set->setting_strings[MP3_PATH] = value; } else if (MATCH("Settings", "Show_Buttons")) { set->setting_bools[Show_Buttons] = atoi(value); } else if (MATCH("Settings", "Enable_Theme")) { set->setting_bools[using_theme] = atoi(value); log_debug("Enable_Theme: %d : %d", set->setting_bools[using_theme], atoi(value)); } else if (MATCH("Settings", "Image_path")) { set->setting_strings[IMAGE_PATH] = value;//nReflections } else if (MATCH("Settings", "Reflections")) { use_reflection = atoi(value); } else if (MATCH("Settings", "Fuse_IP")) { set->setting_strings[FUSE_PC_NFS_IP] = value; } else if(MATCH("Settings", "Internal_update")){ set->setting_bools[INTERNAL_UPDATE] = atoi(value); } return 1; } bool LoadOptions(ItemzSettings *set) { bool no_error = true; int error = 1; theme t; unsigned int usb_num = usbpath(); set->setting_bools.reserve(NUMB_OF_BOOLS); set->setting_strings.reserve(NUMB_OF_STRINGS); //"http://IFUpdate.pkg-zone.com" dump = SEL_RESET; set->setting_bools[Daemon_on_start] = true; set->Dump_opt = SEL_DUMP_ALL; set->sort_cat = NO_CATEGORY_FILTER; set->sort_by = multi_select_options::NA_SORT; set->setting_bools[using_sb] = true; set->setting_bools[INTERNAL_UPDATE] = false; set->setting_bools[Show_install_prog] = true; set->setting_bools[Show_Buttons] = true; set->setting_bools[HomeMenu_Redirection] = false; set->setting_bools[cover_message] = true; set->setting_strings[THEME_NAME] = "Default Theme"; set->setting_strings[THEME_AUTHOR] = "LM & MZ"; set->setting_strings[THEME_VERSION] = "1.00"; set->ItemzCore_AppId = sceSystemServiceGetAppIdOfMiniApp(); set->ItemzDaemon_AppId = -1; if(usb_num != -1) set->setting_strings[DUMPER_PATH] = fmt::format("/mnt/usb{}/", usb_num); else set->setting_strings[DUMPER_PATH] = "/mnt/usb0"; set->setting_strings[FNT_PATH] = "/system_ex/app/NPXS20113/bdjstack/lib/fonts/SCE-PS3-RD-R-LATIN.TTF"; /* Initialize INI structure */ if (usb_num != -1) { set->setting_strings[INI_PATH] = fmt::format("{}/settings.ini", set->setting_strings[DUMPER_PATH]); if (!if_exists(set->setting_strings[INI_PATH].c_str())) { log_warn("No INI on USB"); if (if_exists("/user/app/ITEM00001/settings.ini")){ set->setting_strings[INI_PATH] = "/user/app/ITEM00001/settings.ini"; error = ini_parse("/user/app/ITEM00001/settings.ini", print_ini_info, set); if (error < 0) log_error("Bad config file (first error on line %d)!", error); } } else { error = ini_parse(set->setting_strings[INI_PATH].c_str(), print_ini_info, set); if (error < 0) log_error("Bad config file (first error on line %d)!", error); log_info( " Loading ini from USB"); } } else if (!if_exists("/user/app/ITEM00001/settings.ini")) { log_error("CANT FIND INI"); no_error = false; } else { error = ini_parse("/user/app/ITEM00001/settings.ini", print_ini_info, set); if (error < 0) log_error("Bad config file (first error on line %d)!", error); log_info( " Loading ini from APP DIR"); set->setting_strings[INI_PATH] = "/user/app/ITEM00001/settings.ini"; } if(set->setting_bools[using_theme] && (set->setting_bools[using_theme] = if_exists(APP_PATH("theme/theme.ini")))){ log_debug("[THEME] Using Theme detected"); log_debug("[THEME] Getting Theme Info"); t.is_image = false; t.using_sb = false; t.has_font = false; if(ini_parse(APP_PATH("theme/theme.ini"), theme_info, &t) < 0){ log_error("[THEME] Failed to get Theme Info"); set->setting_bools[using_theme] = false; goto no_theme; } log_debug("[THEME] ======== Theme Info ======="); fmt::print("[THEME] Name: {}", t.name); fmt::print("[THEME] Version: {}", t.version); fmt::print("[THEME] Author: {}", t.author); fmt::print("[THEME] Date: {}", t.date); set->setting_strings[THEME_NAME] = t.name; set->setting_strings[THEME_VERSION] = t.version; set->setting_strings[THEME_AUTHOR] = t.author; log_debug("[THEME] Image: %s", t.is_image ? "Yes" : "No"); log_debug("[THEME] Font: %s", t.has_font ? "Yes" : "No"); log_debug("[THEME] Shader: %s", t.using_sb ? "Yes" : "No"); log_debug("[THEME] ======== End of Theme Info ======="); log_debug("[THEME] Setting ItemzCore settings ..."); if(t.has_font){ //THEME FONT CHECK set->setting_strings[FNT_PATH] = fmt::format("{}/font.ttf", APP_PATH("theme")); if(!if_exists(set->setting_strings[FNT_PATH].c_str())){ log_error("[THEME] Font not found"); t.has_font = false; } else{ log_debug("[THEME] Font found"); Fnt_setting_enabled = true; } } else log_debug("[THEME] No Font being used"); if(t.is_image){ set->setting_strings[IMAGE_PATH] = fmt::format("{}/background.png", APP_PATH("theme")); if(!if_exists( set->setting_strings[IMAGE_PATH].c_str())){ fmt::print("[THEME] Image not found, looked @ {}", set->setting_strings[IMAGE_PATH]); set->setting_bools[has_image] = t.is_image = false; } else{ fmt::print("[THEME] Image found @ {}", set->setting_strings[IMAGE_PATH]); set->setting_bools[has_image] = true; set->setting_bools[using_sb] = false; } } else { log_debug("[THEME] No Image being used"); set->setting_strings[SB_PATH] = fmt::format("{}/shader.bin", APP_PATH("theme")); if(!if_exists(set->setting_strings[SB_PATH].c_str())){ log_error("[THEME] Shader not found"); set->setting_bools[using_sb] = t.using_sb = false; } else{ log_debug("[THEME] Shader found"); set->setting_bools[using_sb] = true; set->setting_bools[has_image] = false; } } log_debug("[THEME] Freeing Theme ..."); } no_theme: if(!set->setting_strings[IMAGE_PATH].empty() && !set->setting_bools[has_image]){ set->setting_bools[has_image] = true; set->setting_bools[using_sb] = false; } log_debug("Starting Lang."); uint32_t lang = PS4GetLang(); if (error < 0) log_error("ERROR reading INI setting default vals"); #if TEST_INI_LANGS for (int i = 0; i < 29; i++) { if (!LoadLangs(i)) log_debug("Failed to Load %s", Language_GetName(i)); else log_debug("Successfully Loaded %s", Language_GetName(i)); } #endif //fallback lang #ifdef OVERRIDE_LANG lang = OVERRIDE_LANG; #else set->lang = lang; #endif if (!LoadLangs(lang)) { set->lang = 0x01; /// fallback english if (!LoadLangs(set->lang)) { log_debug(" This PKG has no lang files... trying embedded"); if(!load_embdded_eng()) msgok(MSG_DIALOG::FATAL, "Failed to load Backup Lang...\nThe App is unable to continue"); else log_debug(" Loaded embdded ini lang file"); } else log_debug(" Loaded the backup, %s failed to load", Language_GetName(lang).c_str()); } if (!Fnt_setting_enabled) { switch (lang) { case 0: //jAPN IS GREAT set->setting_strings[FNT_PATH] = asset_path("fonts/NotoSansJP-Regular.ttf"); break; case 9:///THIS IS FOR JOON, IF HE COMES BACK set->setting_strings[FNT_PATH] = asset_path("fonts/NotoSansKR-Regular.ttf"); break; case 21: case 23: case 24: case 25: case 26: set->setting_strings[FNT_PATH] = asset_path("fonts/HelveticaWorld-Multi.ttf"); break; //NO USERS HAS TRANSLATED THE FOLLOWING LANGS, SO I DELETED THEM OUT THE PKG TO SAVE SPACE /* case 10: strcpy(set->opt[FNT_PATH],"/mnt/sandbox/pfsmnt/ITEM00001-app0/assets/fonts/NotoSansTC-Regular.ttf"); break; case 11: strcpy(set->opt[FNT_PATH],"/mnt/sandbox/pfsmnt/ITEM00001-app0/assets/fonts/NotoSansSC-Regular.ttf"); break; case 27: strcpy(set->opt[FNT_PATH],"/mnt/sandbox/pfsmnt/ITEM00001-app0/assets/fonts/NotoSansThai-Regular.ttf"); break;*/ default: break; } } // fmt::print("set->ini_path: {}", set->setting_strings[INI_PATH]); fmt::print("set->dumper_path: {}", set->setting_strings[DUMPER_PATH]); fmt::print("get->fnt_path: {}", set->setting_strings[FNT_PATH]); fmt::print("set->fuse_ip: {}", set->setting_strings[FUSE_PC_NFS_IP]); fmt::print("set->RESTRICTED: {}", set->setting_bools[INTERNAL_UPDATE]); log_info( "set->Daemon_... : %s", set->setting_bools[Daemon_on_start] ? "ON" : "OFF"); log_info( "set->HomeMen... : %s", set->setting_bools[HomeMenu_Redirection] ? "ItemzFlow (ON)" : "Orbis (OFF)"); log_info( "set->sort_by : %i", set->sort_by); log_info( "set->Cover_message : %i", set->setting_bools[Cover_message]); log_info( "set->using_theme : %i", set->setting_bools[using_theme]); log_info( "set->Dump_opt : %i", set->Dump_opt); log_info( "set->Install_prog.: %s", set->setting_bools[Show_install_prog] ? "ON" : "OFF"); log_info( "set->Lang : %s : %i", Language_GetName(lang).c_str(), set->lang); return no_error; } bool SaveOptions(ItemzSettings *set) { std::string ini_content = fmt::format("[Settings]\nSort_By={0:d}\nSort_Cat={1:d}\nSecure_Boot=1\nTTF_Font={2:}\nShow_install_prog={3:d}\nHomeMenu_Redirection={4:d}\nDaemon_on_start={5:d}\ncover_message={6:d}\nDumper_Path={7:}\nMP3_Path={8:}\nShow_Buttons={9:d}\nEnable_Theme={10:d}\nImage_path={11:}\nReflections={12:d}\nFuse_IP={13:}\n{14:}={15:d}", (int)set->sort_by, (int)set->sort_cat, set->setting_strings[FNT_PATH], set->setting_bools[Show_install_prog], set->setting_bools[HomeMenu_Redirection], set->setting_bools[Daemon_on_start], set->setting_bools[cover_message], set->setting_strings[DUMPER_PATH], set->setting_strings[MP3_PATH], set->setting_bools[Show_Buttons], set->setting_bools[using_theme], set->setting_strings[IMAGE_PATH], use_reflection, set->setting_strings[FUSE_PC_NFS_IP], set->setting_bools[INTERNAL_UPDATE] ? "Internal_update" : "RESTRICTED" , set->setting_bools[INTERNAL_UPDATE]); std::ofstream out(set->setting_strings[INI_PATH]); if(out.bad()){ log_error("Failed to create ini file"); return false; } out << ini_content; out.close(); log_info("============ Saving the following settings ============"); fmt::print("set->ini_path: {}", set->setting_strings[INI_PATH]); fmt::print("set->dumper_path: {}", set->setting_strings[DUMPER_PATH]); fmt::print("get->fnt_path: {}", set->setting_strings[FNT_PATH]); fmt::print("set->mp3_path: {}", set->setting_strings[MP3_PATH]); fmt::print("set->fuse_ip: {}", set->setting_strings[FUSE_PC_NFS_IP]); log_info("set->Daemon_... : %s", set->setting_bools[Daemon_on_start] ? "ON" : "OFF"); log_info("set->HomeMen... : %s", set->setting_bools[HomeMenu_Redirection] ? "ItemzFlow (ON)" : "Orbis (OFF)"); log_info("set->sort_by : %i", set->sort_by); log_info("set->Show_Buttons : %i", set->setting_bools[Show_Buttons]); log_info("set->cover_message : %i", set->setting_bools[cover_message]); log_info("set->Dump_opt : %i", set->Dump_opt); log_info("set->using_theme : %i", set->setting_bools[using_theme]); log_info("set->RESTIRCTED : %i", set->setting_bools[INTERNAL_UPDATE]); log_info("set->Install_prog.: %s", set->setting_bools[Show_install_prog] ? "ON" : "OFF"); log_info("set->Lang : %s : %i", Language_GetName(set->lang).c_str(), set->lang); log_info("=========================================================="); /* Load values */ chmod(set->setting_strings[INI_PATH].c_str(), 0777); return true; } uint32_t ps4_fw_version(void) { //cache the FW Version if (0 < sdkVersion) { size_t len = 4; // sysKernelGetLowerLimitUpdVersion == machdep.lower_limit_upd_version // rewrite of sysKernelGetLowerLimitUpdVersion sysctlbyname("machdep.lower_limit_upd_version", &sdkVersion, &len, NULL, 0); } // FW Returned is in HEX return sdkVersion; } ////////////////////////////////////////////////////// bool Keyboard(const char* Title, const char* initialTextBuffer, char* out_buffer, bool keypad) { sceSysmoduleLoadModule(ORBIS_SYSMODULE_IME_DIALOG); wchar_t title[100]; wchar_t inputTextBuffer[255]; char titl[100]; if (initialTextBuffer && strlen(initialTextBuffer) > 1023) return "Too Long"; memset(&inputTextBuffer[0], 0, 255); memset(out_buffer, 0, 255); if (initialTextBuffer) snprintf(out_buffer, 254, "%s", initialTextBuffer); //converts the multibyte string src to a wide-character string starting at dest. mbstowcs(&inputTextBuffer[0], out_buffer, strlen(out_buffer) + 1); // use custom title snprintf(&titl[0], 99, "%s", Title ? Title : "Itemzflow"); //converts the multibyte string src to a wide-character string starting at dest. mbstowcs(title, titl, strlen(titl) + 1); OrbisImeDialogParam param; memset(¶m, 0, sizeof(OrbisImeDialogParam)); param.maxTextLength = 1023; param.inputTextBuffer = inputTextBuffer; param.title = title; param.userId = 0xFE; param.type = ORBIS_IME_TYPE_BASIC_LATIN; if (keypad) param.type = ORBIS_IME_TYPE_NUMBER; param.enterLabel = ORBIS_IME_ENTER_LABEL_DEFAULT; sceImeDialogInit(¶m, NULL); int status; while (1) { status = sceImeDialogGetStatus(); if (status == ORBIS_IME_DIALOG_STATUS_FINISHED) { OrbisImeDialogResult result; memset(&result, 0, sizeof(OrbisImeDialogResult)); sceImeDialogGetResult(&result); log_info("status %i, endstatus %i ", status, result.endstatus); if (result.endstatus == ORBIS_IME_DIALOG_END_STATUS_USER_CANCELED){ log_info("User Cancelled this Keyboard Session"); ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::OP_CANNED), getLangSTR(LANG_STR::OP_CANNED_1)); } else if (result.endstatus == ORBIS_IME_DIALOG_END_STATUS_OK) { log_info("User Entered text.. Entering into buffer"); wcstombs(out_buffer, &inputTextBuffer[0], 254); sceImeDialogTerm(); return true; } goto Finished; } else if (status == ORBIS_IME_DIALOG_STATUS_NONE) goto Finished; } Finished: sceImeDialogTerm(); return false; } bool rmtree(const char path[]) { struct dirent* de; char fname[300]; DIR* dr = opendir(path); if (dr == NULL) { log_debug("No file or directory found: %s", path); return false; } while ((de = readdir(dr)) != NULL) { int ret = -1; struct stat statbuf; snprintf(fname, 299, "%s/%s", path, de->d_name); if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (!stat(fname, &statbuf)) { if (S_ISDIR(statbuf.st_mode)) { log_debug("removing dir: %s Err: %d", fname, ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); if (ret != 0) { rmtree(fname); log_debug("Err: %d", ret = unlinkat(dirfd(dr), fname, AT_REMOVEDIR)); } } else { log_debug("Removing file: %s, Err: %d", fname, unlink(fname)); } } } closedir(dr); return true; } void print_memory() { size_t sz = 0; LibcMallocManagedSize ManagedSize; SCE_LIBC_INIT_MALLOC_MANAGED_SIZE(ManagedSize); malloc_stats_fast(&ManagedSize); sceKernelAvailableFlexibleMemorySize(&sz); double dfp_fmem = (1. - (double)sz / (double)0x8000000) * 100.; log_info("[MEM] CurrentSystemSize: %s, CurrentInUseSize: %s", calculateSize(ManagedSize.currentSystemSize).c_str(), calculateSize(ManagedSize.currentInuseSize).c_str()); log_info("[MEM] MaxSystemSize: %s, MaxInUseSize: %s", calculateSize(ManagedSize.maxSystemSize).c_str(), calculateSize(ManagedSize.maxInuseSize).c_str()); log_info("[VRAM] VRAM_Available: %s, AmountUsed: %.2f%%",calculateSize(sz).c_str(), dfp_fmem); } void Kill_BigApp(std::atomic &status){ int ret = -1; int BigAppid = sceSystemServiceGetAppIdOfBigApp(); if(status == NO_APP_OPENED || status == APP_NA_STATUS) { log_info("[CLOSE] No App is opened"); return; } log_info("[CLOSE] BigApp ID: 0x%X", BigAppid); if ((ret = ItemzLocalKillApp(BigAppid)) == ITEMZCORE_SUCCESS) { ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::APP_CLOSED), ""); log_info("[CLOSE] App Closed"); } else { ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_TO_CLOSE), fmt::format( "{0:}: {1:#x}", getLangSTR(LANG_STR::ERROR_CODE), ret)); log_error("[CLOSE] Failed to close APP: 0x%X", ret); } status = NO_APP_OPENED; } int progstart(const char* format, ...) { int ret = 0; char buff[1024]; memset(buff, 0, 1024); va_list args; va_start(args, format); vsprintf(&buff[0], format, args); va_end(args); sceSysmoduleLoadModule(ORBIS_SYSMODULE_MESSAGE_DIALOG); sceMsgDialogInitialize(); OrbisMsgDialogParam dialogParam; OrbisMsgDialogParamInitialize(&dialogParam); dialogParam.mode = ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR; OrbisMsgDialogProgressBarParam progBarParam; memset(&progBarParam, 0, sizeof(OrbisMsgDialogProgressBarParam)); dialogParam.progBarParam = &progBarParam; dialogParam.progBarParam->barType = ORBIS_MSG_DIALOG_PROGRESSBAR_TYPE_PERCENTAGE; dialogParam.progBarParam->msg = &buff[0]; sceMsgDialogOpen(&dialogParam); return ret; } int Confirmation_Msg(std::string msg) { sceMsgDialogTerminate(); //ds sceMsgDialogInitialize(); OrbisMsgDialogParam param; OrbisMsgDialogParamInitialize(¶m); param.mode = ORBIS_MSG_DIALOG_MODE_USER_MSG; OrbisMsgDialogUserMessageParam userMsgParam; memset(&userMsgParam, 0, sizeof(userMsgParam)); userMsgParam.msg = msg.c_str(); userMsgParam.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_YESNO; param.userMsgParam = &userMsgParam; // cv if (0 < sceMsgDialogOpen(¶m)) return NO; OrbisCommonDialogStatus stat; // while (1) { stat = sceMsgDialogUpdateStatus(); if (stat == ORBIS_COMMON_DIALOG_STATUS_FINISHED) { OrbisMsgDialogResult result; memset(&result, 0, sizeof(result)); sceMsgDialogGetResult(&result); return result.buttonId; } } //c return NO; } // now read frames and decode to s16le samples, from callback static int dr_mp3_decode(void) { if (mp3.pData == NULL) return -1; if (mp3.streamCursor <= dataSize.load()) { int count = drmp3_read_pcm_frames_s16(&mp3, MP3_BUFFER_SIZE, &snd[0] /* &snd */); if (count < 0) { log_info("[MP3] Failed to read from file"); return 0; } else if (count == 0) { memset(&snd, 0, MP3_BUFFER_SIZE * 2 * sizeof(short)); return -1; } off2 += count; /* play audio sample after being decoded */ if (!(mp3.streamCursor % 1000)) { log_info( "[MP3] audioout play %4d pFrames: %p, %zub %zu %zu %lu %.3f%%", count, &snd[0], MP3_BUFFER_SIZE * 2 * sizeof(short), off2, dataSize.load(), mp3.streamCursor, (float)((float)mp3.streamCursor / (float)dataSize.load()) * 100.f); } } return off2; } static void audio_PlayCallback(OrbisAudioSample* _buf2, unsigned int length, void* pdata) { const short* _buf = (short*)_buf2; const int play_size = length * 2 * sizeof(short); int handle = orbisAudioGetHandle(main_ch.load()); if (m_bPlaying.load()) { static int decoded; while (fill.load() < (1024 * 2)){ decoded = dr_mp3_decode(); if (decoded < 1) { if (!(numframe % 500)) log_info("[MP3] Playing, but no audio samples decoded yet..."); memset(&snd[0], 0, sizeof(snd)); // fill with silence log_info("[MP3] ended"); m_bPlaying = false; // break main loop mp3data.clear(); break; } else { memcpy(&play_buf[fill.load()], &snd[0], 4608); // we are doing in two step, sort of waste fill += 1152 /* decoded samples */ * 2 /* channels */; } } if (!(numframe % 500)) log_info("[MP3] play_buf samples: %d", fill.load()); if (!m_bPlaying.load()) { orbisAudioSetCallback(main_ch.load(), 0, 0); return; } /* write to audio device buffers */ memcpy((void*)_buf, &play_buf[0], play_size); // 1024 samples per channel, = 4096b /* move remaining samples to head of buffer */ memcpy(&play_buf[0], &play_buf[1024 * 2], sizeof(play_buf) - play_size); fill -= 1024 * 2; if (fill.load() < 0){ fill = 0; if (!(numframe % 500)) log_info("[MP3] no audio frames to play, fill: %d", fill.load()); log_info("[MP3] fill.load() < 0"); m_bPlaying = false; mp3data.clear(); } } else // Not Playing, so clear buffer { if (!(numframe % 60)) log_info("[MP3] Inside audio_PlayCallback, not playing m_bPlaying: %d", m_bPlaying.load()); /* write silence to audio device buffers */ memset((void*)_buf, 0, play_size); usleep(1000); } numframe++; sceAudioOutOutput(handle, NULL); } /*================================== THEME SHIT BELOW =====================================*/ int on_extract_entry(const char *filename, void *arg) { log_debug("[ZIP] Extracted: %s", filename); return 0; } bool extract_zip(const char* zip_file, const char* dest_path) { uint64_t progress[2]; struct zip_t *archive = zip_open(zip_file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'r'); if (!archive){ log_error("[ZIP] Failed to open archive: %s", zip_file); return false; } progress[0] = 0; progress[1] = zip_entries_total(archive); zip_close(archive); log_debug("[ZIP] Extracting ZIP (%lu files) to %s...", progress[1], dest_path); if(zip_extract(zip_file, dest_path, on_extract_entry, progress) == 0) return true; return false; } /*============================================================================================*/ bool power_settings(Power_control_sel sel) { if(Confirmation_Msg(fmt::format("{}?", power_menu_get_str(sel))) == YES){ #define SYSCORE_PID 1 int num = -1; switch(sel){ case POWER_OFF_OPT: num = 31; break; case RESTART_OPT: num = 30; break; case SUSPEND_OPT: num = 1; break; default: return false; } if (syscall(37, SYSCORE_PID, num) != ITEMZCORE_SUCCESS) return false; } return true; } void* timer_thr(void* args){ sleep(5); log_debug("[TIMER] Rebooting App"); raise(SIGQUIT); return NULL; } void relaunch_timer_thread() { pthread_t id; reboot_app = true; pthread_create(&id, NULL, timer_thr, NULL); } /*==================================== UPDATE SHIT ===========================================*/ update_ret check_update_from_url(const char* tid = ITEMZFLOW_TID) { if(get->setting_bools[INTERNAL_UPDATE]) return IF_NO_UPDATE; sceStoreApiCheckUpdate = (update_ret(*)(const char*))prx_func_loader(asset_path("../Media/store_api.prx"), "sceStoreApiCheckUpdate"); if(sceStoreApiCheckUpdate) { return sceStoreApiCheckUpdate(tid); } else { log_error("Failed to load store_api.prx"); return IF_UPDATE_ERROR; } } void* CheckForUpdateThread(void*) { while (1) { update_ret ret = check_update_from_url(ITEMZFLOW_TID); if (ret == IF_UPDATE_FOUND){ log_info(" Update is available, the server reported a different Hash"); ani_notify(NOTIFI::INFO, getLangSTR(LANG_STR::OPT_UPDATE), ""); sleep(5 * 60); } else if(ret == IF_NO_UPDATE) { sleep(5 * 60); // no update, check again in 5 minutes } else if(ret == IF_UPDATE_ERROR) { log_info("Update Server is connectionn error, retrying in 60 secs"); sleep(60); } } return 0; } void launch_update_thread() { pthread_t id; pthread_create(&id, NULL, CheckForUpdateThread, NULL);// pthread_create(&id, NULL, timer_thr, NULL); } bool Launch_Store_URI() { sceStoreApiLaunchStore = (bool (*)(const char*))prx_func_loader(asset_path("../Media/store_api.prx"), "sceStoreApiLaunchStore"); if(sceStoreApiLaunchStore) { return sceStoreApiLaunchStore("Itemzflow"); } else { log_error("Failed to load store_api.prx"); return false; } return false; } void ProgressUpdate(uint32_t prog, std::string fmt) { int status = sceMsgDialogUpdateStatus(); if (ORBIS_COMMON_DIALOG_STATUS_RUNNING == status) { sceMsgDialogProgressBarSetValue(0, prog); sceMsgDialogProgressBarSetMsg(0, fmt.c_str()); } } mp3_status_t MP3_Player(std::string path) { TagLib::FileRef songf; std::ifstream file; if(path.empty() || path.size() < 3 || !if_exists(path.c_str())) return MP3_STATUS_ERROR; if (m_bPlaying.load() || retry_mp3_count >= 2) // ALREADY PLAYING return MP3_STATUS_PLAYING; current_song.song_title.clear(); current_song.song_artist.clear(); current_song.is_playing = false; dataSize = 0; if (path.substr(path.size() - 3, 3) != "mp3" && mp3_playlist.empty()) { if(!is_folder(path.c_str())) { log_error("Not a folder: %s", path.c_str()); goto err; } FS_FILTER filter = fs_ret.filter; fs_ret.filter = FS_MP3_ONLY; index_items_from_dir_v2(path, mp3_playlist); fs_ret.filter = filter; //skip reserved index if (mp3_playlist.size() <= 1) { log_error("No MP3s in: %s", path.c_str()); mp3_playlist.clear(); goto err; } else { log_info("Found %d MP3 files in %s", mp3_playlist.size()-1, path.c_str()); current_song.total_files = mp3_playlist.size()-1; current_song.current_file = 1; } } if (path.substr(path.size() - 3, 3) != "mp3" && mp3_playlist.size() > 1) { if (current_song.current_file >= mp3_playlist.size()) { log_info("Reached end of playlist, resetting index"); current_song.current_file = 1; } path = path + "/" + mp3_playlist[current_song.current_file].info.id; if (path.substr(path.size() - 3, 3) != "mp3") { log_error("Not a valid MP3 file: %s", path.c_str()); goto err; } } else if (path.substr(path.size() - 3, 3) == "mp3") { current_song.total_files = 1; current_song.current_file = 0; } log_info("[MP3] Trying to play: %s", path.c_str()); songf = TagLib::FileRef(path.c_str()); if(songf.isNull() || !songf.tag()) { log_error("Unknown File: %s", path.c_str()); goto err; } if (path.substr(path.size() - 3, 3) == "mp3") { current_song.song_title = songf.tag()->title().isEmpty() ? path : songf.tag()->title().toCString(true); current_song.song_artist = songf.tag()->artist().isEmpty() ? "Unknown Artist" : songf.tag()->artist().toCString(true); } else { current_song.song_title = songf.tag()->title().isEmpty() ? mp3_playlist[current_song.current_file].info.id : songf.tag()->title().toCString(true); current_song.song_artist = songf.tag()->artist().isEmpty() ? "Unknown Artist" : songf.tag()->artist().toCString(true); } Config.outputSampleRate = 48000; //load mp3 data for the drmp3 mp3 decoder file.open(path.c_str(), std::ios::binary); if(!file.is_open()) { log_error("Failed to open file: %s", path.c_str()); goto err; } file.seekg(0, std::ios::end); dataSize = file.tellg(); file.seekg(0, std::ios::beg); //get the file size if(dataSize == 0 || dataSize > MB(20)) { log_error("MP3 Too big or is truncated: %s", path.c_str()); file.close(); goto err; } mp3data = std::vector(std::istreambuf_iterator(file), {}); file.close(); if (!drmp3_init_memory(&mp3, mp3data.data(), mp3data.size(), &Config)) { mp3data.clear(); log_error("[MP3] drmp3_init_memory failed: %s", path.c_str()); goto err; } // attach audio callback off2 = 0, fill = 0; orbisAudioSetCallback(main_ch.load(), audio_PlayCallback, 0); // start audio callback, and play fmt::print("Playing: {0:} - {1:} ({2:})", current_song.song_title, current_song.song_artist, dataSize, (get->lang == 1 || get->lang == 2)); current_song.is_playing = m_bPlaying = true, orbisAudioResume(0); current_song.current_file++; GLES2_refresh_sysinfo(); return MP3_STATUS_STARTED; err: if (2 >= retry_mp3_count) { ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::FAILED_MP3), fmt::format("({0:d}/2)", retry_mp3_count+1)); retry_mp3_count++; } dataSize = 0; mp3data.clear(); return MP3_STATUS_ERROR; } void Stop_Music(){ if(!m_bPlaying) return; log_info("Stopping MP3 Playback ..."); // set playing to false current_song.is_playing = m_bPlaying = false; //Stop audio callback orbisAudioPause(0); orbisAudioSetCallback(main_ch.load(), NULL, 0); //Clear mp3 data mp3data.clear(); mp3_playlist.clear(); get->setting_strings[MP3_PATH].clear(); //Clear mp3 decoder drmp3_uninit(&mp3); //Clear mp3 tags AND audio channel handle current_song.song_artist = current_song.song_title = ""; retry_mp3_count = current_song.current_file = main_ch = current_song.total_files = 0; } bool do_update(std::string url) { IPC_Client& ipc = IPC_Client::getInstance(); log_debug("[UPDATE] Starting Update..."); log_debug("[UPDATE] Updating..."); if (get->setting_bools[INTERNAL_UPDATE] && !is_connected_app) { log_debug("[UPDATE] Daemon Not connected to app, exiting update mode ... "); return false; } if (get->setting_bools[INTERNAL_UPDATE]) { std::string tmp; long ret = -1; tmp = fmt::format("{}/update/if_update.pkg", url.c_str()); if ((ret = dl_from_url(tmp.c_str(), APP_PATH("../if_update.pkg"))) < 0 && if_exists(APP_PATH("../if_update.pkg"))) { log_error("[UPDATE] Failed to download if_update.pkg ret: %i", ret); return false; } ipc_msg = std::to_string(getpid()); IPC_Ret error = ipc.IPCSendCommand(IPC_Commands::INSTALL_IF_UPDATE, ipc_msg); if (error != IPC_Ret::NO_ERROR) { sceMsgDialogTerminate(); log_error("[UPDATE] IPC INSTALL UPDATE ERROR: %i", error); return false; } log_debug("[UPDATE] Copied, Removing Temp Files"); log_debug("[UPDATE] Complete..."); raise(SIGQUIT); } else { if (!Launch_Store_URI()) { log_error("[UPDATE] Failed to launch store uri"); return false; } } return true; } void check_for_update_file() { IPC_Client& ipc = IPC_Client::getInstance(); loadmsg(getLangSTR(LANG_STR::CHECH_UPDATE_IN_PROGRESS)); bool found = false; log_debug("[STARTUP][UPDATE] Checking for update file ..."); if (if_exists("/user/update_tmp")) { log_debug("[IF_UPDATE] Found Update temp folder, moving files back.."); //if updated via store copy_dir("/user/update_tmp/covers", "/user/app/ITEM00001/covers"); copy_dir("/user/update_tmp/custom_app_names", "/user/app/ITEM00001/custom_app_names"); copyFile("/user/update_tmp/settings.ini", "/user/app/ITEM00001/settings.ini", false); rmtree("/user/update_tmp"); unlink("/user/update_tmp"); log_debug("[IF_UPDATE] Moved files back, removing temp folder.."); } else { log_info("No Update temp folder found"); } if (!is_connected_app) { log_debug("[STARTUP][UPDATE] Daemon Not connected to app, exiting update mode ... "); sceMsgDialogTerminate(); return; } if (if_exists(APP_PATH("../if_update.pkg"))) { log_debug("[STARTUP][UPDATE][HDD] if_update.pkg exists, starting update ..."); found = true; } else if (usbpath() != -1 && if_exists(fmt::format("/mnt/usb{}/if_update.pkg", usbpath()).c_str())) { log_debug("[STARTUP][UPDATE][USB] if_update.pkg exists, starting update ..."); copyFile(fmt::format("/mnt/usb{}/if_update.pkg", usbpath()), APP_PATH("../if_update.pkg"), false); found = true; } else { log_debug("[STARTUP][UPDATE] if_update.pkg does not exist, exiting update mode ..."); sceMsgDialogTerminate(); return; } if (found) { log_debug("[STARTUP][UPDATE] Update Found, Sending IPC Command for Update Installing ..."); ipc_msg = std::to_string(getpid()); log_info("[STARTUP] Sending PID: %s", ipc_msg.c_str()); IPC_Ret error = ipc.IPCSendCommand(IPC_Commands::INSTALL_IF_UPDATE, ipc_msg); if (error != IPC_Ret::NO_ERROR) { log_error("[STARTUP][UPDATE] IPC INSTALL UPDATE ERROR: %i", error); sceMsgDialogTerminate(); return; } } else { log_debug("[STARTUP][UPDATE] if_update.pkg does not exist, exiting update mode ..."); sceMsgDialogTerminate(); return; } raise(SIGQUIT); } /*============================================================================================*/ void notify(const char* message) { log_info(message); sceKernelLoadStartModule("/system/common/lib/libSceSysUtil.sprx", 0, NULL, 0, 0, 0); sceSysUtilSendSystemNotificationWithText(222, message); } // https://github.com/OSM-Made/PS4-Notify/blob/c6d259bc5bd4aa519f5b0ce4f5e27ef7cb01ffdd/Notify.cpp void notifywithicon(const char* IconURI, const char* MessageFMT, ...) { NotifyBuffer Buffer; va_list args; va_start(args, MessageFMT); vsprintf(Buffer.Message, MessageFMT, args); va_end(args); Buffer.Type = NotifyType::NotificationRequest; Buffer.unk3 = 0; Buffer.UseIconImageUri = 1; Buffer.TargetId = -1; strcpy(Buffer.Uri, IconURI); sceKernelSendNotificationRequest(0, (char*)&Buffer, sizeof(Buffer), 0); } bool get_ip_address(std::string &ip) { int ret; SceNetCtlInfo info; ret = sceNetCtlGetInfo(14, &info); if (ret < 0) return false; ip = info.ip_address; return true; } void msgok(MSG_DIALOG::MSG_DIALOG_TYPE level, std::string in) { sceMsgDialogTerminate(); std::string out; switch (level) { case MSG_DIALOG::NORMAL: out = in; break; case MSG_DIALOG::FATAL: log_fatal(in.c_str()); out = fmt::format("{0:} {1:}\n\n {2:}", getLangSTR(LANG_STR::FATAL_ERROR), in, getLangSTR(LANG_STR::PRESS_OK_CLOSE)); break; case MSG_DIALOG::WARNING: log_warn( in.c_str()); out = fmt::format("{0:} {1:}", getLangSTR(LANG_STR::WARNING2), in); break; } sceMsgDialogInitialize(); OrbisMsgDialogParam param; OrbisMsgDialogParamInitialize(¶m); param.mode = ORBIS_MSG_DIALOG_MODE_USER_MSG; OrbisMsgDialogUserMessageParam userMsgParam; memset(&userMsgParam, 0, sizeof(userMsgParam)); userMsgParam.msg = out.c_str(); userMsgParam.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_OK; param.userMsgParam = &userMsgParam; if (sceMsgDialogOpen(¶m) < 0) log_fatal( "MsgD failed"); OrbisCommonDialogStatus stat; while (1) { stat = sceMsgDialogUpdateStatus(); if (stat == ORBIS_COMMON_DIALOG_STATUS_FINISHED) { OrbisMsgDialogResult result; memset(&result, 0, sizeof(result)); if (0 > sceMsgDialogGetResult(&result)) log_fatal( "MsgD failed"); sceMsgDialogTerminate(); break; } } if (level == MSG_DIALOG::FATAL) { log_fatal(in.c_str()); raise(SIGQUIT); } return; } void loadmsg(std::string in) { OrbisMsgDialogButtonsParam buttonsParam; OrbisMsgDialogUserMessageParam messageParam; OrbisMsgDialogParam dialogParam; sceMsgDialogInitialize(); OrbisMsgDialogParamInitialize(&dialogParam); memset(&buttonsParam, 0x00, sizeof(buttonsParam)); memset(&messageParam, 0x00, sizeof(messageParam)); dialogParam.userMsgParam = &messageParam; dialogParam.mode = ORBIS_MSG_DIALOG_MODE_USER_MSG; messageParam.buttonType = ORBIS_MSG_DIALOG_BUTTON_TYPE_WAIT; messageParam.msg = in.c_str(); sceMsgDialogOpen(&dialogParam); return; } void scan_for_disc() { //check if theres a disc //std::lock_guard < std::mutex > lock(disc_lock); char stack_tid[16]; int ret = -1; memset( & stack_tid[0], 0, 16); if ((ret = sceAppInstUtilAppGetInsertedDiscTitleId( & stack_tid[0])) >= 0 && !is_disc_inserted.load()) { if (!all_apps.empty() && all_apps.size() > 1) { for (int i = 0; i < all_apps[0].count.token_c + 1; i++) { // skip if its an FPKG if (all_apps[i].flags.is_fpkg) continue; // log_info("Disc inserted: %s : %i %s", stack_tid, i , all_apps[i].id.c_str()); if (all_apps[i].info.id.find( & stack_tid[0]) != std::string::npos) { //Store the texture ID of the inserted disc inserted_disc = i; //inserted_disc: 1 CUSA00900 1 18 log_info("[UTIL] inserted_disc: %i %s %i %s %i", inserted_disc.load(), stack_tid, i, all_apps[i].info.id.c_str(), all_apps.size()); is_disc_inserted = true; break; } } } } else if (ret == SCE_APP_INSTALLER_ERROR_DISC_NOT_INSERTED) { // log_info("[UTIL] disc not inserted %x", ret); inserted_disc = -999; is_disc_inserted = false; } } bool install_IF_Theme(std::string theme_path){ theme t; int ret = -9; int h, w; std::string tmp; fmt::print("[THEME][START] Installing Theme: {}", theme_path); if(!if_exists(APP_PATH("theme"))){ log_error("[THEME] No Theme already exists, Making folder"); mkdir(APP_PATH("theme"), 0777); } else{ log_debug("[THEME] Theme already exists, Removing"); rmtree(APP_PATH("theme")); log_debug("[THEME] Theme Removed"); } log_debug("[THEME] Extracting Theme ..."); if(!extract_zip(theme_path.c_str(), APP_PATH("theme"))){ log_error("[THEME] Failed to extract Theme"); goto err; } log_debug("[THEME] Extracting Theme Done"); log_debug("[THEME] Getting Theme Info"); t.is_image = false; t.using_sb = false; get->setting_strings[FNT_PATH] = "/system_ex/app/NPXS20113/bdjstack/lib/fonts/SCE-PS3-RD-R-LATIN.TTF"; if(!if_exists(APP_PATH("theme/theme.ini")) || (ret = ini_parse(APP_PATH("theme/theme.ini"), theme_info, &t)) < 0){ log_error("[THEME][ERROR] Failed to Fetch Theme Info, ERROR %i", ret); goto err; } /*============================ QA Theme CHECKS ================================================*/ if(t.version.empty() || t.name.empty() || t.author.empty() || t.date.empty()){ log_error("[THEME][ERROR] Theme Info is missing"); goto err; } if(t.is_image && (!if_exists(APP_PATH("theme/background.png")) || !is_png_vaild(APP_PATH("theme/background.png"), &h, &w))){ log_error("[THEME][ERROR] No vaild Theme Image but the INI reports its using one"); goto err; } if(t.has_font && !if_exists(APP_PATH("theme/font.ttf")) ){ log_error("[THEME][ERROR] No vaild FONT but the INI reports its using one"); goto err; } for(int i = 0; i < sizeof required_theme_files / sizeof required_theme_files[0]; i++){ tmp = fmt::format("{}{}", APP_PATH(""), required_theme_files[i]); if(!if_exists(tmp.c_str()) || !is_png_vaild(tmp.c_str(), &h, &w)){ log_error("[THEME][ERROR] Error Loading file %s which is required for themes, maybe its invaild/corrupt?", &tmp[0]); goto err; } } if(t.using_sb && !if_exists(APP_PATH("theme/shader.bin"))){ log_error("[THEME][ERROR] No Shader Binary but the INI reports its using one"); goto err; } else if(t.using_sb){ int programID = -1; const char* data = (const char*)orbisFileGetFileContent(APP_PATH("theme/shader.bin")); if(data == NULL){ log_debug("[THEME][ERROR] can't load shader.bin"); goto err; } else{ programID = BuildProgram(p_vs1, data, p_vs1_length, _orbisFile_lastopenFile_size); if(programID == -1){ log_debug("[THEME][ERROR] Shader Testing Failed!!!"); free((void*)data); goto err; } else log_debug("[THEME][INFO] Shader Built... programID: %i", programID); // NO leaks pls glDeleteProgram(programID); free((void*)data); } } log_debug("[THEME] ======== Theme Info ======="); fmt::print("[THEME] Name: {}", t.name); fmt::print("[THEME] Version: {}", t.version); fmt::print("[THEME] Date: {}", t.date); fmt::print("[THEME] Author: {}", t.author); log_debug("[THEME] Image: %s", t.is_image ? "Yes" : "No"); log_debug("[THEME] Font: %s", t.has_font ? "Yes" : "No"); log_debug("[THEME] Shader: %s", t.using_sb ? "Yes" : "No"); log_debug("[THEME] ======== End of Theme Info ======="); log_debug("[THEME] Freeing Theme Info ..."); get->setting_bools[has_image] = t.is_image; get->setting_bools[using_sb] = t.using_sb; get->setting_bools[using_theme] = true; if(!SaveOptions(get)){ log_error("[THEME] Failed to save Theme Options"); goto err; } zip_create(APP_PATH("Reset Theme to Default.zip"), &if_d_zip[0], 200); if(usbpath() != -1){ log_debug("[THEME] USB Path is set, updating USB Theme"); tmp = fmt::format("/mnt/usb{}/Reset Theme to Default.zip", usbpath()); zip_create(tmp.c_str(), &if_d_zip[0], 200); } fmt::print("[THEME] Theme Installed: {}", t.name); sceMsgDialogTerminate(); return true; err: log_error("[THEME][ERROR] Freeing Theme Info ..."); log_error("[THEME][ERROR] Deleting Theme Dir ..."); rmtree(APP_PATH("theme")); log_error("[THEME][ERROR] Theme Dir Deleted"); log_error("[THEME][ERROR] Resetting IF Settings ..."); get->setting_strings[FNT_PATH] = "/system_ex/app/NPXS20113/bdjstack/lib/fonts/SCE-PS3-RD-R-LATIN.TTF"; get->setting_bools[using_theme] = false; if(!SaveOptions(get)) log_error("[THEME][ERROR] Failed to save Theme Options"); log_error("[THEME][ERROR] Complete.. returning...."); sceMsgDialogTerminate(); return false; } uint32_t Launch_App(std::string TITLE_ID, bool silent, int index) { uint32_t sys_res = -1; u32 userId = -1; int libcmi = sceKernelLoadStartModule("/system/common/lib/libSceSystemService.sprx", 0, NULL, 0, 0, 0); if (libcmi > 0) { fmt::print("Starting action Launch_App({0:}, {1:d})", TITLE_ID, silent); fmt::print("sceUserServiceGetForegroundUser {0:#x}", sceUserServiceGetForegroundUser(&userId)); fmt::print("Launching App for User: {0:#x}", userId); LncAppParam param; param.sz = sizeof(LncAppParam); param.user_id = userId; param.app_opt = 0; param.crash_report = 0; param.check_flag = SkipSystemUpdateCheck; sys_res = sceLncUtilLaunchApp(TITLE_ID.c_str(), NULL, ¶m); if (IS_ERROR(sys_res)) { if (!silent) { fmt::print("[ERROR] Switch {0:#x}", sys_res); switch (sys_res) { case SCE_LNC_ERROR_APP_NOT_FOUND: { //msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_NOT_FOUND)); if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(LANG_STR::IF_LAUNCH_TROUBLESHOOTER), getLangSTR(LANG_STR::LAUNCH_FAILED_BC_NOT_FOUND), getLangSTR(LANG_STR::CONFIRMATION_2))) == YES){ if(Fix_Game_In_DB(all_apps, index, all_apps[index].flags.is_fpkg)) ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::GAME_INSERT_SUCCESSFUL), getLangSTR(LANG_STR::REPAIR_ATTEMPTED1)); else msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::GAME_INSERT_FAILED)); } break; } case SCE_LNC_UTIL_ERROR_ALREADY_RUNNING: { msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_OPENED)); break; } case SCE_LNC_UTIL_ERROR_APPHOME_EBOOTBIN_NOT_FOUND: { msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::MISSING_EBOOT)); break; } case SCE_LNC_UTIL_ERROR_APPHOME_PARAMSFO_NOT_FOUND: { msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::MISSING_SFO)); break; } case SCE_LNC_UTIL_ERROR_NO_SFOKEY_IN_APP_INFO: { //msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::CORRUPT_SFO)); if(Confirmation_Msg(fmt::format("{0:}\n\n{1:}\n{2:}", getLangSTR(LANG_STR::IF_LAUNCH_TROUBLESHOOTER), getLangSTR(LANG_STR::FIX_BROKEN_SFO_DB), getLangSTR(LANG_STR::CONFIRMATION_3))) == YES){ if(Fix_Game_In_DB(all_apps, index, all_apps[index].flags.is_fpkg)) ani_notify(NOTIFI::GAME, getLangSTR(LANG_STR::REPAIR_ATTEMPTED), getLangSTR(LANG_STR::REPAIR_ATTEMPTED1)); else msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::GAME_INSERT_FAILED)); } break; } case SCE_LNC_UTIL_ERROR_ALREADY_RUNNING_KILL_NEEDED: { log_debug("ALREADY RUNNING KILL NEEDED"); break; } case SCE_LNC_UTIL_ERROR_ALREADY_RUNNING_SUSPEND_NEEDED: { log_debug("ALREADY RUNNING SUSPEND NEEDED"); break; } case SCE_LNC_UTIL_ERROR_SETUP_FS_SANDBOX: { msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::APP_UNL)); break; } case SCE_LNC_UTIL_ERROR_INVALID_TITLE_ID: { msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::ID_NOT_VAILD)); break; } case PROCCESS_STARTER_OP_NOT_SUPPORTED: { if(TITLE_ID == APP_HOME_HOST_TID) msgok(MSG_DIALOG::WARNING, getLangSTR(LANG_STR::LNC_TOO_MANY_ROOT_FILES)); break; } default: { msgok(MSG_DIALOG::WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::LAUNCH_ERROR),sys_res)); break; } } } } fmt::print("launch ret {0:#x}", sys_res); } else { if(!silent) msgok(MSG_DIALOG::WARNING, fmt::format("{0:}: {1:#x}", getLangSTR(LANG_STR::LAUNCH_ERROR),libcmi)); } return sys_res; } bool is_sfo(const char* input_file_name) { FILE* file = fopen(input_file_name, "rb"); if (file == NULL) return false; // Get SFO header offset uint32_t magic; fread(&magic, 4, 1, file); if (magic == 1179865088) { // Param.sfo file rewind(file); } else { log_error("[SFO] Not a param.sfo file. %s", input_file_name); fclose(file); return false; } fclose(file); return true; } bool is_fpkg(std::string pkg_path) { PKG_HDR hdr; // Open path int pkg = open(pkg_path.c_str(), 0, 0); if (pkg < 0){ fmt::print("[ERROR] Error opening file: {}", pkg_path); return false; } lseek(pkg, 0, SEEK_SET); //first byte read(pkg, &hdr, sizeof(PKG_HDR)); close(pkg); //fmt::print("pkg_path: {0:} | hdr.magic: {1:x}", pkg_path, hdr.magic); return (hdr.flags & FPKG) == 0; } bool dlc_pkg_details(const char* src_dest, DLC_PKG_DETAILS &details) { // Read PKG header struct pkg_header hdr; //char title[256]; static const uint8_t pkg_magic[] = { '\x7F', 'C', 'N', 'T' }; // Open path int pkg = sceKernelOpen(src_dest, O_RDONLY, 0); if (pkg < 0){ log_error("Error opening file: %s, err: %s", src_dest, strerror(errno)); return false; } sceKernelLseek(pkg, 0, SEEK_SET); sceKernelRead(pkg, &hdr, sizeof(struct pkg_header)); if (memcmp(hdr.magic, pkg_magic, sizeof(pkg_magic)) != 0) { log_error("PKG Format is wrong"); sceKernelClose(pkg); return false; } sceKernelClose(pkg); details.contentid = std::string(hdr.content_id); if(!extract_sfo_from_pkg(src_dest, "/user/app/ITEM00001/tmp_param.sfo")){ log_error("Could not extract param.sfo"); return false; } //TODO make more effecient std::vector sfo_data = readFile("/user/app/ITEM00001/tmp_param.sfo"); if(sfo_data.empty()){ log_error("Could not read param.sfo"); return false; } SfoReader sfo(sfo_data); details.name = sanitizeString(sfo.GetValueFor("TITLE")); details.version = sfo.GetValueFor("VERSION"); details.esd.insert(std::pair("IRO_TAG", sfo.GetValueFor("IRO_TAG"))); for (int i = 0; i <= 30; i++) { std::string key = fmt::format("TITLE_{0:}{1:d}", (i < 10) ? "0" : "", i); details.esd.insert(std::pair(key, sanitizeString(sfo.GetValueFor(key)))); } //log_info("[DLC] Content ID: %s", details.contentid.c_str()); //log_info("[DLC] Version: %s", details.version.c_str()); log_info("[DLC] Title: %s", details.name.c_str()); return true; } void rebuild_db(){ if (get->sort_cat == NO_CATEGORY_FILTER || (get->sort_cat != NO_CATEGORY_FILTER && Confirmation_Msg(getLangSTR(LANG_STR::CAT_REBUILD_WARNING)) == YES)) { progstart((char *)getLangSTR(LANG_STR::REBUILDING_FOR_FPKG).c_str()); int failures = 0; for (int i = 1; i <= all_apps[0].count.HDD_count; i++) { bool is_if = ((all_apps[i].info.id == "ITEM00001") || (all_apps[i].info.id == "ITEM99999")); if (!all_apps[i].flags.is_fpkg || is_if) { // if its not an fpkg or Itemzflow, skip fmt::print("Skipping {}: {}", is_if ? "IF" : "Retail Game", all_apps[i].info.name); continue; } if (i > all_apps[0].count.HDD_count){ log_info("Skipping external HDD app: %s ???", all_apps[i].info.name.c_str()); continue; } int prog = (uint32_t)(((float)i / all_apps[0].count.HDD_count) * 100.f); ProgSetMessagewText(prog, fmt::format("{0:}\n{1:}\n{2:}\n\n {3:} {4:d}\n{5:} {6:d}", getLangSTR(LANG_STR::REBUILDING_FOR_FPKG), all_apps[i].info.name, all_apps[i].info.id, getLangSTR(LANG_STR::CURRENT_APP), i, getLangSTR(LANG_STR::TOTAL_NUM_OF_APPS), all_apps[0].count.HDD_count).c_str()); log_info("App Info: %s | %i", all_apps[i].info.name.c_str(), i); // function also replaces special chars for SQL smt if (Fix_Game_In_DB(all_apps, i, all_apps[i].flags.is_ext_hdd)) log_info("Game Fixed: %s", all_apps[i].info.name.c_str()); else { log_error("Game NOT Fixed: %s", all_apps[i].info.name.c_str()); failures++; } } sceMsgDialogTerminate(); loadmsg((char *)getLangSTR(LANG_STR::REBUILDING_ADDCONT).c_str()); addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", false); //addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", true); msgok(MSG_DIALOG::NORMAL, fmt::format("{0:}: {1:d}\n{2:}: {3:d}", getLangSTR(LANG_STR::NUMB_OF_FIXED_APPS), all_apps[0].count.HDD_count-failures, getLangSTR(LANG_STR::NOT_FIXED), failures)); } } void rebuild_dlc_db(){ loadmsg((char *)getLangSTR(LANG_STR::REBUILDING_ADDCONT).c_str()); addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", true); if(addcont_dlc_rebuild("/system_data/priv/mms/addcont.db", false)) ani_notify(NOTIFI::SUCCESS, getLangSTR(LANG_STR::ADDCONT_REBUILT), ""); else ani_notify(NOTIFI::WARNING, getLangSTR(LANG_STR::ADDCONT_FAILED), ""); sceMsgDialogTerminate(); } void print_Apps_Array(ThreadSafeVector &b) { for (int i = 0; i <= b.at(0).count.token_c; i++) { fmt::print("b[{0:d}].token_d[0].off ptr: {1:}", i, b.at(i).info.id); fmt::print("b[{0:d}].token_d[1].off ptr: {1:}", i, b.at(i).info.name); } } void delete_apps_array(ThreadSafeVector &apps_arr) { if(apps_arr.empty()) return; for (int i = 1; i <= apps_arr.at(0).count.token_c; i++) { GLuint text = apps_arr.at(i).icon.texture.load(); if (text > GL_NULL){ glDeleteTextures(1, &text); apps_arr.at(i).icon.texture = GL_NULL; } } apps_arr.clear(); } std::ifstream::pos_type file_size(const char* filename) { std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary); return in.tellg(); } std::string NormalizeFWVersion(int version) { std::string versionString = fmt::format("{:x}", version); versionString.insert(1, "."); std::string::size_type pos; while ((pos = versionString.find("0000")) != std::string::npos) { versionString.erase(pos, 4); // 4 is the length of "0000" } return versionString; }