diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml index 130cd76e8f55..11dfff30bd8f 100644 --- a/.github/workflows/runner.yml +++ b/.github/workflows/runner.yml @@ -49,3 +49,8 @@ jobs: name: 🔄 Switch needs: static-checks uses: ./.github/workflows/switch_builds.yml + + vita-build: + name: 🌱 PSVita + needs: static-checks + uses: ./.github/workflows/vita_builds.yml diff --git a/.github/workflows/vita_builds.yml b/.github/workflows/vita_builds.yml new file mode 100644 index 000000000000..8f3f7ea500cf --- /dev/null +++ b/.github/workflows/vita_builds.yml @@ -0,0 +1,59 @@ +name: 🌱 PSVita Builds +on: + workflow_call: + +# Global Settings +env: + GODOT_BASE_BRANCH: 3.5 + SCONSFLAGS: verbose=yes warnings=all werror=no debug_symbols=no + VITASDK: /usr/local/vitasdk + EUID: 1 makepkg + +concurrency: + group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-vita + cancel-in-progress: true + +jobs: + vita-template: + runs-on: "ubuntu-20.04" + name: Template (target=release, tools=no) + + steps: + - uses: actions/checkout@v2 + + # Azure repositories are not reliable, we need to prevent azure giving us packages. + - name: Make apt sources.list use the default Ubuntu repositories + run: | + sudo rm -f /etc/apt/sources.list.d/* + sudo cp -f misc/ci/sources.list /etc/apt/sources.list + sudo apt-get update + - name: Setup Godot build cache + uses: ./.github/actions/godot-cache + continue-on-error: true + + - name: Setup python and scons + uses: ./.github/actions/godot-deps + + - name: Setup VitaSDK + run: | + sudo apt-get install cmake libarchive-tools fakeroot zip -y + git clone https://github.com/vitasdk/vdpm + git clone https://github.com/isage/vita-packages-extra + cd vdpm + ./bootstrap-vitasdk.sh + ./install-all.sh + export PATH=$VITASDK/bin:$PATH + vdpm vita-rss-libdl + cd ../vita-packages-extra/pvr_psp2 + vita-makepkg + vdpm *-arm.tar.xz + - name: Compilation + uses: ./.github/actions/godot-build + with: + sconsflags: ${{ env.SCONSFLAGS }} + platform: vita + target: release + tools: false + + - name: Upload artifact + uses: ./.github/actions/upload-artifact diff --git a/drivers/SCsub b/drivers/SCsub index 775319781250..890877981874 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -24,7 +24,8 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["platform"] != "server": - SConscript("gles3/SCsub") + if env["platform"] != "vita": + SConscript("gles3/SCsub") SConscript("gles2/SCsub") SConscript("gles_common/SCsub") SConscript("gl_context/SCsub") diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp index 8898b99df228..fb8c8fb4672a 100644 --- a/drivers/gles2/rasterizer_gles2.cpp +++ b/drivers/gles2/rasterizer_gles2.cpp @@ -76,6 +76,10 @@ #include #include +#ifdef VITA_ENABLED +#include +#endif // VITA_ENABLED + #include #include #endif diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp index 8d0d845920f0..9610a83cfbb8 100644 --- a/drivers/gles2/rasterizer_storage_gles2.cpp +++ b/drivers/gles2/rasterizer_storage_gles2.cpp @@ -111,9 +111,17 @@ PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT #define glFramebufferTexture2DMultisample glFramebufferTexture2DMultisampleEXT #elif defined(UWP_ENABLED) + #include #define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleANGLE #define glFramebufferTexture2DMultisample glFramebufferTexture2DMultisampleANGLE + +#elif defined(VITA_ENABLED) + +#include +#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleIMG +#define glFramebufferTexture2DMultisample glFramebufferTexture2DMultisampleIMG + #endif #define GL_TEXTURE_3D 0x806F @@ -533,7 +541,7 @@ void RasterizerStorageGLES2::texture_allocate(RID p_texture, int p_width, int p_ texture->images.resize(1); } break; case VS::TEXTURE_TYPE_EXTERNAL: { -#ifdef ANDROID_ENABLED +#if defined(ANDROID_ENABLED) || defined(VITA_ENABLED) texture->target = _GL_TEXTURE_EXTERNAL_OES; #else texture->target = GL_TEXTURE_2D; @@ -1266,7 +1274,11 @@ void RasterizerStorageGLES2::sky_set_texture(RID p_sky, RID p_panorama, int p_ra glCopyTexSubImage2D(_cube_side_enum[i], lod, 0, 0, 0, 0, size, size); } +#ifdef VITA_ENABLED + size = 0; +#else // VITA_ENABLED size >>= 1; +#endif // !VITA_ENABLED mm_level--; @@ -6246,7 +6258,7 @@ void RasterizerStorageGLES2::initialize() { // If the desktop build is using S3TC, and you export / run from the IDE for android, if the device supports // S3TC it will crash trying to load these textures, as they are not exported in the APK. This is a simple way // to prevent Android devices trying to load S3TC, by faking lack of hardware support. -#if defined(ANDROID_ENABLED) || defined(IPHONE_ENABLED) +#if defined(ANDROID_ENABLED) || defined(IPHONE_ENABLED) || defined(VITA_ENABLED) config.s3tc_supported = false; #endif diff --git a/drivers/gles2/shaders/canvas.glsl b/drivers/gles2/shaders/canvas.glsl index f112ee821d97..24105b301db1 100644 --- a/drivers/gles2/shaders/canvas.glsl +++ b/drivers/gles2/shaders/canvas.glsl @@ -308,22 +308,26 @@ VERTEX_SHADER_CODE // texture2DLodEXT and textureCubeLodEXT are fragment shader specific. // Do not copy these defines in the vertex section. +#ifndef VITA_ENABLED + #ifndef USE_GLES_OVER_GL #ifdef GL_EXT_shader_texture_lod #extension GL_EXT_shader_texture_lod : enable #define texture2DLod(img, coord, lod) texture2DLodEXT(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCubeLodEXT(img, coord, lod) -#endif +#endif // GL_EXT_shader_texture_lod #endif // !USE_GLES_OVER_GL #ifdef GL_ARB_shader_texture_lod #extension GL_ARB_shader_texture_lod : enable -#endif +#endif // GL_ARB_shader_texture_lod -#if !defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod) +#endif // !VITA_ENABLED + +#if (!defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod)) || defined(VITA_ENABLED) #define texture2DLod(img, coord, lod) texture2D(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCube(img, coord, lod) -#endif +#endif // (!GL_EXT_shader_texture_lod && !GL_ARB_shader_texture_lod) || VITA_ENABLED #ifdef USE_GLES_OVER_GL #define lowp diff --git a/drivers/gles2/shaders/cubemap_filter.glsl b/drivers/gles2/shaders/cubemap_filter.glsl index f5c91cc70724..5bf16aa54a29 100644 --- a/drivers/gles2/shaders/cubemap_filter.glsl +++ b/drivers/gles2/shaders/cubemap_filter.glsl @@ -26,22 +26,26 @@ void main() { // texture2DLodEXT and textureCubeLodEXT are fragment shader specific. // Do not copy these defines in the vertex section. +#ifndef VITA_ENABLED + #ifndef USE_GLES_OVER_GL #ifdef GL_EXT_shader_texture_lod #extension GL_EXT_shader_texture_lod : enable #define texture2DLod(img, coord, lod) texture2DLodEXT(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCubeLodEXT(img, coord, lod) -#endif +#endif // GL_EXT_shader_texture_lod #endif // !USE_GLES_OVER_GL #ifdef GL_ARB_shader_texture_lod #extension GL_ARB_shader_texture_lod : enable -#endif +#endif // GL_ARB_shader_texture_lod -#if !defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod) +#endif // !VITA_ENABLED + +#if (!defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod)) || defined(VITA_ENABLED) #define texture2DLod(img, coord, lod) texture2D(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCube(img, coord, lod) -#endif +#endif // (!GL_EXT_shader_texture_lod && !GL_ARB_shader_texture_lod) || VITA_ENABLED #ifdef USE_GLES_OVER_GL #define lowp diff --git a/drivers/gles2/shaders/effect_blur.glsl b/drivers/gles2/shaders/effect_blur.glsl index 51fe0774e600..9dc36faa9f65 100644 --- a/drivers/gles2/shaders/effect_blur.glsl +++ b/drivers/gles2/shaders/effect_blur.glsl @@ -37,22 +37,26 @@ void main() { // texture2DLodEXT and textureCubeLodEXT are fragment shader specific. // Do not copy these defines in the vertex section. +#ifndef VITA_ENABLED + #ifndef USE_GLES_OVER_GL #ifdef GL_EXT_shader_texture_lod #extension GL_EXT_shader_texture_lod : enable #define texture2DLod(img, coord, lod) texture2DLodEXT(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCubeLodEXT(img, coord, lod) -#endif +#endif // GL_EXT_shader_texture_lod #endif // !USE_GLES_OVER_GL #ifdef GL_ARB_shader_texture_lod #extension GL_ARB_shader_texture_lod : enable -#endif +#endif // GL_ARB_shader_texture_lod -#if !defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod) +#endif // !VITA_ENABLED + +#if (!defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod)) || defined(VITA_ENABLED) #define texture2DLod(img, coord, lod) texture2D(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCube(img, coord, lod) -#endif +#endif // (!GL_EXT_shader_texture_lod && !GL_ARB_shader_texture_lod) || VITA_ENABLED #ifdef USE_GLES_OVER_GL #define lowp diff --git a/drivers/gles2/shaders/scene.glsl b/drivers/gles2/shaders/scene.glsl index a41cda80e1f7..3584e9bdcdee 100644 --- a/drivers/gles2/shaders/scene.glsl +++ b/drivers/gles2/shaders/scene.glsl @@ -234,7 +234,9 @@ void light_compute( m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ } */ +#ifndef VITA_ENABLED #define SRGB_APPROX(m_var) +#endif // VITA_ENABLED float NdotL = dot(N, L); float cNdotL = max(NdotL, 0.0); // clamped NdotL @@ -271,7 +273,9 @@ void light_compute( diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif +#ifndef VITA_ENABLED SRGB_APPROX(diffuse_brdf_NL) +#endif // VITA_ENABLED diffuse_interp += light_color * diffuse_brdf_NL * attenuation; @@ -727,22 +731,26 @@ VERTEX_SHADER_CODE // texture2DLodEXT and textureCubeLodEXT are fragment shader specific. // Do not copy these defines in the vertex section. +#ifndef VITA_ENABLED + #ifndef USE_GLES_OVER_GL #ifdef GL_EXT_shader_texture_lod #extension GL_EXT_shader_texture_lod : enable #define texture2DLod(img, coord, lod) texture2DLodEXT(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCubeLodEXT(img, coord, lod) -#endif +#endif // GL_EXT_shader_texture_lod #endif // !USE_GLES_OVER_GL #ifdef GL_ARB_shader_texture_lod #extension GL_ARB_shader_texture_lod : enable -#endif +#endif // GL_ARB_shader_texture_lod -#if !defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod) +#endif // !VITA_ENABLED + +#if (!defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod)) || defined(VITA_ENABLED) #define texture2DLod(img, coord, lod) texture2D(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCube(img, coord, lod) -#endif +#endif // (!GL_EXT_shader_texture_lod && !GL_ARB_shader_texture_lod) || VITA_ENABLED #ifdef USE_GLES_OVER_GL #define lowp @@ -1284,7 +1292,9 @@ void light_compute( m_var = 0.662002687 * S1 + 0.684122060 * S2 - 0.323583601 * S3 - 0.0225411470 * m_var;\ } */ +#ifndef VITA_ENABLED #define SRGB_APPROX(m_var) +#endif // VITA_ENABLED #if defined(USE_LIGHT_SHADER_CODE) // light is written by the light shader @@ -1377,7 +1387,9 @@ LIGHT_SHADER_CODE diffuse_brdf_NL = cNdotL * (1.0 / M_PI); #endif +#ifndef VITA_ENABLED SRGB_APPROX(diffuse_brdf_NL) +#endif // VITA_ENABLED diffuse_light += light_color * diffuse_color * diffuse_brdf_NL * attenuation; @@ -1457,7 +1469,9 @@ LIGHT_SHADER_CODE #endif +#ifndef VITA_ENABLED SRGB_APPROX(specular_brdf_NL) +#endif // VITA_ENABLED specular_light += specular_brdf_NL * light_color * specular_blob_intensity * attenuation; #if defined(LIGHT_USE_CLEARCOAT) diff --git a/drivers/gles2/shaders/tonemap.glsl b/drivers/gles2/shaders/tonemap.glsl index 11f1e0370628..d0e334b41cab 100644 --- a/drivers/gles2/shaders/tonemap.glsl +++ b/drivers/gles2/shaders/tonemap.glsl @@ -28,22 +28,26 @@ void main() { // texture2DLodEXT and textureCubeLodEXT are fragment shader specific. // Do not copy these defines in the vertex section. +#ifndef VITA_ENABLED + #ifndef USE_GLES_OVER_GL #ifdef GL_EXT_shader_texture_lod #extension GL_EXT_shader_texture_lod : enable #define texture2DLod(img, coord, lod) texture2DLodEXT(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCubeLodEXT(img, coord, lod) -#endif +#endif // GL_EXT_shader_texture_lod #endif // !USE_GLES_OVER_GL #ifdef GL_ARB_shader_texture_lod #extension GL_ARB_shader_texture_lod : enable -#endif +#endif // GL_ARB_shader_texture_lod -#if !defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod) +#endif // !VITA_ENABLED + +#if (!defined(GL_EXT_shader_texture_lod) && !defined(GL_ARB_shader_texture_lod)) || defined(VITA_ENABLED) #define texture2DLod(img, coord, lod) texture2D(img, coord, lod) #define textureCubeLod(img, coord, lod) textureCube(img, coord, lod) -#endif +#endif // (!GL_EXT_shader_texture_lod && !GL_ARB_shader_texture_lod) || VITA_ENABLED // Allows the use of bitshift operators for bicubic upscale #ifdef GL_EXT_gpu_shader4 diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index b9ff9be255a3..72c69a9c9abe 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -45,6 +45,11 @@ #include #endif +#ifdef VITA_ENABLED +#include +#include +#endif // VITA_ENABLED + #ifdef HAVE_MNTENT #include #endif @@ -130,6 +135,10 @@ String DirAccessUnix::get_next() { String fname = fix_unicode_name(entry->d_name); +#ifdef VITA_ENABLED +#define SCE_SO_ISDIR(m) (((m)&SCE_SO_IFMT) == SCE_SO_IFDIR) + _cisdir = SCE_SO_ISDIR(entry->d_stat.st_attr); +#else // VITA_ENABLED // Look at d_type to determine if the entry is a directory, unless // its type is unknown (the file system does not support it) or if // the type is a link, in that case we want to resolve the link to @@ -147,6 +156,7 @@ String DirAccessUnix::get_next() { } else { _cisdir = (entry->d_type == DT_DIR); } +#endif // !VITA_ENABLED _cishidden = is_hidden(fname); @@ -395,6 +405,9 @@ Error DirAccessUnix::remove(String p_path) { } bool DirAccessUnix::is_link(String p_file) { +#ifdef VITA_ENABLED + return false; +#else // VITA_ENABLED if (p_file.is_rel_path()) p_file = get_current_dir().plus_file(p_file); @@ -405,9 +418,13 @@ bool DirAccessUnix::is_link(String p_file) { return FAILED; return S_ISLNK(flags.st_mode); +#endif // !VITA_ENABLED } String DirAccessUnix::read_link(String p_file) { +#ifdef VITA_ENABLED + return p_file; +#else // VITA_ENABLED if (p_file.is_rel_path()) p_file = get_current_dir().plus_file(p_file); @@ -424,13 +441,14 @@ String DirAccessUnix::read_link(String p_file) { link.parse_utf8(buf, len); } return link; -#endif +#endif // !HORIZON_ENABLED +#endif // !VITA_ENABLED } Error DirAccessUnix::create_link(String p_source, String p_target) { -#ifdef HORIZON_ENABLED +#ifdef HOMEBREW_ENABLED return FAILED; -#else +#else // HOMEBREW_ENABLED if (p_target.is_rel_path()) p_target = get_current_dir().plus_file(p_target); @@ -442,7 +460,7 @@ Error DirAccessUnix::create_link(String p_source, String p_target) { } else { return FAILED; } -#endif +#endif // HOMEBREW_ENABLED } uint64_t DirAccessUnix::get_space_left() { diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 65d72a708682..c7dc85a799ae 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -143,7 +143,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { fcntl(fd, F_SETFD, opts | FD_CLOEXEC); #endif } -#endif +#endif // HORIZON_ENABLED last_error = OK; flags = p_mode_flags; return OK; @@ -280,15 +280,17 @@ bool FileAccessUnix::file_exists(const String &p_path) { return false; } +#ifndef VITA_ENABLED #if defined(UNIX_ENABLED) || defined(HORIZON_ENABLED) // See if we have access to the file if (access(filename.utf8().get_data(), F_OK)) { return false; } -#else +#else // UNIX_ENABLED || HORIZON_ENABLED if (_access(filename.utf8().get_data(), 4) == -1) return false; -#endif +#endif // !(UNIX_ENABLED || HORIZON_ENABLED) +#endif // !VITA_ENABLED // See if this is a regular file switch (st.st_mode & S_IFMT) { diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp index aac057126ddd..5c280e65bfc7 100644 --- a/drivers/unix/ip_unix.cpp +++ b/drivers/unix/ip_unix.cpp @@ -30,7 +30,7 @@ #include "ip_unix.h" -#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) || defined(HORIZON_ENABLED) +#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) || defined(HOMEBREW_ENABLED) #include @@ -53,16 +53,18 @@ #ifdef __FreeBSD__ #include #endif -#ifndef HORIZON_ENABLED +#ifndef HOMEBREW_ENABLED #include -#endif +#endif // !HOMEBREW_ENABLED #endif #include #include #ifdef __FreeBSD__ #include #endif +#ifndef VITA_ENABLED #include // Order is important on OpenBSD, leave as last +#endif // !VITA_ENABLED #endif static IP_Address _sockaddr2ip(struct sockaddr *p_addr) { @@ -161,7 +163,7 @@ void IP_Unix::get_local_interfaces(Map *r_interfaces) co } } -#else +#else // UWP_ENABLED void IP_Unix::get_local_interfaces(Map *r_interfaces) const { ULONG buf_size = 1024; @@ -207,19 +209,15 @@ void IP_Unix::get_local_interfaces(Map *r_interfaces) co memfree(addrs); }; -#endif +#endif // !UWP_ENABLED -#else // UNIX +#else // WINDOWS_ENABLED -#ifdef HORIZON_ENABLED void IP_Unix::get_local_interfaces(Map *r_interfaces) const { - struct ifaddrs *ifAddrStruct = nullptr; - struct ifaddrs *ifa = nullptr; +#ifdef HORIZON_ENABLED // todo: nifm -} - -#else -void IP_Unix::get_local_interfaces(Map *r_interfaces) const { +#else // HORIZON_ENABLED +#ifndef VITA_ENABLED struct ifaddrs *ifAddrStruct = nullptr; struct ifaddrs *ifa = nullptr; int family; @@ -254,9 +252,10 @@ void IP_Unix::get_local_interfaces(Map *r_interfaces) co if (ifAddrStruct != nullptr) { freeifaddrs(ifAddrStruct); } +#endif // !VITA_ENABLED +#endif // !HORIZON_ENABLED } -#endif -#endif +#endif // !WINDOWS_ENABLED void IP_Unix::make_default() { _create = _create_unix; diff --git a/drivers/unix/ip_unix.h b/drivers/unix/ip_unix.h index a3cfb4b3ae76..f49f9b7daddf 100644 --- a/drivers/unix/ip_unix.h +++ b/drivers/unix/ip_unix.h @@ -33,7 +33,7 @@ #include "core/io/ip.h" -#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) || defined(HORIZON_ENABLED) +#if defined(UNIX_ENABLED) || defined(WINDOWS_ENABLED) || defined(HOMEBREW_ENABLED) class IP_Unix : public IP { GDCLASS(IP_Unix, IP); diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 3c0b6fee8752..d22267759318 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -31,7 +31,7 @@ #include "net_socket_posix.h" #ifndef UNIX_SOCKET_UNAVAILABLE -#if defined(UNIX_ENABLED) || defined(HORIZON_ENABLED) +#if defined(UNIX_ENABLED) || defined(HOMEBREW_ENABLED) #include #include @@ -39,7 +39,9 @@ #include #include #include +#ifndef VITA_ENABLED #include +#endif // !VITA_ENABLED #include #include #ifndef NO_FCNTL @@ -50,9 +52,13 @@ #include #include -#if defined(JAVASCRIPT_ENABLED) || defined(HORIZON_ENABLED) +#if defined(JAVASCRIPT_ENABLED) || defined(HOMEBREW_ENABLED) +#ifdef VITA_ENABLED +#define IPPROTO_IPV6 41 +#define IPV6_V6ONLY 26 +#endif // VITA_ENABLED #include -#endif +#endif // JAVASCRIPT_ENABLED || HOMEBREW_ENABLED #include @@ -97,6 +103,7 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IP_Address &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); +#ifndef VITA_ENABLED if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket // IPv6 only socket with IPv4 address @@ -111,8 +118,9 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const addr6->sin6_addr = in6addr_any; } return sizeof(sockaddr_in6); - } else { // IPv4 socket - + } else // IPv4 socket +#endif // !VITA_ENABLED + { // IPv4 socket with IPv6 address ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0); @@ -275,14 +283,14 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IP_Address p_ip, St memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4); memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4); ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); +#ifndef HOMEBREW_ENABLED } else { -#ifndef HORIZON_ENABLED struct ipv6_mreq greq; int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16); greq.ipv6mr_interface = if_v6id; ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); -#endif +#endif // !HOMEBREW_ENABLED } ERR_FAIL_COND_V(ret != 0, FAILED); @@ -660,6 +668,9 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { #if defined(WINDOWS_ENABLED) || defined(NO_FCNTL) unsigned long par = p_enabled ? 0 : 1; ret = SOCK_IOCTL(_sock, FIONBIO, &par); +#elif defined(VITA_ENABLED) + int par = p_enabled ? 0 : 1; + ret = ::setsockopt(_sock, SOL_SOCKET, SO_NONBLOCK, &par, sizeof(int)); #else int opts = fcntl(_sock, F_GETFL); if (p_enabled) { @@ -727,8 +738,13 @@ bool NetSocketPosix::is_open() const { int NetSocketPosix::get_available_bytes() const { ERR_FAIL_COND_V(!is_open(), -1); +#ifdef VITA_ENABLED + ssize_t ret = ::recv(_sock, NULL, 0, MSG_PEEK); + ssize_t len = ret; +#else // VITA_ENABLED unsigned long len; int ret = SOCK_IOCTL(_sock, FIONREAD, &len); +#endif // !VITA_ENABLED if (ret == -1) { _get_socket_error(); print_verbose("Error when checking available bytes on socket."); diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp index 77a9c285206c..a61d8c84137d 100644 --- a/modules/gdnative/gdnative_library_editor_plugin.cpp +++ b/modules/gdnative/gdnative_library_editor_plugin.cpp @@ -330,6 +330,12 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() { // Frameworks is actually a folder with files. platform_ios.library_extension = "*.framework; Framework, *.xcframework; Binary Framework, *.a; Static Library, *.dylib; Dynamic Library"; platforms["iOS"] = platform_ios; + + NativePlatformConfig platform_vita; + platform_vita.name = "PlayStation Vita"; + platform_vita.entries.push_back("armv7"); + platform_vita.library_extension = "*.suprx"; + platforms["Vita"] = platform_vita; } VBoxContainer *container = memnew(VBoxContainer); diff --git a/platform/vita/SCsub b/platform/vita/SCsub new file mode 100644 index 000000000000..e8107364d5f3 --- /dev/null +++ b/platform/vita/SCsub @@ -0,0 +1,46 @@ +#!/usr/bin/env python +Import("env") +files = [ + "os_vita.cpp", + "godot_vita.cpp", + "context_egl_vita.cpp", + "joypad_vita.cpp", + "audio_driver_vita.cpp", +] + +prog = env.add_program("#bin/temp-build/godot", files) +build_target = env["target"] +env.Append( + BUILDERS={ + "VitaELF": Builder(action=env["VITASDK_BIN"] + "vita-elf-create $SOURCE $TARGET"), + "VitaEBOOT": Builder(action=env["VITASDK_BIN"] + "vita-make-fself -c -na $SOURCE $TARGET"), + } +) +stripped_elf_path = "#bin/temp-build/vita_" + env["target"] + "_stripped" +env.Command( + stripped_elf_path, prog, [Copy("$TARGET", "$SOURCE"), env["VITASDK_BIN"] + "arm-vita-eabi-strip -g $TARGET"] +) +env.VitaELF("#bin/temp-build/vita_" + env["target"] + ".velf", stripped_elf_path) +env.VitaEBOOT("#bin/temp-build/eboot.bin", "#bin/temp-build/vita_" + env["target"] + ".velf") + +env.Command( + "#bin/temp-build/vita_release/eboot.bin", + "#bin/temp-build/eboot.bin", + [ + Copy("$TARGET", "$SOURCE"), + ], +) +env.Command( + "#bin/temp-build/vita_release/", + "#platform/vita/app/", + [ + Copy("$TARGET", "$SOURCE"), + ], +) +env.Command( + "#bin/vita_release.zip", + "#bin/temp-build/vita_release", + [ + "cd $SOURCE && zip -FSr ../../../$TARGET .", + ], +) diff --git a/platform/vita/app/module/libGLESv2.suprx b/platform/vita/app/module/libGLESv2.suprx new file mode 100644 index 000000000000..93db5dbaeb8e Binary files /dev/null and b/platform/vita/app/module/libGLESv2.suprx differ diff --git a/platform/vita/app/module/libIMGEGL.suprx b/platform/vita/app/module/libIMGEGL.suprx new file mode 100644 index 000000000000..d56654ab3fdf Binary files /dev/null and b/platform/vita/app/module/libIMGEGL.suprx differ diff --git a/platform/vita/app/module/libgpu_es4_ext.suprx b/platform/vita/app/module/libgpu_es4_ext.suprx new file mode 100644 index 000000000000..abf47607974f Binary files /dev/null and b/platform/vita/app/module/libgpu_es4_ext.suprx differ diff --git a/platform/vita/app/module/libgpu_es4_kernel_ext.skprx b/platform/vita/app/module/libgpu_es4_kernel_ext.skprx new file mode 100644 index 000000000000..f4b9721d5b88 Binary files /dev/null and b/platform/vita/app/module/libgpu_es4_kernel_ext.skprx differ diff --git a/platform/vita/app/module/libpvrPSP2_WSEGL.suprx b/platform/vita/app/module/libpvrPSP2_WSEGL.suprx new file mode 100644 index 000000000000..1b8b516d9edb Binary files /dev/null and b/platform/vita/app/module/libpvrPSP2_WSEGL.suprx differ diff --git a/platform/vita/app/sce_sys/livearea/contents/template.xml b/platform/vita/app/sce_sys/livearea/contents/template.xml new file mode 100644 index 000000000000..160de1bbe9f3 --- /dev/null +++ b/platform/vita/app/sce_sys/livearea/contents/template.xml @@ -0,0 +1,10 @@ + + + + + bg.png + + + startup.png + + diff --git a/platform/vita/audio_driver_vita.cpp b/platform/vita/audio_driver_vita.cpp new file mode 100644 index 000000000000..7d2903ade0c5 --- /dev/null +++ b/platform/vita/audio_driver_vita.cpp @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* audio_driver_vita.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "audio_driver_vita.h" + +#include "core/os/os.h" +#include "core/project_settings.h" + +#include +#include + +static int portId = -1; + +Error AudioDriverVita::init_device() { + int latency = GLOBAL_GET("audio/output_latency"); + mix_rate = GLOBAL_GET("audio/mix_rate"); + channels = 2; + portId = -1; + speaker_mode = SPEAKER_MODE_STEREO; + buffer_size = closest_power_of_2(latency * mix_rate / 1000); + samples_in.resize(buffer_size * channels); + samples_out.resize(buffer_size * channels); + + portId = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_BGM, buffer_size, mix_rate, SCE_AUDIO_OUT_MODE_STEREO); + if (portId < 0) { + return FAILED; + } + + return OK; +} + +Error AudioDriverVita::init() { + active = false; + thread_exited = false; + exit_thread = false; + + Error err = init_device(); + if (err == OK) { + thread.start(AudioDriverVita::thread_func, this); + } + + return err; +} + +void AudioDriverVita::thread_func(void *p_udata) { + AudioDriverVita *ad = (AudioDriverVita *)p_udata; + + while (!ad->exit_thread) { + ad->lock(); + ad->start_counting_ticks(); + + if (!ad->active) { + for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) { + ad->samples_out.write[i] = 0; + } + } else { + ad->audio_server_process(ad->buffer_size, ad->samples_in.ptrw()); + for (unsigned int i = 0; i < ad->buffer_size * ad->channels; i++) { + ad->samples_out.write[i] = ad->samples_in[i] >> 16; + } + } + + sceAudioOutOutput(portId, ad->samples_out.ptr()); + + ad->stop_counting_ticks(); + ad->unlock(); + OS::get_singleton()->delay_usec(1000); + } + + ad->thread_exited = true; +} + +void AudioDriverVita::start() { + active = true; +} + +int AudioDriverVita::get_mix_rate() const { + return mix_rate; +} + +AudioDriver::SpeakerMode AudioDriverVita::get_speaker_mode() const { + return speaker_mode; +} + +Array AudioDriverVita::get_device_list() { + Array list; + list.push_back("Default"); + return list; +} + +String AudioDriverVita::get_device() { + return device_name; +} + +void AudioDriverVita::set_device(String device) { + lock(); + new_device = device; + unlock(); +} + +void AudioDriverVita::lock() { + mutex.lock(); +} + +void AudioDriverVita::unlock() { + mutex.unlock(); +} + +void AudioDriverVita::finish() { + exit_thread = true; + thread.wait_to_finish(); + + sceAudioOutOutput(portId, NULL); + sceAudioOutReleasePort(portId); +} + +AudioDriverVita::AudioDriverVita() : + device_name("Default"), + new_device("Default") { +} + +AudioDriverVita::~AudioDriverVita() { +} diff --git a/platform/vita/audio_driver_vita.h b/platform/vita/audio_driver_vita.h new file mode 100644 index 000000000000..878604d28022 --- /dev/null +++ b/platform/vita/audio_driver_vita.h @@ -0,0 +1,85 @@ +/**************************************************************************/ +/* audio_driver_vita.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef AUDIO_DRIVER_VITA_H +#define AUDIO_DRIVER_VITA_H + +#include "servers/audio_server.h" + +#include + +#include "core/os/mutex.h" +#include "core/os/thread.h" + +class AudioDriverVita : public AudioDriver { + Thread thread; + Mutex mutex; + + unsigned int buffer_size; + Vector samples_in; + Vector samples_out; + + String device_name; + String new_device; + + Error init_device(); + void finish_device(); + + static void thread_func(void *p_udata); + + unsigned int mix_rate; + SpeakerMode speaker_mode; + int channels; + + bool active; + bool thread_exited; + mutable bool exit_thread; + +public: + const char *get_name() const { + return "Vita"; + }; + + virtual Error init(); + virtual void start(); + virtual int get_mix_rate() const; + virtual SpeakerMode get_speaker_mode() const; + virtual Array get_device_list(); + virtual String get_device(); + virtual void set_device(String device); + virtual void lock(); + virtual void unlock(); + virtual void finish(); + + AudioDriverVita(); + ~AudioDriverVita(); +}; + +#endif // AUDIO_DRIVER_VITA_H diff --git a/platform/vita/context_egl_vita.cpp b/platform/vita/context_egl_vita.cpp new file mode 100644 index 000000000000..2ff9024f9057 --- /dev/null +++ b/platform/vita/context_egl_vita.cpp @@ -0,0 +1,165 @@ +/**************************************************************************/ +/* context_egl_vita.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "context_egl_vita.h" + +void ContextEGL_Vita::release_current() { + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context); +} + +void ContextEGL_Vita::make_current() { + eglMakeCurrent(display, surface, surface, context); +} + +int ContextEGL_Vita::get_window_width() { + return width; +} + +int ContextEGL_Vita::get_window_height() { + return height; +} + +void ContextEGL_Vita::reset() { + cleanup(); + initialize(); +} + +void ContextEGL_Vita::swap_buffers() { + if (eglSwapBuffers(display, surface) != EGL_TRUE) { + cleanup(); + initialize(); + } +}; + +Error ContextEGL_Vita::initialize() { + // Get an appropriate EGL framebuffer configuration + static const EGLint attributeList[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 8, + EGL_STENCIL_SIZE, 8, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + static const EGLint contextAttributeList[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLConfig config = nullptr; + EGLint numConfigs = 0; + + // Initialize PVR_PSP2 + PVRSRV_PSP2_APPHINT hint; + + sceKernelLoadStartModule("app0:module/libgpu_es4_ext.suprx", 0, NULL, 0, NULL, NULL); + sceKernelLoadStartModule("app0:module/libIMGEGL.suprx", 0, NULL, 0, NULL, NULL); + + PVRSRVInitializeAppHint(&hint); + hint.ui32SwTexOpCleanupDelay = 16000; + PVRSRVCreateVirtualAppHint(&hint); + + // Connect to the EGL default display + display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!display) { + goto _fail0; + } + + // Initialize the display + eglInitialize(display, NULL, NULL); + + // Get avaiable EGL framebuffer configurations + eglChooseConfig(display, attributeList, &config, 1, &numConfigs); + if (numConfigs == 0) { + goto _fail1; + } + + // Create an EGL window surface + window.type = PSP2_DRAWABLE_TYPE_WINDOW; + window.numFlipBuffers = 2; + window.flipChainThrdAffinity = 0x20000; + window.windowSize = PSP2_WINDOW_960X544; + + // Create a window surface + surface = eglCreateWindowSurface(display, config, &window, NULL); + if (!surface) { + goto _fail1; + } + + // Create an EGL rendering context + context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributeList); + if (!context) { + goto _fail2; + } + + // Connect the context to the surface + eglMakeCurrent(display, surface, surface, context); + + eglQuerySurface(display, surface, EGL_WIDTH, &width); + eglQuerySurface(display, surface, EGL_HEIGHT, &height); + + return OK; + +_fail2: + eglDestroySurface(display, surface); + surface = NULL; +_fail1: + eglTerminate(display); + display = NULL; +_fail0: + return ERR_UNCONFIGURED; +} + +void ContextEGL_Vita::cleanup() { + if (display != EGL_NO_DISPLAY && surface != EGL_NO_SURFACE) { + eglDestroySurface(display, surface); + surface = EGL_NO_SURFACE; + } + + if (display != EGL_NO_DISPLAY && context != EGL_NO_CONTEXT) { + eglDestroyContext(display, context); + context = EGL_NO_CONTEXT; + } + + if (display != EGL_NO_DISPLAY) { + eglTerminate(display); + display = EGL_NO_DISPLAY; + } +} + +ContextEGL_Vita::ContextEGL_Vita(bool gles) : + gles2_context(gles) {} + +ContextEGL_Vita::~ContextEGL_Vita() { + cleanup(); +} diff --git a/platform/vita/context_egl_vita.h b/platform/vita/context_egl_vita.h new file mode 100644 index 000000000000..67793b45836f --- /dev/null +++ b/platform/vita/context_egl_vita.h @@ -0,0 +1,79 @@ +/**************************************************************************/ +/* context_egl_vita.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef CONTEXT_EGL_VITA_H +#define CONTEXT_EGL_VITA_H + +#include "core/os/os.h" +#include +#include +#include + +#include // EGL library + +extern "C" { +#include +} + +class ContextEGL_Vita { + Psp2NativeWindow window; + + EGLDisplay display; + EGLContext context; + EGLSurface surface; + + EGLint width; + EGLint height; + + bool vsync; + bool gles2_context; + +public: + void release_current(); + + void make_current(); + + int get_window_width(); + int get_window_height(); + void swap_buffers(); + + void set_use_vsync(bool use) { vsync = use; } + bool is_using_vsync() const { return vsync; } + + Error initialize(); + void reset(); + + void cleanup(); + + ContextEGL_Vita(bool gles2); + ~ContextEGL_Vita(); +}; + +#endif // CONTEXT_EGL_VITA_H diff --git a/platform/vita/detect.py b/platform/vita/detect.py new file mode 100644 index 000000000000..1922ac071dde --- /dev/null +++ b/platform/vita/detect.py @@ -0,0 +1,178 @@ +import os +import platform +import sys +import os.path + + +def is_active(): + return True + + +def get_name(): + return "Vita" + + +def can_build(): + # Check the minimal dependencies + vitasdk = os.environ.get("VITASDK", "/usr/local/vitasdk") + if not os.path.exists(vitasdk): + print("VITASDK not found. Vita disabled.") + return False + return True + + +def get_opts(): + + from SCons.Variables import BoolVariable, EnumVariable + + return [ + BoolVariable("use_sanitizer", "Use LLVM compiler address sanitizer", False), + BoolVariable("use_leak_sanitizer", "Use LLVM compiler memory leaks sanitizer (implies use_sanitizer)", False), + EnumVariable("debug_symbols", "Add debugging symbols to release builds", "no", ("yes", "no", "full")), + BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), + BoolVariable("touch", "Enable touch events", True), + ] + + +def get_flags(): + return [ + ("tools", False), + ("builtin_bullet", True), + ("builtin_enet", True), # Not in VitaSDK. + ("builtin_libogg", False), + ("builtin_libvpx", False), + ("builtin_libwebsockets", True), # Not in VitaSDK. + ("builtin_mbedtls", False), # VitaSDK package isn't the correct version btw + ("builtin_miniupnpc", False), + ("builtin_opus", False), + ("builtin_pcre2", False), + ("builtin_pcre2_with_jit", False), + ("builtin_squish", True), # Not in VitaSDK. + ("builtin_zstd", False), + ("module_mbedtls_enabled", False), + ("module_upnp_enabled", False), + ("module_enet_enabled", False), + ("module_gdnative_enabled", True), + ("module_regex_enabled", True), + ("module_webm_enabled", False), + ("module_mobile_vr_enabled", False), + ] + + +def configure(env): + vita_sdk_path = os.environ.get("VITASDK", "/usr/local/vitasdk") + + env["CC"] = vita_sdk_path + "/bin/arm-vita-eabi-gcc" + env["CXX"] = vita_sdk_path + "/bin/arm-vita-eabi-g++" + env["LD"] = vita_sdk_path + "/bin/arm-vita-eabi-ld" + env["AR"] = vita_sdk_path + "/bin/arm-vita-eabi-ar" + env["STRIP"] = vita_sdk_path + "/bin/arm-vita-eabi-strip" + env["RANLIB"] = vita_sdk_path + "/bin/arm-vita-eabi-ranlib" + ## Build type + + pkg_config_path = "{}/arm-vita-eabi/lib/pkgconfig/pkgconfig" + + os.environ["PKG_CONFIG_PATH"] = pkg_config_path + env["ENV"]["PKG_CONFIG_PATH"] = pkg_config_path + + env["VITASDK_BIN"] = "{}/bin/".format(vita_sdk_path) + + env.Prepend(CPPPATH=["{}/arm-vita-eabi/include".format(vita_sdk_path)]) + env.Prepend(CPPPATH=["{}/arm-vita-eabi/include/freetype2".format(vita_sdk_path)]) + env.Prepend(CPPPATH=["{}/share/gcc-arm-vita-eabi/samples/common".format(vita_sdk_path)]) + env.Append(LIBPATH=["{}/arm-vita-eabi/lib".format(vita_sdk_path)]) + env.Append(LINKFLAGS=["-Wl,-q,-whole-archive", "-lpthread", "-Wl,-q,-no-whole-archive"]) + print(env.get("CCFLAGS")) + + env.Prepend( + CCFLAGS=[ + "-Wl,-q", + "-D_POSIX_TIMERS", + "-DPTHREAD_ENABLED", + "-DHOMEBREW_ENABLED", + "-DVITA_ENABLED", + "-DPOSH_COMPILER_GCC", + "-DPOSH_OS_VITA", + '-DPOSH_OS_STRING=\\"vita\\"', + "-D__psp2__", + ] + ) + + if env["target"] == "release": + # -O3 -ffast-math is identical to -Ofast. We need to split it out so we can selectively disable + # -ffast-math in code for which it generates wrong results. + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O2", "-ffast-math"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) + + if env["debug_symbols"] == "yes": + env.Prepend(CCFLAGS=["-g1"]) + if env["debug_symbols"] == "full": + env.Prepend(CCFLAGS=["-g2"]) + + elif env["target"] == "release_debug": + if env["optimize"] == "speed": # optimize for speed (default) + env.Prepend(CCFLAGS=["-O2", "-ffast-math", "-DDEBUG_ENABLED"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os", "-DDEBUG_ENABLED"]) + + if env["debug_symbols"] == "yes": + env.Prepend(CCFLAGS=["-g1"]) + if env["debug_symbols"] == "full": + env.Prepend(CCFLAGS=["-g2"]) + + elif env["target"] == "debug": + env.Prepend(CCFLAGS=["-g3", "-DDEBUG_ENABLED", "-DDEBUG_MEMORY_ENABLED"]) + # env.Append(LINKFLAGS=['-rdynamic']) + + # env.Append(LINKFLAGS=['-rdynamic']) + + ## Architecture + + env["bits"] = "32" + + ## Flags + + # Linkflags below this line should typically stay the last ones + # if not env['builtin_zlib']: + # env.ParseConfig('aarch64-none-elf-pkg-config zlib --cflags --libs') + + env.Append(CPPPATH=["#platform/vita"]) + env.Append(CPPFLAGS=["-DLIBC_FILEIO_ENABLED", "-DGLES_ENABLED", "-DGL_GLEXT_PROTOTYPES"]) + env.Append(CPPFLAGS=["-DPTHREAD_NO_RENAME"]) + env.Append(CCFLAGS=["-mtune=cortex-a9", "-mfpu=neon", "-fpermissive", "-ftree-vectorize", "-Wno-attributes"]) + env.Append( + LIBS=[ + "dl", + "taihen_stub", + "SceLibKernel_stub", + "SceKernelThreadMgr_stub", + "SceSblSsMgr_stub", + "SceAppMgr_stub", + "SceIofilemgr_stub", + "SceSysmodule_stub", + "SceDisplay_stub", + "SceFios2_stub", + "SceCtrl_stub", + "SceMotion_stub", + "SceTouch_stub", + "SceIme_stub", + "SceAudio_stub", + "ScePower_stub", + "jpeg", + "png", + "freetype", + "opus", + "vorbis", + "ogg", + "z", + "zstd", + "pcre2-32", + "theora", + "-llibgpu_es4_ext_stub.a", + "-llibIMGEGL_stub.a", + "-llibGLESv2_stub.a", + ] + ) + print(env.get("LIBS")) diff --git a/platform/vita/export/export.cpp b/platform/vita/export/export.cpp new file mode 100644 index 000000000000..b07f784afbe5 --- /dev/null +++ b/platform/vita/export/export.cpp @@ -0,0 +1,360 @@ +/**************************************************************************/ +/* export.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "export.h" +#include "core/io/zip_io.h" +#include "core/version.h" + +#define TEMPLATE_RELEASE "vita_release.zip" + +class ExportPluginVita : public EditorExportPlugin { +public: + Vector editor_id_vec; + +protected: + virtual void _export_begin(const Set &p_features, bool p_debug, const String &p_path, int p_flags) { + if (editor_id_vec.size() != 0) { + add_file("custom_editor_id", editor_id_vec, false); + } + } +}; + +class EditorExportPlatformVita : public EditorExportPlatform { + GDCLASS(EditorExportPlatformVita, EditorExportPlatform) + + Ref logo; + + ExportPluginVita *export_plugin; + +public: + virtual void get_preset_features(const Ref &p_preset, List *r_features) { + String driver = ProjectSettings::get_singleton()->get("rendering/quality/driver/driver_name"); + if ((driver == "GLES2") || (driver == "GLES3" && ProjectSettings::get_singleton()->get("rendering/quality/driver/fallback_to_gles2"))) { + if (p_preset->get("texture_format/etc")) { + r_features->push_back("etc"); + } + if (p_preset->get("texture_format/pvrtc")) { + r_features->push_back("pvrtc"); + } + } + } + + virtual void get_export_options(List *r_options) { + String title = ProjectSettings::get_singleton()->get("application/config/name"); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/pvrtc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "param_sfo/title", PROPERTY_HINT_PLACEHOLDER_TEXT, title), title)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "param_sfo/title_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "GDOT00001 (Make sure it's CAPITALIZED and 9 characters MAX"), "GDOT00001")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "param_sfo/parental_level", PROPERTY_HINT_MAX, "11"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "param_sfo/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Version XX.YY"), "01.00")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "assets/bubble_icon_128x128", PROPERTY_HINT_GLOBAL_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "assets/app_splash_960x544", PROPERTY_HINT_GLOBAL_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "assets/livearea_bg_840x500", PROPERTY_HINT_GLOBAL_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "assets/livearea_startup_button_280x158", PROPERTY_HINT_GLOBAL_FILE, "*.png"), "")); + } + + virtual String get_name() const { + return "PlayStation Vita"; + } + + virtual String get_os_name() const { + return "Vita"; + } + + virtual Ref get_logo() const { + return logo; + } + + virtual Ref get_run_icon() const { + return logo; + } + + virtual bool poll_devices() { + return false; + } + + virtual int get_device_count() const { + return 2; + } + + virtual Error run(const Ref &p_preset, int p_device, int p_debug_flags) { + return OK; + } + + virtual bool can_export(const Ref &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + r_missing_templates = + find_export_template(TEMPLATE_RELEASE) == String(); + + bool valid = !r_missing_templates; + + if (!err.empty()) { + r_error = err; + } + + return valid; + } + + virtual bool has_valid_project_configuration(const Ref &p_preset, String &r_error) const { + return true; + } + + virtual List get_binary_extensions(const Ref &p_preset) const { + List list; + list.push_back("vpk"); + return list; + } + + void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder) { + String dir = p_root_path.plus_file(p_folder); + + DirAccess *da = DirAccess::open(dir); + da->list_dir_begin(); + String f; + while ((f = da->get_next()) != "") { + if (f == "." || f == "..") { + continue; + } + if (da->is_link(f)) { + } else if (da->current_is_dir()) { + _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f)); + } else { + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.min; + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_sec = time.sec; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + // 0100000: regular file type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = 0100777; + zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + FileAccessRef fa = FileAccess::open(dir.plus_file(f), FileAccess::READ); + if (!fa) { + add_message(EXPORT_MESSAGE_ERROR, TTR("ZIP Creation"), vformat(TTR("Could not open file to read from path \"%s\"."), dir.plus_file(f))); + return; + } + const int bufsize = 16384; + uint8_t buf[bufsize]; + + while (true) { + uint64_t got = fa->get_buffer(buf, bufsize); + if (got == 0) { + break; + } + zipWriteInFileInZip(p_zip, buf, got); + } + + zipCloseFileInZip(p_zip); + } + } + da->list_dir_end(); + memdelete(da); + } + + void create_vpk(String outVpk, String dir) { + FileAccess *dst_f = nullptr; + zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); + zipFile zip = zipOpen2(outVpk.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); + _zip_folder_recursive(zip, dir, ""); + zipClose(zip, NULL); + } + + virtual Error export_project(const Ref &p_preset, bool p_debug, const String &p_path, int p_flags = 0) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + const String base_dir = p_path.get_base_dir(); + const String base_path = p_path.get_basename(); + const String base_name = p_path.get_file().get_basename(); + + if (!DirAccess::exists(base_dir)) { + return ERR_FILE_BAD_PATH; + } + + String template_path = find_export_template(TEMPLATE_RELEASE); + if (template_path != String() && !FileAccess::exists(template_path)) { + add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Template file not found: \"%s\"."), template_path)); + return ERR_FILE_NOT_FOUND; + } + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + Error err; + // update nro icon/title/author/version + ParamSFOStruct *sfo = memnew(ParamSFOStruct); + + sfo->title = p_preset->get("param_sfo/title"); + sfo->title_id = p_preset->get("param_sfo/title_id"); + sfo->version = p_preset->get("param_sfo/version"); + sfo->parental_level = p_preset->get("param_sfo/parental_level"); + + String icon = p_preset->get("assets/bubble_icon_128x128"); + String splash = p_preset->get("assets/launch_splash_960x544"); + String livearea_bg = p_preset->get("assets/livearea_bg_840x500"); + String livearea_startup_button = p_preset->get("assets/livearea_startup_button_280x158"); + + String cache = EditorSettings::get_singleton()->get_cache_dir(); + String app_dir = cache.plus_file("app"); + da->make_dir(app_dir); + String game_data_dir = app_dir.plus_file("game_data"); + da->make_dir(game_data_dir); + da->make_dir(app_dir.plus_file("module")); + da->make_dir_recursive(app_dir.plus_file("sce_sys/livearea/contents")); + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + unzFile pkg = unzOpen2(template_path.utf8().get_data(), &io); + + if (!pkg) { + EditorNode::add_io_error("Could not find vita template for exporting:\n" + template_path); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(pkg); + + while (ret == UNZ_OK) { + // get file name + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String path = String::utf8(fname); + + if (path.ends_with("/")) { + // Ignore directories + ret = unzGoToNextFile(pkg); + continue; + } + + Vector data; + bool do_read = true; + + //read + if (do_read) { + data.resize(info.uncompressed_size); + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + } + + print_line("ADDING: " + path); + + FileAccess *fa = FileAccess::open(app_dir.plus_file(path), FileAccess::WRITE); + fa->store_buffer(data.ptr(), data.size()); + fa->flush(); + fa->close(); + + ret = unzGoToNextFile(pkg); + } + + unzClose(pkg); + + //String current_version = VERSION_FULL_CONFIG; + //String template_path = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version); + + err = save_pack(p_preset, game_data_dir.plus_file("game.pck")); + mksfoex(sfo, app_dir.plus_file("sce_sys")); + if (err == OK) { + if (icon != String() && FileAccess::exists(icon)) { + da->copy(icon, app_dir.plus_file("sce_sys/icon0.png")); + } + if (splash != String() && FileAccess::exists(splash)) { + da->copy(splash, app_dir.plus_file("sce_sys/pic0.png")); + } + if (livearea_bg != String() && FileAccess::exists(livearea_bg)) { + da->copy(livearea_bg, app_dir.plus_file("sce_sys/livearea/contents/bg.png")); + } + if (livearea_startup_button != String() && FileAccess::exists(livearea_startup_button)) { + da->copy(livearea_startup_button, app_dir.plus_file("sce_sys/livearea/contents/startup.png")); + } + } + + create_vpk(sfo->title + ".vpk", app_dir); + memdelete(sfo); + memdelete(da); + da = nullptr; + + return OK; + } + + virtual void get_platform_features(List *r_features) { + r_features->push_back("mobile"); + r_features->push_back(get_os_name()); + } + + virtual void resolve_platform_feature_priorities(const Ref &p_preset, Set &p_features) { + } + + EditorExportPlatformVita() { + Ref img = memnew(Image(_vita_logo)); + logo.instance(); + logo->create_from_image(img); + + export_plugin = memnew(ExportPluginVita); + EditorExport::get_singleton()->add_export_plugin(export_plugin); + } + + ~EditorExportPlatformVita() { + } +}; + +void register_vita_exporter() { + Ref exporter; + exporter.instance(); + EditorExport::get_singleton()->add_export_platform(exporter); +} diff --git a/platform/vita/export/export.h b/platform/vita/export/export.h new file mode 100644 index 000000000000..86d1be7eda65 --- /dev/null +++ b/platform/vita/export/export.h @@ -0,0 +1,52 @@ +/**************************************************************************/ +/* export.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef VITA_EXPORT_H +#define VITA_EXPORT_H + +#include "core/io/file_access_memory.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "platform/vita/logo.gen.h" +#include "scene/resources/texture.h" + +typedef struct ParamSFOStruct { + String title; + String title_long; + String title_id; + String author; + String version; + int parental_level; +} ParamSFOStruct; + +int mksfoex(ParamSFOStruct *sfo, String outDir); +void register_vita_exporter(); + +#endif // VITA_EXPORT_H diff --git a/platform/vita/export/types.h b/platform/vita/export/types.h new file mode 100644 index 000000000000..096771e2457e --- /dev/null +++ b/platform/vita/export/types.h @@ -0,0 +1,170 @@ +/**************************************************************************/ +/* types.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +/* + * PSP Software Development Kit - http://www.pspdev.org + * ----------------------------------------------------------------------- + * Licensed under the BSD license, see LICENSE in PSPSDK root for details. + * + * types.h - Definition of basic cross platform types. + * + * Copyright (c) 2005 James Forshaw + * + * $Id: types.h 2333 2007-10-31 19:37:40Z tyranid $ + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include + +/* Re-define some system types */ +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#ifdef WORDS_BIGENDIAN +inline u32 lw_le(u32 data) { + u8 *ptr; + u32 val; + + ptr = (u8 *)&data; + + val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); + + return val; +} + +inline u16 lh_le(u16 data) { + u8 *ptr; + u16 val; + + ptr = (u8 *)&data; + + val = ptr[0] | (ptr[1] << 8); + + return val; +} + +#define LW_LE(x) (lw_le((x))) +#define LW_BE(x) (x) +#define LH_LE(x) (lh_le((x))) +#define LH_BE(x) (x) + +#else + +inline u32 lw_be(u32 data) { + u8 *ptr; + u32 val; + + ptr = (u8 *)&data; + + val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; + + return val; +} + +inline u16 lh_be(u16 data) { + u8 *ptr; + u16 val; + + ptr = (u8 *)&data; + + val = (ptr[0] << 16) | ptr[1]; + + return val; +} + +#define LW_LE(x) (x) +#define LW_BE(x) (lw_be((x))) +#define LH_LE(x) (x) +#define LH_BE(x) (lh_be((x))) + +#endif + +#define LW(x) (LW_LE(x)) +#define LH(x) (LH_LE(x)) + +#ifdef WORDS_BIGENDIAN +inline void sw_le(u32 *data, u32 val) { + u8 *ptr = (u8 *)data; + + ptr[0] = (u8)(val & 0xFF); + ptr[1] = (u8)((val >> 8) & 0xFF); + ptr[2] = (u8)((val >> 16) & 0xFF); + ptr[3] = (u8)((val >> 24) & 0xFF); +} + +inline void sh_le(u16 *data, u16 val) { + u8 *ptr = (u8 *)data; + + ptr[0] = (u8)(val & 0xFF); + ptr[1] = (u8)((val >> 8) & 0xFF); +} + +#define SW_LE(x, v) (sw_le((x), (v))) +#define SW_BE(x, v) (*(x) = (v)) +#define SH_LE(x, v) (sh_le((x), (v))) +#define SH_BE(x, v) (*(x) = (v)) + +#else + +inline void sw_be(u32 *data, u32 val) { + u8 *ptr = (u8 *)data; + + ptr[0] = (u8)((val >> 24) & 0xFF); + ptr[1] = (u8)((val >> 16) & 0xFF); + ptr[2] = (u8)((val >> 8) & 0xFF); + ptr[3] = (u8)(val & 0xFF); +} + +inline void sh_be(u16 *data, u16 val) { + u8 *ptr = (u8 *)data; + + ptr[0] = (u8)((val >> 8) & 0xFF); + ptr[1] = (u8)(val & 0xFF); +} + +#define SW_LE(x, v) (*(x) = (v)) +#define SW_BE(x, v) (sw_be((x), (v))) +#define SH_LE(x, v) (*(x) = (v)) +#define SH_BE(x, v) (sh_be((x), (v))) + +#endif + +#define SW(x, v) (SW_LE(x, v)) +#define SH(x, v) (SH_LE(x, v)) + +#endif diff --git a/platform/vita/export/vita-mksfoex.cpp b/platform/vita/export/vita-mksfoex.cpp new file mode 100644 index 000000000000..3c5edea09fe2 --- /dev/null +++ b/platform/vita/export/vita-mksfoex.cpp @@ -0,0 +1,333 @@ +/**************************************************************************/ +/* vita-mksfoex.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +/* +# _____ ___ ____ ___ ____ +# ____| | ____| | | |____| +# | ___| | ___| ____| | \ PSPDEV Open Source Project. +#----------------------------------------------------------------------- +# Review pspsdk README & LICENSE files for further details. +# +# New and improved mksfo +# $Id$ +*/ +#define _CRT_SECURE_NO_DEPRECATE + +#include "export.h" +#include "types.h" +#include +#include +#include +#include + +#define PSF_MAGIC 0x46535000 +#define PSF_VERSION 0x00000101 + +struct SfoHeader { + uint32_t magic; + uint32_t version; + uint32_t keyofs; + uint32_t valofs; + uint32_t count; +}; + +struct SfoEntry { + uint16_t nameofs; + uint8_t alignment; + uint8_t type; + uint32_t valsize; + uint32_t totalsize; + uint32_t dataofs; +}; + +#define PSF_TYPE_BIN 0 +#define PSF_TYPE_STR 2 +#define PSF_TYPE_VAL 4 + +struct EntryContainer { + const char *name; + int type; + uint32_t value; + const char *data; +}; + +struct EntryContainer g_defaults[] = { + { "APP_VER", PSF_TYPE_STR, 0, "00.00" }, + { "ATTRIBUTE", PSF_TYPE_VAL, 0x8000, NULL }, + { "ATTRIBUTE2", PSF_TYPE_VAL, 0, NULL }, + { "ATTRIBUTE_MINOR", PSF_TYPE_VAL, 0x10, NULL }, + { "BOOT_FILE", PSF_TYPE_STR, 32, "" }, + { "CATEGORY", PSF_TYPE_STR, 0, "gd" }, + { "CONTENT_ID", PSF_TYPE_STR, 48, "" }, + { "EBOOT_APP_MEMSIZE", PSF_TYPE_VAL, 0, NULL }, + { "EBOOT_ATTRIBUTE", PSF_TYPE_VAL, 0, NULL }, + { "EBOOT_PHY_MEMSIZE", PSF_TYPE_VAL, 0, NULL }, + { "LAREA_TYPE", PSF_TYPE_VAL, 0, NULL }, + { "NP_COMMUNICATION_ID", PSF_TYPE_STR, 16, "" }, + { "PARENTAL_LEVEL", PSF_TYPE_VAL, 0, NULL }, + { "PSP2_DISP_VER", PSF_TYPE_STR, 0, "00.000" }, + { "PSP2_SYSTEM_VER", PSF_TYPE_VAL, 0, NULL }, + { "STITLE", PSF_TYPE_STR, 52, "Homebrew" }, + { "TITLE", PSF_TYPE_STR, 0x80, "Homebrew" }, + { "TITLE_ID", PSF_TYPE_STR, 0, "ABCD99999" }, + { "VERSION", PSF_TYPE_STR, 0, "00.00" }, +}; + +#define MAX_OPTIONS (256) + +static const char *g_title = NULL; +static int g_empty = 0; +static struct EntryContainer g_vals[MAX_OPTIONS]; + +struct EntryContainer *find_free() { + int i; + + for (i = 0; i < MAX_OPTIONS; i++) { + if (g_vals[i].name == NULL) { + return &g_vals[i]; + } + } + + return NULL; +} + +struct EntryContainer *find_name(const char *name) { + int i; + + for (i = 0; i < MAX_OPTIONS; i++) { + if ((g_vals[i].name != NULL) && (strcmp(g_vals[i].name, name) == 0)) { + return &g_vals[i]; + } + } + + return NULL; +} + +int add_string(char *str) { + char *equals = NULL; + struct EntryContainer *entry; + + equals = strchr(str, '='); + if (equals == NULL) { + fprintf(stderr, "Invalid option (no =)\n"); + return 0; + } + + *equals++ = 0; + + if ((entry = find_name(str))) { + entry->data = equals; + } else { + entry = find_free(); + if (entry == NULL) { + fprintf(stderr, "Maximum options reached\n"); + return 0; + } + + memset(entry, 0, sizeof(struct EntryContainer)); + entry->name = str; + entry->type = PSF_TYPE_STR; + entry->data = equals; + } + + return 1; +} + +int add_dword(char *str) { + char *equals = NULL; + struct EntryContainer *entry; + + equals = strchr(str, '='); + if (equals == NULL) { + fprintf(stderr, "Invalid option (no =)\n"); + return 0; + } + + *equals++ = 0; + + if ((entry = find_name(str))) { + entry->value = strtoul(equals, NULL, 0); + } else { + entry = find_free(); + if (entry == NULL) { + fprintf(stderr, "Maximum options reached\n"); + return 0; + } + + memset(entry, 0, sizeof(struct EntryContainer)); + entry->name = str; + entry->type = PSF_TYPE_VAL; + entry->value = strtoul(equals, NULL, 0); + } + + return 1; +} + +int mksfoex(ParamSFOStruct *sfo, String outDir) { + FILE *fp; + unsigned int i; + char head[8192]; + char keys[8192]; + char data[8192]; + char title_id[256]; + char version[256]; + char title[256]; + struct SfoHeader *h; + struct SfoEntry *e; + char *k; + char *d; + unsigned int align; + unsigned int keyofs; + unsigned int count; + + struct EntryContainer *entry; + + g_empty = 0; + memset(g_vals, 0, sizeof(EntryContainer) * MAX_OPTIONS); + memset(title_id, 0, sizeof(title_id)); + memset(version, 0, sizeof(version)); + memset(title, 0, sizeof(title)); + for (i = 0; i < (sizeof(g_defaults) / sizeof(struct EntryContainer)); i++) { + entry = find_free(); + if (entry == NULL) { + fprintf(stderr, "Maximum options reached\n"); + return 0; + } + *entry = g_defaults[i]; + } + + if ((entry = find_name("TITLE_ID"))) { + if (sfo->title_id.length() != 9) { + fprintf(stderr, "TITLE_ID must be 9 characters long\n"); + return 1; + } + strcpy(title_id, sfo->title_id.to_upper().utf8().get_data()); + entry->data = title_id; + } + + if ((entry = find_name("VERSION"))) { + if (sfo->version.length() != 5 || sfo->version.find_char('.') != 2) { + entry->data = "01.00"; // Default to 01.00 if invalid version + } else { + strcpy(version, sfo->version.utf8().get_data()); + entry->data = version; + } + } + + if ((entry = find_name("PARENTAL_LEVEL"))) { + if (sfo->parental_level < 0 || sfo->parental_level > 11) { + entry->value = 0; + } else { + entry->value = sfo->parental_level; + } + } + + if (sfo->title.length() > 0) { + strcpy(title, sfo->title.utf8().get_data()); + g_title = title; + } + + if (g_title) { + entry = find_name("TITLE"); + entry->data = g_title; + entry = find_name("STITLE"); + entry->data = g_title; + } + + entry = find_name("ATTRIBUTE2"); + entry->value = 12; + + memset(head, 0, sizeof(head)); + memset(keys, 0, sizeof(keys)); + memset(data, 0, sizeof(data)); + h = (struct SfoHeader *)head; + e = (struct SfoEntry *)(head + sizeof(struct SfoHeader)); + k = keys; + d = data; + SW(&h->magic, PSF_MAGIC); + SW(&h->version, PSF_VERSION); + count = 0; + + for (i = 0; g_vals[i].name; i++) { + SW(&h->count, ++count); + SW(&e->nameofs, k - keys); + SW(&e->dataofs, d - data); + SW(&e->alignment, 4); + SW(&e->type, g_vals[i].type); + + strcpy(k, g_vals[i].name); + k += strlen(k) + 1; + if (e->type == PSF_TYPE_VAL) { + SW(&e->valsize, 4); + SW(&e->totalsize, 4); + SW((uint32_t *)d, g_vals[i].value); + d += 4; + } else { + int totalsize; + int valsize = 0; + + if (g_vals[i].data) + valsize = strlen(g_vals[i].data) + 1; + totalsize = (g_vals[i].value) ? (g_vals[i].value) : ((valsize + 3) & ~3); + SW(&e->valsize, valsize); + SW(&e->totalsize, totalsize); + memset(d, 0, totalsize); + + if (g_vals[i].data) + memcpy(d, g_vals[i].data, valsize); + d += totalsize; + } + e++; + } + + keyofs = (char *)e - head; + SW(&h->keyofs, keyofs); + align = 3 - ((unsigned int)(k - keys) & 3); + while (align < 3) { + k++; + align--; + } + + SW(&h->valofs, keyofs + (k - keys)); + + String output = outDir + "/param.sfo"; + fp = fopen(output.utf8().get_data(), "wb"); + if (fp == NULL) { + fprintf(stderr, "Cannot open filename %s\n", output.utf8().get_data()); + return 0; + } + + fwrite(head, 1, (char *)e - head, fp); + fwrite(keys, 1, k - keys, fp); + fwrite(data, 1, d - data, fp); + fclose(fp); + + return 0; +} diff --git a/platform/vita/godot_vita.cpp b/platform/vita/godot_vita.cpp new file mode 100644 index 000000000000..012f335c430d --- /dev/null +++ b/platform/vita/godot_vita.cpp @@ -0,0 +1,88 @@ +/**************************************************************************/ +/* godot_vita.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include +#include +#include +#include + +#include "main/main.h" +#include "os_vita.h" + +#include + +#ifndef MEMORY_GRAPHICS_MB +#define MEMORY_GRAPHICS_MB 256 // Default Split, 256 Graphics/221 Main +#endif +#define MEMORY_NEWLIB_MB (477 - MEMORY_GRAPHICS_MB) +#define MEMORY_SCELIBC_MB 10 + +//#define DEVKIT_ENABLED 1 + +int _newlib_heap_size_user = MEMORY_NEWLIB_MB * 1024 * 1024; +unsigned int sceLibcHeapSize = MEMORY_SCELIBC_MB * 1024 * 1024; + +int main(int argc, char *argv[]) { + OS_Vita os; + char title_id[0xA]; + char app_dir_path[0x100]; + char app_kernel_module_path[0x100]; + SceUID pid = -1; + sceKernelLoadStartModule("vs0:sys/external/libfios2.suprx", 0, NULL, 0, NULL, NULL); + sceKernelLoadStartModule("vs0:sys/external/libc.suprx", 0, NULL, 0, NULL, NULL); + + pid = sceKernelGetProcessId(); + sceAppMgrAppParamGetString(pid, 12, title_id, sizeof(title_id)); + snprintf(app_dir_path, sizeof(app_dir_path), "ux0:app/%s", title_id); + snprintf(app_kernel_module_path, sizeof(app_kernel_module_path), "%s/module/libgpu_es4_kernel_ext.skprx", app_dir_path); + + SceUID res = taiLoadStartKernelModule(app_kernel_module_path, 0, NULL, 0); + if (res < 0) { + sceClibPrintf("Failed to load kernel module: %08x\n", res); + } + + scePowerSetArmClockFrequency(444); + scePowerSetBusClockFrequency(222); + scePowerSetGpuClockFrequency(222); + scePowerSetGpuXbarClockFrequency(166); + + sceClibPrintf("Showing the path now UwU: %d %s\n", argc, argv[0]); + char *args[] = { "--path", "app0:/game_data", "--main-pack", "app0:/game_data/game.pck" }; + + Error err = Main::setup("", sizeof(args) / sizeof(args[0]), args); + if (err != OK) { + return 255; + } + + if (Main::start()) + os.run(); // it is actually the OS that decides how to run + Main::cleanup(); + return 0; +} diff --git a/platform/vita/joypad_vita.cpp b/platform/vita/joypad_vita.cpp new file mode 100644 index 000000000000..66a31b83ba6d --- /dev/null +++ b/platform/vita/joypad_vita.cpp @@ -0,0 +1,74 @@ +/**************************************************************************/ +/* joypad_vita.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "joypad_vita.h" +#include "core/os/os.h" +#include "core/variant.h" + +static const SceCtrlButtons pad_mapping[] = { + SCE_CTRL_CROSS, SCE_CTRL_CIRCLE, SCE_CTRL_SQUARE, SCE_CTRL_TRIANGLE, + SCE_CTRL_L2, SCE_CTRL_R2, SCE_CTRL_L1, SCE_CTRL_R1, + SCE_CTRL_L3, SCE_CTRL_R3, SCE_CTRL_SELECT, SCE_CTRL_START, + SCE_CTRL_UP, SCE_CTRL_DOWN, SCE_CTRL_LEFT, SCE_CTRL_RIGHT +}; + +JoypadVita::JoypadVita(InputDefault *in) { + sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG_WIDE); + button_count = sizeof(pad_mapping) / sizeof(*pad_mapping); + input = in; + input->joy_connection_changed(0, true, "Sony PlayStation Vita", "__VITA_GAMEPAD__"); +} + +JoypadVita::~JoypadVita() {} + +void JoypadVita::process_joypads() { + static SceCtrlData old_pad_input = { 0 }; + sceCtrlPeekBufferPositive(0, &pad_input, 1); + uint64_t changed; + float lx, ly, rx, ry; + + lx = ((pad_input.lx) / 255.0f) * 2.0 - 1.0; + ly = ((pad_input.ly) / 255.0f) * 2.0 - 1.0; + rx = ((pad_input.rx) / 255.0f) * 2.0 - 1.0; + ry = ((pad_input.ry) / 255.0f) * 2.0 - 1.0; + + input->joy_axis(0, JOY_ANALOG_LX, lx); + input->joy_axis(0, JOY_ANALOG_LY, ly); + input->joy_axis(0, JOY_ANALOG_RX, rx); + input->joy_axis(0, JOY_ANALOG_RY, ry); + + changed = old_pad_input.buttons ^ pad_input.buttons; + old_pad_input = pad_input; + if (changed) { + for (int i = 0; i < button_count; i++) { + input->joy_button(0, i, (bool)(pad_input.buttons & pad_mapping[i])); + } + } +} diff --git a/platform/vita/joypad_vita.h b/platform/vita/joypad_vita.h new file mode 100644 index 000000000000..a4ad6f15ca96 --- /dev/null +++ b/platform/vita/joypad_vita.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* joypad_vita.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "main/input_default.h" +#include +#include +#include + +#ifndef JOYPAD_VITA_H +#define JOYPAD_VITA_H + +class JoypadVita { +public: + JoypadVita(InputDefault *in); + ~JoypadVita(); + void process_joypads(); + +private: + InputDefault *input; + SceCtrlData pad_input; + int button_count; +}; + +#endif // JOYPAD_VITA_H diff --git a/platform/vita/logo.png b/platform/vita/logo.png new file mode 100644 index 000000000000..24217cb6f487 Binary files /dev/null and b/platform/vita/logo.png differ diff --git a/platform/vita/os_vita.cpp b/platform/vita/os_vita.cpp new file mode 100644 index 000000000000..386f56af6b65 --- /dev/null +++ b/platform/vita/os_vita.cpp @@ -0,0 +1,634 @@ +/**************************************************************************/ +/* os_vita.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "os_vita.h" + +#include "core/array.h" +#include "core/os/keyboard.h" +#include "drivers/dummy/rasterizer_dummy.h" +#include "drivers/dummy/texture_loader_dummy.h" +#include "drivers/gles2/rasterizer_gles2.h" +#include "drivers/unix/dir_access_unix.h" +#include "drivers/unix/file_access_unix.h" +#include "drivers/unix/ip_unix.h" +#include "drivers/unix/net_socket_posix.h" +#include "drivers/unix/thread_posix.h" +#include "main/main.h" +#include "servers/audio_server.h" +#include "servers/visual/visual_server_raster.h" +#include "servers/visual/visual_server_wrap_mt.h" + +#include +#include + +/// Clock Setup function (used by get_ticks_usec) +static uint64_t _clock_start = 0; + +static void _setup_clock() { + struct timespec tv_now = { 0, 0 }; + ERR_FAIL_COND_MSG(clock_gettime(CLOCK_MONOTONIC, &tv_now) != 0, "OS CLOCK IS NOT WORKING!"); + _clock_start = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; +} + +int OS_Vita::get_video_driver_count() const { + return 1; +} + +int OS_Vita::get_audio_driver_count() const { + return 1; +} + +const char *OS_Vita::get_audio_driver_name(int p_driver) const { + return "Vita"; +} + +void OS_Vita::initialize_core() { +#if !defined(NO_THREADS) + init_thread_posix(); +#endif + + FileAccess::make_default(FileAccess::ACCESS_RESOURCES); + FileAccess::make_default(FileAccess::ACCESS_USERDATA); + FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); + DirAccess::make_default(DirAccess::ACCESS_RESOURCES); + DirAccess::make_default(DirAccess::ACCESS_USERDATA); + DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM); + +#ifndef NO_NETWORK + NetSocketPosix::make_default(); + IP_Unix::make_default(); +#endif + + _setup_clock(); +} + +void OS_Vita::finalize_core() { +#ifndef NO_NETWORK + NetSocketPosix::cleanup(); +#endif +} + +int OS_Vita::get_current_video_driver() const { + return video_driver_index; +} + +Error OS_Vita::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { + bool gl_initialization_error = false; + bool gles2 = false; + gl_context = NULL; + + if (p_video_driver == VIDEO_DRIVER_GLES2) { + gles2 = true; + } else if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2")) { + p_video_driver = VIDEO_DRIVER_GLES2; + gles2 = true; + } else { + OS::get_singleton()->alert("OpenGL ES 3 is not supported on this device.\n\n" + "Please enable the option \"Fallback to OpenGL ES 2.0\" in the options menu.\n", + "OpenGL ES 3 Not Supported"); + //gl_initialization_error = true; + p_video_driver = VIDEO_DRIVER_GLES2; + gles2 = true; + } + + if (!gl_initialization_error) { + gl_context = memnew(ContextEGL_Vita(gles2)); + if (gl_context->initialize()) { + OS::get_singleton()->alert("Failed to initialize OpenGL ES 2.0\n" + "OpenGL ES 2.0 Initialization Failed"); + memdelete(gl_context); + gl_context = NULL; + gl_initialization_error = true; + } + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + OS::get_singleton()->alert("RasterizerGLES2::is_viable() failed\n" + "RasterizerGLES2 Not Viable"); + memdelete(gl_context); + gl_context = NULL; + gl_initialization_error = true; + } + } + + if (gl_initialization_error) { + OS::get_singleton()->alert("Your device does not support any of the supported OpenGL versions.\n" + "Please check your graphics drivers and try again.\n", + "Graphics Driver Error"); + return ERR_UNAVAILABLE; + } + + video_driver_index = p_video_driver; + + visual_server = memnew(VisualServerRaster); + if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { + visual_server = memnew(VisualServerWrapMT(visual_server, false)); + } + + visual_server->init(); + + AudioDriverManager::initialize(p_audio_driver); + + input = memnew(InputDefault); + input->set_use_input_buffering(true); + input->set_emulate_mouse_from_touch(true); + joypad = memnew(JoypadVita(input)); + + sceSysmoduleLoadModule(SCE_SYSMODULE_IME); // Enable the IME module for Keyboard input + + // Enable SceTouch + sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); + + sceTouchGetPanelInfo(0, &front_panel_info); + front_panel_size = Vector2(front_panel_info.maxAaX, front_panel_info.maxAaY); + + // Enable SceMotion (Battery Usage go brrrrrr) + sceMotionStartSampling(); + sceMotionMagnetometerOn(); + + sceMotionSetAngleThreshold(45); + + return OK; +} + +void OS_Vita::set_main_loop(MainLoop *p_main_loop) { + main_loop = p_main_loop; + input->set_main_loop(p_main_loop); +} + +void OS_Vita::delete_main_loop() { + memdelete(main_loop); +} + +void OS_Vita::finalize() { + memdelete(joypad); + memdelete(input); + visual_server->finish(); + memdelete(visual_server); + memdelete(gl_context); +} + +void OS_Vita::alert(const String &p_alert, const String &p_title) { + sceClibPrintf(p_alert.ascii().get_data()); +} + +Point2 OS_Vita::get_mouse_position() const { + return Point2(0, 0); +} + +int OS_Vita::get_mouse_button_state() const { + return 0; +} + +void OS_Vita::set_window_title(const String &p_title) { +} + +void OS_Vita::set_video_mode(const VideoMode &p_video_mode, int p_screen) { +} + +OS::VideoMode OS_Vita::get_video_mode(int p_screen) const { + return video_mode; +} + +void OS_Vita::get_fullscreen_mode_list(List *p_list, int p_screen) const { + p_list->push_back(video_mode); +} + +Size2 OS_Vita::get_window_size() const { + return Size2(video_mode.width, video_mode.height); +} + +String OS_Vita::get_name() const { + return "Vita"; +} + +MainLoop *OS_Vita::get_main_loop() const { + return main_loop; +} + +void OS_Vita::swap_buffers() { + gl_context->swap_buffers(); +} + +bool OS_Vita::can_draw() const { + return true; +} + +static bool libime_active = false; +void OS_Vita::run() { + if (!main_loop) + return; + + main_loop->init(); + + while (true) { + joypad->process_joypads(); + process_touch(); + process_motion(); + if (libime_active) { + sceImeUpdate(); + } + + if (Main::iteration()) + break; + } + + main_loop->finish(); +} + +void OS_Vita::process_touch() { + sceTouchPeek(0, &touch, 1); + static uint32_t last_touch_count = 0; + + if (touch.reportNum != last_touch_count) { + if (touch.reportNum > last_touch_count) { // new touches + for (uint32_t i = last_touch_count; i < touch.reportNum; i++) { + Vector2 pos(touch.report[i].x, touch.report[i].y); + pos /= front_panel_size; + pos *= Vector2(960, 544); + Ref st; + st.instance(); + st->set_index(i); + st->set_position(pos); + st->set_pressed(true); + input->parse_input_event(st); + } + } else { // lost touches + for (uint32_t i = touch.reportNum; i < last_touch_count; i++) { + Ref st; + st.instance(); + st->set_index(i); + st->set_position(last_touch_pos[i]); + st->set_pressed(false); + input->parse_input_event(st); + } + } + } else { + for (uint32_t i = 0; i < touch.reportNum; i++) { + Vector2 pos(touch.report[i].x, touch.report[i].y); + pos /= front_panel_size; + pos *= Vector2(960, 544); + Ref sd; + sd.instance(); + sd->set_index(i); + sd->set_position(pos); + sd->set_relative(pos - last_touch_pos[i]); + last_touch_pos[i] = pos; + input->parse_input_event(sd); + } + } + + last_touch_count = touch.reportNum; +} + +void OS_Vita::process_motion() { + sceMotionGetState(&motion_state); + process_accelerometer(Vector3(motion_state.acceleration.x, motion_state.acceleration.y, motion_state.acceleration.z)); + process_gravity(Vector3(motion_state.basicOrientation.x, motion_state.basicOrientation.y, motion_state.basicOrientation.z)); + process_gyroscope(Vector3(motion_state.angularVelocity.x, motion_state.angularVelocity.y, motion_state.angularVelocity.z)); + process_magnetometer(Vector3(0, 0, 0)); // No idea how to calculate this. I'm not a linear maths guy. +} + +void OS_Vita::process_accelerometer(const Vector3 &m_accelerometer) { + input->set_accelerometer(m_accelerometer); +} + +void OS_Vita::process_gravity(const Vector3 &m_gravity) { + input->set_gravity(m_gravity); +} + +void OS_Vita::process_magnetometer(const Vector3 &m_magnetometer) { + input->set_magnetometer(m_magnetometer); +} + +void OS_Vita::process_gyroscope(const Vector3 &m_gyroscope) { + input->set_gyroscope(m_gyroscope); +} + +String OS_Vita::get_data_path() const { + return "ux0:/data"; +} + +String OS_Vita::get_user_data_dir() const { + String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); + if (appname != "") { + bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); + if (use_custom_dir) { + String custom_dir = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/custom_user_dir_name"), true); + if (custom_dir == "") { + custom_dir = appname; + } + DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + dir_access->make_dir_recursive(get_data_path().plus_file(custom_dir)); + memdelete(dir_access); + return get_data_path().plus_file(custom_dir); + } else { + DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + dir_access->make_dir_recursive(get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file(appname)); + memdelete(dir_access); + return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file(appname); + } + } + DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + dir_access->make_dir_recursive(get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("__unknown")); + memdelete(dir_access); + return get_data_path().plus_file(get_godot_dir_name()).plus_file("app_userdata").plus_file("__unknown"); +} + +String OS_Vita::get_model_name() const { + return "Sony Playstation Vita"; +} + +void utf16_to_utf8(const uint16_t *src, uint8_t *dst) { + int i; + for (i = 0; src[i]; i++) { + if ((src[i] & 0xFF80) == 0) { + *(dst++) = src[i] & 0xFF; + } else if ((src[i] & 0xF800) == 0) { + *(dst++) = ((src[i] >> 6) & 0xFF) | 0xC0; + *(dst++) = (src[i] & 0x3F) | 0x80; + } else if ((src[i] & 0xFC00) == 0xD800 && (src[i + 1] & 0xFC00) == 0xDC00) { + *(dst++) = (((src[i] + 64) >> 8) & 0x3) | 0xF0; + *(dst++) = (((src[i] >> 2) + 16) & 0x3F) | 0x80; + *(dst++) = ((src[i] >> 4) & 0x30) | 0x80 | ((src[i + 1] << 2) & 0xF); + *(dst++) = (src[i + 1] & 0x3F) | 0x80; + i += 1; + } else { + *(dst++) = ((src[i] >> 12) & 0xF) | 0xE0; + *(dst++) = ((src[i] >> 6) & 0x3F) | 0x80; + *(dst++) = (src[i] & 0x3F) | 0x80; + } + } + + *dst = '\0'; +} + +static char libime_initval[8] = { 1 }; +static unsigned int libime_height = 0; +static char libime_out[SCE_IME_MAX_PREEDIT_LENGTH * 2 + 8]; +static unsigned int libime_work[SCE_IME_WORK_BUFFER_SIZE / sizeof(unsigned int)]; +static SceImeCaret caret_rev; + +void vita_ime_event_handler(void *arg, const SceImeEventData *e) { + uint8_t utf8_buffer[SCE_IME_MAX_TEXT_LENGTH] = { '\0' }; + switch (e->id) { + case SCE_IME_EVENT_OPEN: + libime_height = e->param.rect.height; + break; + case SCE_IME_EVENT_UPDATE_TEXT: + if (e->param.text.caretIndex == 0) { + OS_Vita::get_singleton()->key(KEY_BACKSPACE, true); + OS_Vita::get_singleton()->key(KEY_BACKSPACE, false); + sceImeSetText((SceWChar16 *)libime_initval, 4); + } else { + String character; + utf16_to_utf8((uint16_t *)&libime_out[2], utf8_buffer); + character.parse_utf8(utf8_buffer); + OS_Vita::get_singleton()->key(character[0], true); + OS_Vita::get_singleton()->key(character[0], false); + sceClibMemset(&caret_rev, 0, sizeof(SceImeCaret)); + caret_rev.index = 1; + sceImeSetCaret(&caret_rev); + sceImeSetText((SceWChar16 *)libime_initval, 4); + } + break; + case SCE_IME_EVENT_PRESS_ENTER: + OS_Vita::get_singleton()->key(KEY_ENTER, true); + OS_Vita::get_singleton()->key(KEY_ENTER, false); + case SCE_IME_EVENT_PRESS_CLOSE: + libime_active = false; + libime_height = 0; + sceImeClose(); + break; + } +} + +void OS_Vita::key(uint32_t p_key, bool p_pressed) { + Ref ev; + ev.instance(); + ev->set_echo(false); + ev->set_pressed(p_pressed); + ev->set_scancode(p_key); + ev->set_unicode(p_key); + input->parse_input_event(ev); +} + +bool OS_Vita::has_virtual_keyboard() const { + return true; +} + +int OS_Vita::get_virtual_keyboard_height() const { + return (int)libime_height; +} + +void OS_Vita::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + if (!libime_active) { + SceImeParam param; + sceImeParamInit(¶m); + + sceClibMemset(libime_out, 0, (SCE_IME_MAX_PREEDIT_LENGTH * 2 + 6)); + + param.supportedLanguages = SCE_IME_LANGUAGE_ENGLISH; + param.languagesForced = false; + param.type = SCE_IME_TYPE_DEFAULT; + param.option = SCE_IME_OPTION_NO_ASSISTANCE; + param.inputTextBuffer = (SceWChar16 *)libime_out; + param.maxTextLength = 4; + param.handler = vita_ime_event_handler; + param.filter = NULL; + param.initialText = (SceWChar16 *)libime_initval; + param.arg = NULL; + param.work = libime_work; + + sceImeOpen(¶m); + libime_active = true; + } +} + +void OS_Vita::hide_virtual_keyboard() { + if (libime_active) { + libime_active = false; + sceImeClose(); + } +} + +void OS_Vita::set_offscreen_gl_available(bool p_available) { + secondary_gl_available = false; +} + +bool OS_Vita::is_offscreen_gl_available() const { + return secondary_gl_available; +} + +void OS_Vita::set_offscreen_gl_current(bool p_current) { +} + +bool OS_Vita::_check_internal_feature_support(const String &p_feature) { + if (p_feature == "mobile") { + return true; + } + if (p_feature == "armeabi-v7a" || p_feature == "armeabi") { + return true; + } + return false; +} + +OS_Vita *OS_Vita::get_singleton() { + return (OS_Vita *)OS::get_singleton(); +} + +Error OS_Vita::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) { + String path = p_path; + + if (FileAccess::exists(path) && path.is_rel_path()) { + // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path, + // otherwise it will end up searching various system directories for the lib instead and finally failing. + path = "app0:" + path; + } + + if (!FileAccess::exists(path)) { + //this code exists so gdnative can load .suprx files from within the executable path + path = get_executable_path().get_base_dir().plus_file("app0:").plus_file(p_path.get_file()); + } + + p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW); + ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ". Error: " + dlerror()); + return OK; +} + +Error OS_Vita::close_dynamic_library(void *p_library_handle) { + if (dlclose(p_library_handle)) { + return FAILED; + } + return OK; +} + +Error OS_Vita::get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional) { + const char *error; + dlerror(); // Clear existing errors + + p_symbol_handle = dlsym(p_library_handle, p_name.utf8().get_data()); + + error = dlerror(); + if (error != nullptr) { + ERR_FAIL_COND_V_MSG(!p_optional, ERR_CANT_RESOLVE, "Can't resolve symbol " + p_name + ". Error: " + error + "."); + + return ERR_CANT_RESOLVE; + } + return OK; +} + +OS_Vita::OS_Vita() { + video_mode.width = 960; + video_mode.height = 544; + video_mode.fullscreen = true; + video_mode.resizable = false; + + video_driver_index = 0; + main_loop = nullptr; + visual_server = nullptr; + gl_context = nullptr; + + AudioDriverManager::add_driver(&driver_vita); +} + +OS_Vita::~OS_Vita() { + video_driver_index = 0; + main_loop = nullptr; + visual_server = nullptr; + input = nullptr; + gl_context = nullptr; +} + +// Misc + +Error OS_Vita::shell_open(String p_uri) { + const char *uri = p_uri.utf8().get_data(); + if (strncmp(uri, "http://", 7) || strncmp(uri, "https://", 8)) { + sceAppMgrLaunchAppByUri(0xFFFFF, uri); + } + return FAILED; +} + +Error OS_Vita::execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) { + return FAILED; +} + +Error OS_Vita::kill(const ProcessID &p_pid) { + return FAILED; +} + +bool OS_Vita::is_process_running(const ProcessID &p_pid) const { + return false; +} + +bool OS_Vita::has_environment(const String &p_var) const { + return false; +} + +String OS_Vita::get_environment(const String &p_var) const { + return ""; +} + +bool OS_Vita::set_environment(const String &p_var, const String &p_value) const { + return false; +} + +OS::Date OS_Vita::get_date(bool local) const { + return OS::Date(); +} + +OS::Time OS_Vita::get_time(bool local) const { + return OS::Time(); +} + +OS::TimeZoneInfo OS_Vita::get_time_zone_info() const { + return OS::TimeZoneInfo(); +} + +void OS_Vita::delay_usec(uint32_t p_usec) const { + sceKernelDelayThread(p_usec); +} + +uint64_t OS_Vita::get_ticks_usec() const { + // Unchecked return. Static analyzers might complain. + // If _setup_clock() succeeded, we assume clock_gettime() works. + struct timespec tv_now = { 0, 0 }; + clock_gettime(CLOCK_MONOTONIC, &tv_now); + uint64_t longtime = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L; + longtime -= _clock_start; + + return longtime; +} + +String OS_Vita::get_stdin_string(bool p_block) { + return ""; +} diff --git a/platform/vita/os_vita.h b/platform/vita/os_vita.h new file mode 100644 index 000000000000..e9a411b19b6e --- /dev/null +++ b/platform/vita/os_vita.h @@ -0,0 +1,166 @@ +/**************************************************************************/ +/* os_vita.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef OS_VITA_H +#define OS_VITA_H + +#include "audio_driver_vita.h" +#include "context_egl_vita.h" +#include "core/os/os.h" +#include "drivers/dummy/texture_loader_dummy.h" +#include "drivers/unix/os_unix.h" +#include "joypad_vita.h" +#include "main/input_default.h" +#include "servers/audio_server.h" +#include "servers/visual/rasterizer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class OS_Vita : public OS { + bool secondary_gl_available = false; + + ContextEGL_Vita *gl_context; + AudioDriverVita driver_vita; + VisualServer *visual_server; + VideoMode video_mode; + int video_driver_index; + + InputDefault *input; + JoypadVita *joypad; + + Vector2 last_touch_pos[SCE_TOUCH_MAX_REPORT]; + SceTouchData touch; + SceTouchPanelInfo front_panel_info; + Vector2 front_panel_size; + void process_touch(); + + SceMotionState motion_state; + void process_motion(); + void process_accelerometer(const Vector3 &m_accelerometer); + void process_gravity(const Vector3 &m_gravity); + void process_magnetometer(const Vector3 &m_magnetometer); + void process_gyroscope(const Vector3 &m_gyroscope); + + MainLoop *main_loop; + +protected: + virtual void initialize_core(); + virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + + virtual bool is_offscreen_gl_available() const; + virtual void set_offscreen_gl_current(bool p_current); + + virtual void set_main_loop(MainLoop *p_main_loop); + virtual void delete_main_loop(); + + virtual void finalize(); + virtual void finalize_core(); + +public: + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + + virtual int get_video_driver_count() const; + + virtual int get_audio_driver_count() const; + virtual const char *get_audio_driver_name(int p_driver) const; + + virtual int get_current_video_driver() const; + + virtual Point2 get_mouse_position() const; + virtual int get_mouse_button_state() const; + virtual void set_window_title(const String &p_title); + + virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); + virtual VideoMode get_video_mode(int p_screen = 0) const; + virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; + + virtual Size2 get_window_size() const; + + virtual String get_name() const; + virtual MainLoop *get_main_loop() const; + + virtual void swap_buffers(); + + virtual bool can_draw() const; + + void key(uint32_t p_key, bool p_pressed); + virtual bool has_virtual_keyboard() const; + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + virtual void hide_virtual_keyboard(); + virtual int get_virtual_keyboard_height() const; + + void set_offscreen_gl_available(bool p_available); + + //virtual String get_executable_path() const; + virtual String get_user_data_dir() const; + virtual String get_data_path() const; + //virtual String get_cache_path() const; + //virtual String get_resource_dir() const; + virtual String get_model_name() const; + + void run(); + + virtual bool _check_internal_feature_support(const String &p_feature); + + static OS_Vita *get_singleton(); + + // libdl + virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); + virtual Error close_dynamic_library(void *p_library_handle); + virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false); + + OS_Vita(); + ~OS_Vita(); + + // Misc + virtual Error shell_open(String p_uri); + virtual Error execute(const String &p_path, const List &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false); + virtual Error kill(const ProcessID &p_pid); + virtual bool is_process_running(const ProcessID &p_pid) const; + virtual Date get_date(bool local = false) const; + virtual Time get_time(bool local = false) const; + virtual TimeZoneInfo get_time_zone_info() const; + virtual void delay_usec(uint32_t p_usec) const; + virtual uint64_t get_ticks_usec() const; + virtual String get_stdin_string(bool p_block = true); + virtual bool has_environment(const String &p_var) const; + virtual String get_environment(const String &p_var) const; + virtual bool set_environment(const String &p_var, const String &p_value) const; +}; + +#endif // OS_VITA_H diff --git a/platform/vita/platform_config.h b/platform/vita/platform_config.h new file mode 100644 index 000000000000..c3189bccfb18 --- /dev/null +++ b/platform/vita/platform_config.h @@ -0,0 +1,31 @@ +/**************************************************************************/ +/* platform_config.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include