Index: source/lib/file/vfs/tests/test_vfs_tree.h =================================================================== --- source/lib/file/vfs/tests/test_vfs_tree.h +++ source/lib/file/vfs/tests/test_vfs_tree.h @@ -122,20 +122,20 @@ void check_priority(VfsDirectory& root, const VfsPath& path, size_t priority) { VfsDirectory* dir; VfsFile* file; - TS_ASSERT_OK(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE)); + TS_ASSERT_OK(vfs_Lookup(path, &root, dir, &file, 0)); TS_ASSERT_EQUALS(file->Priority(), priority); } void file_does_not_exists(VfsDirectory& root, const VfsPath& path) { VfsDirectory* dir; VfsFile* file; - TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, &file, VFS_LOOKUP_SKIP_POPULATE), ERR::VFS_FILE_NOT_FOUND); + TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, &file, 0), ERR::VFS_FILE_NOT_FOUND); } void directory_exists(VfsDirectory& root, const VfsPath& path, Status error = INFO::OK) { VfsDirectory* dir; - TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, nullptr, VFS_LOOKUP_SKIP_POPULATE), error); + TS_ASSERT_EQUALS(vfs_Lookup(path, &root, dir, nullptr, 0), error); } public: Index: source/lib/file/vfs/vfs.cpp =================================================================== --- source/lib/file/vfs/vfs.cpp +++ source/lib/file/vfs/vfs.cpp @@ -69,10 +69,13 @@ } VfsDirectory* directory; - WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE)); + WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(mountPoint, &m_rootDirectory, directory, 0, 0)); PRealDirectory realDirectory(new RealDirectory(path, priority, flags)); RETURN_STATUS_IF_ERR(vfs_Attach(directory, realDirectory)); + + RETURN_STATUS_IF_ERR(vfs_Populate(&m_rootDirectory)); + return INFO::OK; } @@ -134,7 +137,7 @@ ScopedLock s; VfsDirectory* directory; Status st; - st = vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS); + st = vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS); if (st == ERR::FILE_ACCESS) return ERR::FILE_ACCESS; @@ -157,7 +160,7 @@ VfsDirectory* directory; VfsFile* file; Status st; - st = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE); + st = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_CREATE); // There is no such file, create it. if (st == ERR::VFS_FILE_NOT_FOUND) @@ -185,7 +188,7 @@ // per 2010-05-01 meeting, this shouldn't raise 'scary error // dialogs', which might fail to display the culprit pathname // instead, callers should log the error, including pathname. - RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file)); + RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file, 0)); fileContents = DummySharedPtr((u8*)0); size = file->Size(); Index: source/lib/file/vfs/vfs_lookup.h =================================================================== --- source/lib/file/vfs/vfs_lookup.h +++ source/lib/file/vfs/vfs_lookup.h @@ -37,24 +37,15 @@ enum VfsLookupFlags { - // add (if they do not already exist) subdirectory components - // encountered in the path[name]. - VFS_LOOKUP_ADD = 1, - // if VFS directories encountered are not already associated // with a real directory, do so (creating the directories // if they do not already exist). - VFS_LOOKUP_CREATE = 2, - - // don't populate the directories encountered. this makes sense - // when adding files from an archive, which would otherwise - // cause nearly every directory to be populated. - VFS_LOOKUP_SKIP_POPULATE = 4, + VFS_LOOKUP_CREATE = 1, // even create directories if they are already present, this is // useful to write new files to the directory that was attached // last, if the directory wasn't mounted with VFS_MOUNT_REPLACEABLE - VFS_LOOKUP_CREATE_ALWAYS = 8 + VFS_LOOKUP_CREATE_ALWAYS = 2 }; /** Index: source/lib/file/vfs/vfs_lookup.cpp =================================================================== --- source/lib/file/vfs/vfs_lookup.cpp +++ source/lib/file/vfs/vfs_lookup.cpp @@ -75,19 +75,14 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDirectory*& directory, VfsFile** pfile, size_t flags) { // extract and validate flags (ensure no unknown bits are set) - const bool addMissingDirectories = (flags & VFS_LOOKUP_ADD) != 0; const bool createMissingDirectories = (flags & VFS_LOOKUP_CREATE) != 0; - const bool skipPopulate = (flags & VFS_LOOKUP_SKIP_POPULATE) != 0; const bool createAlways = (flags & VFS_LOOKUP_CREATE_ALWAYS) != 0; - ENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_SKIP_POPULATE|VFS_LOOKUP_CREATE_ALWAYS)) == 0); + ENSURE((flags & ~(VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS)) == 0); directory = startDirectory; if(pfile) *pfile = 0; - if(!skipPopulate) - RETURN_STATUS_IF_ERR(vfs_Populate(directory)); - // early-out for pathname == "" when mounting into VFS root if(pathname.empty()) // (prevent iterator error in loop end condition) { @@ -109,12 +104,7 @@ VfsDirectory* subdirectory = directory->GetSubdirectory(subdirectoryName); if(!subdirectory) - { - if(addMissingDirectories) - subdirectory = directory->AddSubdirectory(subdirectoryName); - else - return ERR::VFS_DIR_NOT_FOUND; // NOWARN - } + subdirectory = directory->AddSubdirectory(subdirectoryName); if(createMissingDirectories && (!subdirectory->AssociatedDirectory() || (createAlways && (subdirectory->AssociatedDirectory()->Flags() & VFS_MOUNT_REPLACEABLE) != 0))) @@ -129,10 +119,7 @@ PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0)); RETURN_STATUS_IF_ERR(vfs_Attach(subdirectory, realDirectory)); } - - if(!skipPopulate) - RETURN_STATUS_IF_ERR(vfs_Populate(subdirectory)); - + RETURN_STATUS_IF_ERR(vfs_Populate(subdirectory)); directory = subdirectory; } Index: source/lib/file/vfs/vfs_populate.cpp =================================================================== --- source/lib/file/vfs/vfs_populate.cpp +++ source/lib/file/vfs/vfs_populate.cpp @@ -90,11 +90,8 @@ { PopulateHelper* this_ = (PopulateHelper*)cbData; - // (we have to create missing subdirectoryNames because archivers - // don't always place directory entries before their files) - const size_t flags = VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE; VfsDirectory* directory; - WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, flags)); + WARN_IF_ERR(vfs_Lookup(pathname, this_->m_directory, directory, 0, 0)); const VfsPath name = fileInfo.Name(); const VfsFile file(name, (size_t)fileInfo.Size(), fileInfo.MTime(), this_->m_realDirectory->Priority(), archiveFile); Index: source/ps/GameSetup/GameSetup.cpp =================================================================== --- source/ps/GameSetup/GameSetup.cpp +++ source/ps/GameSetup/GameSetup.cpp @@ -361,25 +361,25 @@ if (!init_mods) { // Add the user mod if it should be present - if (add_user && (g_modsLoaded.empty() || g_modsLoaded.back() != "user")) - g_modsLoaded.push_back("user"); + if (add_user && (g_modsToLoad.empty() || g_modsToLoad.back() != "user")) + g_modsToLoad.push_back("user"); - return g_modsLoaded; + return g_modsToLoad; } - g_modsLoaded = args.GetMultiple("mod"); + g_modsToLoad = args.GetMultiple("mod"); if (add_public) - g_modsLoaded.insert(g_modsLoaded.begin(), "public"); + g_modsToLoad.insert(g_modsToLoad.begin(), "public"); - g_modsLoaded.insert(g_modsLoaded.begin(), "mod"); + g_modsToLoad.insert(g_modsToLoad.begin(), "mod"); // Add the user mod if not explicitly disabled or we have a dev copy so // that saved files end up in version control and not in the user mod. if (add_user) - g_modsLoaded.push_back("user"); + g_modsToLoad.push_back("user"); - return g_modsLoaded; + return g_modsToLoad; } void MountMods(const Paths& paths, const std::vector& mods) @@ -408,6 +408,20 @@ // Ensure that user modified files are loaded, if they are present g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority+1); } + + CVFSFile modinfo; + if (modinfo.Load(g_VFS, "mod.json", false) != PSRETURN_OK) + continue; + + // HERE IS WHERE WE WOULD PARSE JSON +/* JS::RootedValue json(cx); + if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json)) + continue; +*/ + CStr version; +// scriptInterface.GetProperty(json, "version", version); + + g_modsMounted[modName.string8()] = version; } } @@ -955,7 +969,7 @@ { std::vector mods; boost::split(mods, modstring, boost::is_any_of(" "), boost::token_compress_on); - std::swap(g_modsLoaded, mods); + std::swap(g_modsToLoad, mods); // Abort init and restart RestartEngine(); Index: source/ps/Mod.h =================================================================== --- source/ps/Mod.h +++ source/ps/Mod.h @@ -22,13 +22,12 @@ #include "ps/GameSetup/CmdLineArgs.h" #include "scriptinterface/ScriptInterface.h" -extern std::vector g_modsLoaded; +extern std::vector g_modsToLoad; +extern std::map g_modsMounted; extern CmdLineArgs g_args; namespace Mod { - JS::Value GetAvailableMods(const ScriptInterface& scriptInterface); - /** * Get the loaded mods and their version. * "user" mod and "mod" mod are ignored as they are irrelevant for compatibility checks. Index: source/ps/Mod.cpp =================================================================== --- source/ps/Mod.cpp +++ source/ps/Mod.cpp @@ -29,99 +29,26 @@ #include "ps/GameSetup/Paths.h" #include "scriptinterface/ScriptInterface.h" -std::vector g_modsLoaded; +std::vector g_modsToLoad; +std::map g_modsMounted; CmdLineArgs g_args; -JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface) -{ - JSContext* cx = scriptInterface.GetContext(); - JSAutoRequest rq(cx); - JS::RootedObject obj(cx, JS_NewPlainObject(cx)); - - const Paths paths(g_args); - - // loop over all possible paths - OsPath modPath = paths.RData()/"mods"; - OsPath modUserPath = paths.UserData()/"mods"; - - DirectoryNames modDirs; - DirectoryNames modDirsUser; - - GetDirectoryEntries(modPath, NULL, &modDirs); - // Sort modDirs so that we can do a fast lookup below - std::sort(modDirs.begin(), modDirs.end()); - - PIVFS vfs = CreateVfs(); - - for (DirectoryNames::iterator iter = modDirs.begin(); iter != modDirs.end(); ++iter) - { - vfs->Clear(); - if (vfs->Mount(L"", modPath / *iter, VFS_MOUNT_MUST_EXIST) < 0) - continue; - - CVFSFile modinfo; - if (modinfo.Load(vfs, L"mod.json", false) != PSRETURN_OK) - continue; - - JS::RootedValue json(cx); - if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json)) - continue; - - // Valid mod, add it to our structure - JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json); - } - - GetDirectoryEntries(modUserPath, NULL, &modDirsUser); - bool dev = InDevelopmentCopy(); - - for (DirectoryNames::iterator iter = modDirsUser.begin(); iter != modDirsUser.end(); ++iter) - { - // If we are in a dev copy we do not mount mods in the user mod folder that - // are already present in the mod folder, thus we skip those here. - if (dev && std::binary_search(modDirs.begin(), modDirs.end(), *iter)) - continue; - - vfs->Clear(); - if (vfs->Mount(L"", modUserPath / *iter, VFS_MOUNT_MUST_EXIST) < 0) - continue; - - CVFSFile modinfo; - if (modinfo.Load(vfs, L"mod.json", false) != PSRETURN_OK) - continue; - - JS::RootedValue json(cx); - if (!scriptInterface.ParseJSON(modinfo.GetAsString(), &json)) - continue; - - // Valid mod, add it to our structure - JS_SetProperty(cx, obj, utf8_from_wstring(iter->string()).c_str(), json); - } - - return JS::ObjectValue(*obj); -} - JS::Value Mod::GetLoadedModsWithVersions(const ScriptInterface& scriptInterface) { JSContext* cx = scriptInterface.GetContext(); JSAutoRequest rq(cx); - JS::RootedValue availableMods(cx, GetAvailableMods(scriptInterface)); - JS::RootedValue ret(cx, JS::ObjectValue(*JS_NewArrayObject(cx, 0))); // Index of the created array size_t j = 0; - for (size_t i = 0; i < g_modsLoaded.size(); ++i) + for (const std::pair& modsMounted : g_modsMounted) { - // Ignore user and mod mod as they are irrelevant for compatibility checks - if (g_modsLoaded[i] == "mod" || g_modsLoaded[i] == "user") + if (modsMounted.first == "mod" || modsMounted.second == "user") continue; - CStr version; JS::RootedValue modData(cx); - if (scriptInterface.GetProperty(availableMods, g_modsLoaded[i].c_str(), &modData)) - scriptInterface.GetProperty(modData, "version", version); - scriptInterface.SetPropertyInt(ret, j++, std::vector{g_modsLoaded[i], version}); + scriptInterface.SetPropertyInt(ret, j++, std::vector{modsMounted.first, modsMounted.second}); } return ret; } Index: source/ps/scripting/JSInterface_Mod.h =================================================================== --- source/ps/scripting/JSInterface_Mod.h +++ source/ps/scripting/JSInterface_Mod.h @@ -26,7 +26,6 @@ void RegisterScriptFunctions(const ScriptInterface& scriptInterface); JS::Value GetEngineInfo(ScriptInterface::CxPrivate* pCxPrivate); - JS::Value GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate); void RestartEngine(ScriptInterface::CxPrivate* pCxPrivate); void SetMods(ScriptInterface::CxPrivate* pCxPrivate, const std::vector& mods); } Index: source/ps/scripting/JSInterface_Mod.cpp =================================================================== --- source/ps/scripting/JSInterface_Mod.cpp +++ source/ps/scripting/JSInterface_Mod.cpp @@ -29,21 +29,6 @@ return Mod::GetEngineInfo(*(pCxPrivate->pScriptInterface)); } -/** - * Returns a JS object containing a listing of available mods that - * have a modname.json file in their modname folder. The returned - * object looks like { modname1: json1, modname2: json2, ... } where - * jsonN is the content of the modnameN/modnameN.json file as a JS - * object. - * - * @return JS object with available mods as the keys of the modname.json - * properties. - */ -JS::Value JSI_Mod::GetAvailableMods(ScriptInterface::CxPrivate* pCxPrivate) -{ - return Mod::GetAvailableMods(*(pCxPrivate->pScriptInterface)); -} - void JSI_Mod::RestartEngine(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) { ::RestartEngine(); @@ -51,13 +36,12 @@ void JSI_Mod::SetMods(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::vector& mods) { - g_modsLoaded = mods; + g_modsToLoad = mods; } void JSI_Mod::RegisterScriptFunctions(const ScriptInterface& scriptInterface) { scriptInterface.RegisterFunction("GetEngineInfo"); - scriptInterface.RegisterFunction("GetAvailableMods"); scriptInterface.RegisterFunction("RestartEngine"); scriptInterface.RegisterFunction, &JSI_Mod::SetMods>("SetMods"); }