Initial Commit

This commit is contained in:
TheBrokenRail 2020-09-25 12:43:53 -04:00
commit 9cba611982
19 changed files with 593 additions and 0 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
.git
.gitignore
Dockerfile
README.md

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/minecraft-pi
/libpng
/core/build
/mods/build

25
Dockerfile Normal file
View File

@ -0,0 +1,25 @@
FROM arm64v8/debian:bullseye
RUN dpkg --add-architecture armhf
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y libglvnd-dev:armhf libsdl1.2-dev:armhf libx11-dev:armhf build-essential zlib1g-dev:armhf git cmake curl gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf gdb
RUN ln -s /usr/lib/arm-linux-gnueabihf/libGLESv2.so.2 /usr/lib/libGLESv2.so
RUN ln -s /usr/lib/arm-linux-gnueabihf/libEGL.so.1 /usr/lib/libEGL.so
ADD . /app
WORKDIR /app
RUN ./scripts/download-minecraft-pi.sh
RUN ./scripts/build-mods.sh
RUN ./scripts/build-libpng12.sh
WORKDIR /app/minecraft-pi
ENTRYPOINT ./launcher

23
README.md Normal file
View File

@ -0,0 +1,23 @@
# Minecraft: Pi Edition For Docker
## Dependencies
```sh
# Required For Hardware Acceleration
sudo apt install virgl-server
# Required For ARM Support
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
```
## Tutorial
```sh
virgl_test_server &
PID="$!"
sudo docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -v /tmp/.virgl_test:/tmp/.virgl_test -v ~/.minecraft-pi:/root/.minecraft -e DISPLAY=unix${DISPLAY} thebrokenrail/minecraft-pi
kill "${PID}"
```
## Tweaks
The included version of Minecraft: Pi Ediiton is slightly modified so it uses the old touchscreen UI.

5
build.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -e
sudo docker build --tag thebrokenrail/minecraft-pi:latest .

5
cmake/toolchain.cmake Normal file
View File

@ -0,0 +1,5 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

14
core/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.1.0)
project(core)
add_compile_options(-Wall -Wextra -Werror)
include_directories(include)
add_library(core SHARED src/core.c)
target_link_libraries(core dl)
add_library(bcm_host SHARED src/bcm_host.c)
add_executable(launcher src/launcher.c)

View File

@ -0,0 +1,37 @@
#ifndef LIBLOADER_H
#define LIBLOADER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#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) { \
fprintf(stderr, "Error Resolving Symbol: "#name": %s\n", dlerror()); \
exit(1); \
} \
} \
}; \
\
__attribute__((__used__)) return_type name args
void overwrite(void *start, void *target);
void patch(void *start, unsigned char patch[]);
#ifdef __cplusplus
}
#endif
#endif

39
core/src/bcm_host.c Normal file
View File

@ -0,0 +1,39 @@
#include <stdint.h>
void bcm_host_init(void) {
}
void bcm_host_deinit(void) {
}
int32_t graphics_get_display_size(__attribute__((unused)) const uint16_t display_number, __attribute__((unused)) uint32_t *width, __attribute__((unused)) uint32_t *height) {
return -1;
}
unsigned bcm_host_get_peripheral_address(void) {
return 0x20000000;
}
unsigned bcm_host_get_peripheral_size(void) {
return 0x01000000;
}
unsigned bcm_host_get_sdram_address(void) {
return 0x40000000;
}
uint32_t vc_dispmanx_display_open(__attribute__((unused)) uint32_t device) {
return 0;
}
uint32_t vc_dispmanx_element_add(__attribute__((unused)) uint32_t update, __attribute__((unused)) uint32_t display, __attribute__((unused)) int32_t layer, __attribute__((unused)) const void *dest_rect, __attribute__((unused)) uint32_t src, __attribute__((unused)) const void *src_rect, __attribute__((unused)) uint32_t protection, __attribute__((unused)) void *alpha, __attribute__((unused)) void *clamp, __attribute__((unused)) uint32_t transform) {
return 0;
}
uint32_t vc_dispmanx_update_start(__attribute__((unused)) int32_t priority) {
return 0;
}
int vc_dispmanx_update_submit_sync(__attribute__((unused)) uint32_t update) {
return 0;
}

44
core/src/core.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <libcore/libcore.h>
#define PREPARE_PATCH(start) \
size_t page_size = sysconf(_SC_PAGESIZE); \
uintptr_t end = ((uintptr_t) start) + 1; \
uintptr_t page_start = ((uintptr_t) start) & -page_size; \
mprotect((void *) page_start, end - page_start, PROT_READ | PROT_WRITE); \
\
unsigned char *data = (unsigned char *) start; \
int thumb = ((size_t) start) & 1; \
if (thumb) { \
data--; \
} \
fprintf(stderr, "Patching - original: %i %i %i %i %i\n", data[0], data[1], data[2], data[3], data[4]);
#define END_PATCH() \
fprintf(stderr, "Patching - result: %i %i %i %i %i\n", data[0], data[1], data[2], data[3], data[4]); \
\
mprotect((void *) page_start, end - page_start, PROT_READ | PROT_EXEC);
void overwrite(void *start, void *target) {
PREPARE_PATCH(start);
if (thumb) {
unsigned char patch[4] = {0xdf, 0xf8, 0x00, 0xf0};
memcpy(data, patch, 4);
} else {
unsigned char patch[4] = {0x04, 0xf0, 0x1f, 0xe5};
memcpy(data, patch, 4);
}
memcpy(&data[4], &target, sizeof (int));
END_PATCH();
}
void patch(void *start, unsigned char patch[]) {
PREPARE_PATCH(start);
memcpy(data, patch, 4);
END_PATCH();
}

87
core/src/launcher.c Normal file
View File

@ -0,0 +1,87 @@
#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>
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;
}
#define MODS_FOLDER "./mods/"
static void set_and_print_env(char *name, char *value) {
fprintf(stderr, "Set %s = %s\n", name, value);
setenv(name, value, 1);
}
static char *get_env_safe(const char *name) {
char *ret = getenv(name);
return ret != NULL ? ret : "";
}
int main(__attribute__((unused)) int argc, char *argv[]) {
fprintf(stderr, "Configuring Game...\n");
char *ld_path = NULL;
char *cwd = getcwd(NULL, 0);
asprintf(&ld_path, "%s:/usr/arm-linux-gnueabihf/lib:%s", cwd, get_env_safe("LD_LIBRARY_PATH"));
free(cwd);
set_and_print_env("LD_LIBRARY_PATH", ld_path);
free(ld_path);
char *ld_preload = NULL;
asprintf(&ld_preload, "%s", get_env_safe("LD_PRELOAD"));
int folder_name_length = strlen(MODS_FOLDER);
DIR *dp = opendir(MODS_FOLDER);
if (dp != NULL) {
struct dirent *entry = NULL;
errno = 0;
while (1) {
entry = readdir(dp);
if (entry != NULL) {
if (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] = MODS_FOLDER[i];
}
for (int i = 0; i < name_length; i++) {
name[folder_name_length + i] = entry->d_name[i];
}
name[total_length] = '\0';
asprintf(&ld_preload, "%s:%s", name, ld_preload);
}
} else if (errno != 0) {
fprintf(stderr, "Error Reading Directory: %s\n", strerror(errno));
exit(1);
} else {
break;
}
}
} else {
fprintf(stderr, "Error Opening Directory: %s\n", strerror(errno));
exit(1);
}
closedir(dp);
set_and_print_env("LD_PRELOAD", ld_preload);
free(ld_preload);
fprintf(stderr, "Starting Game...\n");
return execve("./minecraft-pi", argv, environ);
}

19
mods/CMakeLists.txt Normal file
View File

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.1.0)
project(mods)
add_compile_options(-Wall -Wextra -Werror)
add_subdirectory(../core core)
include_directories(../core/include)
add_library(compat SHARED src/compat.c)
target_link_libraries(compat core SDL EGL GLESv1_CM GLESv2 X11 dl)
# Force GLESv1 Link
target_link_options(compat PRIVATE "-Wl,--no-as-needed")
add_library(touch SHARED src/touch.c)
target_link_libraries(touch core dl)
add_library(readdir SHARED src/readdir.c)
target_link_libraries(readdir core)

204
mods/src/compat.c Normal file
View File

