diff --git a/src/engine/engine_vfs.c b/src/engine/engine_vfs.c index aa8924bc..f6afa0c6 100644 --- a/src/engine/engine_vfs.c +++ b/src/engine/engine_vfs.c @@ -347,6 +347,29 @@ static void vfs_getdir_callback(mjResource* resource, const char** dir, int* ndi } +// modified callback for the VFS resource provider +// return > 0 if modified and 0 if unmodified +static int vfs_modified_callback(const mjResource* resource, const char* timestamp) { + uint64_t filestamp; + if (mju_isValidBase64(timestamp) > sizeof(uint64_t)) { + return 2; // error (assume modified) + } + + mju_decodeBase64((uint8_t*) &filestamp, timestamp); + if (!filestamp) return 3; // no hash (assume modified) + + if (resource) { + const mjVFS* vfs = (const mjVFS*) resource->data; + int i = mj_findFileVFS(vfs, resource->name); + if (i < 0) return 4; // missing file (assume modified) + if (!vfs->filestamp[i]) return 5; // missing filestamp (assume modified) + + if (vfs->filestamp[i] == filestamp) { + return 0; // unmodified + } + } + return 1; // modified +} // open VFS resource mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs) { @@ -362,7 +385,7 @@ mjResource* mju_openVfsResource(const char* name, const mjVFS* vfs) { .read = &vfs_read_callback, .close = &vfs_close_callback, .getdir = &vfs_getdir_callback, - .modified = NULL + .modified = &vfs_modified_callback, }; // create resource diff --git a/test/engine/engine_vfs_test.cc b/test/engine/engine_vfs_test.cc index 93249055..fdc0cbdf 100644 --- a/test/engine/engine_vfs_test.cc +++ b/test/engine/engine_vfs_test.cc @@ -20,6 +20,8 @@ #include #include #include +#include "src/engine/engine_resource.h" +#include "src/engine/engine_vfs.h" #include "test/fixture.h" namespace mujoco { @@ -28,7 +30,7 @@ namespace { using ::testing::NotNull; using EngineVfsTest = MujocoTest; -TEST_F(EngineVfsTest, AddFileTest) { +TEST_F(EngineVfsTest, AddFile) { constexpr char path[] = "engine/testdata/actuation/"; const std::string dir = GetTestDataFilePath(path); std::string file1 = "activation.xml"; @@ -81,7 +83,7 @@ TEST_F(EngineVfsTest, AddFileTest) { mj_deleteVFS(vfs.get()); } -TEST_F(EngineVfsTest, AddBufferTest) { +TEST_F(EngineVfsTest, AddBuffer) { auto vfs = std::make_unique(); mj_defaultVFS(vfs.get()); std::string buffer = ""; @@ -94,5 +96,32 @@ TEST_F(EngineVfsTest, AddBufferTest) { mj_deleteVFS(vfs.get()); } +TEST_F(EngineVfsTest, Timestamps) { + static constexpr char cube[] = R"( + v -0.500000 -0.500000 0.500000 + v 0.500000 -0.500000 0.500000 + v -0.500000 0.500000 0.500000 + v 0.500000 0.500000 0.500000 + v -0.500000 0.500000 -0.500000 + v 0.500000 0.500000 -0.500000 + v -0.500000 -0.500000 -0.500000 + v 0.500000 -0.500000 -0.500000)"; + + auto vfs = std::make_unique(); + mj_defaultVFS(vfs.get()); + mj_addBufferVFS(vfs.get(), "cube.obj", cube, sizeof(cube)); + + mjResource* resource = mju_openVfsResource("cube.obj", vfs.get()); + + // same timestamps + EXPECT_EQ(mju_isModifiedResource(resource, resource->timestamp), 0); + + // different timestamps + EXPECT_EQ(mju_isModifiedResource(resource, "QQ=="), 1); + + mju_closeResource(resource); + mj_deleteVFS(vfs.get()); +} + } // namespace } // namespace mujoco