This commit is contained in:
TheBrokenRail 2021-06-17 17:32:24 -04:00
parent e252afbe22
commit d0c2b98ca6
150 changed files with 5514 additions and 1715 deletions

View File

@ -1,9 +0,0 @@
/.git
/.gitignore
/Dockerfile*
/Jenkinsfile
/out
/LICENSE
*.md
/debian
/scripts

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
/out
/debian/tmp
/.vscode
/build
/CMakeLists.txt.user
*.autosave

105
CMakeLists.txt Normal file
View File

@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.13.0)
# Specify Options
option(MCPI_USE_MEDIA_LAYER_PROXY "Whether To Enable The Media Layer Proxy" FALSE)
option(MCPI_SERVER_MODE "Server Mode" FALSE)
set(MCPI_BUILD_MODE "both" CACHE STRING "\"arm\" = Build Only Code That Must Be ARN; \"native\" = Build Architecture-Independent Code; \"both\" = Build All Code As ARM")
set_property(CACHE MCPI_BUILD_MODE PROPERTY STRINGS "both" "arm" "native")
# Configure Build Mode
if(MCPI_BUILD_MODE STREQUAL "arm")
set(USE_ARM32_TOOLCHAIN TRUE)
set(BUILD_ARM_COMPONENTS TRUE)
set(BUILD_NATIVE_COMPONENTS FALSE)
elseif(MCPI_BUILD_MODE STREQUAL "native")
set(USE_ARM32_TOOLCHAIN FALSE)
set(BUILD_ARM_COMPONENTS FALSE)
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()
message(FATAL_ERROR "Invalid Mode")
endif()
# Use Clang By Default
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
# Setup ARM Cross Compilation
if(USE_ARM32_TOOLCHAIN)
include(cmake/armhf-toolchain.cmake)
endif()
# Use LLD When Using Clang
if(CMAKE_C_COMPILER STREQUAL "clang")
add_link_options("-fuse-ld=lld")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld") # Fix try_compile()
endif()
# Utility Functions
include(cmake/util.cmake)
# Specify Variant Name
set(MCPI_VARIANT_NAME "minecraft-pi-reborn")
if(MCPI_SERVER_MODE)
set(MCPI_VARIANT_NAME "${MCPI_VARIANT_NAME}-server")
else()
set(MCPI_VARIANT_NAME "${MCPI_VARIANT_NAME}-client")
endif()
# Specify Installation Paths
set(MCPI_INSTALL_DIR "opt/${MCPI_VARIANT_NAME}")
set(MCPI_LIB_DIR "${MCPI_INSTALL_DIR}/lib")
set(MCPI_FALLBACK_LIB_DIR "${MCPI_INSTALL_DIR}/fallback-lib")
# Optimizations
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options(-O3)
else()
add_compile_options(-g)
add_definitions(-DDEBUG)
endif()
# Start Project
project(minecraft-pi-reborn)
# Specify Default Installation Prefix
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "" FORCE)
endif()
# Buld LibPNG + ZLib + Download Minecraft: Pi Edition
if(BUILD_ARM_COMPONENTS)
add_subdirectory(dependencies)
endif()
# Warnings
add_compile_options(-Wall -Wextra -Werror)
add_link_options(-Wl,--no-undefined)
add_definitions(-D_GNU_SOURCE)
# Specify Constants
if(MCPI_SERVER_MODE)
add_definitions(-DMCPI_SERVER_MODE)
endif()
# Build libreborn
add_subdirectory(libreborn)
# Build Media Layer
add_subdirectory(media-layer)
# Build Launcher
if(BUILD_NATIVE_COMPONENTS)
add_subdirectory(launcher)
endif()
# Build Mods
if(BUILD_ARM_COMPONENTS)
add_subdirectory(mods)
endif()

View File

@ -1,3 +0,0 @@
FROM ubuntu:focal
RUN apt-get update && apt-get install -y docker.io rsync

View File

