Same checked set as CheckedModIds, but each @modId prefixed with its source's root to form the absolute, semicolon-separated mod path RequestRemountWithMods wants ("" = base game). Local mods mount from localRoot, downloaded (workshop) mods from workshopRoot — so a mod's source is preserved by its on-disk location.
| 646 | const std::string local = localRoot ? localRoot : ""; |
| 647 | const std::string workshop = workshopRoot ? workshopRoot : ""; |
| 648 | std::string out; |
| 649 | for (int i = 0; i < _modRows.Size(); i++) |
| 650 | { |
| 651 | // A ticked mod mounts once it is on disk. Workshop (remote) mods not yet |
| 652 | // downloaded are still Missing and are skipped here (Apply opens the |
| 653 | // download dialog for them first); once downloaded + unpacked their state |
| 654 | // flips off Missing and they mount from the workshop root. |
| 655 | if (!_modRows[i].checked || _modRows[i].state == ModRowState::Missing) |
| 656 | continue; |
| 657 | const std::string& root = (_modRows[i].source == ModRowSource::Workshop) ? workshop : local; |
| 658 | if (!out.empty()) |
| 659 | out += ';'; |
| 660 | out += root; |
| 661 | // Insert a separator if the root didn't already end with one (GamePaths dirs |
| 662 | // end with '/', but an absolute --mods-dir / --workshop-dir does not). |
| 663 | if (!root.empty() && root.back() != '/' && root.back() != '\\') |
| 664 | out += '/'; |
| 665 | const char* folder = _modRows[i].folderName.GetLength() > 0 ? (const char*)_modRows[i].folderName |
| 666 | : (const char*)_modRows[i].modId; |
| 667 | out += folder; |
| 668 | } |
| 669 | return RString(out.c_str()); |
| 670 | } |
| 671 | |
| 672 | // Case-insensitive substring test for the name filter; empty needle matches anything. |
| 673 | bool CModsList::ContainsNoCase(const char* hay, const char* needle) |
| 674 | { |
| 675 | if (needle == nullptr || needle[0] == 0) |
| 676 | return true; |
| 677 | if (hay == nullptr) |
no test coverage detected