├── 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 |
--------------------------------------------------------------------------------