@ -1,52 +0,0 @@
# Runtime Base Environment
FROM arm32v7/debian:bullseye-slim AS runtime-base
ENV DEBIAN_FRONTEND noninteractive
RUN \
# Install Runtime Dependencies
apt-get update && \
apt-get install -y --no-install-recommends tini libgles1 libx11-6 zlib1g libfreeimage3 libglfw3 xinput libxfixes3 gosu tk && \
rm -rf /var/lib/apt/lists/*
# Compile Environment
FROM runtime-base AS build
ENV DEBIAN_FRONTEND noninteractive
RUN \
# Install Dependencies
apt-get update && \
apt-get install -y --no-install-recommends libgles-dev libx11-dev libxrandr-dev libsdl1.2-dev gcc g++ libc-dev make cmake zlib1g-dev git wget ca-certificates libfreeimage-dev libglfw3-dev xinput libxfixes-dev && \
rm -rf /var/lib/apt/lists/*
# Add Build Scripts
ADD ./build /app/build
WORKDIR /app
RUN \
# Download MCPI
./build/download-minecraft-pi.sh && \
# Build LibPNG12
./build/build-libpng12.sh
# Add Code
ADD . /app
# Build Mods
RUN ./build/build-mods.sh
# Runtime Environment
FROM runtime-base AS runtime
# Setup /home Permissions
RUN \
mkdir -p /home && \
chmod -R a+rw /home
# Copy Build
COPY --from=build /app/minecraft-pi /app/minecraft-pi
WORKDIR /app/minecraft-pi
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["./run.sh"]

View File

@ -1,3 +0,0 @@
FROM thebrokenrail/minecraft-pi-reborn:client
ENV MCPI_MODE=server

51
Jenkinsfile vendored
View File

@ -1,36 +1,33 @@
pipeline {
agent {
dockerfile {
filename 'Dockerfile.build'
args '-v /var/run/docker.sock:/var/run/docker.sock'
}
}
agent none
stages {
stage('Install QEMU') {
stage('Build (Debian Bullseye)') {
agent {
docker {
image 'debian:bullseye'
}
}
steps {
sh 'docker run --rm --privileged multiarch/qemu-user-static --reset -p yes'
}
}
stage('Build') {
steps {
sh 'DOCKER_BUILD_OPTIONS="--no-cache" ./scripts/build.sh'
}
}
stage('Publish') {
steps {
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 push thebrokenrail/minecraft-pi-reborn'
}
}
stage('Package') {
steps {
sh './scripts/package.sh'
sh './scripts/ci/run.sh'
}
post {
success {
archiveArtifacts artifacts: 'out/**', fingerprint: true
archiveArtifacts artifacts: 'out/*.deb', fingerprint: true
}
}
}
stage('Build (Debian Buster)') {
agent {
docker {
image 'debian:buster'
}
}
steps {
sh './scripts/ci/run.sh'
}
post {
success {
archiveArtifacts artifacts: 'out/*.deb', fingerprint: true
}
}
}

View File

@ -5,17 +5,5 @@
# Minecraft: Pi Edition: Reborn
Minecraft: Pi Edition Modding Project
## Installation
### Option A: Pi-Apps (Raspberry Pi Only)
[![Pi-Apps](https://github.com/Botspot/pi-apps/blob/master/icons/badge.png?raw=true)](https://github.com/Botspot/pi-apps)
### Option B: Manual Installation
[View Manual Installation](docs/INSTALL.md)
## Documentation
- [View Overriding Assets](docs/OVERRIDING_ASSETS.md)
- [View Troubleshooting](docs/TROUBLESHOOTING.md)
- [View Dedicated Server](docs/DEDICATED_SERVER.md)
- [View Modding](docs/MODDING.md)
- [View Credits](docs/CREDITS.md)
[View Documentation](docs/README.md)

1
VERSION Normal file
View File

@ -0,0 +1 @@
2.0.0

View File

@ -1,18 +0,0 @@
#!/bin/sh
set -e
git clone --depth 1 https://git.code.sf.net/p/libpng/code libpng -b libpng12
cd libpng
./configure
make -j$(nproc)
mkdir -p ../minecraft-pi/lib
cp -L .libs/libpng12.so.0 ../minecraft-pi/lib
cd ../
rm -rf libpng

View File

@ -1,25 +0,0 @@
#!/bin/sh
set -e
cd launcher
mkdir build
cd build
cmake ..
make -j$(nproc)
make install DESTDIR=../../minecraft-pi
cd ../../
cd mods
mkdir build
cd build
cmake ..
make -j$(nproc)
make install DESTDIR=../../minecraft-pi
cd ../../

View File

@ -1,8 +0,0 @@
#!/bin/sh
set -e
URL="https://www.minecraft.net/content/dam/minecraft/edition-pi/minecraft-pi-0.1.1.tar.gz"
mkdir minecraft-pi
wget -O - "${URL}" | tar -xz --strip-components 1 -C minecraft-pi

View File

@ -0,0 +1,13 @@
# Compile For ARM
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64_be" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "armv8b" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "armv8l")
# Force 32-Bit Compile
add_compile_options("-m32")
elseif(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "arm")
# Use ARM Cross-Compiler
set(TARGET "arm-linux-gnueabihf")
set(CMAKE_C_COMPILER "${TARGET}-gcc")
set(CMAKE_CXX_COMPILER "${TARGET}-g++")
set(CMAKE_FIND_ROOT_PATH "/usr/${TARGET}" "/usr/lib/${TARGET}")
endif()
set(CMAKE_SYSTEM_NAME "Linux")
set(CMAKE_SYSTEM_PROCESSOR "arm")

20
cmake/util.cmake Normal file
View File

@ -0,0 +1,20 @@
# Symlink Function
function(install_symlink target link)
install(CODE "\
# Prepare\n \
set(file \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${link}\")\n \
\
# Create Directory\n \
get_filename_component(dir \"\${file}\" DIRECTORY)\n \
file(MAKE_DIRECTORY \${dir})\n \
\
# 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()

7
debian/client-arm vendored Normal file
View File

@ -0,0 +1,7 @@
Package: minecraft-pi-reborn-client
Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: armhf
Depends: zenity, libgles1, libglfw3 | libglfw3-wayland, libfreeimage3

7
debian/client-x86_64 vendored Normal file
View File

@ -0,0 +1,7 @@
Package: minecraft-pi-reborn-client
Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: amd64
Depends: zenity, libgles1, libglfw3 | libglfw3-wayland, libfreeimage3, libc6-armhf-cross, libstdc++6-armhf-cross, qemu-user-static

View File

@ -1,6 +0,0 @@
#!/bin/sh
set -e
docker load < /usr/share/minecraft-pi/client/image.tar.gz
rm /usr/share/minecraft-pi/client/image.tar.gz

View File

@ -1,5 +0,0 @@
#!/bin/sh
set -e
docker image rm -f thebrokenrail/minecraft-pi-reborn:client

View File

@ -1,56 +0,0 @@
#!/bin/sh
set -e
# All Feature Flags
export AVAILABLE_FEATURES="$(tr '\n' ' ' < /usr/share/minecraft-pi/client/features)"
# Print Feature Flags Option
if [ "$1" = "--print-features" ]; then
echo "${AVAILABLE_FEATURES}"
exit 0
fi
# Esnure User Is In docker Group
if ! id -nGz | grep -qzxF 'docker'; then
pkexec /usr/sbin/usermod -aG docker "$(id -un)"
fi
# Export Important Variables
export ZENITY_CLASS='Minecraft - Pi edition'
export DOCKER_COMPOSE_YML="/usr/share/minecraft-pi/client/docker-compose.yml"
# Ensure Features Are Selected
if [ -z "${MCPI_FEATURES+x}" ]; then
MCPI_FEATURES="$(eval "zenity --class \"${ZENITY_CLASS}\" --list --checklist --width 400 --height 400 --column 'Enabled' --column 'Feature' ${AVAILABLE_FEATURES}")"
fi
if [ -z "${MCPI_RENDER_DISTANCE+x}" ]; then
MCPI_RENDER_DISTANCE="$(zenity --class "${ZENITY_CLASS}" --list --radiolist --width 400 --height 400 --text 'Minecraft Render Distance:' --column 'Selected' --column 'Name' FALSE 'Far' FALSE 'Normal' TRUE 'Short' FALSE 'Tiny')"
fi
if [ -z "${MCPI_USERNAME+x}" ]; then
MCPI_USERNAME="$(zenity --class "${ZENITY_CLASS}" --entry --text 'Minecraft Username:' --entry-text 'StevePi')"
fi
export MCPI_FEATURES
export MCPI_RENDER_DISTANCE
export MCPI_USERNAME
# Prepare Environment
export USER_HOME="${HOME}"
export USER_UID="$(id -u)"
export USER_GID="$(id -g)"
get_gid() {
echo "$(getent group "$1" | cut -d : -f 3)"
}
export USER_OTHER_GIDS="$(get_gid video) $(get_gid render)"
# Run
set +e
sg docker /usr/lib/minecraft-pi/pre-launch.sh
RET=$?
set -e
# Handle Crash
if [ ${RET} -ne 0 ]; then
zenity --class "${ZENITY_CLASS}" --error --no-wrap --text 'Minecraft: Pi Edition has crashed!\n\nExit Code: '${RET}'\n\n<a href="file:///tmp/minecraft-pi">Open Log Folder</a>\n<a href="https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn/src/branch/master/docs/TROUBLESHOOTING.md">Open Troubleshooting Guide</a>'
exit ${RET}
fi

View File

@ -1,27 +0,0 @@
#!/bin/sh
set -e
# Prepare Data Folder
mkdir -p "${USER_HOME}/.minecraft-pi"
# Create Log Folder
rm -rf /tmp/minecraft-pi
mkdir -p /tmp/minecraft-pi
# Start Logging
touch /tmp/minecraft-pi/main.log
tail -f /tmp/minecraft-pi/main.log &
TAIL_PID=$!
# Run
set +e
/usr/lib/minecraft-pi/run.sh > /tmp/minecraft-pi/main.log 2>&1
RET=$?
set -e
# Kill Logging
kill ${TAIL_PID} > /dev/null 2>&1 || :
# Exit
exit ${RET}

View File

@ -1,17 +0,0 @@
TRUE 'Touch GUI'
TRUE 'Fix Bow & Arrow'
TRUE 'Fix Attacking'
TRUE 'Mob Spawning'
TRUE 'Fancy Graphics'
TRUE 'Disable Autojump By Default'
TRUE 'Display Nametags By Default'
TRUE 'Fix Sign Placement'
TRUE 'Show Block Outlines'
FALSE 'Expand Creative Inventory'
FALSE 'Peaceful Mode'
TRUE 'Animated Water'
TRUE 'Remove Invalid Item Background'
TRUE 'Disable gui_blocks Atlas'
TRUE 'Smooth Lighting'
FALSE '3D Anaglyph'
FALSE 'Show FPS Monitor'

View File

@ -1,9 +0,0 @@
Package: minecraft-pi-reborn-native
Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: all
Depends: ${DEPENDENCIES}
Recommends: ${RECOMMENDED_DEPENDENCIES}
Conflicts: minecraft-pi, minecraft-pi-reborn-virgl

View File

@ -1,6 +0,0 @@
#!/bin/sh
set -e
# Launch Minecraft
exec docker-compose -f "${DOCKER_COMPOSE_YML}" run --rm minecraft-pi

View File

@ -1,20 +0,0 @@
version: '3'
services:
minecraft-pi:
image: 'thebrokenrail/minecraft-pi-reborn:client'
network_mode: 'host'
volumes:
- /usr/bin/qemu-arm-static:/usr/bin/qemu-arm-static
- '/tmp/.X11-unix:/tmp/.X11-unix'
- '${USER_HOME}/.minecraft-pi:/home/.minecraft-pi'
devices:
- '/dev/dri:/dev/dri'
environment:
- 'DISPLAY=unix${DISPLAY}'
- 'MCPI_FEATURES=${MCPI_FEATURES}'
- 'MCPI_RENDER_DISTANCE=${MCPI_RENDER_DISTANCE}'
- 'MCPI_USERNAME=${MCPI_USERNAME}'
- 'MCPI_MODE=native'
- 'USER_UID=${USER_UID}'
- 'USER_GID=${USER_GID}'
- 'USER_OTHER_GIDS=${USER_OTHER_GIDS}'

View File

@ -1,9 +0,0 @@
Package: minecraft-pi-reborn-virgl
Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: all
Depends: ${DEPENDENCIES}, virgl-server
Recommends: ${RECOMMENDED_DEPENDENCIES}
Conflicts: minecraft-pi, minecraft-pi-reborn-native

View File

@ -1,19 +0,0 @@
#!/bin/sh
set -e
# Start VirGL
virgl_test_server > /tmp/minecraft-pi/virgl.log 2>&1 &
VIRGL_PID=$!
# Launch Minecraft
set +e
docker-compose -f "${DOCKER_COMPOSE_YML}" run --rm minecraft-pi
RET=$?
set -e
# Kill VirGL
kill ${VIRGL_PID} > /dev/null 2>&1 || :
# Exit
exit ${RET}

View File

@ -1,19 +0,0 @@
version: '3'
services:
minecraft-pi:
image: 'thebrokenrail/minecraft-pi-reborn:client'
network_mode: 'host'
volumes:
- /usr/bin/qemu-arm-static:/usr/bin/qemu-arm-static
- '/tmp/.X11-unix:/tmp/.X11-unix'
- '/tmp/.virgl_test:/tmp/.virgl_test'
- '${USER_HOME}/.minecraft-pi:/home/.minecraft-pi'
environment:
- 'DISPLAY=unix${DISPLAY}'
- 'MCPI_FEATURES=${MCPI_FEATURES}'
- 'MCPI_RENDER_DISTANCE=${MCPI_RENDER_DISTANCE}'
- 'MCPI_USERNAME=${MCPI_USERNAME}'
- 'MCPI_MODE=virgl'
- 'USER_UID=${USER_UID}'
- 'USER_GID=${USER_GID}'
- 'USER_OTHER_GIDS=${USER_OTHER_GIDS}'

View File

@ -3,6 +3,4 @@ Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: all
Depends: ${DEPENDENCIES}
Recommends: ${RECOMMENDED_DEPENDENCIES}
Architecture: armhf

7
debian/server-x86_64 vendored Normal file
View File

@ -0,0 +1,7 @@
Package: minecraft-pi-reborn-server
Version: ${VERSION}
Maintainer: TheBrokenRail <connor24nolan@live.com>
Description: Fun with Blocks
Homepage: https://www.minecraft.net/en-us/edition/pi
Architecture: amd64
Depends: libc6-armhf-cross, libstdc++6-armhf-cross, qemu-user-static

View File

@ -1,6 +0,0 @@
#!/bin/sh
set -e
docker load < /usr/share/minecraft-pi/server/image.tar.gz
rm /usr/share/minecraft-pi/server/image.tar.gz

View File

@ -1,12 +0,0 @@
#!/bin/sh
set -e
# Prepare Environment
export MCPI_ROOT="${PWD}"
export USER_UID="$(id -u)"
export USER_GID="$(id -g)"
# Launch Minecraft
DOCKER_COMPOSE_YML='/usr/share/minecraft-pi/server/docker-compose.yml'
docker-compose -f "${DOCKER_COMPOSE_YML}" run --rm minecraft-pi-server

View File

@ -1,13 +0,0 @@
version: '3'
services:
minecraft-pi-server:
image: 'thebrokenrail/minecraft-pi-reborn:server'
network_mode: 'host'
stdin_open: true
tty: true
volumes:
- /usr/bin/qemu-arm-static:/usr/bin/qemu-arm-static
- '${MCPI_ROOT}:/home/.minecraft-pi'
environment:
- 'USER_UID=${USER_UID}'
- 'USER_GID=${USER_GID}'

8
dependencies/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,8 @@
project(dependencies)
# ZLib
add_subdirectory(zlib)
# LibPNG
add_subdirectory(libpng)
# Minecraft: Pi Edition
add_subdirectory(minecraft-pi)

28
dependencies/libpng/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,28 @@
project(libpng)
include(FetchContent)
# Silence Warnings
add_compile_options(-w)
## LibPNG
# Download
set(SKIP_INSTALL_ALL TRUE) # Skip Default LibPNG Installation
FetchContent_Declare(
libpng
GIT_REPOSITORY "https://git.code.sf.net/p/libpng/code"
GIT_TAG "v1.2.59"
)
FetchContent_Populate(libpng)
set(ZLIB_LIBRARY zlib)
set(ZLIB_INCLUDE_DIR "${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}")
set(CMAKE_POLICY_DEFAULT_CMP0054 OLD) # Silence Warning
add_subdirectory("${libpng_SOURCE_DIR}" "${libpng_BINARY_DIR}")
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
set_target_properties(png12 PROPERTIES DEBUG_POSTFIX "") # Fix LibPNG Suffix In Debug Mode
# Install
install(TARGETS png12 DESTINATION "${MCPI_LIB_DIR}")

208
dependencies/libpng/libpng.vers vendored Normal file
View File

@ -0,0 +1,208 @@
PNG12_0 {global:
png_libpng_ver;
png_pass_start;
png_pass_inc;
png_pass_ystart;
png_pass_yinc;
png_pass_mask;
png_pass_dsp_mask;
png_access_version_number;
png_set_sig_bytes;
png_sig_cmp;
png_check_sig;
png_create_read_struct;
png_create_write_struct;
png_get_compression_buffer_size;
png_set_compression_buffer_size;
png_reset_zstream;
png_create_read_struct_2;
png_create_write_struct_2;
png_write_chunk;
png_write_chunk_start;
png_write_chunk_data;
png_write_chunk_end;
png_create_info_struct;
png_info_init;
png_info_init_3;
png_write_info_before_PLTE;
png_write_info;
png_read_info;
png_convert_to_rfc1123;
png_convert_from_struct_tm;
png_convert_from_time_t;
png_set_expand;
png_set_expand_gray_1_2_4_to_8;
png_set_palette_to_rgb;
png_set_tRNS_to_alpha;
png_set_gray_1_2_4_to_8;
png_set_bgr;
png_set_gray_to_rgb;
png_set_rgb_to_gray;
png_set_rgb_to_gray_fixed;
png_get_rgb_to_gray_status;
png_build_grayscale_palette;
png_set_strip_alpha;
png_set_swap_alpha;
png_set_invert_alpha;
png_set_filler;
png_set_add_alpha;
png_set_swap;
png_set_packing;
png_set_packswap;
png_set_shift;
png_set_interlace_handling;
png_set_invert_mono;
png_set_background;
png_set_strip_16;
png_set_dither;
png_set_gamma;
png_permit_empty_plte;
png_set_flush;
png_write_flush;
png_start_read_image;
png_read_update_info;
png_read_rows;
png_read_row;
png_read_image;
png_write_row;
png_write_rows;
png_write_image;
png_write_end;
png_read_end;
png_destroy_info_struct;
png_destroy_read_struct;
png_destroy_write_struct;
png_set_crc_action;
png_set_filter;
png_set_filter_heuristics;
png_set_compression_level;
png_set_compression_mem_level;
png_set_compression_strategy;
png_set_compression_window_bits;
png_set_compression_method;
png_init_io;
png_set_error_fn;
png_get_error_ptr;
png_set_write_fn;
png_set_read_fn;
png_get_io_ptr;
png_set_read_status_fn;
png_set_write_status_fn;
png_set_mem_fn;
png_get_mem_ptr;
png_set_read_user_transform_fn;
png_set_write_user_transform_fn;
png_set_user_transform_info;
png_get_user_transform_ptr;
png_set_read_user_chunk_fn;
png_get_user_chunk_ptr;
png_set_progressive_read_fn;
png_get_progressive_ptr;
png_process_data;
png_progressive_combine_row;
png_malloc;
png_malloc_warn;
png_free;
png_free_data;
png_data_freer;
png_malloc_default;
png_free_default;
png_memcpy_check;
png_memset_check;
png_error;
png_chunk_error;
png_warning;
png_chunk_warning;
png_get_valid;
png_get_rowbytes;
png_get_rows;
png_set_rows;
png_get_channels;
png_get_image_width;
png_get_image_height;
png_get_bit_depth;
png_get_color_type;
png_get_filter_type;
png_get_interlace_type;
png_get_compression_type;
png_get_pixels_per_meter;
png_get_x_pixels_per_meter;
png_get_y_pixels_per_meter;
png_get_pixel_aspect_ratio;
png_get_x_offset_pixels;
png_get_y_offset_pixels;
png_get_x_offset_microns;
png_get_y_offset_microns;
png_get_signature;
png_get_bKGD;
png_set_bKGD;
png_get_cHRM;
png_get_cHRM_fixed;
png_set_cHRM;
png_set_cHRM_fixed;
png_get_gAMA;
png_get_gAMA_fixed;
png_set_gAMA;
png_set_gAMA_fixed;
png_get_hIST;
png_set_hIST;
png_get_IHDR;
png_set_IHDR;
png_get_oFFs;
png_set_oFFs;
png_get_pCAL;
png_set_pCAL;
png_get_pHYs;
png_set_pHYs;
png_get_PLTE;
png_set_PLTE;
png_get_sBIT;
png_set_sBIT;
png_get_sRGB;
png_set_sRGB;
png_set_sRGB_gAMA_and_cHRM;
png_get_iCCP;
png_set_iCCP;
png_get_sPLT;
png_set_sPLT;
png_get_text;
png_set_text;
png_get_tIME;
png_set_tIME;
png_get_tRNS;
png_set_tRNS;
png_get_sCAL;
png_set_sCAL;
png_set_keep_unknown_chunks;
png_handle_as_unknown;
png_set_unknown_chunks;
png_set_unknown_chunk_location;
png_get_unknown_chunks;
png_set_invalid;
png_read_png;
png_write_png;
png_get_copyright;
png_get_header_ver;
png_get_header_version;
png_get_libpng_ver;
png_permit_mng_features;
png_get_mmx_flagmask;
png_get_asm_flagmask;
png_get_asm_flags;
png_get_mmx_bitdepth_threshold;
png_get_mmx_rowbytes_threshold;
png_set_asm_flags;
png_set_mmx_thresholds;
png_mmx_support;
png_set_strip_error_numbers;
png_set_user_limits;
png_get_user_width_max;
png_get_user_height_max;
png_get_uint_32;
png_get_uint_16;
png_get_int_32;
png_get_uint_31;
png_save_uint_32;
png_save_int_32;
png_save_uint_16;
local: *; };

View File

@ -0,0 +1,17 @@
project(minecraft-pi)
include(FetchContent)
## Minecraft: Pi Edition
# Download
FetchContent_Declare(
minecraft-pi
URL "https://www.minecraft.net/content/dam/minecraft/edition-pi/minecraft-pi-0.1.1.tar.gz"
URL_HASH "SHA256=e0d68918874cdd403de1fd399380ae2930913fcefdbf60a3fbfebb62e2cfacab"
)
FetchContent_Populate(minecraft-pi)
# Install
install(DIRECTORY "${minecraft-pi_SOURCE_DIR}/" DESTINATION "${MCPI_INSTALL_DIR}" USE_SOURCE_PERMISSIONS)

23
dependencies/zlib/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,23 @@
project(zlib)
include(FetchContent)
# Silence Warnings
add_compile_options(-w)
## zlib
# Download
set(SKIP_INSTALL_ALL TRUE) # Skip Default ZLib Installation
FetchContent_Declare(
zlib
GIT_REPOSITORY "https://github.com/madler/zlib.git"
GIT_TAG "v1.2.11"
)
FetchContent_Populate(zlib)
include_directories("${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}") # Fix ZLib Build
add_subdirectory("${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}")
# Install
install(TARGETS zlib DESTINATION "${MCPI_LIB_DIR}")

103
docs/ARCHITECTURE.md Normal file
View File

@ -0,0 +1,103 @@
# Architecture
## Launch Sequence
### Client
1. The launcher is started by the user
1. The launcher starts several Zenity dialogs to configure MCPI-Reborn
2. The launcher replaces itself with MCPI
1. MCPI-Reborn components are loaded using ``LD_PRELOAD`` and ``LD_LIBRARY_PATH``
2. If the Media Layer Proxy is enabled, the Media Layer Proxy Client is started as a sub-process
### Server
1. The launcher is started by the user
2. The launcher replaces itself with MCPI
## Components
### Launcher
This component configures the various environmental variables required for MCPI-Reborn to work. When running in client-mode, this component will also launch several Zenity dialogs for interactive configuration.
The environmental variables configured by this component includes:
* ``LD_PRELOAD``
* ``LD_LIBRAR_PATH``
* ``MCPI_FEATURE_FLAGS``
* ``MCPI_RENDER_DISTANCE``
* ``MCPI_USERNAME``
This is always compiled for the host system's architecture.
### Media Layer
The Media Layer handles MCPI's graphics calls and user input. It replaces MCPI's native usage of SDL 1.2 with GLFW.
#### Core
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:
* Taking Screenshots
* Fullscreen
* Etc
This is always compiled for the host system's architecture.
This was created because SDL 1.2 has numerous bugs and is in-general unsupported.
#### Proxy
This sub-component must be used if the host system's architecture isn't ARM. It uses UNIX pipes to cross architectural boundaries and allow MCPI to use the Media Layer Core (which is always compiled for the host system's architecture).
It is made of two parts:
* Media Layer Proxy Server
* Links To MCPI
* Creates The UNIX Pipes
* Same External API As The Media Layer Core
* Compiled For ARM
* Media Layer Proxy Client
* Links To The Media Layer Core
* Connects To The Media Layer Proxy Server
* Uses The System's Native GLES Driver (ie. Mesa)
* Compiled For The Host System's Architecture
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).
#### Extras
This sub-component contains code that must always be linked directly to MCPI.
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) 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
This sub-component includes headers for SDL, GLES, and EGL allowing easy (cross-)compilation.
### Mods
This component links directly to MCPI and patches it to modify its behavior.
This is always compiled for ARM.
### ``libreborn``
This component contains various utility functions including:
* Code Patching (ARM Only)
* Logging
* MCPI Symbols
* Etc
The code patching is ARM only because it relies on hard-coded ARM instructions. However, this is irrelevant since code patching is only needed in ARM code (to patch MCPI).
## Dependencies
MCPI-Reborn has several dependencies:
* MCPI (Bundled)
* GLFW (Only In Client Mode)
* ZLib (Required By LibPNG; Bundled)
* LibPNG (Bundled)
* FreeImage (Only In Client Mode)
* QEMU User Mode (Only On Non-ARM Hosts; Runtime Only)
* Zenity (Only In Client Mode; Runtime Only)

63
docs/BUILDING.md Normal file
View File

@ -0,0 +1,63 @@
# Building
## Build Options
* ``MCPI_BUILD_MODE``
* ``arm``: Only Build ARM Components
* ``native``: Only Build Native Components
* ``both`` (Default): Build Both ARM And Native Components For ARM
* ``MCPI_SERVER_MODE``
* ``ON``: Enable Server Mode
* ``OFF`` (Default): Disable Server Mode
* ``MCPI_USE_MEDIA_LAYER_PROXY``
* ``ON``: Enable The Media Layer Proxy
* ``OFF`` (Default): Disable The Media Layer Proxy
## Build Dependencies
* Common
* ARM Compiler
* Host Compiler (Clang)
* CMake
* Host Architecture Dependencies
* Client Mode Only
* GLFW
* FreeImage
## Runtime Dependencies
* Non-ARM Host Architectures
* QEMU User-Mode Static
* Host Architecture Dependencies
* CLient Mode Only
* OpenGL ES 1.1
* GLFW
* FreeImage
* Zenity
## Two-Step Build
Use this when the host architecture is not ARM.
```sh
# Create Build Directory
mkdir build && cd build
# Build ARM Components
mkdir arm && cd arm
cmake -DMCPI_BUILD_MODE=arm ../..
make -j$(nproc) && sudo make install
# Build Native Components
mkdir native && cd native
cmake -DMCPI_BUILD_MODE=native ../..
make -j$(nproc) && sudo make install
```
## One-Step Build
Use this when the host architecture is ARM.
```sh
# Create Build Directory
mkdir build && cd build
# Build
cmake ..
make -j$(nproc) && sudo make install
```

View File

@ -6,27 +6,14 @@ This server is also compatible with MCPE Alpha v0.6.1.
## Setup
### Debian Package
To use, install the ``minecraft-pi-reborn-server`` package and run ``minecraft-pi-reborn-server`` or use. It will generate the world and ``server.properties`` in the current directory.
### Docker Compose
Make sure you have ``qemu-user-static`` installed and ``binfmt`` setup.
```yml
version: "3.7"
services:
minecraft-pi:
image: thebrokenrail/minecraft-pi-reborn:server
volumes:
- ./minecraft-pi/data:/home/.minecraft-pi
- /usr/bin/qemu-arm-static:/usr/bin/qemu-arm-static
restart: always
stdin_open: true
tty: true
ports:
- "19132:19132/udp"
```
To use, install and run ``minecraft-pi-reborn-server``. It will generate the world and ``server.properties`` in the current directory.
## Server Limitations
- 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
- Survival Mode servers are only compatible with ``minecraft-pi-reborn`` clients
* 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
* Survival Mode servers are incompatible with unmodded MCPI
## Arguments
### ``--only-generate``
If you run the server with ``--only-generate`` it will immediately exit once world generation has completed.

View File

@ -1,66 +1,26 @@
# Manual Installation
[Download Packages Here](https://jenkins.thebrokenrail.com/job/minecraft-pi-reborn/job/master/lastSuccessfulBuild/artifact/out/)
## System Requirements
- At Least Debian/Raspbian Buster Or Ubuntu 20.04
## Picking A Package
## Before You Install
<details>
<summary>Debian/Raspbian Buster</summary>
### ``libseccomp2``
``minecraft-pi-reborn`` requires a newer version of the package ``libseccomp2`` to be installed when using Debian/Raspbian Buster.
```sh
# Install Backports Key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 04EE7237B7D453EC
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 648ACFD622F3D138
# Install Backports Repository
echo 'deb http://deb.debian.org/debian buster-backports main' | sudo tee -a /etc/apt/sources.list
# Update APT Index
sudo apt update
# Install Updated libseccomp2
sudo apt install -t buster-backports libseccomp2
### Name Format
```
minecraft-pi-reborn-<Variant>_X.Y.Z~<Distribution>_<Architecture>
```
### Official Docker Package
``minecraft-pi-reborn`` requires the official Docker package when running Debian/Raspbian Buster instead of the Debian package (``docker.io``).
### Picking A Variant
* ``client``: Client mode, use this if you want to play MCPI
* ``server``: Server mode, use this if you want to host an MCPI server
```sh
# Remove Debian Docker Package
sudo apt-get remove -y docker.io
# Install Official Docker Package
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
```
### Picking A Distribution
This specifies which version of Debian MCPI-Reborn was built against. Which one you should use depends on your current distribution. If your distribution supports it, you should use ``bullseye`` for better mouse sensitivity.
### Existing Installation
If you have un-modded ``minecraft-pi`` installed, you must remove it and transfer your existing worlds to ``minecraft-pi-reborn``'s folder.
* Ubuntu 20.04+: ``bullseye``
* Ubuntu 18.04: ``buster``
* Raspberry Pi OS Buster: ``buster``
* Debian Bullseye+: ``bullseye``
* Debian Buster: ``buster``
```sh
# Transfer Worlds
mv ~/.minecraft ~/.minecraft-pi
# Remove Vanilla Minecraft Pi
sudo apt-get remove -y minecraft-pi
```
</details>
<details>
<summary>NVIDIA Users</summary>
The proprietary NVIDIA drivers are not supported, use either the open-source ``noveau`` drivers or use a different GPU (ie. Intel Integrated GPU).
</details>
## Installing
1. Download Appropriate Package From [Here](https://jenkins.thebrokenrail.com/job/minecraft-pi-reborn/job/master/lastSuccessfulBuild/artifact/out/deb/) (See Table Below To Pick Correct Package)
2. Install With ``sudo apt install ./<Path To File>`` Or Your Preferred Package Installer
3. Have Fun!
### Package Table
| Package | Description |
| --- | --- |
| ``minecraft-pi-reborn-virgl`` | Minecraft Pi Edition Using VirGL For Hardware Acceleration (Recommended For Desktop/Laptop) |
| ``minecraft-pi-reborn-native`` | Minecraft: Pi Edition Using Docker Device Mounting For GPU Acceleration (Recommended For ARM Devices (ie. Raspberry Pi, PinePhone, etc)) |
| ``minecraft-pi-reborn-server`` | Minecraft Pi Edition Modded Into A Dedicated Server |
### Picking An Architecture
* ``amd64``: x86_64, use this if you are using a device with an AMD or Intel processor
* ``armhf``: ARM, use this if you are using an ARM device (like a Raspberry Pi)

View File

@ -1,116 +0,0 @@
# Modding
Modding Minecraft: Pi Edition is possible by patching the binary at runtime. To make this easier ``minecraft-pi-reborn`` includes a library called ``libreborn.so`` which provides several functions to help you patch the game.
## Hex Addresses
Minecraft: Pi Edition has no symbols so you must patch the hex address of an instruction instead of using a function name. Hex addresses can be found using tools like [Ghidra](https://ghidra-sre.org) or [RetDec](https://retdec.com). To find out what a function does, you can find its equivalent in Minecraft: Pocket Edition 0.6.1 and use its name for reference because Minecraft: Pocket Edition 0.6.1 includes symbols.
## Loading Directories
``minecraft-pi-reborn`` loads mods from two locations, ``/app/minecraft-pi/mods``, and ``~/.minecraft-pi/mods``. The first location only exists in the Docker container and is immutable, so you should place your mods in ``~/.minecraft-pi/mods`` which is mounted on the host.
## C++ Strings
Minecraft: Pi Edition was compiled with an old version of GCC, so when interacting with C++ strings, make sure you set ``-D_GLIBCXX_USE_CXX11_ABI=0``.
## ``libreborn.so`` API
Header files and the shared library can be download from [Jenkins](https://jenkins.thebrokenrail.com/job/minecraft-pi-reborn/job/master/lastSuccessfulBuild/artifact/out/lib).
### ``void overwrite(void *start, void *target)``
This method replaces a function with another function.
#### Parameters
- **start:** The function you are replacing.
- **target:** The function you are replacing it with.
#### Return Value
None
#### Warning
This should never be used on functions that are only 1 byte long because it overwrites 2 bytes.
#### Example
```c
static int func_injection(int a, int b) {
return a + 4;
}
__attribute__((constructor)) static void init() {
overwrite((void *) 0xabcde, func_injection);
}
```
### ``void overwrite_call(void *start, void *original)``
This allows you to overwrite a specific call of a function rather than the function itself. This allows you to call the original function. However, this does not effect VTables.
#### Parameters
- **start:** The address of the function call to overwrite.
- **target:** The function call you are replacing it with.
#### Return Value
None
#### Warning
This method can only be safely used 512 times in total.
#### Example
```c
typedef int (*func_t)(int a, int b);
static func_t func = (func_t) 0xabcde;
static void *func_original = NULL;
static int func_injection(int a, int b) {
(*func)(a, b);
return a + 4;
}
__attribute__((constructor)) static void init() {
overwrite_call((void *) 0xabcd, func_injection);
}
```
### ``void overwrite_calls(void *start, void *original)``
This allows you to overwrite all calls of a function rather than the function itself. This allows you to call the original function. However, this does not effect VTables.
#### Parameters
- **start:** The function call to overwrite;
- **target:** The function call you are replacing it with.
#### Return Value
None
#### Warning
This method can only be safely used 512 times in total.
#### Example
```c
typedef int (*func_t)(int a, int b);
static func_t func = (func_t) 0xabcde;
static void *func_original = NULL;
static int func_injection(int a, int b) {
(*func)(a, b);
return a + 4;
}
__attribute__((constructor)) static void init() {
overwrite_calls((void *) func, func_injection);
}
```
### ``void patch(void *start, unsigned char patch[])``
This allows you to replace a specific instruction.
#### Parameters
- **start:** The target instruction.
- **patch:** The new instruction (array length must be 4).
#### Return Value
None
#### Example
```c
__attribute__((constructor)) static void init() {
unsigned char patch_data[4] = {0x00, 0x00, 0x00, 0x00};
patch((void *) 0xabcde, patch_data);
}
```

View File

@ -1,5 +1,5 @@
# Overriding Assets
Normally, Minecraft: Pi Edition assets can be easily overridden by physically replacing the file, however ``minecraft-pi-=reborn`` uses a Docker image making this much harder to do. To make overriding assets easier, ``minecraft-pi-reborn`` provides an overrides folder. Any file located in Minecraft: Pi Edition's ``data`` folder can be overridden by placing a file with the same name and path in the overrides folder. The overrides folder is located at ``~/.minecraft-pi/overrides``.
To make overriding assets easier, MCPI-Reborn provides an overrides folder. Any file located in Minecraft: Pi Edition's ``data`` folder can be overridden by placing a file with the same name and path in the overrides folder. The overrides folder is located at ``~/.minecraft-pi/overrides``.
## Examples
- ``data/images/terrain.png`` -> ``~/.minecraft-pi/overrides/images/terrain.png``

8
docs/README.md Normal file
View File

@ -0,0 +1,8 @@
# Documentation
* [View Manual Installation](docs/INSTALL.md)
* [View Overriding Assets](OVERRIDING_ASSETS.md)
* [View Dedicated Server](DEDICATED_SERVER.md)
* [View Credits](CREDITS.md)
* [View Terminology](TERMINOLOGY.md)
* [View Building](BUILDING.md)
* [View Architecture](ARCHITECTURE.md)

10
docs/TERMINOLOGY.md Normal file
View File

@ -0,0 +1,10 @@
# Terminology
| Name | Description |
| --- | --- |
| MCPI | Shorthand for Minecraft: Pi Edition |
| Host Architecture | The native architecture of the CPU that MCPi-Reborn will be running on |
| Native Component | A component that *can* be compiled for the host architecture |
| ARM Component | A component that *must* be compiled for ARM |
| Server Mode | A mode where MCPI is patched into behaving like a dedicated server |
| Client Mode | The normal behavior of MCPI |
| Stub | An implementation of a library where all functions either do nothing or error |

View File

@ -1,26 +0,0 @@
# Troubleshooting
Game logs are located in ``/tmp/minecraft-pi``.
## ``Couldn't connect to Docker daemon at http+docker://localhost - is it running?``
Start Docker if it isn't running:
```sh
sudo service docker start
```
## ``Error response from daemon: error gathering device information while adding custom device "/dev/dri": no such file or directory``
Make sure you are using the correct GPU drivers for your system.
If you are using a Raspberry Pi, make sure your GPU driver is set to ``Full KMS`` or ``Fake KMS`` in ``raspi-config``.
## ``Segmentation Fault`` (Exit Code: ``139``)
Report an issue with reproduction instructions and system details.
## ``[ERR]: Invalid ~/.minecraft-pi Permissions``
Update ``~/.minecraft-pi`` permissions:
```sh
sudo chown -R "$(id -u):$(id -g)" ~/.minecraft-pi
chmod -R u+rw ~/.minecraft-pi
```
## Other
If you experience a crash/error not listed above, report it on the issue tracker **with your game log attached**.

View File

@ -1,34 +1,18 @@
cmake_minimum_required(VERSION 3.1.0)
project(launcher)
add_compile_options(-Wall -Wextra -Werror)
## Launcher
add_executable(launcher src/launcher.c)
# Launcher
if(BUILD_NATIVE_COMPONENTS)
add_executable(launcher src/bootstrap.c src/ldconfig.cpp)
if(MCPI_SERVER_MODE)
target_sources(launcher PRIVATE src/server/launcher.c)
else()
target_sources(launcher PRIVATE src/client/launcher.cpp)
endif()
target_link_libraries(launcher reborn-headers)
# Install
install(TARGETS launcher DESTINATION /)
install(PROGRAMS src/run.sh DESTINATION /)
## Stubs
# Stub RPI-Specific Graphics
add_library(bcm_host SHARED src/stubs/bcm_host.c)
# Stub EGL
add_library(EGL SHARED src/stubs/EGL.c)
# Stub SDL
add_library(SDL-1.2 SHARED src/stubs/SDL.cpp)
target_link_libraries(SDL-1.2 pthread)
set_target_properties(SDL-1.2 PROPERTIES SOVERSION "0")
# MCPI Links Against GLESv2 But Uses GLESv1_CM
add_library(GLESv2 SHARED src/stubs/GLESv2.c)
target_link_libraries(GLESv2 GLESv1_CM)
target_link_options(GLESv2 PRIVATE "-Wl,--no-as-needed")
# Install Stubs
install(TARGETS bcm_host EGL SDL-1.2 GLESv2 DESTINATION /lib)
install(TARGETS launcher DESTINATION "${MCPI_INSTALL_DIR}")
install_symlink("../../${MCPI_INSTALL_DIR}/launcher" "usr/bin/${MCPI_VARIANT_NAME}")
if(NOT MCPI_SERVER_MODE)
install(DIRECTORY "client-data/" DESTINATION ".")
endif()
endif()

View File

@ -0,0 +1,16 @@
TRUE Touch GUI
TRUE Fix Bow & Arrow
TRUE Fix Attacking
TRUE Mob Spawning
TRUE Fancy Graphics
TRUE Disable Autojump By Default
TRUE Display Nametags By Default
TRUE Fix Sign Placement
TRUE Show Block Outlines
FALSE Expand Creative Inventory
FALSE Peaceful Mode
TRUE Animated Water
TRUE Remove Invalid Item Background
TRUE Disable gui_blocks Atlas
TRUE Smooth Lighting
FALSE 3D Anaglyph

View File

@ -1,10 +1,10 @@
[Desktop Entry]
Name=Minecraft: Pi Edition
Name=Minecraft: Pi Edition: Reborn
Comment=Fun with Blocks
Icon=/usr/share/pixmaps/minecraft-pi.png
Icon=/usr/share/pixmaps/minecraft-pi-reborn-client.png
StartupNotify=false
StartupWMClass=Minecraft - Pi edition
Exec=/usr/bin/minecraft-pi
Exec=/usr/bin/minecraft-pi-reborn-client
Terminal=false
Type=Application
Categories=Application;Game;

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

222
launcher/src/bootstrap.c Normal file
View File

@ -0,0 +1,222 @@
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <libreborn/libreborn.h>
#include "bootstrap.h"
#include "ldconfig.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];
}
}
static void set_and_print_env(const char *name, char *value) {
// Set Variable With No Trailing Colon
trim(&value);
#ifdef DEBUG
// Print New Value
INFO("Set %s = %s", name, value);
#endif
// Set The Value
setenv(name, value, 1);
}
// 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
static void load(char **ld_preload, char *folder) {
int folder_name_length = strlen(folder);
// Retry Until Successful
while (1) {
// Open Folder
DIR *dp = opendir(folder);
if (dp != NULL) {
// Loop Through Folder
struct dirent *entry = NULL;
errno = 0;
while (1) {
errno = 0;
entry = readdir(dp);
if (entry != NULL) {
// Check If File Is Regular
if (entry->d_type == DT_REG) {
// Get Full Name
int name_length = strlen(entry->d_name);
int total_length = folder_name_length + name_length;
char name[total_length + 1];
// Concatenate Folder Name And File Name
for (int i = 0; i < folder_name_length; i++) {
name[i] = folder[i];
}
for (int i = 0; i < name_length; i++) {
name[folder_name_length + i] = entry->d_name[i];
}
// Add Terminator
name[total_length] = '\0';
// Check If File Is Executable
int result = access(name, R_OK);
if (result == 0) {
// Add To LD_PRELOAD
string_append(ld_preload, ":%s", name);
} else if (result == -1 && errno != 0) {
// Fail
INFO("Unable To Acesss: %s: %s", name, strerror(errno));
errno = 0;
}
}
} else if (errno != 0) {
// Error Reading Contents Of Folder
ERR("Error Reading Directory: %s: %s", folder, strerror(errno));
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
// Exit Function
return;
} else if (errno == ENOENT) {
// Folder Doesn't Exists, Attempt Creation
int ret = mkdir(folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
ERR("Error Creating Directory: %s: %s", folder, strerror(errno));
}
// Continue Retrying
} else {
// Unable To Open Folder
ERR("Error Opening Directory: %s: %s", folder, strerror(errno));
}
}
}
#define MCPI_NAME "minecraft-pi"
// Bootstrap
void bootstrap(int argc, char *argv[]) {
INFO("%s", "Configuring Game...");
// Get Binary Directory
char *binary_directory = get_binary_directory();
// Configure LD_LIBRARY_PATH
{
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH");
// Add Library Directory
char *new_ld_path;
safe_asprintf(&new_ld_path, "%s/lib", binary_directory);
// Add Existing LD_LIBRAR_PATH
{
char *value = get_env_safe("LD_LIBRARY_PATH");
if (strlen(value) > 0) {
string_append(&new_ld_path, ":%s", value);
}
}
// Load ARM Libraries
#ifdef __ARM_ARCH
string_append(&new_ld_path, "%s", ":/usr/lib/arm-linux-gnueabihf:/usr/arm-linux-gnueabihf/lib");
#endif
// Add Full Library Search Path
{
char *value = get_full_library_search_path();
if (strlen(value) > 0) {
string_append(&new_ld_path, ":%s", value);
}
free(value);
}
// Add Fallback Library Directory
string_append(&new_ld_path, ":%s/fallback-lib", binary_directory);
// Set And Free
set_and_print_env("LD_LIBRARY_PATH", new_ld_path);
free(new_ld_path);
}
// Configure LD_PRELOAD
{
PRESERVE_ENVIRONMENTAL_VARIABLE("LD_PRELOAD");
char *new_ld_preload = NULL;
safe_asprintf(&new_ld_preload, "%s", get_env_safe("LD_PRELOAD"));
// Get Mods Folder
char *mods_folder = NULL;
safe_asprintf(&mods_folder, "%s/mods/", binary_directory);
// Load Mods From ./mods
load(&new_ld_preload, mods_folder);
// Free Mods Folder
free(mods_folder);
// Set LD_PRELOAD
set_and_print_env("LD_PRELOAD", new_ld_preload);
free(new_ld_preload);
}
// Free Binary Directory
free(binary_directory);
// Start Game
INFO("%s", "Starting Game...");
#ifdef __ARM_ARCH
// Create Arguments List
char *new_argv[argc + 1];
for (int i = 1; i <= argc; i++) {
new_argv[i] = argv[i];
}
new_argv[0] = NULL; // Updated By safe_execvpe()
// Run
safe_execvpe_relative_to_binary(MCPI_NAME, new_argv, environ);
#else
// Use Static QEMU So It Isn't Affected By LD_* Variables
#define QEMU_NAME "qemu-arm-static"
// Use Correct LibC
setenv("QEMU_LD_PREFIX", "/usr/arm-linux-gnueabihf", 1);
// Get Binary Directory
binary_directory = get_binary_directory();
// Create Full Path
char *full_path = NULL;
safe_asprintf(&full_path, "%s/" MCPI_NAME, binary_directory);
// Free Binary Directory
free(binary_directory);
// Create Arguments List
char *new_argv[argc + 2];
for (int i = 1; i <= argc; i++) {
new_argv[i + 1] = argv[i];
}
new_argv[0] = NULL; // Updated By safe_execvpe()
new_argv[1] = full_path; // Path To MCPI
// Run
safe_execvpe(QEMU_NAME, new_argv, environ);
#endif
}

View File

@ -4,7 +4,7 @@
extern "C" {
#endif
void take_screenshot();
void bootstrap(int argc, char *argv[]);
#ifdef __cplusplus
}

View File

@ -0,0 +1,225 @@
#include <fstream>
#include <cstring>
#include <cerrno>
#include <sys/wait.h>
#include <vector>
#include <functional>
#include <libreborn/libreborn.h>
#include "../bootstrap.h"
// Load Available Feature Flags
static void load_available_feature_flags(std::function<void(std::string)> callback) {
// Get Path
char *binary_directory = get_binary_directory();
std::string path = std::string(binary_directory) + "/available-feature-flags";
free(binary_directory);
// Load File
std::ifstream stream(path);
if (stream && stream.good()) {
std::string line;
while (std::getline(stream, line)) {
if (line.length() > 0) {
// Verify Line
if (line.find('|') == std::string::npos) {
callback(line);
} else {
// Invalid Line
ERR("%s", "Feature Flag Contains Invalid '|'");
}
}
}
stream.close();
} else {
ERR("%s", "Unable To Load Available Feature Flags");
}
}
// Run Command And Get Output
static char *run_command(char *command[], int *return_code) {
// Store Output
int output_pipe[2];
safe_pipe2(output_pipe, 0);
// Run
pid_t ret = fork();
if (ret == -1) {
ERR("Unable To Run Command: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Pipe stdout
dup2(output_pipe[1], STDOUT_FILENO);
close(output_pipe[0]);
close(output_pipe[1]);
// Run
safe_execvpe(command[0], command, environ);
} else {
// Parent Process
// Read stdout
close(output_pipe[1]);
char *output = NULL;
char c;
int bytes_read = 0;
while ((bytes_read = read(output_pipe[0], (void *) &c, 1)) > 0) {
string_append(&output, "%c", c);
}
close(output_pipe[0]);
// Get Return Code
int status;
waitpid(ret, &status, 0);
*return_code = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
// Return
return output;
}
}
// Run Command And Set Environmental Variable
static void run_command_and_set_env(const char *env_name, char *command[]) {
// Only Run If Environmental Variable Is NULL
if (getenv(env_name) == NULL) {
// Run
int return_code;
char *output = run_command(command, &return_code);
if (output != NULL) {
// Trim
int length = strlen(output);
if (output[length - 1] == '\n') {
output[length - 1] = '\0';
}
// Set
setenv(env_name, output, 1);
}
// Check Return Code
if (return_code != 0) {
ERR("%s", "Launch Interrupted");
}
}
}
// Use Zenity To Set Environmental Variable
static void run_zenity_and_set_env(const char *env_name, std::vector<std::string> command) {
// Create Full Command
std::vector<std::string> full_command;
full_command.push_back("zenity");
full_command.push_back("--class");
full_command.push_back("Minecraft - Pi edition");
full_command.insert(full_command.end(), command.begin(), command.end());
// Convert To C Array
const char *full_command_array[full_command.size() + 1];
for (std::vector<std::string>::size_type i = 0; i < full_command.size(); i++) {
full_command_array[i] = full_command[i].c_str();
}
full_command_array[full_command.size()] = NULL;
// Run
run_command_and_set_env(env_name, (char **) full_command_array);
}
// Launch
int main(int argc, char *argv[]) {
// Print Features
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--print-available-feature-flags") == 0) {
// Print Available Feature Flags
load_available_feature_flags([](std::string line) {
printf("%s\n", line.c_str());
fflush(stdout);
});
return 0;
}
}
// Create ~/.minecraft-pi If Needed
// Minecraft Folder
{
char *minecraft_folder = NULL;
asprintf(&minecraft_folder, "%s/.minecraft-pi", getenv("HOME"));
ALLOC_CHECK(minecraft_folder);
{
// Check Minecraft Folder
struct stat obj;
if (stat(minecraft_folder, &obj) != 0 || !S_ISDIR(obj.st_mode)) {
// Create Minecraft Folder
int ret = mkdir(minecraft_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
ERR("Error Creating Directory: %s: %s", minecraft_folder, strerror(errno));
}
}
}
free(minecraft_folder);
}
// Setup MCPI_FEATURE_FLAGS
{
std::vector<std::string> command;
command.push_back("--list");
command.push_back("--checklist");
command.push_back("--width");
command.push_back("400");
command.push_back("--height");
command.push_back("400");
command.push_back("--column");
command.push_back("Enabled");
command.push_back("--column");
command.push_back("Feature");
load_available_feature_flags([&command](std::string line) {
if (line.rfind("TRUE ", 0) == 0) {
// Enabled By Default
command.push_back("TRUE");
command.push_back(line.substr(5, std::string::npos));
} else if (line.rfind("FALSE ", 0) == 0) {
// Disabled By Default
command.push_back("FALSE");
command.push_back(line.substr(6, std::string::npos));
} else {
ERR("%s", "Invalid Feature Flag Default");
}
});
// Run
run_zenity_and_set_env("MCPI_FEATURE_FLAGS", command);
}
// Setup MCPI_RENDER_DISTANCE
{
std::vector<std::string> command;
command.push_back("--list");
command.push_back("--radiolist");
command.push_back("--width");
command.push_back("400");
command.push_back("--height");
command.push_back("400");
command.push_back("--text");
command.push_back("Minecraft Render Distance:");
command.push_back("--column");
command.push_back("Selected");
command.push_back("--column");
command.push_back("Name");
command.push_back("FALSE");
command.push_back("Far");
command.push_back("FALSE");
command.push_back("Normal");
command.push_back("TRUE");
command.push_back("Short");
command.push_back("FALSE");
command.push_back("Tiny");
// Run
run_zenity_and_set_env("MCPI_RENDER_DISTANCE", command);
}
// Setup MCPI_USERNAME
{
std::vector<std::string> command;
command.push_back("--entry");
command.push_back("--text");
command.push_back("Minecraft Username:");
command.push_back("--entry-text");
command.push_back("StevePi");
// Run
run_zenity_and_set_env("MCPI_USERNAME", command);
}
// Bootstrap
bootstrap(argc, argv);
}

View File

@ -1,156 +0,0 @@
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
// Check String Prefix/Suffix
static int starts_with(const char *s, const char *t) {
return strncmp(s, t, strlen(t)) == 0;
}
static int ends_with(const char *s, const char *t) {
size_t slen = strlen(s);
size_t tlen = strlen(t);
if (tlen > slen) return 1;
return strcmp(s + slen - tlen, t) == 0;
}
// Set Environmental Variable
static void trim(char *value) {
// Remove Trailing Colon
int length = strlen(value);
if (value[length - 1] == ':') {
value[length - 1] = '\0';
}
}
static void set_and_print_env(char *name, char *value) {
// Set Variable With No Trailing Colon
trim(value);
fprintf(stderr, "Set %s = %s\n", name, value);
setenv(name, value, 1);
}
// 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 SO Files In Folder
static void load(char **ld_preload, char *folder) {
int folder_name_length = strlen(folder);
// Retry Until Successful
while (1) {
// Open Folder
DIR *dp = opendir(folder);
if (dp != NULL) {
// Loop Through Folder
struct dirent *entry = NULL;
errno = 0;
while (1) {
entry = readdir(dp);
if (entry != NULL) {
// Check If File Is A Shared Library
if (entry->d_type == DT_REG && starts_with(entry->d_name, "lib") && ends_with(entry->d_name, ".so")) {
int name_length = strlen(entry->d_name);
int total_length = folder_name_length + name_length;
char name[total_length + 1];
for (int i = 0; i < folder_name_length; i++) {
name[i] = folder[i];
}
for (int i = 0; i < name_length; i++) {
name[folder_name_length + i] = entry->d_name[i];
}
name[total_length] = '\0';
// Add To LD_PRELOAD
asprintf(ld_preload, "%s:%s", name, *ld_preload);
}
} else if (errno != 0) {
// Error Reading Contents Of Folder
fprintf(stderr, "Error Reading Directory: %s: %s\n", folder, strerror(errno));
exit(EXIT_FAILURE);
} else {
// Done!
break;
}
}
// Close Folder
closedir(dp);
// Exit Function
return;
} else if (errno == ENOENT) {
// Folder Doesn't Exists, Attempt Creation
int ret = mkdir(folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
fprintf(stderr, "Error Creating Directory: %s: %s\n", folder, strerror(errno));
exit(EXIT_FAILURE);
}
// Continue Retrying
} else {
// Unable To Open Folder
fprintf(stderr, "Error Opening Directory: %s: %s\n", folder, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
int main(__attribute__((unused)) int argc, char *argv[]) {
fprintf(stderr, "Configuring Game...\n");
// Minecraft Folder
char *minecraft_folder = NULL;
asprintf(&minecraft_folder, "%s/.minecraft-pi", getenv("HOME"));
{
// Check Minecraft Folder
struct stat obj;
if (stat(minecraft_folder, &obj) != 0 || !S_ISDIR(obj.st_mode)) {
// Create Minecraft Folder
int ret = mkdir(minecraft_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (ret != 0) {
// Unable To Create Folder
fprintf(stderr, "Error Creating Directory: %s: %s\n", minecraft_folder, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
free(minecraft_folder);
// Configure LD_LIBRARY_PATH
char *ld_path;
asprintf(&ld_path, "./lib:/usr/arm-linux-gnueabihf/lib:%s", get_env_safe("LD_LIBRARY_PATH"));
set_and_print_env("LD_LIBRARY_PATH", ld_path);
free(ld_path);
// Start Configuring LD_PRELOAD
char *ld_preload = NULL;
asprintf(&ld_preload, "%s", get_env_safe("LD_PRELOAD"));
// Load Mods From ./mods
load(&ld_preload, "./mods/");
// Loads Mods From ~/.minecraft-pi/mods
char *home_mods = NULL;
asprintf(&home_mods, "%s/.minecraft-pi/mods/", getenv("HOME"));
load(&ld_preload, home_mods);
free(home_mods);
// Set LD_PRELOAD
set_and_print_env("LD_PRELOAD", ld_preload);
free(ld_preload);
// Start Game
fprintf(stderr, "Starting Game...\n");
return execve("./minecraft-pi", argv, environ);
}

45
launcher/src/ldconfig.cpp Normal file
View File

@ -0,0 +1,45 @@
#include <unistd.h>
#include <string>
#include <cstring>
#include <cstdio>
#include <libreborn/libreborn.h>
#include "ldconfig.h"
char *get_full_library_search_path() {
std::string output;
// Run
FILE *file = popen("ldconfig -NXv 2> /dev/null", "r");
// Read
int running = 1;
while (running) {
char *line = NULL;
size_t length = 0;
if (getline(&line, &length, file) != -1) {
// Convert to C++ String
std::string str(line);
// Remove Newline
if (str.size() > 0 && str[str.size() - 1] == '\n') {
str.pop_back();
}
// Interpret
if (str.size() >= 2 && str[0] != '\t' && str[str.size() - 1] == ':') {
output.append(str);
}
} else {
running = 0;
}
free(line);
}
// Remove Colon
if (output.size() > 0 && output[output.size() - 1] == ':') {
output.pop_back();
}
// Close Process
pclose(file);
// Return
char *output_str = strdup(output.c_str());
ALLOC_CHECK(output_str);
return output_str;
}

11
launcher/src/ldconfig.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
char *get_full_library_search_path(); // Remember To free()
#ifdef __cplusplus
}
#endif

View File

@ -1,35 +0,0 @@
#!/bin/sh
set -e
# Check Root
if [ ! "$(id -u)" = '0' ]; then
echo 'Must Run As Root' 1>&2
exit 1
fi
# Check
if ! id user > /dev/null 2>&1; then
# Create User Groups
if [ -z "${USER_GID+x}" ]; then
USER_GID='1000'
fi
groupadd --force --gid "${USER_GID}" user
# Create User
if [ -z "${USER_UID+x}" ]; then
USER_UID='1000'
fi
useradd --shell /bin/sh --home-dir /home --no-create-home --uid "${USER_UID}" --gid "${USER_GID}" user
# Add Other Groups
if [ ! -z "${USER_OTHER_GIDS+x}" ]; then
for gid in ${USER_OTHER_GIDS}; do
groupadd --force --gid "${gid}" "group-${gid}"
usermod -aG "${gid}" user
done
fi
fi
# Start
exec gosu user ./launcher

View File

@ -0,0 +1,6 @@
#include "../bootstrap.h"
int main(int argc, char *argv[]) {
// Bootstrap
bootstrap(argc, argv);
}

View File

@ -1,13 +1,11 @@
cmake_minimum_required(VERSION 3.13.0)
project(libreborn)
add_compile_options(-Wall -Wextra -Werror)
add_link_options(-Wl,--no-undefined)
add_library(reborn-headers INTERFACE)
target_include_directories(reborn-headers INTERFACE include)
if(BUILD_ARM_COMPONENTS)
add_library(reborn SHARED src/libreborn.c)
target_link_libraries(reborn dl)
target_include_directories(reborn PUBLIC include)
target_link_libraries(reborn dl reborn-headers)
# Install
install(TARGETS reborn DESTINATION /mods)
install(TARGETS reborn DESTINATION "${MCPI_LIB_DIR}")
endif()

View File

@ -0,0 +1,67 @@
#pragma once
#ifdef __arm__
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <elf.h>
#include "log.h"
// Find And Iterate Over All .text Sections In Current Binary
typedef void (*text_section_callback_t)(void *section, Elf32_Word size, void *data);
static inline void iterate_text_sections(text_section_callback_t callback, void *data) {
// Load Main Binary
char *real_path = realpath("/proc/self/exe", NULL);
FILE *file_obj = fopen(real_path, "rb");
// Verify Binary
if (!file_obj) {
ERR("Unable To Open Binary: %s", real_path);
}
// Get File Size
fseek(file_obj, 0L, SEEK_END);
long int size = ftell(file_obj);
fseek(file_obj, 0L, SEEK_SET);
// Map File To Pointer
unsigned char *file_map = (unsigned char *) mmap(0, size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0);
// Parse ELF
Elf32_Ehdr *elf_header = (Elf32_Ehdr *) file_map;
Elf32_Shdr *elf_section_headers = (Elf32_Shdr *) (file_map + elf_header->e_shoff);
int elf_section_header_count = elf_header->e_shnum;
// Locate Section Names
Elf32_Shdr elf_strtab = elf_section_headers[elf_header->e_shstrndx];
unsigned char *elf_strtab_p = file_map + elf_strtab.sh_offset;
// Track .text Sections
int text_sections = 0;
// Iterate Sections
for (int i = 0; i < elf_section_header_count; ++i) {
Elf32_Shdr header = elf_section_headers[i];
char *name = (char *) (elf_strtab_p + header.sh_name);
// Check Section Type
if (strcmp(name, ".text") == 0) {
// .text Section
(*callback)((void *) header.sh_addr, header.sh_size, data);
text_sections++;
}
}
// Ensure At Least .text Section Was Scanned
if (text_sections < 1) {
ERR("Unable To Find .text Sectons On: %s", real_path);
}
// Free Binary Path
free(real_path);
// Unmap And Close File
munmap(file_map, size);
fclose(file_obj);
}
#endif // #ifdef __arm__

View File

@ -0,0 +1,71 @@
#pragma once
#include <unistd.h>
#include <stdint.h>
#include <errno.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#include "log.h"
#include "string.h"
#include "util.h"
// Safe execvpe()
__attribute__((noreturn)) static inline void safe_execvpe(const char *pathname, char *argv[], char *const envp[]) {
argv[0] = (char *) pathname;
int ret = execvpe(pathname, argv, envp);
if (ret == -1) {
ERR("Unable To Execute Program: %s: %s", pathname, strerror(errno));
} else {
ERR("%s", "Unknown execvpe() Error");
}
}
// Get Binary Directory (Remember To Free)
#define EXE_PATH "/proc/self/exe"
static inline char *get_binary_directory() {
// Get Path To Current Executable
// Get Symlink Path Size
struct stat sb;
if (lstat(EXE_PATH, &sb) == -1) {
ERR("Unable To Get " EXE_PATH " Symlink Size: %s", strerror(errno));
}
ssize_t path_size = sb.st_size + 1;
if (sb.st_size == 0) {
path_size = PATH_MAX;
}
char *exe = (char *) malloc(path_size);
ALLOC_CHECK(exe);
// Read Link
ssize_t r = readlink(EXE_PATH, exe, path_size);
if (r < 0) {
ERR("Unable To Read " EXE_PATH " Symlink: %s", strerror(errno));
}
if (r > path_size) {
ERR("%s", "Size Of Symlink " EXE_PATH " Changed");
}
exe[path_size] = '\0';
// Chop Off Last Component
for (int i = path_size - 1; i >= 0; i--) {
if (exe[i] == '/') {
exe[i] = '\0';
break;
}
}
// Return
return exe;
}
// Safe execvpe() Relative To Binary
__attribute__((noreturn)) static inline void safe_execvpe_relative_to_binary(const char *pathname, char *argv[], 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, pathname);
// Free Binary Directory
free(binary_directory);
// Run
safe_execvpe(full_path, argv, envp);
}

View File

@ -4,39 +4,13 @@
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "log.h"
#include "util.h"
#include "string.h"
#include "exec.h"
#include "elf.h"
// Logging
#define INFO(msg, ...) fprintf(stderr, "[INFO]: " msg "\n", __VA_ARGS__);
#define ERR(msg, ...) fprintf(stderr, "[ERR]: " msg "\n", __VA_ARGS__); exit(EXIT_FAILURE);
// Check Memory Allocation
#define ALLOC_CHECK(obj) if (obj == NULL) { ERR("(%s:%i) Memory Allocation Failed", __FILE__, __LINE__); }
// Set obj To NULL On asprintf() Failure
#define asprintf(obj, ...) if (asprintf(obj, __VA_ARGS__) == -1) { *obj = NULL; }
// Hook Library Function
#define HOOK(name, return_type, args) \
typedef return_type (*name##_t)args; \
static name##_t real_##name = NULL; \
\
__attribute__((__unused__)) static void ensure_##name() { \
if (!real_##name) { \
dlerror(); \
real_##name = (name##_t) dlsym(RTLD_NEXT, #name); \
if (!real_##name) { \
ERR("Error Resolving Symbol: "#name": %s", dlerror()); \
} \
} \
}; \
\
__attribute__((__used__)) return_type name args
// Sanitize String
void sanitize_string(char **str, int max_length, unsigned int allow_newlines);
#ifdef __arm__
// Patching Functions
@ -55,6 +29,8 @@ void _patch(const char *file, int line, void *start, unsigned char patch[]);
void _patch_address(const char *file, int line, void *start, void *target);
#define patch_address(start, target) _patch_address(__FILE__, __LINE__, start, target);
#endif // #ifdef __arm__
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,9 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
// Logging
#define INFO(format, ...) { fprintf(stderr, "[INFO]: " format "\n", __VA_ARGS__); }
#define WARN(format, ...) { fprintf(stderr, "[WARN]: " format "\n", __VA_ARGS__); }
#define ERR(format, ...) { fprintf(stderr, "[ERR]: " format "\n", __VA_ARGS__); exit(EXIT_FAILURE); }

View File

@ -404,7 +404,7 @@ struct ConnectedClient {
// AppPlatform
typedef void (*AppPlatform_saveScreenshot_t)(unsigned char *app_platform, std::string const& param1, std::string const& param_2);
typedef void (*AppPlatform_saveScreenshot_t)(unsigned char *app_platform, std::string const& path, int32_t width, int32_t height);
static void *AppPlatform_linux_saveScreenshot_vtable_addr = (void *) 0x102160;
typedef AppPlatform_readAssetFile_return_value (*AppPlatform_readAssetFile_t)(unsigned char *app_platform, std::string const& path);

View File

@ -0,0 +1,52 @@
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "util.h"
// Set obj To NULL On asprintf() Failure
#define safe_asprintf(obj, ...) \
{ \
if (asprintf(obj, __VA_ARGS__) == -1) { \
*obj = NULL; \
} \
ALLOC_CHECK(*obj); \
}
// Dynamic String Append Macro
#define string_append(str, format, ...) \
{ \
char *old = *str; \
safe_asprintf(str, "%s" format, *str == NULL ? "" : *str, __VA_ARGS__); \
ALLOC_CHECK(*str); \
if (old != NULL && old != *str) { \
free(old); \
} \
}
// Sanitize String
#define MINIMUM_SAFE_CHARACTER 32
#define MAXIMUM_SAFE_CHARACTER 126
#define MINIMUM_EXTENDED_SAFE_CHARACTER 128
static inline void sanitize_string(char **str, int max_length, unsigned int allow_newlines) {
// Store Message Length
int length = strlen(*str);
// Truncate Message
if (max_length != -1 && length > max_length) {
(*str)[max_length] = '\0';
length = max_length;
}
// Loop Through Message
for (int i = 0; i < length; i++) {
if (allow_newlines && ((*str)[i] == '\n' || (*str)[i] == '\r')) {
continue;
}
unsigned char c = (unsigned char) (*str)[i];
if ((c < MINIMUM_SAFE_CHARACTER || c > MAXIMUM_SAFE_CHARACTER) && c < MINIMUM_EXTENDED_SAFE_CHARACTER) {
// Replace Illegal Character
(*str)[i] = '?';
}
}
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include "log.h"
// Check Memory Allocation
#define ALLOC_CHECK(obj) \
{ \
if (obj == NULL) { \
ERR("(%s:%i) Memory Allocation Failed", __FILE__, __LINE__); \
} \
}
// Hook Library Function
#define HOOK(name, return_type, args) \
typedef return_type (*name##_t)args; \
static name##_t real_##name = NULL; \
\
__attribute__((__unused__)) static void ensure_##name() { \
if (!real_##name) { \
dlerror(); \
real_##name = (name##_t) dlsym(RTLD_NEXT, #name); \
if (!real_##name) { \
ERR("Error Resolving Symbol: "#name": %s", dlerror()); \
} \
} \
}; \
\
__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); \
} \
}
// Safe Version Of pipe()
static inline void safe_pipe2(int pipefd[2], int flags) {
if (pipe2(pipefd, flags) != 0) {
ERR("Unable To Create Pipe: %s", strerror(errno));
}
}

View File

@ -4,65 +4,14 @@
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <elf.h>
#include <errno.h>
#include <elf.h>
#include <libreborn/libreborn.h>
// Find And Iterate Over All .text Sections In Current Binary
typedef void (*text_section_callback)(void *section, Elf32_Word size, void *data);
static void iterate_text_section(text_section_callback callback, void *data) {
// Load Main Binary
char *real_path = realpath("/proc/self/exe", NULL);
FILE *file_obj = fopen(real_path, "rb");
// Verify Binary
if (!file_obj) {
ERR("Unable To Open Binary: %s", real_path);
}
// Get File Size
fseek(file_obj, 0L, SEEK_END);
long int size = ftell(file_obj);
fseek(file_obj, 0L, SEEK_SET);
// Map File To Pointer
unsigned char *file_map = mmap(0, size, PROT_READ, MAP_PRIVATE, fileno(file_obj), 0);
// Parse ELF
Elf32_Ehdr *elf_header = (Elf32_Ehdr *) file_map;
Elf32_Shdr *elf_section_headers = (Elf32_Shdr *) (file_map + elf_header->e_shoff);
int elf_section_header_count = elf_header->e_shnum;
// Locate Section Names
Elf32_Shdr elf_strtab = elf_section_headers[elf_header->e_shstrndx];
unsigned char *elf_strtab_p = file_map + elf_strtab.sh_offset;
// Track .text Sections
int text_sections = 0;
// Iterate Sections
for (int i = 0; i < elf_section_header_count; ++i) {
Elf32_Shdr header = elf_section_headers[i];
char *name = (char *) (elf_strtab_p + header.sh_name);
if (strcmp(name, ".text") == 0) {
(*callback)((void *) header.sh_addr, header.sh_size, data);
text_sections++;
}
}
// Ensure At Least .text Section Was Scanned
if (text_sections < 1) {
ERR("Unable To Find .text Sectons On: %s", real_path);
}
// Free Binary Path
free(real_path);
// Unmap And Close File
munmap(file_map, size);
fclose(file_obj);
}
#ifndef __arm__
#error "Patching Code Is ARM Only"
#endif // #ifndef __arm__
// BL Instruction Magic Number
#define BL_INSTRUCTION 0xeb
@ -124,7 +73,9 @@ static void update_code_block(void *target) {
if (code_block == MAP_FAILED) {
ERR("Unable To Allocate Code Block: %s", strerror(errno));
}
#ifdef DEBUG
INFO("Code Block Allocated At: 0x%08x", (uint32_t) code_block);
#endif
}
if (code_block_remaining < CODE_SIZE) {
ERR("%s", "Maximum Amount Of overwrite_calls() Uses Reached");
@ -160,7 +111,7 @@ void _overwrite_calls(const char *file, int line, void *start, void *target) {
data.replacement = code_block;
data.found = 0;
iterate_text_section(overwrite_calls_callback, &data);
iterate_text_sections(overwrite_calls_callback, &data);
// Increment Code Block Position
increment_code_block();
@ -179,10 +130,18 @@ void _overwrite(const char *file, int line, void *start, void *target) {
}
// Print Patch Debug Data
#ifdef DEBUG
#define PATCH_PRINTF(file, line, start, str) if (file != NULL) fprintf(stderr, "[PATCH]: (%s:%i) Patching (0x%08x) - "str": 0x%02x 0x%02x 0x%02x 0x%02x\n", file, line, (uint32_t) start, data[0], data[1], data[2], data[3]);
#else
#define PATCH_PRINTF(file, line, start, str) { (void) file; (void) line; (void) start; (void) str; } // Mark As Used
#endif
// Patch Instruction
void _patch(const char *file, int line, void *start, unsigned char patch[]) {
if (((uint32_t) start) % 4 != 0) {
ERR("%s", "Invalid Address");
}
size_t page_size = sysconf(_SC_PAGESIZE);
uintptr_t end = ((uintptr_t) start) + 4;
uintptr_t page_start = ((uintptr_t) start) & -page_size;
@ -210,27 +169,3 @@ void _patch_address(const char *file, int line, void *start, void *target) {
unsigned char patch_data[4] = {addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff};
_patch(file, line, start, patch_data);
}
// Sanitize String
#define MINIMUM_SAFE_CHARACTER 32
#define MAXIMUM_SAFE_CHARACTER 126
#define MINIMUM_EXTENDED_SAFE_CHARACTER 128
void sanitize_string(char **str, int max_length, unsigned int allow_newlines) {
// Store Message Length
int length = strlen(*str);
// Truncate Message
if (max_length != -1 && length > max_length) {
(*str)[max_length] = '\0';
length = max_length;
}
// Loop Through Message
for (int i = 0; i < length; i++) {
if (allow_newlines && ((*str)[i] == '\n' || (*str)[i] == '\r')) {
continue;
}
if (((*str)[i] < MINIMUM_SAFE_CHARACTER || (*str)[i] > MAXIMUM_SAFE_CHARACTER) && (*str)[i] < MINIMUM_EXTENDED_SAFE_CHARACTER) {
// Replace Illegal Character
(*str)[i] = '?';
}
}
}

View File

@ -0,0 +1,40 @@
project(media-layer)
# Check Options
if(MCPI_USE_MEDIA_LAYER_PROXY)
if(MCPI_SERVER_MODE)
message(FATAL_ERROR "Server 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_library(media-layer-headers INTERFACE)
target_include_directories(media-layer-headers INTERFACE include)
# Add Core
add_subdirectory(core)
# Add Proxy
if(MCPI_USE_MEDIA_LAYER_PROXY)
add_subdirectory(proxy)
endif()
# Add Stubs
add_subdirectory(stubs)
# Add Extras
add_subdirectory(extras)
# Add Symlinks So MCPI Can Locate Libraries
if(BUILD_ARM_COMPONENTS)
if(MCPI_SERVER_MODE OR MCPI_USE_MEDIA_LAYER_PROXY)
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libX11.so.6")
else()
# When Loading In Client Mode On An ARM Host, Use Native X11 By Default
install_symlink("../lib/libmedia-layer-core.so" "${MCPI_FALLBACK_LIB_DIR}/libX11.so.6")
endif()
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libSDL-1.2.so.0")
endif()

View File

@ -0,0 +1,29 @@
project(media-layer-core)
# Configuration
set(CORE_SRC src/base.cpp src/media.c src/screenshot.c) # SDL Re-Implementation Using GLFW
# 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
# Install
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
endif()
# Configure Media Layer Core If Built
if(TARGET media-layer-core)
# Link
target_link_libraries(media-layer-core media-layer-headers reborn-headers pthread dl)
if(NOT MCPI_SERVER_MODE)
# Find GLFW
find_package(glfw3 3.2 REQUIRED)
# Find FreeImage
find_library(FREEIMAGE_LIBRARY NAMES freeimage libfreeimage.so.3 REQUIRED)
# Not Needed In Server Mode
target_link_libraries(media-layer-core "${FREEIMAGE_LIBRARY}" GLESv1_CM glfw)
endif()
endif()

View File

@ -1,25 +1,27 @@
#include <stdlib.h>
#include <cstdlib>
#include <pthread.h>
#include <vector>
#include <SDL/SDL.h>
#include <libreborn/media-layer/internal.h>
// SDL Is Replaced With GLFW
int SDL_Init(__attribute__((unused)) uint32_t flags) {
return 0;
}
void SDL_Quit() {
exit(EXIT_SUCCESS);
}
// Event Queue
static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
static std::vector<SDL_Event> queue;
int SDL_PollEvent(SDL_Event *event) {
// Handle External Events
_media_handle_SDL_PollEvent();
// Poll Event
pthread_mutex_lock(&queue_mutex);
int ret;
if (queue.size() > 0) {

View File

@ -0,0 +1,326 @@
#include <unistd.h>
#include <SDL/SDL.h>
#include <GLES/gl.h>
#ifndef MCPI_SERVER_MODE
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#endif // #ifndef MCPI_SERVER_MODE
#include <libreborn/libreborn.h>
#include <libreborn/media-layer/core.h>
#include <libreborn/media-layer/internal.h>
// GLFW Code Not Needed In Server Mode
#ifndef MCPI_SERVER_MODE
static GLFWwindow *glfw_window;
// Handle GLFW Error
static void glfw_error(__attribute__((unused)) int error, const char *description) {
WARN("GLFW Error: %s", description);
}
// Convert GLFW Key To SDL Key
static SDLKey glfw_key_to_sdl_key(int key) {
switch (key) {
// Movement
case GLFW_KEY_W:
return SDLK_w;
case GLFW_KEY_A:
return SDLK_a;
case GLFW_KEY_S:
return SDLK_s;
case GLFW_KEY_D:
return SDLK_d;
case GLFW_KEY_SPACE:
return SDLK_SPACE;
case GLFW_KEY_LEFT_SHIFT:
return SDLK_LSHIFT;
case GLFW_KEY_RIGHT_SHIFT:
return SDLK_RSHIFT;
// Inventory
case GLFW_KEY_E:
return SDLK_e;
// Hotbar
case GLFW_KEY_1:
return SDLK_1;
case GLFW_KEY_2:
return SDLK_2;
case GLFW_KEY_3:
return SDLK_3;
case GLFW_KEY_4:
return SDLK_4;
case GLFW_KEY_5:
return SDLK_5;
case GLFW_KEY_6:
return SDLK_6;
case GLFW_KEY_7:
return SDLK_7;
case GLFW_KEY_8:
return SDLK_8;
// UI Control
case GLFW_KEY_ESCAPE:
return SDLK_ESCAPE;
case GLFW_KEY_UP:
return SDLK_UP;
case GLFW_KEY_DOWN:
return SDLK_DOWN;
case GLFW_KEY_LEFT:
return SDLK_LEFT;
case GLFW_KEY_RIGHT:
return SDLK_RIGHT;
case GLFW_KEY_TAB:
return SDLK_TAB;
case GLFW_KEY_ENTER:
return SDLK_RETURN;
case GLFW_KEY_BACKSPACE:
return SDLK_BACKSPACE;
// Fullscreen
case GLFW_KEY_F11:
return SDLK_F11;
// Screenshot
case GLFW_KEY_F2:
return SDLK_F2;
// Hide GUI
case GLFW_KEY_F1:
return SDLK_F1;
// Third Person
case GLFW_KEY_F5:
return SDLK_F5;
// Chat
case GLFW_KEY_T:
return SDLK_t;
// Unknown
default:
return SDLK_UNKNOWN;
}
}
// Pass Character Event
static void character_event(char c) {
// SDL_UserEvent Is Never Used In MCPI, So It Is Repurposed For Character Events
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = (int) c;
SDL_PushEvent(&event);
}
// Pass Key Presses To SDL
static void glfw_key(__attribute__((unused)) GLFWwindow *window, int key, int scancode, int action, __attribute__((unused)) int mods) {
SDL_Event event;
int up = action == GLFW_RELEASE;
event.type = up ? SDL_KEYUP : SDL_KEYDOWN;
event.key.state = up ? SDL_RELEASED : SDL_PRESSED;
event.key.keysym.scancode = scancode;
event.key.keysym.mod = KMOD_NONE;
event.key.keysym.sym = glfw_key_to_sdl_key(key);
SDL_PushEvent(&event);
if (key == GLFW_KEY_BACKSPACE && !up) {
character_event((char) '\b');
}
}
// Pass Text To Minecraft
static void glfw_char(__attribute__((unused)) GLFWwindow *window, unsigned int codepoint) {
character_event((char) codepoint);
}
static double last_mouse_x = 0;
static double last_mouse_y = 0;
static int ignore_relative_mouse = 1;
// Pass Mouse Movement To SDL
static void glfw_motion(__attribute__((unused)) GLFWwindow *window, double xpos, double ypos) {
SDL_Event event;
event.type = SDL_MOUSEMOTION;
event.motion.x = xpos;
event.motion.y = ypos;
event.motion.xrel = !ignore_relative_mouse ? (xpos - last_mouse_x) : 0;
event.motion.yrel = !ignore_relative_mouse ? (ypos - last_mouse_y) : 0;
ignore_relative_mouse = 0;
last_mouse_x = xpos;
last_mouse_y = ypos;
SDL_PushEvent(&event);
}
// Create And Push SDL Mouse Click Event
static void click_event(int button, int up) {
SDL_Event event;
event.type = up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN;
event.button.x = last_mouse_x;
event.button.y = last_mouse_y;
event.button.state = up ? SDL_RELEASED : SDL_PRESSED;
event.button.button = button;
SDL_PushEvent(&event);
}
// Pass Mouse Click To SDL
static void glfw_click(__attribute__((unused)) GLFWwindow *window, int button, int action, __attribute__((unused)) int mods) {
int up = action == GLFW_RELEASE;
int sdl_button = button == GLFW_MOUSE_BUTTON_RIGHT ? SDL_BUTTON_RIGHT : (button == GLFW_MOUSE_BUTTON_LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_MIDDLE);
click_event(sdl_button, up);
}
// Pass Mouse Scroll To SDL
static void glfw_scroll(__attribute__((unused)) GLFWwindow *window, __attribute__((unused)) double xoffset, double yoffset) {
if (yoffset != 0) {
int sdl_button = yoffset > 0 ? SDL_BUTTON_WHEELUP : SDL_BUTTON_WHEELDOWN;
click_event(sdl_button, 0);
click_event(sdl_button, 1);
}
}
#endif // #ifndef MCPI_SERVER_MODE
// Init GLFW
void SDL_WM_SetCaption(const char *title, __attribute__((unused)) const char *icon) {
// Don't Enable GLFW In Server Mode
#ifndef MCPI_SERVER_MODE
glfwSetErrorCallback(glfw_error);
if (!glfwInit()) {
ERR("%s", "Unable To Initialize GLFW");
}
// Create OpenGL ES 1.1 Context
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 1);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
// Extra Settings
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
glfwWindowHint(GLFW_ALPHA_BITS, 0); // Fix Transparent Window On Wayland
glfw_window = glfwCreateWindow(DEFAULT_WIDTH, DEFAULT_HEIGHT, title, NULL, NULL);
if (!glfw_window) {
ERR("%s", "Unable To Create GLFW Window");
}
// Don't Process Events In Server Mode
glfwSetKeyCallback(glfw_window, glfw_key);
glfwSetCharCallback(glfw_window, glfw_char);
glfwSetCursorPosCallback(glfw_window, glfw_motion);
glfwSetMouseButtonCallback(glfw_window, glfw_click);
glfwSetScrollCallback(glfw_window, glfw_scroll);
glfwMakeContextCurrent(glfw_window);
#else // #ifndef MCPI_SERVER_MODE
(void) title; // Mark As Used
#endif // #ifndef MCPI_SERVER_MODE
}
void media_swap_buffers() {
#ifndef MCPI_SERVER_MODE
// Don't Swap Buffers In A Context-Less Window
glfwSwapBuffers(glfw_window);
#endif // #ifndef MCPI_SERVER_MODE
}
// Fullscreen Not Needed In Server Mode
#ifndef MCPI_SERVER_MODE
static int is_fullscreen = 0;
// Old Size And Position To Use When Exiting Fullscreen
static int old_width = -1;
static int old_height = -1;
static int old_x = -1;
static int old_y = -1;
// Toggle Fullscreen
void media_toggle_fullscreen() {
if (is_fullscreen) {
glfwSetWindowMonitor(glfw_window, NULL, old_x, old_y, old_width, old_height, GLFW_DONT_CARE);
old_width = -1;
old_height = -1;
old_x = -1;
old_y = -1;
} else {
glfwGetWindowSize(glfw_window, &old_width, &old_height);
glfwGetWindowPos(glfw_window, &old_x, &old_y);
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
const GLFWvidmode *mode = glfwGetVideoMode(monitor);
glfwSetWindowMonitor(glfw_window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE);
}
is_fullscreen = !is_fullscreen;
}
#else // #ifndef MCPI_SERVER_MODE
void media_toggle_fullscreen() {
}
#endif // #ifndef MCPI_SERVER_MODE
// Intercept SDL Events
void _media_handle_SDL_PollEvent() {
// GLFW Is Disabled In Server Mode
#ifndef MCPI_SERVER_MODE
// Process GLFW Events
glfwPollEvents();
// Close Window
if (glfwWindowShouldClose(glfw_window)) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
glfwSetWindowShouldClose(glfw_window, GLFW_FALSE);
}
#endif // #ifndef MCPI_SERVER_MODE
}
// Terminate GLFW
void media_cleanup() {
// GLFW Is Disabled In Server Mode
#ifndef MCPI_SERVER_MODE
glfwDestroyWindow(glfw_window);
glfwTerminate();
#endif // #ifndef MCPI_SERVER_MODE
}
#ifdef MCPI_SERVER_MODE
static SDL_GrabMode fake_grab_mode = SDL_GRAB_OFF;
#endif // #ifdef MCPI_SERVER_MODE
// Fix SDL Cursor Visibility/Grabbing
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
#ifdef MCPI_SERVER_MODE
// Don't Grab Input In Server Mode
if (mode != SDL_GRAB_QUERY) {
fake_grab_mode = mode;
}
return fake_grab_mode;
#else // #ifdef MCPI_SERVER_MODE
if (mode != SDL_GRAB_QUERY && mode != SDL_WM_GrabInput(SDL_GRAB_QUERY)) {
glfwSetInputMode(glfw_window, GLFW_CURSOR, mode == SDL_GRAB_OFF ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3)
glfwSetInputMode(glfw_window, GLFW_RAW_MOUSE_MOTION, mode == SDL_GRAB_OFF ? GLFW_FALSE : GLFW_TRUE);
#endif // #if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 3)
// Reset Last Mouse Position
ignore_relative_mouse = 1;
}
return mode == SDL_GRAB_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_GRAB_OFF : SDL_GRAB_ON) : mode;
#endif // #ifdef MCPI_SERVER_MODE
}
// Stub SDL Cursor Visibility
int SDL_ShowCursor(int toggle) {
#ifdef MCPI_SERVER_MODE
return toggle == SDL_QUERY ? (fake_grab_mode == SDL_GRAB_OFF ? SDL_ENABLE : SDL_DISABLE) : toggle;
#else // #ifdef MCPI_SERVER_MODE
return toggle == SDL_QUERY ? (glfwGetInputMode(glfw_window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL ? SDL_ENABLE : SDL_DISABLE) : toggle;
#endif // #ifdef MCPI_SERVER_MODE
}
// Get Framebuffer Size
void media_get_framebuffer_size(int *width, int *height) {
#ifndef MCPI_SERVER_MODE
if (glfw_window != NULL) {
glfwGetFramebufferSize(glfw_window, width, height);
return;
}
#endif // #ifndef MCPI_SERVER_MODE
*width = DEFAULT_WIDTH;
*height = DEFAULT_HEIGHT;
}

View File

@ -1,4 +1,5 @@
#define _GNU_SOURCE
// Screenshot Code Is Useless In Server Mode
#ifndef MCPI_SERVER_MODE
#include <stdlib.h>
#include <stdio.h>
@ -13,14 +14,13 @@
#include <GLES/gl.h>
#include <libreborn/libreborn.h>
#include "screenshot.h"
#include <libreborn/media-layer/core.h>
// 4 (Year) + 1 (Hyphen) + 2 (Month) + 1 (Hyphen) + 2 (Day) + 1 (Underscore) + 2 (Hour) + 1 (Period) + 2 (Minute) + 1 (Period) + 2 (Second) + 1 (Null Terminator)
#define TIME_SIZE 20
// Take Screenshot
void take_screenshot() {
void media_take_screenshot() {
time_t rawtime;
struct tm *timeinfo;
@ -30,16 +30,15 @@ void take_screenshot() {
strftime(time, TIME_SIZE, "%Y-%m-%d_%H.%M.%S", timeinfo);
char *screenshots = NULL;
asprintf(&screenshots, "%s/.minecraft-pi/screenshots", getenv("HOME"));
ALLOC_CHECK(screenshots);
safe_asprintf(&screenshots, "%s/.minecraft-pi/screenshots", getenv("HOME"));
int num = 1;
char *file = NULL;
asprintf(&file, "%s/%s.png", screenshots, time);
ALLOC_CHECK(file);
safe_asprintf(&file, "%s/%s.png", screenshots, time);
while (access(file, F_OK) != -1) {
asprintf(&file, "%s/%s-%i.png", screenshots, time, num);
ALLOC_CHECK(file);
free(file);
file = NULL;
safe_asprintf(&file, "%s/%s-%i.png", screenshots, time, num);
num++;
}
@ -56,7 +55,7 @@ void take_screenshot() {
unsigned char pixels[size];
glReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#if __BYTE_ORDER == __LITTLE_ENDIAN
// Swap Red And Blue
for (int i = 0; i < (size / 3); i++) {
int pixel = i * 3;
@ -86,8 +85,7 @@ __attribute__((constructor)) static void init() {
// Screenshots Folder
char *screenshots_folder = NULL;
asprintf(&screenshots_folder, "%s/.minecraft-pi/screenshots", getenv("HOME"));
ALLOC_CHECK(screenshots_folder);
safe_asprintf(&screenshots_folder, "%s/.minecraft-pi/screenshots", getenv("HOME"));
{
// Check Screenshots Folder
struct stat obj;
@ -102,3 +100,9 @@ __attribute__((constructor)) static void init() {
}
free(screenshots_folder);
}
#else // #ifndef MCPI_SERVER_MODE
void media_take_screenshot() {
// NOP
}
#endif // #ifndef MCPI_SERVER_MODE

View File

@ -0,0 +1,7 @@
project(media-layer-extras)
if(BUILD_ARM_COMPONENTS)
# Add Source To Media Core
target_sources(media-layer-core PRIVATE src/SDL.c src/X11.cpp)
target_link_libraries(media-layer-core dl)
endif()

View File

@ -0,0 +1,41 @@
#include <SDL/SDL.h>
#include <SDL/SDL_syswm.h>
#include <X11/Xlib.h>
#include <sys/wait.h>
#include <libreborn/libreborn.h>
#include <libreborn/media-layer/core.h>
// SDL Stub
void *SDL_SetVideoMode(__attribute__((unused)) int width, __attribute__((unused)) int height, __attribute__((unused)) int bpp, __attribute__((unused)) uint32_t flags) {
// Return Value Is Only Used For A NULL-Check
return (void *) 1;
}
static void x11_nop() {
// NOP
}
int SDL_GetWMInfo(SDL_SysWMinfo *info) {
// Return Fake Lock Functions Since XLib Isn't Directly Used
SDL_SysWMinfo ret;
ret.info.x11.lock_func = x11_nop;
ret.info.x11.unlock_func = x11_nop;
ret.info.x11.display = NULL;
ret.info.x11.window = 0;
ret.info.x11.wmwindow = ret.info.x11.window;
*info = ret;
return 1;
}
// Quit
__attribute__ ((noreturn)) void SDL_Quit() {
// Cleanup Media Layer
media_cleanup();
// Wait For Children To Stop
while (wait(NULL) > 0) {}
// Exit
INFO("%s", "Stopped");
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,76 @@
#include <elf.h>
#include <vector>
#include <X11/Xlib.h>
#include <libreborn/libreborn.h>
#include <libreborn/media-layer/core.h>
#ifndef MEDIA_LAYER_PROXY_SERVER
// Store Adresses That Are Part Of MCPI
struct text_section_data {
void *section;
Elf32_Word size;
};
static std::vector<text_section_data> &get_text_sections() {
static std::vector<text_section_data> sections;
return sections;
}
static void text_section_callback(void *section, Elf32_Word size, __attribute__((unused)) void *data) {
text_section_data section_data;
section_data.section = section;
section_data.size = size;
get_text_sections().push_back(section_data);
}
// Check If The Current Return Address Is Part Of MCPI Or An External Library
static inline int _is_returning_to_external_library(void *return_addr) {
// Load Text Sections If Not Loaded
if (get_text_sections().size() < 1) {
iterate_text_sections(text_section_callback, NULL);
}
// Iterate Text Sections
for (std::vector<text_section_data>::size_type i = 0; i < get_text_sections().size(); i++) {
text_section_data section_data = get_text_sections()[i];
// Check Text Section
if (return_addr >= section_data.section && return_addr < (((unsigned char *) section_data.section) + section_data.size)) {
// Part Of MCPI
return 0;
}
}
// Part Of An External Library
return 1;
}
#define is_returning_to_external_library() _is_returning_to_external_library(__builtin_extract_return_addr(__builtin_return_address(0)))
#else
// When Using The Media Layer Proxy, None Of These Functions Are Ever Called By An External Library
#define is_returning_to_external_library() 0
#endif
// Don't Directly Use XLib Unless Returning To An External library
HOOK(XTranslateCoordinates, int, (void *display, XID src_w, XID dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, XID *child_return)) {
if (is_returning_to_external_library()) {
// Use Real Function
ensure_XTranslateCoordinates();
return (*real_XTranslateCoordinates)(display, src_w, dest_w, src_x, src_y, dest_x_return, dest_y_return, child_return);
} else {
// Use MCPI Replacemnt Function
*dest_x_return = src_x;
*dest_y_return = src_y;
return 1;
}
}
HOOK(XGetWindowAttributes, int, (void *display, XID w, XWindowAttributes *window_attributes_return)) {
if (is_returning_to_external_library()) {
// Use Real Function
ensure_XGetWindowAttributes();
return (*real_XGetWindowAttributes)(display, w, window_attributes_return);
} else {
// Use MCPI Replacemnt Function
XWindowAttributes attributes;
attributes.x = 0;
attributes.y = 0;
media_get_framebuffer_size(&attributes.width, &attributes.height);
*window_attributes_return = attributes;
return 1;
}
}

View File

@ -0,0 +1,40 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <X11/Xlib.h>
#define EGL_TRUE 1
typedef unsigned int EGLenum;
typedef unsigned int EGLBoolean;
typedef void *EGLDisplay;
typedef void *EGLConfig;
typedef void *EGLSurface;
typedef void *EGLContext;
typedef int32_t EGLint;
typedef void *EGLNativeDisplayType;
typedef XID EGLNativeWindowType;
typedef EGLNativeWindowType NativeWindowType;
typedef EGLNativeDisplayType NativeDisplayType;
EGLDisplay eglGetDisplay(NativeDisplayType native_display);
EGLBoolean eglInitialize(EGLDisplay display, EGLint *major, EGLint *minor);
EGLBoolean eglChooseConfig(EGLDisplay display, EGLint const *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
EGLBoolean eglBindAPI(EGLenum api);
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, EGLint const *attrib_list);
EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, NativeWindowType native_window, EGLint const *attrib_list);
EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
EGLBoolean eglDestroySurface(EGLDisplay display, EGLSurface surface);
EGLBoolean eglDestroyContext(EGLDisplay display, EGLContext context);
EGLBoolean eglTerminate(EGLDisplay display);
EGLBoolean eglSwapBuffers(EGLDisplay display, EGLSurface surface);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,86 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define GL_FALSE 0
#define GL_FOG_COLOR 0x0B66
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_UNSIGNED_BYTE 0x1401
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_MODELVIEW_MATRIX 0x0BA6
#define GL_PROJECTION_MATRIX 0x0BA7
#define GL_VIEWPORT 0x0BA2
#define GL_DEPTH_TEST 0x0B71
#include <stdio.h>
#include <stdint.h>
typedef float GLfloat;
typedef float GLclampf;
typedef int GLint;
typedef unsigned char GLboolean;
typedef int GLsizei;
typedef unsigned int GLuint;
typedef ssize_t GLsizeiptr;
typedef intptr_t GLintptr;
typedef int32_t GLfixed;
typedef unsigned int GLbitfield;
typedef unsigned int GLenum;
void glFogfv(GLenum pname, const GLfloat *params);
void glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void glLineWidth(GLfloat width);
void glBlendFunc(GLenum sfactor, GLenum dfactor);
void glDrawArrays(GLenum mode, GLint first, GLsizei count);
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
void glClear(GLbitfield mask);
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage);
void glFogx(GLenum pname, GLfixed param);
void glFogf(GLenum pname, GLfloat param);
void glMatrixMode(GLenum mode);
void glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
void glTexParameteri(GLenum target, GLenum pname, GLint param);
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
void glEnable(GLenum cap);
void glEnableClientState(GLenum array);
void glPolygonOffset(GLfloat factor, GLfloat units);
void glDisableClientState(GLenum array);
void glDepthRangef(GLclampf near, GLclampf far);
void glDepthFunc(GLenum func);
void glBindBuffer(GLenum target, GLuint buffer);
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
void glPopMatrix();
void glLoadIdentity();
void glScalef(GLfloat x, GLfloat y, GLfloat z);
void glPushMatrix();
void glDepthMask(GLboolean flag);
void glHint(GLenum target, GLenum mode);
void glMultMatrixf(const GLfloat *m);
void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void glDeleteBuffers(GLsizei n, const GLuint *buffers);
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 glGenTextures(GLsizei n, GLuint *textures);
void glDeleteTextures(GLsizei n, const GLuint *textures);
void glAlphaFunc(GLenum func, GLclampf ref);
void glGetFloatv(GLenum pname, GLfloat *params);
void glBindTexture(GLenum target, GLuint texture);
void glTranslatef(GLfloat x, GLfloat y, GLfloat z);
void glShadeModel(GLenum mode);
void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far);
void glDisable(GLenum cap);
void glCullFace(GLenum mode);
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz);
GLboolean glIsEnabled(GLenum cap);
void glGetIntegerv(GLenum pname, GLint *data);
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,39 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "SDL_keysym.h"
#include "SDL_events.h"
#include "SDL_syswm.h"
#include "SDL_version.h"
int SDL_Init(uint32_t flags);
int SDL_PollEvent(SDL_Event *event);
int SDL_PushEvent(SDL_Event *event);
void SDL_WM_SetCaption(const char *title, const char *icon);
typedef enum {
SDL_GRAB_QUERY = -1,
SDL_GRAB_OFF = 0,
SDL_GRAB_ON = 1,
SDL_GRAB_FULLSCREEN
} SDL_GrabMode;
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode);
#define SDL_QUERY -1
#define SDL_IGNORE 0
#define SDL_DISABLE 0
#define SDL_ENABLE 1
int SDL_ShowCursor(int toggle);
void *SDL_SetVideoMode(int width, int height, int bpp, uint32_t flags);
int SDL_GetWMInfo(SDL_SysWMinfo *info);
__attribute__ ((noreturn)) void SDL_Quit();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,160 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "SDL_keysym.h"
#define SDL_RELEASED 0
#define SDL_PRESSED 1
typedef enum {
SDL_NOEVENT = 0,
SDL_ACTIVEEVENT,
SDL_KEYDOWN,
SDL_KEYUP,
SDL_MOUSEMOTION,
SDL_MOUSEBUTTONDOWN,
SDL_MOUSEBUTTONUP,
SDL_JOYAXISMOTION,
SDL_JOYBALLMOTION,
SDL_JOYHATMOTION,
SDL_JOYBUTTONDOWN,
SDL_JOYBUTTONUP,
SDL_QUIT,
SDL_SYSWMEVENT,
SDL_EVENT_RESERVEDA,
SDL_EVENT_RESERVEDB,
SDL_VIDEORESIZE,
SDL_VIDEOEXPOSE,
SDL_EVENT_RESERVED2,
SDL_EVENT_RESERVED3,
SDL_EVENT_RESERVED4,
SDL_EVENT_RESERVED5,
SDL_EVENT_RESERVED6,
SDL_EVENT_RESERVED7,
SDL_USEREVENT = 24,
SDL_NUMEVENTS = 32
} SDL_EventType;
typedef struct SDL_ActiveEvent {
uint8_t type;
uint8_t gain;
uint8_t state;
} SDL_ActiveEvent;
typedef struct SDL_keysym {
uint8_t scancode;
SDLKey sym;
SDLMod mod;
uint16_t unicode;
} SDL_keysym;
typedef struct SDL_KeyboardEvent {
uint8_t type;
uint8_t which;
uint8_t state;
SDL_keysym keysym;
} SDL_KeyboardEvent;
typedef struct SDL_MouseMotionEvent {
uint8_t type;
uint8_t which;
uint8_t state;
uint16_t x, y;
int16_t xrel;
int16_t yrel;
} SDL_MouseMotionEvent;
#define SDL_BUTTON_LEFT 1
#define SDL_BUTTON_MIDDLE 2
#define SDL_BUTTON_RIGHT 3
#define SDL_BUTTON_WHEELUP 4
#define SDL_BUTTON_WHEELDOWN 5
typedef struct SDL_MouseButtonEvent {
uint8_t type;
uint8_t which;
uint8_t button;
uint8_t state;
uint16_t x, y;
} SDL_MouseButtonEvent;
typedef struct SDL_JoyAxisEvent {
uint8_t type;
uint8_t which;
uint8_t axis;
int16_t value;
} SDL_JoyAxisEvent;
typedef struct SDL_JoyBallEvent {
uint8_t type;
uint8_t which;
uint8_t ball;
int16_t xrel;
int16_t yrel;
} SDL_JoyBallEvent;
typedef struct SDL_JoyHatEvent {
uint8_t type;
uint8_t which;
uint8_t hat;
uint8_t value;
} SDL_JoyHatEvent;
typedef struct SDL_JoyButtonEvent {
uint8_t type;
uint8_t which;
uint8_t button;
uint8_t state;
} SDL_JoyButtonEvent;
typedef struct SDL_ResizeEvent {
uint8_t type;
int w;
int h;
} SDL_ResizeEvent;
typedef struct SDL_ExposeEvent {
uint8_t type;
} SDL_ExposeEvent;
typedef struct SDL_QuitEvent {
uint8_t type;
} SDL_QuitEvent;
typedef struct SDL_UserEvent {
uint8_t type;
int code;
void *data1;
void *data2;
} SDL_UserEvent;
typedef struct SDL_SysWMEvent {
uint8_t type;
void *msg;
} SDL_SysWMEvent;
typedef union SDL_Event {
uint8_t type;
SDL_ActiveEvent active;
SDL_KeyboardEvent key;
SDL_MouseMotionEvent motion;
SDL_MouseButtonEvent button;
SDL_JoyAxisEvent jaxis;
SDL_JoyBallEvent jball;
SDL_JoyHatEvent jhat;
SDL_JoyButtonEvent jbutton;
SDL_ResizeEvent resize;
SDL_ExposeEvent expose;
SDL_QuitEvent quit;
SDL_UserEvent user;
SDL_SysWMEvent syswm;
} SDL_Event;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,262 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SDLK_UNKNOWN = 0,
SDLK_FIRST = 0,
SDLK_BACKSPACE = 8,
SDLK_TAB = 9,
SDLK_CLEAR = 12,
SDLK_RETURN = 13,
SDLK_PAUSE = 19,
SDLK_ESCAPE = 27,
SDLK_SPACE = 32,
SDLK_EXCLAIM = 33,
SDLK_QUOTEDBL = 34,
SDLK_HASH = 35,
SDLK_DOLLAR = 36,
SDLK_AMPERSAND = 38,
SDLK_QUOTE = 39,
SDLK_LEFTPAREN = 40,
SDLK_RIGHTPAREN = 41,
SDLK_ASTERISK = 42,
SDLK_PLUS = 43,
SDLK_COMMA = 44,
SDLK_MINUS = 45,
SDLK_PERIOD = 46,
SDLK_SLASH = 47,
SDLK_0 = 48,
SDLK_1 = 49,
SDLK_2 = 50,
SDLK_3 = 51,
SDLK_4 = 52,
SDLK_5 = 53,
SDLK_6 = 54,
SDLK_7 = 55,
SDLK_8 = 56,
SDLK_9 = 57,
SDLK_COLON = 58,
SDLK_SEMICOLON = 59,
SDLK_LESS = 60,
SDLK_EQUALS = 61,
SDLK_GREATER = 62,
SDLK_QUESTION = 63,
SDLK_AT = 64,
SDLK_LEFTBRACKET = 91,
SDLK_BACKSLASH = 92,
SDLK_RIGHTBRACKET = 93,
SDLK_CARET = 94,
SDLK_UNDERSCORE = 95,
SDLK_BACKQUOTE = 96,
SDLK_a = 97,
SDLK_b = 98,
SDLK_c = 99,
SDLK_d = 100,
SDLK_e = 101,
SDLK_f = 102,
SDLK_g = 103,
SDLK_h = 104,
SDLK_i = 105,
SDLK_j = 106,
SDLK_k = 107,
SDLK_l = 108,
SDLK_m = 109,
SDLK_n = 110,
SDLK_o = 111,
SDLK_p = 112,
SDLK_q = 113,
SDLK_r = 114,
SDLK_s = 115,
SDLK_t = 116,
SDLK_u = 117,
SDLK_v = 118,
SDLK_w = 119,
SDLK_x = 120,
SDLK_y = 121,
SDLK_z = 122,
SDLK_DELETE = 127,
SDLK_WORLD_0 = 160,
SDLK_WORLD_1 = 161,
SDLK_WORLD_2 = 162,
SDLK_WORLD_3 = 163,
SDLK_WORLD_4 = 164,
SDLK_WORLD_5 = 165,
SDLK_WORLD_6 = 166,
SDLK_WORLD_7 = 167,
SDLK_WORLD_8 = 168,
SDLK_WORLD_9 = 169,
SDLK_WORLD_10 = 170,
SDLK_WORLD_11 = 171,
SDLK_WORLD_12 = 172,
SDLK_WORLD_13 = 173,
SDLK_WORLD_14 = 174,
SDLK_WORLD_15 = 175,
SDLK_WORLD_16 = 176,
SDLK_WORLD_17 = 177,
SDLK_WORLD_18 = 178,
SDLK_WORLD_19 = 179,
SDLK_WORLD_20 = 180,
SDLK_WORLD_21 = 181,
SDLK_WORLD_22 = 182,
SDLK_WORLD_23 = 183,
SDLK_WORLD_24 = 184,
SDLK_WORLD_25 = 185,
SDLK_WORLD_26 = 186,
SDLK_WORLD_27 = 187,
SDLK_WORLD_28 = 188,
SDLK_WORLD_29 = 189,
SDLK_WORLD_30 = 190,
SDLK_WORLD_31 = 191,
SDLK_WORLD_32 = 192,
SDLK_WORLD_33 = 193,
SDLK_WORLD_34 = 194,
SDLK_WORLD_35 = 195,
SDLK_WORLD_36 = 196,
SDLK_WORLD_37 = 197,
SDLK_WORLD_38 = 198,
SDLK_WORLD_39 = 199,
SDLK_WORLD_40 = 200,
SDLK_WORLD_41 = 201,
SDLK_WORLD_42 = 202,
SDLK_WORLD_43 = 203,
SDLK_WORLD_44 = 204,
SDLK_WORLD_45 = 205,
SDLK_WORLD_46 = 206,
SDLK_WORLD_47 = 207,
SDLK_WORLD_48 = 208,
SDLK_WORLD_49 = 209,
SDLK_WORLD_50 = 210,
SDLK_WORLD_51 = 211,
SDLK_WORLD_52 = 212,
SDLK_WORLD_53 = 213,
SDLK_WORLD_54 = 214,
SDLK_WORLD_55 = 215,
SDLK_WORLD_56 = 216,
SDLK_WORLD_57 = 217,
SDLK_WORLD_58 = 218,
SDLK_WORLD_59 = 219,
SDLK_WORLD_60 = 220,
SDLK_WORLD_61 = 221,
SDLK_WORLD_62 = 222,
SDLK_WORLD_63 = 223,
SDLK_WORLD_64 = 224,
SDLK_WORLD_65 = 225,
SDLK_WORLD_66 = 226,
SDLK_WORLD_67 = 227,
SDLK_WORLD_68 = 228,
SDLK_WORLD_69 = 229,
SDLK_WORLD_70 = 230,
SDLK_WORLD_71 = 231,
SDLK_WORLD_72 = 232,
SDLK_WORLD_73 = 233,
SDLK_WORLD_74 = 234,
SDLK_WORLD_75 = 235,
SDLK_WORLD_76 = 236,
SDLK_WORLD_77 = 237,
SDLK_WORLD_78 = 238,
SDLK_WORLD_79 = 239,
SDLK_WORLD_80 = 240,
SDLK_WORLD_81 = 241,
SDLK_WORLD_82 = 242,
SDLK_WORLD_83 = 243,
SDLK_WORLD_84 = 244,
SDLK_WORLD_85 = 245,
SDLK_WORLD_86 = 246,
SDLK_WORLD_87 = 247,
SDLK_WORLD_88 = 248,
SDLK_WORLD_89 = 249,
SDLK_WORLD_90 = 250,
SDLK_WORLD_91 = 251,
SDLK_WORLD_92 = 252,
SDLK_WORLD_93 = 253,
SDLK_WORLD_94 = 254,
SDLK_WORLD_95 = 255,
SDLK_KP0 = 256,
SDLK_KP1 = 257,
SDLK_KP2 = 258,
SDLK_KP3 = 259,
SDLK_KP4 = 260,
SDLK_KP5 = 261,
SDLK_KP6 = 262,
SDLK_KP7 = 263,
SDLK_KP8 = 264,
SDLK_KP9 = 265,
SDLK_KP_PERIOD = 266,
SDLK_KP_DIVIDE = 267,
SDLK_KP_MULTIPLY = 268,
SDLK_KP_MINUS = 269,
SDLK_KP_PLUS = 270,
SDLK_KP_ENTER = 271,
SDLK_KP_EQUALS = 272,
SDLK_UP = 273,
SDLK_DOWN = 274,
SDLK_RIGHT = 275,
SDLK_LEFT = 276,
SDLK_INSERT = 277,
SDLK_HOME = 278,
SDLK_END = 279,
SDLK_PAGEUP = 280,
SDLK_PAGEDOWN = 281,
SDLK_F1 = 282,
SDLK_F2 = 283,
SDLK_F3 = 284,
SDLK_F4 = 285,
SDLK_F5 = 286,
SDLK_F6 = 287,
SDLK_F7 = 288,
SDLK_F8 = 289,
SDLK_F9 = 290,
SDLK_F10 = 291,
SDLK_F11 = 292,
SDLK_F12 = 293,
SDLK_F13 = 294,
SDLK_F14 = 295,
SDLK_F15 = 296,
SDLK_NUMLOCK = 300,
SDLK_CAPSLOCK = 301,
SDLK_SCROLLOCK = 302,
SDLK_RSHIFT = 303,
SDLK_LSHIFT = 304,
SDLK_RCTRL = 305,
SDLK_LCTRL = 306,
SDLK_RALT = 307,
SDLK_LALT = 308,
SDLK_RMETA = 309,
SDLK_LMETA = 310,
SDLK_LSUPER = 311,
SDLK_RSUPER = 312,
SDLK_MODE = 313,
SDLK_COMPOSE = 314,
SDLK_HELP = 315,
SDLK_PRINT = 316,
SDLK_SYSREQ = 317,
SDLK_BREAK = 318,
SDLK_MENU = 319,
SDLK_POWER = 320,
SDLK_EURO = 321,
SDLK_UNDO = 322,
SDLK_LAST
} SDLKey;
typedef enum {
KMOD_NONE = 0x0000,
KMOD_LSHIFT = 0x0001,
KMOD_RSHIFT = 0x0002,
KMOD_LCTRL = 0x0040,
KMOD_RCTRL = 0x0080,
KMOD_LALT = 0x0100,
KMOD_RALT = 0x0200,
KMOD_LMETA = 0x0400,
KMOD_RMETA = 0x0800,
KMOD_NUM = 0x1000,
KMOD_CAPS = 0x2000,
KMOD_MODE = 0x4000,
KMOD_RESERVED = 0x8000
} SDLMod;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,34 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <X11/Xlib.h>
#include "SDL_events.h"
#include "SDL_version.h"
typedef enum {
SDL_SYSWM_X11
} SDL_SYSWM_TYPE;
typedef struct SDL_SysWMinfo {
SDL_version version;
SDL_SYSWM_TYPE subsystem;
union {
struct {
void *display;
XID window;
void (*lock_func)(void);
void (*unlock_func)(void);
XID fswindow;
XID wmwindow;
void *gfxdisplay;
} x11;
} info;
} SDL_SysWMinfo;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef struct SDL_version {
uint8_t major;
uint8_t minor;
uint8_t patch;
} SDL_version;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef unsigned long XID;
typedef struct {
int x, y;
int width, height;
int border_width;
int depth;
void *visual;
XID root;
int clazz;
int bit_gravity;
int win_gravity;
int backing_store;
unsigned long backing_planes;
unsigned long backing_pixel;
int save_under;
XID colormap;
int map_installed;
int map_state;
long all_event_masks;
long your_event_mask;
long do_not_propagate_mask;
int override_redirect;
void *screen;
} XWindowAttributes;
int XTranslateCoordinates(void *display, XID src_w, XID dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, XID *child_return);
int XGetWindowAttributes(void *display, XID w, XWindowAttributes *window_attributes_return);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Default Window Size
#define DEFAULT_WIDTH 840
#define DEFAULT_HEIGHT 480
void media_take_screenshot();
void media_toggle_fullscreen();
void media_swap_buffers();
void media_cleanup();
void media_get_framebuffer_size(int *width, int *height);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,14 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
// Internal Methods (Not Handled By Media Layer Proxy)
__attribute__((visibility("internal"))) void _media_handle_SDL_PollEvent();
__attribute__((visibility("internal"))) void _media_handle_SDL_Quit();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,35 @@
# Component Details:
# Media Layer Proxy
#
# This components proxies multi-media calls from the ARM
# MCPI to the native architecture by using a UNIX socket.
project(media-layer-proxy)
# Configuration
set(MEDIA_LAYER_PROXY_SRC src/common/common.c src/media-layer-core.c src/GLESv1_CM.c) # Media Layer Proxy Source
set(MEDIA_LAYER_PROXY_LIBS media-layer-core GLESv1_CM) # Media Layer Proxy Client Dependencies
if(BUILD_NATIVE_COMPONENTS)
# Building Native Components
# Build Media Layer Proxy Client
add_executable(media-layer-proxy-client src/client/client.cpp ${MEDIA_LAYER_PROXY_SRC})
target_link_libraries(media-layer-proxy-client media-layer-headers reborn-headers ${MEDIA_LAYER_PROXY_LIBS})
target_compile_definitions(media-layer-proxy-client PRIVATE -DMEDIA_LAYER_PROXY_CLIENT)
# Install
install(TARGETS media-layer-proxy-client DESTINATION "${MCPI_LIB_DIR}")
endif()
if(BUILD_ARM_COMPONENTS)
# Building ARM Components
# Build Media Layer Proxy Server
add_library(media-layer-core SHARED src/server/server.cpp ${MEDIA_LAYER_PROXY_SRC})
target_link_libraries(media-layer-core media-layer-headers reborn-headers)
target_compile_definitions(media-layer-core PRIVATE -DMEDIA_LAYER_PROXY_SERVER)
# Symlink GLESv1_CM To Media Layer Proxy Server
install_symlink("libmedia-layer-core.so" "${MCPI_LIB_DIR}/libGLESv1_CM.so.1")
# Install
install(TARGETS media-layer-core DESTINATION "${MCPI_LIB_DIR}")
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
#include <vector>
#include <cerrno>
#include <unistd.h>
#include <cstring>
#include <sys/prctl.h>
#include <csignal>
#include "../common/common.h"
// Store Handlers
__attribute__((const)) static std::vector<proxy_handler_t> &get_handlers() {
static std::vector<proxy_handler_t> handlers;
return handlers;
}
void _add_handler(unsigned char unique_id, proxy_handler_t handler) {
if (get_handlers().size() > unique_id && get_handlers()[unique_id] != NULL) {
PROXY_ERR("Duplicate ID: %i", (int) unique_id);
}
if (get_handlers().size() <= unique_id) {
get_handlers().resize(unique_id + 1);
}
get_handlers()[unique_id] = handler;
}
// Store Parent PID
static int parent_is_alive = 1;
static void sigusr1_handler(__attribute__((unused)) int sig) {
// Mark Parent As Dead
parent_is_alive = 0;
}
// Check State Of Proxy And Exit If Invalid
void _check_proxy_state() {
// Check Server State
if (!parent_is_alive) {
PROXY_ERR("%s", "Server Terminated");
}
}
// Main
int main(int argc, char *argv[]) {
// Ignore SIGINT, Send Signal To Parent
signal(SIGINT, SIG_IGN);
// Send Signal On Parent Death To Interrupt Connection Read/Write And Exit
prctl(PR_SET_PDEATHSIG, SIGUSR1);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = &sigusr1_handler;
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
PROXY_ERR("Unable To Install Signal Handler: %s", strerror(errno));
}
// Get Connection
if (argc != 3) {
PROXY_ERR("%s", "Invalid Arguments");
}
char *read_str = argv[1];
char *write_str = argv[2];
set_connection(atoi(read_str), atoi(write_str));
PROXY_INFO("%s", "Connected");
// Send Connection Message
write_string((char *) CONNECTED_MSG);
// Loop
int running = is_connection_open();
while (running) {
unsigned char unique_id = read_byte();
if (get_handlers().size() > unique_id && get_handlers()[unique_id] != NULL) {
// Run Method
get_handlers()[unique_id]();
// Check If Connection Is Still Open
if (!is_connection_open()) {
// Exit
running = 0;
}
} else {
PROXY_ERR("Invalid Method ID: %i", (int) unique_id);
}
}
// Exit
PROXY_INFO("%s", "Stopped");
return 0;
}

View File

@ -0,0 +1,13 @@
#pragma once
#define PROXY_LOG_TAG "(Media Layer Proxy Client) "
typedef void (*proxy_handler_t)();
__attribute__((visibility("internal"))) void _add_handler(unsigned char id, proxy_handler_t handler);
#define CALL(unique_id, name, return_type, args) \
static void _run_##name (); \
__attribute__((constructor)) static void _init_##name() { \
_add_handler(unique_id, _run_##name); \
} \
static void _run_##name ()

View File

@ -0,0 +1,142 @@
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
// Safely Send/Receive Data From The Connection
#define CHECK_CONNECTION() \
{ \
_check_proxy_state(); \
if (!is_connection_open()) { \
PROXY_ERR("%s", "Attempting To Access Closed Connection"); \
} else { \
_check_proxy_state(); \
} \
}
void safe_read(void *buf, size_t len) {
if (buf == NULL) {
PROXY_ERR("%s", "Attempting To Read Into NULL Buffer");
}
size_t to_read = len;
while (to_read > 0) {
CHECK_CONNECTION();
ssize_t x = read(get_connection_read(), buf + (len - to_read), to_read);
if (x == -1 && errno != EINTR) {
PROXY_ERR("Failed Reading Data To Connection: %s", strerror(errno));
}
to_read -= x;
}
}
// Buffer Writes
void safe_write(void *buf, size_t len) {
if (buf == NULL) {
PROXY_ERR("%s", "Attempting To Send NULL Data");
}
size_t to_write = len;
while (to_write > 0) {
CHECK_CONNECTION();
ssize_t x = write(get_connection_write(), buf + (len - to_write), to_write);
if (x == -1 && errno != EINTR) {
PROXY_ERR("Failed Writing Data To Connection: %s", strerror(errno));
}
to_write -= x;
}
}
// Read/Write 32-Bit Integers
uint32_t read_int() {
uint32_t ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_int(uint32_t x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Floats
float read_float() {
float ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_float(float x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Bytes
unsigned char read_byte() {
unsigned char ret = 0;
safe_read((void *) &ret, sizeof (ret));
return ret;
}
void write_byte(unsigned char x) {
safe_write((void *) &x, sizeof (x));
}
// Read/Write Strings
char *read_string() {
// Check NULL
unsigned char is_null = read_byte();
if (is_null) {
return NULL;
}
// Allocate String
unsigned char length = read_byte();
char *str = malloc((size_t) length + 1);
// Read String
safe_read((void *) str, length);
// Add Terminator
str[length] = '\0';
// Return String
return strdup(str);
}
#define MAX_STRING_SIZE 256
void write_string(char *str) {
unsigned char is_null = str == NULL;
write_byte(is_null);
if (!is_null) {
int length = strlen(str);
if (length > MAX_STRING_SIZE) {
PROXY_ERR("Unable To Write String To Connection: Larger Than %i Bytes", MAX_STRING_SIZE);
}
write_byte((unsigned char) length);
safe_write((void *) str, length);
}
}
// Close Connection
void close_connection() {
int state_changed = 0;
if (get_connection_read() != -1) {
close(get_connection_read());
state_changed = 1;
}
if (get_connection_write() != -1) {
close(get_connection_write());
state_changed = 1;
}
set_connection(-1, -1);
if (state_changed) {
PROXY_INFO("%s", "Connection Closed");
}
}
// Check If Connection Is Open
int is_connection_open() {
return get_connection_read() != -1 && get_connection_write() != -1;
}
// Pipe
static int _read = -1;
static int _write = -1;
// Set Pipe
void set_connection(int read, int write) {
_read = read;
_write = write;
}
// Get Pipe
int get_connection_read() {
return _read;
}
int get_connection_write() {
return _write;
}

View File

@ -0,0 +1,60 @@
#pragma once
#include <stdint.h>
#include <libreborn/libreborn.h>
#ifdef __cplusplus
extern "C" {
#endif
#if __BYTE_ORDER != __LITTLE_ENDIAN
#error "Only Little Endian Is Supported"
#endif
#if defined(MEDIA_LAYER_PROXY_SERVER)
#include "../server/server.h"
#elif defined(MEDIA_LAYER_PROXY_CLIENT)
#include "../client/client.h"
#else
#error "Invalid Configuration"
#endif
#define CONNECTED_MSG "Connected"
#define PROXY_INFO(format, ...) INFO(PROXY_LOG_TAG format, __VA_ARGS__);
#define PROXY_ERR(format, ...) { close_connection(); ERR(PROXY_LOG_TAG format, __VA_ARGS__); }
// Safely Send/Receive Data From The Connection
__attribute__((visibility("internal"))) void safe_read(void *buf, size_t len);
__attribute__((visibility("internal"))) void safe_write(void *buf, size_t len);
// Read/Write 32-Bit Integers
__attribute__((visibility("internal"))) uint32_t read_int();
__attribute__((visibility("internal"))) void write_int(uint32_t x);
// Read/Write Bytes
__attribute__((visibility("internal"))) unsigned char read_byte();
__attribute__((visibility("internal"))) void write_byte(unsigned char x);
// Read/Write Floats
__attribute__((visibility("internal"))) float read_float();
__attribute__((visibility("internal"))) void write_float(float x);
// Read/Write Strings
__attribute__((visibility("internal"))) char *read_string(); // Remember To free()
__attribute__((visibility("internal"))) void write_string(char *str);
// Manipulate Connection
__attribute__((visibility("internal"))) void set_connection(int read, int write);
__attribute__((visibility("internal"))) int get_connection_read();
__attribute__((visibility("internal"))) int get_connection_write();
__attribute__((visibility("internal"))) void close_connection();
__attribute__((visibility("internal"))) int is_connection_open();
// Check State Of Proxy And Exit If Invalid
__attribute__((visibility("internal"))) void _check_proxy_state();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,351 @@
#include <stdint.h>
#include <SDL/SDL.h>
#include <libreborn/libreborn.h>
#include <libreborn/media-layer/core.h>
#include <libreborn/media-layer/internal.h>
#include "common/common.h"
// Read/Write SDL Events
static void write_SDL_Event(SDL_Event event) {
// Write EVent Type
write_int(event.type);
// Write Event Details
switch (event.type) {
// Focus Event
case SDL_ACTIVEEVENT: {
write_int(event.active.gain);
write_int(event.active.state);
break;
}
// Key Press Events
case SDL_KEYDOWN:
case SDL_KEYUP: {
write_int(event.key.state);
write_int(event.key.keysym.scancode);
write_int(event.key.keysym.sym);
write_int(event.key.keysym.mod);
write_int(event.key.keysym.unicode);
break;
}
// Mouse Motion Event
case SDL_MOUSEMOTION: {
write_int(event.motion.state);
write_int(event.motion.x);
write_int(event.motion.y);
write_int(event.motion.xrel);
write_int(event.motion.yrel);
break;
}
// Mouse Press Events
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: {
write_int(event.button.button);
write_int(event.button.state);
write_int(event.button.x);
write_int(event.button.y);
break;
}
// User-Specified Event (Repurposed As Unicode Character Event)
case SDL_USEREVENT: {
write_int(event.user.code);
break;
}
}
}
static SDL_Event read_SDL_Event() {
// Create Event
SDL_Event event;
event.type = read_int();
// Read Event Details
switch (event.type) {
// Focus Event
case SDL_ACTIVEEVENT: {
event.active.gain = read_int();
event.active.state = read_int();
break;
}
// Key Press Events
case SDL_KEYDOWN:
case SDL_KEYUP: {
event.key.state = read_int();
event.key.keysym.scancode = read_int();
event.key.keysym.sym = read_int();
event.key.keysym.mod = read_int();
event.key.keysym.unicode = read_int();
break;
}
// Mouse Motion Event
case SDL_MOUSEMOTION: {
event.motion.state = read_int();
event.motion.x = read_int();
event.motion.y = read_int();
event.motion.xrel = read_int();
event.motion.yrel = read_int();
break;
}
// Mouse Press Events
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: {
event.button.button = read_int();
event.button.state = read_int();
event.button.x = read_int();
event.button.y = read_int();
break;
}
// Quit Event
case SDL_QUIT: {
break;
}
// User-Specified Event (Repurposed As Unicode Character Event)
case SDL_USEREVENT: {
event.user.code = read_int();
break;
}
// Unsupported Event
default: {
INFO("Unsupported SDL Event: %u", event.type);
}
}
// Return
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
return event;
#pragma GCC diagnostic pop
}
// SDL Functions
CALL(0, SDL_Init, int, (uint32_t flags)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int(flags);
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return
return ret;
#else
uint32_t flags = read_int();
// Run
int ret = SDL_Init(flags);
// Return Values
write_int((uint32_t) ret);
#endif
}
CALL(1, SDL_PollEvent, int, (SDL_Event *event)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// No Arguments
// Get Return Value
int32_t ret = (int32_t) read_int();
if (ret) {
*event = read_SDL_Event();
}
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_Event event;
// Run
int ret = (int32_t) SDL_PollEvent(&event);
// Return Values
write_int(ret);
if (ret) {
write_SDL_Event(event);
}
#endif
}
CALL(2, SDL_PushEvent, int, (SDL_Event *event)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_SDL_Event(*event);
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_Event event = read_SDL_Event();
// Run
int ret = SDL_PushEvent(&event);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(3, SDL_WM_SetCaption, void, (const char *title, const char *icon)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_string((char *) title);
write_string((char *) icon);
// Release Proxy
end_proxy_call();
#else
char *title = read_string();
char *icon = read_string();
// Run
SDL_WM_SetCaption(title, icon);
// Free
free(title);
free(icon);
#endif
}
CALL(4, media_toggle_fullscreen, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_toggle_fullscreen();
#endif
}
CALL(5, SDL_WM_GrabInput, SDL_GrabMode, (SDL_GrabMode mode)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int((uint32_t) mode);
// Get Return Value
SDL_GrabMode ret = (SDL_GrabMode) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
SDL_GrabMode mode = (SDL_GrabMode) read_int();
// Run
SDL_GrabMode ret = SDL_WM_GrabInput(mode);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(6,SDL_ShowCursor, int, (int32_t toggle)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Arguments
write_int((uint32_t) toggle);
// Get Return Value
int32_t ret = (int32_t) read_int();
// Release Proxy
end_proxy_call();
// Return Value
return ret;
#else
int mode = (int) read_int();
// Run
int ret = SDL_ShowCursor(mode);
// Return Value
write_int((uint32_t) ret);
#endif
}
CALL(7, media_take_screenshot, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_take_screenshot();
#endif
}
CALL(8, media_swap_buffers, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Release Proxy
end_proxy_call();
#else
// Run
media_swap_buffers();
#endif
}
// This Method May Be Called In A Situation Where The Proxy Is Disconnected
CALL(9, media_cleanup, void, ()) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Check Connection
if (is_connection_open()) {
// Lock Proxy
start_proxy_call();
// Close The Connection
close_connection();
// Release Proxy
end_proxy_call();
}
#else
// Close The Connection
close_connection();
// Run
media_cleanup();
#endif
}
CALL(10, media_get_framebuffer_size, void, (int *width, int *height)) {
#if defined(MEDIA_LAYER_PROXY_SERVER)
// Lock Proxy
start_proxy_call();
// Get Return Values
*width = (int) read_int();
*height = (int) read_int();
// Release Proxy
end_proxy_call();
#else
int width;
int height;
// Run
media_get_framebuffer_size(&width, &height);
// Return Values
write_int((uint32_t) width);
write_int((uint32_t) height);
#endif
}

View File

@ -0,0 +1,164 @@
#include <cerrno>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
#include <string>
#include <unordered_map>
#include <fstream>
#include "../common/common.h"
// Track Client State
static int _client_is_alive = 0;
static int _client_status = 0;
static void update_client_state(int is_alive, int status) {
_client_is_alive = is_alive;
_client_status = status;
}
// Check State Of Proxy And Exit If Invalid
void _check_proxy_state() {
// Check Client State
if (!_client_is_alive) {
if (WIFEXITED(_client_status)) {
PROXY_ERR("Client Terminated: Exit Code: %i", WEXITSTATUS(_client_status));
} else if (WIFSIGNALED(_client_status)) {
PROXY_ERR("Client Terminated: Signal: %i%s", WTERMSIG(_client_status), WCOREDUMP(_client_status) ? " (Core Dumped)" : "");
} else {
PROXY_ERR("%s", "Client Terminated");
}
}
}
// Start Proxy Client
static pid_t _client_pid;
static void sigchld_handler(__attribute__((unused)) int sig) {
// Track
int status;
// Reap
int saved_errno = errno;
// Only waitpid() Proxy Client, Other Sub-Processes Are Handled By pclose()
if (waitpid(_client_pid, &status, WNOHANG) == _client_pid) {
// Handle Client Death
update_client_state(0, status);
}
errno = saved_errno;
}
static void start_media_layer_proxy_client(int read, int write) {
// Reap Children
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDSTOP;
sa.sa_handler = &sigchld_handler;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
PROXY_ERR("Unable To Install Signal Handler: %s", strerror(errno));
}
// Fork And Start
pid_t ret = fork();
if (ret == -1) {
PROXY_ERR("Unable To Launch Client: %s", strerror(errno));
} else if (ret == 0) {
// Child Process
// Prepare Environment
RESET_ENVIRONMENTAL_VARIABLE("LD_LIBRARY_PATH");
RESET_ENVIRONMENTAL_VARIABLE("LD_PRELOAD");
// Prepare Arguments
char *read_str = NULL;
safe_asprintf(&read_str, "%i", read);
char *write_str = NULL;
safe_asprintf(&write_str, "%i", write);
char *argv[] = {NULL /* Updated By safe_execvpe() */, read_str, write_str, NULL};
// Run
safe_execvpe_relative_to_binary("lib/media-layer-proxy-client", argv, environ);
} else {
// Parent Process
_client_pid = ret;
}
update_client_state(1, 0);
}
// Maximize Pipe Buffer Size
static void maximize_pipe_fd_size(int fd) {
// Read Maximum Pipe Size
std::ifstream max_size_file("/proc/sys/fs/pipe-max-size");
if (!max_size_file.good()) {
PROXY_ERR("%s", "Unable To Open Maximum Pipe Size File");
}
// Read One Line
int max_size;
std::string line;
if (std::getline(max_size_file, line) && line.size() > 0) {
max_size = std::stoi(line);
} else {
PROXY_ERR("%s", "Unable To Read Maximum Pipe Size File");
}
// Set Maximum Pipe Size
errno = 0;
if (fcntl(fd, F_SETPIPE_SZ, max_size) < max_size) {
PROXY_ERR("Unable To Set Maximum Pipe Size: %s", errno != 0 ? strerror(errno) : "Unknown Error");
}
}
static void maximize_pipe_size(int pipe[2]) {
maximize_pipe_fd_size(pipe[0]);
maximize_pipe_fd_size(pipe[1]);
}
// Start Server
__attribute__((constructor)) static void init_media_layer_proxy_server() {
PROXY_INFO("%s", "Starting...");
// Create Connection
int server_to_client_pipe[2];
safe_pipe2(server_to_client_pipe, 0);
maximize_pipe_size(server_to_client_pipe);
int client_to_server_pipe[2];
safe_pipe2(client_to_server_pipe, 0);
maximize_pipe_size(client_to_server_pipe);
// Set Connection
set_connection(client_to_server_pipe[0], server_to_client_pipe[1]);
// Start Client
start_media_layer_proxy_client(server_to_client_pipe[0], client_to_server_pipe[1]);
// Wait For Connection Message
char *str = read_string();
if (strcmp(str, CONNECTED_MSG) == 0) {
PROXY_INFO("%s", "Connected");
} else {
PROXY_ERR("%s", "Unable To Connect");
}
// Free
free(str);
}
// Assign Unique ID To Function
static std::unordered_map<std::string, unsigned char> &get_unique_ids() {
static std::unordered_map<std::string, unsigned char> unique_ids;
return unique_ids;
}
void _assign_unique_id(const char *name, unsigned char id) {
get_unique_ids()[name] = id;
}
__attribute__((const)) static unsigned char get_unique_id(const char *name) {
return get_unique_ids()[name]; // Assume ID Exists
}
// The Proxy Is Single-Threaded
static pthread_mutex_t proxy_mutex = PTHREAD_MUTEX_INITIALIZER;
void _start_proxy_call(const char *call_name) {
// Lock Proxy
pthread_mutex_lock(&proxy_mutex);
write_byte(get_unique_id(call_name));
}
void end_proxy_call() {
// Release Proxy
pthread_mutex_unlock(&proxy_mutex);
}

