diff --git a/Makefile b/Makefile index 05b9c4a..39e0437 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ # Package metadata. TITLE := PS4 Cheats Manager -VERSION := 01.20 +VERSION := 01.22 TITLE_ID := CHTM00777 CONTENT_ID := IV0000-CHTM00777_00-PS4CHEATSMANAGER # Libraries linked into the ELF. LIBS := -lc -lkernel -lSceAudioOut -lSceUserService -lScePigletv2VSH -lSceSysmodule -lSceFreeType \ -lScePad -lSceSystemService -lSceSaveData -lSceCommonDialog -lSceMsgDialog -lSceNet \ - -lSceNetCtl -lSDL2 -lcjson -ldbglogger -lz -ljbc -lmxml -lcurl -lpolarssl -lSQLite + -lSceNetCtl -lSceImeDialog -lSDL2 -lcjson -ldbglogger -lz -ljbc -lmxml -lcurl -lpolarssl -lSQLite # Additional compile flags. EXTRAFLAGS := -fcolor-diagnostics -Wall -D__PS4__ diff --git a/include/cheats.h b/include/cheats.h index 1a57e53..43a57d8 100644 --- a/include/cheats.h +++ b/include/cheats.h @@ -186,6 +186,7 @@ int zip_directory(const char* basedir, const char* inputdir, const char* output_ int extract_zip_gh(const char* zip_file, const char* dest_path); int show_dialog(int dialog_type, const char * format, ...); +int osk_dialog_get_text(const char* title, char* text, uint32_t size); void init_progress_bar(const char* msg); void update_progress_bar(uint64_t progress, const uint64_t total_size, const char* msg); void end_progress_bar(void); diff --git a/include/settings.h b/include/settings.h index cd9727b..060a708 100644 --- a/include/settings.h +++ b/include/settings.h @@ -1,4 +1,4 @@ -#define CHEATSMGR_VERSION "1.2.0" // PS4 Cheats Manager version (about menu) +#define CHEATSMGR_VERSION "1.2.2" // PS4 Cheats Manager version (about menu) #define MENU_TITLE_OFF 45 // Offset of menu title text from menu mini icon #define MENU_ICON_OFF 105 // X Offset to start printing menu mini icon @@ -34,18 +34,13 @@ typedef struct uint8_t update; uint8_t overwrite; uint32_t user_id; + char url_cheats[256]; + char url_patches[256]; + char url_plugins[256]; } app_config_t; extern menu_option_t menu_options[]; extern app_config_t gcm_config; -void log_callback(int sel); -void music_callback(int sel); -void sort_callback(int sel); -void ani_callback(int sel); void update_callback(int sel); -void overwrite_callback(int sel); -void clearcache_callback(int sel); -void clearpatch_callback(int sel); -void setpluginsperms_callback(int sel); diff --git a/source/dialog.c b/source/dialog.c index e83056c..cfc40c3 100644 --- a/source/dialog.c +++ b/source/dialog.c @@ -2,12 +2,17 @@ #include #include #include +#include "menu.h" #include #include +#include -#define MDIALOG_OK 0 -#define MDIALOG_YESNO 1 +#define SCE_IME_DIALOG_MAX_TEXT_LENGTH 512 + +static int ime_dialog_running = 0; +static uint16_t inputTextBuffer[SCE_IME_DIALOG_MAX_TEXT_LENGTH+1]; +static uint16_t input_ime_title[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; static inline void _orbisCommonDialogSetMagicNumber(uint32_t* magic, const OrbisCommonDialogBaseParam* param) @@ -121,3 +126,205 @@ void stop_loading_screen(void) { end_progress_bar(); } + +static int convert_to_utf16(const char* utf8, uint16_t* utf16, uint32_t available) +{ + int count = 0; + while (*utf8) + { + uint8_t ch = (uint8_t)*utf8++; + uint32_t code; + uint32_t extra; + + if (ch < 0x80) + { + code = ch; + extra = 0; + } + else if ((ch & 0xe0) == 0xc0) + { + code = ch & 31; + extra = 1; + } + else if ((ch & 0xf0) == 0xe0) + { + code = ch & 15; + extra = 2; + } + else + { + // TODO: this assumes there won't be invalid utf8 codepoints + code = ch & 7; + extra = 3; + } + + for (uint32_t i=0; i= 0xe000) + { + if (available < 1) goto utf16_end; + utf16[count++] = (uint16_t)code; + available--; + } + else // surrogate pair + { + if (available < 2) goto utf16_end; + code -= 0x10000; + utf16[count++] = 0xd800 | (code >> 10); + utf16[count++] = 0xdc00 | (code & 0x3ff); + available -= 2; + } + } + +utf16_end: + utf16[count]=0; + return count; +} + +static int convert_from_utf16(const uint16_t* utf16, char* utf8, uint32_t size) +{ + int count = 0; + while (*utf16) + { + uint32_t code; + uint16_t ch = *utf16++; + if (ch < 0xd800 || ch >= 0xe000) + { + code = ch; + } + else // surrogate pair + { + uint16_t ch2 = *utf16++; + if (ch < 0xdc00 || ch > 0xe000 || ch2 < 0xd800 || ch2 > 0xdc00) + { + goto utf8_end; + } + code = 0x10000 + ((ch & 0x03FF) << 10) + (ch2 & 0x03FF); + } + + if (code < 0x80) + { + if (size < 1) goto utf8_end; + utf8[count++] = (char)code; + size--; + } + else if (code < 0x800) + { + if (size < 2) goto utf8_end; + utf8[count++] = (char)(0xc0 | (code >> 6)); + utf8[count++] = (char)(0x80 | (code & 0x3f)); + size -= 2; + } + else if (code < 0x10000) + { + if (size < 3) goto utf8_end; + utf8[count++] = (char)(0xe0 | (code >> 12)); + utf8[count++] = (char)(0x80 | ((code >> 6) & 0x3f)); + utf8[count++] = (char)(0x80 | (code & 0x3f)); + size -= 3; + } + else + { + if (size < 4) goto utf8_end; + utf8[count++] = (char)(0xf0 | (code >> 18)); + utf8[count++] = (char)(0x80 | ((code >> 12) & 0x3f)); + utf8[count++] = (char)(0x80 | ((code >> 6) & 0x3f)); + utf8[count++] = (char)(0x80 | (code & 0x3f)); + size -= 4; + } + } + +utf8_end: + utf8[count]=0; + return count; +} + +static int initImeDialog(const char *usrTitle, const char *initialText, int maxTextLen, int type) +{ + OrbisImeDialogSetting param; + + if (ime_dialog_running) + return 0; + + if ((strlen(initialText) > countof(inputTextBuffer)) || (strlen(usrTitle) > countof(input_ime_title))) + { + ime_dialog_running = 0; + return 0; + } + + memset(¶m, 0, sizeof(OrbisImeDialogSetting)); + memset(inputTextBuffer, 0, sizeof(inputTextBuffer)); + memset(input_ime_title, 0, sizeof(input_ime_title)); + + // converts the multibyte string src to a wide-character string starting at dest. + convert_to_utf16(initialText, inputTextBuffer, countof(inputTextBuffer)); + convert_to_utf16(usrTitle, input_ime_title, countof(input_ime_title)); + + param.userId = gcm_config.user_id; + param.supportedLanguages = 0; + param.maxTextLength = maxTextLen; + param.inputTextBuffer = (wchar_t*) inputTextBuffer; + param.title = (wchar_t*) input_ime_title; + param.type = type ? ORBIS_TYPE_TYPE_URL : ORBIS_TYPE_DEFAULT; + param.posx = 1920 / 2; + param.posy = 1080 / 2; + param.horizontalAlignment = ORBIS_H_CENTER; + param.verticalAlignment = ORBIS_V_CENTER; + param.enterLabel = ORBIS_BUTTON_LABEL_DEFAULT; + + if (sceImeDialogInit(¶m, NULL) < 0) + return 0; + + ime_dialog_running = 1; + return 1; +} + +static int updateImeDialog(void) +{ + OrbisDialogStatus status; + OrbisDialogResult result; + + if (!ime_dialog_running) + return 0; + + status = sceImeDialogGetStatus(); + if (status == ORBIS_DIALOG_STATUS_STOPPED) + { + memset(&result, 0, sizeof(OrbisDialogResult)); + sceImeDialogGetResult(&result); + + sceImeDialogTerm(); + ime_dialog_running = 0; + + if (result.endstatus == ORBIS_DIALOG_OK) + return 1; + + return (-1); + } + + return 0; +} + +int osk_dialog_get_text(const char* title, char* text, uint32_t size) +{ + size = (size > SCE_IME_DIALOG_MAX_TEXT_LENGTH) ? SCE_IME_DIALOG_MAX_TEXT_LENGTH : (size-1); + + if (!initImeDialog(title, text, size, 1)) + return 0; + + while (ime_dialog_running) + { + if (updateImeDialog() < 0) + return 0; + } + + return (convert_from_utf16(inputTextBuffer, text, size)); +} diff --git a/source/http.c b/source/http.c index 5b343d4..9f75a4c 100644 --- a/source/http.c +++ b/source/http.c @@ -10,26 +10,24 @@ #include "common.h" #include "cheats.h" -#define HTTP_SUCCESS 1 -#define HTTP_FAILED 0 #define HTTP_USER_AGENT "Mozilla/5.0 (PLAYSTATION 4; 1.00)" int http_init(void) { if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET) < 0) - return HTTP_FAILED; + return false; if(sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NETCTL) < 0) - return HTTP_FAILED; + return false; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) - return HTTP_FAILED; + return false; if(sceNetCtlInit() < 0) - return HTTP_FAILED; + return false; - return HTTP_SUCCESS; + return true; } /* follow the CURLOPT_XFERINFOFUNCTION callback definition */ @@ -53,13 +51,13 @@ int http_download(const char* url, const char* filename, const char* local_dst, if(!curl) { LOG("ERROR: CURL INIT"); - return HTTP_FAILED; + return false; } fd = fopen(local_dst, "wb"); if (!fd) { LOG("fopen Error: File path '%s'", local_dst); - return HTTP_FAILED; + return false; } snprintf(full_url, sizeof(full_url), "%s%s", url, filename); @@ -128,10 +126,10 @@ int http_download(const char* url, const char* filename, const char* local_dst, { LOG("curl_easy_perform() failed: %s", curl_easy_strerror(res)); unlink_secure(local_dst); - return HTTP_FAILED; + return false; } - return HTTP_SUCCESS; + return true; } void http_end(void) diff --git a/source/main.c b/source/main.c index d12043d..5623794 100644 --- a/source/main.c +++ b/source/main.c @@ -48,6 +48,9 @@ app_config_t gcm_config = { .update = 1, .overwrite = 1, .user_id = 0, + .url_cheats = GOLDCHEATS_URL, + .url_patches = GOLDPATCH_URL, + .url_plugins = GOLDPLUGINS_UPDATE_URL, }; int close_app = 0; diff --git a/source/settings.c b/source/settings.c index 99bc482..5488fa1 100644 --- a/source/settings.c +++ b/source/settings.c @@ -17,6 +17,16 @@ static char * sort_opt[] = {"Disabled", "by Name", "by Title ID", NULL}; +static void log_callback(int sel); +static void music_callback(int sel); +static void sort_callback(int sel); +static void ani_callback(int sel); +static void overwrite_callback(int sel); +static void clearcache_callback(int sel); +static void clearpatch_callback(int sel); +static void set_pluginsperms_callback(int sel); +static void change_url_callback(int sel); + menu_option_t menu_options[] = { { .name = "Background Music", .options = NULL, @@ -48,6 +58,12 @@ menu_option_t menu_options[] = { .value = &gcm_config.overwrite, .callback = overwrite_callback }, + { .name = "Change Update Download URLs", + .options = NULL, + .type = APP_OPTION_CALL, + .value = NULL, + .callback = change_url_callback + }, { .name = "\nClear Temp Folder", .options = NULL, .type = APP_OPTION_CALL, @@ -64,7 +80,7 @@ menu_option_t menu_options[] = { .options = NULL, .type = APP_OPTION_CALL, .value = NULL, - .callback = setpluginsperms_callback + .callback = set_pluginsperms_callback }, { .name = "Enable Debug Log", .options = NULL, @@ -76,27 +92,27 @@ menu_option_t menu_options[] = { }; -void music_callback(int sel) +static void music_callback(int sel) { gcm_config.music = !sel; } -void sort_callback(int sel) +static void sort_callback(int sel) { gcm_config.doSort = sel; } -void ani_callback(int sel) +static void ani_callback(int sel) { gcm_config.doAni = !sel; } -void overwrite_callback(int sel) +static void overwrite_callback(int sel) { gcm_config.overwrite = !sel; } -void clearcache_callback(int sel) +static void clearcache_callback(int sel) { LOG("Cleaning folder '%s'...", CHEATSMGR_LOCAL_CACHE); clean_directory(CHEATSMGR_LOCAL_CACHE); @@ -104,14 +120,14 @@ void clearcache_callback(int sel) show_message("Local cache folder cleaned:\n" CHEATSMGR_LOCAL_CACHE); } -void clearpatch_callback(int sel) +static void clearpatch_callback(int sel) { LOG("Cleaning folder '" GOLDPATCH_SETTINGS_PATH "'..."); clean_directory(GOLDPATCH_SETTINGS_PATH); show_message("Patch settings folder cleaned:\n" GOLDPATCH_SETTINGS_PATH); } -void setpluginsperms_callback(int sel) +static void set_pluginsperms_callback(int sel) { if (set_perms_directory(GOLDCHEATS_PLUGINS_PATH, 0777) == SUCCESS) { @@ -123,6 +139,27 @@ void setpluginsperms_callback(int sel) } } +static void change_url_callback(int sel) +{ + if (osk_dialog_get_text("Enter the Cheat Download URL (1/3)", gcm_config.url_cheats, sizeof(gcm_config.url_cheats))) + show_message("Cheat Download URL changed to:\n%s", gcm_config.url_cheats); + + if (osk_dialog_get_text("Enter the Patch Download URL (2/3)", gcm_config.url_patches, sizeof(gcm_config.url_patches))) + show_message("Patch Download URL changed to:\n%s", gcm_config.url_patches); + + if (osk_dialog_get_text("Enter the Plugin Download URL (3/3)", gcm_config.url_plugins, sizeof(gcm_config.url_plugins))) + show_message("Plugin Download URL changed to:\n%s", gcm_config.url_plugins); + + if (gcm_config.url_cheats[strlen(gcm_config.url_cheats)-1] != '/') + strcat(gcm_config.url_cheats, "/"); + + if (gcm_config.url_patches[strlen(gcm_config.url_patches)-1] != '/') + strcat(gcm_config.url_patches, "/"); + + if (gcm_config.url_plugins[strlen(gcm_config.url_plugins)-1] != '/') + strcat(gcm_config.url_plugins, "/"); +} + void update_callback(int sel) { gcm_config.update = !sel; @@ -193,7 +230,7 @@ void update_callback(int sel) return; } -void log_callback(int sel) +static void log_callback(int sel) { dbglogger_init_mode(FILE_LOGGER, CHEATSMGR_PATH "cheatsmgr.log", 0); show_message("Debug Logging Enabled!\n\n" CHEATSMGR_PATH "cheatsmgr.log");