@ -0,0 +1,204 @@
#define _GNU_SOURCE
#include <SDL/SDL.h>
#include <SDL/SDL_syswm.h>
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <X11/Xlib.h>
#include <libcore/libcore.h>
static Display *x11_display;
static EGLDisplay egl_display;
static Window x11_window;
static Window x11_root_window;
static EGLConfig egl_config;
static int window_loaded = 0;
static EGLContext egl_context;
static EGLSurface egl_surface;
HOOK(eglGetDisplay, EGLDisplay, (__attribute__((unused)) NativeDisplayType native_display)) {
// Handled In ensure_x11_window()
return 0;
}
// Get Reference To X Window
static void ensure_x11_window() {
if (!window_loaded) {
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
x11_display = info.info.x11.display;
x11_window = info.info.x11.window;
x11_root_window = RootWindow(x11_display, DefaultScreen(x11_display));
ensure_eglGetDisplay();
egl_display = (*real_eglGetDisplay)(x11_display);
window_loaded = 1;
}
}
// Handled In SDL_WM_SetCaption
HOOK(eglInitialize, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLint *major, __attribute__((unused)) EGLint *minor)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(eglChooseConfig, EGLBoolean, (__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;
}
// Handled In SDL_WM_SetCaption
HOOK(eglBindAPI, EGLBoolean, (__attribute__((unused)) EGLenum api)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(eglCreateContext, EGLContext, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) EGLContext share_context, __attribute__((unused)) EGLint const *attrib_list)) {
return 0;
}
// Handled In SDL_WM_SetCaption
HOOK(eglCreateWindowSurface, EGLSurface, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLConfig config, __attribute__((unused)) NativeWindowType native_window, __attribute__((unused)) EGLint const *attrib_list)) {
return 0;
}
// Handled In SDL_WM_SetCaption
HOOK(eglMakeCurrent, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface draw, __attribute__((unused)) EGLSurface read, __attribute__((unused)) EGLContext context)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglDestroySurface, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglDestroyContext, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLContext context)) {
return EGL_TRUE;
}
// Handled In SDL_Quit
HOOK(eglTerminate, EGLBoolean, (__attribute__((unused)) EGLDisplay display)) {
return EGL_TRUE;
}
// Handled In SDL_WM_SetCaption
HOOK(SDL_SetVideoMode, SDL_Surface *, (__attribute__((unused)) int width, __attribute__((unused)) int height, __attribute__((unused)) int bpp, __attribute__((unused)) Uint32 flags)) {
// Return Value Is Only Used For A NULL-Check
return (SDL_Surface *) 1;
}
// EGL Config
EGLint const set_attrib_list[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 16,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
// Init EGL
HOOK(SDL_WM_SetCaption, void, (const char *title, const char *icon)) {
ensure_SDL_SetVideoMode();
(*real_SDL_SetVideoMode)(848, 480, 32, 16);
ensure_SDL_WM_SetCaption();
(*real_SDL_WM_SetCaption)(title, icon);
ensure_x11_window();
ensure_eglInitialize();
(*real_eglInitialize)(egl_display, NULL, NULL);
EGLint number_of_config;
ensure_eglChooseConfig();
(*real_eglChooseConfig)(egl_display, set_attrib_list, &egl_config, 1, &number_of_config);
ensure_eglBindAPI();
(*real_eglBindAPI)(EGL_OPENGL_ES_API);
ensure_eglCreateContext();
egl_context = (*real_eglCreateContext)(egl_display, egl_config, EGL_NO_CONTEXT, NULL);
ensure_eglCreateWindowSurface();
egl_surface = (*real_eglCreateWindowSurface)(egl_display, egl_config, x11_window, NULL);
ensure_eglMakeCurrent();
(*real_eglMakeCurrent)(egl_display, egl_surface, egl_surface, egl_context);
eglSwapInterval(egl_display, 1);
}
HOOK(eglSwapBuffers, EGLBoolean, (__attribute__((unused)) EGLDisplay display, __attribute__((unused)) EGLSurface surface)) {
ensure_x11_window();
ensure_eglSwapBuffers();
EGLBoolean ret = (*real_eglSwapBuffers)(egl_display, egl_surface);
return ret;
}
HOOK(SDL_PollEvent, int, (SDL_Event *event)) {
ensure_SDL_PollEvent();
int ret = (*real_SDL_PollEvent)(event);
// Resize EGL
if (event != NULL && event->type == SDL_VIDEORESIZE) {
ensure_SDL_SetVideoMode();
(*real_SDL_SetVideoMode)(event->resize.w, event->resize.h, 32, 16);
// OpenGL state modification for resizing
glViewport(0, 0, event->resize.w, event->resize.h);
glMatrixMode(GL_PROJECTION);
glOrthox(0, event->resize.w, 0, event->resize.h, -1, 1);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
}
return ret;
}
// Terminate EGL
HOOK(SDL_Quit, void, ()) {
ensure_SDL_Quit();
(*real_SDL_Quit)();
ensure_eglDestroyContext();
(*real_eglDestroyContext)(egl_display, egl_context);
ensure_eglDestroySurface();
(*real_eglDestroySurface)(egl_display, egl_surface);
ensure_eglTerminate();
(*real_eglTerminate)(egl_display);
}
HOOK(XTranslateCoordinates, int, (Display *display, Window src_w, Window dest_w, int src_x, int src_y, int *dest_x_return, int *dest_y_return, Window *child_return)) {
ensure_XTranslateCoordinates();
if (window_loaded) {
return (*real_XTranslateCoordinates)(x11_display, x11_window, x11_root_window, src_x, src_y, dest_x_return, dest_y_return, child_return);
} else {
return (*real_XTranslateCoordinates)(display, src_w, dest_w, src_x, src_y, dest_x_return, dest_y_return, child_return);
}
}
HOOK(XGetWindowAttributes, int, (Display *display, Window w, XWindowAttributes *window_attributes_return)) {
ensure_XGetWindowAttributes();
if (window_loaded) {
return (*real_XGetWindowAttributes)(x11_display, x11_window, window_attributes_return);
} else {
return (*real_XGetWindowAttributes)(display, w, window_attributes_return);
}
}
#include <stdlib.h>
// Use VirGL
__attribute__((constructor)) static void init() {
setenv("LIBGL_ALWAYS_SOFTWARE", "1", 1);
setenv("GALLIUM_DRIVER", "virpipe", 1);
}

23
mods/src/readdir.c Normal file
View File

@ -0,0 +1,23 @@
#define _GNU_SOURCE
#define __USE_LARGEFILE64
#include <stddef.h>
#include <stdint.h>
#include <dirent.h>
#include <libcore/libcore.h>
#define FILENAME_SIZE 256
HOOK(readdir, struct dirent *, (DIR *dirp)) {
struct dirent64 *original = readdir64(dirp);
if (original == NULL) {
return NULL;
}
static struct dirent new;
for (int i = 0; i < FILENAME_SIZE; i++) {
new.d_name[i] = original->d_name[i];
}
new.d_type = original->d_type;
return &new;
}

8
mods/src/touch.c Normal file
View File

@ -0,0 +1,8 @@
#include <libcore/libcore.h>
__attribute__((constructor)) static void init() {
unsigned char patch_data[4] = {0x01, 0x00, 0x50, 0xe3};
patch((void *) 0x292fc, patch_data);
//unsigned char patch_data_2[4] = {0x00, 0x30, 0xa0, 0xe3};
//patch((void *) 0x3d9b8, patch_data_2);
}

10
run.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
set -e
virgl_test_server &
PID="$!"
sudo docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -v /tmp/.virgl_test:/tmp/.virgl_test -v ~/.minecraft-pi:/root/.minecraft -e DISPLAY=unix${DISPLAY} thebrokenrail/minecraft-pi
kill "${PID}"

15
scripts/build-libpng12.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
set -e
git clone --depth 1 https://git.code.sf.net/p/libpng/code libpng -b libpng12
cd libpng
./configure --host arm-linux-gnueabihf --prefix /usr/arm-linux-gnueabihf
make -j$(nproc)
make install
cd ../
rm -rf libpng

19
scripts/build-mods.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
cd mods
mkdir build
cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../../cmake/toolchain.cmake ..
make -j$(nproc)
cd ../../
mkdir minecraft-pi/mods
cp mods/build/lib*.so minecraft-pi/mods
cp mods/build/core/lib*.so minecraft-pi
cp mods/build/core/launcher minecraft-pi

View File

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