View File

@ -0,0 +1,17 @@
#pragma once
#define PROXY_LOG_TAG "(Media Layer Proxy Server) "
// Assign Unique ID To Function
__attribute__((visibility("internal"))) void _assign_unique_id(const char *name, unsigned char id);
// Must Call After Every Call
__attribute__((visibility("internal"))) void _start_proxy_call(const char *name);
#define start_proxy_call() _start_proxy_call(__func__)
__attribute__((visibility("internal"))) void end_proxy_call();
#define CALL(unique_id, name, return_type, args) \
__attribute__((constructor)) static void _init_##name() { \
_assign_unique_id(#name, unique_id); \
} \
return_type name args

View File

@ -0,0 +1,32 @@
project(media-layer-stubs)
# Add GLES Stubs For Linking
add_library(GLESv1_CM SHARED src/GLESv1_CM.c)
target_link_libraries(GLESv1_CM media-layer-headers)
set_target_properties(GLESv1_CM PROPERTIES SOVERSION "1")
if(BUILD_ARM_COMPONENTS)
# Stub RPI-Specific Graphics
add_library(bcm_host SHARED src/bcm_host.c)
# Install
install(TARGETS bcm_host DESTINATION "${MCPI_LIB_DIR}")
# Stub EGL
add_library(EGL SHARED src/EGL.c)
target_link_libraries(EGL reborn-headers media-layer-headers)
# Install
install(TARGETS EGL DESTINATION "${MCPI_FALLBACK_LIB_DIR}") # Place At The End Of LD_LIBRARY_PATH
# Install GLESv1_CM Stubs In Server Mode
if(MCPI_SERVER_MODE)
install(TARGETS GLESv1_CM DESTINATION "${MCPI_LIB_DIR}")
endif()
# Add NOP GLESv2 That Dpends On Actual GLESv1_CM (This Cannot Be A Symlink Because The Location Of GLESv1_CM Is Dynamic)
add_library(GLESv2 SHARED src/nop.c)
target_link_libraries(GLESv2 GLESv1_CM)
# Force Link
target_link_options(GLESv2 PRIVATE "-Wl,--no-as-needed")
# Install
install(TARGETS GLESv2 DESTINATION "${MCPI_LIB_DIR}")
endif()

