├── sce_module ├── .gitkeep ├── libc.prx ├── libjbc.prx └── libSceFios2.prx ├── sce_sys ├── about │ ├── .gitkeep │ └── right.sprx ├── icon0.png ├── pic0.png └── pic1.png ├── RPI ├── KPutil.h ├── RPI.vcxproj.user ├── server.h ├── syscalls.S ├── net.h ├── syscalls.h ├── http.h ├── sfo.h ├── module.h ├── KPutil.c ├── util.h ├── installer.h ├── sandbird.h ├── pkg.h ├── common.h ├── RPI.vcxproj.filters ├── net.c ├── build.bat ├── RPI.vcxproj ├── module.c ├── utringbuffer.h ├── sfo.c ├── tiny-json.h ├── util.c ├── main.c ├── http.c ├── utstring.h ├── utarray.h ├── pkg.c ├── tiny-json.c ├── installer.c └── sandbird.c ├── README.md └── RPI.sln /sce_module/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sce_sys/about/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sce_sys/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_sys/icon0.png -------------------------------------------------------------------------------- /sce_sys/pic0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_sys/pic0.png -------------------------------------------------------------------------------- /sce_sys/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_sys/pic1.png -------------------------------------------------------------------------------- /sce_module/libc.prx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_module/libc.prx -------------------------------------------------------------------------------- /sce_module/libjbc.prx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_module/libjbc.prx -------------------------------------------------------------------------------- /sce_sys/about/right.sprx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_sys/about/right.sprx -------------------------------------------------------------------------------- /sce_module/libSceFios2.prx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Backporter/ps4_remote_pkg_installer-OOSDK/HEAD/sce_module/libSceFios2.prx -------------------------------------------------------------------------------- /RPI/KPutil.h: -------------------------------------------------------------------------------- 1 | void Notify(const char* FMT, ...); 2 | void KernelPrintOut(const char* FMT, ...); 3 | void SafeExit(const char* reason, ...); -------------------------------------------------------------------------------- /RPI/RPI.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /RPI/server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | bool server_start(const char* ip_address, int port, const char* work_dir); 6 | bool server_listen(void); 7 | void server_stop(void); 8 | -------------------------------------------------------------------------------- /RPI/syscalls.S: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .global syscall 4 | syscall: 5 | movq $0, %rax 6 | movq %rcx, %r10 7 | syscall 8 | jb err 9 | retq 10 | err: 11 | pushq %rax 12 | callq __error 13 | popq %rcx 14 | movl %ecx, 0(%rax) 15 | movq $0xFFFFFFFFFFFFFFFF, %rax 16 | movq $0xFFFFFFFFFFFFFFFF, %rdx 17 | retq 18 | -------------------------------------------------------------------------------- /RPI/net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #include 6 | 7 | bool net_init(void); 8 | bool net_is_initialized(void); 9 | int net_get_mem_id(void); 10 | void net_fini(void); 11 | 12 | int net_get_ipv4(char* buf, size_t buf_size); 13 | 14 | int net_send_all(int sock_id, const void* data, size_t size, size_t* sent); 15 | int net_recv_all(int sock_id, void* data, size_t size, size_t* received); 16 | -------------------------------------------------------------------------------- /RPI/syscalls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | #define SYS_unmount 22 10 | #define SYS_getdents 272 11 | #define SYS_nmount 378 12 | #define SYS_supercall 394 13 | #define SYS_gain_privileges 410 14 | #define SYS_dynlib_get_info 593 15 | #define SYS_dynlib_get_info_ex 608 16 | 17 | // int syscall(int num, ...); 18 | 19 | #ifdef __cplusplus 20 | } 21 | #endif 22 | -------------------------------------------------------------------------------- /RPI/http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | bool http_init(void); 6 | void http_fini(void); 7 | 8 | bool http_get_file_size(const char* url, uint64_t* total_size); 9 | bool http_download_file(const char* url, uint8_t** data, uint64_t* data_size, uint64_t* total_size, uint64_t offset); 10 | 11 | bool http_escape_uri(char** out, size_t* out_size, const char* in); 12 | bool http_unescape_uri(char** out, size_t* out_size, const char* in); 13 | 14 | bool http_escape_json_string(char* out, size_t max_out_size, const char* in); 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ps4_remote_pkg_installer-OOSDK 2 | This is a OOSDK port of flat's ps4_remote_pkg_installer, all credit goes to them and their amazing work! 3 | 4 | # NOTES 5 | - Some functions are not in OOSDK yet(:tm:) so i worked around this by manually importing them. 6 | - Some freeBSD net functions where giving issue with printing debug info out, so i swapped these to the sceNet* versions as they seemed to work. 7 | - if you have ran the orginal remote package installer it will cause a port conflict and this one will fail to function, restart your unit and only run this 8 | - WINDOWS building only for the time being -------------------------------------------------------------------------------- /RPI/sfo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | enum sfo_value_format { 6 | SFO_FORMAT_STRING_SPECIAL = 0x4, 7 | SFO_FORMAT_STRING = 0x204, 8 | SFO_FORMAT_UINT32 = 0x404, 9 | }; 10 | 11 | struct sfo_entry { 12 | char* key; 13 | size_t size; 14 | size_t area; 15 | void* value; 16 | enum sfo_value_format format; 17 | struct sfo_entry* next; 18 | struct sfo_entry* prev; 19 | }; 20 | 21 | struct sfo { 22 | struct sfo_entry* entries; 23 | }; 24 | 25 | struct sfo* sfo_alloc(void); 26 | void sfo_free(struct sfo* sfo); 27 | 28 | bool sfo_load_from_file(struct sfo* sfo, const char* file_path); 29 | bool sfo_load_from_memory(struct sfo* sfo, const void* data, size_t data_size); 30 | 31 | struct sfo_entry* sfo_find_entry(struct sfo* sfo, const char* key); 32 | -------------------------------------------------------------------------------- /RPI/module.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #include 6 | 7 | #define DLSYM_MANGLED_NAME 0x1 8 | 9 | 10 | int32_t sceKernelLoadStartModuleFromSandbox(const char* name, size_t args, const void* argp, unsigned int flags, const OrbisKernelEventFlagOptParam* opts, int* res); 11 | 12 | // int sceKernelGetModuleInfo(int32_t handle, OrbisKernelModuleInfo* info); 13 | // int sceKernelGetModuleInfoEx(int32_t handle, OrbisKernelModuleInfo* info); 14 | 15 | int sceKernelGetModuleInfoByName(const char* name, OrbisKernelModuleInfo* info); 16 | int sceKernelGetModuleInfoExByName(const char* name, OrbisKernelModuleInfo* info); 17 | 18 | int sceKernelDlsymEx(int32_t handle, const char* symbol, const char* lib, unsigned int flags, void** addrp); 19 | 20 | bool get_module_base(const char* name, uint64_t* base, uint64_t* size); 21 | 22 | typedef void module_patch_cb_t(void* arg, uint8_t* base, uint64_t size); 23 | bool patch_module(const char* name, module_patch_cb_t* cb, void* arg); 24 | -------------------------------------------------------------------------------- /RPI/KPutil.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | void Notify(const char* FMT, ...) 8 | { 9 | OrbisNotificationRequest Buffer; 10 | 11 | va_list args; 12 | va_start(args, FMT); 13 | vsprintf(Buffer.message, FMT, args); 14 | va_end(args); 15 | 16 | Buffer.type = NotificationRequest; 17 | Buffer.unk3 = 0; 18 | Buffer.useIconImageUri = 1; 19 | Buffer.targetId = -1; 20 | strcpy(Buffer.iconUri, "cxml://psnotification/tex_icon_champions_league"); 21 | 22 | sceKernelSendNotificationRequest(0, &Buffer, 3120, 0); 23 | } 24 | 25 | void KernelPrintOut(const char* FMT, ...) 26 | { 27 | char MessageBuf[1024]; 28 | va_list args; 29 | va_start(args, FMT); 30 | vsprintf(MessageBuf, FMT, args); 31 | va_end(args); 32 | 33 | sceKernelDebugOutText(0, MessageBuf); 34 | } 35 | 36 | void SafeExit(const char* reason, ...) 37 | { 38 | char MessageBuf[1024]; 39 | va_list args; 40 | va_start(args, reason); 41 | vsprintf(MessageBuf, reason, args); 42 | va_end(args); 43 | 44 | KernelPrintOut(reason); 45 | sceSystemServiceLoadExec((char*)"exit", NULL); 46 | } -------------------------------------------------------------------------------- /RPI.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2016 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RPI", "RPI\RPI.vcxproj", "{79DFD476-4249-4BA5-B6EF-1A96074BCD5D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {79DFD476-4249-4BA5-B6EF-1A96074BCD5D}.Debug|x64.ActiveCfg = Debug|x64 15 | {79DFD476-4249-4BA5-B6EF-1A96074BCD5D}.Debug|x64.Build.0 = Debug|x64 16 | {79DFD476-4249-4BA5-B6EF-1A96074BCD5D}.Release|x64.ActiveCfg = Release|x64 17 | {79DFD476-4249-4BA5-B6EF-1A96074BCD5D}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {F61FB497-00A6-4138-85B4-4D5856BDCF28} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /RPI/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | bool get_language_id(int* lang_id); 6 | 7 | int bytes_to_hex(char* buf, size_t buf_size, const void* data, size_t data_size); 8 | 9 | bool read_file(const char* path, void** data, uint64_t* size, uint64_t max_size, uint64_t* nread); 10 | bool write_file(const char* path, const void* data, uint64_t size, uint64_t* nwritten, int mode, unsigned int flags); 11 | bool write_file_trunc(const char* path, const void* data, uint64_t size, uint64_t* nwritten, int mode); 12 | 13 | bool is_file_exists(const char* path); 14 | 15 | void hexdump(const void* data, size_t size); 16 | 17 | bool starts_with(const char* haystack, const char* needle); 18 | bool ends_with(const char* haystack, const char* needle); 19 | 20 | bool ends_with(const char* haystack, const char* needle); 21 | bool ends_with_nocase(const char* haystack, const char* needle); 22 | 23 | char* rtrim(char* s); 24 | 25 | #define NSEC_PER_USEC INT64_C(1000) 26 | #define NSEC_PER_MSEC INT64_C(1000000) 27 | #define NSEC_PER_SEC INT64_C(1000000000) 28 | 29 | struct timespec* timespec_now(struct timespec* tp); 30 | 31 | struct timespec* timespec_sub(struct timespec* tp, const struct timespec* a, const struct timespec* b); 32 | 33 | /* a < b: return < 0; a == b: return 0; a > b: return > 0 */ 34 | int timespec_compare(const struct timespec* a, const struct timespec* b); 35 | 36 | static inline int64_t usec_to_nsec(int64_t x) { 37 | return NSEC_PER_USEC * x; 38 | } 39 | -------------------------------------------------------------------------------- /RPI/installer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | bool app_inst_util_init(void); 6 | void app_inst_util_fini(void); 7 | 8 | bool app_inst_util_uninstall_game(const char* title_id, int* error); 9 | bool app_inst_util_uninstall_ac(const char* content_id, int* error); 10 | bool app_inst_util_uninstall_patch(const char* title_id, int* error); 11 | bool app_inst_util_uninstall_theme(const char* content_id, int* error); 12 | 13 | bool app_inst_util_is_exists(const char* title_id, bool* exists, int* error); 14 | bool app_inst_util_get_size(const char* title_id, unsigned long* size, int* error); 15 | 16 | struct bgft_download_task_progress_info { 17 | unsigned int bits; 18 | int error_result; 19 | unsigned long length; 20 | unsigned long transferred; 21 | unsigned long length_total; 22 | unsigned long transferred_total; 23 | unsigned int num_index; 24 | unsigned int num_total; 25 | unsigned int rest_sec; 26 | unsigned int rest_sec_total; 27 | int preparing_percent; 28 | int local_copy_percent; 29 | }; 30 | 31 | bool bgft_init(void); 32 | void bgft_fini(void); 33 | 34 | bool bgft_download_register_package_task(const char* content_id, const char* content_url, const char* content_name, const char* icon_path, const char* package_type, const char* package_sub_type, unsigned long package_size, bool is_patch, int* task_id, int* error); 35 | bool bgft_download_start_task(int task_id, int* error); 36 | bool bgft_download_stop_task(int task_id, int* error); 37 | bool bgft_download_pause_task(int task_id, int* error); 38 | bool bgft_download_resume_task(int task_id, int* error); 39 | bool bgft_download_unregister_task(int task_id, int* error); 40 | bool bgft_download_reregister_task_patch(int old_task_id, int* new_task_id, int* error); 41 | bool bgft_download_get_task_progress(int task_id, struct bgft_download_task_progress_info* progress_info, int* error); 42 | bool bgft_download_find_task_by_content_id(const char* content_id, int sub_type, int* task_id, int* error); 43 | -------------------------------------------------------------------------------- /RPI/sandbird.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | 9 | #ifndef SANDBIRD_H 10 | #define SANDBIRD_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #define SB_VERSION "0.1.4" 22 | 23 | typedef struct sb_Server sb_Server; 24 | typedef struct sb_Stream sb_Stream; 25 | typedef struct sb_Event sb_Event; 26 | typedef struct sb_Options sb_Options; 27 | typedef int (*sb_Handler)(sb_Event*); 28 | 29 | struct sb_Event { 30 | int type; 31 | void *udata; 32 | sb_Server *server; 33 | sb_Stream *stream; 34 | const char *address; 35 | const char *method; 36 | const char *path; 37 | }; 38 | 39 | struct sb_Options { 40 | sb_Handler handler; 41 | void *udata; 42 | const char *host; 43 | const char *port; 44 | const char *timeout; 45 | const char *max_lifetime; 46 | const char *max_request_size; 47 | }; 48 | 49 | enum { 50 | SB_ESUCCESS = 0, 51 | SB_EFAILURE = -1, 52 | SB_EOUTOFMEM = -2, 53 | SB_ETRUNCATED = -3, 54 | SB_EBADSTATE = -4, 55 | SB_EBADRESULT = -5, 56 | SB_ECANTOPEN = -6, 57 | SB_ENOTFOUND = -7, 58 | }; 59 | 60 | enum { 61 | SB_EV_CONNECT, 62 | SB_EV_CLOSE, 63 | SB_EV_REQUEST 64 | }; 65 | 66 | enum { 67 | SB_RES_OK, 68 | SB_RES_CLOSE 69 | }; 70 | 71 | const char *sb_error_str(int code); 72 | sb_Server *sb_new_server(const sb_Options *opt); 73 | void sb_close_server(sb_Server *srv); 74 | int sb_poll_server(sb_Server *srv); 75 | int sb_send_status(sb_Stream *st, int code, const char *msg); 76 | int sb_send_header(sb_Stream *st, const char *field, const char *val); 77 | int sb_send_file(sb_Stream *st, const char *filename); 78 | int sb_write(sb_Stream *st, const void *data, size_t len); 79 | int sb_vwritef(sb_Stream *st, const char *fmt, va_list args); 80 | int sb_writef(sb_Stream *st, const char *fmt, ...); 81 | int sb_get_header(sb_Stream *st, const char *field, char *dst, size_t len); 82 | int sb_get_var_ex(sb_Stream *st, const char *name, char *dst, size_t len, bool from_data_only); 83 | int sb_get_var(sb_Stream *st, const char *name, char *dst, size_t len); 84 | char *sb_get_content_data(sb_Stream *st, size_t *len); 85 | int sb_get_cookie(sb_Stream *st, const char *name, char *dst, size_t len); 86 | const void *sb_get_multipart(sb_Stream *st, const char *name, size_t *len); 87 | time_t sb_stream_get_init_time(sb_Stream *st); 88 | 89 | #ifdef __cplusplus 90 | } // extern "C" 91 | #endif 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /RPI/pkg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | enum pkg_entry_id { 6 | PKG_ENTRY_ID__PARAM_SFO = 0x1000, 7 | PKG_ENTRY_ID__ICON0_PNG = 0x1200, 8 | }; 9 | 10 | enum pkg_content_type { 11 | PKG_CONTENT_TYPE_GD = 0x1A, /* pkg_ps4_app, pkg_ps4_patch, pkg_ps4_remaster */ 12 | PKG_CONTENT_TYPE_AC = 0x1B, /* pkg_ps4_ac_data, pkg_ps4_sf_theme, pkg_ps4_theme */ 13 | PKG_CONTENT_TYPE_AL = 0x1C, /* pkg_ps4_ac_nodata */ 14 | PKG_CONTENT_TYPE_DP = 0x1E, /* pkg_ps4_delta_patch */ 15 | }; 16 | 17 | #define PKG_TITLE_ID_SIZE 0x9 18 | #define PKG_SERVICE_ID_SIZE 0x13 19 | #define PKG_CONTENT_ID_SIZE 0x24 20 | #define PKG_LABEL_SIZE 0x10 21 | #define PKG_DIGEST_SIZE 0x20 22 | #define PKG_MINI_DIGEST_SIZE 0x14 23 | 24 | #define SIZEOF_PKG_HEADER 0x2000 25 | 26 | TYPE_BEGIN(struct pkg_header, SIZEOF_PKG_HEADER); 27 | TYPE_FIELD(uint8_t magic[4], 0x00); 28 | TYPE_FIELD(uint32_t entry_count, 0x10); 29 | TYPE_FIELD(uint16_t sc_entry_count, 0x14); 30 | TYPE_FIELD(uint32_t entry_table_offset, 0x18); 31 | TYPE_FIELD(char content_id[PKG_CONTENT_ID_SIZE + 1], 0x40); 32 | TYPE_FIELD(uint32_t content_type, 0x74); 33 | TYPE_FIELD(uint32_t content_flags, 0x78); 34 | # define PKG_CONTENT_FLAGS_FIRST_PATCH 0x00100000 35 | # define PKG_CONTENT_FLAGS_PATCHGO 0x00200000 36 | # define PKG_CONTENT_FLAGS_REMASTER 0x00400000 37 | # define PKG_CONTENT_FLAGS_PS_CLOUD 0x00800000 38 | # define PKG_CONTENT_FLAGS_GD_AC 0x02000000 39 | # define PKG_CONTENT_FLAGS_NON_GAME 0x04000000 40 | # define PKG_CONTENT_FLAGS_0x8000000 0x08000000 /* has data? */ 41 | # define PKG_CONTENT_FLAGS_SUBSEQUENT_PATCH 0x40000000 42 | # define PKG_CONTENT_FLAGS_DELTA_PATCH 0x41000000 43 | # define PKG_CONTENT_FLAGS_CUMULATIVE_PATCH 0x60000000 44 | TYPE_FIELD(uint64_t package_size, 0x430); 45 | TYPE_FIELD(uint8_t digest[PKG_DIGEST_SIZE], 0xFE0); 46 | TYPE_END(); 47 | TYPE_CHECK_SIZE(struct pkg_header, SIZEOF_PKG_HEADER); 48 | 49 | #define SIZEOF_PKG_TABLE_ENTRY 0x20 50 | 51 | TYPE_BEGIN(struct pkg_table_entry, SIZEOF_PKG_TABLE_ENTRY); 52 | TYPE_FIELD(uint32_t id, 0x00); // enum pkg_entry_id 53 | TYPE_FIELD(uint32_t offset, 0x10); 54 | TYPE_FIELD(uint32_t size, 0x14); 55 | TYPE_END(); 56 | TYPE_CHECK_SIZE(struct pkg_table_entry, SIZEOF_PKG_TABLE_ENTRY); 57 | 58 | struct pkg_content_info { 59 | char content_id[PKG_CONTENT_ID_SIZE + 1]; 60 | char service_id[PKG_SERVICE_ID_SIZE + 1]; 61 | char title_id[PKG_TITLE_ID_SIZE + 1]; 62 | char label[PKG_LABEL_SIZE + 1]; 63 | }; 64 | 65 | bool pkg_parse_content_id(const char* content_id, struct pkg_content_info* info); 66 | 67 | char** pkg_extract_piece_urls_from_ref_pkg_json(const char* url, size_t* piece_count); 68 | 69 | bool pkg_setup_prerequisites(char** piece_urls, size_t piece_count, const char* ref_pkg_json_path, const char* param_sfo_path, const char* icon0_png_path, enum pkg_content_type* content_type, uint64_t* package_size, bool* is_patch, bool* has_icon, char* error_buf, size_t error_buf_size); 70 | 71 | bool pkg_is_patch(struct pkg_header* hdr); 72 | -------------------------------------------------------------------------------- /RPI/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "syscalls.h" 16 | #include "KPutil.h" 17 | 18 | #define STRINGIFY(x) #x 19 | #define STRINGIFY_DEEP(x) STRINGIFY(x) 20 | 21 | #define JOIN_HELPER(x, y) x##y 22 | #define JOIN(x, y) JOIN_HELPER(x, y) 23 | 24 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 25 | 26 | #define ALIGN_UP(x, alignment) (((x) + ((alignment) - 1)) & ~((alignment) - 1)) 27 | #define ALIGN_DOWN(x, alignment) ((x) & ~((alignment) - 1)) 28 | 29 | #define UNUSED(x) (void)(x) 30 | #if 1 31 | # define EPRINTF(msg, ...) KernelPrintOut("Error at %s:%s(%d): " msg, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) 32 | #else 33 | # define EPRINTF(msg, ...) 34 | #endif 35 | 36 | #define SWAP16(x) \ 37 | ((uint16_t)( \ 38 | (((uint16_t)(x) & UINT16_C(0x00FF)) << 8) | \ 39 | (((uint16_t)(x) & UINT16_C(0xFF00)) >> 8) \ 40 | )) 41 | 42 | #define SWAP32(x) \ 43 | ((uint32_t)( \ 44 | (((uint32_t)(x) & UINT32_C(0x000000FF)) << 24) | \ 45 | (((uint32_t)(x) & UINT32_C(0x0000FF00)) << 8) | \ 46 | (((uint32_t)(x) & UINT32_C(0x00FF0000)) >> 8) | \ 47 | (((uint32_t)(x) & UINT32_C(0xFF000000)) >> 24) \ 48 | )) 49 | 50 | #define SWAP64(x) \ 51 | ((uint64_t)( \ 52 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x00000000000000FF)) << 56) | \ 53 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x000000000000FF00)) << 40) | \ 54 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x0000000000FF0000)) << 24) | \ 55 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x00000000FF000000)) << 8) | \ 56 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x000000FF00000000)) >> 8) | \ 57 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x0000FF0000000000)) >> 24) | \ 58 | (uint64_t)(((uint64_t)(x) & UINT64_C(0x00FF000000000000)) >> 40) | \ 59 | (uint64_t)(((uint64_t)(x) & UINT64_C(0xFF00000000000000)) >> 56) \ 60 | )) 61 | 62 | #define LE16(x) (x) 63 | #define LE32(x) (x) 64 | #define LE64(x) (x) 65 | 66 | #define BE16(x) SWAP16(x) 67 | #define BE32(x) SWAP32(x) 68 | #define BE64(x) SWAP64(x) 69 | 70 | #ifndef MIN 71 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) 72 | #endif 73 | #ifndef MAX 74 | # define MAX(a, b) (((a) > (b)) ? (a) : (b)) 75 | #endif 76 | 77 | #define TYPE_PAD(size) char JOIN(_pad_, __COUNTER__)[size] 78 | #define TYPE_VARIADIC_BEGIN(name) name { union { 79 | #define TYPE_BEGIN(name, size) name { union { TYPE_PAD(size) 80 | #define TYPE_END(...) }; } __VA_ARGS__ 81 | #define TYPE_FIELD(field, offset) struct { TYPE_PAD(offset); field; } 82 | 83 | #define TYPE_CHECK_SIZE(name, size) \ 84 | _Static_assert(sizeof(name) == (size), "Size of " #name " != " #size) 85 | 86 | #define TYPE_CHECK_FIELD_OFFSET(name, member, offset) \ 87 | _Static_assert(offsetof(name, member) == (offset), "Offset of " #name "." #member " != " #offset) 88 | 89 | #define TYPE_CHECK_FIELD_SIZE(name, member, size) \ 90 | _Static_assert(sizeof(((name*)0)->member) == (size), "Size of " #name "." #member " != " #size) 91 | 92 | enum { 93 | SUPERCALL_NULL, 94 | SUPERCALL_PEEK_POKE, 95 | SUPERCALL_GET_MEMORY_LAYOUT, 96 | SUPERCALL_SET_AUTH_INFO, 97 | SUPERCALL_GATE, 98 | SUPERCALL_DLSYM, 99 | }; 100 | -------------------------------------------------------------------------------- /RPI/RPI.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | 14 | 15 | 16 | Source Files 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | Header Files 106 | 107 | 108 | 109 | 110 | Header Files 111 | 112 | 113 | -------------------------------------------------------------------------------- /RPI/net.c: -------------------------------------------------------------------------------- 1 | #include "net.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #define NET_HEAP_SIZE (1 * 1024 * 1024) 9 | 10 | static int s_libnet_mem_id = -1; 11 | 12 | static bool s_net_initialized = false; 13 | 14 | bool net_init(void) { 15 | int ret; 16 | 17 | if (s_net_initialized) { 18 | goto done; 19 | } 20 | 21 | ret = sceNetCtlInit(); 22 | if (ret) { 23 | EPRINTF("sceNetCtlInit failed: 0x%08X\n", ret); 24 | goto err; 25 | } 26 | 27 | ret = sceNetInit(); 28 | if (ret) { 29 | EPRINTF("sceNetInit failed: 0x%08X\n", sceNetErrnoLoc); 30 | goto err_netctl_terminate; 31 | } 32 | 33 | ret = sceNetPoolCreate("remote_pkg_inst_net_pool", NET_HEAP_SIZE, 0); 34 | if (ret < 0) { 35 | EPRINTF("sceNetPoolCreate failed: 0x%08X\n", sceNetErrnoLoc); 36 | goto err_net_terminate; 37 | } 38 | s_libnet_mem_id = ret; 39 | 40 | s_net_initialized = true; 41 | 42 | done: 43 | return true; 44 | 45 | err_pool_destroy: 46 | ret = sceNetPoolDestroy(s_libnet_mem_id); 47 | if (ret < 0) { 48 | EPRINTF("sceNetPoolDestroy failed: 0x%08X\n", sceNetErrnoLoc()); 49 | } 50 | s_libnet_mem_id = -1; 51 | 52 | err_net_terminate: 53 | ret = sceNetTerm(); 54 | if (ret) { 55 | EPRINTF("sceNetTerm failed: 0x%08X\n", sceNetErrnoLoc()); 56 | } 57 | 58 | err_netctl_terminate: 59 | sceNetCtlTerm(); 60 | 61 | err: 62 | return false; 63 | } 64 | 65 | bool net_is_initialized(void) { 66 | return s_net_initialized; 67 | } 68 | 69 | int net_get_mem_id(void) { 70 | if (!s_net_initialized) { 71 | return -1; 72 | } 73 | 74 | return s_libnet_mem_id; 75 | } 76 | 77 | void net_fini(void) { 78 | int ret; 79 | 80 | if (!s_net_initialized) { 81 | return; 82 | } 83 | 84 | ret = sceNetPoolDestroy(s_libnet_mem_id); 85 | if (ret < 0) { 86 | EPRINTF("sceNetPoolDestroy failed: 0x%08X\n", sceNetErrnoLoc()); 87 | } 88 | s_libnet_mem_id = -1; 89 | 90 | ret = sceNetTerm(); 91 | if (ret < 0) { 92 | EPRINTF("sceNetTerm failed: 0x%08X\n", sceNetErrnoLoc()); 93 | } 94 | 95 | sceNetCtlTerm(); 96 | 97 | s_net_initialized = false; 98 | } 99 | 100 | int net_get_ipv4(char* buf, size_t buf_size) { 101 | OrbisNetCtlInfo info; 102 | int ret; 103 | 104 | if (buf_size < sizeof(info.ip_address)) { 105 | ret = 0x80020016; 106 | goto err; 107 | } 108 | 109 | memset(&info, 0, sizeof(info)); 110 | ret = sceNetCtlGetInfo(ORBIS_NET_CTL_INFO_IP_ADDRESS, &info); 111 | if (ret) { 112 | EPRINTF("sceNetCtlGetInfo failed: 0x%08X\n", ret); 113 | goto err; 114 | } 115 | 116 | strncpy(buf, info.ip_address, buf_size); 117 | 118 | err: 119 | return ret; 120 | } 121 | 122 | int net_send_all(int sock_id, const void* data, size_t size, size_t* sent) { 123 | uint8_t* ptr = (uint8_t*)data; 124 | size_t total_sent = 0; 125 | size_t cur_size; 126 | int ret; 127 | 128 | while (total_sent < size) { 129 | cur_size = size - total_sent; 130 | 131 | ret = sceNetSend(sock_id, ptr, cur_size, 0); 132 | if (ret < 0) { 133 | EPRINTF("sceNetSend failed: 0x%08X\n", sceNetErrnoLoc()); 134 | goto err; 135 | } 136 | if (ret == 0) { 137 | break; 138 | } 139 | 140 | total_sent += ret; 141 | ptr += ret; 142 | } 143 | 144 | ret = 0; 145 | 146 | err: 147 | if (sent) 148 | *sent = total_sent; 149 | 150 | return ret; 151 | } 152 | 153 | int net_recv_all(int sock_id, void* data, size_t size, size_t* received) { 154 | uint8_t* ptr = (uint8_t*)data; 155 | size_t total_received = 0; 156 | size_t cur_size; 157 | int ret; 158 | 159 | while (total_received < size) { 160 | cur_size = size - total_received; 161 | 162 | ret = sceNetRecv(sock_id, ptr, cur_size, 0); 163 | if (ret < 0) { 164 | EPRINTF("sceNetRecv failed: 0x%08X\n", sceNetErrnoLoc()); 165 | goto err; 166 | } 167 | if (ret == 0) { 168 | break; 169 | } 170 | 171 | total_received += ret; 172 | ptr += ret; 173 | } 174 | 175 | ret = 0; 176 | 177 | err: 178 | if (received) 179 | *received = total_received; 180 | 181 | return ret; 182 | } 183 | -------------------------------------------------------------------------------- /RPI/build.bat: -------------------------------------------------------------------------------- 1 | SETLOCAL EnableDelayedExpansion 2 | 3 | Rem Package information 4 | set PKG_TITLE="Remote Package Installer (OOSDK Port)" 5 | set PKG_VERSION="01.00" 6 | set PKG_ASSETS="assets" 7 | set PKG_TITLE_ID="KPBR01111" 8 | set PKG_CONTENT_ID="IV0000-KPBR01111_00-AAAAAAAAAAAAAAAA" 9 | 10 | Rem Libraries to link in 11 | set libraries=-lc -lkernel -lc++ -lSceUserService -lSceSystemService -lSceNet -lSceHttp -lSceBgft -lSceAppInstUtil -lSceSsl -lSceSysmodule -lSceNetCtl -lSceJson -lSceNpUtility -lSceNpCommon 12 | 13 | Rem set extra_flags= 14 | 15 | Rem Read the script arguments into local vars 16 | set intdir=%1 17 | set targetname=%~2 18 | set outputPath=%3 19 | 20 | set outputElf=%intdir%\%targetname%.elf 21 | set outputOelf=%intdir%\%targetname%.oelf 22 | 23 | @mkdir %intdir% 24 | 25 | Rem Compile object files for all the source files 26 | for %%f in (*.c) do ( 27 | clang --target=x86_64-pc-freebsd12-elf -fPIC -funwind-tables -I"%OO_PS4_TOOLCHAIN%\\include" -I"%OO_PS4_TOOLCHAIN%\\include\\c++\\v1" %extra_flags% -c -o %intdir%\%%~nf.o %%~nf.c 28 | ) 29 | 30 | for %%f in (*.cpp) do ( 31 | clang++ --target=x86_64-pc-freebsd12-elf -fPIC -funwind-tables -I"%OO_PS4_TOOLCHAIN%\\include" -I"%OO_PS4_TOOLCHAIN%\\include\\c++\\v1" %extra_flags% -c -o %intdir%\%%~nf.o %%~nf.cpp 32 | ) 33 | 34 | Rem Get a list of object files for linking 35 | set obj_files= 36 | for %%f in (%1\\*.o) do set obj_files=!obj_files! .\%%f 37 | 38 | Rem Link the input ELF 39 | ld.lld -m elf_x86_64 -pie --script "%OO_PS4_TOOLCHAIN%\link.x" --eh-frame-hdr -o "%outputElf%" "-L%OO_PS4_TOOLCHAIN%\\lib" %libraries% --verbose "%OO_PS4_TOOLCHAIN%\lib\crt1.o" %obj_files% 40 | 41 | Rem Create the eboot 42 | %OO_PS4_TOOLCHAIN%\bin\windows\create-fself.exe -in "%outputElf%" --out "%outputOelf%" --eboot "eboot.bin" --paid 0x3800000000000011 43 | 44 | Rem Eboot cleanup 45 | copy "eboot.bin" %outputPath%\eboot.bin 46 | del "eboot.bin" 47 | 48 | Rem Create param.sfo 49 | cd .. 50 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_new sce_sys/param.sfo 51 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo APP_TYPE --type Integer --maxsize 4 --value 1 52 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo APP_VER --type Utf8 --maxsize 8 --value %PKG_VERSION% 53 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo ATTRIBUTE --type Integer --maxsize 4 --value 0 54 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo CATEGORY --type Utf8 --maxsize 4 --value "gd" 55 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo CONTENT_ID --type Utf8 --maxsize 48 --value %PKG_CONTENT_ID% 56 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo DOWNLOAD_DATA_SIZE --type Integer --maxsize 4 --value 0 57 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo SYSTEM_VER --type Integer --maxsize 4 --value 0 58 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo TITLE --type Utf8 --maxsize 128 --value %PKG_TITLE% 59 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo TITLE_ID --type Utf8 --maxsize 12 --value %PKG_TITLE_ID% 60 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe sfo_setentry sce_sys/param.sfo VERSION --type Utf8 --maxsize 8 --value %PKG_VERSION% 61 | 62 | Rem Get a list of assets for packaging 63 | set module_files= 64 | for %%f in (sce_module\\*) do set module_files=!module_files! sce_module/%%~nxf 65 | 66 | set asset_audio_files= 67 | for %%f in (assets\\audio\\*) do set asset_audio_files=!asset_audio_files! assets/audio/%%~nxf 68 | 69 | set asset_fonts_files= 70 | for %%f in (assets\\fonts\\*) do set asset_fonts_files=!asset_fonts_files! assets/fonts/%%~nxf 71 | 72 | set asset_images_files= 73 | for %%f in (assets\\images\\*) do set asset_images_files=!asset_images_files! assets/images/%%~nxf 74 | 75 | set asset_misc_files= 76 | for %%f in (assets\\misc\\*) do set asset_misc_files=!asset_misc_files! assets/misc/%%~nxf 77 | 78 | set asset_videos_files= 79 | for %%f in (assets\\videos\\*) do set asset_videos_files=!asset_videos_files! assets/videos/%%~nxf 80 | 81 | Rem Create gp4 82 | %OO_PS4_TOOLCHAIN%\bin\windows\create-gp4.exe -out pkg.gp4 --content-id=%PKG_CONTENT_ID% --files "eboot.bin sce_sys/about/right.sprx sce_sys/param.sfo sce_sys/icon0.png %module_files% %asset_audio_files% %asset_fonts_files% %asset_images_files% %asset_misc_files% %asset_videos_files%" 83 | 84 | Rem Create pkg 85 | %OO_PS4_TOOLCHAIN%\bin\windows\PkgTool.Core.exe pkg_build pkg.gp4 . 86 | -------------------------------------------------------------------------------- /RPI/RPI.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | 15.0 15 | {79dfd476-4249-4ba5-b6ef-1a96074bcd5d} 16 | Win32Proj 17 | RPI 18 | 19 | 20 | 21 | Makefile 22 | true 23 | v141 24 | 25 | 26 | Makefile 27 | false 28 | v141 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | eboot.bin 44 | _DEBUG;$(NMakePreprocessorDefinitions) 45 | call build.bat $(IntDir) "$(TargetName)" "$(SolutionDir)" 46 | del /s /q /f $(IntDir)\*.o 47 | del /s /q /f $(IntDir)\*.elf 48 | del /s /q /f $(IntDir)\*.oelf 49 | call build.bat $(IntDir) "$(TargetName)" "$(SolutionDir)" 50 | del /s /q /f $(IntDir)\*.o 51 | del /s /q /f $(IntDir)\*.elf 52 | del /s /q /f $(IntDir)\*.oelf 53 | $(SolutionDir) 54 | $(OO_PS4_TOOLCHAIN)\include;$(NMakeIncludeSearchPath) 55 | 56 | 57 | eboot.bin 58 | NDEBUG;$(NMakePreprocessorDefinitions) 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /RPI/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "module.h" 4 | #include "util.h" 5 | 6 | int32_t sceKernelLoadStartModuleFromSandbox(const char* name, size_t args, const void* argp, unsigned int flags, const OrbisKernelEventFlagOptParam* opts, int* res) { 7 | static const char* sandboxWord = NULL; 8 | char filePath[1024]; 9 | int32_t handle; 10 | 11 | if (!sandboxWord) { 12 | sandboxWord = sceKernelGetFsSandboxRandomWord(); 13 | if (!sandboxWord) { 14 | return 0x8002000E; 15 | } 16 | } 17 | 18 | snprintf(filePath, sizeof(filePath), "/%s/common/lib/%s", sandboxWord, name); 19 | 20 | handle = sceKernelLoadStartModule(filePath, args, argp, flags, opts, res); 21 | 22 | return handle; 23 | } 24 | 25 | 26 | // int sceKernelGetModuleInfo(int32_t handle, OrbisKernelModuleInfo* info) { 27 | // int ret; 28 | // 29 | // if (!info) { 30 | // ret = 0x8002000E; 31 | // goto err; 32 | // } 33 | // 34 | // memset(info, 0, sizeof(*info)); 35 | // { 36 | // info->size = sizeof(*info); 37 | // } 38 | // 39 | // ret = syscall(SYS_dynlib_get_info, handle, info); /* TODO: make proper error code */ 40 | // 41 | // err: 42 | // return ret; 43 | // } 44 | 45 | 46 | int sceKernelGetModuleInfoByName(const char* name, OrbisKernelModuleInfo* info) { 47 | OrbisKernelModuleInfo tmpInfo; 48 | int32_t handles[255]; 49 | size_t numModules; 50 | size_t i; 51 | int ret; 52 | 53 | if (!name) { 54 | ret = 0x8002000E; 55 | goto err; 56 | } 57 | if (!info) { 58 | ret = 0x8002000E; 59 | goto err; 60 | } 61 | 62 | memset(handles, 0, sizeof(handles)); 63 | 64 | ret = sceKernelGetModuleList(handles, ARRAY_SIZE(handles), &numModules); 65 | if (ret) { 66 | goto err; 67 | } 68 | 69 | for (i = 0; i < numModules; ++i) { 70 | ret = sceKernelGetModuleInfo(handles[i], &tmpInfo); 71 | if (ret) { 72 | goto err; 73 | } 74 | 75 | if (strcmp(tmpInfo.name, name) == 0) { 76 | memcpy(info, &tmpInfo, sizeof(tmpInfo)); 77 | ret = 0; 78 | goto err; 79 | } 80 | } 81 | 82 | ret = 0x80020002; 83 | 84 | err: 85 | return ret; 86 | } 87 | 88 | // int sceKernelGetModuleInfoEx(int32_t handle, OrbisKernelModuleInfo* info) { 89 | // int ret; 90 | // 91 | // if (!info) { 92 | // ret = 0x8002000E; 93 | // goto err; 94 | // } 95 | // 96 | // memset(info, 0, sizeof(*info)); 97 | // { 98 | // info->size = sizeof(*info); 99 | // } 100 | // 101 | // ret = syscall(SYS_dynlib_get_info_ex, handle, info); /* TODO: make proper error code */ 102 | // 103 | // err: 104 | // return ret; 105 | // } 106 | 107 | // int sceKernelGetModuleInfoExByName(const char* name, OrbisKernelModuleInfo* info) { 108 | // OrbisKernelModuleInfo tmpInfo; 109 | // int32_t handles[255]; 110 | // size_t numModules; 111 | // size_t i; 112 | // int ret; 113 | // 114 | // if (!name) { 115 | // ret = 0x8002000E; 116 | // goto err; 117 | // } 118 | // if (!info) { 119 | // ret = 0x8002000E; 120 | // goto err; 121 | // } 122 | // 123 | // memset(handles, 0, sizeof(handles)); 124 | // 125 | // ret = sceKernelGetModuleList(handles, ARRAY_SIZE(handles), &numModules); 126 | // if (ret) { 127 | // goto err; 128 | // } 129 | // 130 | // for (i = 0; i < numModules; ++i) { 131 | // ret = sceKernelGetModuleInfoEx(handles[i], &tmpInfo); 132 | // if (ret) { 133 | // goto err; 134 | // } 135 | // 136 | // if (strcmp(tmpInfo.name, name) == 0) { 137 | // memcpy(info, &tmpInfo, sizeof(tmpInfo)); 138 | // ret = 0; 139 | // goto err; 140 | // } 141 | // } 142 | // 143 | // ret = 0x80020002; 144 | // 145 | // err: 146 | // return ret; 147 | // } 148 | 149 | int sceKernelDlsymEx(int32_t handle, const char* symbol, const char* lib, unsigned int flags, void** addrp) { 150 | int ret; 151 | 152 | if (!symbol) { 153 | ret = 0x8002000E; 154 | goto err; 155 | } 156 | if (!lib) { 157 | ret = 0x8002000E; 158 | goto err; 159 | } 160 | if (!addrp) { 161 | ret = 0x8002000E; 162 | goto err; 163 | } 164 | 165 | ret = syscall(SYS_supercall, SUPERCALL_DLSYM, handle, symbol, lib, flags, addrp); /* TODO: make proper error code */ 166 | 167 | err: 168 | return ret; 169 | } 170 | 171 | bool get_module_base(const char* name, uint64_t* base, uint64_t* size) 172 | { 173 | OrbisKernelModuleInfo moduleInfo; 174 | int ret; 175 | 176 | // FIX 177 | 178 | ret = sceKernelGetModuleInfoByName(name, &moduleInfo); 179 | if (ret) { 180 | EPRINTF("sceKernelGetModuleInfoByName(%s) failed: 0x%08X\n", name, ret); 181 | goto err; 182 | } 183 | 184 | if (base) 185 | *base = (uint64_t)moduleInfo.segmentInfo[0].address; 186 | if (size) 187 | *size = moduleInfo.segmentInfo[0].size; 188 | 189 | return true; 190 | 191 | err: 192 | return false; 193 | } 194 | 195 | bool patch_module(const char* name, module_patch_cb_t* cb, void* arg) { 196 | uint64_t base, size; 197 | int ret; 198 | 199 | if (!get_module_base(name, &base, &size)) { 200 | goto err; 201 | } 202 | printf("%s: base:0x%" PRIX64 " size:0x%" PRIX64 "\n", name, base, size); 203 | 204 | ret = sceKernelMprotect((void*)base, size, ORBIS_KERNEL_PROT_CPU_READ | ORBIS_KERNEL_PROT_CPU_WRITE | ORBIS_KERNEL_PROT_CPU_EXEC); 205 | if (ret) { 206 | EPRINTF("sceKernelMprotect(%s) failed: 0x%08X\n", name, ret); 207 | goto err; 208 | } 209 | 210 | if (cb) { 211 | (*cb)(arg, (uint8_t*)base, size); 212 | } 213 | 214 | return true; 215 | 216 | err: 217 | return false; 218 | } 219 | -------------------------------------------------------------------------------- /RPI/utringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | /* a ring-buffer implementation using macros 25 | */ 26 | #ifndef UTRINGBUFFER_H 27 | #define UTRINGBUFFER_H 28 | 29 | #define UTRINGBUFFER_VERSION 2.0.2 30 | 31 | #include 32 | #include 33 | #include "utarray.h" // for "UT_icd" 34 | 35 | typedef struct { 36 | unsigned i; /* index of next available slot; wraps at n */ 37 | unsigned n; /* capacity */ 38 | unsigned char f; /* full */ 39 | UT_icd icd; /* initializer, copy and destructor functions */ 40 | char *d; /* n slots of size icd->sz */ 41 | } UT_ringbuffer; 42 | 43 | #define utringbuffer_init(a, _n, _icd) do { \ 44 | memset(a, 0, sizeof(UT_ringbuffer)); \ 45 | (a)->icd = *(_icd); \ 46 | (a)->n = (_n); \ 47 | if ((a)->n) { (a)->d = (char*)malloc((a)->n * (_icd)->sz); } \ 48 | } while(0) 49 | 50 | #define utringbuffer_clear(a) do { \ 51 | if ((a)->icd.dtor) { \ 52 | if ((a)->f) { \ 53 | unsigned _ut_i; \ 54 | for (_ut_i = 0; _ut_i < (a)->n; ++_ut_i) { \ 55 | (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ 56 | } \ 57 | } else { \ 58 | unsigned _ut_i; \ 59 | for (_ut_i = 0; _ut_i < (a)->i; ++_ut_i) { \ 60 | (a)->icd.dtor(utringbuffer_eltptr(a, _ut_i)); \ 61 | } \ 62 | } \ 63 | } \ 64 | (a)->i = 0; \ 65 | (a)->f = 0; \ 66 | } while(0) 67 | 68 | #define utringbuffer_done(a) do { \ 69 | utringbuffer_clear(a); \ 70 | free((a)->d); (a)->d = NULL; \ 71 | (a)->n = 0; \ 72 | } while(0) 73 | 74 | #define utringbuffer_new(a,n,_icd) do { \ 75 | a = (UT_ringbuffer*)malloc(sizeof(UT_ringbuffer)); \ 76 | utringbuffer_init(a, n, _icd); \ 77 | } while(0) 78 | 79 | #define utringbuffer_free(a) do { \ 80 | utringbuffer_done(a); \ 81 | free(a); \ 82 | } while(0) 83 | 84 | #define utringbuffer_push_back(a,p) do { \ 85 | if ((a)->icd.dtor && (a)->f) { (a)->icd.dtor(_utringbuffer_internalptr(a,(a)->i)); } \ 86 | if ((a)->icd.copy) { (a)->icd.copy( _utringbuffer_internalptr(a,(a)->i), p); } \ 87 | else { memcpy(_utringbuffer_internalptr(a,(a)->i), p, (a)->icd.sz); }; \ 88 | if (++(a)->i == (a)->n) { (a)->i = 0; (a)->f = 1; } \ 89 | } while(0) 90 | 91 | #define utringbuffer_len(a) ((a)->f ? (a)->n : (a)->i) 92 | #define utringbuffer_empty(a) ((a)->i == 0 && !(a)->f) 93 | #define utringbuffer_full(a) ((a)->f != 0) 94 | 95 | #define _utringbuffer_real_idx(a,j) ((a)->f ? ((j) + (a)->i) % (a)->n : (j)) 96 | #define _utringbuffer_internalptr(a,j) ((void*)((a)->d + ((a)->icd.sz * (j)))) 97 | #define utringbuffer_eltptr(a,j) ((0 <= (j) && (j) < utringbuffer_len(a)) ? _utringbuffer_internalptr(a,_utringbuffer_real_idx(a,j)) : NULL) 98 | 99 | #define _utringbuffer_fake_idx(a,j) ((a)->f ? ((j) + (a)->n - (a)->i) % (a)->n : (j)) 100 | #define _utringbuffer_internalidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) 101 | #define utringbuffer_eltidx(a,e) _utringbuffer_fake_idx(a, _utringbuffer_internalidx(a,e)) 102 | 103 | #define utringbuffer_front(a) utringbuffer_eltptr(a,0) 104 | #define utringbuffer_next(a,e) ((e)==NULL ? utringbuffer_front(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)+1)) 105 | #define utringbuffer_prev(a,e) ((e)==NULL ? utringbuffer_back(a) : utringbuffer_eltptr(a, utringbuffer_eltidx(a,e)-1)) 106 | #define utringbuffer_back(a) (utringbuffer_empty(a) ? NULL : utringbuffer_eltptr(a, utringbuffer_len(a) - 1)) 107 | 108 | #endif /* UTRINGBUFFER_H */ 109 | -------------------------------------------------------------------------------- /RPI/sfo.c: -------------------------------------------------------------------------------- 1 | #include "sfo.h" 2 | #include "util.h" 3 | 4 | #include 5 | #include 6 | 7 | #include "uthash.h" 8 | #include "utarray.h" 9 | #include "utlist.h" 10 | #define SFO_MAGIC "\0PSF" 11 | 12 | #define SIZEOF_SFO_HEADER 0x14 13 | 14 | TYPE_BEGIN(struct sfo_header, SIZEOF_SFO_HEADER); 15 | TYPE_FIELD(char magic[4], 0x00); 16 | TYPE_FIELD(uint32_t version, 0x04); 17 | TYPE_FIELD(uint32_t key_table_offset, 0x08); 18 | TYPE_FIELD(uint32_t value_table_offset, 0x0C); 19 | TYPE_FIELD(uint32_t entry_count, 0x10); 20 | TYPE_END(); 21 | TYPE_CHECK_SIZE(struct sfo_header, SIZEOF_SFO_HEADER); 22 | 23 | #define SIZEOF_SFO_TABLE_ENTRY 0x10 24 | 25 | TYPE_BEGIN(struct sfo_table_entry, SIZEOF_SFO_TABLE_ENTRY); 26 | TYPE_FIELD(uint16_t key_offset, 0x00); 27 | TYPE_FIELD(uint16_t format, 0x02); 28 | TYPE_FIELD(uint32_t size, 0x04); 29 | TYPE_FIELD(uint32_t max_size, 0x08); 30 | TYPE_FIELD(uint32_t value_offset, 0x0C); 31 | TYPE_END(); 32 | TYPE_CHECK_SIZE(struct sfo_table_entry, SIZEOF_SFO_TABLE_ENTRY); 33 | 34 | struct sfo* sfo_alloc(void) { 35 | struct sfo* sfo = NULL; 36 | 37 | sfo = (struct sfo*)malloc(sizeof(*sfo)); 38 | if (!sfo) 39 | goto error; 40 | memset(sfo, 0, sizeof(*sfo)); 41 | 42 | return sfo; 43 | 44 | error: 45 | if (sfo) 46 | free(sfo); 47 | 48 | return NULL; 49 | } 50 | 51 | void sfo_free(struct sfo* sfo) { 52 | struct sfo_entry* entry; 53 | struct sfo_entry* tmp; 54 | 55 | if (!sfo) 56 | return; 57 | 58 | DL_FOREACH_SAFE(sfo->entries, entry, tmp) 59 | { 60 | DL_DELETE(sfo->entries, entry); 61 | 62 | if (entry->key) 63 | free(entry->key); 64 | 65 | if (entry->value) 66 | free(entry->value); 67 | 68 | free(entry); 69 | } 70 | 71 | free(sfo); 72 | } 73 | 74 | bool sfo_load_from_file(struct sfo* sfo, const char* file_path) { 75 | struct stat stats; 76 | uint8_t* data = NULL; 77 | size_t data_size; 78 | ssize_t nread; 79 | int fd = -1; 80 | bool status = false; 81 | int ret; 82 | 83 | assert(sfo != NULL); 84 | assert(file_path != NULL); 85 | 86 | fd = open(file_path, O_RDONLY); 87 | if (fd < 0) { 88 | EPRINTF("Unable to open file.\n"); 89 | goto err; 90 | } 91 | 92 | ret = fstat(fd, &stats); 93 | if (ret < 0) { 94 | EPRINTF("Unable to get file information.\n"); 95 | goto err; 96 | } 97 | data_size = (size_t)stats.st_size; 98 | 99 | data = (uint8_t*)malloc(data_size); 100 | if (!data) { 101 | EPRINTF("Unable to allocate memory of 0x%" PRIuMAX " bytes.\n", (uintmax_t)data_size); 102 | goto err; 103 | } 104 | 105 | nread = read(fd, data, data_size); 106 | if (nread < 0) { 107 | EPRINTF("Unable to read file.\n"); 108 | goto err; 109 | } 110 | if ((size_t)nread != data_size) { 111 | EPRINTF("Insufficient data read.\n"); 112 | goto err; 113 | } 114 | 115 | if (!sfo_load_from_memory(sfo, data, data_size)) { 116 | EPRINTF("Unable to load system file object.\n"); 117 | goto err; 118 | } 119 | 120 | status = true; 121 | 122 | err: 123 | if (data) { 124 | free(data); 125 | } 126 | 127 | if (fd > 0) { 128 | close(fd); 129 | } 130 | 131 | return status; 132 | } 133 | 134 | bool sfo_load_from_memory(struct sfo* sfo, const void* data, size_t data_size) { 135 | struct sfo_header* hdr; 136 | struct sfo_table_entry* entry_table; 137 | struct sfo_table_entry* entry; 138 | struct sfo_entry* entries = NULL; 139 | struct sfo_entry* new_entry = NULL; 140 | const char* key_table; 141 | const uint8_t* value_table; 142 | size_t entry_count, i; 143 | bool status = false; 144 | 145 | assert(sfo != NULL); 146 | assert(data != NULL); 147 | 148 | if (data_size < sizeof(*hdr)) { 149 | EPRINTF("Insufficient data.\n"); 150 | goto err; 151 | } 152 | 153 | hdr = (struct sfo_header*)data; 154 | if (memcmp(hdr->magic, SFO_MAGIC, sizeof(hdr->magic)) != 0) { 155 | EPRINTF("Invalid system file object format.\n"); 156 | goto err; 157 | } 158 | 159 | entry_table = (struct sfo_table_entry*)(data + sizeof(*hdr)); 160 | entry_count = LE32(hdr->entry_count); 161 | if (data_size < sizeof(*hdr) + entry_count * sizeof(*entry_table)) { 162 | EPRINTF("Insufficient data.\n"); 163 | goto err; 164 | } 165 | 166 | key_table = (const char*)data + LE32(hdr->key_table_offset); 167 | value_table = (const uint8_t*)data + LE32(hdr->value_table_offset); 168 | 169 | for (i = 0; i < entry_count; ++i) { 170 | entry = entry_table + i; 171 | 172 | new_entry = (struct sfo_entry*)malloc(sizeof(*new_entry)); 173 | if (!new_entry) { 174 | EPRINTF("Unable to allocate memory for entry.\n"); 175 | goto err; 176 | } 177 | memset(new_entry, 0, sizeof(*new_entry)); 178 | 179 | new_entry->format = (enum sfo_value_format)LE16(entry->format); 180 | 181 | new_entry->size = LE32(entry->size); 182 | new_entry->area = LE32(entry->max_size); 183 | if (new_entry->area < new_entry->size) { 184 | EPRINTF("Unexpected entry sizes.\n"); 185 | goto err; 186 | } 187 | 188 | new_entry->key = strdup(key_table + LE16(entry->key_offset)); 189 | if (!new_entry->key) { 190 | EPRINTF("Unable to allocate memory for entry key.\n"); 191 | goto err; 192 | } 193 | 194 | new_entry->value = (uint8_t*)malloc(new_entry->area); 195 | if (!new_entry->value) { 196 | EPRINTF("Unable to allocate memory for entry value.\n"); 197 | goto err; 198 | } 199 | memset(new_entry->value, 0, new_entry->area); 200 | memcpy(new_entry->value, value_table + LE16(entry->value_offset), new_entry->size); 201 | 202 | DL_APPEND(entries, new_entry); 203 | } 204 | new_entry = NULL; 205 | 206 | sfo->entries = entries; 207 | 208 | status = true; 209 | 210 | err: 211 | if (new_entry) { 212 | if (new_entry->key) { 213 | free(new_entry->key); 214 | } 215 | 216 | if (new_entry->value) { 217 | free(new_entry->value); 218 | } 219 | 220 | free(new_entry); 221 | } 222 | 223 | return status; 224 | } 225 | 226 | struct sfo_entry* sfo_find_entry(struct sfo* sfo, const char* key) { 227 | struct sfo_entry* entry; 228 | 229 | assert(sfo != NULL); 230 | assert(key != NULL); 231 | 232 | DL_FOREACH(sfo->entries, entry) 233 | { 234 | if (strcmp(entry->key, key) == 0) { 235 | return entry; 236 | } 237 | } 238 | 239 | return NULL; 240 | } 241 | -------------------------------------------------------------------------------- /RPI/tiny-json.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | 5 | 6 | Licensed under the MIT License . 7 | SPDX-License-Identifier: MIT 8 | Copyright (c) 2016-2018 Rafa Garcia . 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | #ifndef _TINY_JSON_H_ 31 | #define _TINY_JSON_H_ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | /** @defgroup tinyJson Tiny JSON parser. 42 | * @{ */ 43 | 44 | /** Enumeration of codes of supported JSON properties types. */ 45 | typedef enum { 46 | JSON_OBJ, JSON_ARRAY, JSON_TEXT, JSON_BOOLEAN, 47 | JSON_INTEGER, JSON_REAL, JSON_NULL 48 | } jsonType_t; 49 | 50 | /** Structure to handle JSON properties. */ 51 | typedef struct json_s { 52 | struct json_s* sibling; 53 | char const* name; 54 | union { 55 | char const* value; 56 | struct { 57 | struct json_s* child; 58 | struct json_s* last_child; 59 | } c; 60 | } u; 61 | jsonType_t type; 62 | } json_t; 63 | 64 | /** Parse a string to get a json. 65 | * @param str String pointer with a JSON object. It will be modified. 66 | * @param mem Array of json properties to allocate. 67 | * @param qty Number of elements of mem. 68 | * @retval Null pointer if any was wrong in the parse process. 69 | * @retval If the parser process was successfully a valid handler of a json. 70 | * This property is always unnamed and its type is JSON_OBJ. */ 71 | json_t const* json_create( char* str, json_t mem[], unsigned int qty ); 72 | 73 | /** Get the name of a json property. 74 | * @param json A valid handler of a json property. 75 | * @retval Pointer to null-terminated if property has name. 76 | * @retval Null pointer if the property is unnamed. */ 77 | static inline char const* json_getName( json_t const* json ) { 78 | return json->name; 79 | } 80 | 81 | /** Get the value of a json property. 82 | * The type of property cannot be JSON_OBJ or JSON_ARRAY. 83 | * @param json A valid handler of a json property. 84 | * @return Pointer to null-terminated string with the value. */ 85 | static inline char const* json_getValue( json_t const* property ) { 86 | return property->u.value; 87 | } 88 | 89 | /** Get the type of a json property. 90 | * @param json A valid handler of a json property. 91 | * @return The code of type.*/ 92 | static inline jsonType_t json_getType( json_t const* json ) { 93 | return json->type; 94 | } 95 | 96 | /** Get the next sibling of a JSON property that is within a JSON object or array. 97 | * @param json A valid handler of a json property. 98 | * @retval The handler of the next sibling if found. 99 | * @retval Null pointer if the json property is the last one. */ 100 | static inline json_t const* json_getSibling( json_t const* json ) { 101 | return json->sibling; 102 | } 103 | 104 | /** Search a property by its name in a JSON object. 105 | * @param obj A valid handler of a json object. Its type must be JSON_OBJ. 106 | * @param property The name of property to get. 107 | * @retval The handler of the json property if found. 108 | * @retval Null pointer if not found. */ 109 | json_t const* json_getProperty( json_t const* obj, char const* property ); 110 | 111 | 112 | /** Search a property by its name in a JSON object and return its value. 113 | * @param obj A valid handler of a json object. Its type must be JSON_OBJ. 114 | * @param property The name of property to get. 115 | * @retval If found a pointer to null-terminated string with the value. 116 | * @retval Null pointer if not found or it is an array or an object. */ 117 | char const* json_getPropertyValue( json_t const* obj, char const* property ); 118 | 119 | /** Get the first property of a JSON object or array. 120 | * @param json A valid handler of a json property. 121 | * Its type must be JSON_OBJ or JSON_ARRAY. 122 | * @retval The handler of the first property if there is. 123 | * @retval Null pointer if the json object has not properties. */ 124 | static inline json_t const* json_getChild( json_t const* json ) { 125 | return json->u.c.child; 126 | } 127 | 128 | /** Get the value of a json boolean property. 129 | * @param property A valid handler of a json object. Its type must be JSON_BOOLEAN. 130 | * @return The value stdbool. */ 131 | static inline bool json_getBoolean( json_t const* property ) { 132 | return *property->u.value == 't'; 133 | } 134 | 135 | /** Get the value of a json integer property. 136 | * @param property A valid handler of a json object. Its type must be JSON_INTEGER. 137 | * @return The value stdint. */ 138 | static inline int64_t json_getInteger( json_t const* property ) { 139 | return (int64_t)atoll( property->u.value ); 140 | } 141 | 142 | /** Get the value of a json real property. 143 | * @param property A valid handler of a json object. Its type must be JSON_REAL. 144 | * @return The value. */ 145 | static inline double json_getReal( json_t const* property ) { 146 | return atof( property->u.value ); 147 | } 148 | 149 | /** @ } */ 150 | 151 | #ifdef __cplusplus 152 | } 153 | #endif 154 | 155 | #endif /* _TINY_JSON_H_ */ 156 | -------------------------------------------------------------------------------- /RPI/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | bool get_language_id(int* lang_id) { 8 | int value; 9 | int ret; 10 | 11 | ret = sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &value); 12 | if (ret) { 13 | EPRINTF("sceSystemServiceParamGetInt failed: 0x%08x\n", ret); 14 | goto err; 15 | } 16 | 17 | if (lang_id) { 18 | *lang_id = value; 19 | } 20 | 21 | return true; 22 | 23 | err: 24 | return false; 25 | } 26 | 27 | int bytes_to_hex(char* buf, size_t buf_size, const void* data, size_t data_size) { 28 | static const char* digits = "0123456789ABCDEF"; 29 | const uint8_t* in = (const uint8_t*)data; 30 | char* out = buf; 31 | uint8_t c; 32 | size_t i; 33 | int ret; 34 | 35 | if (!buf || !data) { 36 | ret = ORBIS_KERNEL_ERROR_EINVAL; 37 | goto err; 38 | } 39 | if (!buf_size || buf_size < (data_size * 2 + 1)) { 40 | ret = ORBIS_KERNEL_ERROR_ENOSPC; 41 | goto err; 42 | } 43 | if (!data_size) { 44 | *out = '\0'; 45 | goto done; 46 | } 47 | 48 | for (i = 0; i < data_size; ++i) { 49 | c = in[i]; 50 | *out++ = digits[c >> 4]; 51 | *out++ = digits[c & 0xF]; 52 | } 53 | *out++ = '\0'; 54 | 55 | done: 56 | ret = 0; 57 | 58 | err: 59 | return ret; 60 | } 61 | 62 | bool read_file(const char* path, void** data, uint64_t* size, uint64_t max_size, uint64_t* nread) { 63 | int fd = -1; 64 | struct stat info; 65 | uint64_t file_size, user_size; 66 | uint64_t size_left, total = 0; 67 | uint8_t* buf = NULL; 68 | uint8_t* p; 69 | ssize_t n; 70 | bool need_alloc = false; 71 | bool status = false; 72 | int ret; 73 | 74 | assert(path != NULL); 75 | assert(data != NULL); 76 | assert(size != NULL); 77 | 78 | ret = fd = open(path, O_RDONLY); 79 | if (ret < 0) 80 | goto err; 81 | 82 | ret = fstat(fd, &info); 83 | if (ret < 0) 84 | goto err; 85 | file_size = (uint64_t)info.st_size; 86 | 87 | /* User may provide his own buffer. */ 88 | buf = (uint8_t*)*data; 89 | 90 | /* If user size is not specified or it's bigger than real size, then use real size 91 | and update user size with its value. */ 92 | user_size = *size; 93 | if (user_size == (uint64_t)-1) 94 | user_size = file_size; 95 | else if (user_size > file_size) 96 | user_size = file_size; 97 | 98 | /* If max size is set then do additional size check. */ 99 | if (max_size > 0 && max_size != (uint64_t)-1) 100 | user_size = MIN(user_size, max_size); 101 | else if (buf) { 102 | /* If buffer pointer is specified, then we should have max size parameter set. */ 103 | if (buf) 104 | goto err; 105 | } 106 | 107 | /* If buffer pointer is null then we need to allocate memory by ourselves. */ 108 | if (!buf) { 109 | need_alloc = true; 110 | buf = (uint8_t*)malloc(user_size); 111 | if (!buf) 112 | goto err; 113 | } 114 | 115 | p = buf; 116 | for (size_left = user_size; size_left > 0;) { 117 | n = read(fd, p, (size_t)size_left); 118 | if (n <= 0) 119 | break; /* status is okay but we need to check num read parameter */ 120 | size_left -= n; 121 | total += n; 122 | p += n; 123 | } 124 | 125 | /* We have read something, so it's okay to fill some information. */ 126 | *data = buf; 127 | *size = user_size; 128 | 129 | /* Set temporary buffer pointer to null to not free it later (user must do it by himself). */ 130 | buf = NULL; 131 | 132 | status = true; 133 | 134 | err: 135 | if (fd > 0) 136 | close(fd); 137 | 138 | if (nread) 139 | *nread = total; 140 | 141 | if (!status) { 142 | /* If we have failed at beginning then we have no information. */ 143 | *data = NULL; 144 | *size = 0; 145 | 146 | /* Free memory if we have allocated it before. */ 147 | if (need_alloc) { 148 | if (buf) 149 | free(buf); 150 | } 151 | } 152 | 153 | return status; 154 | } 155 | 156 | bool write_file(const char* path, const void* data, uint64_t size, uint64_t* nwritten, int mode, unsigned int flags) { 157 | int fd = -1; 158 | uint64_t size_left, total = 0; 159 | const uint8_t* buf; 160 | bool status = false; 161 | ssize_t n; 162 | 163 | assert(path != NULL); 164 | assert(size == 0 || data != NULL); 165 | 166 | fd = open(path, O_CREAT | O_WRONLY | flags, mode); 167 | if (fd < 0) 168 | goto err; 169 | 170 | if (size > 0) { 171 | buf = (const uint8_t*)data; 172 | for (size_left = size; size_left > 0;) { 173 | n = write(fd, buf, (size_t)size_left); 174 | if (n <= 0) 175 | break; /* status is okay but we need to check num written parameter */ 176 | size_left -= n; 177 | total += n; 178 | buf += n; 179 | } 180 | } 181 | 182 | status = true; 183 | 184 | err: 185 | if (fd > 0) 186 | close(fd); 187 | 188 | if (nwritten) 189 | *nwritten = total; 190 | 191 | return status; 192 | } 193 | 194 | bool write_file_trunc(const char* path, const void* data, uint64_t size, uint64_t* nwritten, int mode) { 195 | return write_file(path, data, size, nwritten, mode, O_TRUNC); 196 | } 197 | 198 | bool is_file_exists(const char* path) { 199 | OrbisKernelStat stat_buf; 200 | int ret; 201 | 202 | assert(path != NULL); 203 | 204 | ret = sceKernelStat(path, &stat_buf); 205 | if (ret) { 206 | return false; 207 | } 208 | 209 | return S_ISREG(stat_buf.st_mode); 210 | } 211 | 212 | void hexdump(const void* data, size_t size) { 213 | const uint8_t* p = (const uint8_t*)data; 214 | const size_t n = 16; 215 | size_t i, j, k; 216 | for (i = 0; i < size; i += n) { 217 | k = (i + n) <= size ? n : (size - i); 218 | printf("%8p:", (uint8_t*)data + i); 219 | for (j = 0; j < k; ++j) { 220 | printf(" %02x", p[i + j]); 221 | } 222 | for (j = k; j < n; ++j) { 223 | printf(" "); 224 | } 225 | printf(" "); 226 | for (j = 0; j < k; ++j) { 227 | printf("%c", isprint(p[i + j]) ? p[i + j] : '.'); 228 | } 229 | for (j = k; j < n; ++j) { 230 | printf(" "); 231 | } 232 | printf("\n"); 233 | } 234 | } 235 | 236 | bool starts_with(const char* haystack, const char* needle) { 237 | int i; 238 | 239 | assert(haystack != NULL); 240 | assert(needle != NULL); 241 | 242 | for (i = 0; haystack[i] != '\0'; ++i) { 243 | if (haystack[i] != needle[i]) { 244 | break; 245 | } 246 | } 247 | 248 | return (needle[i] == '\0'); 249 | } 250 | 251 | bool starts_with_nocase(const char* haystack, const char* needle) { 252 | int i; 253 | 254 | assert(haystack != NULL); 255 | assert(needle != NULL); 256 | 257 | for (i = 0; haystack[i] != '\0'; ++i) { 258 | if (tolower(haystack[i]) != tolower(needle[i])) { 259 | break; 260 | } 261 | } 262 | 263 | return (needle[i] == '\0'); 264 | } 265 | 266 | bool ends_with(const char* haystack, const char* needle) { 267 | ptrdiff_t diff; 268 | int i; 269 | 270 | assert(haystack != NULL); 271 | assert(needle != NULL); 272 | 273 | diff = strlen(haystack) - strlen(needle); 274 | if (diff < 0) { 275 | return false; 276 | } 277 | 278 | for (i = 0; needle[i] != '\0'; ++i) { 279 | if (needle[i] != haystack[i + diff]) { 280 | return false; 281 | } 282 | } 283 | 284 | return true; 285 | } 286 | 287 | bool ends_with_nocase(const char* haystack, const char* needle) { 288 | ptrdiff_t diff; 289 | int i; 290 | 291 | assert(haystack != NULL); 292 | assert(needle != NULL); 293 | 294 | diff = strlen(haystack) - strlen(needle); 295 | if (diff < 0) { 296 | return false; 297 | } 298 | 299 | for (i = 0; needle[i] != '\0'; ++i) { 300 | if (tolower(needle[i]) != tolower(haystack[i + diff])) { 301 | return false; 302 | } 303 | } 304 | 305 | return true; 306 | } 307 | 308 | static char* rtrim_ex(char* s, int (*check)(int ch)) { 309 | char* end; 310 | size_t len; 311 | 312 | assert(s != NULL); 313 | assert(check != NULL); 314 | 315 | if (*s == '\0') 316 | return s; 317 | 318 | len = strlen(s); 319 | for (end = &s[len - 1]; end >= s && check(*end); --end); 320 | end[1] = '\0'; 321 | 322 | return end >= s ? end : NULL; 323 | } 324 | 325 | static int check_space(int ch) { 326 | return isspace(ch); 327 | } 328 | 329 | char* rtrim(char* s) { 330 | return rtrim_ex(s, &check_space); 331 | } 332 | 333 | struct timespec* timespec_now(struct timespec* tp) { 334 | struct timeval tv; 335 | int ret; 336 | 337 | ret = sceKernelGettimeofday(&tv); 338 | if (ret) { 339 | EPRINTF("sceKernelGettimeofday failed: 0x%08X\n", ret); 340 | return NULL; 341 | } 342 | 343 | tp->tv_sec = tv.tv_sec; 344 | tp->tv_nsec = usec_to_nsec(tv.tv_usec); 345 | 346 | return tp; 347 | } 348 | 349 | struct timespec* timespec_sub(struct timespec* tp, const struct timespec* a, const struct timespec* b) { 350 | if ((a->tv_sec < b->tv_sec) || ((a->tv_sec == b->tv_sec) && (a->tv_nsec <= b->tv_nsec))) { /* a <= b? */ 351 | tp->tv_sec = tp->tv_nsec = 0; 352 | } else { /* a > b? */ 353 | tp->tv_sec = a->tv_sec - b->tv_sec; 354 | 355 | if (a->tv_nsec < b->tv_nsec) { 356 | tp->tv_nsec = a->tv_nsec + NSEC_PER_SEC - b->tv_nsec; 357 | --tp->tv_sec; /* borrow a second */ 358 | } else { 359 | tp->tv_nsec = a->tv_nsec - b->tv_nsec; 360 | } 361 | } 362 | 363 | return tp; 364 | } 365 | 366 | int timespec_compare(const struct timespec* a, const struct timespec* b) { 367 | if (a->tv_sec < b->tv_sec) 368 | return -1; 369 | if (a->tv_sec > b->tv_sec) 370 | return 1; 371 | return a->tv_nsec - b->tv_nsec; 372 | } 373 | 374 | -------------------------------------------------------------------------------- /RPI/main.c: -------------------------------------------------------------------------------- 1 | #include "installer.h" 2 | #include "net.h" 3 | #include "http.h" 4 | #include "server.h" 5 | #include "util.h" 6 | #include "KPutil.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define SERVER_PORT (12800) 15 | 16 | int sceUserMainThreadPriority = 700; 17 | 18 | size_t sceUserMainThreadStackSize = 512 * 1024; 19 | size_t sceLibcHeapSize = 256 * 1024 * 1024; 20 | 21 | static bool s_modules_loaded = false; 22 | 23 | static bool load_modules(void); 24 | static void unload_modules(void); 25 | 26 | static void set_privileges(void); 27 | static void unset_privileges(void); 28 | 29 | static void cleanup(void); 30 | 31 | typedef struct { 32 | uid_t uid; 33 | uid_t ruid; 34 | uid_t svuid; 35 | gid_t rgid; 36 | gid_t svgid; 37 | uintptr_t prison; 38 | uintptr_t cdir; 39 | uintptr_t rdir; 40 | uintptr_t jdir; 41 | uint64_t sceProcType; 42 | uint64_t sonyCred; 43 | uint64_t sceProcCap; 44 | } jbc_cred; 45 | 46 | typedef enum { 47 | USERSPACE, 48 | KERNEL_HEAP, 49 | KERNEL_TEXT 50 | } KmemKind; 51 | 52 | enum { 53 | CWD_KEEP, 54 | CWD_ROOT, 55 | CWD_RESET, 56 | }; 57 | 58 | jbc_cred jailbreak; 59 | jbc_cred og; 60 | 61 | int(*jbc_get_cred)(jbc_cred *ans); 62 | int(*jbc_jailbreak_cred)(jbc_cred *ans); 63 | int(*jbc_set_cred)(const jbc_cred *ans); 64 | 65 | 66 | void Jailbreak() 67 | { 68 | // unjail 69 | int urmom = 0; 70 | int32_t handlejbc = sceKernelLoadStartModule("/app0/sce_module/libjbc.prx", NULL, NULL, NULL, NULL, NULL); 71 | KernelPrintOut("libjbc handle is 0x%lx\n", handlejbc); 72 | 73 | if (handlejbc > 0) 74 | { 75 | urmom = sceKernelDlsym(handlejbc, "jbc_get_cred", (void**)&jbc_get_cred); 76 | KernelPrintOut("sceKernelDlsym returned 0x%lx\n", urmom); 77 | 78 | 79 | urmom = sceKernelDlsym(handlejbc, "jbc_jailbreak_cred", (void**)&jbc_jailbreak_cred); 80 | KernelPrintOut("sceKernelDlsym returned 0x%lx\n", urmom); 81 | 82 | urmom = sceKernelDlsym(handlejbc, "jbc_set_cred", (void**)&jbc_set_cred); 83 | KernelPrintOut("sceKernelDlsym returned 0x%lx\n", urmom); 84 | } 85 | else 86 | { 87 | KernelPrintOut("Failed to load libjbc"); 88 | sceSystemServiceLoadExec((char*)"exit", NULL); 89 | } 90 | 91 | jbc_get_cred(&og); 92 | jbc_jailbreak_cred(&jailbreak); 93 | jbc_set_cred(&jailbreak); 94 | } 95 | 96 | int main(int argc, const char* const argv[]) 97 | { 98 | char* work_dir; 99 | char ip_address[16]; 100 | int ret; 101 | 102 | atexit(&cleanup); 103 | 104 | Jailbreak(); 105 | 106 | if (!load_modules()) { 107 | EPRINTF("Unable to load modules.\n"); 108 | goto err; 109 | } 110 | 111 | 112 | //printf("Initializing user service...\n"); 113 | ret = sceUserServiceInitialize(NULL); 114 | if (ret) { 115 | EPRINTF("User service initialization failed.\n"); 116 | goto err; 117 | } 118 | 119 | work_dir = "/data"; 120 | //printf("Working directory: %s\n", work_dir); 121 | 122 | //printf("Initializing AppInstUtil...\n"); 123 | if (!app_inst_util_init()) { 124 | EPRINTF("AppInstUtil initialization failed.\n"); 125 | goto err_user_service_terminate; 126 | } 127 | 128 | //printf("Initializing BGFT...\n"); 129 | if (!bgft_init()) { 130 | EPRINTF("BGFT initialization failed.\n"); 131 | goto err_appinstutil_finalize; 132 | } 133 | 134 | //printf("Initializing net...\n"); 135 | if (!net_init()) { 136 | EPRINTF("Net initialization failed.\n"); 137 | goto err_bgft_finalize; 138 | } 139 | 140 | ret = net_get_ipv4(ip_address, sizeof(ip_address)); 141 | if (ret) { 142 | EPRINTF("Unable to get IP address: 0x%08X\n", ret); 143 | goto err_net_finalize; 144 | } 145 | 146 | //printf("Initializing HTTP/SSL...\n"); 147 | if (!http_init()) { 148 | EPRINTF("HTTP/SSL initialization failed.\n"); 149 | goto err_net_finalize; 150 | } 151 | 152 | //printf("Starting server...\n"); 153 | if (!server_start(ip_address, SERVER_PORT, work_dir)) { 154 | EPRINTF("Server start failed.\n"); 155 | goto err_http_finalize; 156 | } 157 | 158 | printf("Listening for incoming connections on %s:%d...\n", ip_address, SERVER_PORT); 159 | if (!server_listen()) { 160 | goto err_server_stop; 161 | } 162 | 163 | err_server_stop: 164 | //printf("Stopping server...\n"); 165 | server_stop(); 166 | 167 | err_http_finalize: 168 | //printf("Finalizing HTTP/SSL...\n"); 169 | http_fini(); 170 | 171 | err_net_finalize: 172 | //printf("Finalizing net...\n"); 173 | net_fini(); 174 | 175 | err_bgft_finalize: 176 | //printf("Finalizing BGFT...\n"); 177 | bgft_fini(); 178 | 179 | err_appinstutil_finalize: 180 | //printf("Finalizing AppInstUtil...\n"); 181 | app_inst_util_fini(); 182 | 183 | err_user_service_terminate: 184 | //printf("Terminating user service...\n"); 185 | ret = sceUserServiceTerminate(); 186 | if (ret) { 187 | EPRINTF("sceUserServiceTerminate failed: 0x%08X\n", ret); 188 | } 189 | 190 | err:; 191 | 192 | done: 193 | exit(0); 194 | return 0; 195 | } 196 | 197 | static bool load_modules(void) { 198 | int ret; 199 | 200 | if (s_modules_loaded) { 201 | goto done; 202 | } 203 | 204 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSCORE); 205 | if (ret) { 206 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYS_CORE), ret); 207 | goto err; 208 | } 209 | 210 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE); 211 | if (ret) { 212 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE), ret); 213 | goto err_unload_sys_core; 214 | } 215 | 216 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE); 217 | if (ret) { 218 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE), ret); 219 | goto err_unload_system_service; 220 | } 221 | 222 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NETCTL); 223 | if (ret) { 224 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NETCTL), ret); 225 | goto err_unload_user_service; 226 | } 227 | 228 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); 229 | if (ret) { 230 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NET), ret); 231 | goto err_unload_netctl; 232 | } 233 | 234 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_HTTP); 235 | if (ret) { 236 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_HTTP), ret); 237 | goto err_unload_net; 238 | } 239 | 240 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SSL); 241 | if (ret) { 242 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SSL), ret); 243 | goto err_unload_http; 244 | } 245 | 246 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_APP_INST_UTIL); 247 | if (ret) { 248 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_APPINSTUTIL), ret); 249 | goto err_unload_ssl; 250 | } 251 | 252 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_BGFT); 253 | if (ret) { 254 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_BGFT), ret); 255 | goto err_unload_appinstutil; 256 | } 257 | 258 | ret = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NP_COMMON); 259 | if (ret) { 260 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NP_COMMON), ret); 261 | goto err_unload_bgft; 262 | } 263 | 264 | s_modules_loaded = true; 265 | 266 | done: 267 | return true; 268 | 269 | err_unload_np_common: 270 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NP_COMMON); 271 | if (ret) { 272 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NP_COMMON), ret); 273 | } 274 | 275 | err_unload_bgft: 276 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_BGFT); 277 | if (ret) { 278 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_BGFT), ret); 279 | } 280 | 281 | err_unload_appinstutil: 282 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_APP_INST_UTIL); 283 | if (ret) { 284 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_APPINSTUTIL), ret); 285 | } 286 | 287 | err_unload_ssl: 288 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SSL); 289 | if (ret) { 290 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SSL), ret); 291 | } 292 | 293 | err_unload_http: 294 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_HTTP); 295 | if (ret) { 296 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_HTTP), ret); 297 | } 298 | 299 | err_unload_net: 300 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); 301 | if (ret) { 302 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NET), ret); 303 | } 304 | 305 | err_unload_netctl: 306 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NETCTL); 307 | if (ret) { 308 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NETCTL), ret); 309 | } 310 | 311 | err_unload_user_service: 312 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE); 313 | if (ret) { 314 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE), ret); 315 | } 316 | 317 | err_unload_system_service: 318 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE); 319 | if (ret) { 320 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE), ret); 321 | } 322 | 323 | err_unload_sys_core: 324 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSCORE); 325 | if (ret) { 326 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYS_CORE), ret); 327 | } 328 | 329 | err: 330 | return false; 331 | } 332 | 333 | static void unload_modules(void) { 334 | int ret; 335 | 336 | if (!s_modules_loaded) { 337 | return; 338 | } 339 | 340 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NP_COMMON); 341 | if (ret) { 342 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NP_COMMON), ret); 343 | } 344 | 345 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_BGFT); 346 | if (ret) { 347 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_BGFT), ret); 348 | } 349 | 350 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_APP_INST_UTIL); 351 | if (ret) { 352 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_APPINSTUTIL), ret); 353 | } 354 | 355 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SSL); 356 | if (ret) { 357 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SSL), ret); 358 | } 359 | 360 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_HTTP); 361 | if (ret) { 362 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_HTTP), ret); 363 | } 364 | 365 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); 366 | if (ret) { 367 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NET), ret); 368 | } 369 | 370 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NETCTL); 371 | if (ret) { 372 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_NETCTL), ret); 373 | } 374 | 375 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE); 376 | if (ret) { 377 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE), ret); 378 | } 379 | 380 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE); 381 | if (ret) { 382 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYSTEM_SERVICE), ret); 383 | } 384 | 385 | ret = sceSysmoduleUnloadModuleInternal(ORBIS_SYSMODULE_INTERNAL_SYSCORE); 386 | if (ret) { 387 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", STRINGIFY_DEEP(ORBIS_SYSMODULE_INTERNAL_SYS_CORE), ret); 388 | } 389 | 390 | s_modules_loaded = false; 391 | } 392 | 393 | static void cleanup(void) 394 | { 395 | unload_modules(); 396 | } 397 | 398 | void catchReturnFromMain(int exit_code) {} 399 | -------------------------------------------------------------------------------- /RPI/http.c: -------------------------------------------------------------------------------- 1 | #include "http.h" 2 | #include "net.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #define HTTP_HEAP_SIZE (1024 * 1024) 11 | #define SSL_HEAP_SIZE (128 * 1024) 12 | 13 | #define DOWNLOAD_CHUNK_SIZE (16384) 14 | 15 | #define USER_AGENT "Download/1.00" 16 | 17 | struct download_file_cb_args { 18 | uint8_t* data; 19 | uint64_t data_size; 20 | uint64_t actual_size; 21 | uint64_t content_length; 22 | uint8_t* chunk; 23 | size_t chunk_size; 24 | bool is_partial; 25 | int status_code; 26 | }; 27 | 28 | static int s_libssl_ctx_id = -1; 29 | static int s_libhttp_ctx_id = -1; 30 | 31 | static bool s_http_initialized = false; 32 | 33 | typedef int request_cb_t(void* arg, int req_id, int status_code, uint64_t content_length, int content_length_type); 34 | 35 | static int download_file_cb(void* arg, int req_id, int status_code, uint64_t content_length, int content_length_type); 36 | 37 | static int do_request(const char* url, int method, const void* data, size_t data_size, const char** headers, size_t header_count, request_cb_t* cb, void* arg); 38 | 39 | static inline bool is_good_status(int status_code); 40 | 41 | bool http_init(void) 42 | { 43 | int ret; 44 | 45 | if (s_http_initialized) { 46 | goto done; 47 | } 48 | 49 | if (!net_is_initialized()) { 50 | goto err; 51 | } 52 | 53 | ret = sceSslInit(SSL_HEAP_SIZE); 54 | if (ret < 0) { 55 | EPRINTF("sceSslInit failed: 0x%08X\n", ret); 56 | goto err; 57 | } 58 | s_libssl_ctx_id = ret; 59 | 60 | ret = sceHttpInit(net_get_mem_id(), s_libssl_ctx_id, HTTP_HEAP_SIZE); 61 | if (ret < 0) { 62 | EPRINTF("sceHttpInit failed: 0x%08X\n", ret); 63 | goto err; 64 | } 65 | s_libhttp_ctx_id = ret; 66 | 67 | s_http_initialized = true; 68 | 69 | done: 70 | return true; 71 | 72 | err_http_terminate: 73 | ret = sceHttpTerm(s_libhttp_ctx_id); 74 | if (ret) { 75 | EPRINTF("sceHttpTerm failed: 0x%08X\n", ret); 76 | } 77 | s_libhttp_ctx_id = -1; 78 | 79 | err_ssl_terminate: 80 | ret = sceSslTerm(s_libssl_ctx_id); 81 | if (ret) { 82 | EPRINTF("sceSslTerm failed: 0x%08X\n", ret); 83 | } 84 | s_libssl_ctx_id = -1; 85 | 86 | err: 87 | return false; 88 | } 89 | 90 | void http_fini(void) { 91 | int ret; 92 | 93 | if (!s_http_initialized) { 94 | return; 95 | } 96 | 97 | ret = sceHttpTerm(s_libhttp_ctx_id); 98 | if (ret) { 99 | EPRINTF("sceHttpTerm failed: 0x%08X\n", ret); 100 | } 101 | s_libhttp_ctx_id = -1; 102 | 103 | ret = sceSslTerm(s_libssl_ctx_id); 104 | if (ret) { 105 | EPRINTF("sceSslTerm failed: 0x%08X\n", ret); 106 | } 107 | s_libssl_ctx_id = -1; 108 | 109 | s_http_initialized = false; 110 | } 111 | 112 | bool http_get_file_size(const char* url, uint64_t* total_size) { 113 | struct download_file_cb_args args; 114 | bool status = false; 115 | int ret; 116 | 117 | if (!s_http_initialized) { 118 | goto err; 119 | } 120 | if (!url) { 121 | goto err; 122 | } 123 | 124 | memset(&args, 0, sizeof(args)); 125 | { 126 | args.data_size = 0; 127 | } 128 | 129 | 130 | ret = do_request(url, ORBIS_HTTP_METHOD_GET, NULL, 0, NULL, 0, &download_file_cb, &args); 131 | if (ret) { 132 | goto err; 133 | } 134 | if (!is_good_status(args.status_code)) { 135 | goto err; 136 | } 137 | 138 | if (total_size) { 139 | *total_size = args.content_length; 140 | } 141 | 142 | status = true; 143 | 144 | err: 145 | return status; 146 | } 147 | 148 | bool http_download_file(const char* url, uint8_t** data, uint64_t* data_size, uint64_t* total_size, uint64_t offset) { 149 | struct download_file_cb_args args; 150 | uint8_t chunk[DOWNLOAD_CHUNK_SIZE]; 151 | const char* headers[8 * 2]; 152 | size_t header_count = 0; 153 | char range_str[48]; 154 | bool status = false; 155 | int ret; 156 | 157 | if (!s_http_initialized) { 158 | goto err; 159 | } 160 | if (!url) { 161 | goto err; 162 | } 163 | 164 | memset(&args, 0, sizeof(args)); 165 | { 166 | args.data_size = data_size ? *data_size : (uint64_t)-1; 167 | args.chunk = chunk; 168 | args.chunk_size = sizeof(chunk); 169 | } 170 | 171 | memset(headers, 0, sizeof(headers)); 172 | { 173 | headers[header_count * 2 + 0] = "Accept-Encoding"; 174 | headers[header_count * 2 + 1] = "identity"; 175 | ++header_count; 176 | } 177 | 178 | if (offset > 0 && args.data_size > 0) { 179 | snprintf(range_str, sizeof(range_str), "bytes=%" PRIu64 "-%" PRIu64, offset, offset + args.data_size - 1); 180 | headers[header_count * 2 + 0] = "Range"; 181 | headers[header_count * 2 + 1] = range_str; 182 | ++header_count; 183 | } 184 | 185 | ret = do_request(url, ORBIS_HTTP_METHOD_GET, NULL, 0, headers, header_count, &download_file_cb, &args); 186 | if (ret) { 187 | goto err_data_free; 188 | } 189 | if (!is_good_status(args.status_code)) { 190 | goto err; 191 | } 192 | 193 | if (data) { 194 | *data = args.data; 195 | args.data = NULL; 196 | } 197 | if (data_size) { 198 | *data_size = args.actual_size; 199 | } 200 | if (total_size) { 201 | *total_size = args.content_length; 202 | } 203 | 204 | status = true; 205 | 206 | err_data_free: 207 | if (args.data) { 208 | free(args.data); 209 | } 210 | 211 | err: 212 | return status; 213 | } 214 | 215 | bool http_escape_uri(char** out, size_t* out_size, const char* in) { 216 | char* tmp = NULL; 217 | size_t tmp_size; 218 | bool status = false; 219 | int ret; 220 | 221 | if (!s_http_initialized) { 222 | goto err; 223 | } 224 | 225 | if (!in) { 226 | goto err; 227 | } 228 | 229 | ret = sceHttpUriEscape(NULL, &tmp_size, 0, in); 230 | if (ret) { 231 | EPRINTF("sceHttpUriEscape failed: 0x%08X\n", ret); 232 | goto err; 233 | } 234 | 235 | tmp = (char*)malloc(tmp_size); 236 | if (!tmp) { 237 | EPRINTF("malloc failed\n"); 238 | goto err; 239 | } 240 | memset(tmp, 0, tmp_size); 241 | 242 | ret = sceHttpUriEscape(tmp, out_size, tmp_size, in); 243 | if (ret) { 244 | EPRINTF("sceHttpUriEscape failed: 0x%08X\n", ret); 245 | goto err; 246 | } 247 | 248 | if (out) { 249 | *out = tmp; 250 | tmp = NULL; 251 | } 252 | 253 | status = true; 254 | 255 | err: 256 | if (tmp) { 257 | free(tmp); 258 | } 259 | 260 | return status; 261 | } 262 | 263 | bool http_unescape_uri(char** out, size_t* out_size, const char* in) { 264 | char* tmp = NULL; 265 | size_t tmp_size; 266 | bool status = false; 267 | int ret; 268 | 269 | if (!s_http_initialized) { 270 | goto err; 271 | } 272 | 273 | if (!in) { 274 | goto err; 275 | } 276 | 277 | ret = sceHttpUriUnescape(NULL, &tmp_size, 0, in); 278 | if (ret) { 279 | EPRINTF("sceHttpUriUnescape failed: 0x%08X\n", ret); 280 | goto err; 281 | } 282 | 283 | tmp = (char*)malloc(tmp_size); 284 | if (!tmp) { 285 | EPRINTF("malloc failed\n"); 286 | goto err; 287 | } 288 | memset(tmp, 0, tmp_size); 289 | 290 | ret = sceHttpUriUnescape(tmp, out_size, tmp_size, in); 291 | if (ret) { 292 | EPRINTF("sceHttpUriUnescape failed: 0x%08X\n", ret); 293 | goto err; 294 | } 295 | 296 | if (out) { 297 | *out = tmp; 298 | tmp = NULL; 299 | } 300 | 301 | status = true; 302 | 303 | err: 304 | if (tmp) { 305 | free(tmp); 306 | } 307 | 308 | return status; 309 | } 310 | 311 | bool http_escape_json_string(char* out, size_t max_out_size, const char* in) { 312 | bool status = false; 313 | int ret; 314 | 315 | if (!s_http_initialized) { 316 | goto err; 317 | } 318 | 319 | if (!in) { 320 | goto err; 321 | } 322 | 323 | memset(out, 0, max_out_size); 324 | 325 | ret = sceNpUtilJsonEscape(out, max_out_size, in, strlen(in)); 326 | if (ret) { 327 | EPRINTF("sceNpUtilJsonEscape failed: 0x%08X\n", ret); 328 | goto err; 329 | } 330 | 331 | status = true; 332 | 333 | err: 334 | return status; 335 | } 336 | 337 | static int download_file_cb(void* arg, int req_id, int status_code, uint64_t content_length, int content_length_type) { 338 | struct download_file_cb_args* args = (struct download_file_cb_args*)arg; 339 | uint8_t* chunk; 340 | size_t chunk_size; 341 | uint8_t* cur_data; 342 | uint64_t cur_size = 0; 343 | uint64_t total_size; 344 | int ret; 345 | 346 | assert(args != NULL); 347 | 348 | args->status_code = status_code; 349 | 350 | if (req_id < 0) { 351 | ret = ORBIS_HTTP_ERROR_INVALID_ID; 352 | goto err; 353 | } 354 | 355 | if (!is_good_status(status_code)) { 356 | ret = ORBIS_HTTP_ERROR_NOT_FOUND; 357 | goto err; 358 | } 359 | 360 | if (content_length_type != ORBIS_HTTP_CONTENTLEN_EXIST) { 361 | content_length = UINT64_MAX; 362 | } 363 | 364 | if (args->data_size == (uint64_t)-1) { 365 | /* XXX: if Content-Length is not specified then user must specify it by himself */ 366 | if (content_length_type != ORBIS_HTTP_CONTENTLEN_EXIST) { 367 | ret = ORBIS_HTTP_ERROR_NO_CONTENT_LENGTH; 368 | goto err; 369 | } 370 | total_size = content_length; 371 | } else if (args->data_size > content_length) { 372 | total_size = content_length; 373 | } else { 374 | total_size = args->data_size; 375 | } 376 | if (total_size > 0 && !args->chunk) { 377 | ret = ORBIS_HTTP_ERROR_INVALID_VALUE; 378 | goto err; 379 | } 380 | 381 | cur_data = args->data = (uint8_t*)malloc(total_size + 1); /* XXX: allocate one more byte to have valid cstrings */ 382 | if (!cur_data) { 383 | ret = ORBIS_HTTP_ERROR_OUT_OF_MEMORY; 384 | goto err_partial_xfer; 385 | } 386 | memset(cur_data, 0, total_size + 1); 387 | 388 | for (chunk = args->chunk; cur_size < total_size; ) { 389 | chunk_size = total_size - cur_size; 390 | if (chunk_size > args->chunk_size) { 391 | chunk_size = args->chunk_size; 392 | } 393 | ret = sceHttpReadData(req_id, chunk, chunk_size); 394 | if (ret < 0) { 395 | EPRINTF("sceHttpReadData failed: 0x%08X\n", ret); 396 | goto err_partial_xfer; 397 | } else if (ret == 0){ 398 | break; 399 | } 400 | 401 | memcpy(cur_data, chunk, ret); 402 | 403 | cur_data += ret; 404 | cur_size += ret; 405 | } 406 | 407 | ret = 0; 408 | 409 | err_partial_xfer: 410 | args->data_size = total_size; 411 | args->actual_size = cur_size; 412 | args->content_length = content_length; 413 | 414 | err: 415 | return ret; 416 | } 417 | 418 | static int do_request(const char* url, int method, const void* data, size_t data_size, const char** headers, size_t header_count, request_cb_t* cb, void* arg) { 419 | int tpl_id = -1, conn_id = -1, req_id = -1; 420 | unsigned int ssl_flags; 421 | int status_code, content_length_type; 422 | uint64_t content_length; 423 | size_t i; 424 | int ret; 425 | 426 | if (!url) { 427 | ret = ORBIS_HTTP_ERROR_INVALID_VALUE; 428 | goto err; 429 | } 430 | if (!headers) { 431 | header_count = 0; 432 | } 433 | 434 | ret = sceHttpCreateTemplate(s_libhttp_ctx_id, USER_AGENT, ORBIS_HTTP_VERSION_1_1, 1); 435 | if (ret < 0) { 436 | EPRINTF("sceHttpCreateTemplate failed: 0x%08X\n", ret); 437 | goto err; 438 | } 439 | tpl_id = ret; 440 | 441 | ret = sceHttpCreateConnectionWithURL(tpl_id, url, 1); 442 | if (ret < 0) { 443 | EPRINTF("sceHttpCreateConnectionWithURL failed: 0x%08X\n", ret); 444 | goto err_tpl_delete; 445 | } 446 | conn_id = ret; 447 | 448 | ret = sceHttpCreateRequestWithURL(conn_id, method, url, data ? data_size : 0); 449 | if (ret < 0) { 450 | EPRINTF("sceHttpCreateRequestWithURL failed: 0x%08X\n", ret); 451 | goto err_conn_delete; 452 | } 453 | req_id = ret; 454 | 455 | ssl_flags = ORBIS_HTTPS_FLAG_SERVER_VERIFY | ORBIS_HTTPS_FLAG_CLIENT_VERIFY; 456 | ssl_flags |= ORBIS_HTTPS_FLAG_CN_CHECK | ORBIS_HTTPS_FLAG_KNOWN_CA_CHECK; 457 | ssl_flags |= ORBIS_HTTPS_FLAG_NOT_AFTER_CHECK | ORBIS_HTTPS_FLAG_NOT_BEFORE_CHECK; 458 | 459 | ret = sceHttpsDisableOption(tpl_id, ssl_flags); 460 | if (ret) { 461 | #if 0 /* TODO: figure out */ 462 | EPRINTF("sceHttpsDisableOption failed: 0x%08X\n", ret); 463 | goto err; 464 | #endif 465 | } 466 | 467 | for (i = 0; i < header_count; ++i) { 468 | ret = sceHttpAddRequestHeader(req_id, headers[i * 2 + 0], headers[i * 2 + 1], ORBIS_HTTP_HEADER_OVERWRITE); 469 | if (ret) { 470 | EPRINTF("sceHttpAddRequestHeader failed: 0x%08X\n", ret); 471 | goto err_req_delete; 472 | } 473 | } 474 | 475 | ret = sceHttpSendRequest(req_id, data, data ? data_size : 0); 476 | if (ret) { 477 | EPRINTF("sceHttpSendRequest failed: 0x%08X\n", ret); 478 | goto err_req_delete; 479 | } 480 | 481 | ret = sceHttpGetStatusCode(req_id, &status_code); 482 | if (ret < 0) { 483 | EPRINTF("sceHttpGetStatusCode failed: 0x%08X\n", ret); 484 | goto err_req_delete; 485 | } 486 | 487 | if (is_good_status(status_code)) { 488 | ret = sceHttpGetResponseContentLength(req_id, &content_length_type, &content_length); 489 | if (ret) { 490 | EPRINTF("sceHttpGetResponseContentLength failed: 0x%08X\n", ret); 491 | goto err_req_delete; 492 | } 493 | } 494 | if (cb) { 495 | ret = (*cb)(arg, req_id, status_code, content_length, content_length_type); 496 | if (ret) { 497 | goto err_req_delete; 498 | } 499 | } 500 | 501 | err_req_delete: 502 | ret = sceHttpDeleteRequest(req_id); 503 | if (ret) { 504 | EPRINTF("sceHttpDeleteRequest failed: 0x%08X\n", ret); 505 | } 506 | 507 | err_conn_delete: 508 | ret = sceHttpDeleteConnection(conn_id); 509 | if (ret) { 510 | EPRINTF("sceHttpDeleteConnection failed: 0x%08X\n", ret); 511 | } 512 | 513 | err_tpl_delete: 514 | ret = sceHttpDeleteTemplate(tpl_id); 515 | if (ret) { 516 | EPRINTF("sceHttpDeleteTemplate failed: 0x%08X\n", ret); 517 | } 518 | 519 | err: 520 | return ret; 521 | } 522 | 523 | static inline bool is_good_status(int status_code) { 524 | return (status_code == 200 || status_code == 206); 525 | } 526 | -------------------------------------------------------------------------------- /RPI/utstring.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | /* a dynamic string implementation using macros 25 | */ 26 | #ifndef UTSTRING_H 27 | #define UTSTRING_H 28 | 29 | #define UTSTRING_VERSION 2.0.2 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #ifdef __GNUC__ 37 | #define UTSTRING_UNUSED __attribute__((__unused__)) 38 | #else 39 | #define UTSTRING_UNUSED 40 | #endif 41 | 42 | #ifndef oom 43 | #define oom() exit(-1) 44 | #endif 45 | 46 | typedef struct { 47 | char *d; /* pointer to allocated buffer */ 48 | size_t n; /* allocated capacity */ 49 | size_t i; /* index of first unused byte */ 50 | } UT_string; 51 | 52 | #define utstring_reserve(s,amt) \ 53 | do { \ 54 | if (((s)->n - (s)->i) < (size_t)(amt)) { \ 55 | char *utstring_tmp = (char*)realloc( \ 56 | (s)->d, (s)->n + (amt)); \ 57 | if (utstring_tmp == NULL) oom(); \ 58 | (s)->d = utstring_tmp; \ 59 | (s)->n += (amt); \ 60 | } \ 61 | } while(0) 62 | 63 | #define utstring_init(s) \ 64 | do { \ 65 | (s)->n = 0; (s)->i = 0; (s)->d = NULL; \ 66 | utstring_reserve(s,100); \ 67 | (s)->d[0] = '\0'; \ 68 | } while(0) 69 | 70 | #define utstring_done(s) \ 71 | do { \ 72 | if ((s)->d != NULL) free((s)->d); \ 73 | (s)->n = 0; \ 74 | } while(0) 75 | 76 | #define utstring_free(s) \ 77 | do { \ 78 | utstring_done(s); \ 79 | free(s); \ 80 | } while(0) 81 | 82 | #define utstring_new(s) \ 83 | do { \ 84 | (s) = (UT_string*)malloc(sizeof(UT_string)); \ 85 | if (!(s)) oom(); \ 86 | utstring_init(s); \ 87 | } while(0) 88 | 89 | #define utstring_renew(s) \ 90 | do { \ 91 | if (s) { \ 92 | utstring_clear(s); \ 93 | } else { \ 94 | utstring_new(s); \ 95 | } \ 96 | } while(0) 97 | 98 | #define utstring_clear(s) \ 99 | do { \ 100 | (s)->i = 0; \ 101 | (s)->d[0] = '\0'; \ 102 | } while(0) 103 | 104 | #define utstring_bincpy(s,b,l) \ 105 | do { \ 106 | utstring_reserve((s),(l)+1); \ 107 | if (l) memcpy(&(s)->d[(s)->i], b, l); \ 108 | (s)->i += (l); \ 109 | (s)->d[(s)->i]='\0'; \ 110 | } while(0) 111 | 112 | #define utstring_concat(dst,src) \ 113 | do { \ 114 | utstring_reserve((dst),((src)->i)+1); \ 115 | if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \ 116 | (dst)->i += (src)->i; \ 117 | (dst)->d[(dst)->i]='\0'; \ 118 | } while(0) 119 | 120 | #define utstring_len(s) ((s)->i) 121 | 122 | #define utstring_body(s) ((s)->d) 123 | 124 | UTSTRING_UNUSED static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) { 125 | int n; 126 | va_list cp; 127 | for (;;) { 128 | #ifdef _WIN32 129 | cp = ap; 130 | #else 131 | va_copy(cp, ap); 132 | #endif 133 | n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp); 134 | va_end(cp); 135 | 136 | if ((n > -1) && ((size_t) n < (s->n-s->i))) { 137 | s->i += n; 138 | return; 139 | } 140 | 141 | /* Else try again with more space. */ 142 | if (n > -1) utstring_reserve(s,n+1); /* exact */ 143 | else utstring_reserve(s,(s->n)*2); /* 2x */ 144 | } 145 | } 146 | #ifdef __GNUC__ 147 | /* support printf format checking (2=the format string, 3=start of varargs) */ 148 | static void utstring_printf(UT_string *s, const char *fmt, ...) 149 | __attribute__ (( format( printf, 2, 3) )); 150 | #endif 151 | UTSTRING_UNUSED static void utstring_printf(UT_string *s, const char *fmt, ...) { 152 | va_list ap; 153 | va_start(ap,fmt); 154 | utstring_printf_va(s,fmt,ap); 155 | va_end(ap); 156 | } 157 | 158 | /******************************************************************************* 159 | * begin substring search functions * 160 | ******************************************************************************/ 161 | /* Build KMP table from left to right. */ 162 | UTSTRING_UNUSED static void _utstring_BuildTable( 163 | const char *P_Needle, 164 | size_t P_NeedleLen, 165 | long *P_KMP_Table) 166 | { 167 | long i, j; 168 | 169 | i = 0; 170 | j = i - 1; 171 | P_KMP_Table[i] = j; 172 | while (i < (long) P_NeedleLen) 173 | { 174 | while ( (j > -1) && (P_Needle[i] != P_Needle[j]) ) 175 | { 176 | j = P_KMP_Table[j]; 177 | } 178 | i++; 179 | j++; 180 | if (i < (long) P_NeedleLen) 181 | { 182 | if (P_Needle[i] == P_Needle[j]) 183 | { 184 | P_KMP_Table[i] = P_KMP_Table[j]; 185 | } 186 | else 187 | { 188 | P_KMP_Table[i] = j; 189 | } 190 | } 191 | else 192 | { 193 | P_KMP_Table[i] = j; 194 | } 195 | } 196 | 197 | return; 198 | } 199 | 200 | 201 | /* Build KMP table from right to left. */ 202 | UTSTRING_UNUSED static void _utstring_BuildTableR( 203 | const char *P_Needle, 204 | size_t P_NeedleLen, 205 | long *P_KMP_Table) 206 | { 207 | long i, j; 208 | 209 | i = P_NeedleLen - 1; 210 | j = i + 1; 211 | P_KMP_Table[i + 1] = j; 212 | while (i >= 0) 213 | { 214 | while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) ) 215 | { 216 | j = P_KMP_Table[j + 1]; 217 | } 218 | i--; 219 | j--; 220 | if (i >= 0) 221 | { 222 | if (P_Needle[i] == P_Needle[j]) 223 | { 224 | P_KMP_Table[i + 1] = P_KMP_Table[j + 1]; 225 | } 226 | else 227 | { 228 | P_KMP_Table[i + 1] = j; 229 | } 230 | } 231 | else 232 | { 233 | P_KMP_Table[i + 1] = j; 234 | } 235 | } 236 | 237 | return; 238 | } 239 | 240 | 241 | /* Search data from left to right. ( Multiple search mode. ) */ 242 | UTSTRING_UNUSED static long _utstring_find( 243 | const char *P_Haystack, 244 | size_t P_HaystackLen, 245 | const char *P_Needle, 246 | size_t P_NeedleLen, 247 | long *P_KMP_Table) 248 | { 249 | long i, j; 250 | long V_FindPosition = -1; 251 | 252 | /* Search from left to right. */ 253 | i = j = 0; 254 | while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) ) 255 | { 256 | while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) ) 257 | { 258 | i = P_KMP_Table[i]; 259 | } 260 | i++; 261 | j++; 262 | if (i >= (int)P_NeedleLen) 263 | { 264 | /* Found. */ 265 | V_FindPosition = j - i; 266 | break; 267 | } 268 | } 269 | 270 | return V_FindPosition; 271 | } 272 | 273 | 274 | /* Search data from right to left. ( Multiple search mode. ) */ 275 | UTSTRING_UNUSED static long _utstring_findR( 276 | const char *P_Haystack, 277 | size_t P_HaystackLen, 278 | const char *P_Needle, 279 | size_t P_NeedleLen, 280 | long *P_KMP_Table) 281 | { 282 | long i, j; 283 | long V_FindPosition = -1; 284 | 285 | /* Search from right to left. */ 286 | j = (P_HaystackLen - 1); 287 | i = (P_NeedleLen - 1); 288 | while ( (j >= 0) && (j >= i) ) 289 | { 290 | while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) ) 291 | { 292 | i = P_KMP_Table[i + 1]; 293 | } 294 | i--; 295 | j--; 296 | if (i < 0) 297 | { 298 | /* Found. */ 299 | V_FindPosition = j + 1; 300 | break; 301 | } 302 | } 303 | 304 | return V_FindPosition; 305 | } 306 | 307 | 308 | /* Search data from left to right. ( One time search mode. ) */ 309 | UTSTRING_UNUSED static long utstring_find( 310 | UT_string *s, 311 | long P_StartPosition, /* Start from 0. -1 means last position. */ 312 | const char *P_Needle, 313 | size_t P_NeedleLen) 314 | { 315 | long V_StartPosition; 316 | long V_HaystackLen; 317 | long *V_KMP_Table; 318 | long V_FindPosition = -1; 319 | 320 | if (P_StartPosition < 0) 321 | { 322 | V_StartPosition = s->i + P_StartPosition; 323 | } 324 | else 325 | { 326 | V_StartPosition = P_StartPosition; 327 | } 328 | V_HaystackLen = s->i - V_StartPosition; 329 | if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) 330 | { 331 | V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); 332 | if (V_KMP_Table != NULL) 333 | { 334 | _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table); 335 | 336 | V_FindPosition = _utstring_find(s->d + V_StartPosition, 337 | V_HaystackLen, 338 | P_Needle, 339 | P_NeedleLen, 340 | V_KMP_Table); 341 | if (V_FindPosition >= 0) 342 | { 343 | V_FindPosition += V_StartPosition; 344 | } 345 | 346 | free(V_KMP_Table); 347 | } 348 | } 349 | 350 | return V_FindPosition; 351 | } 352 | 353 | 354 | /* Search data from right to left. ( One time search mode. ) */ 355 | UTSTRING_UNUSED static long utstring_findR( 356 | UT_string *s, 357 | long P_StartPosition, /* Start from 0. -1 means last position. */ 358 | const char *P_Needle, 359 | size_t P_NeedleLen) 360 | { 361 | long V_StartPosition; 362 | long V_HaystackLen; 363 | long *V_KMP_Table; 364 | long V_FindPosition = -1; 365 | 366 | if (P_StartPosition < 0) 367 | { 368 | V_StartPosition = s->i + P_StartPosition; 369 | } 370 | else 371 | { 372 | V_StartPosition = P_StartPosition; 373 | } 374 | V_HaystackLen = V_StartPosition + 1; 375 | if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) ) 376 | { 377 | V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1)); 378 | if (V_KMP_Table != NULL) 379 | { 380 | _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table); 381 | 382 | V_FindPosition = _utstring_findR(s->d, 383 | V_HaystackLen, 384 | P_Needle, 385 | P_NeedleLen, 386 | V_KMP_Table); 387 | 388 | free(V_KMP_Table); 389 | } 390 | } 391 | 392 | return V_FindPosition; 393 | } 394 | /******************************************************************************* 395 | * end substring search functions * 396 | ******************************************************************************/ 397 | 398 | #endif /* UTSTRING_H */ 399 | -------------------------------------------------------------------------------- /RPI/utarray.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 12 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 13 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 14 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 15 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 16 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 17 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 18 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 19 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | */ 23 | 24 | /* a dynamic array implementation using macros 25 | */ 26 | #ifndef UTARRAY_H 27 | #define UTARRAY_H 28 | 29 | #define UTARRAY_VERSION 2.0.2 30 | 31 | #include /* size_t */ 32 | #include /* memset, etc */ 33 | #include /* exit */ 34 | 35 | #ifdef __GNUC__ 36 | #define UTARRAY_UNUSED __attribute__((__unused__)) 37 | #else 38 | #define UTARRAY_UNUSED 39 | #endif 40 | 41 | #ifndef oom 42 | #define oom() exit(-1) 43 | #endif 44 | 45 | typedef void (ctor_f)(void *dst, const void *src); 46 | typedef void (dtor_f)(void *elt); 47 | typedef void (init_f)(void *elt); 48 | typedef struct { 49 | size_t sz; 50 | init_f *init; 51 | ctor_f *copy; 52 | dtor_f *dtor; 53 | } UT_icd; 54 | 55 | typedef struct { 56 | unsigned i,n;/* i: index of next available slot, n: num slots */ 57 | UT_icd icd; /* initializer, copy and destructor functions */ 58 | char *d; /* n slots of size icd->sz*/ 59 | } UT_array; 60 | 61 | #define utarray_init(a,_icd) do { \ 62 | memset(a,0,sizeof(UT_array)); \ 63 | (a)->icd = *(_icd); \ 64 | } while(0) 65 | 66 | #define utarray_done(a) do { \ 67 | if ((a)->n) { \ 68 | if ((a)->icd.dtor) { \ 69 | unsigned _ut_i; \ 70 | for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ 71 | (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ 72 | } \ 73 | } \ 74 | free((a)->d); \ 75 | } \ 76 | (a)->n=0; \ 77 | } while(0) 78 | 79 | #define utarray_new(a,_icd) do { \ 80 | (a) = (UT_array*)malloc(sizeof(UT_array)); \ 81 | if ((a) == NULL) oom(); \ 82 | utarray_init(a,_icd); \ 83 | } while(0) 84 | 85 | #define utarray_free(a) do { \ 86 | utarray_done(a); \ 87 | free(a); \ 88 | } while(0) 89 | 90 | #define utarray_reserve(a,by) do { \ 91 | if (((a)->i+(by)) > (a)->n) { \ 92 | char *utarray_tmp; \ 93 | while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ 94 | utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ 95 | if (utarray_tmp == NULL) oom(); \ 96 | (a)->d=utarray_tmp; \ 97 | } \ 98 | } while(0) 99 | 100 | #define utarray_push_back(a,p) do { \ 101 | utarray_reserve(a,1); \ 102 | if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ 103 | else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ 104 | } while(0) 105 | 106 | #define utarray_pop_back(a) do { \ 107 | if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ 108 | else { (a)->i--; } \ 109 | } while(0) 110 | 111 | #define utarray_extend_back(a) do { \ 112 | utarray_reserve(a,1); \ 113 | if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ 114 | else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ 115 | (a)->i++; \ 116 | } while(0) 117 | 118 | #define utarray_len(a) ((a)->i) 119 | 120 | #define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) 121 | #define _utarray_eltptr(a,j) ((a)->d + ((a)->icd.sz * (j))) 122 | 123 | #define utarray_insert(a,p,j) do { \ 124 | if ((j) > (a)->i) utarray_resize(a,j); \ 125 | utarray_reserve(a,1); \ 126 | if ((j) < (a)->i) { \ 127 | memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ 128 | ((a)->i - (j))*((a)->icd.sz)); \ 129 | } \ 130 | if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ 131 | else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ 132 | (a)->i++; \ 133 | } while(0) 134 | 135 | #define utarray_inserta(a,w,j) do { \ 136 | if (utarray_len(w) == 0) break; \ 137 | if ((j) > (a)->i) utarray_resize(a,j); \ 138 | utarray_reserve(a,utarray_len(w)); \ 139 | if ((j) < (a)->i) { \ 140 | memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ 141 | _utarray_eltptr(a,j), \ 142 | ((a)->i - (j))*((a)->icd.sz)); \ 143 | } \ 144 | if ((a)->icd.copy) { \ 145 | unsigned _ut_i; \ 146 | for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ 147 | (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ 148 | } \ 149 | } else { \ 150 | memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ 151 | utarray_len(w)*((a)->icd.sz)); \ 152 | } \ 153 | (a)->i += utarray_len(w); \ 154 | } while(0) 155 | 156 | #define utarray_resize(dst,num) do { \ 157 | unsigned _ut_i; \ 158 | if ((dst)->i > (unsigned)(num)) { \ 159 | if ((dst)->icd.dtor) { \ 160 | for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ 161 | (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ 162 | } \ 163 | } \ 164 | } else if ((dst)->i < (unsigned)(num)) { \ 165 | utarray_reserve(dst, (num) - (dst)->i); \ 166 | if ((dst)->icd.init) { \ 167 | for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ 168 | (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ 169 | } \ 170 | } else { \ 171 | memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ 172 | } \ 173 | } \ 174 | (dst)->i = (num); \ 175 | } while(0) 176 | 177 | #define utarray_concat(dst,src) do { \ 178 | utarray_inserta(dst, src, utarray_len(dst)); \ 179 | } while(0) 180 | 181 | #define utarray_erase(a,pos,len) do { \ 182 | if ((a)->icd.dtor) { \ 183 | unsigned _ut_i; \ 184 | for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ 185 | (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ 186 | } \ 187 | } \ 188 | if ((a)->i > ((pos) + (len))) { \ 189 | memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ 190 | ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ 191 | } \ 192 | (a)->i -= (len); \ 193 | } while(0) 194 | 195 | #define utarray_renew(a,u) do { \ 196 | if (a) utarray_clear(a); \ 197 | else utarray_new(a, u); \ 198 | } while(0) 199 | 200 | #define utarray_clear(a) do { \ 201 | if ((a)->i > 0) { \ 202 | if ((a)->icd.dtor) { \ 203 | unsigned _ut_i; \ 204 | for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ 205 | (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ 206 | } \ 207 | } \ 208 | (a)->i = 0; \ 209 | } \ 210 | } while(0) 211 | 212 | #define utarray_sort(a,cmp) do { \ 213 | qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ 214 | } while(0) 215 | 216 | #define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) 217 | 218 | #define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) 219 | #define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : ((((a)->i) > (utarray_eltidx(a,e)+1)) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) 220 | #define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) > 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) 221 | #define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) 222 | #define utarray_eltidx(a,e) (((char*)(e) >= (a)->d) ? (((char*)(e) - (a)->d)/(a)->icd.sz) : -1) 223 | 224 | /* last we pre-define a few icd for common utarrays of ints and strings */ 225 | static void utarray_str_cpy(void *dst, const void *src) { 226 | char **_src = (char**)src, **_dst = (char**)dst; 227 | *_dst = (*_src == NULL) ? NULL : strdup(*_src); 228 | } 229 | static void utarray_str_dtor(void *elt) { 230 | char **eltc = (char**)elt; 231 | if (*eltc != NULL) free(*eltc); 232 | } 233 | static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; 234 | static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; 235 | static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; 236 | 237 | 238 | #endif /* UTARRAY_H */ 239 | -------------------------------------------------------------------------------- /RPI/pkg.c: -------------------------------------------------------------------------------- 1 | #include "pkg.h" 2 | #include "http.h" 3 | #include "util.h" 4 | 5 | #include 6 | #include "tiny-json.h" 7 | 8 | union json_value_t { 9 | const json_t* jval; 10 | const char* sval; 11 | int64_t ival; 12 | }; 13 | 14 | static uint8_t s_zero_mini_digest[PKG_MINI_DIGEST_SIZE] = { 0 }; 15 | 16 | bool pkg_parse_content_id(const char* content_id, struct pkg_content_info* info) { 17 | struct pkg_content_info tmp; 18 | char* p1; 19 | char* p2; 20 | bool status = false; 21 | 22 | if (!content_id) { 23 | goto err; 24 | } 25 | 26 | if (strlen(content_id) != PKG_CONTENT_ID_SIZE) { 27 | goto err; 28 | } 29 | 30 | memset(&tmp, 0, sizeof(tmp)); 31 | strlcpy(tmp.content_id, content_id, sizeof(tmp.content_id)); 32 | 33 | p1 = strchr(content_id, '-'); 34 | if (!p1) { 35 | goto err; 36 | } 37 | p2 = strchr(++p1, '-'); 38 | if (!p2) { 39 | goto err; 40 | } 41 | if ((p2 - content_id) != PKG_SERVICE_ID_SIZE) { 42 | goto err; 43 | } 44 | strlcpy(tmp.service_id, content_id, sizeof(tmp.service_id)); 45 | if (strlen(tmp.service_id) != PKG_SERVICE_ID_SIZE) { 46 | goto err; 47 | } 48 | 49 | p2 = strchr(p1, '_'); 50 | if (!p2) { 51 | goto err; 52 | } 53 | strlcpy(tmp.title_id, p1, sizeof(tmp.title_id)); 54 | if (strlen(tmp.title_id) != PKG_TITLE_ID_SIZE) { 55 | goto err; 56 | } 57 | 58 | p1 = strrchr(content_id, '-'); 59 | if (!p1) { 60 | goto err; 61 | } 62 | strlcpy(tmp.label, p1 + 1, sizeof(tmp.label)); 63 | if (strlen(tmp.label) != PKG_LABEL_SIZE) { 64 | goto err; 65 | } 66 | 67 | if (info) { 68 | memcpy(info, &tmp, sizeof(*info)); 69 | } 70 | 71 | status = true; 72 | 73 | err: 74 | return status; 75 | } 76 | 77 | char** pkg_extract_piece_urls_from_ref_pkg_json(const char* url, size_t* piece_count) { 78 | static json_t* pool = NULL; 79 | const size_t pool_size = 256; 80 | const json_t* root; 81 | const json_t* field; 82 | union json_value_t val; 83 | const char* prop_val; 84 | char* data = NULL; 85 | uint64_t size = (uint64_t)-1; 86 | uint64_t total_size; 87 | char** piece_urls = NULL; 88 | size_t count; 89 | char* unescaped_url = NULL; 90 | size_t unescaped_url_size; 91 | size_t i; 92 | 93 | if (!url) { 94 | EPRINTF("No URL specified.\n"); 95 | goto err; 96 | } 97 | 98 | //printf("Downloading reference package json: %s\n", url); 99 | if (!http_download_file(url, (uint8_t**)&data, &size, &total_size, 0)) { 100 | EPRINTF("Unable to download reference package json '%s'.\n", url); 101 | goto err; 102 | } 103 | //printf("Reference package json total size: 0x%" PRIX64 "\n", total_size); 104 | if (total_size == 0) { 105 | EPRINTF("Empty reference package json.\n"); 106 | goto err; 107 | } 108 | 109 | pool = (json_t*)malloc(sizeof(*pool) * pool_size); 110 | if (!pool) { 111 | EPRINTF("No memory.\n"); 112 | goto err; 113 | } 114 | memset(pool, 0, sizeof(*pool) * pool_size); 115 | 116 | root = json_create(data, pool, pool_size); 117 | if (!root) { 118 | EPRINTF("Invalid JSON format.\n"); 119 | goto err; 120 | } 121 | 122 | field = json_getProperty(root, "pieces"); 123 | if (!field) { 124 | EPRINTF("No '%s' parameter found.\n", "pieces"); 125 | goto err; 126 | } 127 | if (json_getType(field) != JSON_ARRAY) { 128 | EPRINTF("Invalid type for parameter '%s'.\n", "pieces"); 129 | goto err; 130 | } 131 | for (val.jval = json_getChild(field), count = 0; val.jval != NULL; val.jval = json_getSibling(val.jval)) { 132 | if (json_getType(val.jval) != JSON_OBJ) { 133 | EPRINTF("Invalid type for element of parameter '%s'.\n", "pieces"); 134 | goto err; 135 | } 136 | 137 | prop_val = json_getPropertyValue(val.jval, "url"); 138 | if (!prop_val) { 139 | EPRINTF("No '%s' property found in element of parameter '%s'.\n", "url", "pieces"); 140 | goto err; 141 | } 142 | if (strlen(prop_val) == 0) { 143 | EPRINTF("Empty value of property '%s' in element of parameter '%s'.\n", "url", "pieces"); 144 | goto err; 145 | } 146 | 147 | if (!http_unescape_uri(&unescaped_url, &unescaped_url_size, prop_val)) { 148 | EPRINTF("Unable to unescape value of property '%s' in element of parameter '%s'.\n", "url", "pieces"); 149 | goto err; 150 | } 151 | 152 | if (!starts_with(unescaped_url, "http://") && !starts_with(unescaped_url, "https://")) { 153 | EPRINTF("Unexpected value of property '%s' in element of parameter '%s'.\n", "url", "pieces"); 154 | goto err; 155 | } 156 | 157 | free(unescaped_url); 158 | unescaped_url = NULL; 159 | 160 | ++count; 161 | } 162 | if (count == 0) { 163 | EPRINTF("No pieces.\n"); 164 | goto err; 165 | } 166 | 167 | piece_urls = (char**)malloc(count * sizeof(*piece_urls)); 168 | if (!piece_urls) { 169 | EPRINTF("No memory.\n"); 170 | goto err; 171 | } 172 | memset(piece_urls, 0, count * sizeof(*piece_urls)); 173 | 174 | for (val.jval = json_getChild(field), i = 0; val.jval != NULL; val.jval = json_getSibling(val.jval)) { 175 | prop_val = json_getPropertyValue(val.jval, "url"); 176 | 177 | if (!http_unescape_uri(&unescaped_url, &unescaped_url_size, prop_val)) { 178 | EPRINTF("Unable to unescape value of property '%s' in element of parameter '%s'.\n", "url", "pieces"); 179 | goto err; 180 | } 181 | 182 | piece_urls[i++] = unescaped_url; 183 | unescaped_url = NULL; 184 | } 185 | 186 | if (piece_count) { 187 | *piece_count = count; 188 | } 189 | 190 | if (unescaped_url) { 191 | free(unescaped_url); 192 | } 193 | 194 | if (data) { 195 | free(data); 196 | } 197 | 198 | return piece_urls; 199 | 200 | err: 201 | if (piece_count) { 202 | *piece_count = 0; 203 | } 204 | 205 | if (piece_urls) { 206 | for (i = 0; i < count; ++i) { 207 | free(piece_urls[i]); 208 | } 209 | free(piece_urls); 210 | } 211 | 212 | if (unescaped_url) { 213 | free(unescaped_url); 214 | } 215 | 216 | if (data) { 217 | free(data); 218 | } 219 | 220 | return NULL; 221 | } 222 | 223 | #define PKG_THROW_ERROR(format, ...) \ 224 | do { \ 225 | if (error_buf) \ 226 | snprintf(error_buf, error_buf_size, format, ##__VA_ARGS__); \ 227 | EPRINTF(format, ##__VA_ARGS__); \ 228 | } while (0) 229 | 230 | bool pkg_setup_prerequisites(char** piece_urls, size_t piece_count, const char* ref_pkg_json_path, const char* param_sfo_path, const char* icon0_png_path, enum pkg_content_type* content_type, uint64_t* package_size, bool* is_patch, bool* has_icon, char* error_buf, size_t error_buf_size) { 231 | static const uint8_t magic[] = { '\x7F', 'C', 'N', 'T' }; 232 | struct pkg_header* hdr; 233 | struct pkg_table_entry* entries; 234 | uint8_t* hdr_data = NULL; 235 | uint64_t hdr_size = sizeof(*hdr); 236 | uint8_t* entry_table_data = NULL; 237 | uint32_t entry_table_offset; 238 | uint64_t entry_table_size; 239 | uint8_t* param_sfo_data = NULL; 240 | uint32_t param_sfo_offset = 0; 241 | uint32_t param_sfo_size = 0; 242 | uint64_t param_sfo_dl_size; 243 | uint8_t* icon0_png_data = NULL; 244 | uint32_t icon0_png_offset = 0; 245 | uint32_t icon0_png_size = 0; 246 | uint64_t icon0_png_dl_size; 247 | uint64_t offset, total_size; 248 | size_t entry_count; 249 | char pkg_digest_str[PKG_DIGEST_SIZE * 2 + 1]; 250 | char piece_digest_str[PKG_MINI_DIGEST_SIZE * 2 + 1]; 251 | #ifdef ESCAPE_URL 252 | char* escaped_url = NULL; 253 | size_t escaped_url_size; 254 | #endif 255 | FILE* fp = NULL; 256 | size_t i; 257 | bool status = false; 258 | 259 | if (!piece_urls) { 260 | PKG_THROW_ERROR("No pieces URLs specified.\n"); 261 | goto err; 262 | } 263 | if (piece_count == 0) { 264 | PKG_THROW_ERROR("No pieces.\n"); 265 | goto err; 266 | } 267 | if (!ref_pkg_json_path || strlen(ref_pkg_json_path) == 0) { 268 | PKG_THROW_ERROR("Empty reference package json file path specified.\n"); 269 | goto err; 270 | } 271 | if (!param_sfo_path || strlen(param_sfo_path) == 0) { 272 | PKG_THROW_ERROR("Empty param.sfo file path specified.\n"); 273 | goto err; 274 | } 275 | if (!icon0_png_path || strlen(icon0_png_path) == 0) { 276 | PKG_THROW_ERROR("Empty icon0.png file path specified.\n"); 277 | goto err; 278 | } 279 | 280 | unlink(ref_pkg_json_path); 281 | unlink(param_sfo_path); 282 | unlink(icon0_png_path); 283 | 284 | //printf("Downloading package header: %s\n", piece_urls[0]); 285 | if (!http_download_file(piece_urls[0], &hdr_data, &hdr_size, &total_size, 0)) { 286 | PKG_THROW_ERROR("Unable to download package header for '%s'.\n", piece_urls[0]); 287 | goto err; 288 | } 289 | //printf("Package header size: 0x%" PRIX64 "\n", hdr_size); 290 | if (hdr_size != sizeof(*hdr)) { 291 | PKG_THROW_ERROR("Package header size mismatch for '%s'.\n", piece_urls[0]); 292 | goto err; 293 | } 294 | //printf("Package total size: 0x%" PRIX64 "\n", total_size); 295 | 296 | hdr = (struct pkg_header*)hdr_data; 297 | 298 | if (memcmp(hdr->magic, magic, sizeof(magic)) != 0) { 299 | PKG_THROW_ERROR("Invalid package format for '%s'.\n", piece_urls[0]); 300 | goto err; 301 | } 302 | 303 | if (is_patch) { 304 | *is_patch = pkg_is_patch(hdr); 305 | } 306 | 307 | if (piece_count == 1 && BE64(hdr->package_size) > 0 && total_size != BE64(hdr->package_size)) { 308 | PKG_THROW_ERROR("Unexpected file size for '%s'.\n", piece_urls[0]); 309 | goto err; 310 | } 311 | 312 | entry_count = BE32(hdr->entry_count); 313 | entry_table_offset = BE32(hdr->entry_table_offset); 314 | entry_table_size = entry_count * sizeof(*entries); 315 | if (entry_table_size == 0) { 316 | PKG_THROW_ERROR("Empty entry table for '%s'.\n", piece_urls[0]); 317 | goto err; 318 | } 319 | 320 | //printf("Downloading package entry table: %s\n", piece_urls[0]); 321 | if (!http_download_file(piece_urls[0], &entry_table_data, &entry_table_size, NULL, entry_table_offset)) { 322 | PKG_THROW_ERROR("Unable to download package entry table for '%s'.\n", piece_urls[0]); 323 | goto err; 324 | } 325 | //printf("Package entry table size: 0x%" PRIX64 "\n", entry_table_size); 326 | if (entry_table_size != entry_count * sizeof(*entries)) { 327 | PKG_THROW_ERROR("Package entry table size mismatch for '%s'.\n", piece_urls[0]); 328 | goto err; 329 | } 330 | 331 | entries = (struct pkg_table_entry*)entry_table_data; 332 | for (i = 0; i < entry_count; ++i) { 333 | switch (BE32(entries[i].id)) { 334 | case PKG_ENTRY_ID__PARAM_SFO: 335 | param_sfo_offset = BE32(entries[i].offset); 336 | param_sfo_size = BE32(entries[i].size); 337 | break; 338 | case PKG_ENTRY_ID__ICON0_PNG: 339 | icon0_png_offset = BE32(entries[i].offset); 340 | icon0_png_size = BE32(entries[i].size); 341 | break; 342 | default: 343 | goto next; 344 | } 345 | next:; 346 | } 347 | 348 | if (param_sfo_offset > 0 && param_sfo_size > 0) { 349 | //printf("Downloading %s: %s\n", "param.sfo", piece_urls[0]); 350 | param_sfo_dl_size = param_sfo_size; 351 | if (!http_download_file(piece_urls[0], ¶m_sfo_data, ¶m_sfo_dl_size, NULL, param_sfo_offset)) { 352 | PKG_THROW_ERROR("Unable to download %s for '%s'.\n", "param.sfo", piece_urls[0]); 353 | goto err; 354 | } 355 | //printf("param.sfo size: 0x%" PRIX64 "\n", param_sfo_dl_size); 356 | if (param_sfo_dl_size != param_sfo_size) { 357 | PKG_THROW_ERROR("%s size mismatch for '%s'.\n", "param.sfo", piece_urls[0]); 358 | goto err; 359 | } 360 | } 361 | 362 | if (icon0_png_offset > 0 && icon0_png_size > 0) { 363 | //printf("Downloading %s: %s\n", "icon0.png", piece_urls[0]); 364 | icon0_png_dl_size = icon0_png_size; 365 | if (!http_download_file(piece_urls[0], &icon0_png_data, &icon0_png_dl_size, NULL, icon0_png_offset)) { 366 | PKG_THROW_ERROR("Unable to download %s for '%s'.\n", "icon0.png", piece_urls[0]); 367 | goto err; 368 | } 369 | //printf("icon0.png size: 0x%" PRIX64 "\n", icon0_png_dl_size); 370 | if (icon0_png_dl_size != icon0_png_size) { 371 | PKG_THROW_ERROR("%s size mismatch for '%s'.\n", "icon0.png", piece_urls[0]); 372 | goto err; 373 | } 374 | } 375 | 376 | if (bytes_to_hex(pkg_digest_str, sizeof(pkg_digest_str), hdr->digest, sizeof(hdr->digest))) { 377 | PKG_THROW_ERROR("Unable to convert digest for '%s'.\n", piece_urls[0]); 378 | goto err; 379 | } 380 | if (bytes_to_hex(piece_digest_str, sizeof(piece_digest_str), s_zero_mini_digest, sizeof(s_zero_mini_digest))) { 381 | PKG_THROW_ERROR("Unable to convert digest for '%s'.\n", piece_urls[0]); 382 | goto err; 383 | } 384 | 385 | fp = fopen(ref_pkg_json_path, "wb"); 386 | if (!fp) { 387 | PKG_THROW_ERROR("fopen(%s) failed: %d\n", ref_pkg_json_path, errno); 388 | goto err; 389 | } 390 | 391 | fprintf(fp, 392 | "{\"originalFileSize\":%" PRIu64 ",\"packageDigest\":\"%s\",\"numberOfSplitFiles\":%" PRIuMAX ",\"pieces\":[", 393 | BE64(hdr->package_size), pkg_digest_str, (uintmax_t)piece_count 394 | ); 395 | 396 | for (i = 0, offset = 0; i < piece_count; ++i) { 397 | if (i > 0) { 398 | //printf("Getting piece information: %s\n", piece_urls[i]); 399 | if (!http_get_file_size(piece_urls[i], &total_size)) { 400 | PKG_THROW_ERROR("Unable to get file size for piece '%s'.\n", piece_urls[i]); 401 | goto err_file_close; 402 | } 403 | //printf("Piece size: 0x%" PRIX64 "\n", total_size); 404 | } 405 | 406 | #ifdef ESCAPE_URL 407 | if (!http_escape_uri(&escaped_url, &escaped_url_size, piece_urls[i])) { 408 | PKG_THROW_ERROR("Unable to escape URL for piece '%s'.\n", piece_urls[i]); 409 | goto err_file_close; 410 | } 411 | #endif 412 | 413 | fprintf(fp, 414 | "{\"url\":\"%s\",\"fileOffset\":%" PRIu64 ",\"fileSize\":%" PRIu64 ",\"hashValue\":\"%s\"}", 415 | #ifdef ESCAPE_URL 416 | escaped_url, offset, total_size, piece_digest_str 417 | #else 418 | piece_urls[i], offset, total_size, piece_digest_str 419 | #endif 420 | ); 421 | if (i + 1 < piece_count) { 422 | fputs(",", fp); 423 | } 424 | 425 | offset += total_size; 426 | 427 | #ifdef ESCAPE_URL 428 | free(escaped_url); 429 | escaped_url = NULL; 430 | #endif 431 | } 432 | 433 | fputs("]}", fp); 434 | 435 | if (BE64(hdr->package_size) > 0 && offset != BE64(hdr->package_size)) { 436 | PKG_THROW_ERROR("Unexpected total file size for '%s'.\n", piece_urls[0]); 437 | goto err_file_close; 438 | } 439 | 440 | if (param_sfo_offset > 0 && param_sfo_size > 0) { 441 | if (!write_file_trunc(param_sfo_path, param_sfo_data, param_sfo_size, NULL, S_IRUSR | S_IWUSR)) { 442 | PKG_THROW_ERROR("Unable to write %s file for '%s'.\n", "param.sfo", piece_urls[0]); 443 | goto err_file_close; 444 | } 445 | } 446 | if (icon0_png_offset > 0 && icon0_png_size > 0) { 447 | if (!write_file_trunc(icon0_png_path, icon0_png_data, icon0_png_size, NULL, S_IRUSR | S_IWUSR)) { 448 | PKG_THROW_ERROR("Unable to write %s file for '%s'.\n", "icon0.png", piece_urls[0]); 449 | goto err_file_close; 450 | } 451 | if (has_icon) { 452 | *has_icon = true; 453 | } 454 | } else { 455 | if (has_icon) { 456 | *has_icon = false; 457 | } 458 | } 459 | 460 | if (content_type) { 461 | *content_type = BE32(hdr->content_type); 462 | } 463 | if (package_size) { 464 | *package_size = BE64(hdr->package_size); 465 | } 466 | 467 | status = true; 468 | 469 | err_file_close: 470 | if (fp) { 471 | fclose(fp); 472 | } 473 | 474 | err_file_unlink: 475 | if (!status) { 476 | unlink(icon0_png_path); 477 | unlink(param_sfo_path); 478 | unlink(ref_pkg_json_path); 479 | } 480 | 481 | err: 482 | #ifdef ESCAPE_URL 483 | if (escaped_url) { 484 | free(escaped_url); 485 | } 486 | #endif 487 | 488 | if (icon0_png_data) { 489 | free(icon0_png_data); 490 | } 491 | 492 | if (param_sfo_data) { 493 | free(param_sfo_data); 494 | } 495 | 496 | if (entry_table_data) { 497 | free(entry_table_data); 498 | } 499 | if (hdr_data) { 500 | free(hdr_data); 501 | } 502 | 503 | return status; 504 | } 505 | 506 | #undef PKG_THROW_ERROR 507 | 508 | bool pkg_is_patch(struct pkg_header* hdr) { 509 | unsigned int flags; 510 | 511 | assert(hdr != NULL); 512 | 513 | flags = BE32(hdr->content_flags); 514 | 515 | if (flags & PKG_CONTENT_FLAGS_FIRST_PATCH) { 516 | return true; 517 | } 518 | if (flags & PKG_CONTENT_FLAGS_SUBSEQUENT_PATCH) { 519 | return true; 520 | } 521 | if (flags & PKG_CONTENT_FLAGS_DELTA_PATCH) { 522 | return true; 523 | } 524 | if (flags & PKG_CONTENT_FLAGS_CUMULATIVE_PATCH) { 525 | return true; 526 | } 527 | 528 | return false; 529 | } 530 | -------------------------------------------------------------------------------- /RPI/tiny-json.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | 4 | 5 | 6 | Licensed under the MIT License . 7 | SPDX-License-Identifier: MIT 8 | Copyright (c) 2016-2018 Rafa Garcia . 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | */ 29 | 30 | #include 31 | #include 32 | #include "tiny-json.h" 33 | 34 | /** Structure to handle a heap of JSON properties. */ 35 | typedef struct jsonPool_s { 36 | json_t* const mem; /**< Pointer to array of json properties. */ 37 | unsigned int const qty; /**< Length of the array of json properties. */ 38 | unsigned int nextFree; /**< The index of the next free json property. */ 39 | } jsonPool_t; 40 | 41 | /* Search a property by its name in a JSON object. */ 42 | json_t const* json_getProperty( json_t const* obj, char const* property ) { 43 | json_t const* sibling; 44 | for( sibling = obj->u.c.child; sibling; sibling = sibling->sibling ) 45 | if ( sibling->name && !strcmp( sibling->name, property ) ) 46 | return sibling; 47 | return 0; 48 | } 49 | 50 | /* Search a property by its name in a JSON object and return its value. */ 51 | char const* json_getPropertyValue( json_t const* obj, char const* property ) { 52 | json_t const* field = json_getProperty( obj, property ); 53 | if ( !field ) return 0; 54 | jsonType_t type = json_getType( field ); 55 | if ( JSON_ARRAY >= type ) return 0; 56 | return json_getValue( field ); 57 | } 58 | 59 | /* Internal prototypes: */ 60 | static char* goBlank( char* str ); 61 | static char* goNum( char* str ); 62 | static json_t* poolInit( jsonPool_t* pool ); 63 | static json_t* poolNew( jsonPool_t* pool ); 64 | static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ); 65 | static char* setToNull( char* ch ); 66 | static bool isEndOfPrimitive( char ch ); 67 | 68 | /* Parse a string to get a json. */ 69 | json_t const* json_create( char* str, json_t mem[], unsigned int qty ) { 70 | char* ptr = goBlank( str ); 71 | if ( !ptr || *ptr != '{' ) return 0; 72 | jsonPool_t pool = { .mem = mem, .qty = qty }; 73 | json_t* obj = poolInit( &pool ); 74 | obj->name = 0; 75 | obj->sibling = 0; 76 | obj->u.c.child = 0; 77 | ptr = objValue( ptr, obj, &pool ); 78 | if ( !ptr ) return 0; 79 | return obj; 80 | } 81 | 82 | /** Get a special character with its escape character. Examples: 83 | * 'b' -> '\b', 'n' -> '\n', 't' -> '\t' 84 | * @param ch The escape character. 85 | * @return The character code. */ 86 | static char getEscape( char ch ) { 87 | static struct { char ch; char code; } const pair[] = { 88 | { '\"', '\"' }, { '\\', '\\' }, 89 | { '/', '/' }, { 'b', '\b' }, 90 | { 'f', '\f' }, { 'n', '\n' }, 91 | { 'r', '\r' }, { 't', '\t' }, 92 | }; 93 | unsigned int i; 94 | for( i = 0; i < sizeof pair / sizeof *pair; ++i ) 95 | if ( pair[i].ch == ch ) 96 | return pair[i].code; 97 | return '\0'; 98 | } 99 | 100 | /** Parse 4 characters. 101 | * @Param str Pointer to first digit. 102 | * @retval '?' If the four characters are hexadecimal digits. 103 | * @retcal '\0' In other cases. */ 104 | static unsigned char getCharFromUnicode( unsigned char const* str ) { 105 | unsigned int i; 106 | for( i = 0; i < 4; ++i ) 107 | if ( !isxdigit( str[i] ) ) 108 | return '\0'; 109 | return '?'; 110 | } 111 | 112 | /** Parse a string and replace the scape characters by their meaning characters. 113 | * This parser stops when finds the character '\"'. Then replaces '\"' by '\0'. 114 | * @param str Pointer to first character. 115 | * @retval Pointer to first non white space after the string. If success. 116 | * @retval Null pointer if any error occur. */ 117 | static char* parseString( char* str ) { 118 | unsigned char* head = (unsigned char*)str; 119 | unsigned char* tail = (unsigned char*)str; 120 | for( ; *head >= ' '; ++head, ++tail ) { 121 | if ( *head == '\"' ) { 122 | *tail = '\0'; 123 | return (char*)++head; 124 | } 125 | if ( *head == '\\' ) { 126 | if ( *++head == 'u' ) { 127 | char const ch = getCharFromUnicode( ++head ); 128 | if ( ch == '\0' ) return 0; 129 | *tail = ch; 130 | head += 3; 131 | } 132 | else { 133 | char const esc = getEscape( *head ); 134 | if ( esc == '\0' ) return 0; 135 | *tail = esc; 136 | } 137 | } 138 | else *tail = *head; 139 | } 140 | return 0; 141 | } 142 | 143 | /** Parse a string to get the name of a property. 144 | * @param str Pointer to first character. 145 | * @param property The property to assign the name. 146 | * @retval Pointer to first of property value. If success. 147 | * @retval Null pointer if any error occur. */ 148 | static char* propertyName( char* ptr, json_t* property ) { 149 | property->name = ++ptr; 150 | ptr = parseString( ptr ); 151 | if ( !ptr ) return 0; 152 | ptr = goBlank( ptr ); 153 | if ( !ptr ) return 0; 154 | if ( *ptr++ != ':' ) return 0; 155 | return goBlank( ptr ); 156 | } 157 | 158 | /** Parse a string to get the value of a property when its type is JSON_TEXT. 159 | * @param str Pointer to first character ('\"'). 160 | * @param property The property to assign the name. 161 | * @retval Pointer to first non white space after the string. If success. 162 | * @retval Null pointer if any error occur. */ 163 | static char* textValue( char* ptr, json_t* property ) { 164 | ++property->u.value; 165 | ptr = parseString( ++ptr ); 166 | if ( !ptr ) return 0; 167 | property->type = JSON_TEXT; 168 | return ptr; 169 | } 170 | 171 | /** Compare two strings until get the null character in the second one. 172 | * @param ptr sub string 173 | * @param str main string 174 | * @retval Pointer to next character. 175 | * @retval Null pointer if any error occur. */ 176 | static char* checkStr( char* ptr, char const* str ) { 177 | while( *str ) 178 | if ( *ptr++ != *str++ ) 179 | return 0; 180 | return ptr; 181 | } 182 | 183 | /** Parser a string to get a primitive value. 184 | * If the first character after the value is different of '}' or ']' is set to '\0'. 185 | * @param str Pointer to first character. 186 | * @param property Property handler to set the value and the type, (true, false or null). 187 | * @param value String with the primitive literal. 188 | * @param type The code of the type. ( JSON_BOOLEAN or JSON_NULL ) 189 | * @retval Pointer to first non white space after the string. If success. 190 | * @retval Null pointer if any error occur. */ 191 | static char* primitiveValue( char* ptr, json_t* property, char const* value, jsonType_t type ) { 192 | ptr = checkStr( ptr, value ); 193 | if ( !ptr || !isEndOfPrimitive( *ptr ) ) return 0; 194 | ptr = setToNull( ptr ); 195 | property->type = type; 196 | return ptr; 197 | } 198 | 199 | /** Parser a string to get a true value. 200 | * If the first character after the value is different of '}' or ']' is set to '\0'. 201 | * @param str Pointer to first character. 202 | * @param property Property handler to set the value and the type, (true, false or null). 203 | * @retval Pointer to first non white space after the string. If success. 204 | * @retval Null pointer if any error occur. */ 205 | static char* trueValue( char* ptr, json_t* property ) { 206 | return primitiveValue( ptr, property, "true", JSON_BOOLEAN ); 207 | } 208 | 209 | /** Parser a string to get a false value. 210 | * If the first character after the value is different of '}' or ']' is set to '\0'. 211 | * @param str Pointer to first character. 212 | * @param property Property handler to set the value and the type, (true, false or null). 213 | * @retval Pointer to first non white space after the string. If success. 214 | * @retval Null pointer if any error occur. */ 215 | static char* falseValue( char* ptr, json_t* property ) { 216 | return primitiveValue( ptr, property, "false", JSON_BOOLEAN ); 217 | } 218 | 219 | /** Parser a string to get a null value. 220 | * If the first character after the value is different of '}' or ']' is set to '\0'. 221 | * @param str Pointer to first character. 222 | * @param property Property handler to set the value and the type, (true, false or null). 223 | * @retval Pointer to first non white space after the string. If success. 224 | * @retval Null pointer if any error occur. */ 225 | static char* nullValue( char* ptr, json_t* property ) { 226 | return primitiveValue( ptr, property, "null", JSON_NULL ); 227 | } 228 | 229 | /** Analyze the exponential part of a real number. 230 | * @param str Pointer to first character. 231 | * @retval Pointer to first non numerical after the string. If success. 232 | * @retval Null pointer if any error occur. */ 233 | static char* expValue( char* ptr ) { 234 | if ( *ptr == '-' || *ptr == '+' ) ++ptr; 235 | if ( !isdigit( *ptr ) ) return 0; 236 | ptr = goNum( ++ptr ); 237 | return ptr; 238 | } 239 | 240 | /** Analyze the decimal part of a real number. 241 | * @param str Pointer to first character. 242 | * @retval Pointer to first non numerical after the string. If success. 243 | * @retval Null pointer if any error occur. */ 244 | static char* fraqValue( char* ptr ) { 245 | if ( !isdigit( *ptr ) ) return 0; 246 | ptr = goNum( ++ptr ); 247 | if ( !ptr ) return 0; 248 | return ptr; 249 | } 250 | 251 | /** Parser a string to get a numerical value. 252 | * If the first character after the value is different of '}' or ']' is set to '\0'. 253 | * @param str Pointer to first character. 254 | * @param property Property handler to set the value and the type: JSON_REAL or JSON_INTEGER. 255 | * @retval Pointer to first non white space after the string. If success. 256 | * @retval Null pointer if any error occur. */ 257 | static char* numValue( char* ptr, json_t* property ) { 258 | if ( *ptr == '-' ) ++ptr; 259 | if ( !isdigit( *ptr ) ) return 0; 260 | if ( *ptr != '0' ) { 261 | ptr = goNum( ptr ); 262 | if ( !ptr ) return 0; 263 | } 264 | else if ( isdigit( *++ptr ) ) return 0; 265 | property->type = JSON_INTEGER; 266 | if ( *ptr == '.' ) { 267 | ptr = fraqValue( ++ptr ); 268 | if ( !ptr ) return 0; 269 | property->type = JSON_REAL; 270 | } 271 | if ( *ptr == 'e' || *ptr == 'E' ) { 272 | ptr = expValue( ++ptr ); 273 | if ( !ptr ) return 0; 274 | property->type = JSON_REAL; 275 | } 276 | if ( !isEndOfPrimitive( *ptr ) ) return 0; 277 | if ( JSON_INTEGER == property->type ) { 278 | char const* value = property->u.value; 279 | bool const negative = *value == '-'; 280 | static char const min[] = "-9223372036854775808"; 281 | static char const max[] = "9223372036854775807"; 282 | unsigned int const maxdigits = ( negative? sizeof min: sizeof max ) - 1; 283 | unsigned int const len = ptr - value; 284 | if ( len > maxdigits ) return 0; 285 | if ( len == maxdigits ) { 286 | char const tmp = *ptr; 287 | *ptr = '\0'; 288 | char const* const threshold = negative ? min: max; 289 | if ( 0 > strcmp( threshold, value ) ) return 0; 290 | *ptr = tmp; 291 | } 292 | } 293 | ptr = setToNull( ptr ); 294 | return ptr; 295 | } 296 | 297 | /** Add a property to a JSON object or array. 298 | * @param obj The handler of the JSON object or array. 299 | * @param property The handler of the property to be added. */ 300 | static void add( json_t* obj, json_t* property ) { 301 | property->sibling = 0; 302 | if ( !obj->u.c.child ){ 303 | obj->u.c.child = property; 304 | obj->u.c.last_child = property; 305 | } else { 306 | obj->u.c.last_child->sibling = property; 307 | obj->u.c.last_child = property; 308 | } 309 | } 310 | 311 | /** Parser a string to get a json object value. 312 | * @param str Pointer to first character. 313 | * @param pool The handler of a json pool for creating json instances. 314 | * @retval Pointer to first character after the value. If success. 315 | * @retval Null pointer if any error occur. */ 316 | static char* objValue( char* ptr, json_t* obj, jsonPool_t* pool ) { 317 | obj->type = JSON_OBJ; 318 | obj->u.c.child = 0; 319 | obj->sibling = 0; 320 | ptr++; 321 | for(;;) { 322 | ptr = goBlank( ptr ); 323 | if ( !ptr ) return 0; 324 | if ( *ptr == ',' ) { 325 | ++ptr; 326 | continue; 327 | } 328 | char const endchar = ( obj->type == JSON_OBJ )? '}': ']'; 329 | if ( *ptr == endchar ) { 330 | *ptr = '\0'; 331 | json_t* parentObj = obj->sibling; 332 | if ( !parentObj ) return ++ptr; 333 | obj->sibling = 0; 334 | obj = parentObj; 335 | ++ptr; 336 | continue; 337 | } 338 | json_t* property = poolNew( pool ); 339 | if ( !property ) return 0; 340 | if( obj->type != JSON_ARRAY ) { 341 | if ( *ptr != '\"' ) return 0; 342 | ptr = propertyName( ptr, property ); 343 | if ( !ptr ) return 0; 344 | } 345 | else property->name = 0; 346 | add( obj, property ); 347 | property->u.value = ptr; 348 | switch( *ptr ) { 349 | case '{': 350 | property->type = JSON_OBJ; 351 | property->u.c.child = 0; 352 | property->sibling = obj; 353 | obj = property; 354 | ++ptr; 355 | break; 356 | case '[': 357 | property->type = JSON_ARRAY; 358 | property->u.c.child = 0; 359 | property->sibling = obj; 360 | obj = property; 361 | ++ptr; 362 | break; 363 | case '\"': ptr = textValue( ptr, property ); break; 364 | case 't': ptr = trueValue( ptr, property ); break; 365 | case 'f': ptr = falseValue( ptr, property ); break; 366 | case 'n': ptr = nullValue( ptr, property ); break; 367 | default: ptr = numValue( ptr, property ); break; 368 | } 369 | if ( !ptr ) return 0; 370 | } 371 | } 372 | 373 | /** Initialize a json pool. 374 | * @param pool The handler of the pool. 375 | * @return a instance of a json. */ 376 | static json_t* poolInit( jsonPool_t* pool ) { 377 | pool->nextFree = 1; 378 | return &pool->mem[0]; 379 | } 380 | 381 | /** Create an instance of a json from a pool. 382 | * @param pool The handler of the pool. 383 | * @retval The handler of the new instance if success. 384 | * @retval Null pointer if the pool was empty. */ 385 | static json_t* poolNew( jsonPool_t* pool ) { 386 | if ( pool->nextFree >= pool->qty ) return 0; 387 | return &pool->mem[pool->nextFree++]; 388 | } 389 | 390 | /** Checks whether an character belongs to set. 391 | * @param ch Character value to be checked. 392 | * @param set Set of characters. It is just a null-terminated string. 393 | * @return true or false there is membership or not. */ 394 | static bool isOneOfThem( char ch, char const* set ) { 395 | while( *set != '\0' ) 396 | if ( ch == *set++ ) 397 | return true; 398 | return false; 399 | } 400 | 401 | /** Increases a pointer while it points to a character that belongs to a set. 402 | * @param str The initial pointer value. 403 | * @param set Set of characters. It is just a null-terminated string. 404 | * @return The final pointer value or null pointer if the null character was found. */ 405 | static char* goWhile( char* str, char const* set ) { 406 | for(; *str != '\0'; ++str ) { 407 | if ( !isOneOfThem( *str, set ) ) 408 | return str; 409 | } 410 | return 0; 411 | } 412 | 413 | /** Set of characters that defines a blank. */ 414 | static char const* const blank = " \n\r\t\f"; 415 | 416 | /** Increases a pointer while it points to a white space character. 417 | * @param str The initial pointer value. 418 | * @return The final pointer value or null pointer if the null character was found. */ 419 | static char* goBlank( char* str ) { 420 | return goWhile( str, blank ); 421 | } 422 | 423 | /** Increases a pointer while it points to a decimal digit character. 424 | * @param str The initial pointer value. 425 | * @return The final pointer value or null pointer if the null character was found. */ 426 | static char* goNum( char* str ) { 427 | for( ; *str != '\0'; ++str ) { 428 | if ( !isdigit( *str ) ) 429 | return str; 430 | } 431 | return 0; 432 | } 433 | 434 | /** Set of characters that defines the end of an array or a JSON object. */ 435 | static char const* const endofblock = "}]"; 436 | 437 | /** Set a char to '\0' and increase its pointer if the char is different to '}' or ']'. 438 | * @param ch Pointer to character. 439 | * @return Final value pointer. */ 440 | static char* setToNull( char* ch ) { 441 | if ( !isOneOfThem( *ch, endofblock ) ) *ch++ = '\0'; 442 | return ch; 443 | } 444 | 445 | /** Indicate if a character is the end of a primitive value. */ 446 | static bool isEndOfPrimitive( char ch ) { 447 | return ch == ',' || isOneOfThem( ch, blank ) || isOneOfThem( ch, endofblock ); 448 | } 449 | -------------------------------------------------------------------------------- /RPI/installer.c: -------------------------------------------------------------------------------- 1 | #include "installer.h" 2 | #include "pkg.h" 3 | #include "util.h" 4 | #include "module.h" 5 | #include "KPutil.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // define them here and import the address later 14 | int(*sceBgftDebugDownloadRegisterPkg)(OrbisBgftDownloadParam* params, OrbisBgftTaskId* taskId); 15 | int(*sceBgftDownloadGetProgress)(OrbisBgftTaskId taskId, OrbisBgftTaskProgress* progress); 16 | int(*sceBgftDownloadPauseTask)(OrbisBgftTaskId taskId); 17 | int(*sceBgftDownloadRegisterTask)(OrbisBgftDownloadParam* params, OrbisBgftTaskId* taskId); 18 | int(*sceBgftDownloadReregisterTaskPatch)(OrbisBgftTaskId oldTaskId, OrbisBgftTaskId* newTaskId); 19 | int(*sceBgftDownloadResumeTask)(OrbisBgftTaskId taskId); 20 | int(*sceBgftDownloadStartTask)(OrbisBgftTaskId taskId); 21 | int(*sceBgftDownloadStopTask)(OrbisBgftTaskId taskId); 22 | int(*sceBgftDownloadUnregisterTask)(OrbisBgftTaskId taskId); 23 | int(*sceBgftFinalize)(); 24 | int(*sceBgftInitialize)(OrbisBgftInitParams* params); 25 | 26 | #define _NDBG 27 | #define BGFT_HEAP_SIZE (1 * 1024 * 1024) 28 | #define WAIT_TIME (UINT64_C(5) * 1000 * 1000) /* 5 secs */ 29 | 30 | static OrbisBgftInitParams s_bgft_init_params; 31 | 32 | static bool s_app_inst_util_initialized = false; 33 | static bool s_bgft_initialized = false; 34 | 35 | static bool modify_download_task_for_patch_internal(const char* path, int index); 36 | static bool modify_download_task_for_patch(OrbisBgftTaskId task_id); 37 | 38 | bool app_inst_util_init(void) { 39 | int ret; 40 | 41 | if (s_app_inst_util_initialized) { 42 | goto done; 43 | } 44 | 45 | ret = sceAppInstUtilInitialize(); 46 | if (ret) { 47 | EPRINTF("sceAppInstUtilInitialize failed: 0x%08X\n", ret); 48 | goto err; 49 | } 50 | 51 | s_app_inst_util_initialized = true; 52 | 53 | done: 54 | return true; 55 | 56 | err: 57 | s_app_inst_util_initialized = false; 58 | 59 | return false; 60 | } 61 | 62 | void app_inst_util_fini(void) { 63 | int ret; 64 | 65 | if (!s_app_inst_util_initialized) { 66 | return; 67 | } 68 | 69 | ret = sceAppInstUtilTerminate(); 70 | if (ret) { 71 | EPRINTF("sceAppInstUtilTerminate failed: 0x%08X\n", ret); 72 | } 73 | 74 | s_app_inst_util_initialized = false; 75 | } 76 | 77 | bool app_inst_util_uninstall_game(const char* title_id, int* error) { 78 | int ret; 79 | 80 | if (!s_app_inst_util_initialized) { 81 | ret = ORBIS_KERNEL_ERROR_ENXIO; 82 | if (error) { 83 | *error = ret; 84 | } 85 | goto err; 86 | } 87 | 88 | if (!title_id) { 89 | ret = ORBIS_KERNEL_ERROR_EINVAL; 90 | if (error) { 91 | *error = ret; 92 | } 93 | goto err; 94 | } 95 | 96 | ret = sceAppInstUtilAppUnInstall(title_id); 97 | if (ret) { 98 | if (error) { 99 | *error = ret; 100 | } 101 | EPRINTF("sceAppInstUtilAppUnInstall failed: 0x%08X\n", ret); 102 | goto err; 103 | } 104 | 105 | return true; 106 | 107 | err: 108 | return false; 109 | } 110 | 111 | bool app_inst_util_uninstall_ac(const char* content_id, int* error) { 112 | struct pkg_content_info content_info; 113 | int ret; 114 | 115 | if (!s_app_inst_util_initialized) { 116 | ret = ORBIS_KERNEL_ERROR_ENXIO; 117 | if (error) { 118 | *error = ret; 119 | } 120 | goto err; 121 | } 122 | 123 | if (!content_id) { 124 | invalid_content_id: 125 | ret = ORBIS_KERNEL_ERROR_EINVAL; 126 | if (error) { 127 | *error = ret; 128 | } 129 | goto err; 130 | } 131 | if (!pkg_parse_content_id(content_id, &content_info)) { 132 | goto invalid_content_id; 133 | } 134 | 135 | ret = sceAppInstUtilAppUnInstallAddcont(content_info.title_id, content_info.label); 136 | if (ret) { 137 | if (error) { 138 | *error = ret; 139 | } 140 | EPRINTF("sceAppInstUtilAppUnInstallAddcont failed: 0x%08X\n", ret); 141 | goto err; 142 | } 143 | 144 | done: 145 | return true; 146 | 147 | err: 148 | return false; 149 | } 150 | 151 | bool app_inst_util_uninstall_patch(const char* title_id, int* error) { 152 | int ret; 153 | 154 | if (!s_app_inst_util_initialized) { 155 | ret = ORBIS_KERNEL_ERROR_ENXIO; 156 | if (error) { 157 | *error = ret; 158 | } 159 | goto err; 160 | } 161 | 162 | if (!title_id) { 163 | ret = ORBIS_KERNEL_ERROR_EINVAL; 164 | if (error) { 165 | *error = ret; 166 | } 167 | goto err; 168 | } 169 | 170 | ret = sceAppInstUtilAppUnInstallPat(title_id); 171 | if (ret) { 172 | if (error) { 173 | *error = ret; 174 | } 175 | EPRINTF("sceAppInstUtilAppUnInstallPat failed: 0x%08X\n", ret); 176 | goto err; 177 | } 178 | 179 | return true; 180 | 181 | err: 182 | return false; 183 | } 184 | 185 | bool app_inst_util_uninstall_theme(const char* content_id, int* error) { 186 | int ret; 187 | 188 | if (!s_app_inst_util_initialized) { 189 | ret = ORBIS_KERNEL_ERROR_ENXIO; 190 | if (error) { 191 | *error = ret; 192 | } 193 | goto err; 194 | } 195 | 196 | if (!content_id) { 197 | ret = ORBIS_KERNEL_ERROR_EINVAL; 198 | if (error) { 199 | *error = ret; 200 | } 201 | goto err; 202 | } 203 | 204 | ret = sceAppInstUtilAppUnInstallTheme(content_id); 205 | if (ret) { 206 | if (error) { 207 | *error = ret; 208 | } 209 | EPRINTF("sceAppInstUtilAppUnInstallTheme failed: 0x%08X\n", ret); 210 | goto err; 211 | } 212 | 213 | return true; 214 | 215 | err: 216 | return false; 217 | } 218 | 219 | bool app_inst_util_is_exists(const char* title_id, bool* exists, int* error) { 220 | int flag; 221 | int ret; 222 | 223 | if (!s_app_inst_util_initialized) { 224 | ret = ORBIS_KERNEL_ERROR_ENXIO; 225 | if (error) { 226 | *error = ret; 227 | } 228 | goto err; 229 | } 230 | 231 | if (!title_id) { 232 | ret = ORBIS_KERNEL_ERROR_EINVAL; 233 | if (error) { 234 | *error = ret; 235 | } 236 | goto err; 237 | } 238 | 239 | ret = sceAppInstUtilAppExists(title_id, &flag); 240 | if (ret) { 241 | if (error) { 242 | *error = ret; 243 | } 244 | EPRINTF("sceAppInstUtilAppExists failed: 0x%08X\n", ret); 245 | goto err; 246 | } 247 | 248 | if (exists) { 249 | *exists = flag; 250 | } 251 | 252 | return true; 253 | 254 | err: 255 | return false; 256 | } 257 | 258 | bool app_inst_util_get_size(const char* title_id, unsigned long* size, int* error) { 259 | int ret; 260 | 261 | if (!s_app_inst_util_initialized) { 262 | ret = ORBIS_KERNEL_ERROR_ENXIO; 263 | if (error) { 264 | *error = ret; 265 | } 266 | goto err; 267 | } 268 | 269 | if (!title_id) { 270 | ret = ORBIS_KERNEL_ERROR_EINVAL; 271 | if (error) { 272 | *error = ret; 273 | } 274 | goto err; 275 | } 276 | 277 | ret = sceAppInstUtilAppGetSize(title_id, size); 278 | if (ret) { 279 | if (error) { 280 | *error = ret; 281 | } 282 | EPRINTF("sceAppInstUtilAppGetSize failed: 0x%08X\n", ret); 283 | goto err; 284 | } 285 | 286 | return true; 287 | 288 | err: 289 | return false; 290 | } 291 | 292 | bool bgft_init(void) 293 | { 294 | int ret; 295 | int32_t handle = sceKernelLoadStartModule("/system/common/lib/libSceBgft.sprx", NULL, NULL, NULL, NULL, NULL); 296 | KernelPrintOut("sceKernelLoadStartModule returned 0x%lx\n", handle); 297 | 298 | if (handle > 0) 299 | { 300 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDebugDownloadRegisterPkg", (void**)&sceBgftDebugDownloadRegisterPkg) != 0)) 301 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 302 | 303 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadGetProgress", (void**)&sceBgftDownloadGetProgress) != 0)) 304 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 305 | 306 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadPauseTask", (void**)&sceBgftDownloadPauseTask) != 0)) 307 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 308 | 309 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceDownloadRegisterTask", (void**)&sceBgftDownloadRegisterTask) != 0)) 310 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 311 | 312 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadReregisterTaskPatch", (void**)&sceBgftDownloadReregisterTaskPatch) != 0)) 313 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 314 | 315 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadResumeTask", (void**)&sceBgftDownloadResumeTask) != 0)) 316 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 317 | 318 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadStartTask", (void**)&sceBgftDownloadStartTask) != 0)) 319 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 320 | 321 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadStopTask", (void**)&sceBgftDownloadStopTask) != 0)) 322 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 323 | 324 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceIntDownloadUnregisterTask", (void**)&sceBgftDownloadUnregisterTask) != 0)) 325 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 326 | 327 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceTerm", (void**)&sceBgftFinalize) != 0)) 328 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 329 | 330 | if ((ret = sceKernelDlsym(handle, "sceBgftServiceInit", (void**)&sceBgftInitialize)!= 0)) 331 | SafeExit("sceKernelDlsym returned 0x%lx\n", ret); 332 | 333 | } 334 | else 335 | { 336 | SafeExit("Failed to load libSceBgft 0x%lx\n", handle); 337 | } 338 | 339 | if (s_bgft_initialized) { 340 | goto done; 341 | } 342 | 343 | memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); 344 | { 345 | s_bgft_init_params.heapSize = BGFT_HEAP_SIZE; 346 | s_bgft_init_params.heap = (uint8_t*)malloc(s_bgft_init_params.heapSize); 347 | if (!s_bgft_init_params.heap) { 348 | EPRINTF("No memory for BGFT heap.\n"); 349 | goto err; 350 | } 351 | memset(s_bgft_init_params.heap, 0, s_bgft_init_params.heapSize); 352 | } 353 | 354 | ret = sceBgftInitialize(&s_bgft_init_params); 355 | if (ret) { 356 | EPRINTF("sceBgftInitialize failed: 0x%08X\n", ret); 357 | goto err_bgft_heap_free; 358 | } 359 | 360 | s_bgft_initialized = true; 361 | 362 | done: 363 | return true; 364 | 365 | err_bgft_heap_free: 366 | if (s_bgft_init_params.heap) { 367 | free(s_bgft_init_params.heap); 368 | s_bgft_init_params.heap = NULL; 369 | } 370 | 371 | memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); 372 | 373 | err: 374 | s_bgft_initialized = false; 375 | 376 | return false; 377 | 378 | } 379 | 380 | void bgft_fini(void) { 381 | int ret; 382 | 383 | if (!s_bgft_initialized) { 384 | return; 385 | } 386 | 387 | ret = sceBgftFinalize(); 388 | if (ret) { 389 | EPRINTF("sceBgftFinalize failed: 0x%08X\n", ret); 390 | } 391 | 392 | if (s_bgft_init_params.heap) { 393 | free(s_bgft_init_params.heap); 394 | s_bgft_init_params.heap = NULL; 395 | } 396 | 397 | memset(&s_bgft_init_params, 0, sizeof(s_bgft_init_params)); 398 | 399 | s_bgft_initialized = false; 400 | } 401 | 402 | bool bgft_download_register_package_task(const char* content_id, const char* content_url, const char* content_name, const char* icon_path, const char* package_type, const char* package_sub_type, unsigned long package_size, bool is_patch, int* out_task_id, int* error) 403 | { 404 | OrbisBgftDownloadParam params; 405 | OrbisBgftDownloadRegisterErrorInfo error_info; 406 | OrbisBgftTaskId task_id; 407 | struct pkg_content_info content_info; 408 | int user_id; 409 | int ret; 410 | 411 | if (!s_app_inst_util_initialized) { 412 | ret = ORBIS_KERNEL_ERROR_ENXIO; 413 | if (error) { 414 | *error = ret; 415 | } 416 | goto err; 417 | } 418 | if (!s_bgft_initialized) { 419 | ret = ORBIS_KERNEL_ERROR_ENXIO; 420 | if (error) { 421 | *error = ret; 422 | } 423 | goto err; 424 | } 425 | 426 | if (!pkg_parse_content_id(content_id, &content_info)) { 427 | ret = ORBIS_KERNEL_ERROR_EINVAL; 428 | if (error) { 429 | *error = ret; 430 | } 431 | goto err; 432 | } 433 | 434 | ret = sceUserServiceGetForegroundUser(&user_id); 435 | if (ret) { 436 | EPRINTF("sceUserServiceGetForegroundUser failed: 0x%08X\n", ret); 437 | goto err; 438 | } 439 | 440 | memset(&error_info, 0, sizeof(error_info)); 441 | 442 | memset(¶ms, 0, sizeof(params)); 443 | { 444 | params.entitlementType = 5; /* TODO: figure out */ 445 | params.userId = user_id; 446 | params.id = content_id; 447 | params.contentUrl = content_url; 448 | params.contentName = content_name; 449 | params.iconPath = icon_path ? icon_path : ""; 450 | params.playgoScenarioId = "0"; 451 | params.option = ORBIS_BGFT_TASK_OPT_DISABLE_CDN_QUERY_PARAM; 452 | params.packageType = package_type; 453 | params.packageSubType = package_sub_type ? package_sub_type : ""; 454 | params.packageSize = package_size; 455 | } 456 | 457 | task_id = ORBIS_BGFT_INVALID_TASK_ID; 458 | 459 | if (!is_patch) { 460 | ret = sceBgftDownloadRegisterTask(¶ms, &task_id); 461 | //ret = sceBgftDownloadRegisterTaskStoreWithErrorInfo(¶ms, &task_id, &error_info); 462 | } 463 | else { 464 | ret = sceBgftDebugDownloadRegisterPkg(¶ms, &task_id); 465 | } 466 | if (ret) { 467 | if (error) { 468 | *error = ret; 469 | } 470 | if (ret == ORBIS_BGFT_ERROR_SAME_APPLICATION_ALREADY_INSTALLED) { 471 | task_id = -1; 472 | //printf("Package already installed.\n"); 473 | goto done; 474 | } 475 | EPRINTF("sceBgftDownloadRegisterTask failed: 0x%08X\n", ret); 476 | goto err; 477 | } 478 | 479 | done: 480 | if (out_task_id) { 481 | *out_task_id = (int)task_id; 482 | } 483 | 484 | return true; 485 | 486 | err: 487 | return false; 488 | } 489 | 490 | bool bgft_download_start_task(int task_id, int* error) { 491 | int ret; 492 | 493 | if (!s_bgft_initialized) { 494 | ret = ORBIS_KERNEL_ERROR_ENXIO; 495 | if (error) { 496 | *error = ret; 497 | } 498 | goto err; 499 | } 500 | 501 | if (task_id < 0) { 502 | ret = ORBIS_KERNEL_ERROR_EINVAL; 503 | if (error) { 504 | *error = ret; 505 | } 506 | goto err; 507 | } 508 | 509 | ret = sceBgftDownloadStartTask((OrbisBgftTaskId)task_id); 510 | if (ret) { 511 | if (error) { 512 | *error = ret; 513 | } 514 | EPRINTF("sceBgftDownloadStartTask failed: 0x%08X\n", ret); 515 | goto err; 516 | } 517 | 518 | return true; 519 | 520 | err: 521 | return false; 522 | } 523 | 524 | bool bgft_download_stop_task(int task_id, int* error) { 525 | int ret; 526 | 527 | if (!s_bgft_initialized) { 528 | ret = ORBIS_KERNEL_ERROR_ENXIO; 529 | if (error) { 530 | *error = ret; 531 | } 532 | goto err; 533 | } 534 | 535 | if (task_id < 0) { 536 | ret = ORBIS_KERNEL_ERROR_EINVAL; 537 | if (error) { 538 | *error = ret; 539 | } 540 | goto err; 541 | } 542 | 543 | ret = sceBgftDownloadStopTask((OrbisBgftTaskId)task_id); 544 | if (ret) { 545 | if (error) { 546 | *error = ret; 547 | } 548 | EPRINTF("sceBgftDownloadStopTask failed: 0x%08X\n", ret); 549 | goto err; 550 | } 551 | 552 | return true; 553 | 554 | err: 555 | return false; 556 | } 557 | 558 | bool bgft_download_pause_task(int task_id, int* error) { 559 | int ret; 560 | 561 | if (!s_bgft_initialized) { 562 | ret = ORBIS_KERNEL_ERROR_ENXIO; 563 | if (error) { 564 | *error = ret; 565 | } 566 | goto err; 567 | } 568 | 569 | if (task_id < 0) { 570 | ret = ORBIS_KERNEL_ERROR_EINVAL; 571 | if (error) { 572 | *error = ret; 573 | } 574 | goto err; 575 | } 576 | 577 | ret = sceBgftDownloadPauseTask((OrbisBgftTaskId)task_id); 578 | if (ret) { 579 | if (error) { 580 | *error = ret; 581 | } 582 | EPRINTF("sceBgftDownloadPauseTask failed: 0x%08X\n", ret); 583 | goto err; 584 | } 585 | 586 | return true; 587 | 588 | err: 589 | return false; 590 | } 591 | 592 | bool bgft_download_resume_task(int task_id, int* error) { 593 | int ret; 594 | 595 | if (!s_bgft_initialized) { 596 | ret = ORBIS_KERNEL_ERROR_ENXIO; 597 | if (error) { 598 | *error = ret; 599 | } 600 | goto err; 601 | } 602 | 603 | if (task_id < 0) { 604 | ret = ORBIS_KERNEL_ERROR_EINVAL; 605 | if (error) { 606 | *error = ret; 607 | } 608 | goto err; 609 | } 610 | 611 | ret = sceBgftDownloadResumeTask((OrbisBgftTaskId)task_id); 612 | if (ret) { 613 | if (error) { 614 | *error = ret; 615 | } 616 | EPRINTF("sceBgftDownloadResumeTask failed: 0x%08X\n", ret); 617 | goto err; 618 | } 619 | 620 | return true; 621 | 622 | err: 623 | return false; 624 | } 625 | 626 | bool bgft_download_unregister_task(int task_id, int* error) { 627 | int ret; 628 | 629 | if (!s_bgft_initialized) { 630 | ret = ORBIS_KERNEL_ERROR_ENXIO; 631 | if (error) { 632 | *error = ret; 633 | } 634 | goto err; 635 | } 636 | 637 | if (task_id < 0) { 638 | ret = ORBIS_KERNEL_ERROR_EINVAL; 639 | if (error) { 640 | *error = ret; 641 | } 642 | goto err; 643 | } 644 | 645 | ret = sceBgftDownloadUnregisterTask((OrbisBgftTaskId)task_id); 646 | if (ret) { 647 | if (error) { 648 | *error = ret; 649 | } 650 | EPRINTF("sceBgftDownloadUnregisterTask failed: 0x%08X\n", ret); 651 | goto err; 652 | } 653 | 654 | return true; 655 | 656 | err: 657 | return false; 658 | } 659 | 660 | bool bgft_download_reregister_task_patch(int old_task_id, int* new_task_id, int* error) { 661 | OrbisBgftTaskId tmp_id; 662 | int ret; 663 | 664 | if (!s_bgft_initialized) { 665 | ret = ORBIS_KERNEL_ERROR_ENXIO; 666 | if (error) { 667 | *error = ret; 668 | } 669 | goto err; 670 | } 671 | 672 | if (old_task_id < 0) { 673 | ret = ORBIS_KERNEL_ERROR_EINVAL; 674 | if (error) { 675 | *error = ret; 676 | } 677 | goto err; 678 | } 679 | 680 | tmp_id = ORBIS_BGFT_INVALID_TASK_ID; 681 | ret = sceBgftDownloadReregisterTaskPatch((OrbisBgftTaskId)old_task_id, &tmp_id); 682 | if (ret) { 683 | if (error) { 684 | *error = ret; 685 | } 686 | EPRINTF("sceBgftDownloadReregisterTaskPatch failed: 0x%08X\n", ret); 687 | goto err; 688 | } 689 | 690 | if (new_task_id) { 691 | *new_task_id = (int)tmp_id; 692 | } 693 | 694 | return true; 695 | 696 | err: 697 | return false; 698 | } 699 | 700 | bool bgft_download_get_task_progress(int task_id, struct bgft_download_task_progress_info* progress_info, int* error) { 701 | OrbisBgftTaskProgress tmp_progress_info; 702 | int ret; 703 | 704 | if (!s_bgft_initialized) { 705 | ret = ORBIS_KERNEL_ERROR_ENXIO; 706 | if (error) { 707 | *error = ret; 708 | } 709 | goto err; 710 | } 711 | 712 | if (task_id < 0) { 713 | ret = ORBIS_KERNEL_ERROR_EINVAL; 714 | if (error) { 715 | *error = ret; 716 | } 717 | goto err; 718 | } 719 | if (!progress_info) { 720 | ret = ORBIS_KERNEL_ERROR_EINVAL; 721 | if (error) { 722 | *error = ret; 723 | } 724 | goto err; 725 | } 726 | 727 | memset(&tmp_progress_info, 0, sizeof(tmp_progress_info)); 728 | ret = sceBgftDownloadGetProgress((OrbisBgftTaskId)task_id, &tmp_progress_info); 729 | if (ret) { 730 | if (error) { 731 | *error = ret; 732 | } 733 | EPRINTF("sceBgftDownloadGetProgress failed: 0x%08X\n", ret); 734 | goto err; 735 | } 736 | 737 | memset(progress_info, 0, sizeof(*progress_info)); 738 | { 739 | progress_info->bits = tmp_progress_info.bits; 740 | progress_info->error_result = tmp_progress_info.errorResult; 741 | progress_info->length = tmp_progress_info.length; 742 | progress_info->transferred = tmp_progress_info.transferred; 743 | progress_info->length_total = tmp_progress_info.lengthTotal; 744 | progress_info->transferred_total = tmp_progress_info.transferredTotal; 745 | progress_info->num_index = tmp_progress_info.numIndex; 746 | progress_info->num_total = tmp_progress_info.numTotal; 747 | progress_info->rest_sec = tmp_progress_info.restSec; 748 | progress_info->rest_sec_total = tmp_progress_info.restSecTotal; 749 | progress_info->preparing_percent = tmp_progress_info.preparingPercent; 750 | progress_info->local_copy_percent = tmp_progress_info.localCopyPercent; 751 | } 752 | 753 | return true; 754 | 755 | err: 756 | return false; 757 | } 758 | 759 | bool bgft_download_find_task_by_content_id(const char* content_id, int sub_type, int* task_id, int* error) { 760 | OrbisBgftTaskId tmp_id; 761 | int ret; 762 | 763 | if (!s_bgft_initialized) { 764 | ret = ORBIS_KERNEL_ERROR_ENXIO; 765 | if (error) { 766 | *error = ret; 767 | } 768 | goto err; 769 | } 770 | 771 | if (!content_id) { 772 | ret = ORBIS_KERNEL_ERROR_EINVAL; 773 | if (error) { 774 | *error = ret; 775 | } 776 | goto err; 777 | } 778 | if (!((OrbisBgftTaskSubType)sub_type > ORBIS_BGFT_TASK_SUB_TYPE_UNKNOWN && (OrbisBgftTaskSubType)sub_type < ORBIS_BGFT_TASK_SUB_TYPE_MAX)) { 779 | ret = ORBIS_KERNEL_ERROR_EINVAL; 780 | if (error) { 781 | *error = ret; 782 | } 783 | goto err; 784 | } 785 | 786 | tmp_id = ORBIS_BGFT_INVALID_TASK_ID; 787 | ret = sceBgftServiceDownloadFindTaskByContentId(content_id, (OrbisBgftTaskSubType)sub_type, &tmp_id); 788 | if (ret) { 789 | if (error) { 790 | *error = ret; 791 | } 792 | EPRINTF("sceBgftServiceDownloadFindTaskByContentId failed: 0x%08X\n", ret); 793 | goto err; 794 | } 795 | 796 | if (task_id) { 797 | *task_id = (int)tmp_id; 798 | } 799 | 800 | return true; 801 | 802 | err: 803 | return false; 804 | } 805 | -------------------------------------------------------------------------------- /RPI/sandbird.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2016 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See LICENSE for details. 6 | */ 7 | 8 | 9 | #ifndef _POSIX_C_SOURCE 10 | #define _POSIX_C_SOURCE 200809L 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "sandbird.h" 29 | #include "KPutil.h" 30 | #include 31 | 32 | typedef int sb_Socket; 33 | #define INVALID_SOCKET -1 34 | 35 | typedef struct sb_Buffer sb_Buffer; 36 | 37 | struct sb_Buffer { char *s; size_t len, cap; }; 38 | 39 | struct sb_Stream { 40 | int state; /* Current state of the stream */ 41 | sb_Server *server; /* The server object which owns this stream */ 42 | char address[46]; /* Remote IP address */ 43 | time_t init_time; /* Time the stream was created */ 44 | time_t last_activity; /* Time of Last I/O activity on the stream */ 45 | size_t expected_recv_len; /* Expected length of the stream's request */ 46 | size_t data_idx; /* Index of data section in recv_buf */ 47 | sb_Socket sockfd; /* Socket for this streams connection */ 48 | sb_Buffer recv_buf; /* Data received from client */ 49 | sb_Buffer send_buf; /* Data waiting to be sent to client */ 50 | int send_fd; /* File descriptor currently being sent to client */ 51 | pthread_t thr; /* Processing thread */ 52 | sb_Stream *next; /* Next stream in linked list */ 53 | sb_Stream *prev; /* Previous stream in linked list */ 54 | }; 55 | 56 | struct sb_Server { 57 | sb_Stream *streams; /* Linked list of all streams */ 58 | sb_Handler handler; /* Event handler callback function */ 59 | sb_Socket sockfd; /* Listeneing server socket */ 60 | void *udata; /* User data value passed to all events */ 61 | time_t now; /* The current time */ 62 | time_t timeout; /* Stream no-activity timeout */ 63 | time_t max_lifetime; /* Maximum time a stream can exist */ 64 | size_t max_request_size; /* Maximum request size in bytes */ 65 | pthread_mutex_t stream_mtx; /* Mutex to lock stream access */ 66 | }; 67 | 68 | enum { 69 | STATE_RECEIVING_HEADER, 70 | STATE_RECEIVING_REQUEST, 71 | STATE_SENDING_STATUS, 72 | STATE_SENDING_HEADER, 73 | STATE_SENDING_DATA, 74 | STATE_SENDING_FILE, 75 | STATE_CLOSING 76 | }; 77 | 78 | 79 | /*=========================================================================== 80 | * Utility 81 | *===========================================================================*/ 82 | 83 | static int set_socket_blocking(sb_Socket sockfd, bool flag) { 84 | int flags; 85 | int ret; 86 | flags = fcntl(sockfd, F_GETFL); 87 | if (flags < 0) { 88 | return SB_EFAILURE; 89 | } 90 | flags &= ~O_NONBLOCK; 91 | if (!flag) { 92 | flags |= O_NONBLOCK; 93 | } 94 | ret = fcntl(sockfd, F_SETFL, flags); 95 | if (ret < 0) { 96 | return SB_EFAILURE; 97 | } 98 | return SB_ESUCCESS; 99 | } 100 | 101 | 102 | static int get_socket_address(sb_Socket sockfd, char *dst) { 103 | int err; 104 | union { struct sockaddr sa; 105 | struct sockaddr_storage sas; 106 | struct sockaddr_in sai; 107 | } addr; 108 | socklen_t sz = sizeof(addr); 109 | err = getpeername(sockfd, &addr.sa, &sz); 110 | if (err == -1) { 111 | *dst = '\0'; 112 | return SB_EFAILURE; 113 | } 114 | inet_ntop(AF_INET, &addr.sai.sin_addr, dst, INET_ADDRSTRLEN); 115 | return SB_ESUCCESS; 116 | } 117 | 118 | 119 | static unsigned str_to_uint(const char *str) { 120 | unsigned n; 121 | if (!str || sscanf(str, "%u", &n) != 1) return 0; 122 | return n; 123 | } 124 | 125 | 126 | static int hex_to_int(int chr) { 127 | return isdigit(chr) ? (chr - '0') : (tolower(chr) - 'a' + 10); 128 | } 129 | 130 | 131 | static int url_decode(char *dst, const char *src, size_t len) { 132 | len--; 133 | while (*src && !strchr("?& \t\r\n", *src) && len) { 134 | if (src[0] == '%' && src[1] && src[2]) { 135 | *dst = (hex_to_int(src[1]) << 4) | hex_to_int(src[2]); 136 | src += 2; 137 | } else if (*src == '+') { 138 | *dst = ' '; 139 | } else { 140 | *dst = *src; 141 | } 142 | dst++, src++, len--; 143 | } 144 | *dst = '\0'; 145 | return (len == 0) ? SB_ETRUNCATED : SB_ESUCCESS; 146 | } 147 | 148 | 149 | static int mem_equal(const void *a, const void *b, size_t len) { 150 | const char *p = a, *q = b; 151 | while (len) { 152 | if (*p != *q) return 0; 153 | p++, q++, len--; 154 | } 155 | return 1; 156 | } 157 | 158 | 159 | static int mem_case_equal(const void *a, const void *b, size_t len) { 160 | const char *p = a, *q = b; 161 | while (len) { 162 | if (tolower(*p) != tolower(*q)) return 0; 163 | p++, q++, len--; 164 | } 165 | return 1; 166 | } 167 | 168 | 169 | static const char *find_header_value(const char *str, const char *field) { 170 | size_t len = strlen(field); 171 | while (*str && !mem_equal(str, "\r\n", 2)) { 172 | if (mem_case_equal(str, field, len) && str[len] == ':') { 173 | str += len + 1; 174 | return str + strspn(str, " \t"); 175 | } 176 | str += strcspn(str, "\r"); 177 | str += mem_equal(str, "\r\n", 2) ? 2 : 0; 178 | } 179 | return NULL; 180 | } 181 | 182 | 183 | static const char *find_var_value(const char *str, const char *name) { 184 | size_t len = strlen(name); 185 | for (;;) { 186 | if (mem_equal(str, name, len) && str[len] == '=') { 187 | return str + len + 1; 188 | } 189 | str += strcspn(str, "& \t\r\n"); 190 | if (*str != '&') break; 191 | str++; 192 | } 193 | return NULL; 194 | } 195 | 196 | 197 | const char *sb_error_str(int code) { 198 | switch (code) { 199 | case SB_ESUCCESS : return "success"; 200 | case SB_EFAILURE : return "failure"; 201 | case SB_EOUTOFMEM : return "out of memory"; 202 | case SB_ETRUNCATED : return "result truncated"; 203 | case SB_EBADSTATE : return "bad stream state for this operation"; 204 | case SB_EBADRESULT : return "bad result code from event handler"; 205 | case SB_ECANTOPEN : return "cannot open file"; 206 | case SB_ENOTFOUND : return "not found"; 207 | default : return "unknown"; 208 | } 209 | } 210 | 211 | 212 | /*=========================================================================== 213 | * Buffer 214 | *===========================================================================*/ 215 | 216 | static void sb_buffer_init(sb_Buffer *buf) { 217 | memset(buf, 0, sizeof(*buf)); 218 | } 219 | 220 | 221 | static void sb_buffer_deinit(sb_Buffer *buf) { 222 | free(buf->s); 223 | } 224 | 225 | 226 | static void sb_buffer_shift(sb_Buffer *buf, size_t n) { 227 | buf->len -= n; 228 | memmove(buf->s, buf->s + n, buf->len); 229 | } 230 | 231 | 232 | static int sb_buffer_reserve(sb_Buffer *buf, size_t n) { 233 | void *p; 234 | if (buf->cap >= n) return SB_ESUCCESS; 235 | p = realloc(buf->s, n); 236 | if (!p) return SB_EOUTOFMEM; 237 | buf->s = p; 238 | buf->cap = n; 239 | return SB_ESUCCESS; 240 | } 241 | 242 | 243 | static int sb_buffer_push_char(sb_Buffer *buf, char chr) { 244 | if (buf->len == buf->cap) { 245 | int err = sb_buffer_reserve(buf, (buf->cap << 1) | (!buf->cap)); 246 | if (err) return err; 247 | } 248 | buf->s[buf->len++] = chr; 249 | return SB_ESUCCESS; 250 | } 251 | 252 | 253 | static int sb_buffer_push_str(sb_Buffer *buf, const char *p, size_t len) { 254 | int err; 255 | size_t orig_len = buf->len; 256 | while (len) { 257 | err = sb_buffer_push_char(buf, *p); 258 | if (err) { 259 | buf->len = orig_len; 260 | return err; 261 | } 262 | p++, len--; 263 | } 264 | return SB_ESUCCESS; 265 | } 266 | 267 | 268 | static inline bool fmt_is_modifier(int c) { 269 | return (c == 'l'); 270 | } 271 | 272 | 273 | static int sb_buffer_vwritef(sb_Buffer *buf, const char *fmt, va_list args) { 274 | int err; 275 | size_t orig_len = buf->len; 276 | char fbuf[64]; 277 | char lbuf[512]; 278 | bool long_mode = false; 279 | char *s; 280 | 281 | while (*fmt) { 282 | if (*fmt == '%') { 283 | switch (*++fmt) { 284 | 285 | case 's': 286 | s = va_arg(args, char*); 287 | if (s == NULL) s = "(null)"; 288 | err = sb_buffer_push_str(buf, s, strlen(s)); 289 | if (err) goto fail; 290 | break; 291 | 292 | default: 293 | fbuf[0] = '%'; 294 | s = fbuf + 1; 295 | while ( !isalpha(*fmt) && *fmt != '%' ) *s++ = *fmt++; 296 | repeat: 297 | s[0] = *fmt, s[1] = '\0'; 298 | switch (*fmt) { 299 | case 'l': 300 | long_mode = true; 301 | ++fmt; 302 | ++s; 303 | goto repeat; 304 | case 'f': 305 | case 'g': 306 | if (long_mode) { 307 | sprintf(lbuf, fbuf, va_arg(args, long double)); 308 | long_mode = false; 309 | } else { 310 | sprintf(lbuf, fbuf, va_arg(args, double)); 311 | } 312 | break; 313 | case 'c': 314 | case 'd': 315 | case 'i': 316 | if (long_mode) { 317 | sprintf(lbuf, fbuf, va_arg(args, long int)); 318 | long_mode = false; 319 | } else { 320 | sprintf(lbuf, fbuf, va_arg(args, int)); 321 | } 322 | break; 323 | case 'u': 324 | case 'x': 325 | case 'X': 326 | if (long_mode) { 327 | sprintf(lbuf, fbuf, va_arg(args, long unsigned)); 328 | long_mode = false; 329 | } else { 330 | sprintf(lbuf, fbuf, va_arg(args, unsigned)); 331 | } 332 | break; 333 | case 'p': 334 | if (long_mode) { 335 | sprintf(lbuf, fbuf, va_arg(args, void*)); 336 | long_mode = false; 337 | } else { 338 | sprintf(lbuf, fbuf, va_arg(args, void*)); 339 | } 340 | break; 341 | default: 342 | long_mode = false; 343 | lbuf[0] = *fmt; 344 | lbuf[1] = '\0'; 345 | break; 346 | } 347 | err = sb_buffer_push_str(buf, lbuf, strlen(lbuf)); 348 | if (err) goto fail; 349 | } 350 | } else { 351 | err = sb_buffer_push_char(buf, *fmt); 352 | if (err) goto fail; 353 | } 354 | fmt++; 355 | } 356 | 357 | return SB_ESUCCESS; 358 | 359 | fail: 360 | buf->len = orig_len; 361 | return err; 362 | } 363 | 364 | 365 | static int sb_buffer_writef(sb_Buffer *buf, const char *fmt, ...) { 366 | int err; 367 | va_list args; 368 | va_start(args, fmt); 369 | err = sb_buffer_vwritef(buf, fmt, args); 370 | va_end(args); 371 | return err; 372 | } 373 | 374 | 375 | static int sb_buffer_null_terminate(sb_Buffer *buf) { 376 | int err = sb_buffer_push_char(buf, '\0'); 377 | if (err) return err; 378 | buf->len--; 379 | return SB_ESUCCESS; 380 | } 381 | 382 | 383 | /*=========================================================================== 384 | * Stream 385 | *===========================================================================*/ 386 | 387 | static sb_Stream *sb_stream_new(sb_Server *srv, sb_Socket sockfd) { 388 | sb_Stream *st = malloc( sizeof(*st) ); 389 | if (!st) return NULL; 390 | memset(st, 0, sizeof(*st)); 391 | sb_buffer_init(&st->recv_buf); 392 | sb_buffer_init(&st->send_buf); 393 | st->sockfd = sockfd; 394 | st->server = srv; 395 | st->init_time = time(NULL); 396 | st->last_activity = srv->now; 397 | set_socket_blocking(sockfd, true); 398 | get_socket_address(sockfd, st->address); 399 | return st; 400 | } 401 | 402 | 403 | static void sb_stream_close(sb_Stream *st) { 404 | st->state = STATE_CLOSING; 405 | } 406 | 407 | 408 | static int sb_stream_emit(sb_Stream *st, sb_Event *e) { 409 | int res; 410 | e->stream = st; 411 | e->udata = st->server->udata; 412 | e->server = st->server; 413 | e->address = st->address; 414 | res = e->server->handler(e); 415 | if (res < 0) return res; 416 | switch (res) { 417 | case SB_RES_CLOSE : sb_stream_close(st); /* Fall through */ 418 | case SB_RES_OK : return SB_ESUCCESS; 419 | default : return SB_EBADRESULT; 420 | } 421 | } 422 | 423 | 424 | static void sb_stream_link(sb_Stream *st) { 425 | sb_Server *srv = st->server; 426 | struct sb_Stream *st2; 427 | 428 | /* Push stream to list */ 429 | pthread_mutex_lock(&srv->stream_mtx); 430 | if (srv->streams) { 431 | st2 = srv->streams; 432 | while (st2->next) { 433 | st2 = st2->next; 434 | } 435 | st2->next = st; 436 | st->prev = st2; 437 | } else { 438 | srv->streams = st; 439 | } 440 | pthread_mutex_unlock(&srv->stream_mtx); 441 | } 442 | 443 | 444 | static void sb_stream_unlink(sb_Stream *st) { 445 | sb_Server *srv = st->server; 446 | 447 | /* Pop stream from list */ 448 | pthread_mutex_lock(&srv->stream_mtx); 449 | if (st->next) { 450 | st->next->prev = st->prev; 451 | } 452 | if (st->prev) { 453 | st->prev->next = st->next; 454 | } 455 | if (srv->streams == st) { 456 | srv->streams = st->next; 457 | } 458 | pthread_mutex_unlock(&srv->stream_mtx); 459 | } 460 | 461 | 462 | static void sb_stream_destroy(sb_Stream *st) { 463 | sb_Event e; 464 | 465 | /* Emit close event */ 466 | memset(&e, 0, sizeof(e)); 467 | e.type = SB_EV_CLOSE; 468 | sb_stream_emit(st, &e); 469 | 470 | /* Clean up */ 471 | shutdown(st->sockfd, SHUT_RDWR); 472 | close(st->sockfd); 473 | if (st->send_fd > 0) { 474 | shutdown(st->send_fd, SHUT_RDWR); 475 | close(st->send_fd); 476 | } 477 | sb_buffer_deinit(&st->recv_buf); 478 | sb_buffer_deinit(&st->send_buf); 479 | free(st); 480 | } 481 | 482 | 483 | static int sb_stream_recv(sb_Stream *st) { 484 | for (;;) { 485 | char buf[4096]; 486 | size_t n; 487 | int err, i, sz; 488 | 489 | /* Receive data */ 490 | sz = recv(st->sockfd, buf, sizeof(buf) - 1, 0); 491 | if (sz <= 0) { 492 | /* Disconnected? */ 493 | if (sz == 0 || errno != EWOULDBLOCK) { 494 | sb_stream_close(st); 495 | } 496 | return SB_ESUCCESS; 497 | } 498 | 499 | /* Update last_activity */ 500 | st->last_activity = st->server->now; 501 | 502 | /* Write to recv_buf */ 503 | for (i = 0; i < sz; i++) { 504 | err = sb_buffer_push_char(&st->recv_buf, buf[i]); 505 | if (err) { 506 | close_stream: 507 | sb_stream_close(st); 508 | return err; 509 | } 510 | 511 | /* Have we received the whole header? */ 512 | if ( 513 | st->state == STATE_RECEIVING_HEADER && 514 | st->recv_buf.len >= 4 && 515 | mem_equal(st->recv_buf.s + st->recv_buf.len - 4, "\r\n\r\n", 4) 516 | ) { 517 | const char *s; 518 | /* Update stream's current state */ 519 | st->state = STATE_RECEIVING_REQUEST; 520 | /* Assure recv_buf is null-terminated */ 521 | err = sb_buffer_null_terminate(&st->recv_buf); 522 | if (err) goto close_stream; 523 | /* If the header contains the Content-Length field we set the 524 | * expected_recv_len and continue writing to the recv_buf, otherwise we 525 | * assume the request is complete */ 526 | s = find_header_value(st->recv_buf.s, "Content-Length"); 527 | if (s) { 528 | st->expected_recv_len = st->recv_buf.len + str_to_uint(s); 529 | st->data_idx = st->recv_buf.len; 530 | } else { 531 | goto handle_request; 532 | } 533 | } 534 | 535 | /* Have we received all the data we're expecting? */ 536 | if (st->expected_recv_len == st->recv_buf.len) { 537 | /* Handle request */ 538 | sb_Event e; 539 | int n, path_idx; 540 | char method[16], path[512], ver[16]; 541 | handle_request: 542 | st->state = STATE_SENDING_STATUS; 543 | /* Assure recv_buf string is NULL-terminated */ 544 | err = sb_buffer_null_terminate(&st->recv_buf); 545 | if (err) return err; 546 | /* Get method, path, version */ 547 | n = sscanf(st->recv_buf.s, "%15s %n%*s %15s", method, &path_idx, ver); 548 | /* Is request line invalid? */ 549 | if (n != 2 || !mem_equal(ver, "HTTP", 4)) { 550 | sb_stream_close(st); 551 | return SB_ESUCCESS; 552 | } 553 | /* Build and emit `request` event */ 554 | url_decode(path, st->recv_buf.s + path_idx, sizeof(path)); 555 | memset(&e, 0, sizeof(e)); 556 | e.type = SB_EV_REQUEST; 557 | e.method = method; 558 | e.path = path; 559 | err = sb_stream_emit(st, &e); 560 | if (err) goto close_stream; 561 | /* No more data needs to be received (nor should it exist) */ 562 | return SB_ESUCCESS; 563 | } 564 | } 565 | } 566 | 567 | return SB_ESUCCESS; 568 | } 569 | 570 | 571 | static int sb_stream_send(sb_Stream *st) { 572 | if (st->send_buf.len > 0) { 573 | int sz; 574 | 575 | /* Send data */ 576 | send_data: 577 | sz = send(st->sockfd, st->send_buf.s, st->send_buf.len, 0); 578 | if (sz <= 0) { 579 | /* Disconnected? */ 580 | if (errno != EWOULDBLOCK) { 581 | sb_stream_close(st); 582 | } 583 | return SB_ESUCCESS; 584 | } 585 | 586 | /* Remove sent bytes from buffer */ 587 | sb_buffer_shift(&st->send_buf, sz); 588 | 589 | /* Update last_activity */ 590 | st->last_activity = st->server->now; 591 | 592 | } else if (st->send_fd > 0) { 593 | /* Read chunk, write to stream and continue sending */ 594 | int err = sb_buffer_reserve(&st->send_buf, 8192); 595 | if (err) return err; 596 | st->send_buf.len = read(st->send_fd, st->send_buf.s, st->send_buf.cap); 597 | if (st->send_buf.len > 0) goto send_data; 598 | 599 | /* Reached end of file */ 600 | close(st->send_fd); 601 | st->send_fd = -1; 602 | 603 | } else { 604 | /* No more data left -- disconnect */ 605 | sb_stream_close(st); 606 | } 607 | 608 | return SB_ESUCCESS; 609 | } 610 | 611 | 612 | static int sb_stream_finalize_header(sb_Stream *st) { 613 | int err; 614 | if (st->state < STATE_SENDING_HEADER) { 615 | err = sb_send_status(st, 200, "OK"); 616 | if (err) return err; 617 | } 618 | err = sb_buffer_push_str(&st->send_buf, "\r\n", 2); 619 | if (err) return err; 620 | st->state = STATE_SENDING_DATA; 621 | return SB_ESUCCESS; 622 | } 623 | 624 | 625 | int sb_send_status(sb_Stream *st, int code, const char *msg) { 626 | int err; 627 | if (st->state != STATE_SENDING_STATUS) { 628 | return SB_EBADSTATE; 629 | } 630 | err = sb_buffer_writef(&st->send_buf, "HTTP/1.1 %d %s\r\n", code, msg); 631 | if (err) return err; 632 | st->state = STATE_SENDING_HEADER; 633 | return SB_ESUCCESS; 634 | } 635 | 636 | 637 | int sb_send_header(sb_Stream *st, const char *field, const char *val) { 638 | int err; 639 | if (st->state > STATE_SENDING_HEADER) { 640 | return SB_EBADSTATE; 641 | } 642 | if (st->state < STATE_SENDING_HEADER) { 643 | err = sb_send_status(st, 200, "OK"); 644 | if (err) return err; 645 | } 646 | err = sb_buffer_writef(&st->send_buf, "%s: %s\r\n", field, val); 647 | if (err) return err; 648 | return SB_ESUCCESS; 649 | } 650 | 651 | 652 | int sb_send_file(sb_Stream *st, const char *filename) { 653 | int err; 654 | char buf[32]; 655 | struct stat stbuf; 656 | int fd = -1; 657 | if (st->state > STATE_SENDING_HEADER) { 658 | return SB_EBADSTATE; 659 | } 660 | /* Try to open file */ 661 | fd = open(filename, O_RDONLY); 662 | if (fd <= 0) return SB_ECANTOPEN; 663 | 664 | /* Get file size and write headers */ 665 | if (fstat(fd, &stbuf) < 0) { 666 | err = SB_EBADRESULT; 667 | goto fail; 668 | } 669 | snprintf(buf, sizeof(buf), "%" PRIuMAX, (uintmax_t)stbuf.st_size); 670 | err = sb_send_header(st, "Content-Length", buf); 671 | if (err) goto fail; 672 | err = sb_stream_finalize_header(st); 673 | if (err) goto fail; 674 | 675 | /* Set stream's file descriptor and state */ 676 | st->send_fd = fd; 677 | st->state = STATE_SENDING_FILE; 678 | return SB_ESUCCESS; 679 | 680 | fail: 681 | if (fd > 0) close(fd); 682 | return err; 683 | } 684 | 685 | 686 | int sb_write(sb_Stream *st, const void *data, size_t len) { 687 | if (st->state < STATE_SENDING_DATA) { 688 | int err = sb_stream_finalize_header(st); 689 | if (err) return err; 690 | } 691 | if (st->state != STATE_SENDING_DATA) return SB_EBADSTATE; 692 | return sb_buffer_push_str(&st->send_buf, data, len); 693 | } 694 | 695 | 696 | int sb_vwritef(sb_Stream *st, const char *fmt, va_list args) { 697 | if (st->state < STATE_SENDING_DATA) { 698 | int err = sb_stream_finalize_header(st); 699 | if (err) return err; 700 | } 701 | if (st->state != STATE_SENDING_DATA) return SB_EBADSTATE; 702 | return sb_buffer_vwritef(&st->send_buf, fmt, args); 703 | } 704 | 705 | 706 | int sb_writef(sb_Stream *st, const char *fmt, ...) { 707 | int err; 708 | va_list args; 709 | va_start(args, fmt); 710 | err = sb_vwritef(st, fmt, args); 711 | va_end(args); 712 | return err; 713 | } 714 | 715 | 716 | int sb_get_header(sb_Stream *st, const char *field, char *dst, size_t len) { 717 | size_t n; 718 | int res = SB_ESUCCESS; 719 | const char *s = find_header_value(st->recv_buf.s, field); 720 | if (!s) { 721 | *dst = '\0'; 722 | return SB_ENOTFOUND; 723 | } 724 | n = strchr(s, '\r') - s; 725 | while (n > 1 && strchr(" \t", s[n-1])) n--; /* trim whitespace from end */ 726 | if (n > len - 1) { 727 | n = len - 1; 728 | res = SB_ETRUNCATED; 729 | } 730 | memcpy(dst, s, n); 731 | dst[n] = '\0'; 732 | return res; 733 | } 734 | 735 | 736 | int sb_get_var_ex(sb_Stream *st, const char *name, char *dst, size_t len, bool from_data_only) { 737 | const char *q, *s = NULL; 738 | 739 | if (!from_data_only) { 740 | /* Find beginning of query string */ 741 | q = st->recv_buf.s + strcspn(st->recv_buf.s, "?\r"); 742 | q = (*q == '?') ? (q + 1) : NULL; 743 | } else { 744 | q = NULL; 745 | } 746 | 747 | /* Try to get var from query string, then data string */ 748 | if (q) s = find_var_value(q, name); 749 | if (!s && st->data_idx) { 750 | s = find_var_value(st->recv_buf.s + st->data_idx, name); 751 | } 752 | if (!s) { 753 | *dst = '\0'; 754 | return SB_ENOTFOUND; 755 | } 756 | return url_decode(dst, s, len); 757 | } 758 | 759 | 760 | int sb_get_var(sb_Stream *st, const char *name, char *dst, size_t len) { 761 | return sb_get_var_ex(st, name, dst, len, false); 762 | } 763 | 764 | 765 | char *sb_get_content_data(sb_Stream *st, size_t *len) { 766 | if (st->data_idx) { 767 | if (len) { 768 | *len = st->expected_recv_len - st->data_idx; 769 | } 770 | return st->recv_buf.s + st->data_idx; 771 | } else { 772 | if (len) { 773 | *len = 0; 774 | } 775 | return NULL; 776 | } 777 | } 778 | 779 | 780 | int sb_get_cookie(sb_Stream *st, const char *name, char *dst, size_t len) { 781 | size_t n; 782 | const char *s = st->recv_buf.s; 783 | int res = SB_ESUCCESS; 784 | size_t name_len = strlen(name); 785 | 786 | /* Get cookie header */ 787 | s = find_header_value(st->recv_buf.s, "Cookie"); 788 | if (!s) goto fail; 789 | 790 | /* Find var */ 791 | while (*s) { 792 | s += strspn(s, " \t"); 793 | /* Found var? find value, get len, copy value and return */ 794 | if ( mem_case_equal(s, name, name_len) && strchr(" =", s[name_len]) ) { 795 | s += name_len; 796 | s += strspn(s, "= \t\r"); 797 | n = strcspn(s, ";\r"); 798 | if (n >= len - 1) { 799 | n = len - 1; 800 | res = SB_ETRUNCATED; 801 | } 802 | memcpy(dst, s, n); 803 | dst[n] = '\0'; 804 | return res; 805 | } 806 | s += strcspn(s, ";\r"); 807 | if (*s != ';') goto fail; 808 | s++; 809 | } 810 | 811 | fail: 812 | *dst = '\0'; 813 | return SB_ENOTFOUND; 814 | } 815 | 816 | 817 | #define P_ATCHK(x) do { if (!(p = (x))) goto fail; } while (0) 818 | #define P_AFTERL(x, l) do {\ 819 | size_t len__ = (l);\ 820 | for (;; p++) {\ 821 | if (p == end - len__) goto fail;\ 822 | if (mem_equal(p, x, len__)) break;\ 823 | }\ 824 | p += len__;\ 825 | } while (0) 826 | #define P_AFTER(s) P_AFTERL(s, strlen(s)) 827 | 828 | const void *sb_get_multipart(sb_Stream *st, const char *name, size_t *len) { 829 | const char *boundary; 830 | size_t boundary_len; 831 | size_t name_len = strlen(name); 832 | const char *p = st->recv_buf.s; 833 | char *end = st->recv_buf.s + st->recv_buf.len; 834 | 835 | /* Get boundary string */ 836 | P_ATCHK( find_header_value(p, "Content-Type") ); 837 | P_AFTER( "boundary=" ); 838 | boundary = p; 839 | P_AFTER( "\r\n" ); 840 | boundary_len = p - boundary - 2; 841 | 842 | next: 843 | /* Move to after first boundary, then to start of name */ 844 | P_AFTERL( boundary, boundary_len ); 845 | P_AFTER( "\r\n" ); 846 | P_ATCHK( find_header_value(p, "Content-Disposition") ); 847 | P_AFTER( "name=\"" ); 848 | 849 | /* Does the name match what we were looking for? */ 850 | if (mem_equal(p, name, name_len) && p[name_len] == '"') { 851 | const char *res; 852 | /* Move to start of data */ 853 | P_AFTER( "\r\n\r\n" ); 854 | res = p; 855 | /* Find boundary, set length and return result */ 856 | P_AFTERL( boundary, boundary_len ); 857 | *len = p - res - boundary_len - 4; 858 | return res; 859 | } 860 | 861 | /* Try the next part */ 862 | goto next; 863 | 864 | fail: 865 | *len = 0; 866 | return NULL; 867 | } 868 | 869 | 870 | time_t sb_stream_get_init_time(sb_Stream *st) { 871 | return st->init_time; 872 | } 873 | 874 | 875 | /*=========================================================================== 876 | * Server 877 | *===========================================================================*/ 878 | sb_Server *sb_new_server(const sb_Options *opt) { 879 | sb_Server *srv; 880 | struct sockaddr_in in_addr; 881 | char *tmp_end; 882 | long port; 883 | int err, optval; 884 | 885 | /* Create server object */ 886 | srv = malloc( sizeof(*srv) ); 887 | if (!srv) { 888 | KernelPrintOut("Failed to malloc memory"); 889 | goto fail; 890 | } 891 | memset(srv, 0, sizeof(*srv)); 892 | srv->sockfd = INVALID_SOCKET; 893 | srv->handler = opt->handler; 894 | srv->udata = opt->udata; 895 | srv->timeout = opt->timeout ? str_to_uint(opt->timeout) : 30000; 896 | srv->max_request_size = str_to_uint(opt->max_request_size); 897 | srv->max_lifetime = str_to_uint(opt->max_lifetime); 898 | 899 | memset(&in_addr, 0, sizeof(in_addr)); 900 | in_addr.sin_family = AF_INET; 901 | if (opt->port == NULL) { 902 | KernelPrintOut("Port is nukk"); 903 | goto fail; 904 | } 905 | port = strtol(opt->port, &tmp_end, 0); 906 | if ( 907 | (tmp_end == opt->port) || (errno == ERANGE) || 908 | !(port > 0 && port <= USHRT_MAX) 909 | ) { 910 | goto fail; 911 | } 912 | in_addr.sin_port = htons((uint16_t)port); 913 | 914 | if (opt->host != NULL) { 915 | if (inet_pton(in_addr.sin_family, opt->host, &in_addr.sin_addr.s_addr) != 1) { 916 | KernelPrintOut("inet_pton failed"); 917 | goto fail; 918 | } 919 | } else { 920 | in_addr.sin_addr.s_addr = htonl(INADDR_ANY); 921 | } 922 | 923 | /* Init socket */ 924 | srv->sockfd = sceNetSocket("RPI", in_addr.sin_family, SOCK_STREAM, IPPROTO_TCP); 925 | if (srv->sockfd == INVALID_SOCKET) 926 | { 927 | KernelPrintOut("sockfd = -1"); 928 | goto fail; 929 | } 930 | 931 | set_socket_blocking(srv->sockfd, false); 932 | 933 | /* Set SO_REUSEADDR so that the socket can be immediately bound without 934 | * having to wait for any closed socket on the same port to timeout */ 935 | optval = 1; 936 | err = sceNetSetsockopt(srv->sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); 937 | 938 | /* Bind and listen */ 939 | err = sceNetBind(srv->sockfd, (struct sockaddr*)&in_addr, sizeof(in_addr)); 940 | if (err) 941 | { 942 | KernelPrintOut("Failed to bind socket ret %d %s\n", err, strerror(errno)); 943 | goto fail; 944 | } 945 | err = listen(srv->sockfd, 1023); 946 | if (err) { 947 | KernelPrintOut("listen failed"); 948 | goto fail; 949 | } 950 | 951 | /* Create mutex */ 952 | err = pthread_mutex_init(&srv->stream_mtx, NULL); 953 | if (err) { 954 | KernelPrintOut("mutex failed"); 955 | goto fail; 956 | } 957 | 958 | return srv; 959 | 960 | fail: 961 | if (srv) sb_close_server(srv); 962 | return NULL; 963 | } 964 | 965 | 966 | void sb_close_server(sb_Server *srv) { 967 | sb_Stream *st; 968 | 969 | /* Destroy all streams */ 970 | pthread_mutex_lock(&srv->stream_mtx); 971 | st = srv->streams; 972 | while (st) { 973 | st = st->next; 974 | sb_stream_destroy(st); 975 | } 976 | srv->streams = NULL; 977 | pthread_mutex_unlock(&srv->stream_mtx); 978 | 979 | /* Clean up */ 980 | if (srv->sockfd != INVALID_SOCKET) { 981 | shutdown(srv->sockfd, SHUT_RDWR); 982 | close(srv->sockfd); 983 | } 984 | 985 | pthread_mutex_destroy(&srv->stream_mtx); 986 | 987 | free(srv); 988 | } 989 | 990 | 991 | static void* conn_thread(void* arg) { 992 | struct sb_Stream *st = (struct sb_Stream *)arg; 993 | sb_Event e; 994 | int err; 995 | 996 | /* Do `connect` event */ 997 | memset(&e, 0, sizeof(e)); 998 | e.type = SB_EV_CONNECT; 999 | err = sb_stream_emit(st, &e); 1000 | if (err) goto fail; 1001 | 1002 | /* Receive data */ 1003 | err = sb_stream_recv(st); 1004 | if (err) goto fail; 1005 | 1006 | while (st->state != STATE_CLOSING) { 1007 | err = sb_stream_send(st); 1008 | if (err) goto fail; 1009 | } 1010 | 1011 | fail: 1012 | /* Unlinking stream from list */ 1013 | sb_stream_unlink(st); 1014 | 1015 | /* Destroy stream */ 1016 | sb_stream_destroy(st); 1017 | 1018 | pthread_exit(NULL); 1019 | } 1020 | 1021 | 1022 | int sb_poll_server(sb_Server *srv) { 1023 | struct sb_Stream *st; 1024 | sb_Socket sockfd = INVALID_SOCKET; 1025 | pthread_t thr; 1026 | int err; 1027 | 1028 | /* Get and store current time */ 1029 | srv->now = time(NULL); 1030 | 1031 | /* Accept connections */ 1032 | while ( (sockfd = accept(srv->sockfd, NULL, NULL)) != INVALID_SOCKET ) { 1033 | /* Init new stream */ 1034 | st = sb_stream_new(srv, sockfd); 1035 | if (!st) { 1036 | err = SB_EOUTOFMEM; 1037 | goto fail; 1038 | } 1039 | 1040 | /* Link stream to list */ 1041 | sb_stream_link(st); 1042 | 1043 | /* Create processing thread */ 1044 | err = pthread_create(&thr, NULL, &conn_thread, st); 1045 | if (err) goto fail; 1046 | 1047 | sockfd = INVALID_SOCKET; 1048 | } 1049 | 1050 | return SB_RES_OK; 1051 | 1052 | fail: 1053 | if (st) { 1054 | /* Unlinking stream from list */ 1055 | sb_stream_unlink(st); 1056 | } 1057 | 1058 | if (sockfd != INVALID_SOCKET) { 1059 | shutdown(sockfd, SHUT_RDWR); 1060 | close(sockfd); 1061 | } 1062 | return err; 1063 | } 1064 | 1065 | --------------------------------------------------------------------------------