musescore / musescore/MuseScore
For files created in development versions, the content does not always match the file format version number
@cbjeukendrup is already working on this.
Since Jan 9, 2024.
- Dominant language
- C++
- Stars
- 15.1k
- Forks
- 3.3k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 91
Description
This issue describes rather a problem for developers and testers than a problem for users. The fact that we are having this problem with our current policy is obvious; the reason that I'm still logging an issue for it, is to discuss if and how we should improve this.
The problem
Our current policy looks something like this:
- While building new features for 4.1, we change the file format, but don't update the File Format Version Number (FFVN) yet, because there might be more changes upcoming.
- A helpful user (or one of us) tests the latest nightly build, and creates a score in it.
- We make more changes to the file format.
- Finally, we update the FFVN.
The score created in step 2 is now in an inconsistent state. Its FFVN is still 400, but the contents of this file will be a mix of (at worst) 3 different versions:
- 410, because it was created after step 1;
- 400, because it was created before step 3;
- no existing version at all but something between 400 and 410, because some of the changes in step 3 might be revisions of the changes in step 1, rather than independent changes.
Later, when the user opens this score in the released version of 4.1, things don't work properly. The score is detected as corrupted, or causes crashes, or other problems occur...
We spend a lot of time trying to find the causes, and eventually we finally realise that this score is from an unreleased version (because luckily the commit of the MuseScore version is also written to the file). We conclude that we can't do anything and don't need to do anything, but we have wasted a lot of time figuring that out.
How common is this and how problematic is it for users?
Not very, of course, since users are advised to use nightly builds with care and not for real work. But for 4.0, this has happened several times, which caused us (and potentially others) to waste quite some time on useless debugging.
Proposed solution
I propose that we simply increase the FFVN in every pull request that changes any aspect of the file format.
This way, we rule out the possibility of a file being somewhere in-between between two versions.
Problems to overcome
Given our current ideas about the reading code (i.e. creating a full isolated version of the reading code for every existing FFVN), the proposed solution would give two problems (or, better said, it magnifies these problems rather than introducing them):
- a lot of work: duplicating the whole reading code every time we change something, even for a simple typo fix, is a lot of tedious work.
- a lot of duplication: that doesn't need any explanation, I guess. (Keep in mind that while we could decide that we will never provide bug fixes for reading files from old versions, we will still need to maintain all versions of the reading code every time when making changes to Libmscore's data structures.)
Possible solutions to these problems
- Drop the idea of making the reading code for each version completely isolated
Instead, allow different versions to share code. That way, there is no duplication anymore and not a lot of extra work. Only for things that are actually different, there will be different code paths. Reg. how to choose which code path to take: I don't have strong opinions on whether that should be done using virtual methods, if-statements, or something else. - Allow a reader version to work for a range of version numbers, rather than exactly one version number.
This might be seen as "worst of both worlds" though. It will mean that there's still some duplication, and still if-statements that make things complicated. - Explicitly drop support for those never-released file format versions
For example, if the current version number is 408, and we make more changes so the number becomes 409, we drop support for 408 scores (which makes testing development builds slightly less convenient, because the only thing you can do with scores from an old development version is throwing them away, but that might be understandable for the user).
Additional thought
We could create named constants for file version numbers. For example:
static constexpr int FFV_NEW_ORNAMENTS = 408;
static constexpr int FFV_NEW_EXPRESSIONS = 409;
static constexpr int FFV_NEW_ORNAMENTS_SOME_EXTRA_FIX = 410;
...
// When reading expressions:
if (ctx.fileFormatVersion < FFV_NEW_EXPRESSIONS) {
// read old expression
} else {
// read new expression
}
...
// When reading ornaments:
if (ctx.fileFormatVersion < FFV_NEW_ORNAMENTS) {
// read old ornament
} else if (ctx.fileFormatVersion < FFV_NEW_ORNAMENTS_SOME_EXTRA_FIX) {
// read ornament from some intermediate version
} else {
// read ornament from latest version
}
(This example does not necessarily correspond to the reality, but you get the idea)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.