View File

@ -1,37 +1,53 @@
/*
* This is only loaded when no other EGL library is present on the system to silence linker errors.
*
* All EGL calls are manually patched out in mods/src/compat/egl.c.
* Unlike with bcm_host, EGL can't just be always stubbed out with LD_PRELOAD, because in some situations GLFW has it as a dependency.
*
* So normally, MCPI just loads the real EGL and never uses it (because MCPI's EGL calls were patched out).
* However, since the real EGL is still loaded, GLFW can use it if it needs to.
*
* This stub library is just in case the system has no EGL library present.
*/
#include <EGL/egl.h>
#include <libreborn/libreborn.h>
#define IMPOSSIBLE() ERR("(%s:%i) This Should Never Be Called", __FILE__, __LINE__)
// EGL Is Replaced With GLFW
EGLDisplay eglGetDisplay(__attribute__((unused)) NativeDisplayType native_display) {
return 0;
IMPOSSIBLE();
}
EGLBoolean eglInitialize(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLint *major, __attribute__((unused)) EGLint *minor) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglChooseConfig(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLint const *attrib_list, __attribute__((unused)) EGLConfig *configs, __attribute__((unused)) EGLint config_size, __attribute__((unused)) EGLint *num_config) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglBindAPI(__attribute__((unused)) EGLenum api) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLContext eglCreateContext(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) EGLContext share_context, __attribute__((unused)) EGLint const *attrib_list) {
return 0;
IMPOSSIBLE();
}
EGLSurface eglCreateWindowSurface(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) NativeWindowType native_window, __attribute__((unused)) EGLint const *attrib_list) {
return 0;
IMPOSSIBLE();
}
EGLBoolean eglMakeCurrent(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface draw, __attribute__((unused)) EGLSurface read, __attribute__((unused)) EGLContext context) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglDestroySurface(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglDestroyContext(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLContext context) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglTerminate(__attribute__((unused)) EGLDisplay display) {
return EGL_TRUE;
IMPOSSIBLE();
}
EGLBoolean eglSwapBuffers(__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface) {
return EGL_TRUE;
IMPOSSIBLE();
}

View File

@ -0,0 +1,108 @@
#include <GLES/gl.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void glFogfv(GLenum pname, const GLfloat *params) {
}
void glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
}
void glLineWidth(GLfloat width) {
}
void glBlendFunc(GLenum sfactor, GLenum dfactor) {
}
void glDrawArrays(GLenum mode, GLint first, GLsizei count) {
}
void glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
}
void glClear(GLbitfield mask) {
}
void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage) {
}
void glFogx(GLenum pname, GLfixed param) {
}
void glFogf(GLenum pname, GLfloat param) {
}
void glMatrixMode(GLenum mode) {
}
void glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
}
void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
}
void glTexParameteri(GLenum target, GLenum pname, GLint param) {
}
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
}
void glEnable(GLenum cap) {
}
void glEnableClientState(GLenum array) {
}
void glPolygonOffset(GLfloat factor, GLfloat units) {
}
void glDisableClientState(GLenum array) {
}
void glDepthRangef(GLclampf near, GLclampf far) {
}
void glDepthFunc(GLenum func) {
}
void glBindBuffer(GLenum target, GLuint buffer) {
}
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
}
void glPopMatrix() {
}
void glLoadIdentity() {
}
void glScalef(GLfloat x, GLfloat y, GLfloat z) {
}
void glPushMatrix() {
}
void glDepthMask(GLboolean flag) {
}
void glHint(GLenum target, GLenum mode) {
}
void glMultMatrixf(const GLfloat *m) {
}
void glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer) {
}
void glDeleteBuffers(GLsizei n, const GLuint *buffers) {
}
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 glGenTextures(GLsizei n, GLuint *textures) {
}
void glDeleteTextures(GLsizei n, const GLuint *textures) {
}
void glAlphaFunc(GLenum func, GLclampf ref) {
}
void glGetFloatv(GLenum pname, GLfloat *params) {
}
void glBindTexture(GLenum target, GLuint texture) {
}
void glTranslatef(GLfloat x, GLfloat y, GLfloat z) {
}
void glShadeModel(GLenum mode) {
}
void glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far) {
}
void glDisable(GLenum cap) {
}
void glCullFace(GLenum mode) {
}
void glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) {
}
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
}
void glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) {
}
GLboolean glIsEnabled(GLenum cap) {
return GL_FALSE;
}
void glGetIntegerv(GLenum pname, GLint *data) {
}
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *data) {
}
#pragma GCC diagnostic pop

