Improve Modding
This commit is contained in:
parent
a2d3941bd5
commit
a2b2d6e7c4
82
MODDING.md
Normal file
82
MODDING.md
Normal file
@ -0,0 +1,82 @@
|
||||
# Modding
|
||||
Modding Minecraft: Pi Edition is possible by patching the binary at runtime. To make this easier ``minecraft-pi-dcoker`` includes a libary called ``libcore.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-docker`` loads mods from two locations, ``/app/minecraft-pi/mods``, and ``~/.minecraft/mods``. The first location only exists in the Docker container and is immutable, so you should place your mods in ``~/.minecraft/mods`` which is mounted on the host as ``~/.minecraft-pi/mods``.
|
||||
|
||||
## ``libcore.so`` API
|
||||
Header files and the shared library can be download from [Jenkins](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/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
|
||||
The original contents of the function.
|
||||
|
||||
#### 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 revert_overwrite(void *start, void *original)``
|
||||
This allows you to revert ``overwrite()``. This can be used to call the original version of a function.
|
||||
|
||||
#### Parameters
|
||||
- **start:** The function that was overwritten.
|
||||
- **original:** The return value of ``overwrite()``.
|
||||
|
||||
#### Return Value
|
||||
None
|
||||
|
||||
#### 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) {
|
||||
revert_overwrite((void *) func, func_original);
|
||||
(*func)(a, b);
|
||||
revert_overwrite((void *) func, func_original);
|
||||
|
||||
return a + 4;
|
||||
}
|
||||
|
||||
__attribute__((constructor)) static void init() {
|
||||
func_original = overwrite((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);
|
||||
}
|
||||
```
|
@ -3,3 +3,6 @@ This is a project allowing Minecraft: Pi Edition to be run without a Raspberry P
|
||||
|
||||
## Setup
|
||||
[View Binaries](https://jenkins.thebrokenrail.com/job/minecraft-pi-docker/job/master/lastSuccessfulBuild/artifact/out/)
|
||||
|
||||
## Modding
|
||||
[View Modding](MODDING.md)
|
||||
|
@ -4,11 +4,6 @@ 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)
|
||||
|
@ -19,22 +19,29 @@ static int ends_with(const char *s, const char *t) {
|
||||
return strcmp(s + slen - tlen, t) == 0;
|
||||
}
|
||||
|
||||
static void set_and_print_env(char *name, char *value) {
|
||||
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 Not Trailing Colon
|
||||
trim(value);
|
||||
|
||||
fprintf(stderr, "Set %s = %s\n", name, value);
|
||||
setenv(name, value, 1);
|
||||
}
|
||||
|
||||
static char *get_env_safe(const char *name) {
|
||||
// Get Variable Or Blank String If Not Set
|
||||
char *ret = getenv(name);
|
||||
return ret != NULL ? ret : "";
|
||||
}
|
||||
|
||||
static void load(char **ld_preload, char *folder) {
|
||||
static void load(char **ld_path, char **ld_preload, char *folder) {
|
||||
int folder_name_length = strlen(folder);
|
||||
while (1) {
|
||||
DIR *dp = opendir(folder);
|
||||
@ -44,7 +51,8 @@ static void load(char **ld_preload, char *folder) {
|
||||
while (1) {
|
||||
entry = readdir(dp);
|
||||
if (entry != NULL) {
|
||||
if (starts_with(entry->d_name, "lib") && ends_with(entry->d_name, ".so")) {
|
||||
// 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];
|
||||
@ -58,9 +66,11 @@ static void load(char **ld_preload, char *folder) {
|
||||
|
||||
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\n", strerror(errno));
|
||||
exit(1);
|
||||
} else {
|
||||
@ -68,8 +78,13 @@ static void load(char **ld_preload, char *folder) {
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
|
||||
// Add To LD_LIBRARY_PATH
|
||||
asprintf(ld_path, "%s:%s", *ld_path, folder);
|
||||
|
||||
return;
|
||||
} else if (errno == ENOENT) {
|
||||
// Folder Doesn't Exists, Attempt Creation
|
||||
char *cmd = NULL;
|
||||
asprintf(&cmd, "mkdir -p %s", folder);
|
||||
int ret = system(cmd);
|
||||
@ -77,6 +92,7 @@ static void load(char **ld_preload, char *folder) {
|
||||
exit(ret);
|
||||
}
|
||||
} else {
|
||||
// Unable To Open Folder
|
||||
fprintf(stderr, "Error Opening Directory: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
@ -88,26 +104,36 @@ int main(__attribute__((unused)) int argc, char *argv[]) {
|
||||
|
||||
char *ld_path = NULL;
|
||||
|
||||
// Start Configuring LD_LIBRARY_PATH
|
||||
char *cwd = getcwd(NULL, 0);
|
||||
asprintf(&ld_path, "%s:/usr/arm-linux-gnueabihf/lib:%s", cwd, get_env_safe("LD_LIBRARY_PATH"));
|
||||
asprintf(&ld_path, "%s:/usr/arm-linux-gnueabihf/lib", cwd);
|
||||
free(cwd);
|
||||
|
||||
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(&ld_preload, "./mods/");
|
||||
// Load Mods From ./mods
|
||||
load(&ld_path, &ld_preload, "./mods/");
|
||||
|
||||
// Loads Mods From ~/.minecraft/mods
|
||||
char *home_mods = NULL;
|
||||
asprintf(&home_mods, "%s/.minecraft/mods/", getenv("HOME"));
|
||||
load(&ld_preload, home_mods);
|
||||
load(&ld_path, &ld_preload, home_mods);
|
||||
free(home_mods);
|
||||
|
||||
// Add Existing LD_LIBRARY_PATH
|
||||
asprintf(&ld_path, "%s:%s", ld_path, get_env_safe("LD_LIBRARY_PATH"));
|
||||
|
||||
// Set LD_LIBRARY_PATH
|
||||
set_and_print_env("LD_LIBRARY_PATH", ld_path);
|
||||
free(ld_path);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ project(mods)
|
||||
add_compile_options(-Wall -Wextra -Werror)
|
||||
|
||||
add_subdirectory(../core core)
|
||||
include_directories(../core/include)
|
||||
|
||||
include_directories(include)
|
||||
|
||||
add_library(core SHARED src/core.c)
|
||||
target_link_libraries(core dl)
|
||||
|
||||
add_library(compat SHARED src/compat.c)
|
||||
target_link_libraries(compat core SDL EGL GLESv1_CM GLESv2 X11 dl)
|
||||
|
@ -195,6 +195,7 @@ __attribute__((constructor)) static void init() {
|
||||
|
||||
// Change Username
|
||||
const char *username = get_username();
|
||||
fprintf(stderr, "Setting Username: %s\n", username);
|
||||
patch_address((void *) 0x18fd4, (void *) username);
|
||||
|
||||
if (has_feature("Disable Autojump By Default")) {
|
||||
|
@ -7,7 +7,16 @@ chmod -R g-s debian
|
||||
|
||||
# Clean out Directory
|
||||
rm -rf out
|
||||
mkdir out
|
||||
mkdir -p out/deb
|
||||
|
||||
# Generate DEB
|
||||
dpkg -b debian out
|
||||
dpkg -b debian out/deb
|
||||
|
||||
# Export Libraries
|
||||
mkdir -p out/lib
|
||||
|
||||
# Copy Headers
|
||||
cp -r mods/include out/lib/include
|
||||
|
||||
# Copy Shared Library
|
||||
docker run -v "$(pwd)/out/lib:/out" --entrypoint sh thebrokenrail/minecraft-pi -c 'cp ./mods/lib*.so /out'
|
||||
|
Loading…
Reference in New Issue
Block a user