Rhythm game osu! database parser & serializer.
(For the old stable client, not lazer.)
(Files: osu!.db
, collection.db
, scores.db
)
Last update is for v20250108, last tested on v20250401.
Parser is written in Kaitai Struct .ksy
yaml file according to osu wiki page,
then transpiled to many programming languages.
Precompiled code is released. (Version is the version of osu!.)
To compile manually, install kaitai-struct-compiler
,
then run kaitai-struct-compiler --target all --outdir ./osu_db_kaitai *.ksy
.
Some languages need runtime library to run the compiled parser.
Please refer to Kaitai Struct documentation.
For example, Python: pip install kaitaistruct
from osu_db import OsuDb
osu_data = OsuDb.from_file(osu_db_path)
print(osu_data.osu_version) # 20250108
Bool & String are user-defined types. Use .value
to access.
>>> scores_data.beatmaps[0].scores[0].perfect_combo
<osu_scores.OsuScores.Bool object at 0x...>
>>> scores_data.beatmaps[0].scores[0].perfect_combo.value
True
I compiled .ksy
to Python Construct,
then edited the code manually to make it work.
This is the best solution I know.
As of this writing, Kaitai Struct's compiled code for Python Construct is not good.
It's missing some things, and the CI test rating for Construct is <50%.
Another possible solution is to use the experimental serialization feature (Python & Java),
but it's immature, not included in released compiler, still in its own branch.
To use Construct, which can both parse and serialize,
download/clone this repo (get the osu_db_construct
folder),
pip install construct
,
from osu_db import osu_db
osu_data = osu_db.parse_file(osu_db_path)
print(osu_data.osu_version) # 20250108
osu_binary_data = osu_db.build(osu_data)
Types are more "flat", .value
is no longer needed,
because I wrote Adapter
s to handle them:
>>> scores_data.beatmaps[0].scores[0].perfect_combo # no `.value`
True
>>> osu_data.beatmaps[0].star_rating_osu # no `.pairs`
[...]
When serializing, you don't have to specify list length like num_beatmaps
,
ArrayAdapter
sets it for you behind the scene. Example:
collection_binary = osu_collection.build(dict(
version=20250108,
collections=[dict(
name="my collection",
beatmaps_md5s=["deadbeefcafebabe"]
)]
)) # no `num_collections`
A CLI playlist converter playlist.py
is included in osu_db_construct
.
By default, it converts all collections to .pls
playlist files,
saving to osu_collection_playlist
folder.
python playlist.py -h
to see all options. (.m3u8
is also supported.)
If you found something wrong, maybe the DB structure changed,
please create an issue to let me know.