Compare commits
113 Commits
Author | SHA1 | Date | |
---|---|---|---|
f04d17ce4f | |||
9a521ebca2 | |||
deae36ed94 | |||
00d6ee4f9a | |||
8dd562a20f | |||
c11c7203ef | |||
379da809cd | |||
96baf9627a | |||
279b101e46 | |||
b190851d36 | |||
c3c7d22006 | |||
3abbb0cb16 | |||
dda511f8ff | |||
0ccc1ba6e8 | |||
9d3a0964b0 | |||
ed9bef8492 | |||
c4e26c5be2 | |||
0c82db4116 | |||
ce168c1c16 | |||
93eb7807aa | |||
12074e15d9 | |||
010aaa89e3 | |||
0e32fd36c8 | |||
6c89e64f8b | |||
9fe6a2fb39 | |||
c87a6fa3c0 | |||
69d3832815 | |||
67002006f3 | |||
eb96d80e5a | |||
968001897d | |||
68519f06fd | |||
b3b935dd1d | |||
006243d02f | |||
3c1bce876c | |||
484d3e7f90 | |||
23df63abb7 | |||
126c3d618d | |||
3937f88084 | |||
4bd2fecfa2 | |||
b539491713 | |||
ea4c5c77a1 | |||
e506dbb1bb | |||
699d83c61b | |||
329f92c0a4 | |||
bfa0567ac9 | |||
a94708a1ae | |||
905a569c09 | |||
53f602403a | |||
2f64552926 | |||
2b920f50ba | |||
d859a16b5a | |||
78e17d8c18 | |||
0e7a108a0a | |||
ca21877000 | |||
f1ec29ec86 | |||
13ac816baa | |||
6ba86b9193 | |||
49f8da2a80 | |||
e8faee62fa | |||
a8ff58f0c4 | |||
daccf65361 | |||
3d508d7609 | |||
0061edb3b2 | |||
baeeceeaac | |||
211bf265ff | |||
0dd0706f52 | |||
1743626113 | |||
4ed11b67e7 | |||
365e238c29 | |||
36c4ed7e4d | |||
cf1faf4835 | |||
cb4560a602 | |||
b3a96dc3e2 | |||
be300a2809 | |||
77d7b82a14 | |||
b59c580f6a | |||
524a390921 | |||
ead7e575f3 | |||
f2a9b274d2 | |||
dd4972172d | |||
8822a22987 | |||
53cb68beee | |||
47ae13ac51 | |||
492725ed63 | |||
7c2d0d5625 | |||
9449cdf747 | |||
d95a9e1871 | |||
8a83702c3c | |||
be7e44fd3c | |||
651c49980e | |||
46a53ba3cf | |||
186728ca5f | |||
9412c07c45 | |||
2717e062b3 | |||
0723fb1894 | |||
16f919d147 | |||
e18fc9fc63 | |||
3ebdffd396 | |||
82b6252927 | |||
4a35935daf | |||
89c29f14b1 | |||
4edfaeead4 | |||
bfcdd3c7e9 | |||
5467b5178f | |||
8f49c550ba | |||
50eb4801a0 | |||
a3eef9fc3b | |||
f455780833 | |||
0150879d2b | |||
623cf06516 | |||
0b1849a9ad | |||
157d51e6b6 | |||
ed58356dd8 |
23
.gitignore
vendored
23
.gitignore
vendored
@ -1,10 +1,15 @@
|
|||||||
out
|
/out
|
||||||
debian/tmp
|
/debian/tmp
|
||||||
.vscode
|
/.vscode
|
||||||
build
|
/build*
|
||||||
CMakeLists.txt.user
|
/CMakeLists.txt.user
|
||||||
*.autosave
|
*.autosave
|
||||||
AppImageBuilder.yml
|
/AppImageBuilder.yml
|
||||||
appimage-builder-cache
|
/appimage-builder-cache
|
||||||
AppDir
|
/appimage-build
|
||||||
*.zsync
|
/AppDir
|
||||||
|
/*.zsync
|
||||||
|
/*.AppImage
|
||||||
|
/core*
|
||||||
|
/qemu_*
|
||||||
|
/cmake/.prebuilt-armhf-toolchain
|
||||||
|
7
.gitmodules
vendored
7
.gitmodules
vendored
@ -2,11 +2,14 @@
|
|||||||
path = dependencies/libpng/src
|
path = dependencies/libpng/src
|
||||||
url = https://github.com/glennrp/libpng.git
|
url = https://github.com/glennrp/libpng.git
|
||||||
[submodule "dependencies/zlib/src"]
|
[submodule "dependencies/zlib/src"]
|
||||||
path = dependencies/zlib/src
|
path = dependencies/libpng/zlib/src
|
||||||
url = https://github.com/madler/zlib.git
|
url = https://github.com/madler/zlib.git
|
||||||
[submodule "dependencies/glfw/src"]
|
[submodule "dependencies/glfw/src"]
|
||||||
path = dependencies/glfw/src
|
path = media-layer/core/dependencies/glfw/src
|
||||||
url = https://github.com/glfw/glfw.git
|
url = https://github.com/glfw/glfw.git
|
||||||
[submodule "dependencies/zenity/src"]
|
[submodule "dependencies/zenity/src"]
|
||||||
path = dependencies/zenity/src
|
path = dependencies/zenity/src
|
||||||
url = https://gitea.thebrokenrail.com/TheBrokenRail/zenity.git
|
url = https://gitea.thebrokenrail.com/TheBrokenRail/zenity.git
|
||||||
|
[submodule "launcher/dependencies/patchelf/src"]
|
||||||
|
path = launcher/dependencies/patchelf/src
|
||||||
|
url = https://github.com/NixOS/patchelf.git
|
||||||
|
196
CMakeLists.txt
196
CMakeLists.txt
@ -1,107 +1,171 @@
|
|||||||
cmake_minimum_required(VERSION 3.13.0)
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
# Specify Options
|
# Build Mode
|
||||||
option(MCPI_USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" FALSE)
|
set(MCPI_BUILD_MODE "native" CACHE STRING "\"arm\" = Build Only Code That Must Be ARM; \"native\" = Build Architecture-Independent Code")
|
||||||
option(MCPI_SERVER_MODE "Server Mode" FALSE)
|
set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "arm" "native")
|
||||||
option(MCPI_HEADLESS_MODE "Headless Mode" ${MCPI_SERVER_MODE})
|
|
||||||
set(MCPI_BUILD_MODE "both" CACHE STRING "\"arm\" = Build Only Code That Must Be ARM; \"native\" = Build Architecture-Independent Code; \"both\" = Build All Code As ARM")
|
|
||||||
set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "both" "arm" "native")
|
|
||||||
option(MCPI_OPEN_SOURCE_ONLY "Only Install Open-Source Code (Will Result In Broken Install)" FALSE)
|
|
||||||
option(MCPI_IS_APPIMAGE_BUILD "AppImage Build" FALSE)
|
|
||||||
|
|
||||||
# Configure Build Mode
|
|
||||||
if(MCPI_BUILD_MODE STREQUAL "arm")
|
if(MCPI_BUILD_MODE STREQUAL "arm")
|
||||||
set(USE_ARM32_TOOLCHAIN TRUE)
|
|
||||||
set(BUILD_ARM_COMPONENTS TRUE)
|
set(BUILD_ARM_COMPONENTS TRUE)
|
||||||
set(BUILD_NATIVE_COMPONENTS FALSE)
|
set(BUILD_NATIVE_COMPONENTS FALSE)
|
||||||
elseif(MCPI_BUILD_MODE STREQUAL "native")
|
elseif(MCPI_BUILD_MODE STREQUAL "native")
|
||||||
set(USE_ARM32_TOOLCHAIN FALSE)
|
|
||||||
set(BUILD_ARM_COMPONENTS FALSE)
|
set(BUILD_ARM_COMPONENTS FALSE)
|
||||||
set(BUILD_NATIVE_COMPONENTS TRUE)
|
set(BUILD_NATIVE_COMPONENTS TRUE)
|
||||||
elseif(MCPI_BUILD_MODE STREQUAL "both")
|
|
||||||
set(USE_ARM32_TOOLCHAIN TRUE)
|
|
||||||
set(BUILD_ARM_COMPONENTS TRUE)
|
|
||||||
set(BUILD_NATIVE_COMPONENTS TRUE)
|
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Invalid Mode")
|
message(FATAL_ERROR "Invalid Mode")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Utility Functions
|
# Specify Options
|
||||||
include(cmake/util.cmake)
|
option(MCPI_IS_MIXED_BUILD "Whether The Architecture-Independent And ARM Code Are Different Architecture" FALSE)
|
||||||
|
option(MCPI_OPEN_SOURCE_ONLY "Only Install Open-Source Code (Will Result In Broken Install)" FALSE)
|
||||||
|
option(MCPI_IS_APPIMAGE_BUILD "AppImage Build" FALSE)
|
||||||
|
|
||||||
|
# Server/Headless Builds
|
||||||
|
option(MCPI_SERVER_MODE "Server Mode" FALSE)
|
||||||
|
option(MCPI_HEADLESS_MODE "Headless Mode" ${MCPI_SERVER_MODE})
|
||||||
|
|
||||||
|
# Media Layer
|
||||||
|
if(MCPI_HEADLESS_MODE)
|
||||||
|
set(DEFAULT_USE_MEDIA_LAYER_PROXY FALSE)
|
||||||
|
else()
|
||||||
|
set(DEFAULT_USE_MEDIA_LAYER_PROXY ${MCPI_IS_MIXED_BUILD})
|
||||||
|
endif()
|
||||||
|
option(MCPI_USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" ${DEFAULT_USE_MEDIA_LAYER_PROXY})
|
||||||
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
|
option(MCPI_USE_GLES1_COMPATIBILITY_LAYER "Whether To Enable The GLESv1_CM Compatibility Layer" TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# App ID
|
||||||
|
set(DEFAULT_APP_ID "com.thebrokenrail.MCPIReborn")
|
||||||
|
if(MCPI_SERVER_MODE)
|
||||||
|
string(APPEND DEFAULT_APP_ID "Server")
|
||||||
|
else()
|
||||||
|
string(APPEND DEFAULT_APP_ID "Client")
|
||||||
|
endif()
|
||||||
|
set(MCPI_APP_ID "${DEFAULT_APP_ID}" CACHE STRING "App ID")
|
||||||
|
|
||||||
|
# App Title
|
||||||
|
set(DEFAULT_APP_TITLE "Minecraft: Pi Edition: Reborn")
|
||||||
|
if(MCPI_SERVER_MODE)
|
||||||
|
string(APPEND DEFAULT_APP_TITLE " (Server)")
|
||||||
|
else()
|
||||||
|
string(APPEND DEFAULT_APP_TITLE " (Client)")
|
||||||
|
endif()
|
||||||
|
set(MCPI_APP_TITLE "${DEFAULT_APP_TITLE}" CACHE STRING "App Title")
|
||||||
|
|
||||||
# Specify Variant Name
|
# Specify Variant Name
|
||||||
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
|
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
|
||||||
if(MCPI_SERVER_MODE)
|
if(MCPI_SERVER_MODE)
|
||||||
set(MCPI_VARIANT_NAME "${MCPI_VARIANT_NAME}-server")
|
string(APPEND MCPI_VARIANT_NAME "-server")
|
||||||
else()
|
else()
|
||||||
set(MCPI_VARIANT_NAME "${MCPI_VARIANT_NAME}-client")
|
string(APPEND MCPI_VARIANT_NAME "-client")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Specify Installation Paths
|
# Specify Installation Paths
|
||||||
set(MCPI_INSTALL_DIR "lib/${MCPI_VARIANT_NAME}")
|
set(MCPI_INSTALL_DIR "lib/${MCPI_VARIANT_NAME}")
|
||||||
set(MCPI_LIB_DIR "${MCPI_INSTALL_DIR}/lib")
|
|
||||||
set(MCPI_BIN_DIR "${MCPI_INSTALL_DIR}/bin")
|
set(MCPI_BIN_DIR "${MCPI_INSTALL_DIR}/bin")
|
||||||
|
set(MCPI_LEGAL_DIR "${MCPI_INSTALL_DIR}/legal") # For Software Licenses
|
||||||
|
set(MCPI_SDK_DIR "${MCPI_INSTALL_DIR}/sdk")
|
||||||
|
set(MCPI_SDK_LIB_DIR "${MCPI_SDK_DIR}/lib")
|
||||||
|
set(MCPI_SDK_INCLUDE_DIR "${MCPI_SDK_DIR}/include")
|
||||||
|
|
||||||
|
# Library Directory
|
||||||
|
set(MCPI_LIB_DIR "${MCPI_INSTALL_DIR}/lib")
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
string(APPEND MCPI_LIB_DIR "/arm")
|
||||||
|
elseif(BUILD_NATIVE_COMPONENTS)
|
||||||
|
string(APPEND MCPI_LIB_DIR "/native")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Share Directory
|
||||||
|
set(MCPI_SHARE_DIR "share")
|
||||||
|
if(MCPI_IS_APPIMAGE_BUILD)
|
||||||
|
string(PREPEND MCPI_SHARE_DIR "usr/")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Build Mode
|
# Build Mode
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE "Release")
|
set(CMAKE_BUILD_TYPE "Release")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Prebuilt ARMHF Toolchain
|
||||||
|
option(MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN "Whether To Use A Prebuilt ARMHF Toolchain For Building ARM Components" ${MCPI_IS_MIXED_BUILD})
|
||||||
|
if(BUILD_ARM_COMPONENTS AND MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN)
|
||||||
|
include(cmake/prebuilt-armhf-toolchain.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Start Project
|
# Start Project
|
||||||
project(minecraft-pi-reborn)
|
project(minecraft-pi-reborn)
|
||||||
|
|
||||||
# Require ARM Compilation
|
# Utility Functions
|
||||||
if(USE_ARM32_TOOLCHAIN AND (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") AND (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7l"))
|
include(cmake/util.cmake)
|
||||||
|
|
||||||
|
# Sanity Checks
|
||||||
|
if(BUILD_NATIVE_COMPONENTS AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND NOT MCPI_IS_MIXED_BUILD)
|
||||||
|
message(FATAL_ERROR "Project is configured as a mixed-buld, but MCPI_IS_MIXED_BUILD is disabled.")
|
||||||
|
endif()
|
||||||
|
if(BUILD_ARM_COMPONENTS AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
|
||||||
message(FATAL_ERROR "ARM-Targeting Compiler Required")
|
message(FATAL_ERROR "ARM-Targeting Compiler Required")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Specify Default Installation Prefix
|
# Specify Default Installation Prefix
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "" FORCE)
|
set(DEFAULT_PREFIX "/usr")
|
||||||
|
if(MCPI_IS_APPIMAGE_BUILD)
|
||||||
|
set(DEFAULT_PREFIX "/")
|
||||||
|
endif()
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${DEFAULT_PREFIX}" CACHE PATH "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Required Compile Flags
|
||||||
|
string(CONCAT COMPILE_FLAGS_SETUP
|
||||||
# Optimizations
|
# Optimizations
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
"if(CMAKE_BUILD_TYPE STREQUAL \"Release\")\n"
|
||||||
add_compile_options(-O3)
|
" add_compile_options(-O3 -s)\n"
|
||||||
else()
|
"else()\n"
|
||||||
add_compile_options(-g)
|
" add_compile_options(-g)\n"
|
||||||
endif()
|
"endif()\n"
|
||||||
|
|
||||||
# Use LLD When Using Clang
|
|
||||||
if(CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
|
||||||
add_link_options("-fuse-ld=lld")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# PIC
|
# PIC
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
"set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)\n"
|
||||||
|
|
||||||
|
# Warnings
|
||||||
|
"add_link_options(-Wl,--no-undefined)\n"
|
||||||
|
|
||||||
|
# C Standard
|
||||||
|
"add_definitions(-D_GNU_SOURCE)\n"
|
||||||
|
"set(CMAKE_C_STANDARD 99)\n"
|
||||||
|
"set(CMAKE_CXX_STANDARD 11)\n"
|
||||||
|
|
||||||
|
# Skip RPath
|
||||||
|
"set(CMAKE_SKIP_BUILD_RPATH TRUE)"
|
||||||
|
)
|
||||||
|
cmake_language(EVAL CODE "${COMPILE_FLAGS_SETUP}")
|
||||||
|
|
||||||
# Fast Math
|
# Fast Math
|
||||||
add_compile_options(-ffast-math)
|
add_compile_options(-ffast-math)
|
||||||
|
|
||||||
|
# Warnings
|
||||||
|
add_compile_options(-Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference)
|
||||||
|
if(CMAKE_C_COMPILER_ID STREQUAL \"GNU\")
|
||||||
|
# Prevents False Positives
|
||||||
|
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 10.0)
|
||||||
|
add_compile_options(-Wno-stringop-overflow)
|
||||||
|
endif()
|
||||||
|
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 11.0)
|
||||||
|
add_compile_options(-Wno-array-bounds -Wno-stringop-overread)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Buld Dependencies
|
# Buld Dependencies
|
||||||
add_subdirectory(dependencies)
|
add_subdirectory(dependencies)
|
||||||
|
|
||||||
# Warnings
|
|
||||||
add_compile_options(-Wall -Wextra -Werror -Wpointer-arith -Wshadow -Wnull-dereference)
|
|
||||||
add_link_options(-Wl,--no-undefined)
|
|
||||||
add_definitions(-D_GNU_SOURCE)
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
|
|
||||||
# Specify Constants
|
|
||||||
if(MCPI_SERVER_MODE)
|
|
||||||
add_definitions(-DMCPI_SERVER_MODE)
|
|
||||||
endif()
|
|
||||||
if(MCPI_HEADLESS_MODE)
|
|
||||||
add_definitions(-DMCPI_HEADLESS_MODE)
|
|
||||||
endif()
|
|
||||||
if(MCPI_IS_APPIMAGE_BUILD)
|
|
||||||
add_definitions(-DMCPI_IS_APPIMAGE_BUILD)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Version
|
# Version
|
||||||
file(STRINGS VERSION VERSION)
|
set_property(
|
||||||
add_definitions(-DVERSION="${VERSION}")
|
DIRECTORY
|
||||||
|
APPEND
|
||||||
|
PROPERTY CMAKE_CONFIGURE_DEPENDS VERSION
|
||||||
|
)
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" MCPI_VERSION)
|
||||||
|
file(TIMESTAMP "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" MCPI_VERSION_DATE "%Y-%m-%d" UTC)
|
||||||
|
|
||||||
# Build libreborn
|
# Build libreborn
|
||||||
add_subdirectory(libreborn)
|
add_subdirectory(libreborn)
|
||||||
@ -123,3 +187,23 @@ endif()
|
|||||||
if(BUILD_ARM_COMPONENTS)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
add_subdirectory(mods)
|
add_subdirectory(mods)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Include Images
|
||||||
|
if(BUILD_NATIVE_COMPONENTS)
|
||||||
|
add_subdirectory(images)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install SDK
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(EXPORT sdk DESTINATION "${MCPI_SDK_DIR}" FILE "sdk-targets.cmake" EXPORT_LINK_INTERFACE_LIBRARIES)
|
||||||
|
string(CONCAT SDK_SETUP
|
||||||
|
# Compile Flags
|
||||||
|
"${COMPILE_FLAGS_SETUP}\n"
|
||||||
|
# Log
|
||||||
|
"message(STATUS \"Using Reborn SDK v${MCPI_VERSION}\")\n"
|
||||||
|
# Include Targets
|
||||||
|
"include(\"\${CMAKE_CURRENT_LIST_DIR}/sdk-targets.cmake\")\n"
|
||||||
|
)
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake" "${SDK_SETUP}")
|
||||||
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sdk.cmake" DESTINATION "${MCPI_SDK_DIR}")
|
||||||
|
endif()
|
||||||
|
20
Dockerfile
20
Dockerfile
@ -3,24 +3,12 @@ FROM debian:bullseye-slim
|
|||||||
# Install
|
# Install
|
||||||
RUN \
|
RUN \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y tini sed && \
|
apt-get install -y tini qemu-user && \
|
||||||
apt-get --fix-broken install -y && \
|
apt-get --fix-broken install -y && \
|
||||||
rm -rf /var/lib/apt/lists/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy AppImage
|
# Copy
|
||||||
RUN mkdir /app
|
ADD ./out/server-amd64 /app
|
||||||
ADD ./out/minecraft-pi-reborn-server-*-amd64.AppImage /app
|
|
||||||
|
|
||||||
# Extract AppImage
|
|
||||||
WORKDIR /app
|
|
||||||
RUN \
|
|
||||||
sed -i '0,/AI\x02/{s|AI\x02|\x00\x00\x00|}' ./*.AppImage && \
|
|
||||||
./*.AppImage --appimage-extract && \
|
|
||||||
rm -f ./*.AppImage
|
|
||||||
|
|
||||||
# Setup AppImage
|
|
||||||
ENV OWD=/data
|
|
||||||
ENV APPDIR=/app/squashfs-root
|
|
||||||
|
|
||||||
# Setup Working Directory
|
# Setup Working Directory
|
||||||
RUN mkdir /data
|
RUN mkdir /data
|
||||||
@ -28,4 +16,4 @@ WORKDIR /data
|
|||||||
|
|
||||||
# Setup Entrypoint
|
# Setup Entrypoint
|
||||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||||
CMD ["/app/squashfs-root/AppRun"]
|
CMD ["/app/usr/bin/minecraft-pi-reborn-server"]
|
||||||
|
6
Jenkinsfile
vendored
6
Jenkinsfile
vendored
@ -1,11 +1,11 @@
|
|||||||
pipeline {
|
pipeline {
|
||||||
agent none
|
agent none
|
||||||
stages {
|
stages {
|
||||||
stage('Debian Bullseye') {
|
stage('Debian Buster') {
|
||||||
agent {
|
agent {
|
||||||
dockerfile {
|
dockerfile {
|
||||||
filename 'scripts/ci/Dockerfile'
|
filename 'scripts/ci/Dockerfile'
|
||||||
args '-v /var/run/docker.sock:/var/run/docker.sock'
|
args '-v /var/run/docker.sock:/var/run/docker.sock --network host'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
@ -22,6 +22,8 @@ pipeline {
|
|||||||
stage('Publish') {
|
stage('Publish') {
|
||||||
steps {
|
steps {
|
||||||
sh 'apt-get update && apt-get install -y docker.io'
|
sh 'apt-get update && apt-get install -y docker.io'
|
||||||
|
sh 'rm -rf ./out/server-amd64'
|
||||||
|
sh './scripts/build.sh server amd64'
|
||||||
sh 'docker build --no-cache --tag thebrokenrail/minecraft-pi-reborn-server .'
|
sh 'docker build --no-cache --tag thebrokenrail/minecraft-pi-reborn-server .'
|
||||||
withCredentials([usernamePassword(credentialsId: 'docker_hub_login', usernameVariable: 'DOCKER_HUB_USERNAME', passwordVariable: 'DOCKER_HUB_PASSWORD')]) {
|
withCredentials([usernamePassword(credentialsId: 'docker_hub_login', usernameVariable: 'DOCKER_HUB_USERNAME', passwordVariable: 'DOCKER_HUB_PASSWORD')]) {
|
||||||
sh 'docker login -u "${DOCKER_HUB_USERNAME}" -p "${DOCKER_HUB_PASSWORD}"'
|
sh 'docker login -u "${DOCKER_HUB_USERNAME}" -p "${DOCKER_HUB_PASSWORD}"'
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# Setup Toolchain
|
|
||||||
macro(setup_toolchain target)
|
|
||||||
# Use ARM Cross-Compiler
|
|
||||||
set(CMAKE_C_COMPILER "${target}-gcc")
|
|
||||||
set(CMAKE_CXX_COMPILER "${target}-g++")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH "/usr/${target}" "/usr/lib/${target}")
|
|
||||||
# Extra
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
# pkg-config
|
|
||||||
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/${target}/pkgconfig:/usr/${target}/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig")
|
|
||||||
endmacro()
|
|
84
cmake/prebuilt-armhf-toolchain.cmake
Normal file
84
cmake/prebuilt-armhf-toolchain.cmake
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Locations
|
||||||
|
set(toolchain_dir "${CMAKE_CURRENT_LIST_DIR}/.prebuilt-armhf-toolchain")
|
||||||
|
set(sysroot_dir "${CMAKE_CURRENT_BINARY_DIR}/bundled-armhf-sysroot")
|
||||||
|
|
||||||
|
# Force Toolchain
|
||||||
|
set(CMAKE_C_COMPILER "${toolchain_dir}/bin/arm-none-linux-gnueabihf-gcc")
|
||||||
|
set(CMAKE_CXX_COMPILER "${toolchain_dir}/bin/arm-none-linux-gnueabihf-g++")
|
||||||
|
set(CMAKE_SYSTEM_NAME "Linux")
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR "arm")
|
||||||
|
unset(CMAKE_TOOLCHAIN_FILE CACHE)
|
||||||
|
|
||||||
|
# Download If Needed
|
||||||
|
if(NOT EXISTS "${CMAKE_C_COMPILER}")
|
||||||
|
# Pick URL
|
||||||
|
execute_process(COMMAND uname -m OUTPUT_VARIABLE arch OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
if(arch STREQUAL "x86_64")
|
||||||
|
set(toolchain_url "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz")
|
||||||
|
set(toolchain_sha256 "aa074fa8371a4f73fecbd16bd62c8b1945f23289e26414794f130d6ccdf8e39c")
|
||||||
|
elseif(arch STREQUAL "aarch64" OR arch STREQUAL "armv8b" OR arch STREQUAL "armv8l")
|
||||||
|
set(toolchain_url "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-aarch64-arm-none-linux-gnueabihf.tar.xz")
|
||||||
|
set(toolchain_sha256 "fccd7af76988da2b077f939eb2a78baa9935810918d2bf3f837bc74f52efa825")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unable To Download Prebuilt ARMHF Toolchain")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Download
|
||||||
|
message(STATUS "Downloading Prebuilt ARMHF Toolchain...")
|
||||||
|
file(REMOVE_RECURSE "${toolchain_dir}")
|
||||||
|
include(FetchContent)
|
||||||
|
set(FETCHCONTENT_QUIET FALSE)
|
||||||
|
FetchContent_Declare(
|
||||||
|
prebuilt-armhf-toolchain
|
||||||
|
URL "${toolchain_url}"
|
||||||
|
URL_HASH "SHA256=${toolchain_sha256}"
|
||||||
|
SOURCE_DIR "${toolchain_dir}"
|
||||||
|
)
|
||||||
|
FetchContent_Populate(prebuilt-armhf-toolchain)
|
||||||
|
|
||||||
|
# Force Sysroot Rebuild
|
||||||
|
file(REMOVE_RECURSE "${sysroot_dir}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Build Sysroot
|
||||||
|
if(NOT EXISTS "${sysroot_dir}")
|
||||||
|
# Create Directory
|
||||||
|
file(MAKE_DIRECTORY "${sysroot_dir}")
|
||||||
|
|
||||||
|
# Copy Files From Toolchain
|
||||||
|
file(
|
||||||
|
COPY "${toolchain_dir}/arm-none-linux-gnueabihf/libc/"
|
||||||
|
DESTINATION "${sysroot_dir}"
|
||||||
|
USE_SOURCE_PERMISSIONS
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.so*"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete Unneeded Files
|
||||||
|
file(REMOVE_RECURSE "${sysroot_dir}/usr/lib/audit")
|
||||||
|
|
||||||
|
# Strip Files
|
||||||
|
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE "${sysroot_dir}/*")
|
||||||
|
foreach(file IN LISTS files)
|
||||||
|
execute_process(COMMAND "${toolchain_dir}/bin/arm-none-linux-gnueabihf-strip" "${file}" RESULT_VARIABLE ret)
|
||||||
|
# Check Result
|
||||||
|
if(NOT ret EQUAL 0)
|
||||||
|
# Delete Invalid Files
|
||||||
|
file(REMOVE "${file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Setup gconv
|
||||||
|
file(
|
||||||
|
COPY "${toolchain_dir}/arm-none-linux-gnueabihf/libc/usr/lib/gconv/gconv-modules"
|
||||||
|
DESTINATION "${sysroot_dir}/usr/lib/gconv"
|
||||||
|
USE_SOURCE_PERMISSIONS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install Sysroot (Skipping Empty Directories)
|
||||||
|
file(GLOB_RECURSE files LIST_DIRECTORIES FALSE RELATIVE "${sysroot_dir}" "${sysroot_dir}/*")
|
||||||
|
foreach(file IN LISTS files)
|
||||||
|
get_filename_component(parent "${file}" DIRECTORY)
|
||||||
|
install(PROGRAMS "${sysroot_dir}/${file}" DESTINATION "${MCPI_INSTALL_DIR}/sysroot/${parent}")
|
||||||
|
endforeach()
|
38
cmake/toolchain/base-toolchain.cmake
Normal file
38
cmake/toolchain/base-toolchain.cmake
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Setup Toolchain
|
||||||
|
macro(setup_toolchain target)
|
||||||
|
# Target Variants
|
||||||
|
set(target_variants "${target}")
|
||||||
|
macro(add_target_variant value)
|
||||||
|
string(REPLACE "-linux" "-${value}-linux" target_variant "${target}")
|
||||||
|
list(APPEND target_variants "${target_variant}")
|
||||||
|
endmacro()
|
||||||
|
add_target_variant(unknown)
|
||||||
|
add_target_variant(none)
|
||||||
|
add_target_variant(pc)
|
||||||
|
# Find Compiler
|
||||||
|
macro(find_compiler output name)
|
||||||
|
set(possible_names "")
|
||||||
|
foreach(possible_target IN LISTS target_variants)
|
||||||
|
list(APPEND possible_names "${possible_target}-${name}")
|
||||||
|
endforeach()
|
||||||
|
find_program(
|
||||||
|
"${output}"
|
||||||
|
NAMES ${possible_names}
|
||||||
|
NO_CACHE
|
||||||
|
)
|
||||||
|
if("${${output}}" STREQUAL "${output}-NOTFOUND")
|
||||||
|
message(FATAL_ERROR "Unable To Find ${name}")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
find_compiler(CMAKE_C_COMPILER "gcc")
|
||||||
|
find_compiler(CMAKE_CXX_COMPILER "g++")
|
||||||
|
# Extra
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
# Custom Search Paths
|
||||||
|
if(NOT DEFINED ENV{MCPI_TOOLCHAIN_USE_DEFAULT_SEARCH_PATHS})
|
||||||
|
# Find Root
|
||||||
|
set(CMAKE_FIND_ROOT_PATH "/usr/${target}" "/usr/lib/${target}" "/usr")
|
||||||
|
# pkg-config
|
||||||
|
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/${target}/pkgconfig:/usr/${target}/lib/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
@ -1,20 +1,31 @@
|
|||||||
# Symlink Function
|
# Symlink Function
|
||||||
function(install_symlink target link)
|
function(install_symlink target link)
|
||||||
install(CODE "\
|
get_filename_component(parent "${link}" DIRECTORY)
|
||||||
# Prepare\n \
|
if(parent STREQUAL "")
|
||||||
set(file \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${link}\")\n \
|
set(parent ".")
|
||||||
\
|
endif()
|
||||||
# Create Directory\n \
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/symlink/${parent}")
|
||||||
get_filename_component(dir \"\${file}\" DIRECTORY)\n \
|
file(CREATE_LINK "${target}" "${CMAKE_BINARY_DIR}/symlink/${link}" SYMBOLIC)
|
||||||
file(MAKE_DIRECTORY \${dir})\n \
|
install(FILES "${CMAKE_BINARY_DIR}/symlink/${link}" DESTINATION "${parent}")
|
||||||
\
|
|
||||||
# Create Symlink\n \
|
|
||||||
if(NOT EXISTS \"\${file}\")\n \
|
|
||||||
execute_process(COMMAND \${CMAKE_COMMAND} -E create_symlink ${target} \"\${file}\")\n \
|
|
||||||
message(\"-- Installing: \${file}\")\n \
|
|
||||||
else()\n \
|
|
||||||
message(\"-- Up-to-date: \${file}\")\n \
|
|
||||||
endif() \
|
|
||||||
")
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Embed Resources
|
||||||
|
function(embed_resource target file)
|
||||||
|
# Read Hex Data
|
||||||
|
file(READ "${file}" data HEX)
|
||||||
|
# Convert Hex Data For C Compatibility
|
||||||
|
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," data "${data}")
|
||||||
|
# Get C Name
|
||||||
|
get_filename_component(name "${file}" NAME)
|
||||||
|
string(MAKE_C_IDENTIFIER "${name}" name)
|
||||||
|
# Write Data
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${name}.c" "#include <stddef.h>\nconst unsigned char ${name}[] = {${data}};\nconst size_t ${name}_len = sizeof (${name});\n")
|
||||||
|
# Add To Target
|
||||||
|
target_sources("${target}" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
|
||||||
|
# Mark Dependency
|
||||||
|
set_property(
|
||||||
|
DIRECTORY
|
||||||
|
APPEND
|
||||||
|
PROPERTY CMAKE_CONFIGURE_DEPENDS "${file}"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
10
dependencies/CMakeLists.txt
vendored
10
dependencies/CMakeLists.txt
vendored
@ -1,9 +1,5 @@
|
|||||||
project(dependencies)
|
project(dependencies)
|
||||||
|
|
||||||
# ZLib
|
|
||||||
if(BUILD_ARM_COMPONENTS)
|
|
||||||
add_subdirectory(zlib)
|
|
||||||
endif()
|
|
||||||
# LibPNG
|
# LibPNG
|
||||||
if(BUILD_ARM_COMPONENTS)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
add_subdirectory(libpng)
|
add_subdirectory(libpng)
|
||||||
@ -12,11 +8,7 @@ endif()
|
|||||||
if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY)
|
if(BUILD_ARM_COMPONENTS AND NOT MCPI_OPEN_SOURCE_ONLY)
|
||||||
add_subdirectory(minecraft-pi)
|
add_subdirectory(minecraft-pi)
|
||||||
endif()
|
endif()
|
||||||
# GLFW
|
|
||||||
if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_HEADLESS_MODE)
|
|
||||||
add_subdirectory(glfw)
|
|
||||||
endif()
|
|
||||||
# Zenity (Minimal Build)
|
# Zenity (Minimal Build)
|
||||||
if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_HEADLESS_MODE)
|
if(BUILD_NATIVE_COMPONENTS AND NOT MCPI_SERVER_MODE)
|
||||||
add_subdirectory(zenity)
|
add_subdirectory(zenity)
|
||||||
endif()
|
endif()
|
||||||
|
22
dependencies/glfw/CMakeLists.txt
vendored
22
dependencies/glfw/CMakeLists.txt
vendored
@ -1,22 +0,0 @@
|
|||||||
project(glfw)
|
|
||||||
|
|
||||||
# Silence Warnings
|
|
||||||
add_compile_options(-w)
|
|
||||||
|
|
||||||
## GLFW
|
|
||||||
|
|
||||||
# Download
|
|
||||||
set(BUILD_SHARED_LIBS FALSE)
|
|
||||||
set(GLFW_BUILD_EXAMPLES FALSE)
|
|
||||||
set(GLFW_BUILD_TESTS FALSE)
|
|
||||||
set(GLFW_BUILD_DOCS FALSE)
|
|
||||||
set(GLFW_INSTALL FALSE)
|
|
||||||
set(GLFW_BUILD_WIN32 FALSE)
|
|
||||||
set(GLFW_BUILD_COCOA FALSE)
|
|
||||||
set(GLFW_BUILD_X11 TRUE)
|
|
||||||
set(GLFW_BUILD_WAYLAND TRUE)
|
|
||||||
set(GLFW_LIBRARY_TYPE "STATIC")
|
|
||||||
add_subdirectory(src EXCLUDE_FROM_ALL)
|
|
||||||
|
|
||||||
# Ensure Build
|
|
||||||
add_custom_target(glfw-build ALL DEPENDS glfw)
|
|
1
dependencies/glfw/src
vendored
1
dependencies/glfw/src
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 955fbd9d265fa95adf9cb94896eb9a516aa50420
|
|
28
dependencies/libpng/CMakeLists.txt
vendored
28
dependencies/libpng/CMakeLists.txt
vendored
@ -1,20 +1,44 @@
|
|||||||
project(libpng)
|
project(libpng)
|
||||||
|
|
||||||
|
# ZLib (Needed By libpng)
|
||||||
|
add_subdirectory(zlib)
|
||||||
|
|
||||||
# Silence Warnings
|
# Silence Warnings
|
||||||
add_compile_options(-w)
|
add_compile_options(-w)
|
||||||
|
|
||||||
## LibPNG
|
## LibPNG
|
||||||
|
|
||||||
|
# Options
|
||||||
|
set(PNG_TESTS FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_NO_STDIO FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_STATIC FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(PNG_SHARED TRUE CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
set(ZLIB_LIBRARY zlibstatic)
|
set(ZLIB_LIBRARY zlibstatic)
|
||||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../zlib/src" "${CMAKE_CURRENT_BINARY_DIR}/../zlib/src")
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib/src")
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0054 OLD) # Silence Warning
|
set(CMAKE_POLICY_DEFAULT_CMP0054 OLD) # Silence Warning
|
||||||
|
set(CMAKE_POLICY_DEFAULT_CMP0003 NEW) # Silence Warning
|
||||||
|
set(CMAKE_POLICY_DEFAULT_CMP0022 NEW) # Fix Error
|
||||||
|
set(M_LIBRARY m) # No Full Paths!
|
||||||
add_subdirectory(src EXCLUDE_FROM_ALL)
|
add_subdirectory(src EXCLUDE_FROM_ALL)
|
||||||
set(CMAKE_POLICY_DEFAULT_CMP0054 NEW) # Re-Enable New Behavior
|
set(CMAKE_POLICY_DEFAULT_CMP0054 NEW) # Re-Enable New Behavior
|
||||||
set_target_properties(png12 PROPERTIES LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libpng.vers") # Use Symbol Versioning
|
|
||||||
|
# Setup Target
|
||||||
|
set_target_properties(png12 PROPERTIES LINK_OPTIONS "LINKER:--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libpng.vers") # Use Symbol Versioning
|
||||||
set_target_properties(png12 PROPERTIES DEBUG_POSTFIX "") # Fix LibPNG Suffix In Debug Mode
|
set_target_properties(png12 PROPERTIES DEBUG_POSTFIX "") # Fix LibPNG Suffix In Debug Mode
|
||||||
|
target_include_directories(png12 PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>")
|
||||||
|
foreach(zlib_include_dir IN ITEMS "${ZLIB_INCLUDE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/zlib/src")
|
||||||
|
target_include_directories(png12 PUBLIC "$<BUILD_INTERFACE:${zlib_include_dir}>")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
# Ensure Build
|
# Ensure Build
|
||||||
add_custom_target(png12-build ALL DEPENDS png12)
|
add_custom_target(png12-build ALL DEPENDS png12)
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS png12 DESTINATION "${MCPI_LIB_DIR}")
|
install(TARGETS png12 DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS png12 zlibstatic EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# License
|
||||||
|
install(FILES src/LICENSE DESTINATION "${MCPI_LEGAL_DIR}/libpng")
|
||||||
|
@ -6,7 +6,11 @@ add_compile_options(-w)
|
|||||||
## zlib
|
## zlib
|
||||||
|
|
||||||
# Download
|
# Download
|
||||||
|
set(CMAKE_POLICY_DEFAULT_CMP0022 NEW) # Fix Error
|
||||||
add_subdirectory(src EXCLUDE_FROM_ALL)
|
add_subdirectory(src EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
# Ensure Build
|
# Ensure Build
|
||||||
add_custom_target(zlib-build ALL DEPENDS zlibstatic)
|
add_custom_target(zlib-build ALL DEPENDS zlibstatic)
|
||||||
|
|
||||||
|
# License
|
||||||
|
install(FILES src/README DESTINATION "${MCPI_LEGAL_DIR}/zlib")
|
1
dependencies/libpng/zlib/src
vendored
Submodule
1
dependencies/libpng/zlib/src
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 21767c654d31d2dccdde4330529775c6c5fd5389
|
8
dependencies/minecraft-pi/CMakeLists.txt
vendored
8
dependencies/minecraft-pi/CMakeLists.txt
vendored
@ -12,4 +12,10 @@ FetchContent_Declare(
|
|||||||
FetchContent_Populate(minecraft-pi)
|
FetchContent_Populate(minecraft-pi)
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
install(DIRECTORY "${minecraft-pi_SOURCE_DIR}/" DESTINATION "${MCPI_INSTALL_DIR}" USE_SOURCE_PERMISSIONS)
|
install(
|
||||||
|
DIRECTORY "${minecraft-pi_SOURCE_DIR}/"
|
||||||
|
DESTINATION "${MCPI_INSTALL_DIR}/game"
|
||||||
|
USE_SOURCE_PERMISSIONS
|
||||||
|
REGEX "api" EXCLUDE
|
||||||
|
)
|
||||||
|
install_symlink("game/minecraft-pi" "${MCPI_INSTALL_DIR}/minecraft-pi")
|
||||||
|
3
dependencies/zenity/CMakeLists.txt
vendored
3
dependencies/zenity/CMakeLists.txt
vendored
@ -13,3 +13,6 @@ add_custom_target(zenity-build ALL DEPENDS zenity)
|
|||||||
|
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS zenity DESTINATION "${MCPI_BIN_DIR}")
|
install(TARGETS zenity DESTINATION "${MCPI_BIN_DIR}")
|
||||||
|
|
||||||
|
# License
|
||||||
|
install(FILES src/COPYING DESTINATION "${MCPI_LEGAL_DIR}/zenity")
|
||||||
|
2
dependencies/zenity/src
vendored
2
dependencies/zenity/src
vendored
@ -1 +1 @@
|
|||||||
Subproject commit cdcc55e55f08956f6c5a5f3d63fce4614c75e8d4
|
Subproject commit d673e9aab842d7151d92eb9164872dc05e748db2
|
1
dependencies/zlib/src
vendored
1
dependencies/zlib/src
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit cacf7f1d4e3d44d871b605da3b647f07d718623f
|
|
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## Launch Sequence
|
## Launch Sequence
|
||||||
|
|
||||||
|
### Common
|
||||||
|
1. The launcher forks itself
|
||||||
|
1. The child process continues the launch sequence.
|
||||||
|
2. The original process monitors the child process for crashes.
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
1. The launcher is started by the user
|
1. The launcher is started by the user
|
||||||
1. The launcher starts several Zenity dialogs to configure MCPI-Reborn
|
1. The launcher starts several Zenity dialogs to configure MCPI-Reborn
|
||||||
@ -34,12 +39,11 @@ The Media Layer handles MCPI's graphics calls and user input. It replaces MCPI's
|
|||||||
This sub-component re-implements a subset of SDL 1.2 calls with GLFW. It also provides a few utility functions that are used internally by MCPI-Reborn.
|
This sub-component re-implements a subset of SDL 1.2 calls with GLFW. It also provides a few utility functions that are used internally by MCPI-Reborn.
|
||||||
|
|
||||||
The utility functions include:
|
The utility functions include:
|
||||||
* Taking Screenshots
|
|
||||||
* Fullscreen
|
* Fullscreen
|
||||||
* Audio
|
* Audio
|
||||||
* Etc
|
* Etc
|
||||||
|
|
||||||
This is always compiled for the host system's architecture.
|
This is always compiled for the host system's architecture unless the Media Layer Proxy is disabled.
|
||||||
|
|
||||||
This was created because SDL 1.2 has numerous bugs and is in-general unsupported.
|
This was created because SDL 1.2 has numerous bugs and is in-general unsupported.
|
||||||
|
|
||||||
@ -60,21 +64,13 @@ It is made of two parts:
|
|||||||
|
|
||||||
While proxying all Media Layer Core API calls across UNIX pipes does hurt performance, it is better than emulating the entire graphics stack.
|
While proxying all Media Layer Core API calls across UNIX pipes does hurt performance, it is better than emulating the entire graphics stack.
|
||||||
|
|
||||||
Using this in server-mode is redundant (and disallowed).
|
Using this in server-mode is redundant.
|
||||||
|
|
||||||
#### Extras
|
#### Extras
|
||||||
This sub-component contains code that must always be linked directly to MCPI.
|
This sub-component contains code that must always be linked directly to MCPI.
|
||||||
|
|
||||||
This is always compiled for ARM.
|
This is always compiled for ARM.
|
||||||
|
|
||||||
#### Stubs
|
|
||||||
This sub-component implements stubs for various redundant libraries used by MCPI to silence linker errors.
|
|
||||||
|
|
||||||
This is always compiled for ARM.
|
|
||||||
|
|
||||||
##### What To Stub And What To Patch?
|
|
||||||
Most libraries (like ``bcm_host``) can just be replaced with stubs, because they don't need to do anything and aren't used by anything else. However, some libraries (like EGL and X11) might be used by some of MCPI-Reborn's dependencies (like GLFW) so instead of being replaced by a stub, each call is manually patched out from MCPI. A stub is still generated just in case that library isn't present on the system to silence linker errors, but it is only loaded if no other version is available.
|
|
||||||
|
|
||||||
#### Headers
|
#### Headers
|
||||||
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
|
||||||
|
|
||||||
@ -98,12 +94,11 @@ This component contains all MCPI symbols.
|
|||||||
## Dependencies
|
## Dependencies
|
||||||
MCPI-Reborn has several dependencies:
|
MCPI-Reborn has several dependencies:
|
||||||
* MCPI (Bundled)
|
* MCPI (Bundled)
|
||||||
* GLFW (Only In Client Mode)
|
* GLFW (Only In Client Mode; Bundled)
|
||||||
* Open GL ES 1.1
|
* Open GL ES 2.0
|
||||||
* EGL
|
* EGL
|
||||||
* OpenAL (Only In Client Mode)
|
* OpenAL (Only In Client Mode)
|
||||||
* ZLib (Required By LibPNG; Bundled)
|
* ZLib (Required By LibPNG; Bundled)
|
||||||
* LibPNG (Bundled)
|
* LibPNG (Bundled)
|
||||||
* FreeImage (Only In Client Mode)
|
|
||||||
* QEMU User Mode (Only On Non-ARM Hosts; Runtime Only)
|
* QEMU User Mode (Only On Non-ARM Hosts; Runtime Only)
|
||||||
* Zenity (Only In Client Mode; Runtime Only)
|
* Zenity (Only In Client Mode; Runtime Only; Bundled)
|
||||||
|
@ -1,26 +1,11 @@
|
|||||||
# Building
|
# Building
|
||||||
|
|
||||||
## Build Dependencies
|
## Dependencies
|
||||||
* Common
|
|
||||||
* ARM Compiler
|
|
||||||
* Host Compiler (Clang)
|
|
||||||
* CMake
|
|
||||||
* Host Architecture Dependencies
|
|
||||||
* Client Mode Only
|
|
||||||
* GLFW
|
|
||||||
* FreeImage
|
|
||||||
* OpenAL
|
|
||||||
|
|
||||||
## Runtime Dependencies
|
### Debian/Ubuntu
|
||||||
* Non-ARM Host Architectures
|
```sh
|
||||||
* QEMU User-Mode
|
./scripts/install-dependencies.sh
|
||||||
* Host Architecture Dependencies
|
```
|
||||||
* CLient Mode Only
|
|
||||||
* OpenGL ES 1.1
|
|
||||||
* GLFW
|
|
||||||
* FreeImage
|
|
||||||
* OpenAL
|
|
||||||
* Zenity
|
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
```sh
|
```sh
|
||||||
@ -32,3 +17,6 @@
|
|||||||
./scripts/setup.sh <client|server> <armhf|arm64|i686|amd64> <Custom CMake Arguments>
|
./scripts/setup.sh <client|server> <armhf|arm64|i686|amd64> <Custom CMake Arguments>
|
||||||
./scripts/build.sh <client|server> <armhf|arm64|i686|amd64>
|
./scripts/build.sh <client|server> <armhf|arm64|i686|amd64>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
* ``MCPI_TOOLCHAIN_USE_DEFAULT_SEARCH_PATHS``: Use Default CMake Search Paths Rather Than Guessing
|
||||||
|
@ -1,5 +1,100 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
**2.4.3**
|
||||||
|
* Fix Signs With CP-437
|
||||||
|
|
||||||
|
**2.4.2**
|
||||||
|
* Fix Picking Up Lava
|
||||||
|
* Fix Wayland App ID
|
||||||
|
|
||||||
|
**2.4.1**
|
||||||
|
* Allow More Characters In Usernames And Chat
|
||||||
|
* Fix Running On ARMHF Debian Buster
|
||||||
|
|
||||||
|
**2.4.0**
|
||||||
|
* [Modding SDK](../example-mods/README.md)
|
||||||
|
* Cache Blacklist/Whitelist
|
||||||
|
* More Reliable AppImages
|
||||||
|
* CMake Refactors
|
||||||
|
* Disable Broken Touchscreen-Specific Block Outline Behavior
|
||||||
|
* Add ``Remove Forced GUI Lag (Can Break Joining Servers)`` Feature Flag (Disabled By Default)
|
||||||
|
* Add ``Add Buckets`` Feature Flag (Enabled By Default)
|
||||||
|
* Add ``Classic HUD`` Feature Flag (Enabled By Default)
|
||||||
|
* Add ``Translucent Toolbar`` Feature Flag (Enabled By Default)
|
||||||
|
* Add ``Force EGL`` Feature Flag (Disabled By Default)
|
||||||
|
* Fix Sound Pitch/Volume/Attenuation
|
||||||
|
* Fix Holding Left-Click When Attacking
|
||||||
|
* Don't Force EGL (Should Fix Some NVIDIA Systems)
|
||||||
|
* Performance Fixes
|
||||||
|
|
||||||
|
**2.3.13**
|
||||||
|
* Fix Texture Bug
|
||||||
|
|
||||||
|
**2.3.12**
|
||||||
|
* Media Layer Proxy Optimizations
|
||||||
|
* Bug Fixes
|
||||||
|
|
||||||
|
**2.3.11**
|
||||||
|
* ``--version`` Command Line Option
|
||||||
|
* TPS Measured In Benchmark & Server
|
||||||
|
* Front-Facing Third-Person
|
||||||
|
* GLESv1 Comparability Layer
|
||||||
|
* Miscellaneous Bug Fixes
|
||||||
|
|
||||||
|
**2.3.10**
|
||||||
|
* Add Crash Report Dialog
|
||||||
|
* Disable V-Sync By Default
|
||||||
|
* Refactor Child Process Management
|
||||||
|
* Improve Build System
|
||||||
|
* Support For Building On Ubuntu 22.04
|
||||||
|
|
||||||
|
**2.3.9**
|
||||||
|
* Bundle An ARM Sysroot
|
||||||
|
* Not Used On ARM32 Systems
|
||||||
|
* Based On Debian Bullseye
|
||||||
|
* Colored Log Output
|
||||||
|
|
||||||
|
**2.3.8**
|
||||||
|
* Switch Up Mod Loading Order
|
||||||
|
|
||||||
|
**2.3.7**
|
||||||
|
* Don't Append Hyphens To New World Name, Only Folder Names
|
||||||
|
|
||||||
|
**2.3.6**
|
||||||
|
* Fix ``Invert Y-axis`` Option Name
|
||||||
|
* Improve Touch GUI Inventory In Non-Touch GUI
|
||||||
|
* New Create World Dialog
|
||||||
|
* Controlled By ``Implement Create World Dialog`` Feature Flag (Enabled By Default)
|
||||||
|
* Custom World Names
|
||||||
|
* Game-Mode Selection
|
||||||
|
* Custom Seeds
|
||||||
|
|
||||||
|
**2.3.5**
|
||||||
|
* Renamed Some Feature Flags
|
||||||
|
* Add ``Improved Title Background`` Feature Flag (Enabled By Default)
|
||||||
|
* Non-Touch GUI Rework
|
||||||
|
* Make ``Full Touch GUI`` Feature Flag Disabled By Default
|
||||||
|
* Add ``Force Touch GUI Button Behavior`` Feature Flag (Enabled By Default)
|
||||||
|
* Add ``Improved Button Hover Behavior`` Feature Flag (Enabled By Default)
|
||||||
|
|
||||||
|
**2.3.4**
|
||||||
|
* AppImage Fixes
|
||||||
|
* Make Death Messages Customizable Server-Side
|
||||||
|
* Fix Q-Key Behavior Behavior When Editing Signs
|
||||||
|
* Add ``Force Touch Inventory`` Feature Flag (Disabled By Default)
|
||||||
|
* Add ``Fix Pause Menu`` Feature Flag (Enabled By Default)
|
||||||
|
* Enables Server Visibility Toggle Button
|
||||||
|
* Options Changes (Not Supported On Legacy)
|
||||||
|
* Add ``Fix Options Screen`` Feature Flag (Enabled By Default)
|
||||||
|
* Adds Options Button To Classic UI Start Screen
|
||||||
|
* Removes Useless Options Toggles
|
||||||
|
* Fixes Options Toggles' Default Position
|
||||||
|
* Store Multiple Settings In `options.txt`
|
||||||
|
* ``Peaceful Mode`` Feature Flag Moved To ``game_difficulty``
|
||||||
|
* ``Smooth Lighting`` Feature Flag Moved To ``gfx_ao``
|
||||||
|
* ``Fancy Graphics`` Feature Flag Moved To ``gfx_fancygraphics``
|
||||||
|
* ``Disable Hosting LAN Worlds`` Feature Flag Moved To ``mp_server_visible_default``
|
||||||
|
|
||||||
**2.3.3**
|
**2.3.3**
|
||||||
* Add More Blocks To Expanded Creative Inventory
|
* Add More Blocks To Expanded Creative Inventory
|
||||||
* Add AppStream Metadata
|
* Add AppStream Metadata
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
## Command Line Arguments
|
## Command Line Arguments
|
||||||
|
|
||||||
|
### ``--version`` (Or ``-v``)
|
||||||
|
If you run MCPI-Reborn with ``--version`` it will print its version to ``stdout``.
|
||||||
|
|
||||||
### ``--print-available-feature-flags`` (Client Mode Only)
|
### ``--print-available-feature-flags`` (Client Mode Only)
|
||||||
If you run MCPI-Reborn with ``--print-available-feature-flags``, it will print the available feature flags and then immediately exit.
|
If you run MCPI-Reborn with ``--print-available-feature-flags``, it will print the available feature flags to ``stdout`` and then immediately exit.
|
||||||
|
|
||||||
The feature flags are printed in the following format:
|
The feature flags are printed in the following format:
|
||||||
```
|
```
|
||||||
@ -11,6 +14,9 @@ TRUE This Flag Is On By Default
|
|||||||
FALSE This Flag Is Off By Default
|
FALSE This Flag Is Off By Default
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### ``--default`` (Client Mode Only)
|
||||||
|
If you run MCPI-Reborn with ``--default``, it will skip the startup configuration dialogs and just use the default values.
|
||||||
|
|
||||||
### ``--only-generate`` (Server Mode Only)
|
### ``--only-generate`` (Server Mode Only)
|
||||||
If you run MCPI-Reborn with ``--only-generate``, it will immediately exit once world generation has completed. This is mainly used for automatically testing MCPI-Reborn.
|
If you run MCPI-Reborn with ``--only-generate``, it will immediately exit once world generation has completed. This is mainly used for automatically testing MCPI-Reborn.
|
||||||
|
|
||||||
@ -22,18 +28,18 @@ The world used will always be re-created on start and uses a hard-coded seed.
|
|||||||
## Environmental Variables
|
## Environmental Variables
|
||||||
|
|
||||||
### ``MCPI_DEBUG``
|
### ``MCPI_DEBUG``
|
||||||
This enables debug logging if you set it to any non-zero-length value.
|
This enables debug logging if it is set.
|
||||||
|
|
||||||
### Client Mode Only
|
### Client Mode Only
|
||||||
If a value isn't set for any of the following variables, a GUI will open that allows you to select one.
|
If any of the following variables aren't set, one configuration dialog will open on startup for each unset variable.
|
||||||
|
|
||||||
### ``MCPI_FEATURE_FLAGS``
|
#### ``MCPI_FEATURE_FLAGS``
|
||||||
This corresponds to ``--print-available-feature-flags``. This is just a list of all enabled feature flags separated by ``|``.
|
This corresponds to ``--print-available-feature-flags``. This is just a list of all enabled feature flags separated by ``|``.
|
||||||
|
|
||||||
For instance, the string ``Feature A|Feature B`` would enable both ``Feature A`` and ``Feature B`` and *disable every other available feature flag*.
|
For instance, the string ``Feature A|Feature B`` would enable both ``Feature A`` and ``Feature B`` and *disable every other available feature flag*.
|
||||||
|
|
||||||
### ``MCPI_RENDER_DISTANCE``
|
#### ``MCPI_RENDER_DISTANCE``
|
||||||
This is the render distance. The possible values are: ``Far``, ``Normal``, ``Short``, and ``Tiny``.
|
This is the render distance. The possible values are: ``Far``, ``Normal``, ``Short``, and ``Tiny``.
|
||||||
|
|
||||||
### ``MCPI_USERNAME``
|
#### ``MCPI_USERNAME``
|
||||||
This is the username.
|
This is the username.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Dedicated Server
|
# Dedicated Server
|
||||||
The dedicated server is a version of Minecraft: Pi Edition modified to run in a headless environment. It loads settings from a ``server.properties`` file.
|
The dedicated server is a version of Minecraft: Pi Edition modified to run in a headless environment. It loads settings from a ``server.properties`` file.
|
||||||
|
|
||||||
This server is also compatible with MCPE Alpha v0.6.1.
|
This server is also compatible with MCPE Alpha v0.6.1[^1].
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
@ -15,3 +15,5 @@ An official Docker image is also provided: [thebrokenrail/minecraft-pi-reborn-se
|
|||||||
* Player data is not saved because of limitations with MCPE LAN worlds
|
* Player data is not saved because of limitations with MCPE LAN worlds
|
||||||
* An easy workaround is to place your inventory in a chest before logging off
|
* An easy workaround is to place your inventory in a chest before logging off
|
||||||
* Survival Mode servers are incompatible with unmodded MCPI
|
* Survival Mode servers are incompatible with unmodded MCPI
|
||||||
|
|
||||||
|
[^1]: The exception to this is buckets, those will crash MCPE players.
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
# Manual Installation
|
# Installation
|
||||||
[Download Packages Here](https://jenkins.thebrokenrail.com/job/minecraft-pi-reborn/job/master/lastSuccessfulBuild/artifact/out/)
|
|
||||||
|
|
||||||
## Picking A Package
|
## AppImage
|
||||||
|
Download packages [here](https://jenkins.thebrokenrail.com/job/minecraft-pi-reborn/job/master/lastSuccessfulBuild/artifact/out/).
|
||||||
|
|
||||||
### Name Format
|
### System Requirements
|
||||||
```
|
* Debian Buster/Ubuntu 18.04 Or Higher
|
||||||
minecraft-pi-reborn-<Variant>_X.Y.Z_<Architecture>
|
* QEMU User-Mode
|
||||||
```
|
* Debian/Ubuntu: ``sudo apt install qemu-user``
|
||||||
|
* Arch: ``sudo pacman -Sy qemu-user``
|
||||||
|
* Client-Only Dependencies
|
||||||
|
* Graphics Drivers
|
||||||
|
* GTK+ 3
|
||||||
|
* Debian/Ubuntu: ``sudo apt install libgtk-3-0``
|
||||||
|
* Arch: ``sudo pacman -Sy gtk3``
|
||||||
|
* OpenAL
|
||||||
|
* Debian/Ubuntu: ``sudo apt install libopenal1``
|
||||||
|
* Arch: ``sudo pacman -Sy openal``
|
||||||
|
|
||||||
### Picking A Variant
|
### Running
|
||||||
* ``client``: Client mode, use this if you want to play MCPI
|
Follow [these](https://docs.appimage.org/introduction/quickstart.html#how-to-run-an-appimage) instructions.
|
||||||
* ``server``: Server mode, use this if you want to host a dedicated MCPI server
|
|
||||||
|
|
||||||
### Picking An Architecture
|
## Flatpak
|
||||||
* ``amd64``: x86_64, use this if you are using a device with an AMD or Intel processor
|
<a href="https://flathub.org/apps/details/com.thebrokenrail.MCPIReborn"><img width="240" alt="Download On Flathub" src="https://flathub.org/assets/badges/flathub-badge-en.svg" /></a>
|
||||||
* ``armhf``: ARM 32-Bit, use this if you are using an ARM device (like a Raspberry Pi)
|
|
||||||
* ``arm64``: ARM 64-Bit, ``armhf`` but for 64-bit devices
|
### Note
|
||||||
|
Game data is stored in ``~/.var/app/com.thebrokenrail.MCPIReborn/.minecraft-pi`` instead of ``~/.minecraft-pi``.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Documentation
|
# Documentation
|
||||||
* [View Manual Installation](INSTALL.md)
|
* [View Installation](INSTALL.md)
|
||||||
* [View Overriding Assets](OVERRIDING_ASSETS.md)
|
* [View Overriding Assets](OVERRIDING_ASSETS.md)
|
||||||
* [View Dedicated Server](DEDICATED_SERVER.md)
|
* [View Dedicated Server](DEDICATED_SERVER.md)
|
||||||
* [View Credits](CREDITS.md)
|
* [View Credits](CREDITS.md)
|
||||||
|
19
example-mods/README.md
Normal file
19
example-mods/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Example Mods
|
||||||
|
This is an example of a mod that can be built using the modding SDK.
|
||||||
|
|
||||||
|
* **Expanded Creative Mod**: This specific mod adds even more items and blocks to the Creative Inventory. It was originally by [@Bigjango13](https://github.com/bigjango13).
|
||||||
|
* **Chat Commands Mod**: This specific mod makes an chat message starting with a ``/`` handled by the MCPI API.
|
||||||
|
* **Recipes Mod**: This specific mod demos custom recipes.
|
||||||
|
|
||||||
|
## The SDK
|
||||||
|
The modding SDK is a collection of exported CMake targets that allows anyone to create their own MCPI mod!
|
||||||
|
|
||||||
|
The SDK is copied to ``~/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake`` whenever MCPI-Reborn is started.
|
||||||
|
|
||||||
|
## How do I use this?
|
||||||
|
```sh
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
cp libexpanded-creative.so ~/.minecraft-pi/mods
|
||||||
|
```
|
15
example-mods/chat-commands/.gitignore
vendored
Normal file
15
example-mods/chat-commands/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/out
|
||||||
|
/debian/tmp
|
||||||
|
/.vscode
|
||||||
|
/build*
|
||||||
|
/CMakeLists.txt.user
|
||||||
|
*.autosave
|
||||||
|
/AppImageBuilder.yml
|
||||||
|
/appimage-builder-cache
|
||||||
|
/appimage-build
|
||||||
|
/AppDir
|
||||||
|
/*.zsync
|
||||||
|
/*.AppImage
|
||||||
|
/core*
|
||||||
|
/qemu_*
|
||||||
|
/cmake/.prebuilt-armhf-toolchain
|
15
example-mods/chat-commands/CMakeLists.txt
Normal file
15
example-mods/chat-commands/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
# Build For ARM
|
||||||
|
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
|
||||||
|
|
||||||
|
# Start Project
|
||||||
|
project(chat-commands)
|
||||||
|
|
||||||
|
# Include SDK
|
||||||
|
include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
|
||||||
|
|
||||||
|
# Build
|
||||||
|
add_library(chat-commands SHARED chat-commands.cpp)
|
||||||
|
target_link_libraries(chat-commands mods-headers reborn-patch symbols chat misc)
|
25
example-mods/chat-commands/chat-commands.cpp
Normal file
25
example-mods/chat-commands/chat-commands.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Headers
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
#include <symbols/minecraft.h>
|
||||||
|
#include <mods/chat/chat.h>
|
||||||
|
#include <mods/misc/misc.h>
|
||||||
|
|
||||||
|
// The Actual Mod
|
||||||
|
HOOK(chat_handle_packet_send, void, (unsigned char *minecraft, unsigned char *packet)) {
|
||||||
|
// Get Message
|
||||||
|
char *message = *(char **) (packet + ChatPacket_message_property_offset);
|
||||||
|
if (message[0] == '/') {
|
||||||
|
// API Command
|
||||||
|
unsigned char *gui = minecraft + Minecraft_gui_property_offset;
|
||||||
|
std::string out = chat_send_api_command(minecraft, &message[1]);
|
||||||
|
if (out.length() > 0 && out[out.length() - 1] == '\n') {
|
||||||
|
out[out.length() - 1] = '\0';
|
||||||
|
}
|
||||||
|
misc_add_message(gui, out.c_str());
|
||||||
|
} else {
|
||||||
|
// Call Original Method
|
||||||
|
ensure_chat_handle_packet_send();
|
||||||
|
(*real_chat_handle_packet_send)(minecraft, packet);
|
||||||
|
}
|
||||||
|
}
|
15
example-mods/expanded-creative/.gitignore
vendored
Normal file
15
example-mods/expanded-creative/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/out
|
||||||
|
/debian/tmp
|
||||||
|
/.vscode
|
||||||
|
/build*
|
||||||
|
/CMakeLists.txt.user
|
||||||
|
*.autosave
|
||||||
|
/AppImageBuilder.yml
|
||||||
|
/appimage-builder-cache
|
||||||
|
/appimage-build
|
||||||
|
/AppDir
|
||||||
|
/*.zsync
|
||||||
|
/*.AppImage
|
||||||
|
/core*
|
||||||
|
/qemu_*
|
||||||
|
/cmake/.prebuilt-armhf-toolchain
|
15
example-mods/expanded-creative/CMakeLists.txt
Normal file
15
example-mods/expanded-creative/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
# Build For ARM
|
||||||
|
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
|
||||||
|
|
||||||
|
# Start Project
|
||||||
|
project(expanded-creative)
|
||||||
|
|
||||||
|
# Include SDK
|
||||||
|
include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
|
||||||
|
|
||||||
|
# Build
|
||||||
|
add_library(expanded-creative SHARED expanded-creative.cpp)
|
||||||
|
target_link_libraries(expanded-creative mods-headers reborn-patch symbols misc)
|
638
example-mods/expanded-creative/expanded-creative.cpp
Normal file
638
example-mods/expanded-creative/expanded-creative.cpp
Normal file
@ -0,0 +1,638 @@
|
|||||||
|
// Headers
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
#include <symbols/minecraft.h>
|
||||||
|
#include <mods/misc/misc.h>
|
||||||
|
|
||||||
|
// The Actual Mod
|
||||||
|
|
||||||
|
static void Inventory_setupDefault_FillingContainer_addItem_call_injection(unsigned char *filling_container) {
|
||||||
|
ItemInstance *fire_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(fire_instance);
|
||||||
|
fire_instance->count = 255;
|
||||||
|
fire_instance->auxiliary = 0;
|
||||||
|
fire_instance->id = 51;
|
||||||
|
(*FillingContainer_addItem)(filling_container, fire_instance);
|
||||||
|
|
||||||
|
ItemInstance *mushroomStew_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(mushroomStew_instance);
|
||||||
|
mushroomStew_instance->count = 255;
|
||||||
|
mushroomStew_instance->auxiliary = 0;
|
||||||
|
mushroomStew_instance->id = 282;
|
||||||
|
(*FillingContainer_addItem)(filling_container, mushroomStew_instance);
|
||||||
|
|
||||||
|
ItemInstance *steak_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(steak_instance);
|
||||||
|
steak_instance->count = 255;
|
||||||
|
steak_instance->auxiliary = 0;
|
||||||
|
steak_instance->id = 364;
|
||||||
|
(*FillingContainer_addItem)(filling_container, steak_instance);
|
||||||
|
|
||||||
|
ItemInstance *cookedChicken_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(cookedChicken_instance);
|
||||||
|
cookedChicken_instance->count = 255;
|
||||||
|
cookedChicken_instance->auxiliary = 0;
|
||||||
|
cookedChicken_instance->id = 366;
|
||||||
|
(*FillingContainer_addItem)(filling_container, cookedChicken_instance);
|
||||||
|
|
||||||
|
ItemInstance *porkCooked_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(porkCooked_instance);
|
||||||
|
porkCooked_instance->count = 255;
|
||||||
|
porkCooked_instance->auxiliary = 0;
|
||||||
|
porkCooked_instance->id = 320;
|
||||||
|
(*FillingContainer_addItem)(filling_container, porkCooked_instance);
|
||||||
|
|
||||||
|
ItemInstance *apple_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(apple_instance);
|
||||||
|
apple_instance->count = 255;
|
||||||
|
apple_instance->auxiliary = 0;
|
||||||
|
apple_instance->id = 260;
|
||||||
|
(*FillingContainer_addItem)(filling_container, apple_instance);
|
||||||
|
|
||||||
|
ItemInstance *tallGrass_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(tallGrass_instance);
|
||||||
|
tallGrass_instance->count = 255;
|
||||||
|
tallGrass_instance->auxiliary = 0;
|
||||||
|
tallGrass_instance->id = 31;
|
||||||
|
(*FillingContainer_addItem)(filling_container, tallGrass_instance);
|
||||||
|
|
||||||
|
ItemInstance *crops_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(crops_instance);
|
||||||
|
crops_instance->count = 255;
|
||||||
|
crops_instance->auxiliary = 0;
|
||||||
|
crops_instance->id = 59;
|
||||||
|
(*FillingContainer_addItem)(filling_container, crops_instance);
|
||||||
|
|
||||||
|
ItemInstance *farmland_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(farmland_instance);
|
||||||
|
farmland_instance->count = 255;
|
||||||
|
farmland_instance->auxiliary = 0;
|
||||||
|
farmland_instance->id = 60;
|
||||||
|
(*FillingContainer_addItem)(filling_container, farmland_instance);
|
||||||
|
|
||||||
|
ItemInstance *activeFurnace_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(activeFurnace_instance);
|
||||||
|
activeFurnace_instance->count = 255;
|
||||||
|
activeFurnace_instance->auxiliary = 0;
|
||||||
|
activeFurnace_instance->id = 62;
|
||||||
|
(*FillingContainer_addItem)(filling_container, activeFurnace_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironDoor_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironDoor_instance);
|
||||||
|
ironDoor_instance->count = 255;
|
||||||
|
ironDoor_instance->auxiliary = 0;
|
||||||
|
ironDoor_instance->id = 330;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironDoor_instance);
|
||||||
|
|
||||||
|
ItemInstance *activeRedstoneOre_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(activeRedstoneOre_instance);
|
||||||
|
activeRedstoneOre_instance->count = 255;
|
||||||
|
activeRedstoneOre_instance->auxiliary = 0;
|
||||||
|
activeRedstoneOre_instance->id = 74;
|
||||||
|
(*FillingContainer_addItem)(filling_container, activeRedstoneOre_instance);
|
||||||
|
|
||||||
|
ItemInstance *pumkinStem_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(pumkinStem_instance);
|
||||||
|
pumkinStem_instance->count = 255;
|
||||||
|
pumkinStem_instance->auxiliary = 0;
|
||||||
|
pumkinStem_instance->id = 105;
|
||||||
|
(*FillingContainer_addItem)(filling_container, pumkinStem_instance);
|
||||||
|
|
||||||
|
ItemInstance *newGrass_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(newGrass_instance);
|
||||||
|
newGrass_instance->count = 255;
|
||||||
|
newGrass_instance->auxiliary = 0;
|
||||||
|
newGrass_instance->id = 253;
|
||||||
|
(*FillingContainer_addItem)(filling_container, newGrass_instance);
|
||||||
|
|
||||||
|
ItemInstance *reserved6_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(reserved6_instance);
|
||||||
|
reserved6_instance->count = 255;
|
||||||
|
reserved6_instance->auxiliary = 0;
|
||||||
|
reserved6_instance->id = 1;
|
||||||
|
(*FillingContainer_addItem)(filling_container, reserved6_instance);
|
||||||
|
|
||||||
|
ItemInstance *doubleStoneSlab_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(doubleStoneSlab_instance);
|
||||||
|
doubleStoneSlab_instance->count = 255;
|
||||||
|
doubleStoneSlab_instance->auxiliary = 0;
|
||||||
|
doubleStoneSlab_instance->id = 43;
|
||||||
|
(*FillingContainer_addItem)(filling_container, doubleStoneSlab_instance);
|
||||||
|
|
||||||
|
ItemInstance *arrow_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(arrow_instance);
|
||||||
|
arrow_instance->count = 255;
|
||||||
|
arrow_instance->auxiliary = 0;
|
||||||
|
arrow_instance->id = 262;
|
||||||
|
(*FillingContainer_addItem)(filling_container, arrow_instance);
|
||||||
|
|
||||||
|
ItemInstance *coal_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(coal_instance);
|
||||||
|
coal_instance->count = 255;
|
||||||
|
coal_instance->auxiliary = 0;
|
||||||
|
coal_instance->id = 263;
|
||||||
|
(*FillingContainer_addItem)(filling_container, coal_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamond_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamond_instance);
|
||||||
|
diamond_instance->count = 255;
|
||||||
|
diamond_instance->auxiliary = 0;
|
||||||
|
diamond_instance->id = 264;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamond_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironIngot_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironIngot_instance);
|
||||||
|
ironIngot_instance->count = 255;
|
||||||
|
ironIngot_instance->auxiliary = 0;
|
||||||
|
ironIngot_instance->id = 265;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironIngot_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldIngot_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldIngot_instance);
|
||||||
|
goldIngot_instance->count = 255;
|
||||||
|
goldIngot_instance->auxiliary = 0;
|
||||||
|
goldIngot_instance->id = 266;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldIngot_instance);
|
||||||
|
|
||||||
|
ItemInstance *woodSword_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(woodSword_instance);
|
||||||
|
woodSword_instance->count = 255;
|
||||||
|
woodSword_instance->auxiliary = 0;
|
||||||
|
woodSword_instance->id = 268;
|
||||||
|
(*FillingContainer_addItem)(filling_container, woodSword_instance);
|
||||||
|
|
||||||
|
ItemInstance *woodShovel_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(woodShovel_instance);
|
||||||
|
woodShovel_instance->count = 255;
|
||||||
|
woodShovel_instance->auxiliary = 0;
|
||||||
|
woodShovel_instance->id = 269;
|
||||||
|
(*FillingContainer_addItem)(filling_container, woodShovel_instance);
|
||||||
|
|
||||||
|
ItemInstance *woodPickaxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(woodPickaxe_instance);
|
||||||
|
woodPickaxe_instance->count = 255;
|
||||||
|
woodPickaxe_instance->auxiliary = 0;
|
||||||
|
woodPickaxe_instance->id = 270;
|
||||||
|
(*FillingContainer_addItem)(filling_container, woodPickaxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *woodAxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(woodAxe_instance);
|
||||||
|
woodAxe_instance->count = 255;
|
||||||
|
woodAxe_instance->auxiliary = 0;
|
||||||
|
woodAxe_instance->id = 271;
|
||||||
|
(*FillingContainer_addItem)(filling_container, woodAxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *stoneSword_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(stoneSword_instance);
|
||||||
|
stoneSword_instance->count = 255;
|
||||||
|
stoneSword_instance->auxiliary = 0;
|
||||||
|
stoneSword_instance->id = 272;
|
||||||
|
(*FillingContainer_addItem)(filling_container, stoneSword_instance);
|
||||||
|
|
||||||
|
ItemInstance *stoneShovel_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(stoneShovel_instance);
|
||||||
|
stoneShovel_instance->count = 255;
|
||||||
|
stoneShovel_instance->auxiliary = 0;
|
||||||
|
stoneShovel_instance->id = 273;
|
||||||
|
(*FillingContainer_addItem)(filling_container, stoneShovel_instance);
|
||||||
|
|
||||||
|
ItemInstance *stonePickaxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(stonePickaxe_instance);
|
||||||
|
stonePickaxe_instance->count = 255;
|
||||||
|
stonePickaxe_instance->auxiliary = 0;
|
||||||
|
stonePickaxe_instance->id = 274;
|
||||||
|
(*FillingContainer_addItem)(filling_container, stonePickaxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *stoneAxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(stoneAxe_instance);
|
||||||
|
stoneAxe_instance->count = 255;
|
||||||
|
stoneAxe_instance->auxiliary = 0;
|
||||||
|
stoneAxe_instance->id = 275;
|
||||||
|
(*FillingContainer_addItem)(filling_container, stoneAxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *shovelIron_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(shovelIron_instance);
|
||||||
|
shovelIron_instance->count = 255;
|
||||||
|
shovelIron_instance->auxiliary = 0;
|
||||||
|
shovelIron_instance->id = 256;
|
||||||
|
(*FillingContainer_addItem)(filling_container, shovelIron_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironPick_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironPick_instance);
|
||||||
|
ironPick_instance->count = 255;
|
||||||
|
ironPick_instance->auxiliary = 0;
|
||||||
|
ironPick_instance->id = 257;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironPick_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironAxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironAxe_instance);
|
||||||
|
ironAxe_instance->count = 255;
|
||||||
|
ironAxe_instance->auxiliary = 0;
|
||||||
|
ironAxe_instance->id = 258;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironAxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondSword_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondSword_instance);
|
||||||
|
diamondSword_instance->count = 255;
|
||||||
|
diamondSword_instance->auxiliary = 0;
|
||||||
|
diamondSword_instance->id = 276;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondSword_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondShovel_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondShovel_instance);
|
||||||
|
diamondShovel_instance->count = 255;
|
||||||
|
diamondShovel_instance->auxiliary = 0;
|
||||||
|
diamondShovel_instance->id = 277;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondShovel_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondPickaxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondPickaxe_instance);
|
||||||
|
diamondPickaxe_instance->count = 255;
|
||||||
|
diamondPickaxe_instance->auxiliary = 0;
|
||||||
|
diamondPickaxe_instance->id = 278;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondPickaxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondAxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondAxe_instance);
|
||||||
|
diamondAxe_instance->count = 255;
|
||||||
|
diamondAxe_instance->auxiliary = 0;
|
||||||
|
diamondAxe_instance->id = 279;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondAxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *magicWand_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(magicWand_instance);
|
||||||
|
magicWand_instance->count = 255;
|
||||||
|
magicWand_instance->auxiliary = 0;
|
||||||
|
magicWand_instance->id = 280;
|
||||||
|
(*FillingContainer_addItem)(filling_container, magicWand_instance);
|
||||||
|
|
||||||
|
ItemInstance *bowl_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(bowl_instance);
|
||||||
|
bowl_instance->count = 255;
|
||||||
|
bowl_instance->auxiliary = 0;
|
||||||
|
bowl_instance->id = 281;
|
||||||
|
(*FillingContainer_addItem)(filling_container, bowl_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldSword_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldSword_instance);
|
||||||
|
goldSword_instance->count = 255;
|
||||||
|
goldSword_instance->auxiliary = 0;
|
||||||
|
goldSword_instance->id = 283;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldSword_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldShovel_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldShovel_instance);
|
||||||
|
goldShovel_instance->count = 255;
|
||||||
|
goldShovel_instance->auxiliary = 0;
|
||||||
|
goldShovel_instance->id = 284;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldShovel_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldPickaxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldPickaxe_instance);
|
||||||
|
goldPickaxe_instance->count = 255;
|
||||||
|
goldPickaxe_instance->auxiliary = 0;
|
||||||
|
goldPickaxe_instance->id = 285;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldPickaxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldAxe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldAxe_instance);
|
||||||
|
goldAxe_instance->count = 255;
|
||||||
|
goldAxe_instance->auxiliary = 0;
|
||||||
|
goldAxe_instance->id = 286;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldAxe_instance);
|
||||||
|
|
||||||
|
ItemInstance *string_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(string_instance);
|
||||||
|
string_instance->count = 255;
|
||||||
|
string_instance->auxiliary = 0;
|
||||||
|
string_instance->id = 287;
|
||||||
|
(*FillingContainer_addItem)(filling_container, string_instance);
|
||||||
|
|
||||||
|
ItemInstance *feather_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(feather_instance);
|
||||||
|
feather_instance->count = 255;
|
||||||
|
feather_instance->auxiliary = 0;
|
||||||
|
feather_instance->id = 288;
|
||||||
|
(*FillingContainer_addItem)(filling_container, feather_instance);
|
||||||
|
|
||||||
|
ItemInstance *gunpowder_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(gunpowder_instance);
|
||||||
|
gunpowder_instance->count = 255;
|
||||||
|
gunpowder_instance->auxiliary = 0;
|
||||||
|
gunpowder_instance->id = 289;
|
||||||
|
(*FillingContainer_addItem)(filling_container, gunpowder_instance);
|
||||||
|
|
||||||
|
ItemInstance *woodHoe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(woodHoe_instance);
|
||||||
|
woodHoe_instance->count = 255;
|
||||||
|
woodHoe_instance->auxiliary = 0;
|
||||||
|
woodHoe_instance->id = 290;
|
||||||
|
(*FillingContainer_addItem)(filling_container, woodHoe_instance);
|
||||||
|
|
||||||
|
ItemInstance *stoneHoe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(stoneHoe_instance);
|
||||||
|
stoneHoe_instance->count = 255;
|
||||||
|
stoneHoe_instance->auxiliary = 0;
|
||||||
|
stoneHoe_instance->id = 291;
|
||||||
|
(*FillingContainer_addItem)(filling_container, stoneHoe_instance);
|
||||||
|
|
||||||
|
ItemInstance *flint1_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(flint1_instance);
|
||||||
|
flint1_instance->count = 255;
|
||||||
|
flint1_instance->auxiliary = 0;
|
||||||
|
flint1_instance->id = 292;
|
||||||
|
(*FillingContainer_addItem)(filling_container, flint1_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondHoe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondHoe_instance);
|
||||||
|
diamondHoe_instance->count = 255;
|
||||||
|
diamondHoe_instance->auxiliary = 0;
|
||||||
|
diamondHoe_instance->id = 293;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondHoe_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldHoe_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldHoe_instance);
|
||||||
|
goldHoe_instance->count = 255;
|
||||||
|
goldHoe_instance->auxiliary = 0;
|
||||||
|
goldHoe_instance->id = 294;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldHoe_instance);
|
||||||
|
|
||||||
|
ItemInstance *seeds_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(seeds_instance);
|
||||||
|
seeds_instance->count = 255;
|
||||||
|
seeds_instance->auxiliary = 0;
|
||||||
|
seeds_instance->id = 295;
|
||||||
|
(*FillingContainer_addItem)(filling_container, seeds_instance);
|
||||||
|
|
||||||
|
ItemInstance *wheat_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(wheat_instance);
|
||||||
|
wheat_instance->count = 255;
|
||||||
|
wheat_instance->auxiliary = 0;
|
||||||
|
wheat_instance->id = 296;
|
||||||
|
(*FillingContainer_addItem)(filling_container, wheat_instance);
|
||||||
|
|
||||||
|
ItemInstance *bread_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(bread_instance);
|
||||||
|
bread_instance->count = 255;
|
||||||
|
bread_instance->auxiliary = 0;
|
||||||
|
bread_instance->id = 297;
|
||||||
|
(*FillingContainer_addItem)(filling_container, bread_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondHelm_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondHelm_instance);
|
||||||
|
diamondHelm_instance->count = 255;
|
||||||
|
diamondHelm_instance->auxiliary = 0;
|
||||||
|
diamondHelm_instance->id = 310;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondHelm_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondChest_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondChest_instance);
|
||||||
|
diamondChest_instance->count = 255;
|
||||||
|
diamondChest_instance->auxiliary = 0;
|
||||||
|
diamondChest_instance->id = 311;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondChest_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondLeg_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondLeg_instance);
|
||||||
|
diamondLeg_instance->count = 255;
|
||||||
|
diamondLeg_instance->auxiliary = 0;
|
||||||
|
diamondLeg_instance->id = 312;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondLeg_instance);
|
||||||
|
|
||||||
|
ItemInstance *diamondBoot_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(diamondBoot_instance);
|
||||||
|
diamondBoot_instance->count = 255;
|
||||||
|
diamondBoot_instance->auxiliary = 0;
|
||||||
|
diamondBoot_instance->id = 313;
|
||||||
|
(*FillingContainer_addItem)(filling_container, diamondBoot_instance);
|
||||||
|
|
||||||
|
ItemInstance *leatherCap_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(leatherCap_instance);
|
||||||
|
leatherCap_instance->count = 255;
|
||||||
|
leatherCap_instance->auxiliary = 0;
|
||||||
|
leatherCap_instance->id = 298;
|
||||||
|
(*FillingContainer_addItem)(filling_container, leatherCap_instance);
|
||||||
|
|
||||||
|
ItemInstance *leatherShirt_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(leatherShirt_instance);
|
||||||
|
leatherShirt_instance->count = 255;
|
||||||
|
leatherShirt_instance->auxiliary = 0;
|
||||||
|
leatherShirt_instance->id = 299;
|
||||||
|
(*FillingContainer_addItem)(filling_container, leatherShirt_instance);
|
||||||
|
|
||||||
|
ItemInstance *leatherPants_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(leatherPants_instance);
|
||||||
|
leatherPants_instance->count = 255;
|
||||||
|
leatherPants_instance->auxiliary = 0;
|
||||||
|
leatherPants_instance->id = 300;
|
||||||
|
(*FillingContainer_addItem)(filling_container, leatherPants_instance);
|
||||||
|
|
||||||
|
ItemInstance *leatherBoots_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(leatherBoots_instance);
|
||||||
|
leatherBoots_instance->count = 255;
|
||||||
|
leatherBoots_instance->auxiliary = 0;
|
||||||
|
leatherBoots_instance->id = 301;
|
||||||
|
(*FillingContainer_addItem)(filling_container, leatherBoots_instance);
|
||||||
|
|
||||||
|
ItemInstance *chainHelm_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(chainHelm_instance);
|
||||||
|
chainHelm_instance->count = 255;
|
||||||
|
chainHelm_instance->auxiliary = 0;
|
||||||
|
chainHelm_instance->id = 302;
|
||||||
|
(*FillingContainer_addItem)(filling_container, chainHelm_instance);
|
||||||
|
|
||||||
|
ItemInstance *chainShirt_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(chainShirt_instance);
|
||||||
|
chainShirt_instance->count = 255;
|
||||||
|
chainShirt_instance->auxiliary = 0;
|
||||||
|
chainShirt_instance->id = 303;
|
||||||
|
(*FillingContainer_addItem)(filling_container, chainShirt_instance);
|
||||||
|
|
||||||
|
ItemInstance *chainLegs_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(chainLegs_instance);
|
||||||
|
chainLegs_instance->count = 255;
|
||||||
|
chainLegs_instance->auxiliary = 0;
|
||||||
|
chainLegs_instance->id = 304;
|
||||||
|
(*FillingContainer_addItem)(filling_container, chainLegs_instance);
|
||||||
|
|
||||||
|
ItemInstance *chainBoots_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(chainBoots_instance);
|
||||||
|
chainBoots_instance->count = 255;
|
||||||
|
chainBoots_instance->auxiliary = 0;
|
||||||
|
chainBoots_instance->id = 305;
|
||||||
|
(*FillingContainer_addItem)(filling_container, chainBoots_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldHelm_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldHelm_instance);
|
||||||
|
goldHelm_instance->count = 255;
|
||||||
|
goldHelm_instance->auxiliary = 0;
|
||||||
|
goldHelm_instance->id = 314;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldHelm_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldChest_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldChest_instance);
|
||||||
|
goldChest_instance->count = 255;
|
||||||
|
goldChest_instance->auxiliary = 0;
|
||||||
|
goldChest_instance->id = 315;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldChest_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldLegs_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldLegs_instance);
|
||||||
|
goldLegs_instance->count = 255;
|
||||||
|
goldLegs_instance->auxiliary = 0;
|
||||||
|
goldLegs_instance->id = 316;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldLegs_instance);
|
||||||
|
|
||||||
|
ItemInstance *goldBoots_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(goldBoots_instance);
|
||||||
|
goldBoots_instance->count = 255;
|
||||||
|
goldBoots_instance->auxiliary = 0;
|
||||||
|
goldBoots_instance->id = 317;
|
||||||
|
(*FillingContainer_addItem)(filling_container, goldBoots_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironHelm_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironHelm_instance);
|
||||||
|
ironHelm_instance->count = 255;
|
||||||
|
ironHelm_instance->auxiliary = 0;
|
||||||
|
ironHelm_instance->id = 306;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironHelm_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironChest_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironChest_instance);
|
||||||
|
ironChest_instance->count = 255;
|
||||||
|
ironChest_instance->auxiliary = 0;
|
||||||
|
ironChest_instance->id = 307;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironChest_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironLegs_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironLegs_instance);
|
||||||
|
ironLegs_instance->count = 255;
|
||||||
|
ironLegs_instance->auxiliary = 0;
|
||||||
|
ironLegs_instance->id = 308;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironLegs_instance);
|
||||||
|
|
||||||
|
ItemInstance *ironBoots_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(ironBoots_instance);
|
||||||
|
ironBoots_instance->count = 255;
|
||||||
|
ironBoots_instance->auxiliary = 0;
|
||||||
|
ironBoots_instance->id = 309;
|
||||||
|
(*FillingContainer_addItem)(filling_container, ironBoots_instance);
|
||||||
|
|
||||||
|
ItemInstance *flint2_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(flint2_instance);
|
||||||
|
flint2_instance->count = 255;
|
||||||
|
flint2_instance->auxiliary = 0;
|
||||||
|
flint2_instance->id = 318;
|
||||||
|
(*FillingContainer_addItem)(filling_container, flint2_instance);
|
||||||
|
|
||||||
|
ItemInstance *porkRaw_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(porkRaw_instance);
|
||||||
|
porkRaw_instance->count = 255;
|
||||||
|
porkRaw_instance->auxiliary = 0;
|
||||||
|
porkRaw_instance->id = 319;
|
||||||
|
(*FillingContainer_addItem)(filling_container, porkRaw_instance);
|
||||||
|
|
||||||
|
ItemInstance *leather_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(leather_instance);
|
||||||
|
leather_instance->count = 255;
|
||||||
|
leather_instance->auxiliary = 0;
|
||||||
|
leather_instance->id = 334;
|
||||||
|
(*FillingContainer_addItem)(filling_container, leather_instance);
|
||||||
|
|
||||||
|
ItemInstance *clayBrick_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(clayBrick_instance);
|
||||||
|
clayBrick_instance->count = 255;
|
||||||
|
clayBrick_instance->auxiliary = 0;
|
||||||
|
clayBrick_instance->id = 336;
|
||||||
|
(*FillingContainer_addItem)(filling_container, clayBrick_instance);
|
||||||
|
|
||||||
|
ItemInstance *clay_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(clay_instance);
|
||||||
|
clay_instance->count = 255;
|
||||||
|
clay_instance->auxiliary = 0;
|
||||||
|
clay_instance->id = 337;
|
||||||
|
(*FillingContainer_addItem)(filling_container, clay_instance);
|
||||||
|
|
||||||
|
ItemInstance *notepad_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(notepad_instance);
|
||||||
|
notepad_instance->count = 255;
|
||||||
|
notepad_instance->auxiliary = 0;
|
||||||
|
notepad_instance->id = 339;
|
||||||
|
(*FillingContainer_addItem)(filling_container, notepad_instance);
|
||||||
|
|
||||||
|
ItemInstance *book_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(book_instance);
|
||||||
|
book_instance->count = 255;
|
||||||
|
book_instance->auxiliary = 0;
|
||||||
|
book_instance->id = 340;
|
||||||
|
(*FillingContainer_addItem)(filling_container, book_instance);
|
||||||
|
|
||||||
|
ItemInstance *slimeball_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(slimeball_instance);
|
||||||
|
slimeball_instance->count = 255;
|
||||||
|
slimeball_instance->auxiliary = 0;
|
||||||
|
slimeball_instance->id = 341;
|
||||||
|
(*FillingContainer_addItem)(filling_container, slimeball_instance);
|
||||||
|
|
||||||
|
ItemInstance *compass_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(compass_instance);
|
||||||
|
compass_instance->count = 255;
|
||||||
|
compass_instance->auxiliary = 0;
|
||||||
|
compass_instance->id = 345;
|
||||||
|
(*FillingContainer_addItem)(filling_container, compass_instance);
|
||||||
|
|
||||||
|
ItemInstance *clock_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(clock_instance);
|
||||||
|
clock_instance->count = 255;
|
||||||
|
clock_instance->auxiliary = 0;
|
||||||
|
clock_instance->id = 347;
|
||||||
|
(*FillingContainer_addItem)(filling_container, clock_instance);
|
||||||
|
|
||||||
|
ItemInstance *glowDust_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(glowDust_instance);
|
||||||
|
glowDust_instance->count = 255;
|
||||||
|
glowDust_instance->auxiliary = 0;
|
||||||
|
glowDust_instance->id = 348;
|
||||||
|
(*FillingContainer_addItem)(filling_container, glowDust_instance);
|
||||||
|
|
||||||
|
ItemInstance *bone_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(bone_instance);
|
||||||
|
bone_instance->count = 255;
|
||||||
|
bone_instance->auxiliary = 0;
|
||||||
|
bone_instance->id = 352;
|
||||||
|
(*FillingContainer_addItem)(filling_container, bone_instance);
|
||||||
|
|
||||||
|
ItemInstance *sugar_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(sugar_instance);
|
||||||
|
sugar_instance->count = 255;
|
||||||
|
sugar_instance->auxiliary = 0;
|
||||||
|
sugar_instance->id = 353;
|
||||||
|
(*FillingContainer_addItem)(filling_container, sugar_instance);
|
||||||
|
|
||||||
|
ItemInstance *melon_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(melon_instance);
|
||||||
|
melon_instance->count = 255;
|
||||||
|
melon_instance->auxiliary = 0;
|
||||||
|
melon_instance->id = 360;
|
||||||
|
(*FillingContainer_addItem)(filling_container, melon_instance);
|
||||||
|
|
||||||
|
ItemInstance *beefRaw_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(beefRaw_instance);
|
||||||
|
beefRaw_instance->count = 255;
|
||||||
|
beefRaw_instance->auxiliary = 0;
|
||||||
|
beefRaw_instance->id = 363;
|
||||||
|
(*FillingContainer_addItem)(filling_container, beefRaw_instance);
|
||||||
|
|
||||||
|
ItemInstance *chickenRaw_instance = new ItemInstance;
|
||||||
|
ALLOC_CHECK(chickenRaw_instance);
|
||||||
|
chickenRaw_instance->count = 255;
|
||||||
|
chickenRaw_instance->auxiliary = 0;
|
||||||
|
chickenRaw_instance->id = 365;
|
||||||
|
(*FillingContainer_addItem)(filling_container, chickenRaw_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
__attribute__((constructor)) static void init_expanded_creative() {
|
||||||
|
INFO("Loading Expanded Creative Mod");
|
||||||
|
misc_run_on_creative_inventory_setup(Inventory_setupDefault_FillingContainer_addItem_call_injection);
|
||||||
|
}
|
15
example-mods/recipes/.gitignore
vendored
Normal file
15
example-mods/recipes/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/out
|
||||||
|
/debian/tmp
|
||||||
|
/.vscode
|
||||||
|
/build*
|
||||||
|
/CMakeLists.txt.user
|
||||||
|
*.autosave
|
||||||
|
/AppImageBuilder.yml
|
||||||
|
/appimage-builder-cache
|
||||||
|
/appimage-build
|
||||||
|
/AppDir
|
||||||
|
/*.zsync
|
||||||
|
/*.AppImage
|
||||||
|
/core*
|
||||||
|
/qemu_*
|
||||||
|
/cmake/.prebuilt-armhf-toolchain
|
15
example-mods/recipes/CMakeLists.txt
Normal file
15
example-mods/recipes/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
# Build For ARM
|
||||||
|
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
|
||||||
|
|
||||||
|
# Start Project
|
||||||
|
project(recipes)
|
||||||
|
|
||||||
|
# Include SDK
|
||||||
|
include("$ENV{HOME}/.minecraft-pi/sdk/lib/minecraft-pi-reborn-client/sdk/sdk.cmake")
|
||||||
|
|
||||||
|
# Build
|
||||||
|
add_library(recipes SHARED recipes.cpp)
|
||||||
|
target_link_libraries(recipes mods-headers reborn-util symbols misc)
|
52
example-mods/recipes/recipes.cpp
Normal file
52
example-mods/recipes/recipes.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Headers
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
#include <symbols/minecraft.h>
|
||||||
|
#include <mods/misc/misc.h>
|
||||||
|
|
||||||
|
// Custom Crafting Recipes
|
||||||
|
static void Recipes_injection(unsigned char *recipes) {
|
||||||
|
// Add
|
||||||
|
Recipes_Type type1 = {
|
||||||
|
.item = 0,
|
||||||
|
.tile = 0,
|
||||||
|
.instance = {
|
||||||
|
.count = 1,
|
||||||
|
.id = 12,
|
||||||
|
.auxiliary = 0
|
||||||
|
},
|
||||||
|
.letter = 'a'
|
||||||
|
};
|
||||||
|
Recipes_Type type2 = {
|
||||||
|
.item = 0,
|
||||||
|
.tile = 0,
|
||||||
|
.instance = {
|
||||||
|
.count = 1,
|
||||||
|
.id = 13,
|
||||||
|
.auxiliary = 0
|
||||||
|
},
|
||||||
|
.letter = 'b'
|
||||||
|
};
|
||||||
|
ItemInstance result = {
|
||||||
|
.count = 1,
|
||||||
|
.id = 344,
|
||||||
|
.auxiliary = 0
|
||||||
|
};
|
||||||
|
(*Recipes_addShapelessRecipe)(recipes, result, {type1, type2});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom Furnace Recipes
|
||||||
|
static void FurnaceRecipes_injection(unsigned char *recipes) {
|
||||||
|
// Add
|
||||||
|
(*FurnaceRecipes_addFurnaceRecipe)(recipes, 49, {.count = 1, .id = 246, .auxiliary = 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init
|
||||||
|
__attribute__((constructor)) static void init_recipes() {
|
||||||
|
// Log
|
||||||
|
INFO("Loading Custom Recipes");
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
misc_run_on_recipes_setup(Recipes_injection);
|
||||||
|
misc_run_on_furnace_recipes_setup(FurnaceRecipes_injection);
|
||||||
|
}
|
23
images/CMakeLists.txt
Normal file
23
images/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
project(images)
|
||||||
|
|
||||||
|
# Title Background
|
||||||
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
|
install(
|
||||||
|
FILES "background.png"
|
||||||
|
DESTINATION "${MCPI_INSTALL_DIR}/data/images/gui"
|
||||||
|
RENAME "titleBG.png"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Icon
|
||||||
|
install(
|
||||||
|
FILES "icon.png"
|
||||||
|
DESTINATION "${MCPI_SHARE_DIR}/icons/hicolor/scalable/apps"
|
||||||
|
RENAME "${MCPI_APP_ID}.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
# AppImage
|
||||||
|
if(MCPI_IS_APPIMAGE_BUILD)
|
||||||
|
install_symlink("${MCPI_SHARE_DIR}/icons/hicolor/scalable/apps/${MCPI_APP_ID}.png" "${MCPI_APP_ID}.png")
|
||||||
|
install_symlink("${MCPI_APP_ID}.png" ".DirIcon")
|
||||||
|
endif()
|
BIN
images/background.png
Normal file
BIN
images/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 283 KiB |
BIN
images/icon.png
Normal file
BIN
images/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 117 KiB |
BIN
images/start.png
BIN
images/start.png
Binary file not shown.
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 155 KiB |
@ -1,32 +1,117 @@
|
|||||||
project(launcher)
|
project(launcher)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
add_subdirectory(dependencies)
|
||||||
|
|
||||||
# Launcher
|
# Launcher
|
||||||
if(BUILD_NATIVE_COMPONENTS)
|
add_executable(launcher src/bootstrap.c src/patchelf.c src/crash-report.c)
|
||||||
add_executable(launcher src/bootstrap.c src/patchelf.c)
|
|
||||||
if(MCPI_SERVER_MODE)
|
if(MCPI_SERVER_MODE)
|
||||||
target_sources(launcher PRIVATE src/server/launcher.c)
|
target_sources(launcher PRIVATE src/server/launcher.c)
|
||||||
else()
|
else()
|
||||||
|
embed_resource(launcher src/client/available-feature-flags)
|
||||||
target_sources(launcher PRIVATE src/client/launcher.cpp)
|
target_sources(launcher PRIVATE src/client/launcher.cpp)
|
||||||
endif()
|
endif()
|
||||||
target_link_libraries(launcher reborn-util)
|
target_link_libraries(launcher reborn-util)
|
||||||
|
# RPath
|
||||||
|
set_target_properties(launcher PROPERTIES INSTALL_RPATH "$ORIGIN/lib/native")
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
|
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
|
||||||
install_symlink("../${MCPI_INSTALL_DIR}/launcher" "bin/${MCPI_VARIANT_NAME}")
|
install_symlink("../${MCPI_INSTALL_DIR}/launcher" "bin/${MCPI_VARIANT_NAME}")
|
||||||
set(ICON_PATH "data/com.thebrokenrail.MCPIReborn.png")
|
|
||||||
set(ICON_TARGET_PATH "share/icons/hicolor/scalable/apps")
|
# Install Desktop Entry
|
||||||
if(NOT MCPI_SERVER_MODE)
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
install(DIRECTORY "data/client/" DESTINATION ".")
|
"[Desktop Entry]\n"
|
||||||
install(
|
"Name=${MCPI_APP_TITLE}\n"
|
||||||
FILES "${ICON_PATH}"
|
"Comment=Fun with Blocks\n"
|
||||||
DESTINATION "${ICON_TARGET_PATH}"
|
"Icon=${MCPI_APP_ID}\n"
|
||||||
RENAME "com.thebrokenrail.MCPIRebornClient.png"
|
"Exec=${MCPI_VARIANT_NAME}\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"Categories=Game;\n"
|
||||||
|
)
|
||||||
|
if(MCPI_HEADLESS_MODE)
|
||||||
|
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
|
"Terminal=true\n"
|
||||||
|
"NoDisplay=true\n"
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
install(DIRECTORY "data/server/" DESTINATION ".")
|
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
install(
|
"Terminal=false\n"
|
||||||
FILES "${ICON_PATH}"
|
"StartupNotify=false\n"
|
||||||
DESTINATION "${ICON_TARGET_PATH}"
|
"StartupWMClass=${MCPI_APP_ID}\n"
|
||||||
RENAME "com.thebrokenrail.MCPIRebornServer.png"
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
install(
|
||||||
|
FILES "${CMAKE_CURRENT_BINARY_DIR}/launcher.desktop"
|
||||||
|
DESTINATION "${MCPI_SHARE_DIR}/applications"
|
||||||
|
RENAME "${MCPI_APP_ID}.desktop"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install AppStream Metadata
|
||||||
|
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||||
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
"<component type=\"desktop\">\n"
|
||||||
|
" <id>${MCPI_APP_ID}</id>\n"
|
||||||
|
" <name>${MCPI_APP_TITLE}</name>\n"
|
||||||
|
" <metadata_license>CC0-1.0</metadata_license>\n"
|
||||||
|
" <summary>Fun with Blocks</summary>\n"
|
||||||
|
" <description>\n"
|
||||||
|
" <p>Minecraft: Pi Edition Modding Project.</p>\n"
|
||||||
|
" <p>NOTE: This is not verified by, affiliated with, or supported by Mojang or Microsoft.</p>\n"
|
||||||
|
" </description>\n"
|
||||||
|
" <url type=\"homepage\">https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn</url>\n"
|
||||||
|
" <launchable type=\"desktop-id\">${MCPI_APP_ID}.desktop</launchable>\n"
|
||||||
|
" <provides>\n"
|
||||||
|
" <id>com.thebrokenrail.MCPIRebornClient.desktop</id>\n"
|
||||||
|
" </provides>\n"
|
||||||
|
" <project_license>LicenseRef-proprietary</project_license>\n"
|
||||||
|
" <developer_name>TheBrokenRail & Mojang AB</developer_name>\n"
|
||||||
|
" <content_rating type=\"oars-1.0\">\n"
|
||||||
|
" <content_attribute id=\"violence-cartoon\">moderate</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"violence-fantasy\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"violence-realistic\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"violence-bloodshed\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"violence-sexual\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"drugs-alcohol\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"drugs-narcotics\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"drugs-tobacco\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"sex-nudity\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"sex-themes\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"language-profanity\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"language-humor\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"language-discrimination\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"social-chat\">intense</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"social-info\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"social-audio\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"social-location\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"social-contacts\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"money-purchasing\">none</content_attribute>\n"
|
||||||
|
" <content_attribute id=\"money-gambling\">none</content_attribute>\n"
|
||||||
|
" </content_rating>\n"
|
||||||
|
" <releases>\n"
|
||||||
|
" <release version=\"${MCPI_VERSION}\" date=\"${MCPI_VERSION_DATE}\"></release>\n"
|
||||||
|
" </releases>\n"
|
||||||
|
)
|
||||||
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
|
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||||
|
" <screenshots>\n"
|
||||||
|
" <screenshot type=\"default\">\n"
|
||||||
|
" <image>https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png</image>\n"
|
||||||
|
" </screenshot>\n"
|
||||||
|
" </screenshots>\n"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||||
|
"</component>\n"
|
||||||
|
)
|
||||||
|
install(
|
||||||
|
FILES "${CMAKE_CURRENT_BINARY_DIR}/appstream.xml"
|
||||||
|
DESTINATION "${MCPI_SHARE_DIR}/metainfo"
|
||||||
|
RENAME "${MCPI_APP_ID}.appdata.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
# AppImage
|
||||||
|
if(MCPI_IS_APPIMAGE_BUILD)
|
||||||
|
install_symlink("bin/${MCPI_VARIANT_NAME}" "AppRun")
|
||||||
|
install_symlink("${MCPI_SHARE_DIR}/applications/${MCPI_APP_ID}.desktop" "${MCPI_APP_ID}.desktop")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=Minecraft: Pi Edition: Reborn (Client)
|
|
||||||
Comment=Fun with Blocks
|
|
||||||
Icon=com.thebrokenrail.MCPIRebornClient
|
|
||||||
StartupNotify=false
|
|
||||||
StartupWMClass=Minecraft: Pi Edition: Reborn
|
|
||||||
Exec=minecraft-pi-reborn-client
|
|
||||||
Terminal=false
|
|
||||||
Type=Application
|
|
||||||
Categories=Game;
|
|
@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component type="desktop">
|
|
||||||
<id>com.thebrokenrail.MCPIRebornClient</id>
|
|
||||||
<name>Minecraft: Pi Edition: Reborn (Client)</name>
|
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
|
||||||
<summary>Fun with Blocks</summary>
|
|
||||||
<description><p>Minecraft: Pi Edition Modding Project.</p><p>NOTE: This is not verified by, affiliated with, or supported by Mojang or Microsoft.</p></description>
|
|
||||||
<url type="homepage">https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn</url>
|
|
||||||
<screenshots>
|
|
||||||
<screenshot type="default">
|
|
||||||
<image>https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/raw/branch/master/images/start.png</image>
|
|
||||||
</screenshot>
|
|
||||||
</screenshots>
|
|
||||||
<launchable type="desktop-id">com.thebrokenrail.MCPIRebornClient.desktop</launchable>
|
|
||||||
<provides>
|
|
||||||
<id>com.thebrokenrail.MCPIRebornClient.desktop</id>
|
|
||||||
</provides>
|
|
||||||
<project_license>LicenseRef-proprietary</project_license>
|
|
||||||
<developer_name>TheBrokenRail & Mojang AB</developer_name>
|
|
||||||
<content_rating type="oars-1.0">
|
|
||||||
<content_attribute id="violence-cartoon">moderate</content_attribute>
|
|
||||||
<content_attribute id="violence-fantasy">none</content_attribute>
|
|
||||||
<content_attribute id="violence-realistic">none</content_attribute>
|
|
||||||
<content_attribute id="violence-bloodshed">none</content_attribute>
|
|
||||||
<content_attribute id="violence-sexual">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-alcohol">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-narcotics">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-tobacco">none</content_attribute>
|
|
||||||
<content_attribute id="sex-nudity">none</content_attribute>
|
|
||||||
<content_attribute id="sex-themes">none</content_attribute>
|
|
||||||
<content_attribute id="language-profanity">none</content_attribute>
|
|
||||||
<content_attribute id="language-humor">none</content_attribute>
|
|
||||||
<content_attribute id="language-discrimination">none</content_attribute>
|
|
||||||
<content_attribute id="social-chat">intense</content_attribute>
|
|
||||||
<content_attribute id="social-info">none</content_attribute>
|
|
||||||
<content_attribute id="social-audio">none</content_attribute>
|
|
||||||
<content_attribute id="social-location">none</content_attribute>
|
|
||||||
<content_attribute id="social-contacts">none</content_attribute>
|
|
||||||
<content_attribute id="money-purchasing">none</content_attribute>
|
|
||||||
<content_attribute id="money-gambling">none</content_attribute>
|
|
||||||
</content_rating>
|
|
||||||
</component>
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB |
@ -1,9 +0,0 @@
|
|||||||
[Desktop Entry]
|
|
||||||
Name=Minecraft: Pi Edition: Reborn (Server)
|
|
||||||
Comment=Fun with Blocks
|
|
||||||
Icon=com.thebrokenrail.MCPIRebornServer
|
|
||||||
Exec=minecraft-pi-reborn-server
|
|
||||||
Terminal=true
|
|
||||||
Type=Application
|
|
||||||
Categories=Game;
|
|
||||||
NoDisplay=true
|
|
@ -1,38 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component type="desktop">
|
|
||||||
<id>com.thebrokenrail.MCPIRebornServer</id>
|
|
||||||
<name>Minecraft: Pi Edition: Reborn (Server)</name>
|
|
||||||
<metadata_license>CC0-1.0</metadata_license>
|
|
||||||
<summary>Fun with Blocks</summary>
|
|
||||||
<description><p>Minecraft: Pi Edition Modding Project.</p><p>NOTE: This is not verified by, affiliated with, or supported by Mojang or Microsoft.</p></description>
|
|
||||||
<url type="homepage">https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn</url>
|
|
||||||
<launchable type="desktop-id">com.thebrokenrail.MCPIRebornServer.desktop</launchable>
|
|
||||||
<provides>
|
|
||||||
<id>com.thebrokenrail.MCPIRebornServer.desktop</id>
|
|
||||||
</provides>
|
|
||||||
<project_license>LicenseRef-proprietary</project_license>
|
|
||||||
<developer_name>TheBrokenRail & Mojang AB</developer_name>
|
|
||||||
<content_rating type="oars-1.0">
|
|
||||||
<content_attribute id="violence-cartoon">moderate</content_attribute>
|
|
||||||
<content_attribute id="violence-fantasy">none</content_attribute>
|
|
||||||
<content_attribute id="violence-realistic">none</content_attribute>
|
|
||||||
<content_attribute id="violence-bloodshed">none</content_attribute>
|
|
||||||
<content_attribute id="violence-sexual">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-alcohol">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-narcotics">none</content_attribute>
|
|
||||||
<content_attribute id="drugs-tobacco">none</content_attribute>
|
|
||||||
<content_attribute id="sex-nudity">none</content_attribute>
|
|
||||||
<content_attribute id="sex-themes">none</content_attribute>
|
|
||||||
<content_attribute id="language-profanity">none</content_attribute>
|
|
||||||
<content_attribute id="language-humor">none</content_attribute>
|
|
||||||
<content_attribute id="language-discrimination">none</content_attribute>
|
|
||||||
<content_attribute id="social-chat">intense</content_attribute>
|
|
||||||
<content_attribute id="social-info">none</content_attribute>
|
|
||||||
<content_attribute id="social-audio">none</content_attribute>
|
|
||||||
<content_attribute id="social-location">none</content_attribute>
|
|
||||||
<content_attribute id="social-contacts">none</content_attribute>
|
|
||||||
<content_attribute id="money-purchasing">none</content_attribute>
|
|
||||||
<content_attribute id="money-gambling">none</content_attribute>
|
|
||||||
</content_rating>
|
|
||||||
</component>
|
|
||||||
|
|
4
launcher/dependencies/CMakeLists.txt
Normal file
4
launcher/dependencies/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
project(launcher-dependencies)
|
||||||
|
|
||||||
|
# PatchELF
|
||||||
|
add_subdirectory(patchelf)
|
17
launcher/dependencies/patchelf/CMakeLists.txt
Normal file
17
launcher/dependencies/patchelf/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
project(patchelf)
|
||||||
|
|
||||||
|
# Silence Warnings
|
||||||
|
add_compile_options(-w)
|
||||||
|
|
||||||
|
## PatchELF
|
||||||
|
|
||||||
|
# Build
|
||||||
|
add_executable(patchelf src/src/patchelf.cc)
|
||||||
|
target_compile_definitions(patchelf PRIVATE -D_FILE_OFFSET_BITS=64)
|
||||||
|
set_target_properties(patchelf PROPERTIES CXX_STANDARD 17)
|
||||||
|
|
||||||
|
# Install
|
||||||
|
install(TARGETS patchelf DESTINATION "${MCPI_BIN_DIR}")
|
||||||
|
|
||||||
|
# License
|
||||||
|
install(FILES src/COPYING DESTINATION "${MCPI_LEGAL_DIR}/patchelf")
|
1
launcher/dependencies/patchelf/src
Submodule
1
launcher/dependencies/patchelf/src
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 734daa3d0f79cf1a0c81f927d846ace5d6a2c8e1
|
@ -12,58 +12,7 @@
|
|||||||
|
|
||||||
#include "bootstrap.h"
|
#include "bootstrap.h"
|
||||||
#include "patchelf.h"
|
#include "patchelf.h"
|
||||||
|
#include "crash-report.h"
|
||||||
// Set Environmental Variable
|
|
||||||
#define PRESERVE_ENVIRONMENTAL_VARIABLE(name) \
|
|
||||||
{ \
|
|
||||||
char *original_env_value = getenv(name); \
|
|
||||||
if (original_env_value != NULL) { \
|
|
||||||
setenv("ORIGINAL_" name, original_env_value, 1); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
static void trim(char **value) {
|
|
||||||
// Remove Trailing Colon
|
|
||||||
int length = strlen(*value);
|
|
||||||
if ((*value)[length - 1] == ':') {
|
|
||||||
(*value)[length - 1] = '\0';
|
|
||||||
}
|
|
||||||
if ((*value)[0] == ':') {
|
|
||||||
*value = &(*value)[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void set_and_print_env(const char *name, char *value) {
|
|
||||||
// Set Variable With No Trailing Colon
|
|
||||||
static const char *unmodified_name_prefix = "MCPI_";
|
|
||||||
if (!starts_with(name, unmodified_name_prefix)) {
|
|
||||||
trim(&value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print New Value
|
|
||||||
DEBUG("Set %s = %s", name, value);
|
|
||||||
|
|
||||||
// Set The Value
|
|
||||||
setenv(name, value, 1);
|
|
||||||
}
|
|
||||||
#ifndef __ARM_ARCH
|
|
||||||
#define PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU(name) \
|
|
||||||
{ \
|
|
||||||
char *old_value = getenv("QEMU_SET_ENV"); \
|
|
||||||
char *new_value = NULL; \
|
|
||||||
/* Pass Variable */ \
|
|
||||||
safe_asprintf(&new_value, "%s%s%s=%s", old_value == NULL ? "" : old_value, old_value == NULL ? "" : ",", name, getenv(name)); \
|
|
||||||
setenv("QEMU_SET_ENV", new_value, 1); \
|
|
||||||
free(new_value); \
|
|
||||||
/* Reset Variable */ \
|
|
||||||
RESET_ENVIRONMENTAL_VARIABLE(name); \
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Get Environmental Variable
|
|
||||||
static char *get_env_safe(const char *name) {
|
|
||||||
// Get Variable Or Blank String If Not Set
|
|
||||||
char *ret = getenv(name);
|
|
||||||
return ret != NULL ? ret : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get All Mods In Folder
|
// Get All Mods In Folder
|
||||||
static void load(char **ld_preload, char *folder) {
|
static void load(char **ld_preload, char *folder) {
|
||||||
@ -97,14 +46,14 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
// Add Terminator
|
// Add Terminator
|
||||||
name[total_length] = '\0';
|
name[total_length] = '\0';
|
||||||
|
|
||||||
// Check If File Is Executable
|
// Check If File Is Accessible
|
||||||
int result = access(name, R_OK);
|
int result = access(name, R_OK);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
// Add To LD_PRELOAD
|
// Add To LD_PRELOAD
|
||||||
string_append(ld_preload, ":%s", name);
|
string_append(ld_preload, "%s%s", *ld_preload == NULL ? "" : ":", name);
|
||||||
} else if (result == -1 && errno != 0) {
|
} else if (result == -1 && errno != 0) {
|
||||||
// Fail
|
// Fail
|
||||||
INFO("Unable To Acesss: %s: %s", name, strerror(errno));
|
WARN("Unable To Access: %s: %s", name, strerror(errno));
|
||||||
errno = 0;
|
errno = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,19 +88,56 @@ static void load(char **ld_preload, char *folder) {
|
|||||||
#define MCPI_BINARY "minecraft-pi"
|
#define MCPI_BINARY "minecraft-pi"
|
||||||
#define QEMU_BINARY "qemu-arm"
|
#define QEMU_BINARY "qemu-arm"
|
||||||
|
|
||||||
|
// Exit Handler
|
||||||
|
static void exit_handler(__attribute__((unused)) int signal_id) {
|
||||||
|
// Pass Signal To Child
|
||||||
|
murder_children();
|
||||||
|
while (wait(NULL) > 0) {}
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
// Pre-Bootstrap
|
// Pre-Bootstrap
|
||||||
void pre_bootstrap() {
|
void pre_bootstrap(int argc, char *argv[]) {
|
||||||
|
// Disable stdout Buffering
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
// Set Default Native Component Environment
|
||||||
|
#define set_variable_default(name) set_and_print_env("MCPI_NATIVE_" name, getenv(name));
|
||||||
|
for_each_special_environmental_variable(set_variable_default);
|
||||||
|
|
||||||
|
// Print Version
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
|
||||||
|
// Print
|
||||||
|
printf("Reborn v%s\n", MCPI_VERSION);
|
||||||
|
fflush(stdout);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GTK Dark Mode
|
// GTK Dark Mode
|
||||||
#ifndef MCPI_HEADLESS_MODE
|
#ifndef MCPI_SERVER_MODE
|
||||||
set_and_print_env("GTK_THEME", "Adwaita:dark");
|
set_and_print_env("GTK_THEME", "Adwaita:dark");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Debug Zenity
|
||||||
|
#ifndef MCPI_SERVER_MODE
|
||||||
|
{
|
||||||
|
const char *is_debug = getenv("MCPI_DEBUG");
|
||||||
|
if (is_debug != NULL && strlen(is_debug) > 0) {
|
||||||
|
set_and_print_env("ZENITY_DEBUG", "1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// AppImage
|
// AppImage
|
||||||
#ifdef MCPI_IS_APPIMAGE_BUILD
|
#ifdef MCPI_IS_APPIMAGE_BUILD
|
||||||
|
{
|
||||||
char *owd = getenv("OWD");
|
char *owd = getenv("OWD");
|
||||||
if (owd != NULL && chdir(owd) != 0) {
|
if (owd != NULL && chdir(owd) != 0) {
|
||||||
ERR("AppImage: Unable To Fix Current Directory: %s", strerror(errno));
|
ERR("AppImage: Unable To Fix Current Directory: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Get Binary Directory
|
// Get Binary Directory
|
||||||
@ -160,12 +146,12 @@ void pre_bootstrap() {
|
|||||||
// Configure PATH
|
// Configure PATH
|
||||||
{
|
{
|
||||||
// Add Library Directory
|
// Add Library Directory
|
||||||
char *new_path;
|
char *new_path = NULL;
|
||||||
safe_asprintf(&new_path, "%s/bin", binary_directory);
|
safe_asprintf(&new_path, "%s/bin", binary_directory);
|
||||||
// Add Existing PATH
|
// Add Existing PATH
|
||||||
{
|
{
|
||||||
char *value = get_env_safe("PATH");
|
char *value = getenv("PATH");
|
||||||
if (strlen(value) > 0) {
|
if (value != NULL && strlen(value) > 0) {
|
||||||
string_append(&new_path, ":%s", value);
|
string_append(&new_path, ":%s", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,106 +162,227 @@ void pre_bootstrap() {
|
|||||||
|
|
||||||
// Free Binary Directory
|
// Free Binary Directory
|
||||||
free(binary_directory);
|
free(binary_directory);
|
||||||
|
|
||||||
|
// Setup Crash Reports
|
||||||
|
setup_crash_report();
|
||||||
|
|
||||||
|
// Install Signal Handlers
|
||||||
|
struct sigaction act_sigint;
|
||||||
|
memset((void *) &act_sigint, 0, sizeof (struct sigaction));
|
||||||
|
act_sigint.sa_flags = SA_RESTART;
|
||||||
|
act_sigint.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGINT, &act_sigint, NULL);
|
||||||
|
struct sigaction act_sigterm;
|
||||||
|
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
|
||||||
|
act_sigterm.sa_flags = SA_RESTART;
|
||||||
|
act_sigterm.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGTERM, &act_sigterm, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy SDK Into ~/.minecraft-pi
|
||||||
|
static void run_simple_command(const char *const command[], const char *error) {
|
||||||
|
int status = 0;
|
||||||
|
char *output = run_command(command, &status);
|
||||||
|
if (output != NULL) {
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
if (!is_exit_status_success(status)) {
|
||||||
|
ERR("%s", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void copy_sdk(char *binary_directory) {
|
||||||
|
// Output Directory
|
||||||
|
char *output = NULL;
|
||||||
|
safe_asprintf(&output, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/sdk/" MCPI_SDK_DIR, getenv("HOME"));
|
||||||
|
// Source Directory
|
||||||
|
char *source = NULL;
|
||||||
|
safe_asprintf(&source, "%s/sdk/.", binary_directory);
|
||||||
|
|
||||||
|
// Clean
|
||||||
|
{
|
||||||
|
const char *const command[] = {"rm", "-rf", output, NULL};
|
||||||
|
run_simple_command(command, "Unable To Clean SDK Output Directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make Directory
|
||||||
|
{
|
||||||
|
const char *const command[] = {"mkdir", "-p", output, NULL};
|
||||||
|
run_simple_command(command, "Unable To Create SDK Output Directory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
{
|
||||||
|
const char *const command[] = {"cp", "-ar", source, output, NULL};
|
||||||
|
run_simple_command(command, "Unable To Copy SDK");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free
|
||||||
|
free(output);
|
||||||
|
free(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bootstrap
|
// Bootstrap
|
||||||
void bootstrap(int argc, char *argv[]) {
|
void bootstrap(int argc, char *argv[]) {
|
||||||
INFO("%s", "Configuring Game...");
|
INFO("Configuring Game...");
|
||||||
|
|
||||||
// Get Binary Directory
|
// Get Binary Directory
|
||||||
char *binary_directory = get_binary_directory();
|
char *binary_directory = get_binary_directory();
|
||||||
|
|
||||||
// Handle AppImage
|
// Copy SDK
|
||||||
char *usr_prefix = NULL;
|
copy_sdk(binary_directory);
|
||||||
#ifdef MCPI_IS_APPIMAGE_BUILD
|
|
||||||
usr_prefix = getenv("APPDIR");
|
// Set MCPI_REBORN_ASSETS_PATH
|
||||||
#endif
|
{
|
||||||
if (usr_prefix == NULL) {
|
char *assets_path = realpath("/proc/self/exe", NULL);
|
||||||
usr_prefix = "";
|
ALLOC_CHECK(assets_path);
|
||||||
|
chop_last_component(&assets_path);
|
||||||
|
string_append(&assets_path, "/data");
|
||||||
|
set_and_print_env("MCPI_REBORN_ASSETS_PATH", assets_path);
|
||||||
|
free(assets_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve Binary Path & Set MCPI_DIRECTORY
|
// Resolve Binary Path & Set MCPI_DIRECTORY
|
||||||
|
char *resolved_path = NULL;
|
||||||
{
|
{
|
||||||
// Log
|
// Log
|
||||||
DEBUG("%s", "Resolving File Paths...");
|
DEBUG("Resolving File Paths...");
|
||||||
|
|
||||||
// Resolve Full Binary Path
|
// Resolve Full Binary Path
|
||||||
char *full_path = NULL;
|
char *full_path = NULL;
|
||||||
safe_asprintf(&full_path, "%s/" MCPI_BINARY, binary_directory);
|
safe_asprintf(&full_path, "%s/" MCPI_BINARY, binary_directory);
|
||||||
char *resolved_path = realpath(full_path, NULL);
|
resolved_path = realpath(full_path, NULL);
|
||||||
ALLOC_CHECK(resolved_path);
|
ALLOC_CHECK(resolved_path);
|
||||||
free(full_path);
|
free(full_path);
|
||||||
|
|
||||||
// Set MCPI_EXECUTABLE_PATH
|
|
||||||
set_and_print_env("MCPI_EXECUTABLE_PATH", resolved_path);
|
|
||||||
|
|
||||||
// Set MCPI_DIRECTORY
|
|
||||||
chop_last_component(&resolved_path);
|
|
||||||
set_and_print_env("MCPI_DIRECTORY", resolved_path);
|
|
||||||
free(resolved_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix MCPI Dependencies
|
// Fix MCPI Dependencies
|
||||||
|
char new_mcpi_exe_path[] = MCPI_PATCHED_DIR "/XXXXXX";
|
||||||
{
|
{
|
||||||
// Log
|
// Log
|
||||||
DEBUG("%s", "Patching ELF Dependencies...");
|
DEBUG("Patching ELF Dependencies...");
|
||||||
|
|
||||||
// Find Linker
|
// Find Linker
|
||||||
char *linker = NULL;
|
char *linker = NULL;
|
||||||
#ifndef __arm__
|
// Select Linker
|
||||||
safe_asprintf(&linker, "%s/usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3", usr_prefix);
|
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
// Use ARM Sysroot Linker
|
||||||
|
safe_asprintf(&linker, "%s/sysroot/lib/ld-linux-armhf.so.3", binary_directory);
|
||||||
#else
|
#else
|
||||||
safe_asprintf(&linker, "/lib/ld-linux-armhf.so.3");
|
// Use Current Linker
|
||||||
|
char *exe = realpath("/proc/self/exe", NULL);
|
||||||
|
ALLOC_CHECK(exe);
|
||||||
|
linker = patch_get_interpreter(exe);
|
||||||
|
free(exe);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Patch
|
// Patch
|
||||||
patch_mcpi_elf_dependencies(linker);
|
patch_mcpi_elf_dependencies(resolved_path, new_mcpi_exe_path, linker);
|
||||||
|
|
||||||
// Free Linker Path
|
// Free Linker Path
|
||||||
|
if (linker != NULL) {
|
||||||
free(linker);
|
free(linker);
|
||||||
|
}
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
if (!starts_with(getenv("MCPI_EXECUTABLE_PATH"), "/tmp")) {
|
if (!starts_with(new_mcpi_exe_path, MCPI_PATCHED_DIR)) {
|
||||||
IMPOSSIBLE();
|
IMPOSSIBLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure LD_LIBRARY_PATH
|
// Set MCPI_VANILLA_ASSETS_PATH
|
||||||
|
{
|
||||||
|
char *assets_path = strdup(resolved_path);
|
||||||
|
ALLOC_CHECK(assets_path);
|
||||||
|
chop_last_component(&assets_path);
|
||||||
|
string_append(&assets_path, "/data");
|
||||||
|
set_and_print_env("MCPI_VANILLA_ASSETS_PATH", assets_path);
|
||||||
|
free(assets_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free Resolved Path
|
||||||
|
free(resolved_path);
|
||||||
|
|
||||||
|
// Configure Library Search Path
|
||||||
{
|
{
|
||||||
// Log
|
// Log
|
||||||
DEBUG("%s", "Setting Linker Search Paths...");
|
DEBUG("Setting Linker Search Paths...");
|
||||||
|
|
||||||
// Preserve
|
// Prepare
|
||||||
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH");
|
char *transitive_ld_path = NULL;
|
||||||
char *new_ld_path = NULL;
|
char *mcpi_ld_path = NULL;
|
||||||
|
|
||||||
// Add Library Directory
|
// Library Search Path For Native Components
|
||||||
safe_asprintf(&new_ld_path, "%s/lib", binary_directory);
|
|
||||||
|
|
||||||
// Load ARM Libraries (Ensure Priority)
|
|
||||||
string_append(&new_ld_path, ":%s/usr/lib/arm-linux-gnueabihf:%s/usr/arm-linux-gnueabihf/lib", usr_prefix, usr_prefix);
|
|
||||||
|
|
||||||
// Add LD_LIBRARY_PATH (ARM32 Only)
|
|
||||||
{
|
{
|
||||||
char *value = get_env_safe("LD_LIBRARY_PATH");
|
// Add Native Library Directory
|
||||||
if (strlen(value) > 0) {
|
safe_asprintf(&transitive_ld_path, "%s/lib/native", binary_directory);
|
||||||
string_append(&new_ld_path, ":%s", value);
|
|
||||||
|
// Add Host LD_LIBRARY_PATH
|
||||||
|
{
|
||||||
|
char *value = getenv("LD_LIBRARY_PATH");
|
||||||
|
if (value != NULL && strlen(value) > 0) {
|
||||||
|
string_append(&transitive_ld_path, ":%s", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set And Free
|
// Set
|
||||||
set_and_print_env("LD_LIBRARY_PATH", new_ld_path);
|
set_and_print_env("MCPI_NATIVE_LD_LIBRARY_PATH", transitive_ld_path);
|
||||||
free(new_ld_path);
|
free(transitive_ld_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure LD_PRELOAD
|
// Library Search Path For ARM Components
|
||||||
|
{
|
||||||
|
// Add ARM Library Directory
|
||||||
|
safe_asprintf(&mcpi_ld_path, "%s/lib/arm", binary_directory);
|
||||||
|
|
||||||
|
// Add ARM Sysroot Libraries (Ensure Priority) (Ignore On Actual ARM System)
|
||||||
|
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
string_append(&mcpi_ld_path, ":%s/sysroot/lib:%s/sysroot/lib/arm-linux-gnueabihf:%s/sysroot/usr/lib:%s/sysroot/usr/lib/arm-linux-gnueabihf", binary_directory, binary_directory, binary_directory, binary_directory);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Add Host LD_LIBRARY_PATH
|
||||||
|
{
|
||||||
|
char *value = getenv("LD_LIBRARY_PATH");
|
||||||
|
if (value != NULL && strlen(value) > 0) {
|
||||||
|
string_append(&mcpi_ld_path, ":%s", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set
|
||||||
|
set_and_print_env("MCPI_ARM_LD_LIBRARY_PATH", mcpi_ld_path);
|
||||||
|
free(mcpi_ld_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup iconv
|
||||||
|
{
|
||||||
|
// Native Components
|
||||||
|
char *host_gconv_path = getenv("GCONV_PATH");
|
||||||
|
set_and_print_env("MCPI_NATIVE_GCONV_PATH", host_gconv_path);
|
||||||
|
|
||||||
|
// ARM Components
|
||||||
|
#ifdef MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
char *gconv_path = NULL;
|
||||||
|
safe_asprintf(&gconv_path, "%s/sysroot/usr/lib/gconv", binary_directory);
|
||||||
|
set_and_print_env("MCPI_ARM_GCONV_PATH", gconv_path);
|
||||||
|
free(gconv_path);
|
||||||
|
#else
|
||||||
|
set_and_print_env("MCPI_ARM_GCONV_PATH", host_gconv_path);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure Preloaded Objects
|
||||||
{
|
{
|
||||||
// Log
|
// Log
|
||||||
DEBUG("%s", "Locating Mods...");
|
DEBUG("Locating Mods...");
|
||||||
|
|
||||||
// Preserve
|
// Native Components
|
||||||
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_PRELOAD");
|
char *host_ld_preload = getenv("LD_PRELOAD");
|
||||||
char *new_ld_preload = NULL;
|
set_and_print_env("MCPI_NATIVE_LD_PRELOAD", host_ld_preload);
|
||||||
|
|
||||||
|
// ARM Components
|
||||||
|
{
|
||||||
|
// Prepare
|
||||||
|
char *preload = NULL;
|
||||||
|
|
||||||
// ~/.minecraft-pi/mods
|
// ~/.minecraft-pi/mods
|
||||||
{
|
{
|
||||||
@ -283,7 +390,7 @@ void bootstrap(int argc, char *argv[]) {
|
|||||||
char *mods_folder = NULL;
|
char *mods_folder = NULL;
|
||||||
safe_asprintf(&mods_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/mods/", getenv("HOME"));
|
safe_asprintf(&mods_folder, "%s" HOME_SUBDIRECTORY_FOR_GAME_DATA "/mods/", getenv("HOME"));
|
||||||
// Load Mods From ./mods
|
// Load Mods From ./mods
|
||||||
load(&new_ld_preload, mods_folder);
|
load(&preload, mods_folder);
|
||||||
// Free Mods Folder
|
// Free Mods Folder
|
||||||
free(mods_folder);
|
free(mods_folder);
|
||||||
}
|
}
|
||||||
@ -294,39 +401,30 @@ void bootstrap(int argc, char *argv[]) {
|
|||||||
char *mods_folder = NULL;
|
char *mods_folder = NULL;
|
||||||
safe_asprintf(&mods_folder, "%s/mods/", binary_directory);
|
safe_asprintf(&mods_folder, "%s/mods/", binary_directory);
|
||||||
// Load Mods From ./mods
|
// Load Mods From ./mods
|
||||||
load(&new_ld_preload, mods_folder);
|
load(&preload, mods_folder);
|
||||||
// Free Mods Folder
|
// Free Mods Folder
|
||||||
free(mods_folder);
|
free(mods_folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add MCPI_LD_PRELOAD
|
// Add LD_PRELOAD
|
||||||
{
|
{
|
||||||
char *value = get_env_safe("MCPI_LD_PRELOAD");
|
char *value = getenv("LD_PRELOAD");
|
||||||
if (strlen(value) > 0) {
|
if (value != NULL && strlen(value) > 0) {
|
||||||
string_append(&new_ld_preload, ":%s", value);
|
string_append(&preload, ":%s", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add LD_PRELOAD (ARM32 Only)
|
// Set
|
||||||
#ifdef __arm__
|
set_and_print_env("MCPI_ARM_LD_PRELOAD", preload);
|
||||||
{
|
free(preload);
|
||||||
char *value = get_env_safe("MCPI_LD_PRELOAD");
|
|
||||||
if (strlen(value) > 0) {
|
|
||||||
string_append(&new_ld_preload, ":%s", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set LD_PRELOAD
|
|
||||||
set_and_print_env("LD_PRELOAD", new_ld_preload);
|
|
||||||
free(new_ld_preload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free Binary Directory
|
// Free Binary Directory
|
||||||
free(binary_directory);
|
free(binary_directory);
|
||||||
|
|
||||||
// Start Game
|
// Start Game
|
||||||
INFO("%s", "Starting Game...");
|
INFO("Starting Game...");
|
||||||
|
|
||||||
// Arguments
|
// Arguments
|
||||||
int argv_start = 1; // argv = &new_args[argv_start]
|
int argv_start = 1; // argv = &new_args[argv_start]
|
||||||
@ -340,16 +438,26 @@ void bootstrap(int argc, char *argv[]) {
|
|||||||
new_args[argv_start + argc] = NULL;
|
new_args[argv_start + argc] = NULL;
|
||||||
|
|
||||||
// Set Executable Argument
|
// Set Executable Argument
|
||||||
new_args[argv_start] = getenv("MCPI_EXECUTABLE_PATH");
|
new_args[argv_start] = new_mcpi_exe_path;
|
||||||
|
|
||||||
// Non-ARM Systems Need QEMU
|
// Non-ARM Systems Need QEMU
|
||||||
#ifndef __ARM_ARCH
|
#ifndef __ARM_ARCH
|
||||||
argv_start--;
|
argv_start--;
|
||||||
new_args[argv_start] = QEMU_BINARY;
|
new_args[argv_start] = QEMU_BINARY;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Prevent QEMU From Being Modded
|
// Setup Environment
|
||||||
PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_LIBRARY_PATH");
|
setup_exec_environment(1);
|
||||||
PASS_ENVIRONMENTAL_VARIABLE_TO_QEMU("LD_PRELOAD");
|
|
||||||
|
// Pass LD_* Variables Through QEMU
|
||||||
|
#ifndef __ARM_ARCH
|
||||||
|
char *qemu_set_env = NULL;
|
||||||
|
#define pass_variable_through_qemu(name) string_append(&qemu_set_env, "%s%s=%s", qemu_set_env == NULL ? "" : ",", name, getenv(name));
|
||||||
|
for_each_special_environmental_variable(pass_variable_through_qemu);
|
||||||
|
set_and_print_env("QEMU_SET_ENV", qemu_set_env);
|
||||||
|
free(qemu_set_env);
|
||||||
|
// Treat QEMU Itself As A Native Component
|
||||||
|
setup_exec_environment(0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_and_print_env(const char *name, char *value);
|
void pre_bootstrap(int argc, char *argv[]);
|
||||||
|
|
||||||
void pre_bootstrap();
|
|
||||||
void bootstrap(int argc, char *argv[]);
|
void bootstrap(int argc, char *argv[]);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
TRUE Touch GUI
|
FALSE Full Touch GUI
|
||||||
TRUE Fix Bow & Arrow
|
TRUE Fix Bow & Arrow
|
||||||
TRUE Fix Attacking
|
TRUE Fix Attacking
|
||||||
FALSE Force Mob Spawning
|
FALSE Force Mob Spawning
|
||||||
TRUE Fancy Graphics
|
|
||||||
TRUE Disable Autojump By Default
|
TRUE Disable Autojump By Default
|
||||||
TRUE Display Nametags By Default
|
TRUE Display Nametags By Default
|
||||||
TRUE Fix Sign Placement
|
TRUE Fix Sign Placement
|
||||||
TRUE Show Block Outlines
|
TRUE Show Block Outlines
|
||||||
FALSE Expand Creative Inventory
|
FALSE Expand Creative Inventory
|
||||||
FALSE Remove Creative Mode Restrictions
|
FALSE Remove Creative Mode Restrictions
|
||||||
FALSE Peaceful Mode
|
|
||||||
TRUE Animated Water
|
TRUE Animated Water
|
||||||
TRUE Remove Invalid Item Background
|
TRUE Remove Invalid Item Background
|
||||||
TRUE Disable "gui_blocks" Atlas
|
TRUE Disable "gui_blocks" Atlas
|
||||||
TRUE Smooth Lighting
|
|
||||||
FALSE 3D Anaglyph
|
FALSE 3D Anaglyph
|
||||||
TRUE Fix Camera Rendering
|
TRUE Fix Camera Rendering
|
||||||
TRUE Implement Chat
|
TRUE Implement Chat
|
||||||
@ -31,6 +28,17 @@ TRUE Implement Sound Engine
|
|||||||
TRUE Close Current Screen On Death
|
TRUE Close Current Screen On Death
|
||||||
FALSE Disable Raw Mouse Motion (Not Recommended)
|
FALSE Disable Raw Mouse Motion (Not Recommended)
|
||||||
TRUE Fix Furnace Not Checking Item Auxiliary
|
TRUE Fix Furnace Not Checking Item Auxiliary
|
||||||
FALSE Disable Hosting LAN Worlds
|
|
||||||
TRUE Improved Cursor Rendering
|
TRUE Improved Cursor Rendering
|
||||||
FALSE Disable V-Sync
|
TRUE Disable V-Sync
|
||||||
|
TRUE Fix Options Screen
|
||||||
|
TRUE Force Touch GUI Inventory
|
||||||
|
TRUE Fix Pause Menu
|
||||||
|
TRUE Improved Title Background
|
||||||
|
TRUE Force Touch GUI Button Behavior
|
||||||
|
TRUE Improved Button Hover Behavior
|
||||||
|
TRUE Implement Create World Dialog
|
||||||
|
FALSE Remove Forced GUI Lag (Can Break Joining Servers)
|
||||||
|
TRUE Add Buckets
|
||||||
|
TRUE Classic HUD
|
||||||
|
TRUE Translucent Toolbar
|
||||||
|
FALSE Force EGL
|
@ -1,4 +1,4 @@
|
|||||||
#include <fstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -31,19 +31,22 @@ static std::string strip_feature_flag_default(std::string flag, bool *default_re
|
|||||||
return flag.substr(false_str.length(), std::string::npos);
|
return flag.substr(false_str.length(), std::string::npos);
|
||||||
} else {
|
} else {
|
||||||
// Invalid
|
// Invalid
|
||||||
ERR("%s", "Invalid Feature Flag Default");
|
ERR("Invalid Feature Flag Default");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load Available Feature Flags
|
// Load Available Feature Flags
|
||||||
|
extern unsigned char available_feature_flags[];
|
||||||
|
extern size_t available_feature_flags_len;
|
||||||
static void load_available_feature_flags(std::function<void(std::string)> callback) {
|
static void load_available_feature_flags(std::function<void(std::string)> callback) {
|
||||||
// Get Path
|
// Get Path
|
||||||
char *binary_directory = get_binary_directory();
|
char *binary_directory = get_binary_directory();
|
||||||
std::string path = std::string(binary_directory) + "/available-feature-flags";
|
std::string path = std::string(binary_directory) + "/available-feature-flags";
|
||||||
free(binary_directory);
|
free(binary_directory);
|
||||||
// Load File
|
// Load File
|
||||||
std::ifstream stream(path);
|
std::string data(available_feature_flags, available_feature_flags + available_feature_flags_len);
|
||||||
if (stream && stream.good()) {
|
std::stringstream stream(data);
|
||||||
|
// Store Lines
|
||||||
std::vector<std::string> lines;
|
std::vector<std::string> lines;
|
||||||
// Read File
|
// Read File
|
||||||
{
|
{
|
||||||
@ -55,7 +58,7 @@ static void load_available_feature_flags(std::function<void(std::string)> callba
|
|||||||
lines.push_back(line);
|
lines.push_back(line);
|
||||||
} else {
|
} else {
|
||||||
// Invalid Line
|
// Invalid Line
|
||||||
ERR("%s", "Feature Flag Contains Invalid '|'");
|
ERR("Feature Flag Contains Invalid '|'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,14 +72,9 @@ static void load_available_feature_flags(std::function<void(std::string)> callba
|
|||||||
return stripped_a < stripped_b;
|
return stripped_a < stripped_b;
|
||||||
});
|
});
|
||||||
// Run Callbacks
|
// Run Callbacks
|
||||||
for (std::string line : lines) {
|
for (std::string &line : lines) {
|
||||||
callback(line);
|
callback(line);
|
||||||
}
|
}
|
||||||
// Close File
|
|
||||||
stream.close();
|
|
||||||
} else {
|
|
||||||
ERR("%s", "Unable To Load Available Feature Flags");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Command And Set Environmental Variable
|
// Run Command And Set Environmental Variable
|
||||||
@ -98,19 +96,23 @@ static void run_command_and_set_env(const char *env_name, const char *command[])
|
|||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
// Check Return Code
|
// Check Return Code
|
||||||
if (return_code != 0) {
|
if (!is_exit_status_success(return_code)) {
|
||||||
ERR("%s", "Launch Interrupted");
|
INFO("Launch Interrupted");
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Zenity To Set Environmental Variable
|
// Use Zenity To Set Environmental Variable
|
||||||
|
#define DIALOG_TITLE "Launcher"
|
||||||
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
|
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
|
||||||
// Create Full Command
|
// Create Full Command
|
||||||
std::vector<std::string> full_command;
|
std::vector<std::string> full_command;
|
||||||
full_command.push_back("zenity");
|
full_command.push_back("zenity");
|
||||||
full_command.push_back("--class");
|
full_command.push_back("--title");
|
||||||
full_command.push_back(GUI_TITLE);
|
full_command.push_back(DIALOG_TITLE);
|
||||||
|
full_command.push_back("--name");
|
||||||
|
full_command.push_back(MCPI_APP_ID);
|
||||||
full_command.insert(full_command.end(), command.begin(), command.end());
|
full_command.insert(full_command.end(), command.begin(), command.end());
|
||||||
// Convert To C Array
|
// Convert To C Array
|
||||||
const char *full_command_array[full_command.size() + 1];
|
const char *full_command_array[full_command.size() + 1];
|
||||||
@ -122,10 +124,30 @@ static void run_zenity_and_set_env(const char *env_name, std::vector<std::string
|
|||||||
run_command_and_set_env(env_name, full_command_array);
|
run_command_and_set_env(env_name, full_command_array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Variable If Not Already Set
|
||||||
|
static void set_env_if_unset(const char *env_name, std::function<std::string()> callback) {
|
||||||
|
if (getenv(env_name) == NULL) {
|
||||||
|
char *value = strdup(callback().c_str());
|
||||||
|
ALLOC_CHECK(value);
|
||||||
|
set_and_print_env(env_name, value);
|
||||||
|
free(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
#define DEFAULT_USERNAME "StevePi"
|
||||||
|
#define DEFAULT_RENDER_DISTANCE "Short"
|
||||||
|
|
||||||
// Launch
|
// Launch
|
||||||
|
#define LIST_DIALOG_SIZE "400"
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
// Don't Run As Root
|
||||||
|
if (getenv("_MCPI_SKIP_ROOT_CHECK") == NULL && (getuid() == 0 || geteuid() == 0)) {
|
||||||
|
ERR("Don't Run As Root");
|
||||||
|
}
|
||||||
|
|
||||||
// Pre-Bootstrap
|
// Pre-Bootstrap
|
||||||
pre_bootstrap();
|
pre_bootstrap(argc, argv);
|
||||||
|
|
||||||
// Print Features
|
// Print Features
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
@ -139,6 +161,37 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --default
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "--default") == 0) {
|
||||||
|
// Use Default Feature Flags
|
||||||
|
set_env_if_unset("MCPI_FEATURE_FLAGS", []() {
|
||||||
|
std::string feature_flags = "";
|
||||||
|
load_available_feature_flags([&feature_flags](std::string flag) {
|
||||||
|
bool default_value;
|
||||||
|
// Strip Default Value
|
||||||
|
std::string stripped_flag = strip_feature_flag_default(flag, &default_value);
|
||||||
|
// Specify Default Value
|
||||||
|
if (default_value) {
|
||||||
|
// Enabled By Default
|
||||||
|
feature_flags += stripped_flag + '|';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (feature_flags.length() > 0 && feature_flags[feature_flags.length() - 1] == '|') {
|
||||||
|
feature_flags.pop_back();
|
||||||
|
}
|
||||||
|
return feature_flags;
|
||||||
|
});
|
||||||
|
set_env_if_unset("MCPI_RENDER_DISTANCE", []() {
|
||||||
|
return DEFAULT_RENDER_DISTANCE;
|
||||||
|
});
|
||||||
|
set_env_if_unset("MCPI_USERNAME", []() {
|
||||||
|
return DEFAULT_USERNAME;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create ~/.minecraft-pi If Needed
|
// Create ~/.minecraft-pi If Needed
|
||||||
// Minecraft Folder
|
// Minecraft Folder
|
||||||
{
|
{
|
||||||
@ -165,9 +218,9 @@ int main(int argc, char *argv[]) {
|
|||||||
command.push_back("--list");
|
command.push_back("--list");
|
||||||
command.push_back("--checklist");
|
command.push_back("--checklist");
|
||||||
command.push_back("--width");
|
command.push_back("--width");
|
||||||
command.push_back("400");
|
command.push_back(LIST_DIALOG_SIZE);
|
||||||
command.push_back("--height");
|
command.push_back("--height");
|
||||||
command.push_back("400");
|
command.push_back(LIST_DIALOG_SIZE);
|
||||||
command.push_back("--column");
|
command.push_back("--column");
|
||||||
command.push_back("Enabled");
|
command.push_back("Enabled");
|
||||||
command.push_back("--column");
|
command.push_back("--column");
|
||||||
@ -196,23 +249,20 @@ int main(int argc, char *argv[]) {
|
|||||||
command.push_back("--list");
|
command.push_back("--list");
|
||||||
command.push_back("--radiolist");
|
command.push_back("--radiolist");
|
||||||
command.push_back("--width");
|
command.push_back("--width");
|
||||||
command.push_back("400");
|
command.push_back(LIST_DIALOG_SIZE);
|
||||||
command.push_back("--height");
|
command.push_back("--height");
|
||||||
command.push_back("400");
|
command.push_back(LIST_DIALOG_SIZE);
|
||||||
command.push_back("--text");
|
command.push_back("--text");
|
||||||
command.push_back("Minecraft Render Distance:");
|
command.push_back("Select Minecraft Render Distance:");
|
||||||
command.push_back("--column");
|
command.push_back("--column");
|
||||||
command.push_back("Selected");
|
command.push_back("Selected");
|
||||||
command.push_back("--column");
|
command.push_back("--column");
|
||||||
command.push_back("Name");
|
command.push_back("Name");
|
||||||
command.push_back("FALSE");
|
std::string render_distances[] = {"Far", "Normal", "Short", "Tiny"};
|
||||||
command.push_back("Far");
|
for (std::string &render_distance : render_distances) {
|
||||||
command.push_back("FALSE");
|
command.push_back(render_distance.compare(DEFAULT_RENDER_DISTANCE) == 0 ? "TRUE" : "FALSE");
|
||||||
command.push_back("Normal");
|
command.push_back(render_distance);
|
||||||
command.push_back("TRUE");
|
}
|
||||||
command.push_back("Short");
|
|
||||||
command.push_back("FALSE");
|
|
||||||
command.push_back("Tiny");
|
|
||||||
// Run
|
// Run
|
||||||
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command);
|
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command);
|
||||||
}
|
}
|
||||||
@ -221,9 +271,9 @@ int main(int argc, char *argv[]) {
|
|||||||
std::vector<std::string> command;
|
std::vector<std::string> command;
|
||||||
command.push_back("--entry");
|
command.push_back("--entry");
|
||||||
command.push_back("--text");
|
command.push_back("--text");
|
||||||
command.push_back("Minecraft Username:");
|
command.push_back("Enter Minecraft Username:");
|
||||||
command.push_back("--entry-text");
|
command.push_back("--entry-text");
|
||||||
command.push_back("StevePi");
|
command.push_back(DEFAULT_USERNAME);
|
||||||
// Run
|
// Run
|
||||||
run_zenity_and_set_env("MCPI_USERNAME", command);
|
run_zenity_and_set_env("MCPI_USERNAME", command);
|
||||||
}
|
}
|
||||||
|
246
launcher/src/crash-report.c
Normal file
246
launcher/src/crash-report.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "crash-report.h"
|
||||||
|
|
||||||
|
// Show Crash Report Dialog
|
||||||
|
#ifndef MCPI_HEADLESS_MODE
|
||||||
|
#define DIALOG_TITLE "Crash Report"
|
||||||
|
#define CRASH_REPORT_DIALOG_WIDTH "640"
|
||||||
|
#define CRASH_REPORT_DIALOG_HEIGHT "480"
|
||||||
|
static void show_report(const char *log_filename) {
|
||||||
|
const char *command[] = {
|
||||||
|
"zenity",
|
||||||
|
"--title", DIALOG_TITLE,
|
||||||
|
"--name", MCPI_APP_ID,
|
||||||
|
"--width", CRASH_REPORT_DIALOG_WIDTH,
|
||||||
|
"--height", CRASH_REPORT_DIALOG_HEIGHT,
|
||||||
|
"--text-info",
|
||||||
|
"--text", "Minecraft: Pi Edition: Reborn has crashed!\n\nNeed help? Consider asking on the <a href=\"https://discord.com/invite/aDqejQGMMy\">Discord server</a>!",
|
||||||
|
"--filename", log_filename,
|
||||||
|
"--no-wrap",
|
||||||
|
"--font", "Monospace",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
free(run_command(command, NULL));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Exit Handler
|
||||||
|
static void exit_handler(__attribute__((unused)) int signal) {
|
||||||
|
// Murder
|
||||||
|
murder_children();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
#define PIPE_READ 0
|
||||||
|
#define PIPE_WRITE 1
|
||||||
|
#define MCPI_LOGS_DIR "/tmp/.minecraft-pi-logs"
|
||||||
|
void setup_crash_report() {
|
||||||
|
// Store Output
|
||||||
|
int output_pipe[2];
|
||||||
|
safe_pipe2(output_pipe, 0);
|
||||||
|
int error_pipe[2];
|
||||||
|
safe_pipe2(error_pipe, 0);
|
||||||
|
int input_pipe[2];
|
||||||
|
safe_pipe2(input_pipe, 0);
|
||||||
|
|
||||||
|
// Fork
|
||||||
|
pid_t ret = fork();
|
||||||
|
if (ret == -1) {
|
||||||
|
ERR("Unable To Fork: %s", strerror(errno));
|
||||||
|
} else if (ret == 0) {
|
||||||
|
// Child Process
|
||||||
|
|
||||||
|
// Pipe stdio
|
||||||
|
dup2(output_pipe[PIPE_WRITE], STDOUT_FILENO);
|
||||||
|
close(output_pipe[PIPE_READ]);
|
||||||
|
close(output_pipe[PIPE_WRITE]);
|
||||||
|
dup2(error_pipe[PIPE_WRITE], STDERR_FILENO);
|
||||||
|
close(error_pipe[PIPE_READ]);
|
||||||
|
close(error_pipe[PIPE_WRITE]);
|
||||||
|
dup2(input_pipe[PIPE_READ], STDIN_FILENO);
|
||||||
|
close(input_pipe[PIPE_READ]);
|
||||||
|
close(input_pipe[PIPE_WRITE]);
|
||||||
|
|
||||||
|
// Create New Process Group
|
||||||
|
setpgid(0, 0);
|
||||||
|
|
||||||
|
// Continue Execution
|
||||||
|
} else {
|
||||||
|
// Parent Process
|
||||||
|
track_child(ret);
|
||||||
|
|
||||||
|
// Install Signal Handlers
|
||||||
|
struct sigaction act_sigint;
|
||||||
|
memset((void *) &act_sigint, 0, sizeof (struct sigaction));
|
||||||
|
act_sigint.sa_flags = SA_RESTART;
|
||||||
|
act_sigint.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGINT, &act_sigint, NULL);
|
||||||
|
struct sigaction act_sigterm;
|
||||||
|
memset((void *) &act_sigterm, 0, sizeof (struct sigaction));
|
||||||
|
act_sigterm.sa_flags = SA_RESTART;
|
||||||
|
act_sigterm.sa_handler = &exit_handler;
|
||||||
|
sigaction(SIGTERM, &act_sigterm, NULL);
|
||||||
|
|
||||||
|
// Close Unneeded File Descriptors
|
||||||
|
close(output_pipe[PIPE_WRITE]);
|
||||||
|
close(error_pipe[PIPE_WRITE]);
|
||||||
|
close(input_pipe[PIPE_READ]);
|
||||||
|
|
||||||
|
// Setup Logging
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
char buf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
// Ensure Temporary Directory
|
||||||
|
{
|
||||||
|
// Check If It Exists
|
||||||
|
struct stat tmp_stat;
|
||||||
|
int exists = stat(MCPI_LOGS_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
|
||||||
|
if (!exists) {
|
||||||
|
// Doesn't Exist
|
||||||
|
if (mkdir(MCPI_LOGS_DIR, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
|
||||||
|
ERR("Unable To Create Temporary Folder: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Temporary File
|
||||||
|
char log_filename[] = MCPI_LOGS_DIR "/XXXXXX";
|
||||||
|
int log_file_fd = mkstemp(log_filename);
|
||||||
|
if (log_file_fd == -1) {
|
||||||
|
ERR("Unable To Create Log File: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Polling
|
||||||
|
int number_fds = 3;
|
||||||
|
struct pollfd poll_fds[number_fds];
|
||||||
|
poll_fds[0].fd = output_pipe[PIPE_READ];
|
||||||
|
poll_fds[1].fd = error_pipe[PIPE_READ];
|
||||||
|
poll_fds[2].fd = STDIN_FILENO;
|
||||||
|
for (int i = 0; i < number_fds; i++) {
|
||||||
|
poll_fds[i].events = POLLIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll Data
|
||||||
|
int number_open_fds = number_fds;
|
||||||
|
while (number_open_fds > 0) {
|
||||||
|
int poll_ret = poll(poll_fds, number_fds, -1);
|
||||||
|
if (poll_ret == -1) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
ERR("Unable To Poll Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle Data
|
||||||
|
for (int i = 0; i < number_fds; i++) {
|
||||||
|
if (poll_fds[i].revents != 0) {
|
||||||
|
if (poll_fds[i].revents & POLLIN) {
|
||||||
|
if (poll_fds[i].fd == STDIN_FILENO) {
|
||||||
|
// Data Available From stdin
|
||||||
|
int bytes_available;
|
||||||
|
if (ioctl(fileno(stdin), FIONREAD, &bytes_available) == -1) {
|
||||||
|
bytes_available = 0;
|
||||||
|
}
|
||||||
|
// Read
|
||||||
|
ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE);
|
||||||
|
if (bytes_read == -1) {
|
||||||
|
ERR("Unable To Read Log Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
// Write To Child
|
||||||
|
if (write(input_pipe[PIPE_WRITE], (void *) buf, bytes_read) == -1) {
|
||||||
|
ERR("Unable To Write Input To Child: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Data Available From Child's stdout/stderr
|
||||||
|
ssize_t bytes_read = read(poll_fds[i].fd, (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */);
|
||||||
|
if (bytes_read == -1) {
|
||||||
|
ERR("Unable To Read Log Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print To Terminal
|
||||||
|
buf[bytes_read] = '\0';
|
||||||
|
fprintf(i == 0 ? stdout : stderr, "%s", buf);
|
||||||
|
|
||||||
|
// Write To log
|
||||||
|
if (write(log_file_fd, (void *) buf, bytes_read) == -1) {
|
||||||
|
ERR("Unable To Write Log Data: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File Descriptor No Longer Accessible
|
||||||
|
if (poll_fds[i].events != 0 && close(poll_fds[i].fd) == -1) {
|
||||||
|
ERR("Unable To Close File Descriptor: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
poll_fds[i].events = 0;
|
||||||
|
number_open_fds--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close Input Pipe
|
||||||
|
close(input_pipe[PIPE_WRITE]);
|
||||||
|
|
||||||
|
// Get Return Code
|
||||||
|
int status;
|
||||||
|
waitpid(ret, &status, 0);
|
||||||
|
untrack_child(ret);
|
||||||
|
|
||||||
|
// Check If Is Crash
|
||||||
|
int is_crash = !is_exit_status_success(status);
|
||||||
|
|
||||||
|
// Log Exit Code To log If Crash
|
||||||
|
if (is_crash) {
|
||||||
|
// Create Exit Code Log Line
|
||||||
|
char *exit_status = NULL;
|
||||||
|
get_exit_status_string(status, &exit_status);
|
||||||
|
char *exit_code_line = NULL;
|
||||||
|
safe_asprintf(&exit_code_line, "[CRASH]: Terminated%s\n", exit_status);
|
||||||
|
free(exit_status);
|
||||||
|
|
||||||
|
// Print Exit Code Log Line
|
||||||
|
fprintf(stderr, "%s", exit_code_line);
|
||||||
|
|
||||||
|
// Write Exit Code Log Line
|
||||||
|
if (write(log_file_fd, (void *) exit_code_line, strlen(exit_code_line)) == -1) {
|
||||||
|
ERR("Unable To Write Exit Code To Log: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free Exit Code Log Line
|
||||||
|
free(exit_code_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close Log File FD
|
||||||
|
if (close(log_file_fd) == -1) {
|
||||||
|
ERR("Unable To Close Log File Descriptor: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show Crash Log
|
||||||
|
#ifndef MCPI_HEADLESS_MODE
|
||||||
|
if (is_crash) {
|
||||||
|
show_report(log_filename);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Delete Log File
|
||||||
|
if (unlink(log_filename) == -1) {
|
||||||
|
ERR("Unable To Delete Log File: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit
|
||||||
|
exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int feature_has(const char *name, int server_default);
|
void setup_crash_report();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
@ -8,12 +8,21 @@
|
|||||||
#include "patchelf.h"
|
#include "patchelf.h"
|
||||||
|
|
||||||
// Duplicate MCPI Executable Into /tmp
|
// Duplicate MCPI Executable Into /tmp
|
||||||
static void duplicate_mcpi_executable() {
|
static void duplicate_mcpi_executable(const char *original_path, char *new_path) {
|
||||||
// Get Original Path
|
// Ensure Temporary Directory
|
||||||
const char *original_path = getenv("MCPI_EXECUTABLE_PATH");
|
{
|
||||||
|
// Check If It Exists
|
||||||
|
struct stat tmp_stat;
|
||||||
|
int exists = stat(MCPI_PATCHED_DIR, &tmp_stat) != 0 ? 0 : S_ISDIR(tmp_stat.st_mode);
|
||||||
|
if (!exists) {
|
||||||
|
// Doesn't Exist
|
||||||
|
if (mkdir(MCPI_PATCHED_DIR, S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
|
||||||
|
ERR("Unable To Create Temporary Folder: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate New File
|
// Generate New File
|
||||||
char new_path[] = "/tmp/.minecraft-pi-XXXXXX";
|
|
||||||
int new_file_fd = mkstemp(new_path);
|
int new_file_fd = mkstemp(new_path);
|
||||||
if (new_file_fd == -1) {
|
if (new_file_fd == -1) {
|
||||||
ERR("Unable To Create Temporary File: %s", strerror(errno));
|
ERR("Unable To Create Temporary File: %s", strerror(errno));
|
||||||
@ -22,7 +31,6 @@ static void duplicate_mcpi_executable() {
|
|||||||
if (new_file == NULL) {
|
if (new_file == NULL) {
|
||||||
ERR("Unable To Open Temporary File: %s", strerror(errno));
|
ERR("Unable To Open Temporary File: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
set_and_print_env("MCPI_EXECUTABLE_PATH", new_path);
|
|
||||||
|
|
||||||
// Copy Original File
|
// Copy Original File
|
||||||
{
|
{
|
||||||
@ -56,35 +64,73 @@ static void duplicate_mcpi_executable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fix MCPI Dependencies
|
// Fix MCPI Dependencies
|
||||||
void patch_mcpi_elf_dependencies(const char *linker) {
|
#define patch_mcpi_elf_dependencies_with_extra_patchelf_args(...) \
|
||||||
|
({ \
|
||||||
|
const char *const _macro_command[] = { \
|
||||||
|
"patchelf", \
|
||||||
|
##__VA_ARGS__, \
|
||||||
|
"--remove-needed", "libbcm_host.so", \
|
||||||
|
"--remove-needed", "libX11.so.6", \
|
||||||
|
"--remove-needed", "libEGL.so", \
|
||||||
|
"--remove-needed", "libGLESv2.so", \
|
||||||
|
"--remove-needed", "libSDL-1.2.so.0", \
|
||||||
|
"--add-needed", "libmedia-layer-core.so", \
|
||||||
|
new_path, \
|
||||||
|
NULL \
|
||||||
|
}; \
|
||||||
|
int _macro_return_code = 0; \
|
||||||
|
char *_macro_output = run_command(_macro_command, &_macro_return_code); \
|
||||||
|
if (_macro_output != NULL) { \
|
||||||
|
free(_macro_output); \
|
||||||
|
} \
|
||||||
|
_macro_return_code; \
|
||||||
|
})
|
||||||
|
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path, const char *linker) {
|
||||||
// Duplicate MCPI executable into /tmp so it can be modified.
|
// Duplicate MCPI executable into /tmp so it can be modified.
|
||||||
duplicate_mcpi_executable();
|
duplicate_mcpi_executable(original_path, new_path);
|
||||||
|
|
||||||
// Get Path
|
|
||||||
char *exe = getenv("MCPI_EXECUTABLE_PATH");
|
|
||||||
|
|
||||||
// Run patchelf
|
// Run patchelf
|
||||||
const char *const command[] = {
|
int return_code;
|
||||||
"patchelf",
|
if (linker == NULL) {
|
||||||
"--set-interpreter", linker,
|
return_code = patch_mcpi_elf_dependencies_with_extra_patchelf_args();
|
||||||
"--remove-needed", "libbcm_host.so",
|
} else {
|
||||||
"--remove-needed", "libX11.so.6",
|
return_code = patch_mcpi_elf_dependencies_with_extra_patchelf_args("--set-interpreter", linker);
|
||||||
"--remove-needed", "libEGL.so",
|
|
||||||
"--replace-needed", "libGLESv2.so", "libGLESv1_CM.so.1",
|
|
||||||
exe,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
int return_code = 0;
|
|
||||||
char *output = run_command(command, &return_code);
|
|
||||||
if (output != NULL) {
|
|
||||||
free(output);
|
|
||||||
}
|
}
|
||||||
if (return_code != 0) {
|
if (!is_exit_status_success(return_code)) {
|
||||||
ERR("patchelf Failed: Exit Code: %i", return_code);
|
char *exit_status_line = NULL;
|
||||||
|
get_exit_status_string(return_code, &exit_status_line);
|
||||||
|
ERR("patchelf Failed%s", exit_status_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix Permissions
|
// Fix Permissions
|
||||||
if (chmod(exe, S_IRUSR | S_IXUSR) != 0) {
|
if (chmod(new_path, S_IRUSR | S_IXUSR) != 0) {
|
||||||
ERR("Unable To Set File Permissions: %s: %s", exe, strerror(errno));
|
ERR("Unable To Set File Permissions: %s: %s", new_path, strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Interpreter
|
||||||
|
char *patch_get_interpreter(const char *file) {
|
||||||
|
// Run
|
||||||
|
const char *const command[] = {
|
||||||
|
"patchelf",
|
||||||
|
"--print-interpreter",
|
||||||
|
file,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
int return_code;
|
||||||
|
char *output = run_command(command, &return_code);
|
||||||
|
if (!is_exit_status_success(return_code)) {
|
||||||
|
char *exit_status_line = NULL;
|
||||||
|
get_exit_status_string(return_code, &exit_status_line);
|
||||||
|
ERR("patchelf Failed%s", exit_status_line);
|
||||||
|
}
|
||||||
|
if (output != NULL) {
|
||||||
|
// Trim
|
||||||
|
int length = strlen(output);
|
||||||
|
if (output[length - 1] == '\n') {
|
||||||
|
output[length - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
@ -4,7 +4,10 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void patch_mcpi_elf_dependencies(const char *linker);
|
#define MCPI_PATCHED_DIR "/tmp/.minecraft-pi-patched"
|
||||||
|
|
||||||
|
void patch_mcpi_elf_dependencies(const char *original_path, char *new_path, const char *linker);
|
||||||
|
char *patch_get_interpreter(const char *file);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
#include "../bootstrap.h"
|
#include "../bootstrap.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// Pre-Bootstrap
|
// Pre-Bootstrap
|
||||||
pre_bootstrap();
|
pre_bootstrap(argc, argv);
|
||||||
|
|
||||||
// Set Home To Current Directory, So World Data Is Stored There
|
// Set Home To Current Directory, So World Data Is Stored There
|
||||||
char *launch_directory = getcwd(NULL, 0);
|
char *launch_directory = getcwd(NULL, 0);
|
||||||
|
@ -1,12 +1,34 @@
|
|||||||
project(libreborn)
|
project(libreborn)
|
||||||
|
|
||||||
add_library(reborn-util STATIC src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c)
|
# Config
|
||||||
target_include_directories(reborn-util PUBLIC include)
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn")
|
||||||
|
configure_file(include/libreborn/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/libreborn/config.h" ESCAPE_QUOTES @ONLY)
|
||||||
|
|
||||||
|
# Util
|
||||||
|
add_library(reborn-util SHARED src/util/elf.c src/util/exec.c src/util/string.c src/util/util.c)
|
||||||
|
target_include_directories(
|
||||||
|
reborn-util
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/libreborn>"
|
||||||
|
)
|
||||||
|
# Install
|
||||||
|
install(TARGETS reborn-util DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
# SDK
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS reborn-util EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
install(DIRECTORY "include/" DESTINATION "${MCPI_SDK_INCLUDE_DIR}/libreborn")
|
||||||
|
install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/" DESTINATION "${MCPI_SDK_INCLUDE_DIR}/libreborn")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Patch
|
||||||
if(BUILD_ARM_COMPONENTS)
|
if(BUILD_ARM_COMPONENTS)
|
||||||
add_library(reborn-patch SHARED src/patch/patch.c)
|
add_library(reborn-patch SHARED src/patch/patch.c)
|
||||||
target_link_libraries(reborn-patch dl reborn-util)
|
target_link_libraries(reborn-patch dl pthread reborn-util)
|
||||||
target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE)
|
target_compile_definitions(reborn-patch PUBLIC -DREBORN_HAS_PATCH_CODE)
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS reborn-patch DESTINATION "${MCPI_LIB_DIR}")
|
install(TARGETS reborn-patch DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
# SDK
|
||||||
|
install(TARGETS reborn-patch EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define GUI_TITLE "Minecraft: Pi Edition: Reborn"
|
|
11
libreborn/include/libreborn/config.h.in
Normal file
11
libreborn/include/libreborn/config.h.in
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#cmakedefine MCPI_SERVER_MODE
|
||||||
|
#cmakedefine MCPI_HEADLESS_MODE
|
||||||
|
#cmakedefine MCPI_IS_APPIMAGE_BUILD
|
||||||
|
#cmakedefine MCPI_USE_PREBUILT_ARMHF_TOOLCHAIN
|
||||||
|
#cmakedefine MCPI_USE_GLES1_COMPATIBILITY_LAYER
|
||||||
|
#cmakedefine MCPI_APP_TITLE "@MCPI_APP_TITLE@"
|
||||||
|
#cmakedefine MCPI_APP_ID "@MCPI_APP_ID@"
|
||||||
|
#cmakedefine MCPI_VERSION "@MCPI_VERSION@"
|
||||||
|
#cmakedefine MCPI_SDK_DIR "@MCPI_SDK_DIR@"
|
@ -13,9 +13,9 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Find And Iterate Over All .text Sections In Current Binary
|
// Find And Iterate Over All Segments In Current Binary
|
||||||
typedef void (*text_section_callback_t)(ElfW(Addr) section, ElfW(Word) size, void *data);
|
typedef void (*segment_callback_t)(ElfW(Addr) section, ElfW(Word) size, void *data);
|
||||||
void iterate_text_sections(const char *exe, text_section_callback_t callback, void *data);
|
void iterate_segments(segment_callback_t callback, void *data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
@ -15,7 +16,15 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Set Environmental Variable
|
||||||
|
void set_and_print_env(const char *name, const char *value);
|
||||||
|
|
||||||
// Safe execvpe()
|
// Safe execvpe()
|
||||||
|
#define for_each_special_environmental_variable(handle) \
|
||||||
|
handle("LD_LIBRARY_PATH"); \
|
||||||
|
handle("GCONV_PATH"); \
|
||||||
|
handle("LD_PRELOAD");
|
||||||
|
void setup_exec_environment(int is_arm_component);
|
||||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
|
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]);
|
||||||
|
|
||||||
// Chop Off Last Component
|
// Chop Off Last Component
|
||||||
@ -23,14 +32,17 @@ void chop_last_component(char **str);
|
|||||||
// Get Binary Directory (Remember To Free)
|
// Get Binary Directory (Remember To Free)
|
||||||
char *get_binary_directory();
|
char *get_binary_directory();
|
||||||
|
|
||||||
// Safe execvpe() Relative To Binary
|
|
||||||
__attribute__((noreturn)) void safe_execvpe_relative_to_binary(const char *const argv[], const char *const envp[]);
|
|
||||||
|
|
||||||
// Get MCPI Directory
|
|
||||||
char *get_mcpi_directory();
|
|
||||||
|
|
||||||
// Run Command And Get Output
|
// Run Command And Get Output
|
||||||
char *run_command(const char *const command[], int *return_code);
|
char *run_command(const char *const command[], int *exit_status);
|
||||||
|
#define is_exit_status_success(status) (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||||
|
|
||||||
|
// Get Exit Status String
|
||||||
|
void get_exit_status_string(int status, char **out);
|
||||||
|
|
||||||
|
// Track Children
|
||||||
|
void track_child(pid_t pid);
|
||||||
|
void untrack_child(pid_t pid);
|
||||||
|
void murder_children();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "config.h"
|
#include <libreborn/config.h>
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
#define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", __VA_ARGS__); }
|
#define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", ##__VA_ARGS__); }
|
||||||
#define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", __VA_ARGS__); }
|
#define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", ##__VA_ARGS__); }
|
||||||
#define DEBUG(format, ...) { const char *debug = getenv("MCPI_DEBUG"); if (debug != NULL && strlen(debug) > 0) { fprintf(stderr, "[DEBUG]: " format "\n", __VA_ARGS__); } }
|
#define DEBUG(format, ...) { const char *debug = getenv("MCPI_DEBUG"); if (debug != NULL) { fprintf(stderr, "[DEBUG]: " format "\n", ##__VA_ARGS__); } }
|
||||||
#define ERR(format, ...) { fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, __VA_ARGS__); exit(EXIT_FAILURE); }
|
#define ERR(format, ...) { fprintf(stderr, "[ERR]: (%s:%i): " format "\n", __FILE__, __LINE__, ##__VA_ARGS__); exit(EXIT_FAILURE); }
|
||||||
#define IMPOSSIBLE() ERR("%s", "This Should Never Be Called")
|
#define IMPOSSIBLE() ERR("This Should Never Be Called")
|
||||||
|
@ -14,6 +14,8 @@ void _overwrite_call(const char *file, int line, void *start, void *target);
|
|||||||
void _overwrite_calls(const char *file, int line, void *start, void *target);
|
void _overwrite_calls(const char *file, int line, void *start, void *target);
|
||||||
#define overwrite_calls(start, target) _overwrite_calls(__FILE__, __LINE__, start, target);
|
#define overwrite_calls(start, target) _overwrite_calls(__FILE__, __LINE__, start, target);
|
||||||
|
|
||||||
|
void *extract_from_bl_instruction(unsigned char *from);
|
||||||
|
|
||||||
void _overwrite(const char *file, int line, void *start, void *target);
|
void _overwrite(const char *file, int line, void *start, void *target);
|
||||||
#define overwrite(start, target) _overwrite(__FILE__, __LINE__, start, target);
|
#define overwrite(start, target) _overwrite(__FILE__, __LINE__, start, target);
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <iconv.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@ -19,7 +21,7 @@
|
|||||||
#define string_append(str, format, ...) \
|
#define string_append(str, format, ...) \
|
||||||
{ \
|
{ \
|
||||||
char *old = *str; \
|
char *old = *str; \
|
||||||
safe_asprintf(str, "%s" format, *str == NULL ? "" : *str, __VA_ARGS__); \
|
safe_asprintf(str, "%s" format, *str == NULL ? "" : *str, ##__VA_ARGS__); \
|
||||||
ALLOC_CHECK(*str); \
|
ALLOC_CHECK(*str); \
|
||||||
if (old != NULL && old != *str) { \
|
if (old != NULL && old != *str) { \
|
||||||
free(old); \
|
free(old); \
|
||||||
@ -33,6 +35,11 @@ extern "C" {
|
|||||||
// Sanitize String
|
// Sanitize String
|
||||||
void sanitize_string(char **str, int max_length, unsigned int allow_newlines);
|
void sanitize_string(char **str, int max_length, unsigned int allow_newlines);
|
||||||
|
|
||||||
|
// CP437
|
||||||
|
void safe_iconv(iconv_t cd, char *input, size_t input_size, char *output, size_t output_size);
|
||||||
|
char *to_cp437(const char *input);
|
||||||
|
char *from_cp437(const char *input);
|
||||||
|
|
||||||
// Starts With
|
// Starts With
|
||||||
int starts_with(const char *str, const char *prefix);
|
int starts_with(const char *str, const char *prefix);
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
#define ALLOC_CHECK(obj) \
|
#define ALLOC_CHECK(obj) \
|
||||||
{ \
|
{ \
|
||||||
if (obj == NULL) { \
|
if (obj == NULL) { \
|
||||||
ERR("%s", "Memory Allocation Failed"); \
|
ERR("Memory Allocation Failed"); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,23 +32,14 @@
|
|||||||
\
|
\
|
||||||
__attribute__((__used__)) return_type name args
|
__attribute__((__used__)) return_type name args
|
||||||
|
|
||||||
// Macro To Reset Environmental Variables To Pre-MCPI State
|
|
||||||
#define RESET_ENVIRONMENTAL_VARIABLE(name) \
|
|
||||||
{ \
|
|
||||||
char *original_env_value = getenv("ORIGINAL_" name); \
|
|
||||||
if (original_env_value != NULL) { \
|
|
||||||
setenv(name, original_env_value, 1); \
|
|
||||||
} else { \
|
|
||||||
unsetenv(name); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Safe Version Of pipe()
|
// Safe Version Of pipe()
|
||||||
void safe_pipe2(int pipefd[2], int flags);
|
void safe_pipe2(int pipefd[2], int flags);
|
||||||
|
// Check If Two Percentages Are Different Enough To Be Logged
|
||||||
|
int is_progress_difference_significant(int32_t new_val, int32_t old_val);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,12 @@ static unsigned char *code_block = NULL;
|
|||||||
#define CODE_SIZE 8
|
#define CODE_SIZE 8
|
||||||
static int code_block_remaining = CODE_BLOCK_SIZE;
|
static int code_block_remaining = CODE_BLOCK_SIZE;
|
||||||
|
|
||||||
|
static void _long_overwrite(void *start, void *target) {
|
||||||
|
unsigned char patch_data[4] = {0x04, 0xf0, 0x1f, 0xe5}; // "ldr pc, [pc, #-0x4]"
|
||||||
|
|
||||||
|
_patch(NULL, -1, start, patch_data);
|
||||||
|
_patch_address(NULL, -1, (void *) (((unsigned char *) start) + 4), target);
|
||||||
|
}
|
||||||
static void update_code_block(void *target) {
|
static void update_code_block(void *target) {
|
||||||
// BL Instructions Can Only Access A Limited Portion of Memory, So This Allocates Memory Closer To The Original Instruction, That When Run, Will Jump Into The Actual Target
|
// BL Instructions Can Only Access A Limited Portion of Memory, So This Allocates Memory Closer To The Original Instruction, That When Run, Will Jump Into The Actual Target
|
||||||
if (code_block == NULL) {
|
if (code_block == NULL) {
|
||||||
@ -82,28 +88,33 @@ static void update_code_block(void *target) {
|
|||||||
DEBUG("Code Block Allocated At: 0x%08x", (uint32_t) code_block);
|
DEBUG("Code Block Allocated At: 0x%08x", (uint32_t) code_block);
|
||||||
}
|
}
|
||||||
if (code_block_remaining < CODE_SIZE) {
|
if (code_block_remaining < CODE_SIZE) {
|
||||||
ERR("%s", "Maximum Amount Of overwrite_calls() Uses Reached");
|
ERR("Maximum Amount Of overwrite_calls() Uses Reached");
|
||||||
}
|
}
|
||||||
_overwrite(NULL, -1, code_block, target);
|
_long_overwrite(code_block, target);
|
||||||
}
|
}
|
||||||
static void increment_code_block() {
|
static void increment_code_block() {
|
||||||
code_block = code_block + CODE_SIZE;
|
code_block = code_block + CODE_SIZE;
|
||||||
code_block_remaining = code_block_remaining - CODE_SIZE;
|
code_block_remaining = code_block_remaining - CODE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite Specific BL Instruction
|
// Overwrite Specific B(L) Instruction
|
||||||
void _overwrite_call(const char *file, int line, void *start, void *target) {
|
static void _overwrite_call_internal(const char *file, int line, void *start, void *target, int use_b_instruction) {
|
||||||
// Add New Target To Code Block
|
// Add New Target To Code Block
|
||||||
update_code_block(target);
|
update_code_block(target);
|
||||||
|
|
||||||
uint32_t new_instruction = generate_bl_instruction(start, code_block, 0);
|
// Patch
|
||||||
|
uint32_t new_instruction = generate_bl_instruction(start, code_block, use_b_instruction);
|
||||||
_patch(file, line, start, (unsigned char *) &new_instruction);
|
_patch(file, line, start, (unsigned char *) &new_instruction);
|
||||||
|
|
||||||
// Increment Code Block Position
|
// Increment Code Block Position
|
||||||
increment_code_block();
|
increment_code_block();
|
||||||
}
|
}
|
||||||
|
void _overwrite_call(const char *file, int line, void *start, void *target) {
|
||||||
|
int use_b_instruction = ((unsigned char *) start)[3] == B_INSTRUCTION;
|
||||||
|
_overwrite_call_internal(file, line, start, target, use_b_instruction);
|
||||||
|
}
|
||||||
|
|
||||||
// Overwrite Function Calls
|
// Overwrite All B(L) Intrusctions That Target The Specified Address
|
||||||
void _overwrite_calls(const char *file, int line, void *start, void *target) {
|
void _overwrite_calls(const char *file, int line, void *start, void *target) {
|
||||||
// Add New Target To Code Block
|
// Add New Target To Code Block
|
||||||
update_code_block(target);
|
update_code_block(target);
|
||||||
@ -115,7 +126,7 @@ void _overwrite_calls(const char *file, int line, void *start, void *target) {
|
|||||||
data.replacement = code_block;
|
data.replacement = code_block;
|
||||||
data.found = 0;
|
data.found = 0;
|
||||||
|
|
||||||
iterate_text_sections("/proc/self/exe", overwrite_calls_callback, &data);
|
iterate_segments(overwrite_calls_callback, &data);
|
||||||
|
|
||||||
// Increment Code Block Position
|
// Increment Code Block Position
|
||||||
increment_code_block();
|
increment_code_block();
|
||||||
@ -126,13 +137,24 @@ void _overwrite_calls(const char *file, int line, void *start, void *target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite Function
|
// Extract Target Address From B(L) Instruction
|
||||||
// NOTE: "start" Must Be At Least 8 Bytes Long
|
void *extract_from_bl_instruction(unsigned char *from) {
|
||||||
void _overwrite(const char *file, int line, void *start, void *target) {
|
unsigned char *pc = ((unsigned char *) from) + 8;
|
||||||
unsigned char patch_data[4] = {0x04, 0xf0, 0x1f, 0xe5}; // "ldr pc, [pc, #-0x4]"
|
|
||||||
|
|
||||||
_patch(file, line, start, patch_data);
|
int32_t target = 0;
|
||||||
_patch_address(file, line, (void *) (((unsigned char *) start) + 4), target);
|
unsigned char *target_array = (unsigned char *) ⌖
|
||||||
|
target_array[0] = from[0];
|
||||||
|
target_array[1] = from[1];
|
||||||
|
target_array[2] = from[2];
|
||||||
|
|
||||||
|
int32_t offset = target << 2;
|
||||||
|
|
||||||
|
return (void *) (pc + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite Function
|
||||||
|
void _overwrite(const char *file, int line, void *start, void *target) {
|
||||||
|
_overwrite_call_internal(file, line, start, target, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print Patch Debug Data
|
// Print Patch Debug Data
|
||||||
@ -141,7 +163,7 @@ void _overwrite(const char *file, int line, void *start, void *target) {
|
|||||||
// Patch Instruction
|
// Patch Instruction
|
||||||
void _patch(const char *file, int line, void *start, unsigned char patch[4]) {
|
void _patch(const char *file, int line, void *start, unsigned char patch[4]) {
|
||||||
if (((uint32_t) start) % 4 != 0) {
|
if (((uint32_t) start) % 4 != 0) {
|
||||||
ERR("%s", "Invalid Address");
|
ERR("Invalid Address");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t page_size = sysconf(_SC_PAGESIZE);
|
size_t page_size = sysconf(_SC_PAGESIZE);
|
||||||
|
@ -1,53 +1,28 @@
|
|||||||
#include <libreborn/elf.h>
|
#include <libreborn/elf.h>
|
||||||
|
|
||||||
// Find And Iterate Over All .text Sections In Current Binary
|
// Find And Iterate Over All Segments In Current Binary
|
||||||
void iterate_text_sections(const char *exe, text_section_callback_t callback, void *data) {
|
typedef struct {
|
||||||
// Load Main Binary
|
segment_callback_t callback;
|
||||||
FILE *file_obj = fopen(exe, "rb");
|
void *data;
|
||||||
|
} dl_iterate_callback_data;
|
||||||
// Verify Binary
|
static int dl_iterate_callback(struct dl_phdr_info *info, __attribute__((unused)) size_t size, void *data) {
|
||||||
if (!file_obj) {
|
dl_iterate_callback_data *callback_data = (dl_iterate_callback_data *) data;
|
||||||
ERR("%s", "Unable To Open Binary");
|
// Only Search Current Program
|
||||||
}
|
if (strcmp(info->dlpi_name, "") == 0) {
|
||||||
|
for (int i = 0; i < info->dlpi_phnum; i++) {
|
||||||
// Get File Size
|
// Only Executable Segemnts
|
||||||
fseek(file_obj, 0L, SEEK_END);
|
if (info->dlpi_phdr[i].p_type == PT_LOAD && (info->dlpi_phdr[i].p_flags & PF_X) != 0) {
|
||||||
long int file_size = ftell(file_obj);
|
// Callback
|
||||||
fseek(file_obj, 0L, SEEK_SET);
|
(*callback_data->callback)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr, info->dlpi_phdr[i].p_memsz, callback_data->data);
|
||||||
|
|
||||||
// Map File To Pointer
|
|
||||||
unsigned char *file_map = (unsigned char *) mmap(0, file_size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0);
|
|
||||||
|
|
||||||
// Parse ELF
|
|
||||||
ElfW(Ehdr) *elf_header = (ElfW(Ehdr) *) file_map;
|
|
||||||
ElfW(Shdr) *elf_section_headers = (ElfW(Shdr) *) (file_map + elf_header->e_shoff);
|
|
||||||
int elf_section_header_count = elf_header->e_shnum;
|
|
||||||
|
|
||||||
// Locate Section Names
|
|
||||||
ElfW(Shdr) elf_shstrtab = elf_section_headers[elf_header->e_shstrndx];
|
|
||||||
unsigned char *elf_shstrtab_p = file_map + elf_shstrtab.sh_offset;
|
|
||||||
|
|
||||||
// Track .text Sections
|
|
||||||
int text_sections = 0;
|
|
||||||
|
|
||||||
// Iterate Sections
|
|
||||||
for (int i = 0; i < elf_section_header_count; ++i) {
|
|
||||||
ElfW(Shdr) header = elf_section_headers[i];
|
|
||||||
char *name = (char *) (elf_shstrtab_p + header.sh_name);
|
|
||||||
// Check Section Type
|
|
||||||
if (strcmp(name, ".text") == 0) {
|
|
||||||
// .text Section
|
|
||||||
(*callback)(header.sh_addr, header.sh_size, data);
|
|
||||||
text_sections++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure At Least .text Section Was Scanned
|
|
||||||
if (text_sections < 1) {
|
|
||||||
ERR("%s", "Unable To Find .text Sectons");
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
// Unmap And Close File
|
}
|
||||||
munmap(file_map, file_size);
|
void iterate_segments(segment_callback_t callback, void *data) {
|
||||||
fclose(file_obj);
|
dl_iterate_callback_data callback_data = {
|
||||||
|
.callback = callback,
|
||||||
|
.data = data
|
||||||
|
};
|
||||||
|
dl_iterate_phdr(dl_iterate_callback, (void *) &callback_data);
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,45 @@
|
|||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <libreborn/exec.h>
|
#include <libreborn/exec.h>
|
||||||
|
|
||||||
|
// Set Environmental Variable
|
||||||
|
static void setenv_safe(const char *name, const char *value) {
|
||||||
|
if (value != NULL) {
|
||||||
|
setenv(name, value, 1);
|
||||||
|
} else {
|
||||||
|
unsetenv(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void set_and_print_env(const char *name, const char *value) {
|
||||||
|
// Print New Value
|
||||||
|
DEBUG("Set %s = %s", name, value != NULL ? value : "(unset)");
|
||||||
|
|
||||||
|
// Set The Value
|
||||||
|
setenv_safe(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
// Safe execvpe()
|
// Safe execvpe()
|
||||||
|
#define handle_environmental_variable(var) \
|
||||||
|
{ \
|
||||||
|
const char *full_var = is_arm_component ? "MCPI_ARM_" var : "MCPI_NATIVE_" var; \
|
||||||
|
const char *var_value = getenv(full_var); \
|
||||||
|
set_and_print_env(var, var_value); \
|
||||||
|
}
|
||||||
|
void setup_exec_environment(int is_arm_component) {
|
||||||
|
for_each_special_environmental_variable(handle_environmental_variable);
|
||||||
|
}
|
||||||
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
|
__attribute__((noreturn)) void safe_execvpe(const char *const argv[], const char *const envp[]) {
|
||||||
|
// Log
|
||||||
|
DEBUG("Running Command:");
|
||||||
|
for (int i = 0; argv[i] != NULL; i++) {
|
||||||
|
DEBUG(" %s", argv[i]);
|
||||||
|
}
|
||||||
|
// Run
|
||||||
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
|
int ret = execvpe(argv[0], (char *const *) argv, (char *const *) envp);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
|
if (errno == ENOENT && strcmp(argv[0], "qemu-qrm")) {
|
||||||
|
ERR("Unable to find QEMU! To install on Ubuntu/Debian, run \"sudo apt install qemu-user\". To install on Arch Linux, run \"sudo pacman -Sy qemu-user\".");
|
||||||
|
}
|
||||||
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
|
ERR("Unable To Execute Program: %s: %s", argv[0], strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
IMPOSSIBLE();
|
IMPOSSIBLE();
|
||||||
@ -34,36 +70,8 @@ char *get_binary_directory() {
|
|||||||
return exe;
|
return exe;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe execvpe() Relative To Binary
|
|
||||||
__attribute__((noreturn)) void safe_execvpe_relative_to_binary(const char *const argv[], const char *const envp[]) {
|
|
||||||
// Get Binary Directory
|
|
||||||
char *binary_directory = get_binary_directory();
|
|
||||||
// Create Full Path
|
|
||||||
char *full_path = NULL;
|
|
||||||
safe_asprintf(&full_path, "%s/%s", binary_directory, argv[0]);
|
|
||||||
// Free Binary Directory
|
|
||||||
free(binary_directory);
|
|
||||||
|
|
||||||
// Build New argv
|
|
||||||
int argc;
|
|
||||||
for (argc = 0; argv[argc] != NULL; argc++);
|
|
||||||
const char *new_argv[argc + 1];
|
|
||||||
for (int i = 1; i < argc; i++) {
|
|
||||||
new_argv[i] = argv[i];
|
|
||||||
}
|
|
||||||
new_argv[0] = full_path;
|
|
||||||
new_argv[argc] = NULL;
|
|
||||||
// Run
|
|
||||||
safe_execvpe(new_argv, envp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get MCPI Directory
|
|
||||||
char *get_mcpi_directory() {
|
|
||||||
return getenv("MCPI_DIRECTORY");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run Command And Get Output
|
// Run Command And Get Output
|
||||||
char *run_command(const char *const command[], int *return_code) {
|
char *run_command(const char *const command[], int *exit_status) {
|
||||||
// Store Output
|
// Store Output
|
||||||
int output_pipe[2];
|
int output_pipe[2];
|
||||||
safe_pipe2(output_pipe, 0);
|
safe_pipe2(output_pipe, 0);
|
||||||
@ -79,25 +87,21 @@ char *run_command(const char *const command[], int *return_code) {
|
|||||||
close(output_pipe[0]);
|
close(output_pipe[0]);
|
||||||
close(output_pipe[1]);
|
close(output_pipe[1]);
|
||||||
|
|
||||||
// Close stderr (But Not In Debug Mode)
|
// Setup Environment
|
||||||
const char *is_debug = getenv("MCPI_DEBUG");
|
setup_exec_environment(0);
|
||||||
if (is_debug == NULL || strlen(is_debug) < 1) {
|
|
||||||
int null_fd = open("/dev/null", O_WRONLY);
|
|
||||||
dup2(null_fd, STDERR_FILENO);
|
|
||||||
close(null_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
safe_execvpe(command, (const char *const *) environ);
|
safe_execvpe(command, (const char *const *) environ);
|
||||||
} else {
|
} else {
|
||||||
// Parent Process
|
// Parent Process
|
||||||
|
track_child(ret);
|
||||||
|
|
||||||
// Read stdout
|
// Read stdout
|
||||||
close(output_pipe[1]);
|
close(output_pipe[1]);
|
||||||
char *output = NULL;
|
char *output = NULL;
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
char buf[BUFFER_SIZE];
|
char buf[BUFFER_SIZE];
|
||||||
size_t bytes_read = 0;
|
ssize_t bytes_read = 0;
|
||||||
while ((bytes_read = read(output_pipe[0], (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */)) > 0) {
|
while ((bytes_read = read(output_pipe[0], (void *) buf, BUFFER_SIZE - 1 /* Account For NULL-Terminator */)) > 0) {
|
||||||
buf[bytes_read] = '\0';
|
buf[bytes_read] = '\0';
|
||||||
string_append(&output, "%s", buf);
|
string_append(&output, "%s", buf);
|
||||||
@ -107,9 +111,59 @@ char *run_command(const char *const command[], int *return_code) {
|
|||||||
// Get Return Code
|
// Get Return Code
|
||||||
int status;
|
int status;
|
||||||
waitpid(ret, &status, 0);
|
waitpid(ret, &status, 0);
|
||||||
*return_code = WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
|
untrack_child(ret);
|
||||||
|
if (exit_status != NULL) {
|
||||||
|
*exit_status = status;
|
||||||
|
}
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Exit Status String
|
||||||
|
void get_exit_status_string(int status, char **out) {
|
||||||
|
if (out != NULL) {
|
||||||
|
*out =NULL;
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
safe_asprintf(out, ": Exit Code: %i", WEXITSTATUS(status));
|
||||||
|
} else if (WIFSIGNALED(status)) {
|
||||||
|
safe_asprintf(out, ": Signal: %i%s", WTERMSIG(status), WCOREDUMP(status) ? " (Core Dumped)" : "");
|
||||||
|
} else {
|
||||||
|
safe_asprintf(out, ": Terminated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track Children
|
||||||
|
#define MAX_CHILDREN 128
|
||||||
|
static pid_t children[MAX_CHILDREN] = { 0 };
|
||||||
|
static pthread_mutex_t children_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
void track_child(pid_t pid) {
|
||||||
|
pthread_mutex_lock(&children_lock);
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
if (children[i] == 0) {
|
||||||
|
children[i] = pid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&children_lock);
|
||||||
|
}
|
||||||
|
void untrack_child(pid_t pid) {
|
||||||
|
pthread_mutex_lock(&children_lock);
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
if (children[i] == pid) {
|
||||||
|
children[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&children_lock);
|
||||||
|
}
|
||||||
|
void murder_children() {
|
||||||
|
pthread_mutex_lock(&children_lock);
|
||||||
|
for (int i = 0; i < MAX_CHILDREN; i++) {
|
||||||
|
if (children[i] != 0) {
|
||||||
|
kill(children[i], SIGTERM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&children_lock);
|
||||||
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
#include <iconv.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <libreborn/string.h>
|
#include <libreborn/string.h>
|
||||||
|
|
||||||
// Sanitize String
|
// Sanitize String
|
||||||
#define MINIMUM_SAFE_CHARACTER 32
|
|
||||||
#define MAXIMUM_SAFE_CHARACTER 126
|
|
||||||
void sanitize_string(char **str, int max_length, unsigned int allow_newlines) {
|
void sanitize_string(char **str, int max_length, unsigned int allow_newlines) {
|
||||||
// Store Message Length
|
// Store Message Length
|
||||||
int length = strlen(*str);
|
int length = strlen(*str);
|
||||||
@ -12,17 +13,134 @@ void sanitize_string(char **str, int max_length, unsigned int allow_newlines) {
|
|||||||
length = max_length;
|
length = max_length;
|
||||||
}
|
}
|
||||||
// Loop Through Message
|
// Loop Through Message
|
||||||
|
if (!allow_newlines) {
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
if (allow_newlines && ((*str)[i] == '\n' || (*str)[i] == '\r')) {
|
if ((*str)[i] == '\n' || (*str)[i] == '\r') {
|
||||||
continue;
|
// Replace Newline
|
||||||
}
|
(*str)[i] = ' ';
|
||||||
unsigned char c = (unsigned char) (*str)[i];
|
|
||||||
if (c < MINIMUM_SAFE_CHARACTER || c > MAXIMUM_SAFE_CHARACTER) {
|
|
||||||
// Replace Illegal Character
|
|
||||||
(*str)[i] = '?';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minecraft-Flavored CP437
|
||||||
|
void safe_iconv(iconv_t cd, char *input, size_t input_size, char *output, size_t output_size) {
|
||||||
|
iconv(cd, &input, &input_size, &output, &output_size);
|
||||||
|
}
|
||||||
|
#define CP437_CHARACTERS 256
|
||||||
|
static const char *cp437_characters_map[CP437_CHARACTERS] = {
|
||||||
|
"\0", "☺", "☻", "♥", "♦", "♣", "♠", "•", "◘", "○", "\n", "♂", "♀", "\r", "♫", "☼",
|
||||||
|
"►", "◄", "↕", "‼", "¶", "§", "▬", "↨", "↑", "↓", "→", "←", "∟", "↔", "▲", "▼",
|
||||||
|
" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
|
||||||
|
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O",
|
||||||
|
"P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_",
|
||||||
|
"`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
|
||||||
|
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "⌂",
|
||||||
|
"Ç", "ü", "é", "â", "ä", "à", "å", "ç", "ê", "ë", "è", "ï", "î", "ì", "Ä", "Å",
|
||||||
|
"É", "æ", "Æ", "ô", "ö", "ò", "û", "ù", "ÿ", "Ö", "Ü", "¢", "£", "¥", "₧", "ƒ",
|
||||||
|
"á", "í", "ó", "ú", "ñ", "Ñ", "ª", "º", "¿", "⌐", "¬", "½", "¼", "¡", "«", "»",
|
||||||
|
"░", "▒", "▓", "│", "┤", "╡", "╢", "╖", "╕", "╣", "║", "╗", "╝", "╜", "╛", "┐",
|
||||||
|
"└", "┴", "┬", "├", "─", "┼", "╞", "╟", "╚", "╔", "╩", "╦", "╠", "═", "╬", "╧",
|
||||||
|
"╨", "╤", "╥", "╙", "╘", "╒", "╓", "╫", "╪", "┘", "┌", "█", "▄", "▌", "▐", "▀",
|
||||||
|
"α", "ß", "Γ", "π", "Σ", "σ", "µ", "τ", "Φ", "Θ", "Ω", "δ", "∞", "φ", "ε", "∩",
|
||||||
|
"≡", "±", "≥", "≤", "⌠", "⌡", "÷", "≈", "°", "∙", "·", "√", "ⁿ", "²", "■", "©"
|
||||||
|
};
|
||||||
|
static uint32_t *get_cp437_characters_codepoint_map() {
|
||||||
|
static uint32_t map[CP437_CHARACTERS];
|
||||||
|
static int is_setup = 0;
|
||||||
|
if (!is_setup) {
|
||||||
|
// Build Map
|
||||||
|
iconv_t cd = iconv_open("UTF-32LE", "UTF-8");
|
||||||
|
if (cd != (iconv_t) -1) {
|
||||||
|
size_t str_size = 4;
|
||||||
|
uint32_t *str = (uint32_t *) malloc(str_size);
|
||||||
|
ALLOC_CHECK(str);
|
||||||
|
for (int i = 0; i < CP437_CHARACTERS; i++) {
|
||||||
|
// Convert to UTF-32, Then Extract Codepoint
|
||||||
|
safe_iconv(cd, (char *) cp437_characters_map[i], strlen(cp437_characters_map[i]), (char *) str, str_size);
|
||||||
|
// Extract
|
||||||
|
map[i] = str[0];
|
||||||
|
}
|
||||||
|
// Free
|
||||||
|
free(str);
|
||||||
|
iconv_close(cd);
|
||||||
|
} else {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
is_setup = 1;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
char *to_cp437(const char *input) {
|
||||||
|
// Convert To UTF-32 For Easier Parsing
|
||||||
|
size_t in_size = strlen(input);
|
||||||
|
size_t utf32_str_size = in_size * 4;
|
||||||
|
size_t real_utf32_str_size = utf32_str_size + 4 /* NULL-terminator */;
|
||||||
|
uint32_t *utf32_str = (uint32_t *) malloc(real_utf32_str_size);
|
||||||
|
ALLOC_CHECK(utf32_str);
|
||||||
|
memset(utf32_str, 0, real_utf32_str_size);
|
||||||
|
iconv_t cd = iconv_open("UTF-32LE", "UTF-8");
|
||||||
|
if (cd != (iconv_t) -1) {
|
||||||
|
safe_iconv(cd, (char *) input, in_size, (char *) utf32_str, utf32_str_size);
|
||||||
|
iconv_close(cd);
|
||||||
|
} else {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
// Allocate String
|
||||||
|
size_t cp437_str_size;
|
||||||
|
for (cp437_str_size = 0; utf32_str[cp437_str_size] != 0; cp437_str_size++);
|
||||||
|
size_t real_cp437_str_size = cp437_str_size + 1 /* NULL-terminator */;
|
||||||
|
char *cp437_str = (char *) malloc(real_cp437_str_size);
|
||||||
|
ALLOC_CHECK(cp437_str);
|
||||||
|
memset(cp437_str, 0, real_cp437_str_size);
|
||||||
|
// Handle Characters
|
||||||
|
for (size_t i = 0; utf32_str[i] != 0; i++) {
|
||||||
|
uint32_t codepoint = utf32_str[i];
|
||||||
|
for (int j = 0; j < CP437_CHARACTERS; j++) {
|
||||||
|
uint32_t test_codepoint = get_cp437_characters_codepoint_map()[j];
|
||||||
|
if (codepoint == test_codepoint) {
|
||||||
|
cp437_str[i] = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cp437_str[i] == '\0') {
|
||||||
|
cp437_str[i] = '?';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Free
|
||||||
|
free(utf32_str);
|
||||||
|
// Return
|
||||||
|
return cp437_str;
|
||||||
|
}
|
||||||
|
char *from_cp437(const char *input) {
|
||||||
|
// Convert To UTF-32 For Easier Parsing
|
||||||
|
size_t in_size = strlen(input);
|
||||||
|
size_t utf32_str_size = in_size * 4;
|
||||||
|
size_t real_utf32_str_size = utf32_str_size + 4 /* NULL-terminator */;
|
||||||
|
uint32_t *utf32_str = (uint32_t *) malloc(real_utf32_str_size);
|
||||||
|
ALLOC_CHECK(utf32_str);
|
||||||
|
memset(utf32_str, 0, real_utf32_str_size);
|
||||||
|
// Handle Characters
|
||||||
|
for (size_t i = 0; input[i] != '\0'; i++) {
|
||||||
|
utf32_str[i] = get_cp437_characters_codepoint_map()[(uint32_t) input[i]];
|
||||||
|
}
|
||||||
|
// Convert To UTF-8
|
||||||
|
size_t out_size = utf32_str_size;
|
||||||
|
size_t real_out_size = utf32_str_size + 1 /* NULL-terminator */;
|
||||||
|
char *output = (char *) malloc(real_out_size);
|
||||||
|
ALLOC_CHECK(output);
|
||||||
|
memset(output, 0, real_out_size);
|
||||||
|
iconv_t cd = iconv_open("UTF-8", "UTF-32LE");
|
||||||
|
if (cd != (iconv_t) -1) {
|
||||||
|
safe_iconv(cd, (char *) utf32_str, utf32_str_size, output, out_size);
|
||||||
|
iconv_close(cd);
|
||||||
|
} else {
|
||||||
|
IMPOSSIBLE();
|
||||||
|
}
|
||||||
|
// Return
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
// Starts With
|
// Starts With
|
||||||
int starts_with(const char *str, const char *prefix) {
|
int starts_with(const char *str, const char *prefix) {
|
||||||
|
@ -6,3 +6,19 @@ void safe_pipe2(int pipefd[2], int flags) {
|
|||||||
ERR("Unable To Create Pipe: %s", strerror(errno));
|
ERR("Unable To Create Pipe: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check If Two Percentages Are Different Enough To Be Logged
|
||||||
|
#define SIGNIFICANT_PROGRESS 5
|
||||||
|
int is_progress_difference_significant(int32_t new_val, int32_t old_val) {
|
||||||
|
if (new_val != old_val) {
|
||||||
|
if (new_val == -1 || old_val == -1) {
|
||||||
|
return 1;
|
||||||
|
} else if (new_val == 0 || new_val == 100) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return new_val - old_val >= SIGNIFICANT_PROGRESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,29 +1,28 @@
|
|||||||
project(media-layer)
|
project(media-layer)
|
||||||
|
|
||||||
# Check Options
|
|
||||||
if(MCPI_USE_MEDIA_LAYER_PROXY)
|
|
||||||
if(MCPI_HEADLESS_MODE)
|
|
||||||
message(FATAL_ERROR "Headless Mode With Media Layer Proxy Configuration Is Redundant")
|
|
||||||
endif()
|
|
||||||
if(MCPI_BUILD_MODE STREQUAL "both")
|
|
||||||
message(FATAL_ERROR "Media Layer Proxy Is Redundant When Building ARM And Native Components In The Same Build")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add Headers
|
# Add Headers
|
||||||
add_library(media-layer-headers INTERFACE)
|
add_library(media-layer-headers INTERFACE)
|
||||||
target_include_directories(media-layer-headers INTERFACE include)
|
target_include_directories(
|
||||||
|
media-layer-headers
|
||||||
|
INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${MCPI_SDK_INCLUDE_DIR}/media-layer>"
|
||||||
|
)
|
||||||
|
# SDK
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS media-layer-headers EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
install(DIRECTORY "include/" DESTINATION "${MCPI_SDK_INCLUDE_DIR}/media-layer")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add Extras
|
||||||
|
add_subdirectory(extras)
|
||||||
|
|
||||||
# Add Core
|
# Add Core
|
||||||
|
if((BUILD_NATIVE_COMPONENTS AND MCPI_USE_MEDIA_LAYER_PROXY) OR (BUILD_ARM_COMPONENTS AND NOT MCPI_USE_MEDIA_LAYER_PROXY))
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Add Proxy
|
# Add Proxy
|
||||||
if(MCPI_USE_MEDIA_LAYER_PROXY)
|
if(MCPI_USE_MEDIA_LAYER_PROXY)
|
||||||
add_subdirectory(proxy)
|
add_subdirectory(proxy)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Add Stubs
|
|
||||||
add_subdirectory(stubs)
|
|
||||||
|
|
||||||
# Add Extras
|
|
||||||
add_subdirectory(extras)
|
|
||||||
|
@ -1,37 +1,32 @@
|
|||||||
project(media-layer-core)
|
project(media-layer-core)
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
add_subdirectory(dependencies)
|
||||||
|
|
||||||
|
# OpenGL
|
||||||
|
add_subdirectory(gles)
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
set(CORE_SRC src/base.cpp src/media.c src/screenshot.c) # SDL Re-Implementation Using GLFW
|
set(CORE_SRC src/base.cpp src/media.c $<TARGET_OBJECTS:media-layer-extras>) # SDL Re-Implementation Using GLFW
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
list(APPEND CORE_SRC src/audio/api.cpp src/audio/engine.c src/audio/file.cpp)
|
list(APPEND CORE_SRC src/audio/api.cpp src/audio/engine.c src/audio/file.cpp)
|
||||||
|
else()
|
||||||
|
list(APPEND CORE_SRC src/audio/stubs.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
if(MCPI_USE_MEDIA_LAYER_PROXY AND BUILD_NATIVE_COMPONENTS)
|
|
||||||
# Building Native Components
|
|
||||||
add_library(media-layer-core OBJECT ${CORE_SRC}) # Dependencies Are Setup Later
|
|
||||||
elseif(NOT MCPI_USE_MEDIA_LAYER_PROXY AND BUILD_ARM_COMPONENTS)
|
|
||||||
# Building ARM Components
|
|
||||||
add_library(media-layer-core SHARED ${CORE_SRC}) # Dependencies Are Setup Later
|
add_library(media-layer-core SHARED ${CORE_SRC}) # Dependencies Are Setup Later
|
||||||
# Install
|
# Install
|
||||||
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
|
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS media-layer-core EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Configure Media Layer Core If Built
|
|
||||||
if(TARGET media-layer-core)
|
|
||||||
# Link
|
# Link
|
||||||
target_link_libraries(media-layer-core media-layer-headers reborn-util pthread dl)
|
target_link_libraries(media-layer-core PUBLIC media-layer-headers PUBLIC reborn-util PUBLIC GLESv1_CM PUBLIC dl)
|
||||||
if(NOT MCPI_HEADLESS_MODE)
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
# Find FreeImage
|
|
||||||
find_library(FREEIMAGE_LIBRARY NAMES freeimage libfreeimage.so.3 REQUIRED)
|
|
||||||
# OpenAL
|
# OpenAL
|
||||||
find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
|
find_library(OPENAL_LIBRARY NAMES openal REQUIRED)
|
||||||
# Link
|
# Link
|
||||||
target_link_libraries(media-layer-core "${FREEIMAGE_LIBRARY}" "${OPENAL_LIBRARY}" m GLESv1_CM glfw)
|
target_link_libraries(media-layer-core PRIVATE "${OPENAL_LIBRARY}" PRIVATE m PRIVATE glfw)
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Add Symlinks So MCPI Can Locate Libraries
|
|
||||||
if(BUILD_ARM_COMPONENTS)
|
|
||||||
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libSDL-1.2.so.0")
|
|
||||||
endif()
|
endif()
|
||||||
|
6
media-layer/core/dependencies/CMakeLists.txt
Normal file
6
media-layer/core/dependencies/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
project(media-layer-core-dependencies)
|
||||||
|
|
||||||
|
# GLFW
|
||||||
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
|
add_subdirectory(glfw)
|
||||||
|
endif()
|
31
media-layer/core/dependencies/glfw/CMakeLists.txt
Normal file
31
media-layer/core/dependencies/glfw/CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
project(glfw)
|
||||||
|
|
||||||
|
# Silence Warnings
|
||||||
|
add_compile_options(-w)
|
||||||
|
|
||||||
|
## GLFW
|
||||||
|
|
||||||
|
# Download
|
||||||
|
set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_EXAMPLES FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_TESTS FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_DOCS FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_INSTALL FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_WIN32 FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_COCOA FALSE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_X11 TRUE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_BUILD_WAYLAND TRUE CACHE BOOL "" FORCE)
|
||||||
|
set(GLFW_LIBRARY_TYPE "SHARED" CACHE BOOL "" FORCE)
|
||||||
|
add_subdirectory(src EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
# Ensure Build
|
||||||
|
add_custom_target(glfw-build ALL DEPENDS glfw)
|
||||||
|
|
||||||
|
# Install
|
||||||
|
install(TARGETS glfw DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS glfw EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# License
|
||||||
|
install(FILES src/LICENSE.md DESTINATION "${MCPI_LEGAL_DIR}/glfw")
|
1
media-layer/core/dependencies/glfw/src
Submodule
1
media-layer/core/dependencies/glfw/src
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c18851f52ec9704eb06464058a600845ec1eada1
|
32
media-layer/core/gles/CMakeLists.txt
Normal file
32
media-layer/core/gles/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
project(media-layer-gles)
|
||||||
|
|
||||||
|
# Build
|
||||||
|
if(MCPI_HEADLESS_MODE)
|
||||||
|
# Stubs For Headless Mode
|
||||||
|
set(GLES_SRC src/stubs.c)
|
||||||
|
elseif(MCPI_USE_GLES1_COMPATIBILITY_LAYER)
|
||||||
|
# GLESv1_CM Compatibility Layer
|
||||||
|
set(GLES_SRC src/compatibility-layer/state.c src/compatibility-layer/passthrough.c src/compatibility-layer/matrix.c src/compatibility-layer/draw.c src/compatibility-layer/buffer.cpp)
|
||||||
|
else()
|
||||||
|
# Passthrough To glfwGetProcAddress()
|
||||||
|
set(GLES_SRC src/passthrough.c)
|
||||||
|
endif()
|
||||||
|
add_library(GLESv1_CM SHARED ${GLES_SRC})
|
||||||
|
if(NOT MCPI_HEADLESS_MODE)
|
||||||
|
target_link_libraries(GLESv1_CM PRIVATE glfw PUBLIC reborn-util PRIVATE dl PRIVATE m)
|
||||||
|
# Shaders
|
||||||
|
if(MCPI_USE_GLES1_COMPATIBILITY_LAYER)
|
||||||
|
embed_resource(GLESv1_CM src/compatibility-layer/shaders/main.vert)
|
||||||
|
embed_resource(GLESv1_CM src/compatibility-layer/shaders/main.frag)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Common
|
||||||
|
target_link_libraries(GLESv1_CM PUBLIC media-layer-headers)
|
||||||
|
set_target_properties(GLESv1_CM PROPERTIES SOVERSION "1")
|
||||||
|
# Install
|
||||||
|
install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}")
|
||||||
|
# SDK
|
||||||
|
if(BUILD_ARM_COMPONENTS)
|
||||||
|
install(TARGETS GLESv1_CM EXPORT sdk DESTINATION "${MCPI_SDK_LIB_DIR}")
|
||||||
|
endif()
|
35
media-layer/core/gles/src/compatibility-layer/buffer.cpp
Normal file
35
media-layer/core/gles/src/compatibility-layer/buffer.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include "../passthrough.h"
|
||||||
|
|
||||||
|
// Store Buffers
|
||||||
|
static std::unordered_map<GLuint, GLuint> buffers_map;
|
||||||
|
// Get Buffer
|
||||||
|
GL_FUNC(glGenBuffers, void, (GLsizei n, GLuint *buffers));
|
||||||
|
static GLuint get_real_buffer(GLuint fake_buffer) {
|
||||||
|
if (buffers_map.count(fake_buffer) > 0) {
|
||||||
|
return buffers_map[fake_buffer];
|
||||||
|
} else {
|
||||||
|
GLuint new_buffer;
|
||||||
|
real_glGenBuffers()(1, &new_buffer);
|
||||||
|
buffers_map[fake_buffer] = new_buffer;
|
||||||
|
return get_real_buffer(fake_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Fake Buffers To Real Buffers When Calling GL
|
||||||
|
GL_FUNC(glBindBuffer, void, (GLenum target, GLuint buffer));
|
||||||
|
void glBindBuffer(GLenum target, GLuint buffer) {
|
||||||
|
real_glBindBuffer()(target, get_real_buffer(buffer));
|
||||||
|
}
|
||||||
|
GL_FUNC(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers));
|
||||||
|
void glDeleteBuffers(GLsizei n, const GLuint *buffers) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (buffers_map.count(buffers[i]) > 0) {
|
||||||
|
real_glDeleteBuffers()(1, &buffers_map[i]);
|
||||||
|
buffers_map.erase(buffers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
204
media-layer/core/gles/src/compatibility-layer/draw.c
Normal file
204
media-layer/core/gles/src/compatibility-layer/draw.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "state.h"
|
||||||
|
#include "../passthrough.h"
|
||||||
|
|
||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
// Shaders
|
||||||
|
#define REAL_GL_FRAGMENT_SHADER 0x8b30
|
||||||
|
#define REAL_GL_VERTEX_SHADER 0x8b31
|
||||||
|
#define REAL_GL_INFO_LOG_LENGTH 0x8b84
|
||||||
|
#define REAL_GL_COMPILE_STATUS 0x8b81
|
||||||
|
GL_FUNC(glUseProgram, void, (GLuint program));
|
||||||
|
GL_FUNC(glGetUniformLocation, GLint, (GLuint program, const GLchar *name));
|
||||||
|
GL_FUNC(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value));
|
||||||
|
GL_FUNC(glUniform1i, void, (GLint location, GLint v0));
|
||||||
|
GL_FUNC(glUniform1f, void, (GLint location, GLfloat v0));
|
||||||
|
GL_FUNC(glUniform4f, void, (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3));
|
||||||
|
GL_FUNC(glGetAttribLocation, GLint, (GLuint program, const GLchar *name));
|
||||||
|
GL_FUNC(glEnableVertexAttribArray, void, (GLuint index));
|
||||||
|
GL_FUNC(glDisableVertexAttribArray, void, (GLuint index));
|
||||||
|
GL_FUNC(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer));
|
||||||
|
GL_FUNC(glVertexAttrib3f, void, (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2));
|
||||||
|
GL_FUNC(glVertexAttrib4f, void, (GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3));
|
||||||
|
GL_FUNC(glCreateShader, GLuint, (GLenum type));
|
||||||
|
GL_FUNC(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const *string, const GLint *length));
|
||||||
|
GL_FUNC(glCompileShader, void, (GLuint shader));
|
||||||
|
GL_FUNC(glCreateProgram, GLuint, ());
|
||||||
|
GL_FUNC(glAttachShader, void, (GLuint program, GLuint shader));
|
||||||
|
GL_FUNC(glLinkProgram, void, (GLuint program));
|
||||||
|
GL_FUNC(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint *params));
|
||||||
|
GL_FUNC(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog));
|
||||||
|
|
||||||
|
// Compile Shader
|
||||||
|
static void log_shader(GLuint shader, const char *name) {
|
||||||
|
// Log
|
||||||
|
GLint log_length = 0;
|
||||||
|
real_glGetShaderiv()(shader, REAL_GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
GLchar *log = malloc(log_length * sizeof (GLchar));
|
||||||
|
ALLOC_CHECK(log);
|
||||||
|
real_glGetShaderInfoLog()(shader, log_length, &log_length, log);
|
||||||
|
if (log_length > 0) {
|
||||||
|
if (log_length > 1 && log[log_length - 1] == '\n') {
|
||||||
|
log[log_length - 1] = '\0';
|
||||||
|
}
|
||||||
|
DEBUG("%s Shader Compile Log: %s", name, log);
|
||||||
|
}
|
||||||
|
free(log);
|
||||||
|
|
||||||
|
// Check Status
|
||||||
|
GLint is_compiled = 0;
|
||||||
|
real_glGetShaderiv()(shader, REAL_GL_COMPILE_STATUS, &is_compiled);
|
||||||
|
if (!is_compiled) {
|
||||||
|
ERR("Failed To Compile %s Shader", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static GLuint compile_shader(const char *vertex_shader_text, const int vertex_shader_length, const char *fragment_shader_text, const int fragment_shader_length) {
|
||||||
|
// Vertex Shader
|
||||||
|
const GLuint vertex_shader = real_glCreateShader()(REAL_GL_VERTEX_SHADER);
|
||||||
|
real_glShaderSource()(vertex_shader, 1, &vertex_shader_text, &vertex_shader_length);
|
||||||
|
real_glCompileShader()(vertex_shader);
|
||||||
|
log_shader(vertex_shader, "Vertex");
|
||||||
|
|
||||||
|
// Fragment Shader
|
||||||
|
const GLuint fragment_shader = real_glCreateShader()(REAL_GL_FRAGMENT_SHADER);
|
||||||
|
real_glShaderSource()(fragment_shader, 1, &fragment_shader_text, &fragment_shader_length);
|
||||||
|
real_glCompileShader()(fragment_shader);
|
||||||
|
log_shader(fragment_shader, "Fragment");
|
||||||
|
|
||||||
|
// Link
|
||||||
|
GLuint program = real_glCreateProgram()();
|
||||||
|
real_glAttachShader()(program, vertex_shader);
|
||||||
|
real_glAttachShader()(program, fragment_shader);
|
||||||
|
real_glLinkProgram()(program);
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shader
|
||||||
|
extern unsigned char main_vert[];
|
||||||
|
extern size_t main_vert_len;
|
||||||
|
extern unsigned char main_frag[];
|
||||||
|
extern size_t main_frag_len;
|
||||||
|
static GLuint get_shader() {
|
||||||
|
static GLuint program = 0;
|
||||||
|
if (program == 0) {
|
||||||
|
program = compile_shader((const char *) main_vert, main_vert_len, (const char *) main_frag, main_frag_len);
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shader Switching
|
||||||
|
static void use_shader(GLuint program) {
|
||||||
|
static GLuint current_program = 0;
|
||||||
|
if (current_program != program) {
|
||||||
|
real_glUseProgram()(program);
|
||||||
|
current_program = program;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array Pointer Drawing
|
||||||
|
GL_FUNC(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count));
|
||||||
|
#define lazy_uniform(name) \
|
||||||
|
static GLint name##_handle = -1; \
|
||||||
|
if (name##_handle == -1) { \
|
||||||
|
name##_handle = real_glGetUniformLocation()(program, #name); \
|
||||||
|
}
|
||||||
|
void glDrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||||
|
// Verify
|
||||||
|
if (gl_state.array_pointers.vertex.size != 3 || !gl_state.array_pointers.vertex.enabled || gl_state.array_pointers.vertex.type != GL_FLOAT) {
|
||||||
|
ERR("Unsupported Vertex Conifguration");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Mode
|
||||||
|
int use_color_pointer = gl_state.array_pointers.color.enabled;
|
||||||
|
if (use_color_pointer && (gl_state.array_pointers.color.size != 4 || gl_state.array_pointers.color.type != GL_UNSIGNED_BYTE)) {
|
||||||
|
ERR("Unsupported Color Conifguration");
|
||||||
|
}
|
||||||
|
int use_texture = gl_state.texture_2d && gl_state.array_pointers.tex_coord.enabled;
|
||||||
|
if (use_texture && (gl_state.array_pointers.tex_coord.size != 2 || gl_state.array_pointers.tex_coord.type != GL_FLOAT)) {
|
||||||
|
ERR("Unsupported Texture Conifguration");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Shader
|
||||||
|
GLuint program = get_shader();
|
||||||
|
use_shader(program);
|
||||||
|
|
||||||
|
// Projection Matrix
|
||||||
|
lazy_uniform(u_projection);
|
||||||
|
matrix_t *p = &gl_state.matrix_stacks.projection.stack[gl_state.matrix_stacks.projection.i];
|
||||||
|
real_glUniformMatrix4fv()(u_projection_handle, 1, 0, (GLfloat *) &p->data[0][0]);
|
||||||
|
|
||||||
|
// Model View Matrix
|
||||||
|
lazy_uniform(u_model_view);
|
||||||
|
p = &gl_state.matrix_stacks.model_view.stack[gl_state.matrix_stacks.model_view.i];
|
||||||
|
real_glUniformMatrix4fv()(u_model_view_handle, 1, 0, (GLfloat *) &p->data[0][0]);
|
||||||
|
|
||||||
|
// Has Texture
|
||||||
|
lazy_uniform(u_has_texture); \
|
||||||
|
real_glUniform1i()(u_has_texture_handle, use_texture); \
|
||||||
|
|
||||||
|
// Texture Matrix
|
||||||
|
lazy_uniform(u_texture);
|
||||||
|
p = &gl_state.matrix_stacks.texture.stack[gl_state.matrix_stacks.texture.i];
|
||||||
|
real_glUniformMatrix4fv()(u_texture_handle, 1, 0, (GLfloat *) &p->data[0][0]);
|
||||||
|
|
||||||
|
// Texture Unit
|
||||||
|
lazy_uniform(u_texture_unit);
|
||||||
|
real_glUniform1i()(u_texture_unit_handle, 0);
|
||||||
|
|
||||||
|
// Alpha Test
|
||||||
|
lazy_uniform(u_alpha_test);
|
||||||
|
real_glUniform1i()(u_alpha_test_handle, gl_state.alpha_test);
|
||||||
|
|
||||||
|
// Color
|
||||||
|
GLint a_color_handle = real_glGetAttribLocation()(program, "a_color");
|
||||||
|
if (use_color_pointer) {
|
||||||
|
real_glVertexAttribPointer()(a_color_handle, gl_state.array_pointers.color.size, gl_state.array_pointers.color.type, 1, gl_state.array_pointers.color.stride, gl_state.array_pointers.color.pointer);
|
||||||
|
real_glEnableVertexAttribArray()(a_color_handle);
|
||||||
|
} else {
|
||||||
|
real_glVertexAttrib4f()(a_color_handle, gl_state.color.red, gl_state.color.green, gl_state.color.blue, gl_state.color.alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fog
|
||||||
|
lazy_uniform(u_fog);
|
||||||
|
real_glUniform1i()(u_fog_handle, gl_state.fog.enabled);
|
||||||
|
if (gl_state.fog.enabled) {
|
||||||
|
lazy_uniform(u_fog_color);
|
||||||
|
real_glUniform4f()(u_fog_color_handle, gl_state.fog.color[0], gl_state.fog.color[1], gl_state.fog.color[2], gl_state.fog.color[3]);
|
||||||
|
lazy_uniform(u_fog_is_linear);
|
||||||
|
real_glUniform1i()(u_fog_is_linear_handle, gl_state.fog.mode == GL_LINEAR);
|
||||||
|
lazy_uniform(u_fog_start);
|
||||||
|
real_glUniform1f()(u_fog_start_handle, gl_state.fog.start);
|
||||||
|
lazy_uniform(u_fog_end);
|
||||||
|
real_glUniform1f()(u_fog_end_handle, gl_state.fog.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertices
|
||||||
|
GLint a_vertex_coords_handle = real_glGetAttribLocation()(program, "a_vertex_coords");
|
||||||
|
real_glVertexAttribPointer()(a_vertex_coords_handle, gl_state.array_pointers.vertex.size, gl_state.array_pointers.vertex.type, 0, gl_state.array_pointers.vertex.stride, gl_state.array_pointers.vertex.pointer);
|
||||||
|
real_glEnableVertexAttribArray()(a_vertex_coords_handle);
|
||||||
|
|
||||||
|
// Texture Coordinates
|
||||||
|
GLint a_texture_coords_handle = real_glGetAttribLocation()(program, "a_texture_coords");
|
||||||
|
if (use_texture) {
|
||||||
|
real_glVertexAttribPointer()(a_texture_coords_handle, gl_state.array_pointers.tex_coord.size, gl_state.array_pointers.tex_coord.type, 0, gl_state.array_pointers.tex_coord.stride, gl_state.array_pointers.tex_coord.pointer);
|
||||||
|
real_glEnableVertexAttribArray()(a_texture_coords_handle);
|
||||||
|
} else {
|
||||||
|
real_glVertexAttrib3f()(a_texture_coords_handle, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw
|
||||||
|
real_glDrawArrays()(mode, first, count);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
if (use_color_pointer) {
|
||||||
|
real_glDisableVertexAttribArray()(a_color_handle);
|
||||||
|
}
|
||||||
|
real_glDisableVertexAttribArray()(a_vertex_coords_handle);
|
||||||
|
if (use_texture) {
|
||||||
|
real_glDisableVertexAttribArray()(a_texture_coords_handle);
|
||||||
|
}
|
||||||
|
}
|
131
media-layer/core/gles/src/compatibility-layer/matrix.c
Normal file
131
media-layer/core/gles/src/compatibility-layer/matrix.c
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
// Matrix Common
|
||||||
|
static void matrix_copy(matrix_t *src, matrix_t *dst) {
|
||||||
|
memcpy((void *) dst->data, (void *) src->data, MATRIX_DATA_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity Matrix
|
||||||
|
static matrix_t identity_matrix = {
|
||||||
|
.data = {
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
{0, 1, 0, 0},
|
||||||
|
{0, 0, 1, 0},
|
||||||
|
{0, 0, 0, 1}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static void init_matrix_stack(matrix_stack_t *stack) {
|
||||||
|
matrix_copy(&identity_matrix, &stack->stack[0]);
|
||||||
|
}
|
||||||
|
__attribute__((constructor)) static void init_matrix_stacks() {
|
||||||
|
init_matrix_stack(&gl_state.matrix_stacks.model_view);
|
||||||
|
init_matrix_stack(&gl_state.matrix_stacks.projection);
|
||||||
|
init_matrix_stack(&gl_state.matrix_stacks.texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix Mode
|
||||||
|
static matrix_stack_t *get_matrix_stack() {
|
||||||
|
switch (gl_state.matrix_stacks.mode) {
|
||||||
|
case GL_MODELVIEW: {
|
||||||
|
return &gl_state.matrix_stacks.model_view;
|
||||||
|
}
|
||||||
|
case GL_PROJECTION: {
|
||||||
|
return &gl_state.matrix_stacks.projection;
|
||||||
|
}
|
||||||
|
case GL_TEXTURE: {
|
||||||
|
return &gl_state.matrix_stacks.texture;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ERR("Unsupported Matrix Mode: %i", gl_state.matrix_stacks.mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matrix Functions
|
||||||
|
void glMatrixMode(GLenum mode) {
|
||||||
|
gl_state.matrix_stacks.mode = mode;
|
||||||
|
}
|
||||||
|
void glPopMatrix() {
|
||||||
|
get_matrix_stack()->i--;
|
||||||
|
}
|
||||||
|
void glLoadIdentity() {
|
||||||
|
matrix_stack_t *stack = get_matrix_stack();
|
||||||
|
matrix_copy(&identity_matrix, &stack->stack[stack->i]);
|
||||||
|
}
|
||||||
|
void glPushMatrix() {
|
||||||
|
matrix_stack_t *stack = get_matrix_stack();
|
||||||
|
matrix_copy(&stack->stack[stack->i], &stack->stack[stack->i + 1]);
|
||||||
|
stack->i++;
|
||||||
|
}
|
||||||
|
void glMultMatrixf(const GLfloat *m) {
|
||||||
|
matrix_t new_matrix;
|
||||||
|
matrix_stack_t *stack = get_matrix_stack();
|
||||||
|
matrix_t *current_matrix = &stack->stack[stack->i];
|
||||||
|
for (int x = 0; x < MATRIX_SIZE; x++) {
|
||||||
|
for (int y = 0; y < MATRIX_SIZE; y++) {
|
||||||
|
GLfloat result = 0;
|
||||||
|
for (int i = 0; i < MATRIX_SIZE; i++) {
|
||||||
|
result += (current_matrix->data[i][y] * m[(x * MATRIX_SIZE) + i]);
|
||||||
|
}
|
||||||
|
new_matrix.data[x][y] = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matrix_copy(&new_matrix, current_matrix);
|
||||||
|
}
|
||||||
|
void glScalef(GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
GLfloat m[] = {
|
||||||
|
x, 0, 0, 0,
|
||||||
|
0, y, 0, 0,
|
||||||
|
0, 0, z, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
glMultMatrixf(m);
|
||||||
|
}
|
||||||
|
void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
GLfloat m[] = {
|
||||||
|
1, 0, 0, 0,
|
||||||
|
0, 1, 0, 0,
|
||||||
|
0, 0, 1, 0,
|
||||||
|
x, y, z, 1
|
||||||
|
};
|
||||||
|
glMultMatrixf(m);
|
||||||
|
}
|
||||||
|
void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far) {
|
||||||
|
GLfloat m[] = {
|
||||||
|
(2.f / (right - left)), 0, 0, 0,
|
||||||
|
0, (2.f / (top - bottom)), 0, 0,
|
||||||
|
0, 0, (-2.f / (far - near)), 0,
|
||||||
|
-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((far + near) / (far - near)), 1
|
||||||
|
};
|
||||||
|
glMultMatrixf(m);
|
||||||
|
}
|
||||||
|
#define DEG2RAD (M_PI / 180.f)
|
||||||
|
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
// Normalize
|
||||||
|
GLfloat length = sqrtf((x * x) + (y * y) + (z * z));
|
||||||
|
x /= length;
|
||||||
|
y /= length;
|
||||||
|
z /= length;
|
||||||
|
|
||||||
|
// Values
|
||||||
|
GLfloat angle_radians = angle * DEG2RAD;
|
||||||
|
GLfloat c = cosf(angle_radians);
|
||||||
|
GLfloat s = sinf(angle_radians);
|
||||||
|
GLfloat x2 = x * x;
|
||||||
|
GLfloat y2 = y * y;
|
||||||
|
GLfloat z2 = z * z;
|
||||||
|
|
||||||
|
// Multiply
|
||||||
|
GLfloat m[] = {
|
||||||
|
x2 * (1.f - c) + c, (x * y) * (1.f - c) + (z * s), (x * z) * (1.f - c) - (y * s), 0,
|
||||||
|
(x * y) * (1.f - c) - (z * s), y2 * (1.f - c) + c, (y * z) * (1.f - c) + (x * s), 0,
|
||||||
|
(x * z) * (1.f - c) + (y * s), (y * z) * (1.f - c) - (x * s), z2 * (1.f - c) + c, 0,
|
||||||
|
0, 0, 0, 1.f
|
||||||
|
};
|
||||||
|
glMultMatrixf(m);
|
||||||
|
}
|
9
media-layer/core/gles/src/compatibility-layer/matrix.h
Normal file
9
media-layer/core/gles/src/compatibility-layer/matrix.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
// Matrix Common
|
||||||
|
#define MATRIX_SIZE 4
|
||||||
|
#define MATRIX_DATA_SIZE (sizeof (float) * MATRIX_SIZE * MATRIX_SIZE)
|
||||||
|
// OpenGL Matricies Are Column-Major
|
||||||
|
typedef struct {
|
||||||
|
GLfloat data[MATRIX_SIZE][MATRIX_SIZE];
|
||||||
|
} matrix_t;
|
105
media-layer/core/gles/src/compatibility-layer/passthrough.c
Normal file
105
media-layer/core/gles/src/compatibility-layer/passthrough.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include "../passthrough.h"
|
||||||
|
|
||||||
|
// Simple v1.1 -> v2.0 Passthrough Functions
|
||||||
|
GL_FUNC(glLineWidth, void, (GLfloat width));
|
||||||
|
void glLineWidth(GLfloat width) {
|
||||||
|
real_glLineWidth()(width);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBlendFunc, void, (GLenum sfactor, GLenum dfactor));
|
||||||
|
void glBlendFunc(GLenum sfactor, GLenum dfactor) {
|
||||||
|
real_glBlendFunc()(sfactor, dfactor);
|
||||||
|
}
|
||||||
|
GL_FUNC(glClear, void, (GLbitfield mask));
|
||||||
|
void glClear(GLbitfield mask) {
|
||||||
|
real_glClear()(mask);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage));
|
||||||
|
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {
|
||||||
|
real_glBufferData()(target, size, data, usage);
|
||||||
|
}
|
||||||
|
GL_FUNC(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height));
|
||||||
|
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||||
|
real_glScissor()(x, y, width, height);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexParameteri, void, (GLenum target, GLenum pname, GLint param));
|
||||||
|
void glTexParameteri(GLenum target, GLenum pname, GLint param) {
|
||||||
|
real_glTexParameteri()(target, pname, param);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels));
|
||||||
|
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
|
||||||
|
real_glTexImage2D()(target, level, internalformat, width, height, border, format, type, pixels);
|
||||||
|
}
|
||||||
|
GL_FUNC(glPolygonOffset, void, (GLfloat factor, GLfloat units));
|
||||||
|
void glPolygonOffset(GLfloat factor, GLfloat units) {
|
||||||
|
real_glPolygonOffset()(factor, units);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthRangef, void, (GLclampf near, GLclampf far));
|
||||||
|
void glDepthRangef(GLclampf near, GLclampf far) {
|
||||||
|
real_glDepthRangef()(near, far);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthFunc, void, (GLenum func));
|
||||||
|
void glDepthFunc(GLenum func) {
|
||||||
|
real_glDepthFunc()(func);
|
||||||
|
}
|
||||||
|
GL_FUNC(glClearColor, void, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha));
|
||||||
|
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
|
||||||
|
real_glClearColor()(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthMask, void, (GLboolean flag));
|
||||||
|
void glDepthMask(GLboolean flag) {
|
||||||
|
real_glDepthMask()(flag);
|
||||||
|
}
|
||||||
|
GL_FUNC(glHint, void, (GLenum target, GLenum mode));
|
||||||
|
void glHint(GLenum target, GLenum mode) {
|
||||||
|
if (target != GL_PERSPECTIVE_CORRECTION_HINT) {
|
||||||
|
real_glHint()(target, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GL_FUNC(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha));
|
||||||
|
void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
|
||||||
|
real_glColorMask()(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels));
|
||||||
|
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
|
||||||
|
real_glTexSubImage2D()(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||||
|
}
|
||||||
|
GL_FUNC(glGenTextures, void, (GLsizei n, GLuint *textures));
|
||||||
|
void glGenTextures(GLsizei n, GLuint *textures) {
|
||||||
|
real_glGenTextures()(n, textures);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDeleteTextures, void, (GLsizei n, const GLuint *textures));
|
||||||
|
void glDeleteTextures(GLsizei n, const GLuint *textures) {
|
||||||
|
real_glDeleteTextures()(n, textures);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBindTexture, void, (GLenum target, GLuint texture));
|
||||||
|
void glBindTexture(GLenum target, GLuint texture) {
|
||||||
|
real_glBindTexture()(target, texture);
|
||||||
|
}
|
||||||
|
GL_FUNC(glCullFace, void, (GLenum mode));
|
||||||
|
void glCullFace(GLenum mode) {
|
||||||
|
real_glCullFace()(mode);
|
||||||
|
}
|
||||||
|
GL_FUNC(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height));
|
||||||
|
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||||
|
real_glViewport()(x, y, width, height);
|
||||||
|
}
|
||||||
|
GL_FUNC(glIsEnabled, GLboolean, (GLenum cap));
|
||||||
|
GLboolean glIsEnabled(GLenum cap) {
|
||||||
|
return real_glIsEnabled()(cap);
|
||||||
|
}
|
||||||
|
GL_FUNC(glGetIntegerv, void, (GLenum pname, GLint *data));
|
||||||
|
void glGetIntegerv(GLenum pname, GLint *data) {
|
||||||
|
real_glGetIntegerv()(pname, data);
|
||||||
|
}
|
||||||
|
GL_FUNC(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data));
|
||||||
|
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) {
|
||||||
|
real_glReadPixels()(x, y, width, height, format, type, data);
|
||||||
|
}
|
||||||
|
void glShadeModel(__attribute__((unused)) GLenum mode) {
|
||||||
|
// Do Nothing
|
||||||
|
}
|
||||||
|
void glNormal3f(__attribute__((unused)) GLfloat nx, __attribute__((unused)) GLfloat ny, __attribute__((unused)) GLfloat nz) {
|
||||||
|
// Do Nothing
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
// Texture
|
||||||
|
uniform bool u_has_texture;
|
||||||
|
uniform sampler2D u_texture_unit;
|
||||||
|
// Color
|
||||||
|
varying vec4 v_color;
|
||||||
|
varying vec4 v_texture_pos;
|
||||||
|
// Alpha Test
|
||||||
|
uniform bool u_alpha_test;
|
||||||
|
// Fog
|
||||||
|
uniform bool u_fog;
|
||||||
|
uniform vec4 u_fog_color;
|
||||||
|
uniform bool u_fog_is_linear;
|
||||||
|
uniform float u_fog_start;
|
||||||
|
uniform float u_fog_end;
|
||||||
|
varying vec4 v_fog_eye_position;
|
||||||
|
// Main
|
||||||
|
void main(void) {
|
||||||
|
gl_FragColor = v_color;
|
||||||
|
// Texture
|
||||||
|
if (u_has_texture) {
|
||||||
|
gl_FragColor *= texture2D(u_texture_unit, v_texture_pos.xy);
|
||||||
|
}
|
||||||
|
// Alpha Test
|
||||||
|
if (u_alpha_test && gl_FragColor.a <= 0.1) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
// Fog
|
||||||
|
if (u_fog) {
|
||||||
|
float fog_factor;
|
||||||
|
if (u_fog_is_linear) {
|
||||||
|
fog_factor = (u_fog_end - length(v_fog_eye_position)) / (u_fog_end - u_fog_start);
|
||||||
|
} else {
|
||||||
|
fog_factor = exp(-u_fog_start * length(v_fog_eye_position));
|
||||||
|
}
|
||||||
|
gl_FragColor = mix(gl_FragColor, u_fog_color, 1.0 - clamp(fog_factor, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
// Matrices
|
||||||
|
uniform mat4 u_projection;
|
||||||
|
uniform mat4 u_model_view;
|
||||||
|
uniform mat4 u_texture;
|
||||||
|
// Texture
|
||||||
|
attribute vec3 a_vertex_coords;
|
||||||
|
attribute vec2 a_texture_coords;
|
||||||
|
varying vec4 v_texture_pos;
|
||||||
|
// Color
|
||||||
|
attribute vec4 a_color;
|
||||||
|
varying vec4 v_color;
|
||||||
|
// Fog
|
||||||
|
varying vec4 v_fog_eye_position;
|
||||||
|
// Main
|
||||||
|
void main(void) {
|
||||||
|
v_texture_pos = u_texture * vec4(a_texture_coords.xy, 0.0, 1.0);
|
||||||
|
gl_Position = u_projection * u_model_view * vec4(a_vertex_coords.xyz, 1.0);
|
||||||
|
v_color = a_color;
|
||||||
|
v_fog_eye_position = u_model_view * vec4(a_vertex_coords.xyz, 1.0);
|
||||||
|
}
|
178
media-layer/core/gles/src/compatibility-layer/state.c
Normal file
178
media-layer/core/gles/src/compatibility-layer/state.c
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
#include "state.h"
|
||||||
|
#include "../passthrough.h"
|
||||||
|
|
||||||
|
// GL State
|
||||||
|
gl_state_t gl_state = {
|
||||||
|
.color = {
|
||||||
|
.red = 1,
|
||||||
|
.green = 1,
|
||||||
|
.blue = 1,
|
||||||
|
.alpha = 1
|
||||||
|
},
|
||||||
|
.matrix_stacks = {
|
||||||
|
.mode = GL_MODELVIEW
|
||||||
|
},
|
||||||
|
.alpha_test = 0,
|
||||||
|
.texture_2d = 0,
|
||||||
|
.fog = {
|
||||||
|
.enabled = 0,
|
||||||
|
.mode = GL_LINEAR,
|
||||||
|
.color = {0, 0, 0, 0},
|
||||||
|
.start = 0,
|
||||||
|
.end = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Change Color
|
||||||
|
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
|
||||||
|
gl_state.color.red = red;
|
||||||
|
gl_state.color.green = green;
|
||||||
|
gl_state.color.blue = blue;
|
||||||
|
gl_state.color.alpha = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Array Pointer Storage
|
||||||
|
#define ARRAY_POINTER_FUNC(func, name) \
|
||||||
|
void func(GLint size, GLenum type, GLsizei stride, const void *pointer) { \
|
||||||
|
gl_state.array_pointers.name.size = size; \
|
||||||
|
gl_state.array_pointers.name.type = type; \
|
||||||
|
gl_state.array_pointers.name.stride = stride; \
|
||||||
|
gl_state.array_pointers.name.pointer = pointer; \
|
||||||
|
}
|
||||||
|
ARRAY_POINTER_FUNC(glVertexPointer, vertex)
|
||||||
|
ARRAY_POINTER_FUNC(glColorPointer, color)
|
||||||
|
ARRAY_POINTER_FUNC(glTexCoordPointer, tex_coord)
|
||||||
|
static array_pointer_t *get_array_pointer(GLenum array) {
|
||||||
|
switch (array) {
|
||||||
|
case GL_VERTEX_ARRAY: {
|
||||||
|
return &gl_state.array_pointers.vertex;
|
||||||
|
}
|
||||||
|
case GL_COLOR_ARRAY: {
|
||||||
|
return &gl_state.array_pointers.color;
|
||||||
|
}
|
||||||
|
case GL_TEXTURE_COORD_ARRAY: {
|
||||||
|
return &gl_state.array_pointers.tex_coord;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
ERR("Unsupported Array Pointer: %i", array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void glEnableClientState(GLenum array) {
|
||||||
|
get_array_pointer(array)->enabled = 1;
|
||||||
|
}
|
||||||
|
void glDisableClientState(GLenum array) {
|
||||||
|
get_array_pointer(array)->enabled = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable/Disable State
|
||||||
|
GL_FUNC(glEnable, void, (GLenum cap));
|
||||||
|
void glEnable(GLenum cap) {
|
||||||
|
switch (cap) {
|
||||||
|
case GL_ALPHA_TEST: {
|
||||||
|
gl_state.alpha_test = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_TEXTURE_2D: {
|
||||||
|
gl_state.texture_2d = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_COLOR_MATERIAL: {
|
||||||
|
// Ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_FOG: {
|
||||||
|
gl_state.fog.enabled = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
real_glEnable()(cap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GL_FUNC(glDisable, void, (GLenum cap));
|
||||||
|
void glDisable(GLenum cap) {
|
||||||
|
switch (cap) {
|
||||||
|
case GL_ALPHA_TEST: {
|
||||||
|
gl_state.alpha_test = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_TEXTURE_2D: {
|
||||||
|
gl_state.texture_2d = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_COLOR_MATERIAL: {
|
||||||
|
// Ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_FOG: {
|
||||||
|
gl_state.fog.enabled = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
real_glDisable()(cap);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void glAlphaFunc(GLenum func, GLclampf ref) {
|
||||||
|
if (func != GL_GREATER && ref != 0.1f) {
|
||||||
|
ERR("Unsupported Alpha Function");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fog
|
||||||
|
#define UNSUPPORTED_FOG() ERR("Unsupported Fog Configuration")
|
||||||
|
void glFogfv(GLenum pname, const GLfloat *params) {
|
||||||
|
if (pname == GL_FOG_COLOR) {
|
||||||
|
memcpy((void *) gl_state.fog.color, params, sizeof (gl_state.fog.color));
|
||||||
|
} else {
|
||||||
|
UNSUPPORTED_FOG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void glFogx(GLenum pname, GLfixed param) {
|
||||||
|
if (pname == GL_FOG_MODE && (param == GL_LINEAR || param == GL_EXP)) {
|
||||||
|
gl_state.fog.mode = param;
|
||||||
|
} else {
|
||||||
|
UNSUPPORTED_FOG();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void glFogf(GLenum pname, GLfloat param) {
|
||||||
|
switch (pname) {
|
||||||
|
case GL_FOG_DENSITY:
|
||||||
|
case GL_FOG_START: {
|
||||||
|
gl_state.fog.start = param;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_FOG_END: {
|
||||||
|
gl_state.fog.end = param;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
UNSUPPORTED_FOG();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Matrix Data
|
||||||
|
GL_FUNC(glGetFloatv, void, (GLenum pname, GLfloat *params));
|
||||||
|
void glGetFloatv(GLenum pname, GLfloat *params) {
|
||||||
|
switch (pname) {
|
||||||
|
case GL_MODELVIEW_MATRIX: {
|
||||||
|
memcpy((void *) params, gl_state.matrix_stacks.model_view.stack[gl_state.matrix_stacks.model_view.i].data, MATRIX_DATA_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_PROJECTION_MATRIX: {
|
||||||
|
memcpy((void *) params, gl_state.matrix_stacks.projection.stack[gl_state.matrix_stacks.projection.i].data, MATRIX_DATA_SIZE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
real_glGetFloatv()(pname, params);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
media-layer/core/gles/src/compatibility-layer/state.h
Normal file
50
media-layer/core/gles/src/compatibility-layer/state.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include "matrix.h"
|
||||||
|
|
||||||
|
// Matrix Data
|
||||||
|
#define MATRIX_STACK_DEPTH 256
|
||||||
|
typedef struct {
|
||||||
|
matrix_t stack[MATRIX_STACK_DEPTH];
|
||||||
|
unsigned int i;
|
||||||
|
} matrix_stack_t;
|
||||||
|
|
||||||
|
// Array Pointer Storage
|
||||||
|
typedef struct {
|
||||||
|
GLboolean enabled;
|
||||||
|
GLint size;
|
||||||
|
GLenum type;
|
||||||
|
GLsizei stride;
|
||||||
|
const void *pointer;
|
||||||
|
} array_pointer_t;
|
||||||
|
|
||||||
|
// GL State
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
GLfloat red;
|
||||||
|
GLfloat green;
|
||||||
|
GLfloat blue;
|
||||||
|
GLfloat alpha;
|
||||||
|
} color;
|
||||||
|
struct {
|
||||||
|
GLenum mode;
|
||||||
|
matrix_stack_t model_view;
|
||||||
|
matrix_stack_t projection;
|
||||||
|
matrix_stack_t texture;
|
||||||
|
} matrix_stacks;
|
||||||
|
struct {
|
||||||
|
array_pointer_t vertex;
|
||||||
|
array_pointer_t color;
|
||||||
|
array_pointer_t tex_coord;
|
||||||
|
} array_pointers;
|
||||||
|
GLboolean alpha_test;
|
||||||
|
GLboolean texture_2d;
|
||||||
|
struct {
|
||||||
|
GLboolean enabled;
|
||||||
|
GLfixed mode;
|
||||||
|
GLfloat color[4];
|
||||||
|
GLfloat start;
|
||||||
|
GLfloat end;
|
||||||
|
} fog;
|
||||||
|
} gl_state_t;
|
||||||
|
extern gl_state_t gl_state;
|
204
media-layer/core/gles/src/passthrough.c
Normal file
204
media-layer/core/gles/src/passthrough.c
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include <GLES/gl.h>
|
||||||
|
|
||||||
|
#include "passthrough.h"
|
||||||
|
|
||||||
|
GL_FUNC(glFogfv, void, (GLenum pname, const GLfloat *params));
|
||||||
|
void glFogfv(GLenum pname, const GLfloat *params) {
|
||||||
|
real_glFogfv()(pname, params);
|
||||||
|
}
|
||||||
|
GL_FUNC(glVertexPointer, void, (GLint size, GLenum type, GLsizei stride, const void *pointer));
|
||||||
|
void glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
|
||||||
|
real_glVertexPointer()(size, type, stride, pointer);
|
||||||
|
}
|
||||||
|
GL_FUNC(glLineWidth, void, (GLfloat width));
|
||||||
|
void glLineWidth(GLfloat width) {
|
||||||
|
real_glLineWidth()(width);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBlendFunc, void, (GLenum sfactor, GLenum dfactor));
|
||||||
|
void glBlendFunc(GLenum sfactor, GLenum dfactor) {
|
||||||
|
real_glBlendFunc()(sfactor, dfactor);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count));
|
||||||
|
void glDrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||||
|
real_glDrawArrays()(mode, first, count);
|
||||||
|
}
|
||||||
|
GL_FUNC(glColor4f, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha));
|
||||||
|
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
|
||||||
|
real_glColor4f()(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
GL_FUNC(glClear, void, (GLbitfield mask));
|
||||||
|
void glClear(GLbitfield mask) {
|
||||||
|
real_glClear()(mask);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBufferData, void, (GLenum target, GLsizeiptr size, const void *data, GLenum usage));
|
||||||
|
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {
|
||||||
|
real_glBufferData()(target, size, data, usage);
|
||||||
|
}
|
||||||
|
GL_FUNC(glFogx, void, (GLenum pname, GLfixed param));
|
||||||
|
void glFogx(GLenum pname, GLfixed param) {
|
||||||
|
real_glFogx()(pname, param);
|
||||||
|
}
|
||||||
|
GL_FUNC(glFogf, void, (GLenum pname, GLfloat param));
|
||||||
|
void glFogf(GLenum pname, GLfloat param) {
|
||||||
|
real_glFogf()(pname, param);
|
||||||
|
}
|
||||||
|
GL_FUNC(glMatrixMode, void, (GLenum mode));
|
||||||
|
void glMatrixMode(GLenum mode) {
|
||||||
|
real_glMatrixMode()(mode);
|
||||||
|
}
|
||||||
|
GL_FUNC(glColorPointer, void, (GLint size, GLenum type, GLsizei stride, const void *pointer));
|
||||||
|
void glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
|
||||||
|
real_glColorPointer()(size, type, stride, pointer);
|
||||||
|
}
|
||||||
|
GL_FUNC(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height));
|
||||||
|
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||||
|
real_glScissor()(x, y, width, height);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexParameteri, void, (GLenum target, GLenum pname, GLint param));
|
||||||
|
void glTexParameteri(GLenum target, GLenum pname, GLint param) {
|
||||||
|
real_glTexParameteri()(target, pname, param);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels));
|
||||||
|
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
|
||||||
|
real_glTexImage2D()(target, level, internalformat, width, height, border, format, type, pixels);
|
||||||
|
}
|
||||||
|
GL_FUNC(glEnable, void, (GLenum cap));
|
||||||
|
void glEnable(GLenum cap) {
|
||||||
|
real_glEnable()(cap);
|
||||||
|
}
|
||||||
|
GL_FUNC(glEnableClientState, void, (GLenum array));
|
||||||
|
void glEnableClientState(GLenum array) {
|
||||||
|
real_glEnableClientState()(array);
|
||||||
|
}
|
||||||
|
GL_FUNC(glPolygonOffset, void, (GLfloat factor, GLfloat units));
|
||||||
|
void glPolygonOffset(GLfloat factor, GLfloat units) {
|
||||||
|
real_glPolygonOffset()(factor, units);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDisableClientState, void, (GLenum array));
|
||||||
|
void glDisableClientState(GLenum array) {
|
||||||
|
real_glDisableClientState()(array);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthRangef, void, (GLclampf near, GLclampf far));
|
||||||
|
void glDepthRangef(GLclampf near, GLclampf far) {
|
||||||
|
real_glDepthRangef()(near, far);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthFunc, void, (GLenum func));
|
||||||
|
void glDepthFunc(GLenum func) {
|
||||||
|
real_glDepthFunc()(func);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBindBuffer, void, (GLenum target, GLuint buffer));
|
||||||
|
void glBindBuffer(GLenum target, GLuint buffer) {
|
||||||
|
real_glBindBuffer()(target, buffer);
|
||||||
|
}
|
||||||
|
GL_FUNC(glClearColor, void, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha));
|
||||||
|
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
|
||||||
|
real_glClearColor()(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
GL_FUNC(glPopMatrix, void, ());
|
||||||
|
void glPopMatrix() {
|
||||||
|
real_glPopMatrix()();
|
||||||
|
}
|
||||||
|
GL_FUNC(glLoadIdentity, void, ());
|
||||||
|
void glLoadIdentity() {
|
||||||
|
real_glLoadIdentity()();
|
||||||
|
}
|
||||||
|
GL_FUNC(glScalef, void, (GLfloat x, GLfloat y, GLfloat z));
|
||||||
|
void glScalef(GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
real_glScalef()(x, y, z);
|
||||||
|
}
|
||||||
|
GL_FUNC(glPushMatrix, void, ());
|
||||||
|
void glPushMatrix() {
|
||||||
|
real_glPushMatrix()();
|
||||||
|
}
|
||||||
|
GL_FUNC(glDepthMask, void, (GLboolean flag));
|
||||||
|
void glDepthMask(GLboolean flag) {
|
||||||
|
real_glDepthMask()(flag);
|
||||||
|
}
|
||||||
|
GL_FUNC(glHint, void, (GLenum target, GLenum mode));
|
||||||
|
void glHint(GLenum target, GLenum mode) {
|
||||||
|
real_glHint()(target, mode);
|
||||||
|
}
|
||||||
|
GL_FUNC(glMultMatrixf, void, (const GLfloat *m));
|
||||||
|
void glMultMatrixf(const GLfloat *m) {
|
||||||
|
real_glMultMatrixf()(m);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexCoordPointer, void, (GLint size, GLenum type, GLsizei stride, const void *pointer));
|
||||||
|
void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
|
||||||
|
real_glTexCoordPointer()(size, type, stride, pointer);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDeleteBuffers, void, (GLsizei n, const GLuint *buffers));
|
||||||
|
void glDeleteBuffers(GLsizei n, const GLuint *buffers) {
|
||||||
|
real_glDeleteBuffers()(n, buffers);
|
||||||
|
}
|
||||||
|
GL_FUNC(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha));
|
||||||
|
void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
|
||||||
|
real_glColorMask()(red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels));
|
||||||
|
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
|
||||||
|
real_glTexSubImage2D()(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
||||||
|
}
|
||||||
|
GL_FUNC(glGenTextures, void, (GLsizei n, GLuint *textures));
|
||||||
|
void glGenTextures(GLsizei n, GLuint *textures) {
|
||||||
|
real_glGenTextures()(n, textures);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDeleteTextures, void, (GLsizei n, const GLuint *textures));
|
||||||
|
void glDeleteTextures(GLsizei n, const GLuint *textures) {
|
||||||
|
real_glDeleteTextures()(n, textures);
|
||||||
|
}
|
||||||
|
GL_FUNC(glAlphaFunc, void, (GLenum func, GLclampf ref));
|
||||||
|
void glAlphaFunc(GLenum func, GLclampf ref) {
|
||||||
|
real_glAlphaFunc()(func, ref);
|
||||||
|
}
|
||||||
|
GL_FUNC(glGetFloatv, void, (GLenum pname, GLfloat *params));
|
||||||
|
void glGetFloatv(GLenum pname, GLfloat *params) {
|
||||||
|
real_glGetFloatv()(pname, params);
|
||||||
|
}
|
||||||
|
GL_FUNC(glBindTexture, void, (GLenum target, GLuint texture));
|
||||||
|
void glBindTexture(GLenum target, GLuint texture) {
|
||||||
|
real_glBindTexture()(target, texture);
|
||||||
|
}
|
||||||
|
GL_FUNC(glTranslatef, void, (GLfloat x, GLfloat y, GLfloat z));
|
||||||
|
void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
real_glTranslatef()(x, y, z);
|
||||||
|
}
|
||||||
|
GL_FUNC(glShadeModel, void, (GLenum mode));
|
||||||
|
void glShadeModel(GLenum mode) {
|
||||||
|
real_glShadeModel()(mode);
|
||||||
|
}
|
||||||
|
GL_FUNC(glOrthof, void, (GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far));
|
||||||
|
void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far) {
|
||||||
|
real_glOrthof()(left, right, bottom, top, near, far);
|
||||||
|
}
|
||||||
|
GL_FUNC(glDisable, void, (GLenum cap));
|
||||||
|
void glDisable(GLenum cap) {
|
||||||
|
real_glDisable()(cap);
|
||||||
|
}
|
||||||
|
GL_FUNC(glCullFace, void, (GLenum mode));
|
||||||
|
void glCullFace(GLenum mode) {
|
||||||
|
real_glCullFace()(mode);
|
||||||
|
}
|
||||||
|
GL_FUNC(glRotatef, void, (GLfloat angle, GLfloat x, GLfloat y, GLfloat z));
|
||||||
|
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
|
||||||
|
real_glRotatef()(angle, x, y, z);
|
||||||
|
}
|
||||||
|
GL_FUNC(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height));
|
||||||
|
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||||
|
real_glViewport()(x, y, width, height);
|
||||||
|
}
|
||||||
|
GL_FUNC(glNormal3f, void, (GLfloat nx, GLfloat ny, GLfloat nz));
|
||||||
|
void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) {
|
||||||
|
real_glNormal3f()(nx, ny, nz);
|
||||||
|
}
|
||||||
|
GL_FUNC(glIsEnabled, GLboolean, (GLenum cap));
|
||||||
|
GLboolean glIsEnabled(GLenum cap) {
|
||||||
|
return real_glIsEnabled()(cap);
|
||||||
|
}
|
||||||
|
GL_FUNC(glGetIntegerv, void, (GLenum pname, GLint *data));
|
||||||
|
void glGetIntegerv(GLenum pname, GLint *data) {
|
||||||
|
real_glGetIntegerv()(pname, data);
|
||||||
|
}
|
||||||
|
GL_FUNC(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data));
|
||||||
|
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) {
|
||||||
|
real_glReadPixels()(x, y, width, height, format, type, data);
|
||||||
|
}
|
19
media-layer/core/gles/src/passthrough.h
Normal file
19
media-layer/core/gles/src/passthrough.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#define GLFW_INCLUDE_NONE
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include <libreborn/libreborn.h>
|
||||||
|
|
||||||
|
// Load GL Function
|
||||||
|
#define GL_FUNC(name, return_type, args) \
|
||||||
|
typedef return_type (*real_##name##_t)args; \
|
||||||
|
\
|
||||||
|
static real_##name##_t real_##name() { \
|
||||||
|
static real_##name##_t func = NULL; \
|
||||||
|
if (!func) { \
|
||||||
|
func = (real_##name##_t) glfwGetProcAddress(#name); \
|
||||||
|
if (!func) { \
|
||||||
|
ERR("Error Resolving GL Symbol: " #name ": %s", dlerror()); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
return func; \
|
||||||
|
}
|
@ -45,7 +45,11 @@ void glDepthRangef(GLclampf near, GLclampf far) {
|
|||||||
}
|
}
|
||||||
void glDepthFunc(GLenum func) {
|
void glDepthFunc(GLenum func) {
|
||||||
}
|
}
|
||||||
|
static GLuint current_buffer = 0;
|
||||||
void glBindBuffer(GLenum target, GLuint buffer) {
|
void glBindBuffer(GLenum target, GLuint buffer) {
|
||||||
|
if (target == GL_ARRAY_BUFFER) {
|
||||||
|
current_buffer = buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
|
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
|
||||||
}
|
}
|
||||||
@ -72,14 +76,48 @@ void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha
|
|||||||
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
|
void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
|
||||||
}
|
}
|
||||||
void glGenTextures(GLsizei n, GLuint *textures) {
|
void glGenTextures(GLsizei n, GLuint *textures) {
|
||||||
|
static int i = 0;
|
||||||
|
for (int j = 0; j < n; j++) {
|
||||||
|
textures[j] = i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void glDeleteTextures(GLsizei n, const GLuint *textures) {
|
void glDeleteTextures(GLsizei n, const GLuint *textures) {
|
||||||
}
|
}
|
||||||
void glAlphaFunc(GLenum func, GLclampf ref) {
|
void glAlphaFunc(GLenum func, GLclampf ref) {
|
||||||
}
|
}
|
||||||
void glGetFloatv(GLenum pname, GLfloat *params) {
|
void glGetFloatv(GLenum pname, GLfloat *params) {
|
||||||
|
switch (pname) {
|
||||||
|
case GL_MODELVIEW_MATRIX:
|
||||||
|
case GL_PROJECTION_MATRIX: {
|
||||||
|
params[0] = 1;
|
||||||
|
params[1] = 0;
|
||||||
|
params[2] = 0;
|
||||||
|
params[3] = 0;
|
||||||
|
params[4] = 0;
|
||||||
|
params[5] = 1;
|
||||||
|
params[6] = 0;
|
||||||
|
params[7] = 0;
|
||||||
|
params[8] = 0;
|
||||||
|
params[9] = 0;
|
||||||
|
params[10] = 1;
|
||||||
|
params[11] = 0;
|
||||||
|
params[12] = 0;
|
||||||
|
params[13] = 0;
|
||||||
|
params[14] = 0;
|
||||||
|
params[15] = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default: {
|
||||||
|
params[0] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static GLuint current_texture = 0;
|
||||||
void glBindTexture(GLenum target, GLuint texture) {
|
void glBindTexture(GLenum target, GLuint texture) {
|
||||||
|
if (target == GL_TEXTURE_2D) {
|
||||||
|
current_texture = texture;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {
|
void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {
|
||||||
}
|
}
|
||||||
@ -101,6 +139,24 @@ GLboolean glIsEnabled(GLenum cap) {
|
|||||||
return GL_FALSE;
|
return GL_FALSE;
|
||||||
}
|
}
|
||||||
void glGetIntegerv(GLenum pname, GLint *data) {
|
void glGetIntegerv(GLenum pname, GLint *data) {
|
||||||
|
switch (pname) {
|
||||||
|
case GL_TEXTURE_BINDING_2D: {
|
||||||
|
data[0] = current_texture;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_ARRAY_BUFFER_BINDING: {
|
||||||
|
data[0] = current_buffer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_UNPACK_ALIGNMENT: {
|
||||||
|
data[0] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
data[0] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) {
|
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) {
|
||||||
}
|
}
|
@ -8,13 +8,16 @@
|
|||||||
|
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
// Store Audio Sources
|
// Store Audio Sources
|
||||||
static std::vector<ALuint> &get_sources() {
|
|
||||||
static std::vector<ALuint> sources;
|
static std::vector<ALuint> sources;
|
||||||
return sources;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Store Idle Audio Sources
|
||||||
|
#define MAX_IDLE_SOURCES 50
|
||||||
|
static std::vector<ALuint> idle_sources;
|
||||||
|
|
||||||
|
// Error Checking
|
||||||
#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError())
|
#define AL_ERROR_CHECK() AL_ERROR_CHECK_MANUAL(alGetError())
|
||||||
#define AL_ERROR_CHECK_MANUAL(val) \
|
#define AL_ERROR_CHECK_MANUAL(val) \
|
||||||
{ \
|
{ \
|
||||||
@ -24,6 +27,22 @@ static std::vector<ALuint> &get_sources() {
|
|||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete Sources
|
||||||
|
void _media_audio_delete_sources() {
|
||||||
|
if (_media_audio_is_loaded()) {
|
||||||
|
for (ALuint source : idle_sources) {
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
AL_ERROR_CHECK();
|
||||||
|
}
|
||||||
|
for (ALuint source : sources) {
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
AL_ERROR_CHECK();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idle_sources.clear();
|
||||||
|
sources.clear();
|
||||||
|
}
|
||||||
|
|
||||||
// Update Listener
|
// Update Listener
|
||||||
void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
||||||
// Check
|
// Check
|
||||||
@ -43,8 +62,8 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
|||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
|
|
||||||
// Clear Finished Sources
|
// Clear Finished Sources
|
||||||
std::vector<ALuint>::iterator it = get_sources().begin();
|
std::vector<ALuint>::iterator it = sources.begin();
|
||||||
while (it != get_sources().end()) {
|
while (it != sources.end()) {
|
||||||
ALuint source = *it;
|
ALuint source = *it;
|
||||||
bool remove = false;
|
bool remove = false;
|
||||||
// Check
|
// Check
|
||||||
@ -56,16 +75,20 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
|||||||
if (source_state != AL_PLAYING) {
|
if (source_state != AL_PLAYING) {
|
||||||
// Finished Playing
|
// Finished Playing
|
||||||
remove = true;
|
remove = true;
|
||||||
|
if (idle_sources.size() < MAX_IDLE_SOURCES) {
|
||||||
|
idle_sources.push_back(source);
|
||||||
|
} else {
|
||||||
alDeleteSources(1, &source);
|
alDeleteSources(1, &source);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Not A Source
|
// Not A Source
|
||||||
remove = true;
|
remove = true;
|
||||||
}
|
}
|
||||||
// Remove If Needed
|
// Remove If Needed
|
||||||
if (remove) {
|
if (remove) {
|
||||||
it = get_sources().erase(it);
|
it = sources.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@ -73,14 +96,21 @@ void media_audio_update(float volume, float x, float y, float z, float yaw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Play
|
||||||
void media_audio_play(const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui) {
|
void media_audio_play(const char *source, const char *name, float x, float y, float z, float pitch, float volume, int is_ui) {
|
||||||
// Check
|
// Check
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
// Load Sound
|
// Load Sound
|
||||||
ALuint buffer = _media_audio_get_buffer(source, name);
|
ALuint buffer = _media_audio_get_buffer(source, name);
|
||||||
if (volume > 0.0f && buffer) {
|
if (volume > 0.0f && buffer) {
|
||||||
// Create Source
|
// Get Source
|
||||||
ALuint al_source;
|
ALuint al_source;
|
||||||
|
if (idle_sources.size() > 0) {
|
||||||
|
// Use Idle Source
|
||||||
|
al_source = idle_sources.back();
|
||||||
|
idle_sources.pop_back();
|
||||||
|
} else {
|
||||||
|
// Create Source
|
||||||
alGenSources(1, &al_source);
|
alGenSources(1, &al_source);
|
||||||
// Special Out-Of-Memory Handling
|
// Special Out-Of-Memory Handling
|
||||||
{
|
{
|
||||||
@ -91,6 +121,7 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
|||||||
AL_ERROR_CHECK_MANUAL(err);
|
AL_ERROR_CHECK_MANUAL(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set Properties
|
// Set Properties
|
||||||
alSourcef(al_source, AL_PITCH, pitch);
|
alSourcef(al_source, AL_PITCH, pitch);
|
||||||
@ -107,13 +138,13 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
|||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
|
|
||||||
// Set Attenuation
|
// Set Attenuation
|
||||||
alSourcei(al_source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE);
|
alSourcei(al_source, AL_DISTANCE_MODEL, AL_LINEAR_DISTANCE_CLAMPED);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
alSourcef(al_source, AL_MAX_DISTANCE, 16.0f);
|
alSourcef(al_source, AL_MAX_DISTANCE, 22.0f);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
alSourcef(al_source, AL_ROLLOFF_FACTOR, 1.0f);
|
alSourcef(al_source, AL_ROLLOFF_FACTOR, 1.0f);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
alSourcef(al_source, AL_REFERENCE_DISTANCE, 0.0f);
|
alSourcef(al_source, AL_REFERENCE_DISTANCE, 2.0f);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
|
|
||||||
// Set Buffer
|
// Set Buffer
|
||||||
@ -123,7 +154,7 @@ void media_audio_play(const char *source, const char *name, float x, float y, fl
|
|||||||
// Play
|
// Play
|
||||||
alSourcePlay(al_source);
|
alSourcePlay(al_source);
|
||||||
AL_ERROR_CHECK();
|
AL_ERROR_CHECK();
|
||||||
get_sources().push_back(al_source);
|
sources.push_back(al_source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
media-layer/core/src/audio/api.h
Normal file
11
media-layer/core/src/audio/api.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__attribute__((visibility("internal"))) void _media_audio_delete_sources();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
#include "api.h"
|
||||||
|
|
||||||
// Store Device
|
// Store Device
|
||||||
static ALCdevice *device = NULL;
|
static ALCdevice *device = NULL;
|
||||||
@ -22,7 +23,7 @@ void _media_audio_init() {
|
|||||||
// Open Device
|
// Open Device
|
||||||
device = alcOpenDevice(NULL);
|
device = alcOpenDevice(NULL);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
WARN("%s", "Unable To Load Audio Engine");
|
WARN("Unable To Load Audio Engine");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,13 +49,16 @@ void _media_audio_init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
INFO("%s", "Loaded Audio Engine");
|
DEBUG("Loaded Audio Engine");
|
||||||
is_loaded = 1;
|
is_loaded = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// De-Init
|
// De-Init
|
||||||
void _media_audio_cleanup() {
|
void _media_audio_cleanup() {
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
|
// Delete Audio Sources
|
||||||
|
_media_audio_delete_sources();
|
||||||
|
|
||||||
// Delete Audio Buffers
|
// Delete Audio Buffers
|
||||||
_media_audio_delete_buffers();
|
_media_audio_delete_buffers();
|
||||||
|
|
||||||
@ -80,6 +84,6 @@ void _media_audio_cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
// Log
|
||||||
INFO("%s", "Unloaded Audio Engine");
|
DEBUG("Unloaded Audio Engine");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,36 +207,33 @@ static ALuint load_sound(const char *source, const char *name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store Buffers
|
// Store Buffers
|
||||||
static std::unordered_map<std::string, ALuint> &get_buffers() {
|
|
||||||
static std::unordered_map<std::string, ALuint> buffers;
|
static std::unordered_map<std::string, ALuint> buffers;
|
||||||
return buffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Buffer For Sound
|
// Get Buffer For Sound
|
||||||
ALuint _media_audio_get_buffer(const char *source, const char *name) {
|
ALuint _media_audio_get_buffer(const char *source, const char *name) {
|
||||||
// Check
|
// Check
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
if (get_buffers().count(name) > 0) {
|
if (buffers.count(name) > 0) {
|
||||||
// Return
|
// Return
|
||||||
return get_buffers()[name];
|
return buffers[name];
|
||||||
} else {
|
} else {
|
||||||
// Load And Return
|
// Load And Return
|
||||||
get_buffers()[name] = load_sound(source, name);
|
buffers[name] = load_sound(source, name);
|
||||||
return _media_audio_get_buffer(source, name);
|
return _media_audio_get_buffer(source, name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ERR("%s", "Audio Engine Isn't Loaded");
|
ERR("Audio Engine Isn't Loaded");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete Buffers
|
// Delete Buffers
|
||||||
void _media_audio_delete_buffers() {
|
void _media_audio_delete_buffers() {
|
||||||
if (_media_audio_is_loaded()) {
|
if (_media_audio_is_loaded()) {
|
||||||
for (auto it : get_buffers()) {
|
for (auto &it : buffers) {
|
||||||
if (it.second && alIsBuffer(it.second)) {
|
if (it.second && alIsBuffer(it.second)) {
|
||||||
alDeleteBuffers(1, &it.second);
|
alDeleteBuffers(1, &it.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get_buffers().clear();
|
buffers.clear();
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user