Hello everyone!
I was a bit bored at work as I do not have a lot of stuff to do at the moment. I was playing on my PlayStation 2 and thought: “How hard could it be to create a homebrew game for the PS2?”.
I went online and started looking for homebrew games. To my surprise, there weren’t that many games. I saw an active community surrounding the PS2 and a great set of open-source tools to create homebrew games.
So why weren’t there any games written, I asked myself. Turns out the PS2 is a very complicated piece of technology. Apart from simple games, you would have to write low-level instructions (yes, I am talking assembly) for the two coprocessors VU0 and VU1 that the PS2 uses, to get anything decent in terms of visual fidelity.
I searched a bit more and found a game engine that takes care of the complicated stuff - the Tyra game engine. But even if you were to use Tyra, you would have to write all of the logic in C++. There is no built-in script support. Getting Lua to run is pretty simple, but you would still lack an environment to manage your assets, code, scenes, etc.
I am a big fan of the Godot engine. The idea of compiling Godot for the PS2 captivated me. I had previously stumbled upon godot-switch and godot-3ds and godot-psp, so it should theoretically be possible.
Now, Godot uses a renderer based on OpenGL ES 2.0 and 3.0. This is not a thing of the PS2, you see. The PS2 uses a chip, called the Graphics Synthesizer (GS) in combination with the VU0 and VU1 coprocessors to get the job done. But I thought that with the help of Tyra, I would use what was already there and fill in the gaps where necessary.
So, I downloaded all the required repositories and the Godot 3.5.2 repository to start compiling.
I browsed the platforms section of the code and saw that the first step was to create the detect.py
script. It configures the SCons build system (used by Godot) to compile for the target platform.
Initially, I was not aware that a guide for porting to other platforms was released. That is why, I was experimenting and trying to figure out how to do things on my own.
To build for the PS2, we need to have the correct toolchain. Luckily, the Tyra game engine came with a Dockerfile
that downloads and compiles the toolchain.
I modified the Dockerfile to include the Godot source code as a volume so that I could compile it inside the Docker container. I set up tasks inside Visual Studio Code that would run SCons inside the container to compile Godot.
Inside the detect.py
file, I had the following functions to implement:
# Checks if the platform is active. Always true.
def is_active():
return True
# The name of the platform
def get_name():
return "Playstation 2"
# Checks if the toolchain is present and working
def can_build():
return True
# Build options. Here, different features of the Godot engine can be enabled/disabled.
def get_opts():
return []
# Additional options that can be passed to SCons when building for the platform
def get_flags():
return []
# Configures the compiler. Here we set the path to the compiler, the compiler flags, defines, include directories, etc.
def configure(env):
pass
Initially, I decided to compile Godot 3.5.2, but I ran into multiple compilation issues. I bashed my head, trying different compile flags and I asked ChatGPT, but nothing helped. Because of this, I created a post on the Godot forums to ask people for help.
Somebody suggested I start with Godot version 2.1.x as it is much simpler, needs fewer system resources, and uses C++ 11, instead of C++ 17, which Godot 3.5.x uses. So, I downloaded the Godot 2.1.6 source and started working with that.
Of course, things did not go well at first. I was getting a weird error about the variant_parser.cpp
file on line 1152:
} else if (id == "IntArray") {
Vector<int32_t> args;
Error err = _parse_construct<int32_t>(p_stream, args, line, r_err_str);
if (err)
return err;
DVector<int32_t> arr;
{
int len = args.size();
arr.resize(len);
DVector<int32_t>::Write w = arr.write();
for (int i = 0; i < len; i++) {
w[i] = int(args[i]);
}
}
// value = arr;
// TODO: idortulov - The compiler gets confused on this part so do a c-style cast
value = *(Variant*)&arr;
return OK;
}
The compiler was telling me that no suitable conversion existed between a DVector<int32_t>
and Variant
. The fix was to do a C-style cast and force the conversion.
That solved this issue, but the code was still failing to compile. It was giving me errors that did not make sense. I scoured the internet, trying to figure out what was wrong. Until I stumbled on a comment somewhere that the PS2 toolchain would fail to compile when doing threaded compilation. For whatever reason, I used a -j1
parameter and to my surprise, the compilation succeeded! As the compilation with only one thread is slow, I tested different values until I found the highest value was -j8
. This was good enough for me.
Godot was now compiling, even though I hadn’t started writing the platform-specific code.
This is what I managed to accomplish in Week 1. Stay tuned for the next update and how week 2 went.
The code can be found on GitHub.