diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b9e8f5..8c65f82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ add_executable(ezremote_client source/main.cpp source/orbis_jbc.c source/system.cpp + source/sfo.cpp source/textures.cpp source/windows.cpp source/zip_util.cpp diff --git a/source/actions.h b/source/actions.h index f0b9a8a..bf0b81e 100644 --- a/source/actions.h +++ b/source/actions.h @@ -53,7 +53,10 @@ enum ACTIONS ACTION_NEW_REMOTE_FILE, ACTION_SET_DEFAULT_LOCAL_FOLDER, ACTION_SET_DEFAULT_REMOTE_FOLDER, - ACTION_VIEW_IMAGE + ACTION_VIEW_LOCAL_IMAGE, + ACTION_VIEW_REMOTE_IMAGE, + ACTION_VIEW_LOCAL_PKG, + ACTION_VIEW_REMOTE_PKG }; enum OverWriteType diff --git a/source/config.cpp b/source/config.cpp index 042230a..bdf88bb 100644 --- a/source/config.cpp +++ b/source/config.cpp @@ -28,6 +28,8 @@ char display_site[32]; char language[128]; std::vector sites; std::vector http_servers; +std::set text_file_extensions; +std::set image_file_extensions; std::map site_settings; PackageUrlInfo install_pkg_url; char favorite_urls[MAX_FAVORITE_URLS][512]; @@ -142,6 +144,8 @@ namespace CONFIG "Site 11", "Site 12", "Site 13", "Site 14", "Site 15", "Site 16", "Site 17", "Site 18", "Site 19", "Site 20"}; http_servers = {HTTP_SERVER_APACHE, HTTP_SERVER_MS_IIS, HTTP_SERVER_NGINX, HTTP_SERVER_NPX_SERVE}; + text_file_extensions = { ".txt", ".ini", ".json", ".xml", ".html", ".xhtml", ".conf" }; + image_file_extensions = { ".bmp", ".jpg", ".jpeg", ".png", ".webp" }; OpenIniFile(CONFIG_INI_FILE); diff --git a/source/config.h b/source/config.h index 67f8be5..4a31fc7 100644 --- a/source/config.h +++ b/source/config.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "clients/remote_client.h" @@ -14,6 +15,8 @@ #define CONFIG_INI_FILE DATA_PATH "/config.ini" #define COOKIE_FILE DATA_PATH "/cookies.txt" #define TMP_EDITOR_FILE DATA_PATH "/tmp_editor.txt" +#define TMP_SFO_PATH DATA_PATH "/tmp_pkg.sfo" +#define TMP_ICON_PATH DATA_PATH "/tmp_icon.png" #define CONFIG_GLOBAL "Global" @@ -110,6 +113,8 @@ struct PackageUrlInfo extern std::vector sites; extern std::vector http_servers; +extern std::set text_file_extensions; +extern std::set image_file_extensions; extern std::map site_settings; extern char local_directory[255]; extern char remote_directory[255]; diff --git a/source/installer.cpp b/source/installer.cpp index 556018b..8f487db 100644 --- a/source/installer.cpp +++ b/source/installer.cpp @@ -116,7 +116,7 @@ namespace INSTALLER path = uri.quote(curl); curl_easy_cleanup(curl); } - + return host + path; } else @@ -265,7 +265,8 @@ namespace INSTALLER goto retry; } } - else if (ret > 0) goto err; + else if (ret > 0) + goto err; ret = sceBgftServiceDownloadStartTask(task_id); if (ret) @@ -346,7 +347,8 @@ namespace INSTALLER FS::Rm(filename); } } - else if (ret > 0) goto err; + else if (ret > 0) + goto err; ret = sceBgftServiceDownloadStartTask(task_id); if (ret) @@ -380,4 +382,80 @@ namespace INSTALLER err: return 0; } + + bool ExtractLocalPkg(const std::string &filename, pkg_header *pkg_hdr, const std::string sfo_path, const std::string icon_path) + { + pkg_header tmp_hdr; + pkg_header *p_hdr = pkg_hdr; + if (p_hdr == nullptr) + { + FS::Head(filename, &tmp_hdr, sizeof(pkg_header)); + p_hdr = &tmp_hdr; + } + + size_t entry_count = BE32(p_hdr->pkg_entry_count); + uint32_t entry_table_offset = BE32(p_hdr->pkg_table_offset); + uint64_t entry_table_size = entry_count * sizeof(pkg_table_entry); + void *entry_table_data = malloc(entry_table_size); + + FILE *fd = FS::OpenRead(filename); + FS::Seek(fd, entry_table_offset); + FS::Read(fd, entry_table_data, entry_table_size); + + pkg_table_entry *entries = (pkg_table_entry *)entry_table_data; + void* param_sfo_data = NULL; + uint32_t param_sfo_offset = 0; + uint32_t param_sfo_size = 0; + void *icon0_png_data = NULL; + uint32_t icon0_png_offset = 0; + uint32_t icon0_png_size = 0; + short items = 0; + for (size_t i = 0; i < entry_count; ++i) + { + switch (BE32(entries[i].id)) + { + case PKG_ENTRY_ID__PARAM_SFO: + param_sfo_offset = BE32(entries[i].offset); + param_sfo_size = BE32(entries[i].size); + items++; + break; + case PKG_ENTRY_ID__ICON0_PNG: + icon0_png_offset = BE32(entries[i].offset); + icon0_png_size = BE32(entries[i].size); + items++; + break; + default: + continue; + } + + if (items == 2) + break; + } + free(entry_table_data); + + if (param_sfo_offset > 0 && param_sfo_size > 0) + { + param_sfo_data = malloc(param_sfo_size); + FILE *out = FS::Create(sfo_path); + FS::Seek(fd, param_sfo_offset); + FS::Read(fd, param_sfo_data, param_sfo_size); + FS::Write(out, param_sfo_data, param_sfo_size); + FS::Close(out); + free(param_sfo_data); + } + + if (icon0_png_offset > 0 && icon0_png_size > 0) + { + icon0_png_data = malloc(icon0_png_size); + FILE *out = FS::Create(icon_path); + FS::Seek(fd, icon0_png_offset); + FS::Read(fd, icon0_png_data, icon0_png_size); + FS::Write(out, icon0_png_data, icon0_png_size); + FS::Close(out); + free(icon0_png_data); + } + + FS::Close(fd); + return true; + } } \ No newline at end of file diff --git a/source/installer.h b/source/installer.h index 4d352b0..9de1d02 100644 --- a/source/installer.h +++ b/source/installer.h @@ -1,30 +1,24 @@ #pragma once -#define SWAP16(x) \ - ((uint16_t)( \ - (((uint16_t)(x) & UINT16_C(0x00FF)) << 8) | \ - (((uint16_t)(x) & UINT16_C(0xFF00)) >> 8) \ - )) +#define SWAP16(x) \ + ((uint16_t)((((uint16_t)(x)&UINT16_C(0x00FF)) << 8) | \ + (((uint16_t)(x)&UINT16_C(0xFF00)) >> 8))) -#define SWAP32(x) \ - ((uint32_t)( \ - (((uint32_t)(x) & UINT32_C(0x000000FF)) << 24) | \ - (((uint32_t)(x) & UINT32_C(0x0000FF00)) << 8) | \ - (((uint32_t)(x) & UINT32_C(0x00FF0000)) >> 8) | \ - (((uint32_t)(x) & UINT32_C(0xFF000000)) >> 24) \ - )) +#define SWAP32(x) \ + ((uint32_t)((((uint32_t)(x)&UINT32_C(0x000000FF)) << 24) | \ + (((uint32_t)(x)&UINT32_C(0x0000FF00)) << 8) | \ + (((uint32_t)(x)&UINT32_C(0x00FF0000)) >> 8) | \ + (((uint32_t)(x)&UINT32_C(0xFF000000)) >> 24))) -#define SWAP64(x) \ - ((uint64_t)( \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x00000000000000FF)) << 56) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x000000000000FF00)) << 40) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x0000000000FF0000)) << 24) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x00000000FF000000)) << 8) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x000000FF00000000)) >> 8) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x0000FF0000000000)) >> 24) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0x00FF000000000000)) >> 40) | \ - (uint64_t)(((uint64_t)(x) & UINT64_C(0xFF00000000000000)) >> 56) \ - )) +#define SWAP64(x) \ + ((uint64_t)((uint64_t)(((uint64_t)(x)&UINT64_C(0x00000000000000FF)) << 56) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x000000000000FF00)) << 40) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x0000000000FF0000)) << 24) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x00000000FF000000)) << 8) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x000000FF00000000)) >> 8) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x0000FF0000000000)) >> 24) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0x00FF000000000000)) >> 40) | \ + (uint64_t)(((uint64_t)(x)&UINT64_C(0xFF00000000000000)) >> 56))) #define LE16(x) (x) #define LE32(x) (x) @@ -47,6 +41,9 @@ #define PKG_CONTENT_FLAGS_DELTA_PATCH 0x41000000 #define PKG_CONTENT_FLAGS_CUMULATIVE_PATCH 0x60000000 +#define PKG_ENTRY_ID__PARAM_SFO 0x1000 +#define PKG_ENTRY_ID__ICON0_PNG 0x1200 + typedef struct { uint32_t pkg_magic; // 0x000 - 0x7F434E54 @@ -98,11 +95,23 @@ typedef struct unsigned char pkg_digest[0x20]; // 0xFE0 } pkg_header; -enum pkg_content_type { - PKG_CONTENT_TYPE_GD = 0x1A, /* pkg_ps4_app, pkg_ps4_patch, pkg_ps4_remaster */ - PKG_CONTENT_TYPE_AC = 0x1B, /* pkg_ps4_ac_data, pkg_ps4_sf_theme, pkg_ps4_theme */ - PKG_CONTENT_TYPE_AL = 0x1C, /* pkg_ps4_ac_nodata */ - PKG_CONTENT_TYPE_DP = 0x1E, /* pkg_ps4_delta_patch */ +typedef struct +{ + uint32_t id; // File ID, useful for files without a filename entry + uint32_t filename_offset; // Offset into the filenames table (ID 0x200) where this file's name is located + uint32_t flags1; // Flags including encrypted flag, etc + uint32_t flags2; // Flags including encryption key index, etc + uint32_t offset; // Offset into PKG to find the file + uint32_t size; // Size of the file + uint64_t padding; // blank padding +} pkg_table_entry; + +enum pkg_content_type +{ + PKG_CONTENT_TYPE_GD = 0x1A, /* pkg_ps4_app, pkg_ps4_patch, pkg_ps4_remaster */ + PKG_CONTENT_TYPE_AC = 0x1B, /* pkg_ps4_ac_data, pkg_ps4_sf_theme, pkg_ps4_theme */ + PKG_CONTENT_TYPE_AL = 0x1C, /* pkg_ps4_ac_nodata */ + PKG_CONTENT_TYPE_DP = 0x1E, /* pkg_ps4_delta_patch */ }; namespace INSTALLER @@ -111,7 +120,8 @@ namespace INSTALLER void Exit(void); bool canInstallRemotePkg(const std::string &url); - std::string getRemoteUrl(const std::string filename, bool encodeUrl=false); + std::string getRemoteUrl(const std::string filename, bool encodeUrl = false); int InstallRemotePkg(const std::string &filename, pkg_header *header); - int InstallLocalPkg(const std::string &filename, pkg_header *header, bool remove_after_install=false); + int InstallLocalPkg(const std::string &filename, pkg_header *header, bool remove_after_install = false); + bool ExtractLocalPkg(const std::string &filename, pkg_header *pkg_header, const std::string sfo_path, const std::string icon_path); } \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 1ce1280..de60173 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +// #include #include "imgui.h" #include "SDL2/SDL.h" @@ -26,7 +26,6 @@ #include "installer.h" #include "system.h" #include "textures.h" -// #include "dbglogger.h" extern "C" { diff --git a/source/sfo.cpp b/source/sfo.cpp new file mode 100644 index 0000000..fb863cd --- /dev/null +++ b/source/sfo.cpp @@ -0,0 +1,30 @@ +#include +#include "sfo.h" + +static constexpr uint32_t SFO_MAGIC = 0x46535000; + +namespace SFO { + const char* GetString(const char* buffer, size_t size, const char *name) + { + if (size < sizeof(SfoHeader)) + return nullptr; + + const SfoHeader* header = reinterpret_cast(buffer); + const SfoEntry* entries = + reinterpret_cast(buffer + sizeof(SfoHeader)); + + if (header->magic != SFO_MAGIC) + return nullptr; + + if (size < sizeof(SfoHeader) + header->count * sizeof(SfoEntry)) + return nullptr; + + for (uint32_t i = 0; i < header->count; i++) { + const char* key = reinterpret_cast(buffer + header->keyofs + entries[i].nameofs); + if (strcmp(key, name) == 0) + return reinterpret_cast(buffer + header->valofs + entries[i].dataofs); + } + + return {}; + } +} \ No newline at end of file diff --git a/source/sfo.h b/source/sfo.h new file mode 100644 index 0000000..5662d0e --- /dev/null +++ b/source/sfo.h @@ -0,0 +1,32 @@ +#ifndef LAUNCHER_SFO_H +#define LAUNCHER_SFO_H + +#pragma once + +#include +#include + +struct SfoHeader +{ + uint32_t magic; + uint32_t version; + uint32_t keyofs; + uint32_t valofs; + uint32_t count; +} __attribute__((packed)); + +struct SfoEntry +{ + uint16_t nameofs; + uint8_t alignment; + uint8_t type; + uint32_t valsize; + uint32_t totalsize; + uint32_t dataofs; +} __attribute__((packed)); + +namespace SFO { + const char* GetString(const char* buffer, size_t size, const char *name); +} + +#endif \ No newline at end of file diff --git a/source/textures.cpp b/source/textures.cpp index 5809654..9b4ae32 100644 --- a/source/textures.cpp +++ b/source/textures.cpp @@ -10,16 +10,23 @@ namespace Textures { SDL_Surface *image = IMG_Load(filename.c_str()); if (image == nullptr) return false; - image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_RGBA8888, 0); - SDL_Texture *sdl_texture = SDL_CreateTextureFromSurface(renderer, image); + SDL_Surface *formated_image = SDL_ConvertSurfaceFormat(image, SDL_PIXELFORMAT_RGBA8888, 0); + if (formated_image == nullptr) + { + SDL_FreeSurface(image); + } + SDL_Texture *sdl_texture = SDL_CreateTextureFromSurface(renderer, formated_image); if (sdl_texture == nullptr) { + SDL_FreeSurface(formated_image); SDL_FreeSurface(image); return false; } texture->id = sdl_texture; - texture->height = image->h; - texture->width = image->w; + texture->height = formated_image->h; + texture->width = formated_image->w; + + SDL_FreeSurface(formated_image); SDL_FreeSurface(image); return true; diff --git a/source/windows.cpp b/source/windows.cpp index 5f3f291..6cf0ab9 100644 --- a/source/windows.cpp +++ b/source/windows.cpp @@ -587,10 +587,13 @@ namespace Windows if (dot_pos != std::string::npos) { std::string ext = filename.substr(dot_pos); - if (ext.compare(".bmp") == 0 || ext.compare(".jpg") == 0 || ext.compare(".png") == 0 || - ext.compare(".jpeg") == 0 || ext.compare(".webp") == 0) + if (image_file_extensions.find(ext) != image_file_extensions.end()) { - selected_action = ACTION_VIEW_IMAGE; + selected_action = ACTION_VIEW_LOCAL_IMAGE; + } + else if (ext.compare(".pkg") == 0) + { + INSTALLER::ExtractLocalPkg(selected_local_file.path, nullptr, TMP_SFO_PATH, TMP_ICON_PATH); } } } @@ -734,11 +737,24 @@ namespace Windows ImGui::PushID(i); if (ImGui::Selectable(item.name, false, ImGuiSelectableFlags_SpanAllColumns, ImVec2(919, 0))) { + selected_remote_file = item; if (item.isDir) { - selected_remote_file = item; selected_action = ACTION_CHANGE_REMOTE_DIRECTORY; } + else + { + std::string filename = Util::ToLower(selected_remote_file.name); + size_t dot_pos = filename.find_last_of("."); + if (dot_pos != std::string::npos) + { + std::string ext = filename.substr(dot_pos); + if (image_file_extensions.find(ext) != image_file_extensions.end()) + { + selected_action = ACTION_VIEW_REMOTE_IMAGE; + } + } + } } if (ImGui::IsItemFocused()) { @@ -2022,13 +2038,21 @@ namespace Windows sprintf(status_message, "\"%s\" %s", remote_directory, lang_strings[STR_SET_DEFAULT_DIRECTORY_MSG]); selected_action = ACTION_NONE; break; - case ACTION_VIEW_IMAGE: + case ACTION_VIEW_LOCAL_IMAGE: if (Textures::LoadImageFile(selected_local_file.path, &texture)) { view_image = true; } selected_action = ACTION_NONE; break; + case ACTION_VIEW_REMOTE_IMAGE: + remoteclient->Get(TMP_ICON_PATH, selected_remote_file.path); + if (Textures::LoadImageFile(TMP_ICON_PATH, &texture)) + { + view_image = true; + } + selected_action = ACTION_NONE; + break; default: break; }