mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
Make real directory paths on the VFS predictable (retry)
This is the same commit as4bb31f084e, reverted in5d1899785a. Noted issues were fixed. Partial revert/fix of2f19cf86d3and2567fee329. Before this diff, it was possible for a mod containing a cache/ or a config/ folder to get written to incorrectly. The issue is VFS can map multiple directories to one 'virtual' path, for reading mods. However, writing data is problematic: which path to choose? The only viable solution is to use a path relative to the highest priority directory encountered in the VFS path, or write paths could be 'hijacked' by lower-priority mods. This fixes these issues by: - Adding a new lookup mode ('Real-path') that explicitly picks the real path relative to the highest-priority subdirectory in the VFS Path. - Preventing overwriting a real directory with a lower priority one in general. - Revert c0c8132dd4's GetRealPath change, re-introducing the function as GetOriginalPath. This also cleans up some duplication that led to empty mod folders in the user mod path, and cleans up loading the 'user' mod. It also makes it explicit that a directory must be passed to Mount(). Note that the new 'realpath' lookup can still be somewhat complex with many mount points at various hierarchy levels, but it is at least predictable/deterministic without having to be careful about populating order. Fixes #2553 Differential Revision: https://code.wildfiregames.com/D3728 This was SVN commit r25107.
This commit is contained in:
@@ -48,7 +48,6 @@ RL client:
|
|||||||
Configuration:
|
Configuration:
|
||||||
-conf=KEY:VALUE set a config value
|
-conf=KEY:VALUE set a config value
|
||||||
-nosound disable audio
|
-nosound disable audio
|
||||||
-noUserMod disable loading of the user mod
|
|
||||||
-shadows enable shadows
|
-shadows enable shadows
|
||||||
-vsync enable VSync, i.e. lock FPS to monitor refresh rate
|
-vsync enable VSync, i.e. lock FPS to monitor refresh rate
|
||||||
-xres=N set screen X resolution to 'N'
|
-xres=N set screen X resolution to 'N'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -25,8 +25,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
g_VFS->Mount(L"", DataDir() / "mods" / "mod", VFS_MOUNT_MUST_EXIST);
|
g_VFS->Mount(L"", DataDir() / "mods" / "mod" / "", VFS_MOUNT_MUST_EXIST);
|
||||||
g_VFS->Mount(L"", DataDir() / "mods" / "public", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2017 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -29,8 +29,8 @@
|
|||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#include "ps/XML/RelaxNG.h"
|
#include "ps/XML/RelaxNG.h"
|
||||||
|
|
||||||
static OsPath MOD_PATH(DataDir()/"mods"/"_test.mesh");
|
static OsPath MOD_PATH(DataDir() / "mods" / "_test.mesh" / "");
|
||||||
static OsPath CACHE_PATH(DataDir()/"_testcache");
|
static OsPath CACHE_PATH(DataDir() / "_testcache" / "");
|
||||||
|
|
||||||
const OsPath srcDAE(L"collada/sphere.dae");
|
const OsPath srcDAE(L"collada/sphere.dae");
|
||||||
const OsPath srcPMD(L"collada/sphere.pmd");
|
const OsPath srcPMD(L"collada/sphere.pmd");
|
||||||
@@ -61,12 +61,8 @@ class TestMeshManager : public CxxTest::TestSuite
|
|||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
|
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", MOD_PATH));
|
TS_ASSERT_OK(g_VFS->Mount(L"", MOD_PATH));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"collada/", DataDir()/"tests"/"collada", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"collada/", DataDir() / "tests" / "collada" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
|
TS_ASSERT_OK(g_VFS->Mount(L"cache/", CACHE_PATH, 0, VFS_MAX_PRIORITY));
|
||||||
// Mount _testcache onto virtual /cache - don't use the normal cache
|
|
||||||
// directory because that's full of loads of cached files from the
|
|
||||||
// proper game and takes a long time to load.
|
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache/", CACHE_PATH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinitVfs()
|
void deinitVfs()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2017 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -35,8 +35,8 @@ public:
|
|||||||
DeleteDirectory(DataDir()/"_testcache"); // clean up in case the last test run failed
|
DeleteDirectory(DataDir()/"_testcache"); // clean up in case the last test run failed
|
||||||
|
|
||||||
m_VFS = CreateVfs();
|
m_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(m_VFS->Mount(L"", DataDir()/"mods"/"_test.tex", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(m_VFS->Mount(L"", DataDir() / "mods" / "_test.tex" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(m_VFS->Mount(L"cache/", DataDir()/"_testcache"));
|
TS_ASSERT_OK(m_VFS->Mount(L"cache/", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tearDown()
|
void tearDown()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2017 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -36,8 +36,8 @@ public:
|
|||||||
DeleteDirectory(DataDir()/"_testcache"); // clean up in case the last test run failed
|
DeleteDirectory(DataDir()/"_testcache"); // clean up in case the last test run failed
|
||||||
|
|
||||||
m_VFS = CreateVfs();
|
m_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(m_VFS->Mount(L"", DataDir()/"mods"/"_test.tex", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(m_VFS->Mount(L"", DataDir() / "mods" / "_test.tex" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(m_VFS->Mount(L"cache/", DataDir()/"_testcache"));
|
TS_ASSERT_OK(m_VFS->Mount(L"cache/", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
|
|
||||||
h_mgr_init();
|
h_mgr_init();
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.gui", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.gui" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
|
|
||||||
configDB = new CConfigDB;
|
configDB = new CConfigDB;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2010 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
RealDirectory::RealDirectory(const OsPath& path, size_t priority, size_t flags)
|
RealDirectory::RealDirectory(const OsPath& path, size_t priority, size_t flags)
|
||||||
: m_path(path), m_priority(priority), m_flags(flags)
|
: m_path(path), m_priority(priority), m_flags(flags)
|
||||||
{
|
{
|
||||||
|
ENSURE(path.IsDirectory());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/self_test.h"
|
||||||
|
|
||||||
|
#include "lib/file/vfs/vfs_populate.h"
|
||||||
|
#include "lib/os_path.h"
|
||||||
|
|
||||||
|
static OsPath TEST_FOLDER(DataDir()/"_test.temp");
|
||||||
|
|
||||||
|
extern PIVFS g_VFS;
|
||||||
|
|
||||||
|
class TestVfsPopulate : public CxxTest::TestSuite
|
||||||
|
{
|
||||||
|
void initVfs()
|
||||||
|
{
|
||||||
|
g_VFS = CreateVfs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinitVfs()
|
||||||
|
{
|
||||||
|
if(DirectoryExists(TEST_FOLDER))
|
||||||
|
DeleteDirectory(TEST_FOLDER);
|
||||||
|
|
||||||
|
g_VFS.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createRealDir(const OsPath& path)
|
||||||
|
{
|
||||||
|
if(DirectoryExists(path))
|
||||||
|
DeleteDirectory(path);
|
||||||
|
CreateDirectories(path, 0700, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setUp()
|
||||||
|
{
|
||||||
|
initVfs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown()
|
||||||
|
{
|
||||||
|
deinitVfs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a regression test. Before priorities were used for real directories, things were... Rather undefined.
|
||||||
|
* The new spec is that the path is relative to the highest priority subdirectory in the path.
|
||||||
|
* The order remains undefined in case of equal priority.
|
||||||
|
* (see below for tests on that).
|
||||||
|
*/
|
||||||
|
void test_write_path_hijacking()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "cache" / "some_folder");
|
||||||
|
createRealDir(TEST_FOLDER / "some_mod" / "cache" / "some_mod");
|
||||||
|
|
||||||
|
shared_ptr<u8> buf(new u8(1));
|
||||||
|
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "some_mod" / "", 0, 0);
|
||||||
|
|
||||||
|
// Access the subfolder, creating subdirectories in the VFS.
|
||||||
|
g_VFS->CreateFile(L"cache/some_mod/peek.txt", buf, 0);
|
||||||
|
|
||||||
|
g_VFS->Mount(L"cache/", TEST_FOLDER / "cache" / "", 0, 1);
|
||||||
|
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->GetDirectoryRealPath(L"cache/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "cache" / "");
|
||||||
|
g_VFS->CreateFile(L"cache/test.txt", buf, 0);
|
||||||
|
g_VFS->GetRealPath(L"cache/test.txt", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "cache" / "test.txt");
|
||||||
|
|
||||||
|
g_VFS->GetDirectoryRealPath(L"cache/some_mod/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "cache" / "some_mod" / "");
|
||||||
|
g_VFS->CreateFile(L"cache/some_mod/test.txt", buf, 0);
|
||||||
|
g_VFS->GetRealPath(L"cache/some_mod/test.txt", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "cache" / "some_mod" / "test.txt");
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_priority()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "mod_a");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_b");
|
||||||
|
|
||||||
|
// Check that the real directory points to the highest priority mod.
|
||||||
|
g_VFS->Mount(L"mod", TEST_FOLDER / "mod_a" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"mod", TEST_FOLDER / "mod_b" / "", 0, 0);
|
||||||
|
|
||||||
|
// For consistency, populate everything.
|
||||||
|
g_VFS->TextRepresentation().c_str();
|
||||||
|
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->GetDirectoryRealPath(L"mod", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "mod_a" / "");
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_real_path_0()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder_a" / "subfolder");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder_b");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_1" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "folder_a");
|
||||||
|
|
||||||
|
g_VFS->Mount(L"folder_a/", TEST_FOLDER / "folder_a" / "", 0, 2);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_1" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_0" / "", 0, 0);
|
||||||
|
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder_a/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "folder_a" / "");
|
||||||
|
// Despite being lower priority, we still load non-conflicting files of mod_0
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder_b/", realPath);
|
||||||
|
// However their real path is rewritten to mod_1.
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "mod_1" / "folder_b" / "");
|
||||||
|
|
||||||
|
// (including sub-subfolders)
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder_a/subfolder/", realPath);
|
||||||
|
// However their real path is rewritten.
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "folder_a" / "subfolder" / "");
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_real_path_1()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_1" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "folder_a");
|
||||||
|
|
||||||
|
// Equal priority, the order is undetermined.
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_0" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"folder_a/", TEST_FOLDER / "folder_a" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_1" / "", 0, 0);
|
||||||
|
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder_a/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "folder_a" / "");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Same as test_real_path_1, but invert the mounting order
|
||||||
|
// (may or may not result in different behaviour).
|
||||||
|
void test_real_path_2()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_1" / "folder_a");
|
||||||
|
createRealDir(TEST_FOLDER / "folder_a");
|
||||||
|
|
||||||
|
// Equal priority, the order is undetermined.
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->Mount(L"folder_a/", TEST_FOLDER / "folder_a" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_1" / "", 0, 0);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_0" / "", 0, 1);
|
||||||
|
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder_a/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "folder_a" / "");
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_real_path_subpath()
|
||||||
|
{
|
||||||
|
createRealDir(TEST_FOLDER / "mod_0" / "folder" / "subfolder");
|
||||||
|
createRealDir(TEST_FOLDER / "mod_1" / "folder" / "subfolder");
|
||||||
|
createRealDir(TEST_FOLDER / "other_folder");
|
||||||
|
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_0" / "", 0, 0);
|
||||||
|
g_VFS->Mount(L"", TEST_FOLDER / "mod_1" / "", 0, 1);
|
||||||
|
g_VFS->Mount(L"folder/subfolder/", TEST_FOLDER / "other_folder" / "", 0, 2);
|
||||||
|
|
||||||
|
OsPath realPath;
|
||||||
|
g_VFS->GetDirectoryRealPath(L"folder/subfolder/", realPath);
|
||||||
|
TS_ASSERT_EQUALS(realPath, TEST_FOLDER / "other_folder" / "");
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2019 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
#include "lib/file/file_system.h"
|
#include "lib/file/file_system.h"
|
||||||
#include "lib/os_path.h"
|
#include "lib/os_path.h"
|
||||||
|
|
||||||
static OsPath MOD_PATH(DataDir()/"mods"/"_test.vfs");
|
static OsPath MOD_PATH(DataDir() / "mods" / "_test.vfs" / "");
|
||||||
static OsPath CACHE_PATH(DataDir()/"_testcache");
|
static OsPath CACHE_PATH(DataDir() / "_testcache" / "");
|
||||||
|
|
||||||
extern PIVFS g_VFS;
|
extern PIVFS g_VFS;
|
||||||
|
|
||||||
@@ -47,11 +47,7 @@ class TestVfsUtil : public CxxTest::TestSuite
|
|||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
|
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", MOD_PATH));
|
TS_ASSERT_OK(g_VFS->Mount(L"", MOD_PATH));
|
||||||
|
TS_ASSERT_OK(g_VFS->Mount(L"cache/", CACHE_PATH, 0, VFS_MAX_PRIORITY));
|
||||||
// Mount _testcache onto virtual /cache - don't use the normal cache
|
|
||||||
// directory because that's full of loads of cached files from the
|
|
||||||
// proper game and takes a long time to load.
|
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache/", CACHE_PATH));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinitVfs()
|
void deinitVfs()
|
||||||
|
|||||||
+20
-31
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2017 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -54,6 +54,8 @@ public:
|
|||||||
|
|
||||||
virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)
|
virtual Status Mount(const VfsPath& mountPoint, const OsPath& path, size_t flags /* = 0 */, size_t priority /* = 0 */)
|
||||||
{
|
{
|
||||||
|
ENSURE(path.IsDirectory());
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(vfs_mutex);
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
if(!DirectoryExists(path))
|
if(!DirectoryExists(path))
|
||||||
{
|
{
|
||||||
@@ -129,7 +131,7 @@ public:
|
|||||||
std::lock_guard<std::mutex> lock(vfs_mutex);
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
VfsDirectory* directory;
|
VfsDirectory* directory;
|
||||||
Status st;
|
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_ADD|VFS_LOOKUP_REAL_PATH);
|
||||||
if (st == ERR::FILE_ACCESS)
|
if (st == ERR::FILE_ACCESS)
|
||||||
return ERR::FILE_ACCESS;
|
return ERR::FILE_ACCESS;
|
||||||
|
|
||||||
@@ -146,32 +148,6 @@ public:
|
|||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(vfs_mutex);
|
|
||||||
VfsDirectory* directory;
|
|
||||||
VfsFile* file;
|
|
||||||
Status st;
|
|
||||||
st = vfs_Lookup(pathname, &m_rootDirectory, directory, &file, VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE);
|
|
||||||
|
|
||||||
// There is no such file, create it.
|
|
||||||
if (st == ERR::VFS_FILE_NOT_FOUND)
|
|
||||||
{
|
|
||||||
lock.unlock();
|
|
||||||
return CreateFile(pathname, fileContents, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_RETURN_STATUS_IF_ERR(st);
|
|
||||||
|
|
||||||
RealDirectory realDirectory(file->Loader()->Path(), file->Priority(), directory->AssociatedDirectory()->Flags());
|
|
||||||
RETURN_STATUS_IF_ERR(realDirectory.Store(pathname.Filename(), fileContents, size));
|
|
||||||
|
|
||||||
directory->AddFile(*file);
|
|
||||||
|
|
||||||
m_trace->NotifyStore(pathname, size);
|
|
||||||
return INFO::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size)
|
virtual Status LoadFile(const VfsPath& pathname, shared_ptr<u8>& fileContents, size_t& size)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(vfs_mutex);
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
@@ -203,7 +179,7 @@ public:
|
|||||||
return textRepresentation;
|
return textRepresentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname)
|
virtual Status GetOriginalPath(const VfsPath& pathname, OsPath& realPathname)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(vfs_mutex);
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
VfsDirectory* directory; VfsFile* file;
|
VfsDirectory* directory; VfsFile* file;
|
||||||
@@ -212,11 +188,24 @@ public:
|
|||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname)
|
virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
|
VfsDirectory* directory; VfsFile* file;
|
||||||
|
size_t flags = VFS_LOOKUP_REAL_PATH | (createMissingDirectories ? VFS_LOOKUP_ADD : 0);
|
||||||
|
WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, &file, flags));
|
||||||
|
ENSURE(directory->AssociatedDirectory());
|
||||||
|
realPathname = directory->AssociatedDirectory()->Path() / pathname.Filename();
|
||||||
|
return INFO::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(vfs_mutex);
|
std::lock_guard<std::mutex> lock(vfs_mutex);
|
||||||
VfsDirectory* directory;
|
VfsDirectory* directory;
|
||||||
WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, NULL));
|
size_t flags = VFS_LOOKUP_REAL_PATH | (createMissingDirectories ? VFS_LOOKUP_ADD : 0);
|
||||||
|
WARN_RETURN_STATUS_IF_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, NULL, flags));
|
||||||
|
ENSURE(directory->AssociatedDirectory());
|
||||||
realPathname = directory->AssociatedDirectory()->Path();
|
realPathname = directory->AssociatedDirectory()->Path();
|
||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-29
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2017 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -31,6 +31,9 @@
|
|||||||
#include "lib/file/file_system.h" // CFileInfo
|
#include "lib/file/file_system.h" // CFileInfo
|
||||||
#include "lib/file/vfs/vfs_path.h"
|
#include "lib/file/vfs/vfs_path.h"
|
||||||
|
|
||||||
|
constexpr size_t VFS_MIN_PRIORITY = 0;
|
||||||
|
constexpr size_t VFS_MAX_PRIORITY = std::numeric_limits<size_t>::max();
|
||||||
|
|
||||||
namespace ERR
|
namespace ERR
|
||||||
{
|
{
|
||||||
const Status VFS_DIR_NOT_FOUND = -110100;
|
const Status VFS_DIR_NOT_FOUND = -110100;
|
||||||
@@ -65,16 +68,7 @@ enum VfsMountFlags
|
|||||||
* ".DELETED" suffix will still apply.
|
* ".DELETED" suffix will still apply.
|
||||||
* (the default behavior is to hide both the suffixed and unsuffixed files)
|
* (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
|
// (member functions are thread-safe after the instance has been
|
||||||
@@ -95,6 +89,11 @@ struct IVFS
|
|||||||
* if files are encountered that already exist in the VFS (sub)directories,
|
* if files are encountered that already exist in the VFS (sub)directories,
|
||||||
* the most recent / highest priority/precedence version is preferred.
|
* the most recent / highest priority/precedence version is preferred.
|
||||||
*
|
*
|
||||||
|
* Note that the 'real directory' associated with a VFS Path
|
||||||
|
* will be relative to the highest priority subdirectory in the path,
|
||||||
|
* and that in case of equal priority, the order is _undefined_,
|
||||||
|
* and will depend on the exact order of populate() calls.
|
||||||
|
*
|
||||||
* if files with archive extensions are seen, their contents are added
|
* if files with archive extensions are seen, their contents are added
|
||||||
* as well.
|
* as well.
|
||||||
**/
|
**/
|
||||||
@@ -143,17 +142,6 @@ struct IVFS
|
|||||||
**/
|
**/
|
||||||
virtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;
|
virtual Status CreateFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace a file with the given contents.
|
|
||||||
*
|
|
||||||
* @see CreateFile
|
|
||||||
*
|
|
||||||
* Used to replace a file if it is already present (even if the file is not
|
|
||||||
* in the attached vfs directory). Calls CreateFile if the file doesn't yet
|
|
||||||
* exist.
|
|
||||||
**/
|
|
||||||
virtual Status ReplaceFile(const VfsPath& pathname, const shared_ptr<u8>& fileContents, size_t size) = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an entire file into memory.
|
* Read an entire file into memory.
|
||||||
*
|
*
|
||||||
@@ -170,18 +158,32 @@ struct IVFS
|
|||||||
virtual std::wstring TextRepresentation() const = 0;
|
virtual std::wstring TextRepresentation() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve the real (POSIX) pathname underlying a VFS file.
|
* Retrieve the POSIX pathname a VFS file was loaded from.
|
||||||
|
* This is distinct from the current 'real' path, since that depends on the parent directory's real path,
|
||||||
|
* which may have been overwritten by a mod or another call to Mount().
|
||||||
*
|
*
|
||||||
* this is useful for passing paths to external libraries.
|
* This is used by the caching to split by mod, and you also ought to call this to delete a file.
|
||||||
|
* (note that deleting has other issues, see below).
|
||||||
**/
|
**/
|
||||||
virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;
|
virtual Status GetOriginalPath(const VfsPath& filename, OsPath& loadedPathname) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve the real (POSIX) pathname underlying a VFS directory.
|
* Retrieve the real (POSIX) pathname underlying a VFS file.
|
||||||
*
|
* This is useful for passing paths to external libraries.
|
||||||
* this is useful for passing paths to external libraries.
|
* Note that this returns the real path relative to the highest priority VFS subpath.
|
||||||
|
* @param createMissingDirectories - if true, create subdirectories on the file system as required.
|
||||||
|
* (this defaults to true because, in general, this function is then used to create new files).
|
||||||
**/
|
**/
|
||||||
virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname) = 0;
|
virtual Status GetRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories = true) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the real (POSIX) pathname underlying a VFS directory.
|
||||||
|
* This is useful for passing paths to external libraries.
|
||||||
|
* Note that this returns the real path relative to the highest priority VFS subpath.
|
||||||
|
* @param createMissingDirectories - if true, create subdirectories on the file system as required.
|
||||||
|
* (this defaults to true because, in general, this function is then used to create new files).
|
||||||
|
**/
|
||||||
|
virtual Status GetDirectoryRealPath(const VfsPath& pathname, OsPath& realPathname, bool createMissingDirectories = true) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve the VFS pathname that corresponds to a real file.
|
* retrieve the VFS pathname that corresponds to a real file.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2013 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -47,14 +47,12 @@ static Status CreateDirectory(const OsPath& path)
|
|||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// failed because the directory already exists. this can happen
|
// Failed because the directory already exists.
|
||||||
// when the first vfs_Lookup has addMissingDirectories &&
|
// Return 'success' to attach the existing directory.
|
||||||
// !createMissingDirectories, and the directory is subsequently
|
|
||||||
// created. return 'success' to attach the existing directory..
|
|
||||||
if(errno == EEXIST)
|
if(errno == EEXIST)
|
||||||
{
|
{
|
||||||
// but first ensure it's really a directory (otherwise, a
|
// But first ensure it's really a directory
|
||||||
// file is "in the way" and needs to be deleted)
|
// (otherwise, a file is "in the way" and needs to be deleted).
|
||||||
struct stat s;
|
struct stat s;
|
||||||
const int ret = wstat(path, &s);
|
const int ret = wstat(path, &s);
|
||||||
ENSURE(ret == 0); // (wmkdir said it existed)
|
ENSURE(ret == 0); // (wmkdir said it existed)
|
||||||
@@ -76,10 +74,9 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire
|
|||||||
{
|
{
|
||||||
// extract and validate flags (ensure no unknown bits are set)
|
// extract and validate flags (ensure no unknown bits are set)
|
||||||
const bool addMissingDirectories = (flags & VFS_LOOKUP_ADD) != 0;
|
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 skipPopulate = (flags & VFS_LOOKUP_SKIP_POPULATE) != 0;
|
||||||
const bool createAlways = (flags & VFS_LOOKUP_CREATE_ALWAYS) != 0;
|
const bool realPath = (flags & VFS_LOOKUP_REAL_PATH) != 0;
|
||||||
ENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_CREATE|VFS_LOOKUP_SKIP_POPULATE|VFS_LOOKUP_CREATE_ALWAYS)) == 0);
|
ENSURE((flags & ~(VFS_LOOKUP_ADD|VFS_LOOKUP_SKIP_POPULATE|VFS_LOOKUP_REAL_PATH)) == 0);
|
||||||
|
|
||||||
directory = startDirectory;
|
directory = startDirectory;
|
||||||
if (pfile)
|
if (pfile)
|
||||||
@@ -91,7 +88,9 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire
|
|||||||
// early-out for pathname == "" when mounting into VFS root
|
// early-out for pathname == "" when mounting into VFS root
|
||||||
if (pathname.empty()) // (prevent iterator error in loop end condition)
|
if (pathname.empty()) // (prevent iterator error in loop end condition)
|
||||||
{
|
{
|
||||||
if(pfile) // preserve a guarantee that if pfile then we either return an error or set *pfile
|
// Preserve a guarantee that if pfile then we either return an error or set *pfile,
|
||||||
|
// and if looking for a real path ensure an associated directory.
|
||||||
|
if (pfile || (realPath && !directory->AssociatedDirectory()))
|
||||||
return ERR::VFS_FILE_NOT_FOUND;
|
return ERR::VFS_FILE_NOT_FOUND;
|
||||||
else
|
else
|
||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
@@ -115,18 +114,29 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire
|
|||||||
else
|
else
|
||||||
return ERR::VFS_DIR_NOT_FOUND; // NOWARN
|
return ERR::VFS_DIR_NOT_FOUND; // NOWARN
|
||||||
}
|
}
|
||||||
|
// When looking for a real path, we need to keep the path of the highest priority subdirectory.
|
||||||
if(createMissingDirectories && (!subdirectory->AssociatedDirectory()
|
// If the current directory has an associated directory, and the subdir does not / is lower priority,
|
||||||
|| (createAlways && (subdirectory->AssociatedDirectory()->Flags() & VFS_MOUNT_REPLACEABLE) != 0)))
|
// we will overwrite it.
|
||||||
|
PRealDirectory realDir = directory->AssociatedDirectory();
|
||||||
|
if (realPath && realDir &&
|
||||||
|
(!subdirectory->AssociatedDirectory() ||
|
||||||
|
realDir->Priority() > subdirectory->AssociatedDirectory()->Priority()))
|
||||||
{
|
{
|
||||||
OsPath currentPath;
|
OsPath currentPath = directory->AssociatedDirectory()->Path();
|
||||||
if(directory->AssociatedDirectory()) // (is NULL when mounting into root)
|
currentPath = currentPath / subdirectoryName / "";
|
||||||
currentPath = directory->AssociatedDirectory()->Path();
|
|
||||||
currentPath = currentPath / subdirectoryName;
|
|
||||||
|
|
||||||
|
// Only actually create the directory if we're in LOOKUP_ADD mode.
|
||||||
|
if (addMissingDirectories)
|
||||||
RETURN_STATUS_IF_ERR(CreateDirectory(currentPath));
|
RETURN_STATUS_IF_ERR(CreateDirectory(currentPath));
|
||||||
|
else if (!DirectoryExists(currentPath))
|
||||||
|
return ERR::VFS_DIR_NOT_FOUND;
|
||||||
|
|
||||||
PRealDirectory realDirectory(new RealDirectory(currentPath, 0, 0));
|
// Propagate priority and flags to the subdirectory.
|
||||||
|
// If it already existed, it will be replaced & the memory freed.
|
||||||
|
PRealDirectory realDirectory(new RealDirectory(currentPath,
|
||||||
|
realDir ? realDir->Priority() : 0,
|
||||||
|
realDir ? realDir->Flags() : 0)
|
||||||
|
);
|
||||||
RETURN_STATUS_IF_ERR(vfs_Attach(subdirectory, realDirectory));
|
RETURN_STATUS_IF_ERR(vfs_Attach(subdirectory, realDirectory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,6 +146,9 @@ Status vfs_Lookup(const VfsPath& pathname, VfsDirectory* startDirectory, VfsDire
|
|||||||
directory = subdirectory;
|
directory = subdirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (realPath && !directory->AssociatedDirectory())
|
||||||
|
return ERR::VFS_DIR_NOT_FOUND;
|
||||||
|
|
||||||
if (pfile)
|
if (pfile)
|
||||||
{
|
{
|
||||||
ENSURE(!pathname.IsDirectory());
|
ENSURE(!pathname.IsDirectory());
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2013 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -37,24 +37,24 @@ class VfsDirectory;
|
|||||||
|
|
||||||
enum VfsLookupFlags
|
enum VfsLookupFlags
|
||||||
{
|
{
|
||||||
// add (if they do not already exist) subdirectory components
|
// Add (if they do not already exist) subdirectory components
|
||||||
// encountered in the path[name].
|
// encountered in the path[name].
|
||||||
|
// If subdirectores do not exist on disk, they will be created.
|
||||||
VFS_LOOKUP_ADD = 1,
|
VFS_LOOKUP_ADD = 1,
|
||||||
|
|
||||||
// if VFS directories encountered are not already associated
|
// Don't populate the directories encountered. This makes sense
|
||||||
// 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
|
// when adding files from an archive, which would otherwise
|
||||||
// cause nearly every directory to be populated.
|
// cause nearly every directory to be populated.
|
||||||
VFS_LOOKUP_SKIP_POPULATE = 4,
|
VFS_LOOKUP_SKIP_POPULATE = 2,
|
||||||
|
|
||||||
// even create directories if they are already present, this is
|
// Perform a 'real path' lookup.
|
||||||
// useful to write new files to the directory that was attached
|
// Because the VFS maps multiple 'disk paths' to a single tree of paths,
|
||||||
// last, if the directory wasn't mounted with VFS_MOUNT_REPLACEABLE
|
// the 'real directory' of a VFS directory at any given time may be almost anything,
|
||||||
VFS_LOOKUP_CREATE_ALWAYS = 8
|
// in particular not its real parent directory on disk.
|
||||||
|
// To make writing predictable, we'll return a path relative to the 'disk path' of the
|
||||||
|
// highest priority subdirectory found in the lookup path.
|
||||||
|
// See test_vfs_real_paths.h for examples of this behaviour.
|
||||||
|
VFS_LOOKUP_REAL_PATH = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -168,7 +168,29 @@ Status vfs_Populate(VfsDirectory* directory)
|
|||||||
|
|
||||||
Status vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)
|
Status vfs_Attach(VfsDirectory* directory, const PRealDirectory& realDirectory)
|
||||||
{
|
{
|
||||||
|
PRealDirectory existingRealDir = directory->AssociatedDirectory();
|
||||||
|
|
||||||
|
// Don't allow replacing the real directory by a lower-priority one.
|
||||||
|
if (!existingRealDir || existingRealDir->Priority() < realDirectory->Priority())
|
||||||
|
{
|
||||||
|
// This ordering is peculiar but useful, as it "defers" the population call.
|
||||||
|
// If there is already a real directory, we will replace it (and lose track of it),
|
||||||
|
// so we'll populate it right away, but the 'new' real directory can wait until we access it.
|
||||||
RETURN_STATUS_IF_ERR(vfs_Populate(directory));
|
RETURN_STATUS_IF_ERR(vfs_Populate(directory));
|
||||||
directory->SetAssociatedDirectory(realDirectory);
|
directory->SetAssociatedDirectory(realDirectory);
|
||||||
return INFO::OK;
|
return INFO::OK;
|
||||||
}
|
}
|
||||||
|
// We are attaching a lower-priority real directory.
|
||||||
|
// Because of deferred population, we need to immediately populate this new directory.
|
||||||
|
bool shouldPop = directory->ShouldPopulate();
|
||||||
|
// This sets "should populate" to true, so the vfs_Populate call below immediately populates.
|
||||||
|
directory->SetAssociatedDirectory(realDirectory);
|
||||||
|
RETURN_STATUS_IF_ERR(vfs_Populate(directory));
|
||||||
|
// Reset to the higher priority realDirectory, which resets ShouldPopulate to true.
|
||||||
|
directory->SetAssociatedDirectory(existingRealDir);
|
||||||
|
// Avoid un-necessary repopulation by clearing the flag.
|
||||||
|
if (!shouldPop)
|
||||||
|
directory->ShouldPopulate();
|
||||||
|
|
||||||
|
return INFO::OK;
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -593,7 +593,8 @@ static void RunGameOrAtlas(int argc, const char* argv[])
|
|||||||
|
|
||||||
Paths paths(args);
|
Paths paths(args);
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE);
|
// Mount with highest priority, we don't want mods overwriting this.
|
||||||
|
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE, VFS_MAX_PRIORITY);
|
||||||
MountMods(paths, GetMods(args, INIT_MODS));
|
MountMods(paths, GetMods(args, INIT_MODS));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
|
|
||||||
// Need some stuff for terrain movement costs:
|
// Need some stuff for terrain movement costs:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -35,10 +35,10 @@ CArchiveBuilder::CArchiveBuilder(const OsPath& mod, const OsPath& tempdir) :
|
|||||||
|
|
||||||
DeleteDirectory(m_TempDir/"_archivecache"); // clean up in case the last run failed
|
DeleteDirectory(m_TempDir/"_archivecache"); // clean up in case the last run failed
|
||||||
|
|
||||||
m_VFS->Mount(L"cache/", m_TempDir/"_archivecache"/"");
|
m_VFS->Mount(L"cache/", m_TempDir/"_archivecache"/"", 0, VFS_MAX_PRIORITY);
|
||||||
|
|
||||||
// Mount with highest priority so base mods do not overwrite files in this mod
|
// Mount with highest priority so base mods do not overwrite files in this mod
|
||||||
m_VFS->Mount(L"", mod/"", VFS_MOUNT_MUST_EXIST | VFS_MOUNT_KEEP_DELETED, (size_t)-1);
|
m_VFS->Mount(L"", mod/"", VFS_MOUNT_MUST_EXIST | VFS_MOUNT_KEEP_DELETED, VFS_MAX_PRIORITY);
|
||||||
|
|
||||||
// Collect the list of files before loading any base mods
|
// Collect the list of files before loading any base mods
|
||||||
vfs::ForEachFile(m_VFS, L"", &CollectFileCB, (uintptr_t)static_cast<void*>(this), 0, vfs::DIR_RECURSIVE);
|
vfs::ForEachFile(m_VFS, L"", &CollectFileCB, (uintptr_t)static_cast<void*>(this), 0, vfs::DIR_RECURSIVE);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2018 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -140,7 +140,7 @@ VfsPath CCacheLoader::LooseCachePath(const VfsPath& sourcePath, const MD5& initi
|
|||||||
|
|
||||||
// Get the mod path
|
// Get the mod path
|
||||||
OsPath path;
|
OsPath path;
|
||||||
m_VFS->GetRealPath(sourcePath, path);
|
m_VFS->GetOriginalPath(sourcePath, path);
|
||||||
|
|
||||||
return VfsPath("cache") /
|
return VfsPath("cache") /
|
||||||
path_name_only(path.BeforeCommon(sourcePath).Parent().string().c_str()) /
|
path_name_only(path.BeforeCommon(sourcePath).Parent().string().c_str()) /
|
||||||
|
|||||||
@@ -375,17 +375,10 @@ ErrorReactionInternal psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED(
|
|||||||
const std::vector<CStr>& GetMods(const CmdLineArgs& args, int flags)
|
const std::vector<CStr>& GetMods(const CmdLineArgs& args, int flags)
|
||||||
{
|
{
|
||||||
const bool init_mods = (flags & INIT_MODS) == INIT_MODS;
|
const bool init_mods = (flags & INIT_MODS) == INIT_MODS;
|
||||||
const bool add_user = !InDevelopmentCopy() && !args.Has("noUserMod");
|
|
||||||
const bool add_public = (flags & INIT_MODS_PUBLIC) == INIT_MODS_PUBLIC;
|
const bool add_public = (flags & INIT_MODS_PUBLIC) == INIT_MODS_PUBLIC;
|
||||||
|
|
||||||
if (!init_mods)
|
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");
|
|
||||||
|
|
||||||
return g_modsLoaded;
|
return g_modsLoaded;
|
||||||
}
|
|
||||||
|
|
||||||
g_modsLoaded = args.GetMultiple("mod");
|
g_modsLoaded = args.GetMultiple("mod");
|
||||||
|
|
||||||
@@ -394,11 +387,6 @@ const std::vector<CStr>& GetMods(const CmdLineArgs& args, int flags)
|
|||||||
|
|
||||||
g_modsLoaded.insert(g_modsLoaded.begin(), "mod");
|
g_modsLoaded.insert(g_modsLoaded.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");
|
|
||||||
|
|
||||||
return g_modsLoaded;
|
return g_modsLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,29 +394,24 @@ void MountMods(const Paths& paths, const std::vector<CStr>& mods)
|
|||||||
{
|
{
|
||||||
OsPath modPath = paths.RData()/"mods";
|
OsPath modPath = paths.RData()/"mods";
|
||||||
OsPath modUserPath = paths.UserData()/"mods";
|
OsPath modUserPath = paths.UserData()/"mods";
|
||||||
|
|
||||||
|
size_t userFlags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE;
|
||||||
|
size_t baseFlags = userFlags|VFS_MOUNT_MUST_EXIST;
|
||||||
|
size_t priority;
|
||||||
for (size_t i = 0; i < mods.size(); ++i)
|
for (size_t i = 0; i < mods.size(); ++i)
|
||||||
{
|
{
|
||||||
size_t priority = (i+1)*2; // mods are higher priority than regular mountings, which default to priority 0
|
priority = i + 1; // 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 baseFlags = userFlags|VFS_MOUNT_MUST_EXIST;
|
|
||||||
|
|
||||||
OsPath modName(mods[i]);
|
OsPath modName(mods[i]);
|
||||||
if (InDevelopmentCopy())
|
// Only mount mods from the user path if they don't exist in the 'rdata' path.
|
||||||
{
|
|
||||||
// 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 / ""))
|
if (DirectoryExists(modPath / modName / ""))
|
||||||
g_VFS->Mount(L"", modPath / modName / "", baseFlags, priority);
|
g_VFS->Mount(L"", modPath / modName / "", baseFlags, priority);
|
||||||
else
|
else
|
||||||
g_VFS->Mount(L"", modUserPath / modName / "", userFlags, priority);
|
g_VFS->Mount(L"", modUserPath / modName / "", userFlags, priority);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Mount the user mod last. In dev copy, mount it with a low priority. Otherwise, make it writable.
|
||||||
g_VFS->Mount(L"", modPath / modName/"", baseFlags, priority);
|
g_VFS->Mount(L"", modUserPath / "user" / "", userFlags, InDevelopmentCopy() ? 0 : priority + 1);
|
||||||
// Ensure that user modified files are loaded, if they are present
|
|
||||||
g_VFS->Mount(L"", modUserPath / modName/"", userFlags, priority+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void InitVfs(const CmdLineArgs& args, int flags)
|
static void InitVfs(const CmdLineArgs& args, int flags)
|
||||||
@@ -455,23 +438,20 @@ static void InitVfs(const CmdLineArgs& args, int flags)
|
|||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
|
|
||||||
const OsPath readonlyConfig = paths.RData()/"config"/"";
|
const OsPath readonlyConfig = paths.RData()/"config"/"";
|
||||||
g_VFS->Mount(L"config/", readonlyConfig);
|
|
||||||
|
|
||||||
// Engine localization files.
|
// Mount these dirs with highest priority so that mods can't overwrite them.
|
||||||
|
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE, VFS_MAX_PRIORITY); // (adding XMBs to archive speeds up subsequent reads)
|
||||||
|
if (readonlyConfig != paths.Config())
|
||||||
|
g_VFS->Mount(L"config/", readonlyConfig, 0, VFS_MAX_PRIORITY-1);
|
||||||
|
g_VFS->Mount(L"config/", paths.Config(), 0, VFS_MAX_PRIORITY);
|
||||||
|
g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/"", 0, VFS_MAX_PRIORITY);
|
||||||
|
g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"", VFS_MOUNT_WATCH, VFS_MAX_PRIORITY);
|
||||||
|
|
||||||
|
// Engine localization files (regular priority, these can be overwritten).
|
||||||
g_VFS->Mount(L"l10n/", paths.RData()/"l10n"/"");
|
g_VFS->Mount(L"l10n/", paths.RData()/"l10n"/"");
|
||||||
|
|
||||||
MountMods(paths, GetMods(args, flags));
|
MountMods(paths, GetMods(args, flags));
|
||||||
|
|
||||||
// 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);
|
|
||||||
// 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())
|
|
||||||
g_VFS->Mount(L"config/", paths.Config(), 0, (size_t)-1);
|
|
||||||
|
|
||||||
g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads)
|
|
||||||
|
|
||||||
// note: don't bother with g_VFS->TextRepresentation - directories
|
// note: don't bother with g_VFS->TextRepresentation - directories
|
||||||
// haven't yet been populated and are empty.
|
// haven't yet been populated and are empty.
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -59,7 +59,8 @@ JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface)
|
|||||||
for (DirectoryNames::iterator iter = modDirs.begin(); iter != modDirs.end(); ++iter)
|
for (DirectoryNames::iterator iter = modDirs.begin(); iter != modDirs.end(); ++iter)
|
||||||
{
|
{
|
||||||
vfs->Clear();
|
vfs->Clear();
|
||||||
if (vfs->Mount(L"", modPath / *iter, VFS_MOUNT_MUST_EXIST) < 0)
|
// Mount with lowest priority, we don't want to overwrite anything
|
||||||
|
if (vfs->Mount(L"", modPath / *iter / "", VFS_MOUNT_MUST_EXIST, VFS_MIN_PRIORITY) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CVFSFile modinfo;
|
CVFSFile modinfo;
|
||||||
@@ -85,7 +86,8 @@ JS::Value Mod::GetAvailableMods(const ScriptInterface& scriptInterface)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
vfs->Clear();
|
vfs->Clear();
|
||||||
if (vfs->Mount(L"", modUserPath / *iter, VFS_MOUNT_MUST_EXIST) < 0)
|
// Mount with lowest priority, we don't want to overwrite anything
|
||||||
|
if (vfs->Mount(L"", modUserPath / *iter / "", VFS_MOUNT_MUST_EXIST, VFS_MIN_PRIORITY) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CVFSFile modinfo;
|
CVFSFile modinfo;
|
||||||
@@ -114,8 +116,8 @@ void Mod::CacheEnabledModVersions(const shared_ptr<ScriptContext>& scriptContext
|
|||||||
|
|
||||||
for (const CStr& mod : g_modsLoaded)
|
for (const CStr& mod : g_modsLoaded)
|
||||||
{
|
{
|
||||||
// Ignore user and mod mod as they are irrelevant for compatibility checks
|
// Ignore mod mod as it is irrelevant for compatibility checks
|
||||||
if (mod == "mod" || mod == "user")
|
if (mod == "mod")
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
CStr version;
|
CStr version;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -284,8 +284,8 @@ bool SavedGames::DeleteSavedGame(const std::wstring& name)
|
|||||||
const VfsPath filename = basename.ChangeExtension(L".0adsave");
|
const VfsPath filename = basename.ChangeExtension(L".0adsave");
|
||||||
OsPath realpath;
|
OsPath realpath;
|
||||||
|
|
||||||
// Make sure it exists in VFS and find its real path
|
// Make sure it exists in VFS and find its path
|
||||||
if (!VfsFileExists(filename) || g_VFS->GetRealPath(filename, realpath) != INFO::OK)
|
if (!VfsFileExists(filename) || g_VFS->GetOriginalPath(filename, realpath) != INFO::OK)
|
||||||
return false; // Error
|
return false; // Error
|
||||||
|
|
||||||
// Remove from VFS
|
// Remove from VFS
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"config", DataDir()/"_testconfig"));
|
TS_ASSERT_OK(g_VFS->Mount(L"config", DataDir() / "_testconfig" / ""));
|
||||||
|
|
||||||
configDB = new CConfigDB;
|
configDB = new CConfigDB;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"config", DataDir()/"_testconfig"));
|
TS_ASSERT_OK(g_VFS->Mount(L"config", DataDir() / "_testconfig" / ""));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / ""));
|
||||||
|
|
||||||
configDB = new CConfigDB;
|
configDB = new CConfigDB;
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.sim" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tearDown()
|
void tearDown()
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
g_VFS->Mount(L"", DataDir()/"mods"/"mod", VFS_MOUNT_MUST_EXIST);
|
g_VFS->Mount(L"", DataDir() / "mods" / "mod" / "", VFS_MOUNT_MUST_EXIST);
|
||||||
g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
|
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
g_VFS->Mount(L"", DataDir()/"mods"/"mod", VFS_MOUNT_MUST_EXIST);
|
g_VFS->Mount(L"", DataDir() / "mods" / "mod" / "", VFS_MOUNT_MUST_EXIST);
|
||||||
g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST, 1); // ignore directory-not-found errors
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -37,8 +37,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.sim" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,9 +221,9 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"mod", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "mod" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -44,8 +44,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.sim" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2020 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -875,8 +875,8 @@ public:
|
|||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
|
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"public", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "public" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
|
|
||||||
// Need some stuff for terrain movement costs:
|
// Need some stuff for terrain movement costs:
|
||||||
// (TODO: this ought to be independent of any graphics code)
|
// (TODO: this ought to be independent of any graphics code)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2019 Wildfire Games.
|
/* Copyright (C) 2021 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -43,8 +43,8 @@ public:
|
|||||||
void setUp()
|
void setUp()
|
||||||
{
|
{
|
||||||
g_VFS = CreateVfs();
|
g_VFS = CreateVfs();
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir()/"mods"/"_test.sim", VFS_MOUNT_MUST_EXIST));
|
TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.sim" / "", VFS_MOUNT_MUST_EXIST));
|
||||||
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir()/"_testcache"));
|
TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY));
|
||||||
CXeromyces::Startup();
|
CXeromyces::Startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user