├── sce_sys
├── icon0.png
├── pic0.png
├── pronunciation.sig
├── pronunciation.xml
└── param.sfx
├── .gitignore
├── README
├── util.h
├── gl_test.gp4
├── common.h
├── util.c
├── Makefile
└── main.c
/sce_sys/icon0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatz/ps4_gl_test/HEAD/sce_sys/icon0.png
--------------------------------------------------------------------------------
/sce_sys/pic0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatz/ps4_gl_test/HEAD/sce_sys/pic0.png
--------------------------------------------------------------------------------
/sce_sys/pronunciation.sig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flatz/ps4_gl_test/HEAD/sce_sys/pronunciation.sig
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | obj/*
3 |
4 | *.prx
5 | *.a
6 | *.lib
7 |
8 | *.o
9 | *.obj
10 |
11 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | OpenGL ES test
2 |
3 | Prerequisites:
4 | SDK (oouch! need to move to open source SDK in the future...)
5 | Linux & Wine (it could be backported to Windows and to other existing custom SDKs)
6 |
--------------------------------------------------------------------------------
/sce_sys/pronunciation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Payload Loader
6 | p ey l ow d ___ l ow d er
7 |
8 |
9 | Payload Loader
10 | p ey l ow d ___ l ao d ax r
11 |
12 |
13 |
--------------------------------------------------------------------------------
/util.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "common.h"
4 |
5 | #include
6 |
7 | SceKernelModule load_module_from_sandbox(const char* name, size_t args, const void* argp, unsigned int flags, const SceKernelLoadModuleOpt* opts, int* res);
8 |
9 | bool get_module_base(const char* name, uint64_t* base, uint64_t* size);
10 |
11 | typedef void module_patch_cb_t(void* arg, uint8_t* base, uint64_t size);
12 | bool patch_module(const char* name, module_patch_cb_t* cb, void* arg);
13 |
14 | void hexdump(const void* data, size_t size);
15 |
16 | void send_notify(const char* format, ...);
17 |
--------------------------------------------------------------------------------
/sce_sys/param.sfx:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 | 01.00
5 | 18
6 | gde
7 | UP9000-FLTZ00002_00-GLTEST0000000000
8 | 0
9 | obs
10 | 1
11 | 1020
12 | OpenGL test
13 | FLTZ00002
14 | 01.00
15 |
16 |
--------------------------------------------------------------------------------
/gl_test.gp4:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | pkg_ps4_app
5 | PS4VOLUME
6 | 2018-09-17 21:00:00
7 |
8 |
9 |
10 |
11 |
12 |
13 | 0
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/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 | #define STRINGIFY(x) #x
16 | #define STRINGIFY_DEEP(x) STRINGIFY(x)
17 |
18 | #define JOIN_HELPER(x, y) x##y
19 | #define JOIN(x, y) JOIN_HELPER(x, y)
20 |
21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
22 |
23 | #define ALIGN_UP(x, alignment) (((x) + ((alignment) - 1)) & ~((alignment) - 1))
24 | #define ALIGN_DOWN(x, alignment) ((x) & ~((alignment) - 1))
25 |
26 | #define UNUSED(x) (void)(x)
27 |
28 | #define EPRINTF(msg, ...) printf("Error at %s:%s(%d): " msg, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
29 |
30 | #define TYPE_PAD(size) char JOIN(_pad_, __COUNTER__)[size]
31 | #define TYPE_VARIADIC_BEGIN(name) name { union {
32 | #define TYPE_BEGIN(name, size) name { union { TYPE_PAD(size)
33 | #define TYPE_END(...) }; } __VA_ARGS__
34 | #define TYPE_FIELD(field, offset) struct { TYPE_PAD(offset); field; }
35 |
36 | #define TYPE_CHECK_SIZE(name, size) \
37 | _Static_assert(sizeof(name) == (size), "Size of " #name " != " #size)
38 |
39 | #define TYPE_CHECK_FIELD_OFFSET(name, member, offset) \
40 | _Static_assert(offsetof(name, member) == (offset), "Offset of " #name "." #member " != " #offset)
41 |
42 | #define TYPE_CHECK_FIELD_SIZE(name, member, size) \
43 | _Static_assert(sizeof(((name*)0)->member) == (size), "Size of " #name "." #member " != " #size)
44 |
45 | extern const char* g_sandbox_word;
46 |
--------------------------------------------------------------------------------
/util.c:
--------------------------------------------------------------------------------
1 | #include "util.h"
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | SceKernelModule load_module_from_sandbox(const char* name, size_t args, const void* argp, unsigned int flags, const SceKernelLoadModuleOpt* opts, int* res) {
9 | char file_path[SCE_KERNEL_MAX_NAME_LENGTH];
10 | SceKernelModule module;
11 |
12 | snprintf(file_path, sizeof(file_path), "/%s/common/lib/%s", g_sandbox_word, name);
13 |
14 | module = sceKernelLoadStartModule(file_path, args, argp, flags, opts, res);
15 |
16 | return module;
17 | }
18 |
19 | bool get_module_base(const char* name, uint64_t* base, uint64_t* size) {
20 | SceKernelModuleInfo moduleInfo;
21 | int ret;
22 |
23 | ret = sceKernelGetModuleInfoByName(name, &moduleInfo);
24 | if (ret) {
25 | EPRINTF("sceKernelGetModuleInfoByName(%s) failed: 0x%08X\n", name, ret);
26 | goto err;
27 | }
28 |
29 | if (base) {
30 | *base = (uint64_t)moduleInfo.segmentInfo[0].baseAddr;
31 | }
32 | if (size) {
33 | *size = moduleInfo.segmentInfo[0].size;
34 | }
35 |
36 | return true;
37 |
38 | err:
39 | return false;
40 | }
41 |
42 | bool patch_module(const char* name, module_patch_cb_t* cb, void* arg) {
43 | uint64_t base, size;
44 | int ret;
45 |
46 | if (!get_module_base(name, &base, &size)) {
47 | goto err;
48 | }
49 |
50 | ret = sceKernelMprotect((void*)base, size, SCE_KERNEL_PROT_CPU_READ | SCE_KERNEL_PROT_CPU_WRITE | SCE_KERNEL_PROT_CPU_EXEC);
51 | if (ret) {
52 | EPRINTF("sceKernelMprotect(%s) failed: 0x%08X\n", name, ret);
53 | goto err;
54 | }
55 |
56 | if (cb) {
57 | (*cb)(arg, (uint8_t*)base, size);
58 | }
59 |
60 | return true;
61 |
62 | err:
63 | return false;
64 | }
65 |
66 | void send_notify(const char* format, ...) {
67 | SceNotificationRequest req;
68 | va_list args;
69 |
70 | memset(&req, 0, sizeof(req));
71 | {
72 | req.unk_0x10 = -1;
73 |
74 | va_start(args, format);
75 | req.buf[0] = '%'; /* XXX: hackity hack */
76 | vsnprintf((char*)req.buf + 1, 0xB4, format, args);
77 | va_end(args);
78 | }
79 |
80 | sceKernelSendNotificationRequest(0, &req, sizeof(req), 1);
81 | }
82 |
83 | void hexdump(const void* data, size_t size) {
84 | const uint8_t* p = (const uint8_t*)data;
85 | const size_t n = 16;
86 | size_t i, j, k;
87 | for (i = 0; i < size; i += n) {
88 | k = (i + n) <= size ? n : (size - i);
89 | printf("%8p:", (uint8_t*)data + i);
90 | for (j = 0; j < k; ++j) {
91 | printf(" %02x", p[i + j]);
92 | }
93 | for (j = k; j < n; ++j) {
94 | printf(" ");
95 | }
96 | printf(" ");
97 | for (j = 0; j < k; ++j) {
98 | printf("%c", isprint(p[i + j]) ? p[i + j] : '.');
99 | }
100 | for (j = k; j < n; ++j) {
101 | printf(" ");
102 | }
103 | printf("\n");
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | WINE = wine
2 | WINE_PATH_TOOL = winepath
3 |
4 | CC = $(WINE) orbis-clang
5 | CXX = $(WINE) orbis-clang++
6 | LD = $(WINE) orbis-ld
7 | OBJCOPY = $(WINE) orbis-objcopy
8 | PUBCMD = $(WINE) orbis-pub-cmd
9 | MAKE_FSELF = make_fself.py
10 |
11 | OBJDIR = obj
12 | BLDDIR = build
13 | MODDIR = sce_module
14 |
15 | TARGET = gl_test
16 | LIBS = -lkernel_tau_stub_weak -lSceSysmodule_tau_stub_weak -lSceSystemService_stub_weak -lSceSystemService_tau_stub_weak -lSceShellCoreUtil_tau_stub_weak -lScePigletv2VSH_tau_stub_weak -lkernel_util
17 | SDK_MODULES =
18 | EXTRA_MODULES =
19 | AUTH_INFO = 000000000000000000000000001C004000FF000000000080000000000000000000000000000000000000008000400040000000000000008000000000000000080040FFFF000000F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
20 |
21 | ASM_SRCS =
22 | C_SRCS = main.c util.c
23 |
24 | COMMON_FLAGS = -Wall
25 | COMMON_FLAGS += -fdiagnostics-color=always
26 | COMMON_FLAGS += -I $(TAUON_SDK_DIR)/include -I $(SCE_ORBIS_SDK_DIR)/target/include -I $(SCE_ORBIS_SDK_DIR)/target/include/common
27 | COMMON_FLAGS += -DNDEBUG
28 | COMMON_FLAGS += -g
29 |
30 | CFLAGS = $(COMMON_FLAGS)
31 | CFLAGS += -std=c11
32 | CFLAGS += -Wno-unused-function -Wno-unused-label -Werror=implicit-function-declaration
33 | CFLAGS += -fno-strict-aliasing
34 | CFLAGS += -fPIC
35 | CFLAGS += -O3
36 |
37 | ASFLAGS = $(COMMON_FLAGS)
38 |
39 | LDFLAGS = -s -Wl,--addressing=non-aslr,--strip-unused-data
40 | LDFLAGS += -L $(TAUON_SDK_DIR)/lib -L $(SCE_ORBIS_SDK_DIR)/target/lib
41 |
42 | OBJS = $(addprefix $(OBJDIR)/,$(ASM_SRCS:.S=.S.o) $(C_SRCS:.c=.c.o))
43 |
44 | .PHONY: all clean
45 |
46 | all: post-build
47 |
48 | pre-build:
49 | @mkdir -p $(MODDIR) $(OBJDIR) $(BLDDIR)
50 | @for filename in $(SDK_MODULES); do \
51 | if [ ! -f "$(MODDIR)/$$filename" ]; then \
52 | echo Copying $$filename...; \
53 | cp "`$(WINE_PATH_TOOL) -u \"$(SCE_ORBIS_SDK_DIR)/target/sce_module/$$filename\"`" $(MODDIR)/; \
54 | fi; \
55 | done;
56 | @for filename in $(EXTRA_MODULES); do \
57 | if [ ! -f "$(MODDIR)/$$filename" ]; then \
58 | echo Copying $$filename...; \
59 | cp "extra/$$filename" $(MODDIR)/; \
60 | fi; \
61 | done;
62 |
63 | post-build: main-build
64 |
65 | main-build: pre-build
66 | @$(MAKE) --no-print-directory pkg
67 |
68 | eboot: pre-build $(OBJS)
69 | $(CC) $(LDFLAGS) -o $(BLDDIR)/$(TARGET).elf $(OBJS) $(LIBS)
70 |
71 | $(OBJDIR)/%.S.o: %.S
72 | @mkdir -p $(dir $@)
73 | $(CC) $(ASFLAGS) -c $< -o $@
74 |
75 | $(OBJDIR)/%.c.o: %.c
76 | @mkdir -p $(dir $@)
77 | $(CC) $(CFLAGS) -c $< -o $@
78 |
79 | sfo:
80 | $(PUBCMD) sfo_create sce_sys/param.sfx $(BLDDIR)/param.sfo
81 |
82 | pkg: sfo eboot
83 | $(MAKE_FSELF) --auth-info $(AUTH_INFO) $(BLDDIR)/$(TARGET).elf $(BLDDIR)/$(TARGET).self
84 | $(PUBCMD) img_create $(TARGET).gp4 $(BLDDIR)/$(TARGET).pkg
85 |
86 | clean:
87 | @rm -rf $(OBJDIR) $(BLDDIR)
88 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #include "util.h"
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #define RENDER_WIDTH 1920
14 | #define RENDER_HEIGHT 1080
15 |
16 | #define PIGLET_MODULE_NAME "libScePigletv2VSH.sprx"
17 | #define SHCOMP_MODULE_NAME "libSceShaccVSH.sprx"
18 |
19 | #if 1
20 | # define MODULE_PATH_PREFIX "/app0/sce_module"
21 | #else
22 | # define MODULE_PATH_PREFIX "/data/self/system/common/lib"
23 | #endif
24 |
25 | static SceKernelModule s_piglet_module = -1;
26 | static SceKernelModule s_shcomp_module = -1;
27 |
28 | static EGLDisplay s_display = EGL_NO_DISPLAY;
29 | static EGLSurface s_surface = EGL_NO_SURFACE;
30 | static EGLContext s_context = EGL_NO_CONTEXT;
31 |
32 | static GLuint s_texture_id = 0;
33 | static GLuint s_program_id = 0;
34 |
35 | static GLint s_xyz_loc;
36 | static GLint s_uv_loc;
37 | static GLint s_sampler_loc;
38 |
39 | static const GLfloat s_obj_vertices[] = {
40 | -0.5f, 0.5f, 0.0f, /* XYZ #0 */
41 | 0.0f, 0.0f, /* UV #0 */
42 | -0.5f, -0.5f, 0.0f, /* XYZ #1 */
43 | 0.0f, 1.0f, /* UV #1 */
44 | 0.5f, -0.5f, 0.0f, /* XYZ #2 */
45 | 1.0f, 1.0f, /* UV #2 */
46 | 0.5f, 0.5f, 0.0f, /* XYZ #3 */
47 | 1.0f, 0.0f /* UV #3 */
48 | };
49 | static const GLushort s_obj_indices[] = {
50 | 0, 1, 2, 0, 2, 3,
51 | };
52 |
53 | static const GLubyte s_texture_data[4 * 3] = {
54 | 255, 0, 0, /* red */
55 | 0, 255, 0, /* green */
56 | 0, 0, 255, /* blue */
57 | 255, 255, 0, /* yellow */
58 | };
59 |
60 | static const GLchar s_vertex_shader_code[] =
61 | "attribute vec4 a_xyz;\n"
62 | "attribute vec2 a_uv;\n"
63 | "varying vec2 v_uv;\n"
64 | "\n"
65 | "void main() {\n"
66 | " gl_Position = a_xyz;\n"
67 | " v_uv = a_uv;\n"
68 | "}\n";
69 |
70 | static const GLchar s_fragment_shader_code[] =
71 | "precision mediump float;\n"
72 | "varying vec2 v_uv;\n"
73 | "uniform sampler2D s_texture;\n"
74 | "\n"
75 | "void main() {\n"
76 | " gl_FragColor = texture2D(s_texture, v_uv);\n"
77 | "}\n";
78 |
79 | const char* g_sandbox_word = NULL;
80 |
81 | static bool load_modules(void) {
82 | int ret;
83 |
84 | ret = sceKernelLoadStartModule(MODULE_PATH_PREFIX "/" PIGLET_MODULE_NAME, 0, NULL, 0, NULL, NULL);
85 | if (ret < 0) {
86 | EPRINTF("sceKernelLoadStartModule(%s) failed: 0x%08X\n", PIGLET_MODULE_NAME, ret);
87 | goto err;
88 | }
89 | s_piglet_module = ret;
90 |
91 | ret = sceKernelLoadStartModule(MODULE_PATH_PREFIX "/" SHCOMP_MODULE_NAME, 0, NULL, 0, NULL, NULL);
92 | if (ret < 0) {
93 | EPRINTF("sceKernelLoadStartModule(%s) failed: 0x%08X\n", SHCOMP_MODULE_NAME, ret);
94 | goto err;
95 | }
96 | s_shcomp_module = ret;
97 |
98 | return true;
99 |
100 | err_unload_shcompt:
101 | ret = sceKernelStopUnloadModule(s_shcomp_module, 0, NULL, 0, NULL, NULL);
102 | if (ret < 0) {
103 | EPRINTF("sceKernelStopUnloadModule(%s) failed: 0x%08X\n", SHCOMP_MODULE_NAME, ret);
104 | } else {
105 | s_shcomp_module = -1;
106 | }
107 |
108 | err_unload_piglet:
109 | ret = sceKernelStopUnloadModule(s_piglet_module, 0, NULL, 0, NULL, NULL);
110 | if (ret < 0) {
111 | EPRINTF("sceKernelStopUnloadModule(%s) failed: 0x%08X\n", PIGLET_MODULE_NAME, ret);
112 | } else {
113 | s_piglet_module = -1;
114 | }
115 |
116 | err:
117 | return false;
118 | }
119 |
120 | static void unload_modules(void) {
121 | int ret;
122 |
123 | if (s_shcomp_module > 0) {
124 | ret = sceKernelStopUnloadModule(s_shcomp_module, 0, NULL, 0, NULL, NULL);
125 | if (ret < 0) {
126 | EPRINTF("sceKernelStopUnloadModule(%s) failed: 0x%08X\n", SHCOMP_MODULE_NAME, ret);
127 | } else {
128 | s_shcomp_module = -1;
129 | }
130 | }
131 |
132 | if (s_piglet_module > 0) {
133 | ret = sceKernelStopUnloadModule(s_piglet_module, 0, NULL, 0, NULL, NULL);
134 | if (ret < 0) {
135 | EPRINTF("sceKernelStopUnloadModule(%s) failed: 0x%08X\n", PIGLET_MODULE_NAME, ret);
136 | } else {
137 | s_piglet_module = -1;
138 | }
139 | }
140 | }
141 |
142 | /* XXX: patches below are given for Piglet module from 4.74 Devkit PUP */
143 | static void pgl_patches_cb(void* arg, uint8_t* base, uint64_t size) {
144 | /* Patch runtime compiler check */
145 | const uint8_t p_set_eax_to_1[] = {
146 | 0x31, 0xC0, 0xFF, 0xC0, 0x90,
147 | };
148 | memcpy(base + 0x5451F, p_set_eax_to_1, sizeof(p_set_eax_to_1));
149 |
150 | /* Tell that runtime compiler exists */
151 | *(uint8_t*)(base + 0xB2DEC) = 0;
152 | *(uint8_t*)(base + 0xB2DED) = 0;
153 | *(uint8_t*)(base + 0xB2DEE) = 1;
154 | *(uint8_t*)(base + 0xB2E21) = 1;
155 |
156 | /* Inform Piglet that we have shader compiler module loaded */
157 | *(int32_t*)(base + 0xB2E24) = s_shcomp_module;
158 | }
159 |
160 | static bool do_patches(void) {
161 | if (!patch_module(PIGLET_MODULE_NAME, &pgl_patches_cb, NULL)) {
162 | EPRINTF("Unable to patch PGL module.\n");
163 | goto err;
164 | }
165 |
166 | return true;
167 |
168 | err:
169 | return false;
170 | }
171 |
172 | static void cleanup(void) {
173 | int ret;
174 |
175 | unload_modules();
176 |
177 | ret = sceSysmoduleUnloadModuleInternal(SCE_SYSMODULE_INTERNAL_SYSTEM_SERVICE);
178 | if (ret) {
179 | EPRINTF("sceSysmoduleUnloadModuleInternal(%s) failed: 0x%08X\n", "SCE_SYSMODULE_INTERNAL_SYSTEM_SERVICE", ret);
180 | }
181 | }
182 |
183 | static bool create_context(unsigned int width, unsigned int height) {
184 | ScePglConfig pgl_config;
185 | SceWindow render_window = { 0, width, height };
186 | EGLConfig config = NULL;
187 | EGLint num_configs;
188 |
189 | EGLint attribs[] = {
190 | EGL_RED_SIZE, 8,
191 | EGL_GREEN_SIZE, 8,
192 | EGL_BLUE_SIZE, 8,
193 | EGL_ALPHA_SIZE, 8,
194 | EGL_DEPTH_SIZE, 0,
195 | EGL_STENCIL_SIZE, 0,
196 | EGL_SAMPLE_BUFFERS, 0,
197 | EGL_SAMPLES, 0,
198 | EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
199 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
200 | EGL_NONE,
201 | };
202 |
203 | EGLint ctx_attribs[] = {
204 | EGL_CONTEXT_CLIENT_VERSION, 2,
205 | EGL_NONE,
206 | };
207 |
208 | EGLint window_attribs[] = {
209 | EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
210 | EGL_NONE,
211 | };
212 |
213 | int major, minor;
214 | int ret;
215 |
216 | memset(&pgl_config, 0, sizeof(pgl_config));
217 | {
218 | pgl_config.size = sizeof(pgl_config);
219 | pgl_config.flags = SCE_PGL_FLAGS_USE_COMPOSITE_EXT | SCE_PGL_FLAGS_USE_FLEXIBLE_MEMORY | 0x60;
220 | pgl_config.processOrder = 1;
221 | pgl_config.systemSharedMemorySize = 0x200000;
222 | pgl_config.videoSharedMemorySize = 0x2400000;
223 | pgl_config.maxMappedFlexibleMemory = 0xAA00000;
224 | pgl_config.drawCommandBufferSize = 0xC0000;
225 | pgl_config.lcueResourceBufferSize = 0x10000;
226 | pgl_config.dbgPosCmd_0x40 = 1920;
227 | pgl_config.dbgPosCmd_0x44 = 1080;
228 | pgl_config.dbgPosCmd_0x48 = 0;
229 | pgl_config.dbgPosCmd_0x4C = 0;
230 | pgl_config.unk_0x5C = 2;
231 | }
232 |
233 | if (!scePigletSetConfigurationVSH(&pgl_config)) {
234 | EPRINTF("scePigletSetConfigurationVSH failed.\n");
235 | goto err;
236 | }
237 |
238 | s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
239 | if (s_display == EGL_NO_DISPLAY) {
240 | EPRINTF("eglGetDisplay failed.\n");
241 | goto err;
242 | }
243 |
244 | if (!eglInitialize(s_display, &major, &minor)) {
245 | ret = eglGetError();
246 | EPRINTF("eglInitialize failed: 0x%08X\n", ret);
247 | goto err;
248 | }
249 | printf("EGL version major:%d, minor:%d\n", major, minor);
250 |
251 | if (!eglBindAPI(EGL_OPENGL_ES_API)) {
252 | ret = eglGetError();
253 | EPRINTF("eglBindAPI failed: 0x%08X\n", ret);
254 | goto err;
255 | }
256 |
257 | if (!eglSwapInterval(s_display, 0)) {
258 | ret = eglGetError();
259 | EPRINTF("eglSwapInterval failed: 0x%08X\n", ret);
260 | goto err;
261 | }
262 |
263 | if (!eglChooseConfig(s_display, attribs, &config, 1, &num_configs)) {
264 | ret = eglGetError();
265 | EPRINTF("eglChooseConfig failed: 0x%08X\n", ret);
266 | goto err;
267 | }
268 | if (num_configs != 1) {
269 | EPRINTF("No available configuration found.\n");
270 | goto err;
271 | }
272 |
273 | s_surface = eglCreateWindowSurface(s_display, config, &render_window, window_attribs);
274 | if (s_surface == EGL_NO_SURFACE) {
275 | ret = eglGetError();
276 | EPRINTF("eglCreateWindowSurface failed: 0x%08X\n", ret);
277 | goto err;
278 | }
279 |
280 | s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, ctx_attribs);
281 | if (s_context == EGL_NO_CONTEXT) {
282 | ret = eglGetError();
283 | EPRINTF("eglCreateContext failed: 0x%08X\n", ret);
284 | goto err;
285 | }
286 |
287 | if (!eglMakeCurrent(s_display, s_surface, s_surface, s_context)) {
288 | ret = eglGetError();
289 | EPRINTF("eglMakeCurrent failed: 0x%08X\n", ret);
290 | goto err;
291 | }
292 |
293 | printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
294 | printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
295 |
296 | return true;
297 |
298 | err:
299 | return false;
300 | }
301 |
302 | static void destroy_context(void) {
303 | int ret;
304 |
305 | if (!eglDestroyContext(s_display, s_context)) {
306 | ret = eglGetError();
307 | EPRINTF("eglDestroyContext failed: 0x%08X\n", ret);
308 | }
309 | s_context = EGL_NO_CONTEXT;
310 |
311 | if (!eglDestroySurface(s_display, s_surface)) {
312 | ret = eglGetError();
313 | EPRINTF("eglDestroySurface failed: 0x%08X\n", ret);
314 | }
315 | s_surface = EGL_NO_SURFACE;
316 |
317 | if (!eglTerminate(s_display)) {
318 | ret = eglGetError();
319 | EPRINTF("eglTerminate failed: 0x%08X\n", ret);
320 | }
321 | s_display = EGL_NO_DISPLAY;
322 | }
323 |
324 | static bool create_texture(void) {
325 | int ret;
326 |
327 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
328 | ret = glGetError();
329 | if (ret) {
330 | EPRINTF("glPixelStorei failed: 0x%08X\n", ret);
331 | goto err;
332 | }
333 |
334 | glGenTextures(1, &s_texture_id);
335 | ret = glGetError();
336 | if (ret) {
337 | EPRINTF("glGenTextures failed: 0x%08X\n", ret);
338 | goto err;
339 | }
340 |
341 | glBindTexture(GL_TEXTURE_2D, s_texture_id);
342 | ret = glGetError();
343 | if (ret) {
344 | EPRINTF("glBindTexture failed: 0x%08X\n", ret);
345 | goto err;
346 | }
347 |
348 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, s_texture_data);
349 | ret = glGetError();
350 | if (ret) {
351 | EPRINTF("glTexImage2D failed: 0x%08X\n", ret);
352 | goto err;
353 | }
354 |
355 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
356 | ret = glGetError();
357 | if (ret) {
358 | EPRINTF("glTexParameteri failed: 0x%08X\n", ret);
359 | goto err;
360 | }
361 |
362 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
363 | ret = glGetError();
364 | if (ret) {
365 | EPRINTF("glTexParameteri failed: 0x%08X\n", ret);
366 | goto err;
367 | }
368 |
369 | return true;
370 |
371 | err:
372 | return false;
373 | }
374 |
375 | static bool destroy_texture(void) {
376 | int ret;
377 |
378 | glDeleteTextures(1, &s_texture_id);
379 | ret = glGetError();
380 | if (ret) {
381 | EPRINTF("glDeleteTextures failed: 0x%08X\n", ret);
382 | goto err;
383 | }
384 |
385 | return true;
386 |
387 | err:
388 | return false;
389 | }
390 |
391 | static bool compile_shader(GLenum type, const char* source, size_t length, GLuint* pShader) {
392 | GLuint shader;
393 | GLint tmp_length = (int)length;
394 | GLint success;
395 | char log_buf[256];
396 | int ret;
397 |
398 | shader = glCreateShader(type);
399 | if (!shader) {
400 | ret = glGetError();
401 | EPRINTF("glCreateShader failed: 0x%08X\n", ret);
402 | goto err;
403 | }
404 |
405 | glShaderSource(shader, 1, &source, &tmp_length);
406 | ret = glGetError();
407 | if (ret) {
408 | EPRINTF("glShaderSource failed: 0x%08X\n", ret);
409 | goto err;
410 | }
411 |
412 | glCompileShader(shader);
413 | ret = glGetError();
414 | if (ret) {
415 | EPRINTF("glCompileShader failed: 0x%08X\n", ret);
416 | goto err;
417 | }
418 |
419 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
420 | ret = glGetError();
421 | if (ret) {
422 | EPRINTF("glGetShaderiv failed: 0x%08X\n", ret);
423 | goto err;
424 | }
425 | if (!success) {
426 | glGetShaderInfoLog(shader, sizeof(log_buf), NULL, log_buf);
427 | ret = glGetError();
428 | if (ret) {
429 | EPRINTF("glGetShaderInfoLog failed: 0x%08X\n", ret);
430 | goto err;
431 | }
432 | if (strlen(log_buf) > 1) {
433 | EPRINTF("shader compilation failed with log:\n%s\n", log_buf);
434 | } else {
435 | EPRINTF("shader compilation failed\n");
436 | }
437 | goto err;
438 | }
439 |
440 | if (pShader) {
441 | *pShader = shader;
442 | }
443 |
444 | return true;
445 |
446 | err:
447 | return false;
448 | }
449 |
450 | static bool create_program(void) {
451 | GLuint vertex_shader_id = 0;
452 | GLuint fragment_shader_id = 0;
453 | GLuint program_id = 0;
454 | GLint success;
455 | char log[256];
456 | int ret;
457 |
458 | if (!compile_shader(GL_VERTEX_SHADER, s_vertex_shader_code, strlen(s_vertex_shader_code), &vertex_shader_id)) {
459 | EPRINTF("Unable to compile vertex shader.\n");
460 | goto err;
461 | }
462 |
463 | if (!compile_shader(GL_FRAGMENT_SHADER, s_fragment_shader_code, strlen(s_fragment_shader_code), &fragment_shader_id)) {
464 | EPRINTF("Unable to compile fragment shader.\n");
465 | goto err;
466 | }
467 |
468 | program_id = glCreateProgram();
469 | if (!program_id) {
470 | ret = glGetError();
471 | EPRINTF("glCreateProgram failed: 0x%08X\n", ret);
472 | goto err;
473 | }
474 |
475 | glAttachShader(program_id, vertex_shader_id);
476 | ret = glGetError();
477 | if (ret) {
478 | EPRINTF("glAttachShader(vertex_shader) failed: 0x%08X\n", ret);
479 | goto err;
480 | }
481 |
482 | glAttachShader(program_id, fragment_shader_id);
483 | ret = glGetError();
484 | if (ret) {
485 | EPRINTF("glAttachShader(fragment_shader) failed: 0x%08X\n", ret);
486 | goto err;
487 | }
488 |
489 | glLinkProgram(program_id);
490 | ret = glGetError();
491 | if (ret) {
492 | EPRINTF("glLinkProgram() failed: 0x%08X\n", ret);
493 | goto err;
494 | }
495 |
496 | glGetProgramiv(program_id, GL_LINK_STATUS, &success);
497 | ret = glGetError();
498 | if (ret) {
499 | EPRINTF("glGetProgramiv() failed: 0x%08X\n", ret);
500 | goto err;
501 | }
502 | if (!success) {
503 | glGetProgramInfoLog(program_id, sizeof(log), NULL, log);
504 | ret = glGetError();
505 | if (ret) {
506 | EPRINTF("glGetProgramInfoLog failed: 0x%08X\n", ret);
507 | goto err;
508 | }
509 | if (strlen(log) > 1) {
510 | EPRINTF("Unable to link shader program:\n%s\n", log);
511 | } else {
512 | EPRINTF("Unable to link shader program.\n");
513 | }
514 | goto err;
515 | }
516 |
517 | glDeleteShader(fragment_shader_id);
518 | ret = glGetError();
519 | if (ret) {
520 | EPRINTF("glDeleteShader(fragment_shader) failed: 0x%08X\n", ret);
521 | goto err;
522 | }
523 |
524 | glDeleteShader(vertex_shader_id);
525 | ret = glGetError();
526 | if (ret) {
527 | EPRINTF("glDeleteShader(vertex_shader) failed: 0x%08X\n", ret);
528 | goto err;
529 | }
530 |
531 | s_xyz_loc = glGetAttribLocation(program_id, "a_xyz");
532 | s_uv_loc = glGetAttribLocation(program_id, "a_uv");
533 | s_sampler_loc = glGetUniformLocation(program_id, "s_texture");
534 |
535 | s_program_id = program_id;
536 |
537 | return true;
538 |
539 | err:
540 | if (program_id > 0) {
541 | glDeleteProgram(program_id);
542 | ret = glGetError();
543 | if (ret) {
544 | EPRINTF("glDeleteProgram failed: 0x%08X\n", ret);
545 | goto err;
546 | }
547 | }
548 |
549 | if (fragment_shader_id > 0) {
550 | glDeleteShader(fragment_shader_id);
551 | ret = glGetError();
552 | if (ret) {
553 | EPRINTF("glDeleteShader(fragment_shader) failed: 0x%08X\n", ret);
554 | goto err;
555 | }
556 | fragment_shader_id = 0;
557 | }
558 |
559 | if (vertex_shader_id > 0) {
560 | glDeleteShader(vertex_shader_id);
561 | ret = glGetError();
562 | if (ret) {
563 | EPRINTF("glDeleteShader(vertex_shader) failed: 0x%08X\n", ret);
564 | goto err;
565 | }
566 | vertex_shader_id = 0;
567 | }
568 |
569 | return false;
570 | }
571 |
572 | static bool destroy_program(void) {
573 | int ret;
574 |
575 | glDeleteProgram(s_program_id);
576 | ret = glGetError();
577 | if (ret) {
578 | EPRINTF("glDeleteProgram failed: 0x%08X\n", ret);
579 | goto err;
580 | }
581 |
582 | return true;
583 |
584 | err:
585 | return false;
586 | }
587 |
588 | static bool main_loop(void) {
589 | int ret;
590 |
591 | while (1) {
592 | glClear(GL_COLOR_BUFFER_BIT);
593 | ret = glGetError();
594 | if (ret) {
595 | EPRINTF("glClear failed: 0x%08X\n", ret);
596 | goto err;
597 | }
598 |
599 | glUseProgram(s_program_id);
600 | ret = glGetError();
601 | if (ret) {
602 | EPRINTF("glUseProgram failed: 0x%08X\n", ret);
603 | goto err;
604 | }
605 |
606 | glVertexAttribPointer(s_xyz_loc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), s_obj_vertices);
607 | ret = glGetError();
608 | if (ret) {
609 | EPRINTF("glVertexAttribPointer failed: 0x%08X\n", ret);
610 | goto err;
611 | }
612 | glVertexAttribPointer(s_uv_loc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &s_obj_vertices[3]);
613 | ret = glGetError();
614 | if (ret) {
615 | EPRINTF("glVertexAttribPointer failed: 0x%08X\n", ret);
616 | goto err;
617 | }
618 |
619 | glEnableVertexAttribArray(s_xyz_loc);
620 | ret = glGetError();
621 | if (ret) {
622 | EPRINTF("glEnableVertexAttribArray failed: 0x%08X\n", ret);
623 | goto err;
624 | }
625 | glEnableVertexAttribArray(s_uv_loc);
626 | ret = glGetError();
627 | if (ret) {
628 | EPRINTF("glEnableVertexAttribArray failed: 0x%08X\n", ret);
629 | goto err;
630 | }
631 |
632 | glActiveTexture(GL_TEXTURE0);
633 | ret = glGetError();
634 | if (ret) {
635 | EPRINTF("glActiveTexture failed: 0x%08X\n", ret);
636 | goto err;
637 | }
638 | glBindTexture(GL_TEXTURE_2D, s_texture_id);
639 | ret = glGetError();
640 | if (ret) {
641 | EPRINTF("glBindTexture failed: 0x%08X\n", ret);
642 | goto err;
643 | }
644 |
645 | glUniform1i(s_sampler_loc, 0);
646 | ret = glGetError();
647 | if (ret) {
648 | EPRINTF("glUniform1i failed: 0x%08X\n", ret);
649 | goto err;
650 | }
651 |
652 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, s_obj_indices);
653 | ret = glGetError();
654 | if (ret) {
655 | EPRINTF("glDrawElements failed: 0x%08X\n", ret);
656 | goto err;
657 | }
658 |
659 | if (!eglSwapBuffers(s_display, s_surface)) {
660 | ret = eglGetError();
661 | EPRINTF("eglSwapBuffers failed: 0x%08X\n", ret);
662 | goto err;
663 | }
664 | }
665 |
666 | return true;
667 |
668 | err:
669 | return false;
670 | }
671 |
672 | int main(int argc, const char* const argv[]) {
673 | int ret;
674 |
675 | atexit(&cleanup);
676 |
677 | ret = sceSysmoduleLoadModuleInternal(SCE_SYSMODULE_INTERNAL_SYSTEM_SERVICE);
678 | if (ret) {
679 | EPRINTF("sceSysmoduleLoadModuleInternal(%s) failed: 0x%08X\n", "SCE_SYSMODULE_INTERNAL_SYSTEM_SERVICE", ret);
680 | goto err;
681 | }
682 |
683 | g_sandbox_word = sceKernelGetFsSandboxRandomWord();
684 | if (!g_sandbox_word) {
685 | EPRINTF("sceKernelGetFsSandboxRandomWord failed.\n");
686 | goto err;
687 | }
688 |
689 | if (!load_modules()) {
690 | EPRINTF("Unable to load modules.\n");
691 | goto err;
692 | }
693 | if (!do_patches()) {
694 | EPRINTF("Unable to patch modules.\n");
695 | goto err;
696 | }
697 |
698 | ret = sceSystemServiceHideSplashScreen();
699 | if (ret) {
700 | EPRINTF("sceSystemServiceHideSplashScreen failed: 0x%08X\n", ret);
701 | goto err;
702 | }
703 |
704 | if (!create_context(RENDER_WIDTH, RENDER_HEIGHT)) {
705 | EPRINTF("Unable to create context.\n");
706 | goto err;
707 | }
708 | if (!create_texture()) {
709 | EPRINTF("Unable to create texture.\n");
710 | goto err_destroy_context;
711 | }
712 | if (!create_program()) {
713 | EPRINTF("Unable to create shader program.\n");
714 | goto err_destroy_texture;
715 | }
716 |
717 | glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
718 | ret = glGetError();
719 | if (ret) {
720 | EPRINTF("glViewport failed: 0x%08X\n", ret);
721 | goto err_destroy_program;
722 | }
723 |
724 | glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
725 | ret = glGetError();
726 | if (ret) {
727 | EPRINTF("glClearColor failed: 0x%08X\n", ret);
728 | goto err_destroy_program;
729 | }
730 |
731 | if (!main_loop()) {
732 | EPRINTF("Main loop failed.\n");
733 | goto err_destroy_program;
734 | }
735 |
736 | err_destroy_program:
737 | destroy_program();
738 |
739 | err_destroy_texture:
740 | destroy_texture();
741 |
742 | err_destroy_context:
743 | destroy_context();
744 |
745 | err:
746 | return 0;
747 | }
748 |
749 | void catchReturnFromMain(int exit_code) {
750 | /* dummy */
751 | }
752 |
--------------------------------------------------------------------------------