diff --git a/binaries/data/config/dev.cfg b/binaries/data/config/dev.cfg new file mode 100644 index 0000000000..ef3e5be923 --- /dev/null +++ b/binaries/data/config/dev.cfg @@ -0,0 +1,11 @@ +; +; Developer configuration file +; +; NOTE: This is not read by the config system currently. +; +; This file is used by the engine to decide which mods are mounted, and in what +; order. The precense of this file suppresses loading of mods in the user mod +; path (see http://trac.wildfiregames.com/wiki/GameDataPaths) if the same mod is +; present in binaries/data/mods. It also implies -noUserMod. +; This is done to make saved maps end up in the right mod folder (and in the +; game-relative data path) to commit them later on. diff --git a/build/workspaces/build-osx-bundle.sh b/build/workspaces/build-osx-bundle.sh index 3f861d5cb0..ae09be7aee 100755 --- a/build/workspaces/build-osx-bundle.sh +++ b/build/workspaces/build-osx-bundle.sh @@ -158,9 +158,13 @@ cp -v ../../binaries/system/libCollada.dylib ${BUNDLE_FRAMEWORKS} # Copy data echo "\nCopying non-archived game data\n" +# Removing it now and restoring it later, cp has no exclusion switch +# and using find is a bit over-the-top +rm ../../binaries/data/config/dev.cfg cp -v ../resources/0ad.icns ${BUNDLE_RESOURCES} cp -R -v ../../binaries/data/config ${BUNDLE_RESOURCES}/data/ cp -R -v ../../binaries/data/tools ${BUNDLE_RESOURCES}/data/ +svn revert ../../binaries/data/config/dev.cfg # Copy license/readmes # TODO: Also want copies in the DMG - decide on layout diff --git a/source/lib/file/vfs/vfs.cpp b/source/lib/file/vfs/vfs.cpp index 8a609f8f20..b0551c7a3e 100644 --- a/source/lib/file/vfs/vfs.cpp +++ b/source/lib/file/vfs/vfs.cpp @@ -132,7 +132,7 @@ public: { ScopedLock s; VfsDirectory* directory; - WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE)); + WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_CREATE_ALWAYS)); const PRealDirectory& realDirectory = directory->AssociatedDirectory(); const OsPath name = pathname.Filename(); diff --git a/source/lib/file/vfs/vfs.h b/source/lib/file/vfs/vfs.h index fcf10037e9..e038c5b48d 100644 --- a/source/lib/file/vfs/vfs.h +++ b/source/lib/file/vfs/vfs.h @@ -65,7 +65,16 @@ enum VfsMountFlags * ".DELETED" suffix will still apply. * (the default behavior is to hide both the suffixed and unsuffixed files) **/ - VFS_MOUNT_KEEP_DELETED = 8 + VFS_MOUNT_KEEP_DELETED = 8, + + /** + * mark a directory replaceable, so that when writing a file to this path + * new real directories will be created instead of reusing already existing + * ones mounted at a subpath of the VFS path. + * (the default behaviour is to write to the real directory associated + * with the VFS directory that was last mounted to this path (or subpath)) + **/ + VFS_MOUNT_REPLACEABLE = 16 }; // (member functions are thread-safe after the instance has been diff --git a/source/lib/file/vfs/vfs_lookup.cpp b/source/lib/file/vfs/vfs_lookup.cpp index fb7ba52f8a..5c99ea73e6 100644 --- a/source/lib/file/vfs/vfs_lookup.cpp +++ b/source/lib/file/vfs/vfs_lookup.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2013 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -74,7 +74,8 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire 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; - ENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|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); directory = startDirectory; if(pfile) @@ -111,7 +112,8 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire return ERR::VFS_DIR_NOT_FOUND; // NOWARN } - if(createMissingDirectories && !subdirectory->AssociatedDirectory()) + if(createMissingDirectories && (!subdirectory->AssociatedDirectory() + || (createAlways && (subdirectory->AssociatedDirectory()->Flags() & VFS_MOUNT_REPLACEABLE) != 0))) { OsPath currentPath; if(directory->AssociatedDirectory()) // (is NULL when mounting into root) diff --git a/source/lib/file/vfs/vfs_lookup.h b/source/lib/file/vfs/vfs_lookup.h index 3a369a378c..733eb599e2 100644 --- a/source/lib/file/vfs/vfs_lookup.h +++ b/source/lib/file/vfs/vfs_lookup.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 Wildfire Games +/* Copyright (c) 2013 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -49,7 +49,12 @@ enum VfsLookupFlags // 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_SKIP_POPULATE = 4, + + // 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 }; /** diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 5744e8ba76..b5e551eca5 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -421,6 +421,20 @@ ErrorReactionInternal psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED( return ERI_NOT_IMPLEMENTED; } +static std::vector GetMods(const CmdLineArgs& args, bool dev) +{ + std::vector mods = args.GetMultiple("mod"); + // TODO: It would be nice to remove this hard-coding + mods.insert(mods.begin(), "public"); + + // 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 (!dev && !args.Has("noUserMod")) + mods.push_back("user"); + + return mods; +} + static void InitVfs(const CmdLineArgs& args) { TIMER(L"InitVfs"); @@ -441,28 +455,44 @@ static void InitVfs(const CmdLineArgs& args) const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); + + // Work out whether we are a dev version to make sure saved files + // (maps, etc) end up in version control. + const OsPath readonlyConfig = paths.RData()/"config"/""; + g_VFS->Mount(L"config/", readonlyConfig); + bool dev = (g_VFS->GetFileInfo(L"config/dev.cfg", NULL) == INFO::OK); - std::vector mods = args.GetMultiple("mod"); - mods.insert(mods.begin(), "public"); - - if (!args.Has("noUserMod")) - mods.push_back("user"); + const std::vector mods = GetMods(args, dev); OsPath modPath = paths.RData()/"mods"; OsPath modUserPath = paths.UserData()/"mods"; for (size_t i = 0; i < mods.size(); ++i) { - size_t priority = i+1; // mods are higher priority than regular mountings, which default to priority 0 - size_t flags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_MUST_EXIST; + size_t priority = (i+1)*2; // mods are higher priority than regular mountings, which default to priority 0 + size_t userFlags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_REPLACEABLE; + size_t flags = userFlags|VFS_MOUNT_MUST_EXIST; + OsPath modName(mods[i]); - g_VFS->Mount(L"", modPath / modName/"", flags, priority); - g_VFS->Mount(L"", modUserPath / modName/"", flags, priority); + if (dev) + { + // We are running a dev copy, so only mount mods in the user mod path + // if the mod does not exist in the data path. + if (DirectoryExists(modPath / modName/"")) + g_VFS->Mount(L"", modPath / modName/"", flags, priority); + else + g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority); + } + else + { + g_VFS->Mount(L"", modPath / modName/"", flags, priority); + // Ensure that user modified files are loaded, if they are present + g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority+1); + } } // We mount these dirs last as otherwise writing could result in files being placed in a mod's dir. g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/""); g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"", VFS_MOUNT_WATCH); - const OsPath readonlyConfig = paths.RData()/"config"/""; // Mounting with highest priority, so that a mod supplied user.cfg is harmless g_VFS->Mount(L"config/", readonlyConfig, 0, (size_t)-1); if(readonlyConfig != paths.Config()) @@ -889,13 +919,13 @@ void Init(const CmdLineArgs& args, int UNUSED(flags)) // g_ConfigDB, command line args, globals CONFIG_Init(args); - + // before scripting if (g_JSDebuggerEnabled) g_DebuggingServer = new CDebuggingServer(); InitScripting(); // before GUI - + g_ConfigDB.RegisterJSConfigDB(); // after scripting // Optionally start profiler HTTP output automatically diff --git a/source/tools/dist/build.sh b/source/tools/dist/build.sh index e9345270c5..8966f35535 100755 --- a/source/tools/dist/build.sh +++ b/source/tools/dist/build.sh @@ -39,7 +39,9 @@ tar cf $PREFIX-unix-build.tar \ --exclude='libraries/source/fcollada/src/FCollada/FColladaTest' \ --exclude='libraries/source/spidermonkey/include-win32' \ ${PREFIX}/{source,build,libraries/source,binaries/system/readme.txt,binaries/data/tests,binaries/data/mods/_test.*,*.txt} -tar cf $PREFIX-unix-data.tar ${PREFIX}/binaries/data/{config,mods/public/public.zip,tools} +tar cf $PREFIX-unix-data.tar \ + --exclude='binaries/data/config/dev.cfg' \ + ${PREFIX}/binaries/data/{config,mods/public/public.zip,tools} # TODO: ought to include generated docs in here, perhaps? # Compress