About
This is the v2.1.0 release of simfile for Python 3. This is a feature release primarily focused on new abstractions around filesystem traversal & asset discovery for simfiles. It also includes some critical bugfixes & significant performance boosts, mostly by upgrading msdparser to its new major release v2.0.0.
Up-to-date documentation for this release can be found on simfile.readthedocs.io.
Filesystem traversal
The new simfile.dir module offers SimfileDirectory and SimfilePack classes for nagivating simfile filesystem structures:
from simfile.dir import SimfilePack
pack = SimfilePack(R"C:\StepMania\Songs\My Pack")
for simfile_dir in pack.simfile_dirs():
print("Simfile paths: ", simfile_dir.sm_path, simfile_dir.ssc_path)
sim = simfile_dir.open()
print("Title:", sim.title)
Additionally, the top-level simfile module now offers opendir() and openpack() functions as simplified interfaces to the simfile.dir API:
import simfile
for sim, filename in simfile.openpack(R"C:\StepMania\Songs\My Pack"):
print(f"Loaded {sim.title} from {filename}")
These new features aren't limited to the OS filesystem, either. PyFilesystem2 has been integrated throughout this library’s filesystem interactions, enabling OS and non-OS filesystems to be traversed using the same code. All functions, methods, and constructors that lead to filesystem interactions now have an optional filesystem
parameter for specifying a PyFS filesystem object. When omitted, the filesystem defaults to the native OS filesystem as before.
This means you can open a ZIP file (or any other supported filesystem) as a SimfileDirectory or SimfilePack, without needing to extract to disk:
import simfile
from fs.zipfs import ZipFS
zip_fs = ZipFS(R"C:\Users\micro\Downloads\Coconut [13].zip")
sim, filename = simfile.opendir("Coconut", filesystem=zip_fs)
print(f"Loaded {sim.title} from {filename} inside ZIP")
Asset discovery
The new simfile.assets module provides an Assets class that can reliably discover paths to simfile assets, even if they’re not specified in the simfile. This functionality is also accessible through SimfileDirectory's assets() method.
from simfile.dir import SimfileDirectory
simfile_dir = SimfileDirectory(R"C:\StepMania\Songs\My Pack\My Song")
assets = simfile_dir.assets()
assert assets.music, "No music file"
with open(assets.music, "rb") as audio_file:
...
Assets include the song file (music
) and graphics files (banner
, background
, cdtitle
, jacket
, cdimage
, disc
). All paths are absolute and normalized, and they should match the assets loaded by StepMania in virtually all cases.
DisplayBPM ergonomics
The DisplayBPM classes now all expose the same four properties; the ones that don’t apply to a particular class return None. This enables you to handle all three cases without having to import the types for isinstance
checks. Refer to "Getting the displayed BPM" for more details.
msdparser upgrade
simfile's dependency on msdparser has been upgraded to v2.0.0, a major release that corrects how backslashes \
and colons :
are parsed. These fixes apply to both reading and writing simfiles. From the perspective of client code, all you need to know is that the values you get are always unescaped: #TITLE:rE\:Voltagers;
parses to a title
property with value rE:Voltagers
, and serializing it back to MSD adds the necessary \
before the colon in the title automatically.
Additionally, msdparser v2.0.0 parses large documents up to 10 times faster than before!
Note: if you get dependency errors when trying to upgrade simfile, try removing msdparser as an explicit dependency to allow it to cross the major version.
Miscellaneous bugfixes
- The
charts
property on simfiles is now writable, meaning the list of charts can be overwritten directly (not just added to / removed from).
- sm_to_ssc() no longer produces invalid output when there are negative BPMs or stops in the timing data. (It throws
NotImplementedError
as a temporary stopgap. In the future, negative timing data will be converted to warps, as StepMania does automatically.)
- Various type annotations have been improved throughout the library. In particular,
Iterator
input arguments have been replaced with Iterable
so that you don’t need to wrap them in iter(...)
to suppress type errors from static analyzers.
Documentation
In addition to tutorials & API docs, now you can learn by example! This page provides recipes & full scripts that demonstrate correct & type-checked library usage.
Many other pages & API docstrings were updated for clarity & precision.
Questions?
Use the Discussions tab in this repository if you have questions about how to use the simfile library or ideas for new features! Holding these discussions on the open web fosters communication across a broader community of open-source enthusiasts than Discord can offer.
Credits
Thanks to @bkirz, @telperion, and an anonymous contributor for their input on the API additions & enhancements for this release.
This project wouldn't exist were it not for the free creative work of hundreds of stepartists & open-source software enthusiasts. Thank you very much for your best dance!
Source code(tar.gz)
Source code(zip)