Hi everyone. I love all the rom hacks that people have been posting here, and just made an account to share that I’ve been able to get Lex Talionis games working on my retro handheld via PortMaster. To my knowledge this hasn’t been done publicly yet, but let me know if there’s something that exists out there already.
I only have the TrimUI Smart Pro using muOS, but in principle this should work with any device and OS capable of running PortMaster. I would certainly expect the TrimUI Brick and Anbernic xx devices running muOS to work. Performance isn’t perfect, but definitely playable – I’m getting on average 40-50 fps with rare slowdowns outside of loading. Some LT games run super well at 60 fps, even.
I’ll try to outline my process and help other people who might want try this. Making an actual port on PortMaster seems to require a lot more work, and I’d have to wait for permission from all the creators of the games I’d be including, so for now I’ll just leave this as a guide. If you’re fairly familiar with Linux/Python (or even just Python), you can probably just skip to the TL;DR at the bottom.
I: Setting up the LT engine
- Get a retro handheld with an OS that supports PortMaster, and make sure PortMaster is installed. Again, I’m personally using muOS.
- Ensure Westonpack 0.2 is installed in PortMaster under Options > System > Runtime Manager. If you’ve been using PortMaster there’s a good chance you already have it installed.
- Ensure Python is installed on your retro handheld. You can do this with
python --versionafter ssh-ing into your handheld. Lex Talionis uses Python 3.11.7, but muOS comes preinstalled with 3.12.x which has been working fine for me. If you need to install Python, your options are probably ssh or to cross compile an appropriate Linux build of Python and copy it over. Someone else definitely knows more about this than me. - It’s probably a good idea to also install Lex Talionis on your computer to troubleshoot with. Instructions can be found here in the official Lex Talionis documentation (sorry, I can’t post links).
- Copy the files for Lex Talionis (from step 4) onto your SD card. This can be anywhere, but I put this in a folder named
LTon the root of my SD card. If you didn’t do step 4, you can also just download the Lex Talionis engine from gitlab. - Install the necessary packages for Lex Talionis’ engine onto your device/SD card. These are found in
requirements_engine.txtin your Lex Talionis folder, and arepygame-ce 2.3.2,pyinstaller 6.2.0,typing-extensions 4.8.0. You can do this several ways:- I personally used ssh to install them. To do this, ssh onto your device and make sure you have pip by typing
python -m ensurepip. Then you can just install these packages by typingpython -m pip install package_here==version_here, or you can navigate to your Lex Talionis directory and userequirements_engine.txtto install them all in one go. - I also tried installing the latest version of
pygame-ce, which seems to work just as well. There might be some performance benefits to doing this (I’m not sure I noticed any though), but it may have worse compatability (again I’m not sure I noticed this either). - You’ll get a warning about installing them as the root user, but I chose to ignore this my first time doing this and didn’t run into any issues. The “proper way” to set this up is to use a virtual environment – if you know how to do that then probably do that. I won’t cover that here.
- You could also install the packages (again for Linux) on a different machine. I did not try this, but look up how to use pip to install a cross platform package.
- I personally used ssh to install them. To do this, ssh onto your device and make sure you have pip by typing
The Lex Talionis engine should be working on our device now, but we can’t quite test it yet. Let’s get an LT game running.
II: Getting a game to run in the engine
- Download your favorite Lex Talionis game (that does not use a custom engine!) and locate the
.ltprojfolder in the game files. In the directory you find the.exeor.batto run the game, there should also be another folder. The.ltprojfolder should usually be in that folder. - Copy the
.ltprojfolder to your Lex Talionis folder. I would recommend doing this first on your computer. - Then open up
run_engine.pyin any text/code editor (eg. Notepad++/Sublime) and find a try block towards the bottom of the script that calls the functionsfind_and_run_project(),main(), andtest_play(). This should be lines 80-82. - Comment out (or delete)
find_and_run_project()andtest_play()and uncomment themain()function (in Python we comment with#, there should be examples to follow in the file already). Also change the argument of themain()function to the name of the.ltprojfolder from step 1 in quotes (single or double are fine, and do not include.ltproj). So for examplemilieu.ltprojwe would have the linemain(‘milieu’). Do note that games sometimes use a different name for their.ltprojfolder. - Check to see that the game runs. You can do this by opening Command Prompt, navigating to your Lex Talionis folder, and typing
python run_engine.py.- I found that many releases don’t run straight away. Just copy over whatever files are needed from a working game (I just use
default.ltproj). Usually this iscredit.jsonin thegame_datadirectory of your.ltprojfolder, but sometimes also thefontsfolder inresourcesor something else entirely. The engine will give you useful error messages. - I’m assuming these discrepancies are due to many games being released on earlier versions of the Lex Talionis engine, and every game I’ve tried has worked by just using the default files.
- I would love to include the correct
credit.jsonfile, but I haven’t spent to time to figure it out and how to create said file. - For Milieu specifically, I found that the game wasn’t naming its saves correctly. To fix this, go into
constants.jsonin thegame_datafolder and look for agame_nidfield. Change this to anything unique.
- I found that many releases don’t run straight away. Just copy over whatever files are needed from a working game (I just use
- Once you have the game running (with any additional files), copy the new
run_engine.pyand.ltprojfolder into the Lex Talionis folder on your SD card. - Create a new folder in the Lex Talionis folder on your SD card called
conf. You can leave it empty. In fact this shouldn’t even be needed as long as we remove the corresponding lines in a later.shfile. I didn’t bother going through that process though. - Create a new file in the Lex Talionis folder on your SD card and call it
run.sh. Inside a text/code editor paste the following, and make sure to save with Unix end of lines (so probably use at least something like Notepad++). You can load your venv here if you’re doing that:run.sh
#!/bin/bash python “./run_engine.py” - Also in the Lex Talionis folder of your SD card create a file called
controls.gptkand paste the following contents. Feel free to read the PortMaster page for documentation to change this file to your liking – these are just some modified defaults I pulled of the page.In particular, I included the first line because I was experiencing some doubled inputs. This makes inputs wait longer before repeating. And save with Unix end of lines.I’m not actually sure this does anything.Update: I’ve been editing the engine incontrols.gptk
repeat_delay = 1000 start = enter guide = enter a = x b = z x = c y = a l1 = rightshift l2 = home l3 = mouse_right r1 = leftshift r2 = end r3 = mouse_left up = up down = down left = left right = right left_analog_up = w left_analog_down = s left_analog_left = a left_analog_right = d right_analog_up = end right_analog_down = home right_analog_left = left right_analog_right = right\app\engine\fluid_scroll.pyby changingspeed=andslow_speed=to fairly high values (200 and 4, respectively) to control scrolling speed in menus. To do this for the minimap, I’ve been editing\app\engine\cursor.pyand changing the arguments in line 25 whereFluidScrollis called. I have it atframe2m(4), 3.25, again to slow down the scrolling speed. - Somewhere in the ROM folder of your SD card, create an
.shfile with the contents below. I’ll call itLex Talionis.shfor now. This should probably be with your other PortMaster.shfiles, especially if your OS has strict folder requirements. If you didn’t put your Lex Talionis folder on the root of your SD card, or changed any of the file/folder names in previous steps, make sure you check thatGAMEDIR,CONFDIR,EXECUTABLE, andCONTROLSCHEMEare correct (towards the top of the file). Again, make sure we have Unix end of lines.Lex Talionis.sh
#!/bin/bash XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share} if [ -d "/opt/system/Tools/PortMaster/" ]; then controlfolder="/opt/system/Tools/PortMaster" elif [ -d "/opt/tools/PortMaster/" ]; then controlfolder="/opt/tools/PortMaster" elif [ -d "$XDG_DATA_HOME/PortMaster/" ]; then controlfolder="$XDG_DATA_HOME/PortMaster" else controlfolder="/roms/ports/PortMaster" fi source $controlfolder/control.txt [ -f "${controlfolder}/mod_${CFW_NAME}.txt" ] && source "${controlfolder}/mod_${CFW_NAME}.txt" get_controls GAMEDIR=/$directory/LT/ CONFDIR="$GAMEDIR/conf/" EXECUTABLE="run.sh" CONTROLSCHEME="controls" mkdir -p "$GAMEDIR/conf" cd $GAMEDIR > "$GAMEDIR/log.txt" && exec > >(tee "$GAMEDIR/log.txt") 2>&1 export XDG_DATA_HOME="$CONFDIR" export LD_LIBRARY_PATH="$GAMEDIR/libs.${DEVICE_ARCH}:$LD_LIBRARY_PATH" export SDL_GAMECONTROLLERCONFIG="$sdl_controllerconfig" # Mount Weston runtime weston_dir=/tmp/weston $ESUDO mkdir -p "${weston_dir}" weston_runtime="weston_pkg_0.2" if [ ! -f "$controlfolder/libs/${weston_runtime}.squashfs" ]; then if [ ! -f "$controlfolder/harbourmaster" ]; then pm_message "This port requires the latest PortMaster to run, please go to https://portmaster.games/ for more info." sleep 5 exit 1 fi $ESUDO $controlfolder/harbourmaster --quiet --no-check runtime_check "${weston_runtime}.squashfs" fi if [[ "$PM_CAN_MOUNT" != "N" ]]; then $ESUDO umount "${weston_dir}" fi $ESUDO mount "$controlfolder/libs/${weston_runtime}.squashfs" "${weston_dir}" if [ -f "${controlfolder}/libgl_${CFW_NAME}.txt" ]; then source "${controlfolder}/libgl_${CFW_NAME}.txt" else source "${controlfolder}/libgl_default.txt" fi $GPTOKEYB "${EXECUTABLE}" -c "./${CONTROLSCHEME}.gptk" & # xbox360 tag runs the app with virtual xbox360 controller, but is currently broken for muOS pm_platform_helper "$GAMEDIR/${EXECUTABLE}" $ESUDO env HOME=$HOME CRUSTY_SHOW_CURSOR=0 $weston_dir/westonwrap.sh headless noop kiosk crusty_glx_gl4es \ XDG_DATA_HOME="$GAMEDIR" WAYLAND_DISPLAY= \ ./${EXECUTABLE} #Clean up after ourselves $ESUDO $weston_dir/westonwrap.sh cleanup if [[ "$PM_CAN_MOUNT" != "N" ]]; then $ESUDO umount "${weston_dir}" fi pm_finish
Now we’re ready to run the game. Eject your SD card, put it into your retro handheld, and run the Lex Talionis however your OS runs ports via PortMaster. Be sure to run it in performance mode if your OS has options for that. Booting it up can sometimes take a bit especially the first time, but hopefully it does run and we can quit out for now with either select or hotkey+start. I want to just go over some other tweaks I’ve made to improve my experience:
III: Extras
- Go into the
savesfolder in the Lex Talionis directory and editconfig.iniso thatdebug=0. This removes the debug menu from our game, and also makes it so the escape key (select) does not quit out of the game. You can also change any other settings you want here, but I would recommend doing keybindings in game. - In the Lex Talionis directory, edit
\app\engine\engine.py. Look for a line (for me line 41) that says
and change thereturn pygame.display.set_mode(size, pygame.FULLSCREEN if cf.SETTINGS['fullscreen'] else 0)0topygame.SCALEDso that it reads
This will make it so that your game is integer scaled and doesn’t stretch out to the aspect ratio of your screen. I’m not aware of a method for preserving aspect ratio without integer scaling. There’s a bunch of other flags/options you could try here, such asreturn pygame.display.set_mode(size, pygame.FULLSCREEN if cf.SETTINGS['fullscreen'] else pygame.SCALED)vsync=1; consult the pygame-ce documentation. - I’ve found that the using the smallest screen size in the settings results in a slight boost in performance. But since I’m using
pygame.SCALEDin the previous step, I still see the largest possible integer scaled image. Honestly I can’t even tell the difference, if there is any, between having the game render a larger image or just scaling it up afterward. - To add more Lex Talionis games, simply repeat the steps above to copy over your
.ltprojfolder and create newrun_engine.py,run.sh, andLex Talionis.shfiles. Obviously you’ll have to name these something else. In fact, I renamed these all to reflect the name of the game I’m running. You’ll have to editrun.shto run the corresponding.pyfile andLex Talionis.shto have the correctEXECUTABLEfile. Or if you’re familiar with some coding, go ahead and pass along an argument through these files. In any case the good news is that the engine is shared between all of these games. - A few games (eg. Blade and Claw or The Unbroken Thread) will default to displaying “The Lion Throne” on the title screen. Certainly not game breaking, but if you want their intended blank title screen, in
game_project.ltproj/resources/custom_sprites(make the folder if it doesn’t exist), copy in a blank/transparent 400x400 png. It probably can be any (small) size, but to be safe I simply copied over the one that is used in Milieu. - For games running the legacy engine (eg. Snow and Fire or Absolution), I’ve been able to download the file
main.pyfrom the old build of LT on gitlab (sorry I can’t post links, but it shows up if you Google lex talionis gitlab). Then copymain.pyto the directory with the foldersCode,Data,Saves, etc. and do the steps above, runningmain.pyinstead ofrun_engine.py. I’ve only booted these games and briefly looked at Chapter 1 though, so YMMV. - To get custom engines running, you’ll need a new installation of the Lex Talionis engine. Get a working version of the engine on your computer, and copy that into a different directory on your SD card. You do not need to install the Python packages again, but everything else you will need to repeat. Akira Sou has a nice video on how to set up Embrace of the Fog, for example (I’m not actually aware of other games with custom engines, so please let me know what else is out there). I’m getting substantially worse performance on Embrace of the Fog though, around 20-30 fps in the actual chapters.
- Out of the things I’ve tested, the only game not really running is Myriad Fortunes. I had to mess around with some of the game data to even get into the casino, and the menus are still wonky/slightly glitchy. I’m assuming it’s got some engine modifications present… If anyone knows how I can get a working engine for Myriad Fortunes I’d be interested.
TL;DR: For custom OSes with Python and PortMaster, install the requirements for the LT engine on your handheld and you can run LT games with the westonpack runtime. Copy over the .ltproj folder of your favorite LT game into your LT engine, and on a PC check that it runs by running run_engine.py (step 5 under section II for some troubleshooting). Take a look at the run.sh, controls.gptk, and Lex Talionis.sh scripts above (steps 8-10 under section II), and any tweaks/tips as needed (section III).
Wow that was quite a bit of text but I promise the process isn’t all that difficult. I did go through everything a while ago and tinkered a lot along the way, and this guide is my first draft at recreating the steps from memory, so if something doesn’t work please let me know and I’ll help troubleshoot/fix the guide. I’d love to hear feedback on how this works on other devices and OSes as well. Update: I’ve got this working on Knulli, but performance is a bit worse than MuOS. Something like 10-20% lower fps.
And finally, thanks to the developers of Lex Talionis for all their work, as well as all the people here making rom hacks and fan games, Lex Talionis or otherwise. I don’t want to post any files, but if I get the okay from the devs of Lex Talionis and the makers of LT games, I could upload a zip of all the files you would be creating above, in an almost-ready-to-run state.