Porting my game to PlayStation2
When the quest with cmake begins...
A week ago I was sitting at the late night and found ps2dev repo on my local disk. It was there for a very long time. Sometimes I took a look on the examples and close them. Everything seems very complicated.
But since I'm developing my games with SDL2 which has a PS2 support I've put my fingers on the keyboard and experiments begin.
PS2DEV: https://github.com/ps2dev/ps2dev
First of all the ps2dev and ps2sdk should be buit on local. Install instruction in ps2dev is very straight forward and was completed almost without errors.
Except one with some port building. I don't rememember how it was fixed.
The next thing was to figure out how to build ELF file with cmake. Almost all examples in the ps2dev were created with Makefile.
I'm using cmake in vscode where it's really handy to select the build I need. Debug or Release by default. No other options.
Here comes solution with cmake-variants.yaml file in the root folder of the project.
buildType:
default: debug
choices:
debug:
short: Debug
long: Emit debug information
buildType: Debug
release:
short: Release
long: Optimize generated code
buildType: Release
release_ps2:
short: Release PS2
long: Optimize generated code
buildType: Release_PS2
Also I want to have a separate build folder per buildType. It's not controlled via CMakeLists.txt file, but with vscode settings.
In .vscode/settings.json
"cmake.buildDirectory":"${workspaceFolder}/build/${buildType}"
Then in CMakeLists.txt I can handle variants with CMAKE_BUILD_TYPE variable.
if(CMAKE_BUILD_TYPE STREQUAL "Release_PS2")
endif ()
Important settings
set(PS2 1)
set(EE 1)
set(CMAKE_SYSTEM_NAME "PlayStation2")
set(CMAKE_SYSTEM_PROCESSOR "mips64r5900el")
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_NO_SYSTEM_FROM_IMPORTED ON)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(PS2DEV "$ENV{PS2DEV}")
set(PS2SDK "$ENV{PS2SDK}")
set(GSKIT "$ENV{GSKIT}")
Add extension to the output file if you need it to run in the emulator
set(CMAKE_EXECUTABLE_SUFFIX ".elf")
Output compiled file to custom folder
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/ps2)
All includes and libs
set(
MY_LINK_DIRS
${PS2DEV}/gsKit/lib
${PS2SDK}/ports/lib
${PS2SDK}/ee/lib
)
set(
MY_LIBRARIES
SDL2main
SDL2
SDL2_mixer
SDL2_ttf
z
ps2_drivers
gskit
dmakit
modplug
freetype
audsrv
debug
pad
patches
xmp
png
)
set(
MY_INCLUDE_DIRS
"${PS2SDK}/ports/include/SDL2/"
"${PS2SDK}/ports/include/SDL_ttf/"
"${PS2SDK}/ports/include/SDL_mixer/"
"${PS2SDK}/ports/include/"
"${PS2DEV}/gsKit/include/"
"${PS2SDK}/ee/include/"
"${PS2SDK}/common/include/"
)
And set flags to compiler as is in Makefiles
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_EE -G0 -O2 -Wall -gdwarf-2 -gz"
Link everything
target_link_directories(${PROJECT_NAME} PUBLIC ${MY_LINK_DIRS} )
include_directories(${MY_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PUBLIC {MY_LIBRARIES})
Somehow it was compiled. Now it's time to test it. For this task there is a ps2link and ps2client programs.
Run and debug on PlayStation2
ps2link elf file should be executed from FreeMcBoot. I've create a link in the main menu for the fast access. Configuration is simple, just add correct IP in the config file and place it with the elf.
ps2client will be compiled within the ps2dev installation to /usr/local/ps2dev/bin/ .
PS2 should be connected to the router.
Then run ps2link on the PS2 and run
ps2client -h PS2_IP_FROM_CONFIG execee host:MYGAME.ELF
Also you can see logs for ps2link via
ps2client -h PS2_IP_FROM_CONFIG listen
And it was working. But I didn't see the logs!
SDL_Log is not very useful, since it's writing only the last message to the file SDL_Log.txt in the folder with ELF file.
Compiled examples shows logs, my binaries compiled with cmake - NO.
The issue was with missed flag: -T${PS2SDK}/ee/startup/linkfile
so the CMAKE_CXX_FLAGS should be like this one.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -T${PS2SDK}/ee/startup/linkfile -D_EE -G0 -O2 -gdwarf-2 -gz")
the I can do a printf("Some logs, or error %s\n", SDL_GetError());
Nice.
Next thing was to setup correct screen for PS2. It has 640x448 in NTSC mode. Most of my app has lower resolution.
It was fixed with
SDL_RenderSetClipRect(renderer, &viewport);
where viewport is a SDL_Rect centered on the screen. With some other fixes all sprites were placed in the correct place on the screen.
Ah... sprites are loading. Another issue I was struggled for a long.
When launching ELF files from ps2client host0: should be added at the beginning of filename. I'm using "assets" folder for graphics. It was working with ps2 client.
But I will have more problems later when I will play with ISO file.
SDL2 has a nice function SDL_RenderTarget() where you can update texture which was created with SDL_TEXTUREACCESS_TARGET flag.
PS2 textures do not have this option.
You can check if your renderer supports texture_target in SDL_RendererInfo.
SDL_RenderDriver PS2_RenderDriver = {
.CreateRenderer = PS2_CreateRenderer,
.info = {
.name = "PS2 gsKit",
.flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE,
.num_texture_formats = 2,
.texture_formats = {
[0] = SDL_PIXELFORMAT_ABGR1555,
[1] = SDL_PIXELFORMAT_ABGR8888,
},
.max_texture_width = 1024,
.max_texture_height = 1024,
}
};
but
static int PS2_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
{
return 0;
}
So all my texture manipulations flows were not working.
It was fixed with creating surface, blit everything I need on it and then create texture from the surface.
For textures which should be updated often I need to have surface and texture available. Something like this.
SDL_Texture *texture;
SDL_Surface *texture_map_surface = SDL_LoadBMP("host:IMAGE.BMP");
SDL_Surface *surface = SDL_CreateRGBSurface(0, width, height, 32, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
SDL_BlitSurface(texture_map_surface, &src_rect, surface, &src_rect);
if (texture != NULL) SDL_DestroyTexture(texture);
texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
Next one interesting this was to use gamepad in the menu. All my previous games were designed for touch interface.
So interesting this is here. Init game controller (which will init SDL_INIT_JOYSTICK and SDL_INIT_EVENTS).
if (SDL_Init(SDL_INIT_GAMECONTROLLER) == 0)
{
printf("Try to enable joystick ++++\n");
EnableGamepad();
}
And it will work somehow within ps2client.
But will not work on pcsx emu.
The issue was again with cmake configs. Missing -Dmain=SDL_main directive
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -T${PS2SDK}/ee/startup/linkfile -Dmain=SDL_main -D_EE -G0 -O2 -gdwarf-2 -gz")
At this point lots of errors were fixed in my engine and game was running fine on PS2 except some small graphics issues.
Creating ISO
Next step is to create an ISO file (and write DVD disk later =))
Should be really straight forward:
create a folder, place SYSTEM.CNF file with info
BOOT2 = cdrom0:\SOMEGAME.00;1
VER = 1.00
VMODE = PAL
SOMEGAME.00 is the main game ELF file. it should have 8 symbols in filename and only 2 in extension. 3 will not work there. I tried. And everything uppercase.
Create iso with the command
genisoimage -udf -o ${ISO_NAME} ${PROJECT_ROOT}/bin/ps2_iso
Nice to have debug console available in pcsx2 emulator.
But loading the game from ISO within OPL loader was another headache.
None of my files were processed.
- First rule is to have cdrom0: in the filename.
- Second - filenames should be uppercase 8.3 symbols.
- Third - you can't use folders. I don't know why but all my backslashes were vanished in the compiled ELF file. Possibly the issue with flags to compilator.
- Fourth -
SDL_RWops *fp = SDL_RWFromFile("cdrom0:IMAGE.BMP", "rb");will not work. This was one of the issues I've spent lot's of time to fix and didn't sleep a couple of nights.
The solution is to use:
FILE *fpt = fopen("cdrom0:IMAGE.BMP", "r");
SDL_RWops *fp = SDL_RWFromFP(fpt, SDL_TRUE);
I was playing a lot with loading IRX files but nothing helped with reading the files from cdrom.
also irx loading files from cdrom was working fine with
SifLoadModule("cdrom0:\\CDFS.IRX", 0, NULL);
But still the iso is not working on PS2 but works fine on pcsx2 emulator.
Disk read is slow.
To test files in pcxs2 "Enable host filesystem" should be checked!