View File

@ -1,44 +1,33 @@
cmake_minimum_required(VERSION 3.13.0)
project(mods)
## Setup
add_compile_options(-Wall -Wextra -Werror)
add_link_options(-Wl,--no-undefined)
# Disable C++11 String ABI
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
# Add libreborn
add_subdirectory(../libreborn libreborn)
# Find GLFW
find_package(glfw3 3.3 REQUIRED)
## Mods
add_library(compat SHARED src/compat/compat.c)
target_link_libraries(compat feature input screenshot SDL GLESv1_CM X11 dl glfw Xfixes)
add_library(compat SHARED src/compat/compat.c src/compat/egl.c)
target_link_libraries(compat feature input media-layer-core dl)
add_library(readdir SHARED src/readdir/readdir.c)
add_library(feature SHARED src/feature/feature.c)
target_link_libraries(feature reborn)
if(MCPI_SERVER_MODE)
add_library(server SHARED src/server/server.cpp src/server/server_properties.cpp)
target_link_libraries(server reborn feature dl SDL pthread)
add_library(screenshot SHARED src/screenshot/screenshot.c)
target_link_libraries(screenshot reborn GLESv1_CM freeimage)
target_link_libraries(server reborn feature home compat dl media-layer-core pthread)
endif()
add_library(camera SHARED src/camera/camera.cpp)
target_link_libraries(camera reborn screenshot)
target_link_libraries(camera reborn media-layer-core)
add_library(game_mode SHARED src/game_mode/game_mode.c src/game_mode/game_mode.cpp)
target_link_libraries(game_mode reborn)
add_library(input SHARED src/input/input.c src/input/input.cpp)
target_link_libraries(input reborn feature SDL chat)
target_link_libraries(input reborn feature media-layer-core chat)
add_library(misc SHARED src/misc/misc.c src/misc/misc.cpp)
target_link_libraries(misc reborn feature util)
@ -47,7 +36,7 @@ add_library(options SHARED src/options/options.c)
target_link_libraries(options reborn feature)
add_library(override SHARED src/override/override.c)
target_link_libraries(override reborn dl)
target_link_libraries(override reborn dl home)
add_library(textures SHARED src/textures/textures.cpp)
target_link_libraries(textures reborn feature GLESv1_CM)
@ -55,11 +44,20 @@ target_link_libraries(textures reborn feature GLESv1_CM)
add_library(chat SHARED src/chat/chat.cpp src/chat/ui.c)
target_link_libraries(chat reborn pthread)
add_library(home SHARED src/home/home.c)
target_link_libraries(home reborn)
add_library(test SHARED src/test/test.c)
target_link_libraries(test reborn)
target_link_libraries(test reborn home)
add_library(init SHARED src/init/init.c)
target_link_libraries(init compat server game_mode camera input misc options textures chat test)
target_link_libraries(init compat game_mode camera input misc options textures chat home test)
if(MCPI_SERVER_MODE)
target_link_libraries(init server)
endif()
## Install Mods
install(TARGETS init compat readdir feature screenshot override server game_mode camera input misc options textures chat test DESTINATION /mods)
install(TARGETS init compat readdir feature override game_mode camera input misc options textures chat home test DESTINATION "${MCPI_INSTALL_DIR}/mods")
if(MCPI_SERVER_MODE)
install(TARGETS server DESTINATION "${MCPI_INSTALL_DIR}/mods")
endif()

Some files were not shown because too many files have changed in this diff Show More