8f7db143a6
This is a change to something that was so massively stupid and overcomplicated that I feel like I need to explain and justify myself: After figuring out the format for manifests and spending countless hours staring at IDA/Ghidra I kinda was sick of that, so I decided to figure out what to do with the manifest myself by playing around with it, which was also a lot more fun than looking through disassembly. When looking at the chunks and files it quickly became obvious that the way they're created is by concatenating all files into 1 MiB chunks that can be downloaded and reassmebled (mostly) sequentially. What I did not know was how the order of files in this "stream" was determined. In playing around with it I came up with the old method: essentially forming a chain of files, because each file's end generally pointed to the start of the next file. And it worked great! At least until now... Yesterday somebody alerted me to a game where this failed and it took me a bit to figure out. Essentially the chaining had failed because multiple files started at the same offset, but some of them would follow another chain that never went back to the chunk it started at, effectively skipping those files. This was rather annoying to deal with, I came up with a workaround but it wasn't pretty. So I decided to jump back into IDA/Ghidra and find out how Epic does it for real. Well it took me a while, but thanks to symbols (yay macOS!) and a decent decompiler in Ghidra even a noob like me was able to find it eventually. The answer is as simple as it can be: the files are sorted alphabetically (case-insensitive). So really all I ever had to do was to sort files alphabetically and then run through them to create the list of tasks. I feel so stupid. P.S.: I tested a few games and for the most part the resulting file processing order is identical between the old and the new method. The cases where it differs is when there's heavy de-duplication happening (e.g. Diabotical's small model files) but the runtime cache size remains the same so both methods are equally efficient, the old one just can't handle certain cases. |
||
---|---|---|
legendary | ||
.gitignore | ||
LICENSE | ||
README.md | ||
requirements.txt | ||
setup.py |
Legendary Game Launcher
A free and open-source Epic Games Launcher replacement
Legendary is an open-source game launcher that can download and install games from the Epic Games Store on Linux and Windows. It's name as a tongue-in-cheek play on tiers of item rarity in many MMORPGs.
Right now it is in an early public testing stage and still needs a lot of work to work. But it does work!
What works:
- Authenticate with Epic
- Download and install games and their DLC
- Delta patching/updating of installed games
- Launch games with online authentication
Planned:
- PyPI/PPA distribution
- Simple GUI for managing/launching games
- Importing installed games from the EGS launcher
- Lots and lots of bug fixes, optimizations, and refactoring...
Requirements
- python 3.8+ (64-bit)
- requests
- setuptools (for installation)
How to run/install
Package Manager
Depending on your Distro legendary may be available via (unofficial) packages that can be directly installed using your package manager. Check out the Available Linux Packages wiki page for details.
Currently this is limited to Arch Linux (AUR) but will hopefully be expanded to other distros rather soon.
Standalone
Download the latest legendary
or legendary.exe
binary from the latest release
and move it to somewhere in your $PATH
/%PATH%
. Don't forget to chmod +x
it on Linux.
The Windows .exe and Linux executable were created with PyInstaller and will run standalone even without python being installed. Note that on Linux glibc >= 2.25 is required, so older distributions such as Ubuntu 16.04 or Debian stretch will not work.
Python package
(A PyPI package will follow once it has gotten more testing.)
- Install python3.8, setuptools and requests
- Clone the git repository
- Run
python3.8 setup.py install
Ubuntu 20.04 example
Ubuntu 20.04's standard repositories include everything needed to install legendary:
sudo apt install python3 python3-requests python3-setuptools-git
git clone https://github.com/derrod/legendary.git
cd legendary
sudo python3 setup.py install
Note that in this example we used sudo
to install the package on the system, this may not be advisable depending on your setup.
Directly from the repo (for dev/testing)
- Install python3.8 and requests (optionally in a venv)
- cd into
legendary/
(the folder withcli.py
) - run
PYTHONPATH=.. python3.8 cli.py
Quickstart
To log in:
$ legendary auth
Authentication is a little finicky since we have to go through the Epic website. The login page should open in your browser and after logging in you should be presented with a JSON response that contains a code, just copy and paste the code into your terminal to log in.
Listing your games
$ legendary list-games
This will fetch a list of games available on your account, the first time may take a while depending on how many games you have.
Installing a game
$ legendary download Anemone
Important: the name used for these commands is the app name, not the game's name! The app name is in the parentheses after the game title in the games list.
List installed games and check for updates
$ legendary list-installed --check-updates
Launch (run) a game with online authentication
$ legendary launch Anemone
Tip: most games will run fine offline (--offline
), and thus won't require launching through legendary for online authentication. You can run legendary launch <App Name> --offline --dry-run
to get a command line that will launch the game with all parameters that would be used by the Epic Launcher. These can then be entered into any other game launcher (e.g. Lutris/Steam) if the game requires them.
Usage
usage: legendary [-h] [-v] [-y] [-V] {auth,download,uninstall,launch,list-games,list-installed} ...
Legendary v0.0.X - "Witty Name"
optional arguments:
-h, --help show this help message and exit
-v Set loglevel to debug
-y Default to yes for all prompts
-V Print version and exit
Commands:
{auth,download,uninstall,launch,list-games,list-installed}
auth Authenticate with EPIC
download Download a game
uninstall Uninstall (delete) a game
launch Launch a game
list-games List available (installable) games
list-installed List installed games
Individual command help:
Command: auth
usage: legendary auth [-h] [--import]
optional arguments:
-h, --help show this help message and exit
--import Import EGS authentication data
Command: download
usage: legendary download <App Name> [options]
positional arguments:
<App Name> Name of the app
optional arguments:
-h, --help show this help message and exit
--base-path <path> Path for game installations (defaults to ~/legendary)
--game-folder <path> Folder for game installation (defaults to folder in metadata)
--max-shared-memory <size>
Maximum amount of shared memory to use (in MiB), default: 1 GiB
--max-workers <num> Maximum amount of download workers, default: 2 * logical CPU
--manifest <uri> Manifest URL or path to use instead of the CDN one (e.g. for downgrading)
--old-manifest <uri> Manifest URL or path to use as the old one (e.g. for testing patching)
--base-url <url> Base URL to download from (e.g. to test or switch to a different CDNs)
--force Ignore existing files (overwrite)
--disable-patching Do not attempt to patch existing installations (download entire changed file)
--download-only Do not mark game as intalled and do not run prereq installers after download
--update-only Abort if game is not already installed (for automation)
--dlm-debug Set download manager and worker processes' loglevel to debug
--platform <Platform>
Platform override for download (disables install)
Command: uninstall
usage: legendary uninstall [-h] <App Name>
positional arguments:
<App Name> Name of the app
optional arguments:
-h, --help show this help message and exit
Command: launch
usage: legendary launch <App Name> [options]
Note: additional arguments are passed to the game
positional arguments:
<App Name> Name of the app
optional arguments:
-h, --help show this help message and exit
--offline Skip login and launch game without online authentication
--skip-version-check Skip version check when launching game in online mode
--override-username <username>
Override username used when launching the game (only works with some titles)
--dry-run Print the command line that would have been used to launch the game and exit
Command: list-games
usage: legendary list-games [-h] [--platform <Platform>] [--include-ue]
optional arguments:
-h, --help show this help message and exit
--platform <Platform>
Override platform that games are shown for
--include-ue Also include Unreal Engine content in list
Command: list-installed
usage: legendary list-installed [-h] [--check-updates]
optional arguments:
-h, --help show this help message and exit
--check-updates Check for updates when listing installed games
Config file
Legendary supports some options as well as game specific configuration in ~/.config/legendary/config.ini
:
[Legendary]
log_level = debug
; maximum shared memory (in MiB) to use for installation
max_memory = 1024
; default install directory
install_dir = /mnt/tank/games
; default settings to use (currently limited to WINE executable)
[default]
; (linux) specify wine executable to use
wine_executable = wine
; default environment variables to set (overriden by game specific ones)
[default.env]
WINEPREFIX = /home/user/legendary/.wine
; Settings to only use for "AppName"
[AppName]
; launch game without online authentication by default
offline = true
; Skip checking for updates when launching this game
skip_update_check = true
; start parameters to use (in addition to the required ones)
start_params = -windowed
wine_executable = proton
[AppName.env]
; environment variables to set for this game (mostly useful on linux)
WINEPREFIX = /mnt/tank/games/Game/.wine
DXVK_CONFIG_FILE = /mnt/tank/games/Game/dxvk.conf