fix: layer GUID-primary project identity so reopened/new projects reload the bank
classifyProjectTransition now checks the stored GUID first, then the pointer, fixing the w10 regression where a recycled ReaProject* address stopped the bank reloading. poll()'s fork re-GUID gate is bound to !sameProjectObject. Full transition matrix pinned in tests.
This commit is contained in:
+33
-16
@@ -110,26 +110,43 @@ ProjectTransition classifyProjectTransition(bool sameProjectObject,
|
||||
const std::string& lastPath,
|
||||
const std::string& currentGuid,
|
||||
const std::string& currentPath) {
|
||||
// A DIFFERENT project object is a tab-switch / open / recycled pointer — load
|
||||
// ITS index; NEVER relocate a bank. This is the load-bearing safety fix: it
|
||||
// holds even when currentGuid == lastGuid, which is exactly the forked-sibling
|
||||
// case (Save-As copied our GUID, so two distinct projects share it on disk).
|
||||
// The GUID is deliberately NOT consulted here — the object identity alone
|
||||
// decides, and it cannot be fooled by a copied GUID.
|
||||
(void)lastGuid;
|
||||
(void)currentGuid;
|
||||
// 1. The GUID is the identity of record and is checked FIRST. A different
|
||||
// stored GUID means a genuinely different project is active — Load ITS index.
|
||||
// This catches the regression that pointer-primary classification missed:
|
||||
// REAPER RECYCLES ReaProject* addresses across close/open, so a reopened /
|
||||
// new project can reuse the previous project's address (sameProjectObject ==
|
||||
// true) while carrying a different stored GUID. Deciding on the pointer alone
|
||||
// then returned NoOp/SaveAsRelocate and the bank never reloaded. The GUID is
|
||||
// immune to address recycling, so it leads. Also covers new/unsaved<->saved
|
||||
// transitions (one GUID empty, the other not) and switching between two
|
||||
// distinct saved projects.
|
||||
if (currentGuid != lastGuid) {
|
||||
return ProjectTransition::Load;
|
||||
}
|
||||
|
||||
// From here currentGuid == lastGuid (they are equal; both may be empty for
|
||||
// unsaved projects). The pointer now disambiguates the same-GUID case.
|
||||
|
||||
// 2. Same GUID but a DIFFERENT object is a forked sibling: Save-As copied our
|
||||
// GUID onto a distinct project object. Load its (own) index; never relocate.
|
||||
// Two unsaved projects (both GUIDs empty, distinct objects) also land here —
|
||||
// Load, so switching between them installs the right in-memory state.
|
||||
if (!sameProjectObject) {
|
||||
return ProjectTransition::Load;
|
||||
}
|
||||
|
||||
// Same object from here on: identity is PROVEN by the pointer. A path change is
|
||||
// a Save-As (or a first save, when the old path was empty); an unchanged path
|
||||
// is Save-in-place / idle. Note SaveAsRelocate is safe even for a first save:
|
||||
// the old project dir is empty, so deriveRelocationPlan makes `needed` false
|
||||
// and nothing is physically relocated (empty-GUID safety preserved), while
|
||||
// poll() still mints a GUID on that branch.
|
||||
return (currentPath == lastPath) ? ProjectTransition::NoOp
|
||||
: ProjectTransition::SaveAsRelocate;
|
||||
// 3. Same object AND same GUID with a NEW path is a genuine Save-As (the object
|
||||
// identity is proven and the record identity is unchanged — only the .rpp
|
||||
// moved). Also the first save of an unsaved project (both GUIDs empty, old
|
||||
// path empty): SaveAsRelocate is safe there because deriveRelocationPlan
|
||||
// no-ops on the empty old dir (empty-GUID safety preserved) while poll()
|
||||
// mints a GUID.
|
||||
if (currentPath != lastPath) {
|
||||
return ProjectTransition::SaveAsRelocate;
|
||||
}
|
||||
|
||||
// 4. Same object, same GUID, same path — Save in place / idle tick.
|
||||
return ProjectTransition::NoOp;
|
||||
}
|
||||
|
||||
} // namespace reasampler
|
||||
|
||||
Reference in New Issue
Block a user