Skip to content

Instantly share code, notes, and snippets.

@torralbaa
Last active June 11, 2022 00:54
Show Gist options
  • Save torralbaa/032b2c8576cd06d44b4ba1beb64fba0c to your computer and use it in GitHub Desktop.
Save torralbaa/032b2c8576cd06d44b4ba1beb64fba0c to your computer and use it in GitHub Desktop.
Minecraft Pi Patched.

MCPI Patched

How to use this patch?

First, you'll need to make sure you have libsdl2-dev and libopengl-dev installed:

sudo apt-get install -y libsdl2-dev libopengl-dev

If the second isn't avaiable, install libgl-dev.

Then compile it as following:

gcc -shared -fPIC preload.c -o preload.so

To use it, give the mcpi-patch.sh execution permissions (with sudo chmod a+x mcpi-patch.sh, for example), and replace PRELOAD_PATH_HERE with the path of the preload.so file.

#!/bin/sh
cd /opt/minecraft-pi
if grep -q okay /proc/device-tree/soc/v3d@7ec00000/status \
/proc/device-tree/soc/firmwarekms@7e600000/status 2> /dev/null; then
export LD_PRELOAD=libbcm_host.so.1.0
export LD_LIBRARY_PATH=./lib/mesa
else
export LD_LIBRARY_PATH=./lib/brcm
fi
export LD_PRELOAD=PRELOAD_PATH_HERE # Replace `PRELOAD_PATH_HERE` with the actual preload.so path.
./minecraft-pi
/*
* preload.c - Minecraft Pi Dynamic Patches.
*
* Copyright 2020 Alvarito050506 <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#define _GNU_SOURCE /* Required for RTLD_NEXT */
/* Lots of standard includes and libraries, for types and function prototypes. */
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <stdint.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/socket.h>
#include <sys/ptrace.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <GL/gl.h>
#include <SDL2/SDL.h>
#include <errno.h>
/* Standard/POSIX. */
static FILE* (*old_fopen)(const char* pathname, const char* mode) = NULL;
ssize_t (*old_sendto)(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
ssize_t (*old_recv)(int sockfd, void *buf, size_t len, int flags);
ssize_t (*old_send)(int sockfd, const void* buf, size_t len, int flags);
ssize_t (*old_recvfrom)(int sockfd, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
int (*old_open)(const char *pathname, int flags);
/* OpenGL. */
void (*old_glShadeModel)(GLenum mode);
void (*old_glFogfv)(GLenum pname, const GLfloat* params);
/* SDL. */
int (*old_SDL_PollEvent)(SDL_Event* event);
void (*old_SDL_WM_SetCaption)(const char* title, const char* icon) = NULL;
/* Please ignore the variable names. */
void *addr_0, *addr_1, *addr_2;
int game_mode = 1;
int ex_game_mode = 1;
char level_dat_name[512];
int reading = 1;
int connected = 1;
char buffer[64];
int i = 0;
int api_fd = 0;
int api_setup();
int api_kill();
/* To be optimized. */
void* search(char* data, int from)
{
void* i;
i = (void*)from;
while (i < (void*)0x20000)
{
if (memcmp(i, data, 4) == 0 && i != (void*)data)
{
return i;
}
i += 1;
}
return NULL;
}
void unprotect(uint32_t addr)
{
mprotect((void*)(addr - (addr % 4096)), 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
}
/* [Work In Progress] Dinamyclly change the game mode. */
FILE* fopen(const char* filename, const char* mode)
{
if (strcmp(basename((char*)filename), "player.dat") == 0)
{
FILE* level_dat_file;
char* andseq;
level_dat_name[0] = 0;
sprintf(level_dat_name, "%s/level.dat", dirname((char*)filename));
level_dat_file = fopen(level_dat_name, "rb");
fseek(level_dat_file, 0x16, SEEK_SET);
fread(&game_mode, 1, 1, level_dat_file);
if (ex_game_mode == 1)
{
andseq = "\x00\x2d\x10\x00";
} else
{
andseq = "\x60\x2f\x10\x00";
}
/* To be optimized. */
addr_0 = search(andseq, 0xde60);
unprotect((uint32_t)(addr_0));
addr_1 = search(andseq, (int)addr_0 + 4);
unprotect((uint32_t)(addr_1));
/* Don't replace this with loops or memory/string functions, it will not work. */
if (game_mode == 0)
{
((char*)addr_0)[0] = '\x60';
((char*)addr_0)[1] = '\x2f';
((char*)addr_0)[2] = '\x10';
((char*)addr_0)[3] = '\x00';
((char*)addr_1)[0] = '\x60';
((char*)addr_1)[1] = '\x2f';
((char*)addr_1)[2] = '\x10';
((char*)addr_1)[3] = '\x00';
} else if (game_mode == 1)
{
((char*)addr_0)[0] = '\x00';
((char*)addr_0)[1] = '\x2d';
((char*)addr_0)[2] = '\x10';
((char*)addr_0)[3] = '\x00';
((char*)addr_1)[0] = '\x00';
((char*)addr_1)[1] = '\x2d';
((char*)addr_1)[2] = '\x10';
((char*)addr_1)[3] = '\x00';
}
ex_game_mode = (int)game_mode;
fclose(level_dat_file);
} else if (strcmp(basename((char*)filename), "entities.dat") == 0 && strcmp(basename((char*)mode), "rb") == 0)
{
api_kill();
}
return old_fopen(filename, mode);
}
/* Fancy stuff: Change the window name. */
void SDL_WM_SetCaption(const char* title, const char* icon)
{
return old_SDL_WM_SetCaption("Minecraft - Pi Edition - Patched", "/usr/share/pixmaps/minecraft-pi.png");
}
/* "Smooth" shading. */
void glShadeModel(GLenum mode)
{
if (connected == 1)
{
api_kill();
api_setup();
}
return old_glShadeModel(GL_SMOOTH);
}
/* Fancy stuff: Green fog. */
void glFogfv(GLenum pname, const GLfloat* params)
{
GLfloat fog[] = {0.0, 1.0, 0.0, 1.0};
GLfloat start = 0.0;
if (pname == GL_FOG_COLOR)
{
return old_glFogfv(GL_FOG_COLOR, fog);
} else if (pname == GL_FOG_START)
{
return old_glFogfv(GL_FOG_START, &start);
} else
{
return old_glFogfv(pname, params);
}
}
/* Extends port search: 19132-19139. */
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen)
{
struct sockaddr_in* addr = (struct sockaddr_in*)dest_addr;
int i = 19136;
if (addr->sin_addr.s_addr == -1 && ntohs(addr->sin_port) == 19135)
{
while (i < 19139)
{
addr->sin_port = htons(i);
if (old_sendto(sockfd, buf, len, flags, dest_addr, addrlen) < 0)
{
printf("sendto failed with exit errno %i\n", errno);
}
i++;
}
addr->sin_port = htons(19135);
}
return old_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
}
/* Failed, thanks to the lack of debug symbols. Supposed to change the slot number to 2. */
int _ZN3Gui11getNumSlotsEv()
{
return 2;
}
/* [Work In Progress] T-Chat. Stores the message in `buffer` and then prints it to `stdout`. */
int SDL_PollEvent(SDL_Event* event)
{
int rt = old_SDL_PollEvent(event);
/* MCPI uses custom events, and the Mojang folks decided that storing the keycode in the (optional) window ID was a good idea. */
char chr = (char)event->user.windowID;
if (event->type == 65538)
{
/*
* 0x0d == CR/LF?
* 0x1b == Esc.
* 0x00 == Ignore, other "special" keys.
*
*/
if (reading == 0 && chr != 0x00 && chr != 0x0d && chr != 0x1b && i < 64)
{
buffer[i] = chr;
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */
i++;
} else if (reading == 1 && chr == 't')
{
reading = 0;
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */
} else if (reading == 0 && (chr == 0x0d || chr == 0x1b))
{
if (chr == 0x0d)
{
write(api_fd, "chat.post(", strlen("chat.post("));
write(api_fd, buffer, strlen(buffer));
write(api_fd, ")\n", strlen(")\n"));
}
event->user.windowID = 0x00; /* Say "ignore" to MCPI. */
reading = 1;
buffer[i] = 0x00;
i = 0;
memset(buffer, 0x00, 63); /* Clear the buffer. */
}
}
return rt;
}
/* Setup API connection. */
int api_setup()
{
int rt = 0;
struct sockaddr_in api_addr;
api_addr.sin_family = AF_INET;
api_addr.sin_port = htons(4711);
rt = inet_pton(AF_INET, "127.0.0.1", &api_addr.sin_addr);
api_fd = socket(AF_INET, SOCK_STREAM, 0);
if (api_fd < -1)
{
connected = 1;
return -1;
}
rt = connect(api_fd, (struct sockaddr*)&api_addr, sizeof(api_addr));
if (rt < 0)
{
connected = 1;
return -1;
}
connected = 0;
return 0;
}
/* Kill API connection. */
int api_kill()
{
if (connected == 0)
{
shutdown(api_fd, SHUT_RDWR);
close(api_fd);
connected = 1;
}
return 0;
}
/* Constructor, gets called before MCPI itself. */
void __attribute__((constructor)) init()
{
old_fopen = dlsym(RTLD_NEXT, "fopen");
old_sendto = dlsym(RTLD_NEXT, "sendto");
old_glShadeModel = dlsym(RTLD_NEXT, "glShadeModel");
old_glFogfv = dlsym(RTLD_NEXT, "glFogfv");
old_SDL_WM_SetCaption = dlsym(RTLD_NEXT, "SDL_WM_SetCaption");
old_SDL_PollEvent = dlsym(RTLD_NEXT, "SDL_PollEvent");
return;
}
/* Constructor, gets called when the library is unloaded from memory, and closes the API connection. */
void __attribute__((destructor)) destroy()
{
api_kill();
return;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment