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