├── .clang-format ├── .git-blame-ignore-revs ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── linting.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── cmake └── config.cmake.in ├── include └── usvfs │ ├── dllimport.h │ ├── logging.h │ ├── sharedparameters.h │ ├── usvfs.h │ ├── usvfs_version.h │ ├── usvfsparameters.h │ └── usvfsparametersprivate.h ├── licenses ├── asmjit.txt ├── boost.txt ├── cppformat.txt ├── googletest.txt ├── qt.txt ├── spdlog.txt └── udis86.txt ├── src ├── shared │ ├── CMakeLists.txt │ ├── addrtools.h │ ├── directory_tree.cpp │ ├── directory_tree.h │ ├── exceptionex.cpp │ ├── exceptionex.h │ ├── formatters.h │ ├── loghelpers.cpp │ ├── loghelpers.h │ ├── ntdll_declarations.cpp │ ├── ntdll_declarations.h │ ├── pch.cpp │ ├── pch.h │ ├── shared_memory.h │ ├── shmlogger.cpp │ ├── shmlogger.h │ ├── stringcast.cpp │ ├── stringcast.h │ ├── stringutils.cpp │ ├── stringutils.h │ ├── tree_container.h │ ├── unicodestring.cpp │ ├── unicodestring.h │ ├── wildcard.cpp │ ├── wildcard.h │ ├── winapi.cpp │ ├── winapi.h │ └── windows_sane.h ├── thooklib │ ├── CMakeLists.txt │ ├── asmjit_sane.h │ ├── hooklib.cpp │ ├── hooklib.h │ ├── pch.cpp │ ├── ttrampolinepool.cpp │ ├── ttrampolinepool.h │ ├── udis86wrapper.cpp │ ├── udis86wrapper.h │ ├── utility.cpp │ └── utility.h ├── tinjectlib │ ├── CMakeLists.txt │ ├── asmjit_sane.h │ ├── injectlib.cpp │ └── injectlib.h ├── usvfs_dll │ ├── CMakeLists.txt │ ├── hookcallcontext.cpp │ ├── hookcallcontext.h │ ├── hookcontext.cpp │ ├── hookcontext.h │ ├── hookmanager.cpp │ ├── hookmanager.h │ ├── hooks │ │ ├── file_information_utils.h │ │ ├── kernel32.cpp │ │ ├── kernel32.h │ │ ├── ntdll.cpp │ │ ├── ntdll.h │ │ └── sharedids.h │ ├── maptracker.h │ ├── pch.cpp │ ├── redirectiontree.cpp │ ├── redirectiontree.h │ ├── semaphore.cpp │ ├── semaphore.h │ ├── sharedparameters.cpp │ ├── stringcast_boost.h │ ├── usvfs.cpp │ ├── usvfsparameters.cpp │ └── version.rc ├── usvfs_helper │ ├── CMakeLists.txt │ ├── inject.cpp │ └── inject.h └── usvfs_proxy │ ├── CMakeLists.txt │ ├── main.cpp │ └── version.rc ├── test ├── CMakeLists.txt ├── fixtures │ ├── usvfs_global_test │ │ ├── BasicTest │ │ │ ├── expected │ │ │ │ ├── data │ │ │ │ │ └── file.txt │ │ │ │ └── mods │ │ │ │ │ └── mod1 │ │ │ │ │ ├── docs │ │ │ │ │ └── doc.txt │ │ │ │ │ ├── empty │ │ │ │ │ └── .gitkeep │ │ │ │ │ └── readme.txt │ │ │ └── source │ │ │ │ ├── data │ │ │ │ └── file.txt │ │ │ │ └── mods │ │ │ │ └── mod1 │ │ │ │ ├── docs │ │ │ │ └── doc.txt │ │ │ │ ├── empty │ │ │ │ └── .gitkeep │ │ │ │ ├── info.txt │ │ │ │ └── readme.txt │ │ ├── BoostFilesystemTest │ │ │ ├── expected │ │ │ │ ├── data │ │ │ │ │ └── .gitkeep │ │ │ │ └── mods │ │ │ │ │ └── mod1 │ │ │ │ │ └── docs │ │ │ │ │ ├── doc.txt │ │ │ │ │ └── subdocs │ │ │ │ │ └── .gitkeep │ │ │ └── source │ │ │ │ ├── data │ │ │ │ └── .gitkeep │ │ │ │ └── mods │ │ │ │ └── mod1 │ │ │ │ └── docs │ │ │ │ ├── doc.txt │ │ │ │ └── subdocs │ │ │ │ └── .gitkeep │ │ ├── RedFileSystemTest │ │ │ ├── example.cpp │ │ │ ├── expected │ │ │ │ ├── data │ │ │ │ │ └── .gitkeep │ │ │ │ ├── mods │ │ │ │ │ └── HUD Painter │ │ │ │ │ │ └── r6 │ │ │ │ │ │ └── storages │ │ │ │ │ │ └── HUDPainter │ │ │ │ │ │ └── DEFAULT.json │ │ │ │ └── overwrite │ │ │ │ │ └── r6 │ │ │ │ │ └── storages │ │ │ │ │ └── HUDPainter │ │ │ │ │ └── TEST.json │ │ │ └── source │ │ │ │ ├── data │ │ │ │ └── .gitkeep │ │ │ │ └── mods │ │ │ │ └── HUD Painter │ │ │ │ └── r6 │ │ │ │ └── storages │ │ │ │ └── HUDPainter │ │ │ │ └── DEFAULT.json │ │ └── SkipFilesTest │ │ │ ├── expected │ │ │ ├── data │ │ │ │ ├── docs │ │ │ │ │ └── doc.skip │ │ │ │ └── file.txt │ │ │ ├── mods │ │ │ │ └── mod1 │ │ │ │ │ ├── docs │ │ │ │ │ ├── doc.skip │ │ │ │ │ └── doc.txt │ │ │ │ │ ├── empty │ │ │ │ │ └── .gitkeep │ │ │ │ │ ├── readme.skip │ │ │ │ │ └── readme.txt │ │ │ └── overwrite │ │ │ │ └── readme.skip │ │ │ └── source │ │ │ ├── data │ │ │ ├── docs │ │ │ │ └── doc.skip │ │ │ └── file.txt │ │ │ └── mods │ │ │ └── mod1 │ │ │ ├── docs │ │ │ ├── doc.skip │ │ │ └── doc.txt │ │ │ ├── empty │ │ │ └── .gitkeep │ │ │ ├── readme.skip │ │ │ └── readme.txt │ └── usvfs_test │ │ └── basic │ │ ├── basic_ops32_x64.log │ │ ├── basic_ops32_x64_clean.log │ │ ├── basic_ops64_x86.log │ │ ├── basic_ops64_x86_clean.log │ │ ├── basic_x64.log │ │ ├── basic_x64_clean.log │ │ ├── basic_x86.log │ │ ├── basic_x86_clean.log │ │ ├── mount.postmortem │ │ ├── rfolder │ │ │ ├── rcopyme4.txt │ │ │ ├── rfile0.txt │ │ │ ├── rfiledeletewrite.txt │ │ │ └── rfilerewrite.txt │ │ ├── root0.txt │ │ ├── root0w.txt │ │ ├── root1.txt │ │ └── root1w.txt │ │ ├── mount │ │ ├── rfolder │ │ │ ├── rcopyme4.txt │ │ │ ├── rfile0.txt │ │ │ ├── rfiledelete.txt │ │ │ ├── rfiledeletewrite.txt │ │ │ ├── rfileoldname.txt │ │ │ └── rfilerewrite.txt │ │ ├── root0.txt │ │ ├── root0w.txt │ │ ├── root1.txt │ │ └── root1w.txt │ │ ├── source.postmortem │ │ ├── mod1 │ │ │ ├── mfolder1 │ │ │ │ ├── mfile.txt │ │ │ │ └── mfilew.txt │ │ │ ├── mfolder3 │ │ │ │ └── mdeep3 │ │ │ │ │ └── mfile.txt │ │ │ ├── mod1.txt │ │ │ └── mod1w.txt │ │ ├── mod2 │ │ │ ├── mfolder2 │ │ │ │ └── mfile.txt │ │ │ ├── mfolder4 │ │ │ │ ├── mfile.txt │ │ │ │ ├── mfiledeletemove.txt │ │ │ │ ├── mfiledeletemove2p.txt │ │ │ │ ├── mfiledeletewrite.txt │ │ │ │ ├── mfiledeletewrite2p.txt │ │ │ │ ├── mfilemoveover.txt │ │ │ │ ├── mfileoverwrite.txt │ │ │ │ └── mfilerewrite.txt │ │ │ └── mod2.txt │ │ ├── mod3 │ │ │ ├── mfolder3 │ │ │ │ └── mdeep3 │ │ │ │ │ └── mfile.txt │ │ │ ├── mfolder4 │ │ │ │ └── mfile.txt │ │ │ └── mod3.txt │ │ ├── mod4 │ │ │ └── mfolder4 │ │ │ │ ├── mfile.txt │ │ │ │ ├── mfiledeletemove.txt │ │ │ │ ├── mfiledeletewrite.txt │ │ │ │ ├── mfilemoveover.txt │ │ │ │ ├── mfileoverwrite.txt │ │ │ │ └── mfilerewrite.txt │ │ ├── overwrite │ │ │ ├── .empty │ │ │ ├── mfolder1 │ │ │ │ ├── newfile1.txt │ │ │ │ └── newfolder1 │ │ │ │ │ └── newfile1.txt │ │ │ ├── mfolder2 │ │ │ │ └── newfile2.txt │ │ │ ├── mfolder3 │ │ │ │ └── newfolder3 │ │ │ │ │ ├── newfile3.txt │ │ │ │ │ └── newfile3e.txt │ │ │ ├── mfolder4 │ │ │ │ ├── mfiledeletemove2p.txt │ │ │ │ ├── mfiledeletewrite2p.txt │ │ │ │ ├── newfolder4 │ │ │ │ │ └── d │ │ │ │ │ │ └── e │ │ │ │ │ │ ├── e │ │ │ │ │ │ └── p │ │ │ │ │ │ │ ├── newfile4.txt │ │ │ │ │ │ │ ├── newfile4e.txt │ │ │ │ │ │ │ └── newfile4enr.txt │ │ │ │ │ │ ├── epnewfile4.txt │ │ │ │ │ │ └── epnewfile4r.txt │ │ │ │ ├── newfolder4p │ │ │ │ │ └── rcopyme4.txt │ │ │ │ └── rcopyme4.txt │ │ │ └── rfolder │ │ │ │ └── rfilenewname.txt │ │ ├── root1.txt │ │ ├── root1w.txt │ │ ├── root2.txt │ │ └── root2w.txt │ │ ├── source │ │ ├── mod1 │ │ │ ├── mfolder1 │ │ │ │ ├── mfile.txt │ │ │ │ └── mfilew.txt │ │ │ ├── mfolder3 │ │ │ │ └── mdeep3 │ │ │ │ │ └── mfile.txt │ │ │ ├── mod1.txt │ │ │ └── mod1w.txt │ │ ├── mod2 │ │ │ ├── mfolder2 │ │ │ │ └── mfile.txt │ │ │ ├── mfolder4 │ │ │ │ ├── mfile.txt │ │ │ │ ├── mfiledeletemove.txt │ │ │ │ ├── mfiledeletemove2p.txt │ │ │ │ ├── mfiledeletewrite.txt │ │ │ │ ├── mfiledeletewrite2p.txt │ │ │ │ ├── mfilemoveover.txt │ │ │ │ ├── mfileoverwrite.txt │ │ │ │ └── mfilerewrite.txt │ │ │ └── mod2.txt │ │ ├── mod3 │ │ │ ├── mfolder3 │ │ │ │ └── mdeep3 │ │ │ │ │ └── mfile.txt │ │ │ ├── mfolder4 │ │ │ │ └── mfile.txt │ │ │ └── mod3.txt │ │ ├── mod4 │ │ │ └── mfolder4 │ │ │ │ ├── mfile.txt │ │ │ │ ├── mfiledeletemove.txt │ │ │ │ ├── mfiledeletemove2p.txt │ │ │ │ ├── mfiledeletewrite.txt │ │ │ │ ├── mfiledeletewrite2p.txt │ │ │ │ ├── mfilemoveover.txt │ │ │ │ ├── mfileoverwrite.txt │ │ │ │ └── mfilerewrite.txt │ │ ├── overwrite │ │ │ └── .empty │ │ ├── root1.txt │ │ ├── root1w.txt │ │ ├── root2.txt │ │ └── root2w.txt │ │ └── vfs_mappings.txt ├── gtest_utils │ ├── CMakeLists.txt │ ├── gtest_utils.cpp │ └── gtest_utils.h ├── shared_test │ ├── CMakeLists.txt │ └── main.cpp ├── test_utils │ ├── CMakeLists.txt │ ├── test_helpers.cpp │ └── test_helpers.h ├── thooklib_test │ ├── CMakeLists.txt │ ├── main.cpp │ └── test_hooks.cpp ├── tinjectlib_test │ ├── CMakeLists.txt │ ├── main.cpp │ ├── testinject_bin │ │ └── main.cpp │ └── testinject_dll │ │ ├── main.cpp │ │ └── main.h ├── tvfs_test │ ├── CMakeLists.txt │ └── main.cpp ├── usvfs_global_test_runner │ ├── CMakeLists.txt │ ├── usvfs_global_test │ │ └── usvfs_global_test.cpp │ ├── usvfs_global_test_fixture.cpp │ ├── usvfs_global_test_fixture.h │ └── usvfs_global_test_runner.cpp └── usvfs_test_runner │ ├── CMakeLists.txt │ ├── test_file_operations │ ├── test_file_operations.cpp │ ├── test_filesystem.cpp │ ├── test_filesystem.h │ ├── test_ntapi.cpp │ ├── test_ntapi.h │ ├── test_ntdll_declarations.h │ ├── test_w32api.cpp │ └── test_w32api.h │ ├── usvfs_test │ ├── usvfs_basic_test.cpp │ ├── usvfs_basic_test.h │ ├── usvfs_test.cpp │ ├── usvfs_test_base.cpp │ └── usvfs_test_base.h │ └── usvfs_test_runner.cpp ├── vcpkg-configuration.json └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # We'll use defaults from the LLVM style, but with 4 columns indentation. 3 | BasedOnStyle: LLVM 4 | IndentWidth: 2 5 | --- 6 | Language: Cpp 7 | DeriveLineEnding: false 8 | UseCRLF: true 9 | DerivePointerAlignment: false 10 | PointerAlignment: Left 11 | AlignConsecutiveAssignments: true 12 | AllowShortFunctionsOnASingleLine: Inline 13 | AllowShortIfStatementsOnASingleLine: Never 14 | AllowShortLambdasOnASingleLine: Empty 15 | AlwaysBreakTemplateDeclarations: Yes 16 | AccessModifierOffset: -2 17 | AlignTrailingComments: true 18 | SpacesBeforeTrailingComments: 2 19 | NamespaceIndentation: Inner 20 | MaxEmptyLinesToKeep: 1 21 | BreakBeforeBraces: Custom 22 | BraceWrapping: 23 | AfterCaseLabel: false 24 | AfterClass: true 25 | AfterControlStatement: false 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: true 29 | AfterStruct: true 30 | AfterUnion: true 31 | AfterExternBlock: true 32 | BeforeCatch: false 33 | BeforeElse: false 34 | BeforeLambdaBody: false 35 | BeforeWhile: false 36 | IndentBraces: false 37 | SplitEmptyFunction: false 38 | SplitEmptyRecord: false 39 | SplitEmptyNamespace: true 40 | ColumnLimit: 88 41 | ForEachMacros: ['Q_FOREACH', 'foreach'] 42 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | 1c95b7452585e53ef97954248aed42480d5bb5de 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.cpp text eol=crlf 7 | *.h text eol=crlf 8 | -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Lint USVFS 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: [opened, synchronize, reopened] 7 | 8 | jobs: 9 | lint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Check format 14 | uses: ModOrganizer2/check-formatting-action@master 15 | with: 16 | check-path: "." 17 | exclude-regex: "third-party" 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build outputs: 2 | /bin 3 | /lib 4 | /test/bin 5 | /build* 6 | /install 7 | 8 | # local overides 9 | /vsbuild/external_dependencies_local.props 10 | 11 | # build intermediates: 12 | /vsbuild/Release 13 | /vsbuild/ReleaseTest 14 | /vsbuild/Debug 15 | /vsbuild/DebugTest 16 | /vsbuild32 17 | /vsbuild64 18 | CMakeUserPresets.json 19 | 20 | # test "side effects" 21 | /test/temp 22 | 23 | # VS generated files: 24 | .vs 25 | *.aps 26 | *.vcxproj.user 27 | msbuild.log 28 | 29 | /stderr.log 30 | /stderr_32.log 31 | /stdout.log 32 | /stdout_32.log 33 | 34 | .vscode 35 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-merge-conflict 8 | - id: check-case-conflict 9 | - repo: https://github.com/pre-commit/mirrors-clang-format 10 | rev: v19.1.5 11 | hooks: 12 | - id: clang-format 13 | 'types_or': [c++, c] 14 | 15 | ci: 16 | autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks." 17 | autofix_prs: true 18 | autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate." 19 | autoupdate_schedule: quarterly 20 | submodules: false 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | include(CMakePackageConfigHelpers) 4 | 5 | project(usvfs) 6 | 7 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 8 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 9 | set(CMAKE_CXX_STANDARD 20) 10 | 11 | set(USVFS_BINDIR ${CMAKE_CURRENT_LIST_DIR}/bin) 12 | set(USVFS_LIBDIR ${CMAKE_CURRENT_LIST_DIR}/lib) 13 | 14 | if (MSVC) 15 | # /Zi generate PDBs 16 | # /Gy enable function-level linking 17 | # /Oi enable intrinsic function 18 | add_compile_options("$<$>:/Zi;/Gy;/Oi>") 19 | 20 | # /OPT:ICF enable COMDAT folding 21 | # /DEBUG:FULL generate debug info (PDB) 22 | # /OPT:REF enable references (PDB) 23 | add_link_options("$<$>:/OPT:ICF;/DEBUG:FULL;/OPT:REF>") 24 | endif() 25 | 26 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 27 | set(ARCH_POSTFIX _x86) 28 | else() 29 | set(ARCH_POSTFIX _x64) 30 | endif() 31 | 32 | add_subdirectory(src/shared) 33 | 34 | add_subdirectory(src/thooklib) 35 | add_subdirectory(src/tinjectlib) 36 | add_subdirectory(src/usvfs_helper) 37 | 38 | add_subdirectory(src/usvfs_dll) 39 | add_subdirectory(src/usvfs_proxy) 40 | 41 | configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake.in 42 | "${CMAKE_CURRENT_BINARY_DIR}/usvfsConfig.cmake" 43 | INSTALL_DESTINATION "lib/cmake/usvfs" 44 | NO_SET_AND_CHECK_MACRO 45 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 46 | ) 47 | 48 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/usvfs/usvfs_version.h" usvfs_version) 49 | string(REGEX MATCH "USVFS_VERSION_MAJOR ([0-9]*)" _ ${usvfs_version}) 50 | set(usvfs_version_major ${CMAKE_MATCH_1}) 51 | string(REGEX MATCH "USVFS_VERSION_MINOR ([0-9]*)" _ ${usvfs_version}) 52 | set(usvfs_version_minor ${CMAKE_MATCH_1}) 53 | string(REGEX MATCH "USVFS_VERSION_BUILD ([0-9]*)" _ ${usvfs_version}) 54 | set(usvfs_version_build ${CMAKE_MATCH_1}) 55 | string(REGEX MATCH "USVFS_VERSION_REVISION ([0-9]*)" _ ${usvfs_version}) 56 | set(usvfs_version_revision ${CMAKE_MATCH_1}) 57 | 58 | write_basic_package_version_file( 59 | "${CMAKE_CURRENT_BINARY_DIR}/usvfsConfigVersion.cmake" 60 | VERSION "${usvfs_version_major}.${usvfs_version_minor}.${usvfs_version_build}.${usvfs_version_revision}" 61 | COMPATIBILITY AnyNewerVersion 62 | ARCH_INDEPENDENT 63 | ) 64 | 65 | install(FILES 66 | ${CMAKE_CURRENT_BINARY_DIR}/usvfsConfig.cmake 67 | ${CMAKE_CURRENT_BINARY_DIR}/usvfsConfigVersion.cmake 68 | DESTINATION lib/cmake/usvfs 69 | ) 70 | 71 | if (BUILD_TESTING) 72 | enable_testing() 73 | set(USVFS_TEST_BINDIR ${CMAKE_CURRENT_LIST_DIR}/test/bin) 74 | add_subdirectory(test) 75 | endif() 76 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurePresets": [ 3 | { 4 | "cacheVariables": { 5 | "BUILD_TESTING": { 6 | "type": "BOOL", 7 | "value": "ON" 8 | } 9 | }, 10 | "errors": { 11 | "deprecated": true 12 | }, 13 | "hidden": true, 14 | "name": "cmake-dev", 15 | "warnings": { 16 | "deprecated": true, 17 | "dev": true 18 | } 19 | }, 20 | { 21 | "cacheVariables": { 22 | "VCPKG_MANIFEST_NO_DEFAULT_FEATURES": { 23 | "type": "BOOL", 24 | "value": "ON" 25 | } 26 | }, 27 | "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", 28 | "hidden": true, 29 | "name": "vcpkg" 30 | }, 31 | { 32 | "cacheVariables": { 33 | "VCPKG_MANIFEST_FEATURES": { 34 | "type": "STRING", 35 | "value": "testing" 36 | } 37 | }, 38 | "hidden": true, 39 | "inherits": ["vcpkg"], 40 | "name": "vcpkg-dev" 41 | }, 42 | { 43 | "binaryDir": "${sourceDir}/vsbuild64", 44 | "architecture": { 45 | "strategy": "set", 46 | "value": "x64" 47 | }, 48 | "cacheVariables": { 49 | "VCPKG_TARGET_TRIPLET": { 50 | "type": "STRING", 51 | "value": "x64-windows-static-md" 52 | } 53 | }, 54 | "hidden": true, 55 | "name": "windows-x64" 56 | }, 57 | { 58 | "binaryDir": "${sourceDir}/vsbuild32", 59 | "architecture": { 60 | "strategy": "set", 61 | "value": "Win32" 62 | }, 63 | "cacheVariables": { 64 | "VCPKG_TARGET_TRIPLET": { 65 | "type": "STRING", 66 | "value": "x86-windows-static-md" 67 | } 68 | }, 69 | "hidden": true, 70 | "name": "windows-x86" 71 | }, 72 | { 73 | "cacheVariables": { 74 | "CMAKE_CXX_FLAGS": "/EHsc /MP /W4" 75 | }, 76 | "generator": "Visual Studio 17 2022", 77 | "inherits": ["cmake-dev", "vcpkg-dev"], 78 | "hidden": true, 79 | "name": "vs2022-windows", 80 | "toolset": "v143" 81 | }, 82 | { 83 | "inherits": ["vs2022-windows", "windows-x64"], 84 | "name": "vs2022-windows-x64" 85 | }, 86 | { 87 | "inherits": ["vs2022-windows", "windows-x86"], 88 | "name": "vs2022-windows-x86" 89 | } 90 | ], 91 | "buildPresets": [ 92 | { 93 | "name": "vs2022-windows-x64", 94 | "configurePreset": "vs2022-windows-x64" 95 | }, 96 | { 97 | "name": "vs2022-windows-x86", 98 | "configurePreset": "vs2022-windows-x86" 99 | } 100 | ], 101 | "version": 3 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # usvfs 2 | 3 | [![License](http://img.shields.io/:license-gpl-blue.svg)](http://www.gnu.org/licenses/gpl-3.0.en.html) 4 | [![Build status](https://github.com/github/docs/actions/workflows/build.yml/badge.svg)](https://github.com/ModOrganizer2/usvfs/actions) 5 | 6 | USVFS (short for User Space Virtual File System) aims to allow windows applications to create file or directory links that 7 | are visible to only a select set of processes. 8 | It does so by using api hooking to fool file access functions into discovering/opening files that are in fact somewhere else 9 | 10 | ## Comparison to symbolic links 11 | 12 | The following is based on the final goal for usvfs and doesn't necessary reflect the current development state. 13 | 14 | Unlike symbolic file links provided by NTFS 15 | - links aren't visible to all applications but only to those the caller chooses 16 | - links disappear when the "session ends" 17 | - doesn't require write access to the link destination 18 | - doesn't require administrator rights (neither for installation nor for use) 19 | - links are filesystem independent so you can create links on fat32 drives, read-only media and network drives 20 | - can link multiple directories on top of a single destination (overlaying) 21 | - can also "virtually" unlink files, thus make them invisible to processes or replace existing files 22 | 23 | There are of course drawbacks 24 | - will always impose a memory and cpu overhead though hopefully those will be marginal 25 | - becomes active only during the initialization phase of each process so it may not be active at the time dependent dlls are loaded 26 | - introduces a new source of bugs that can cause hard to diagnose problems in affected processes 27 | - may rub antivirus software the wrong way as the used techniques are similar to what some malware does. 28 | 29 | ## Current state 30 | 31 | usvfs is work in progress and should be considered in alpha state. 32 | It is a core component of Mod Organizer v2 33 | and thus receives serious real world testing 34 | 35 | ## Building 36 | 37 | You will `cmake`, Python 3+ and `vcpkg` to build USVFS: 38 | 39 | ```pwsh 40 | cmake -B build32 -A Win32 "-DCMAKE_TOOLCHAIN_FILE=path\to\vcpkg\scripts\buildsystems\vcpkg.cmake" -DBUILD_USVFS_TESTS=TRUE 41 | cmake --build build32 --config Release 42 | 43 | cmake -B build64 -A Win64 "-DCMAKE_TOOLCHAIN_FILE=path\to\vcpkg\scripts\buildsystems\vcpkg.cmake" -DBUILD_USVFS_TESTS=TRUE 44 | cmake --build build64 --config Release 45 | ``` 46 | 47 | ## License 48 | 49 | usvfs is currently licensed under the GPLv3 but this may change in the future. 50 | 51 | ## Contributing 52 | 53 | Contributions are very welcome but please notice that since I'm still undecided on 54 | licensing I have to ask all contributors to agree to future licensing changes. 55 | -------------------------------------------------------------------------------- /cmake/config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include ( "${CMAKE_CURRENT_LIST_DIR}/usvfs_x86Targets.cmake" ) 4 | include ( "${CMAKE_CURRENT_LIST_DIR}/usvfs_x64Targets.cmake" ) 5 | 6 | add_library(usvfs::usvfs ALIAS usvfs_x64::usvfs_dll) 7 | -------------------------------------------------------------------------------- /include/usvfs/dllimport.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #ifndef DLLEXPORT 24 | 25 | #ifdef BUILDING_USVFS_DLL 26 | #define DLLEXPORT __declspec(dllexport) 27 | #else 28 | #define DLLEXPORT __declspec(dllimport) 29 | #endif 30 | 31 | #endif DLLEXPORT 32 | -------------------------------------------------------------------------------- /include/usvfs/logging.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | enum class LogLevel : uint8_t 24 | { 25 | Debug, 26 | Info, 27 | Warning, 28 | Error 29 | }; 30 | -------------------------------------------------------------------------------- /include/usvfs/sharedparameters.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "dllimport.h" 4 | #include "usvfsparameters.h" 5 | #include 6 | 7 | namespace usvfs 8 | { 9 | 10 | class ForcedLibrary 11 | { 12 | public: 13 | ForcedLibrary(const std::string& processName, const std::string& libraryPath, 14 | const shared::VoidAllocatorT& allocator); 15 | 16 | std::string processName() const; 17 | std::string libraryPath() const; 18 | 19 | private: 20 | shared::StringT m_processName; 21 | shared::StringT m_libraryPath; 22 | }; 23 | 24 | class DLLEXPORT SharedParameters 25 | { 26 | public: 27 | SharedParameters() = delete; 28 | SharedParameters(const SharedParameters& reference) = delete; 29 | SharedParameters& operator=(const SharedParameters& reference) = delete; 30 | 31 | SharedParameters(const usvfsParameters& reference, 32 | const shared::VoidAllocatorT& allocator); 33 | 34 | usvfsParameters makeLocal() const; 35 | 36 | std::string instanceName() const; 37 | std::string currentSHMName() const; 38 | std::string currentInverseSHMName() const; 39 | void setSHMNames(const std::string& current, const std::string& inverse); 40 | 41 | void setDebugParameters(LogLevel level, CrashDumpsType dumpType, 42 | const std::string& dumpPath, 43 | std::chrono::milliseconds delayProcess); 44 | 45 | std::size_t userConnected(); 46 | std::size_t userDisconnected(); 47 | std::size_t userCount(); 48 | 49 | std::size_t registeredProcessCount() const; 50 | std::vector registeredProcesses() const; 51 | void registerProcess(DWORD pid); 52 | void unregisterProcess(DWORD pid); 53 | 54 | void blacklistExecutable(const std::string& name); 55 | void clearExecutableBlacklist(); 56 | bool executableBlacklisted(const std::string& app, const std::string& cmd) const; 57 | 58 | void addSkipFileSuffix(const std::string& fileSuffix); 59 | void clearSkipFileSuffixes(); 60 | std::vector skipFileSuffixes() const; 61 | 62 | void addSkipDirectory(const std::string& directory); 63 | void clearSkipDirectories(); 64 | std::vector skipDirectories() const; 65 | 66 | void addForcedLibrary(const std::string& process, const std::string& path); 67 | std::vector forcedLibraries(const std::string& processName); 68 | void clearForcedLibraries(); 69 | 70 | private: 71 | using StringAllocatorT = shared::VoidAllocatorT::rebind::other; 72 | 73 | using DWORDAllocatorT = shared::VoidAllocatorT::rebind::other; 74 | 75 | using ForcedLibraryAllocatorT = shared::VoidAllocatorT::rebind::other; 76 | 77 | using ProcessBlacklist = 78 | boost::container::flat_set, 79 | StringAllocatorT>; 80 | 81 | using ProcessList = 82 | boost::container::flat_set, DWORDAllocatorT>; 83 | 84 | using FileSuffixSkipList = 85 | boost::container::flat_set, 86 | StringAllocatorT>; 87 | 88 | using DirectorySkipList = 89 | boost::container::flat_set, 90 | StringAllocatorT>; 91 | 92 | using ForcedLibraries = 93 | boost::container::slist; 94 | 95 | mutable bi::interprocess_mutex m_mutex; 96 | shared::StringT m_instanceName; 97 | shared::StringT m_currentSHMName; 98 | shared::StringT m_currentInverseSHMName; 99 | bool m_debugMode; 100 | LogLevel m_logLevel; 101 | CrashDumpsType m_crashDumpsType; 102 | shared::StringT m_crashDumpsPath; 103 | std::chrono::milliseconds m_delayProcess; 104 | uint32_t m_userCount; 105 | ProcessBlacklist m_processBlacklist; 106 | ProcessList m_processList; 107 | FileSuffixSkipList m_fileSuffixSkipList; 108 | DirectorySkipList m_directorySkipList; 109 | ForcedLibraries m_forcedLibraries; 110 | }; 111 | 112 | } // namespace usvfs 113 | -------------------------------------------------------------------------------- /include/usvfs/usvfs_version.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define USVFS_VERSION_MAJOR 0 4 | #define USVFS_VERSION_MINOR 5 5 | #define USVFS_VERSION_BUILD 7 6 | #define USVFS_VERSION_REVISION 2 7 | 8 | #define USVFS_BUILD_STRING "" 9 | #define USVFS_BUILD_WSTRING L"" 10 | 11 | #ifndef USVFS_STRINGIFY 12 | #define USVFS_STRINGIFY(x) USVFS_STRINGIFY_HELPER(x) 13 | #define USVFS_STRINGIFY_HELPER(x) #x 14 | #endif 15 | 16 | #ifndef USVFS_STRINGIFYW 17 | #define USVFS_STRINGIFYW(x) USVFS_STRINGIFYW_HELPER(x) 18 | #define USVFS_STRINGIFYW_HELPER(x) L## #x 19 | #endif 20 | 21 | #define USVFS_VERSION_STRING \ 22 | USVFS_STRINGIFY(USVFS_VERSION_MAJOR) \ 23 | "." USVFS_STRINGIFY(USVFS_VERSION_MINOR) "." USVFS_STRINGIFY( \ 24 | USVFS_VERSION_BUILD) "." USVFS_STRINGIFY(USVFS_VERSION_REVISION) \ 25 | USVFS_BUILD_STRING 26 | #define USVFS_VERSION_WSTRING \ 27 | USVFS_STRINGIFYW(USVFS_VERSION_MAJOR) \ 28 | L"." USVFS_STRINGIFYW(USVFS_VERSION_MINOR) L"." USVFS_STRINGIFYW( \ 29 | USVFS_VERSION_BUILD) L"." USVFS_STRINGIFYW(USVFS_VERSION_REVISION) \ 30 | USVFS_BUILD_WSTRING 31 | -------------------------------------------------------------------------------- /include/usvfs/usvfsparameters.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "dllimport.h" 24 | #include "logging.h" 25 | #include 26 | 27 | enum class CrashDumpsType : uint8_t 28 | { 29 | None, 30 | Mini, 31 | Data, 32 | Full 33 | }; 34 | 35 | extern "C" 36 | { 37 | 38 | // deprecated, use usvfsParameters and usvfsCreateParameters() 39 | // 40 | struct USVFSParameters 41 | { 42 | char instanceName[65]; 43 | char currentSHMName[65]; 44 | char currentInverseSHMName[65]; 45 | bool debugMode{false}; 46 | LogLevel logLevel{LogLevel::Debug}; 47 | CrashDumpsType crashDumpsType{CrashDumpsType::None}; 48 | char crashDumpsPath[260]; 49 | }; 50 | 51 | struct usvfsParameters; 52 | 53 | DLLEXPORT usvfsParameters* usvfsCreateParameters(); 54 | DLLEXPORT usvfsParameters* usvfsDupeParameters(usvfsParameters* p); 55 | DLLEXPORT void usvfsCopyParameters(const usvfsParameters* source, 56 | usvfsParameters* dest); 57 | DLLEXPORT void usvfsFreeParameters(usvfsParameters* p); 58 | 59 | DLLEXPORT void usvfsSetInstanceName(usvfsParameters* p, const char* name); 60 | DLLEXPORT void usvfsSetDebugMode(usvfsParameters* p, BOOL debugMode); 61 | DLLEXPORT void usvfsSetLogLevel(usvfsParameters* p, LogLevel level); 62 | DLLEXPORT void usvfsSetCrashDumpType(usvfsParameters* p, CrashDumpsType type); 63 | DLLEXPORT void usvfsSetCrashDumpPath(usvfsParameters* p, const char* path); 64 | DLLEXPORT void usvfsSetProcessDelay(usvfsParameters* p, int milliseconds); 65 | 66 | DLLEXPORT const char* usvfsLogLevelToString(LogLevel lv); 67 | DLLEXPORT const char* usvfsCrashDumpTypeToString(CrashDumpsType t); 68 | } 69 | -------------------------------------------------------------------------------- /include/usvfs/usvfsparametersprivate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "usvfsparameters.h" 3 | 4 | struct usvfsParameters 5 | { 6 | char instanceName[65]; 7 | char currentSHMName[65]; 8 | char currentInverseSHMName[65]; 9 | bool debugMode; 10 | LogLevel logLevel{LogLevel::Debug}; 11 | CrashDumpsType crashDumpsType{CrashDumpsType::None}; 12 | char crashDumpsPath[260]; 13 | int delayProcessMs; 14 | 15 | usvfsParameters(); 16 | usvfsParameters(const usvfsParameters&) = default; 17 | usvfsParameters& operator=(const usvfsParameters&) = default; 18 | 19 | usvfsParameters(const char* instanceName, const char* currentSHMName, 20 | const char* currentInverseSHMName, bool debugMode, LogLevel logLevel, 21 | CrashDumpsType crashDumpsType, const char* crashDumpsPath, 22 | int delayProcessMs); 23 | 24 | usvfsParameters(const USVFSParameters& oldParams); 25 | 26 | void setInstanceName(const char* name); 27 | void setDebugMode(bool debugMode); 28 | void setLogLevel(LogLevel level); 29 | void setCrashDumpType(CrashDumpsType type); 30 | void setCrashDumpPath(const char* path); 31 | void setProcessDelay(int milliseconds); 32 | }; 33 | -------------------------------------------------------------------------------- /licenses/asmjit.txt: -------------------------------------------------------------------------------- 1 | AsmJit - Complete x86/x64 JIT and Remote Assembler for C++ 2 | Copyright (c) 2008-2015, Petr Kobalicek 3 | 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | 8 | Permission is granted to anyone to use this software for any purpose, 9 | including commercial applications, and to alter it and redistribute it 10 | freely, subject to the following restrictions: 11 | 12 | 1. The origin of this software must not be misrepresented; you must not 13 | claim that you wrote the original software. If you use this software 14 | in a product, an acknowledgment in the product documentation would be 15 | appreciated but is not required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 3. This notice may not be removed or altered from any source distribution. 19 | -------------------------------------------------------------------------------- /licenses/boost.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /licenses/cppformat.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2015, Victor Zverovich 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /licenses/googletest.txt: -------------------------------------------------------------------------------- 1 | Copyright 2008, Google Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /licenses/spdlog.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Gabi Melman. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /licenses/udis86.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002-2012, Vivek Thampi 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /src/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(Boost CONFIG REQUIRED COMPONENTS algorithm interprocess filesystem) 4 | find_package(spdlog CONFIG REQUIRED) 5 | 6 | file(GLOB sources "*.cpp" "*.h") 7 | source_group("" FILES ${sources}) 8 | 9 | add_library(shared STATIC ${sources}) 10 | target_link_libraries(shared 11 | PUBLIC 12 | Boost::algorithm Boost::interprocess comsuppw 13 | Boost::filesystem spdlog::spdlog_header_only) 14 | target_include_directories(shared 15 | PUBLIC ${PROJECT_SOURCE_DIR}/include/usvfs ${CMAKE_CURRENT_SOURCE_DIR}) 16 | target_precompile_headers(shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) 17 | target_compile_options(shared PUBLIC /FI"pch.h") 18 | target_compile_definitions(shared PUBLIC 19 | SPDLOG_USE_STD_FORMAT SPDLOG_NO_NAME SPDLOG_NO_REGISTRY_MUTEX 20 | BOOST_INTERPROCESS_SHARED_DIR_FUNC BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE 21 | NOMINMAX _WINDOWS _UNICODE UNICODE 22 | _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR 23 | ) 24 | -------------------------------------------------------------------------------- /src/shared/addrtools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "windows_sane.h" 24 | 25 | namespace usvfs::shared 26 | { 27 | 28 | #ifdef _M_AMD64 29 | typedef DWORD64 REGWORD; 30 | #elif _M_IX86 31 | typedef DWORD REGWORD; 32 | #endif 33 | 34 | inline LPVOID AddrAdd(LPVOID address, size_t offset) 35 | { 36 | return reinterpret_cast(reinterpret_cast(address) + offset); 37 | } 38 | 39 | inline std::ptrdiff_t AddrDiff(LPVOID lhs, LPVOID rhs) 40 | { 41 | return reinterpret_cast(lhs) - reinterpret_cast(rhs); 42 | } 43 | 44 | // implicitly cast pointer to void*, from there cast to target type. 45 | // This is supposed to be safer than directly reinterpret-casting 46 | template 47 | inline T void_ptr_cast(void* ptr) 48 | { 49 | return reinterpret_cast(ptr); 50 | } 51 | 52 | template <> 53 | inline int64_t void_ptr_cast(void* ptr) 54 | { 55 | return reinterpret_cast(ptr); 56 | } 57 | 58 | template <> 59 | inline uint64_t void_ptr_cast(void* ptr) 60 | { 61 | return reinterpret_cast(ptr); 62 | } 63 | 64 | } // namespace usvfs::shared 65 | -------------------------------------------------------------------------------- /src/shared/directory_tree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "directory_tree.h" 22 | #include "tree_container.h" 23 | 24 | namespace usvfs::shared 25 | { 26 | 27 | fs::path::iterator nextIter(const fs::path::iterator& iter, 28 | const fs::path::iterator& end) 29 | { 30 | fs::path::iterator next = iter; 31 | advanceIter(next, end); 32 | return next; 33 | } 34 | 35 | void advanceIter(fs::path::iterator& iter, const fs::path::iterator& end) 36 | { 37 | ++iter; 38 | while (iter != end && (iter->wstring() == L"/" || iter->wstring() == L"\\" || 39 | iter->wstring() == L".")) 40 | ++iter; 41 | } 42 | 43 | } // namespace usvfs::shared 44 | -------------------------------------------------------------------------------- /src/shared/exceptionex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "exceptionex.h" 22 | #include "winapi.h" 23 | #include 24 | 25 | namespace usvfs::shared 26 | { 27 | 28 | std::string windows_error::constructMessage(const std::string& input, int inErrorCode) 29 | { 30 | std::ostringstream finalMessage; 31 | finalMessage << input; 32 | 33 | LPSTR buffer = nullptr; 34 | 35 | DWORD errorCode = inErrorCode != -1 ? inErrorCode : GetLastError(); 36 | 37 | // TODO: the message is not english? 38 | if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 39 | nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 40 | (LPSTR)&buffer, 0, nullptr) == 0) { 41 | finalMessage << " (errorcode " << errorCode << ")"; 42 | } else { 43 | LPSTR lastChar = buffer + strlen(buffer) - 2; 44 | *lastChar = '\0'; 45 | finalMessage << " (" << buffer << " [" << errorCode << "])"; 46 | LocalFree(buffer); // allocated by FormatMessage 47 | } 48 | 49 | SetLastError( 50 | errorCode); // restore error code because FormatMessage might have modified it 51 | return finalMessage.str(); 52 | } 53 | 54 | } // namespace usvfs::shared 55 | 56 | void logExtInfo(const std::exception& e, LogLevel logLevel) 57 | { 58 | std::string content; 59 | 60 | if (const std::string* msg = boost::get_error_info(e)) { 61 | content = *msg; 62 | } 63 | 64 | if (const DWORD* errorCode = boost::get_error_info(e)) { 65 | content = std::string("error: ") + winapi::ex::ansi::errorString(*errorCode); 66 | } 67 | 68 | switch (logLevel) { 69 | case LogLevel::Debug: 70 | spdlog::get("usvfs")->debug(content); 71 | break; 72 | case LogLevel::Info: 73 | spdlog::get("usvfs")->info(content); 74 | break; 75 | case LogLevel::Warning: 76 | spdlog::get("usvfs")->warn(content); 77 | break; 78 | case LogLevel::Error: 79 | spdlog::get("usvfs")->error(content); 80 | break; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/shared/exceptionex.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "logging.h" 24 | #include 25 | #include 26 | 27 | typedef boost::error_info ex_win_errcode; 28 | typedef boost::error_info ex_msg; 29 | 30 | struct std_boost_exception : virtual boost::exception, virtual std::exception 31 | { 32 | const char* what() const noexcept override 33 | { 34 | return boost::diagnostic_information_what(*this); 35 | } 36 | }; 37 | 38 | struct incompatibility_error : std_boost_exception 39 | {}; 40 | struct usage_error : std_boost_exception 41 | {}; 42 | struct data_error : std_boost_exception 43 | {}; 44 | struct file_not_found_error : std_boost_exception 45 | {}; 46 | struct timeout_error : std_boost_exception 47 | {}; 48 | struct unknown_error : std_boost_exception 49 | {}; 50 | struct node_missing_error : std_boost_exception 51 | {}; 52 | 53 | #define USVFS_THROW_EXCEPTION(x) BOOST_THROW_EXCEPTION(x) 54 | 55 | void logExtInfo(const std::exception& e, LogLevel logLevel = LogLevel::Warning); 56 | 57 | namespace usvfs::shared 58 | { 59 | 60 | class windows_error : public std::runtime_error 61 | { 62 | public: 63 | windows_error(const std::string& message, int errorcode = GetLastError()) 64 | : runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode) 65 | {} 66 | 67 | int getErrorCode() const { return m_ErrorCode; } 68 | 69 | private: 70 | int m_ErrorCode; 71 | 72 | std::string constructMessage(const std::string& input, int errorcode); 73 | }; 74 | 75 | class guard 76 | { 77 | public: 78 | explicit guard(std::function f) : m_f(std::move(f)) {} 79 | 80 | ~guard() 81 | { 82 | if (m_f) { 83 | m_f(); 84 | } 85 | } 86 | 87 | guard(const guard&); 88 | guard& operator=(const guard&); 89 | 90 | private: 91 | std::function m_f; 92 | }; 93 | 94 | } // namespace usvfs::shared 95 | 96 | #define CONCATENATE_DIRECT(s1, s2) s1##s2 97 | #define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2) 98 | #define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__) 99 | #define ON_BLOCK_EXIT(f) usvfs::shared::guard ANONYMOUS_VARIABLE(guard)(f) 100 | -------------------------------------------------------------------------------- /src/shared/formatters.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2024. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | #include "ntdll_declarations.h" 27 | 28 | // formatters for standard types 29 | 30 | namespace usvfs::log 31 | { 32 | std::string to_string(LPCWSTR value); 33 | std::string to_string(PCUNICODE_STRING value); 34 | } // namespace usvfs::log 35 | 36 | template 37 | requires std::is_enum_v 38 | struct std::formatter : std::formatter, CharT> 39 | { 40 | template 41 | FmtContext::iterator format(Enum v, FmtContext& ctx) const 42 | { 43 | return std::formatter, CharT>::format( 44 | static_cast>(v), ctx); 45 | } 46 | }; 47 | 48 | template <> 49 | struct std::formatter : std::formatter 50 | { 51 | template 52 | FmtContext::iterator format(LPCWSTR v, FmtContext& ctx) const 53 | { 54 | return std::formatter::format(usvfs::log::to_string(v), ctx); 55 | } 56 | }; 57 | 58 | template <> 59 | struct std::formatter : std::formatter 60 | { 61 | template 62 | FmtContext::iterator format(const std::wstring& v, FmtContext& ctx) const 63 | { 64 | return std::formatter::format(v.c_str(), ctx); 65 | } 66 | }; 67 | 68 | template <> 69 | struct std::formatter : std::formatter 70 | { 71 | template 72 | FmtContext::iterator format(PCUNICODE_STRING v, FmtContext& ctx) const 73 | { 74 | return std::formatter::format(usvfs::log::to_string(v), ctx); 75 | } 76 | }; 77 | 78 | template <> 79 | struct std::formatter : std::formatter 80 | { 81 | template 82 | FmtContext::iterator format(UNICODE_STRING v, FmtContext& ctx) const 83 | { 84 | return std::formatter::format(usvfs::log::to_string(&v), ctx); 85 | } 86 | }; 87 | 88 | template 89 | requires(std::is_pointer_v && !std::is_same_v && 90 | !std::is_same_v && !std::is_same_v && 91 | !std::is_same_v) 92 | struct std::formatter : std::formatter 93 | { 94 | template 95 | FmtContext::iterator format(Pointer v, FmtContext& ctx) const 96 | { 97 | return std::formatter::format(v, ctx); 98 | } 99 | }; 100 | 101 | namespace usvfs::log 102 | { 103 | 104 | /** 105 | * a small helper class to wrap any object. The whole point is to give us a way 106 | * to ensure our own operator<< is used in addParam calls 107 | */ 108 | template 109 | class Wrap 110 | { 111 | public: 112 | explicit Wrap(const T& data) : m_Data(data) {} 113 | Wrap(Wrap&& reference) : m_Data(std::move(reference.m_Data)) {} 114 | 115 | Wrap(const Wrap& reference) = delete; 116 | Wrap& operator=(const Wrap& reference) = delete; 117 | 118 | private: 119 | friend struct ::std::formatter, char>; 120 | const T& m_Data; 121 | }; 122 | 123 | template 124 | Wrap wrap(const T& data) 125 | { 126 | return Wrap(data); 127 | } 128 | 129 | } // namespace usvfs::log 130 | 131 | template <> 132 | struct std::formatter, char> : std::formatter 133 | { 134 | template 135 | FmtContext::iterator format(const usvfs::log::Wrap& v, FmtContext& ctx) const 136 | { 137 | return std::format_to(ctx.out(), "{:x}", v.m_Data); 138 | } 139 | }; 140 | 141 | template <> 142 | struct std::formatter, char> : std::formatter 143 | { 144 | template 145 | FmtContext::iterator format(const usvfs::log::Wrap& v, 146 | FmtContext& ctx) const 147 | { 148 | switch (v.m_Data) { 149 | case 0x00000000: 150 | return std::format_to(ctx.out(), "ok"); 151 | case 0xC0000022: 152 | return std::format_to(ctx.out(), "access denied"); 153 | case 0xC0000035: 154 | return std::format_to(ctx.out(), "exists already"); 155 | } 156 | return std::format_to(ctx.out(), "err {:x}", v.m_Data); 157 | } 158 | }; 159 | -------------------------------------------------------------------------------- /src/shared/loghelpers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include 22 | 23 | #include "formatters.h" 24 | #include "loghelpers.h" 25 | #include "stringcast.h" 26 | #include "stringutils.h" 27 | 28 | namespace ush = usvfs::shared; 29 | 30 | namespace usvfs::log 31 | { 32 | 33 | // this is declared in formatters but is defined here to avoid a whole .cpp 34 | // file for two small functions 35 | // 36 | // comments from old code, taken as is: 37 | // 38 | // TODO this does not correctly support surrogate pairs since the size used here 39 | // is the number of 16-bit characters in the buffer whereas toNarrow expects the 40 | // actual number of characters. It will always underestimate though, so worst 41 | // case scenario we truncate the string 42 | // 43 | std::string to_string(LPCWSTR value) 44 | { 45 | if (value == nullptr) { 46 | return ""; 47 | } 48 | try { 49 | return ush::string_cast(value, ush::CodePage::UTF8); 50 | } catch (const std::exception& e) { 51 | return std::format("", e.what()); 52 | } 53 | } 54 | std::string to_string(PCUNICODE_STRING value) 55 | { 56 | try { 57 | return ush::string_cast(value->Buffer, ush::CodePage::UTF8, 58 | value->Length / sizeof(WCHAR)); 59 | } catch (const std::exception& e) { 60 | return std::format("", e.what()); 61 | } 62 | } 63 | 64 | spdlog::level::level_enum ConvertLogLevel(LogLevel level) 65 | { 66 | switch (level) { 67 | case LogLevel::Debug: 68 | return spdlog::level::debug; 69 | case LogLevel::Info: 70 | return spdlog::level::info; 71 | case LogLevel::Warning: 72 | return spdlog::level::warn; 73 | case LogLevel::Error: 74 | return spdlog::level::err; 75 | default: 76 | return spdlog::level::debug; 77 | } 78 | } 79 | 80 | LogLevel ConvertLogLevel(spdlog::level::level_enum level) 81 | { 82 | switch (level) { 83 | case spdlog::level::debug: 84 | return LogLevel::Debug; 85 | case spdlog::level::info: 86 | return LogLevel::Info; 87 | case spdlog::level::warn: 88 | return LogLevel::Warning; 89 | case spdlog::level::err: 90 | return LogLevel::Error; 91 | default: 92 | return LogLevel::Debug; 93 | } 94 | } 95 | 96 | } // namespace usvfs::log 97 | -------------------------------------------------------------------------------- /src/shared/loghelpers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "dllimport.h" 24 | #include "formatters.h" 25 | #include "ntdll_declarations.h" 26 | #include "shmlogger.h" 27 | #include "stringutils.h" 28 | 29 | namespace usvfs::log 30 | { 31 | 32 | enum class DisplayStyle : uint8_t 33 | { 34 | Hex = 0x01 35 | }; 36 | 37 | class CallLoggerDummy 38 | { 39 | public: 40 | template 41 | CallLoggerDummy& addParam(const char*, const T&, uint8_t style = 0) 42 | { 43 | return *this; 44 | } 45 | }; 46 | 47 | class CallLogger 48 | { 49 | public: 50 | explicit CallLogger(const char* function) 51 | { 52 | const char* namespaceend = strrchr(function, ':'); 53 | 54 | if (namespaceend != nullptr) { 55 | function = namespaceend + 1; 56 | } 57 | 58 | m_Message = function; 59 | } 60 | 61 | ~CallLogger() 62 | { 63 | try { 64 | static std::shared_ptr log = spdlog::get("hooks"); 65 | log->debug("{}", m_Message); 66 | } catch (...) { 67 | // suppress all exceptions in destructor 68 | } 69 | } 70 | 71 | template 72 | CallLogger& addParam(const char* name, const T& value, uint8_t style = 0); 73 | 74 | private: 75 | std::string m_Message; 76 | }; 77 | 78 | template 79 | CallLogger& CallLogger::addParam(const char* name, const T& value, uint8_t style) 80 | { 81 | static bool enabled = spdlog::get("hooks")->should_log(spdlog::level::debug); 82 | typedef std::underlying_type::type DSType; 83 | 84 | if (enabled) { 85 | if constexpr (std::is_pointer_v) { 86 | if (value == nullptr) { 87 | std::format_to(std::back_inserter(m_Message), "[{}=]", name); 88 | } else { 89 | std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value); 90 | } 91 | } else if constexpr (std::is_integral_v) { 92 | if (style & static_cast(DisplayStyle::Hex)) { 93 | std::format_to(std::back_inserter(m_Message), "[{}={:x}]", name, value); 94 | } else { 95 | std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value); 96 | } 97 | } else { 98 | std::format_to(std::back_inserter(m_Message), "[{}={}]", name, value); 99 | } 100 | } 101 | 102 | return *this; 103 | } 104 | 105 | spdlog::level::level_enum ConvertLogLevel(LogLevel level); 106 | LogLevel ConvertLogLevel(spdlog::level::level_enum level); 107 | 108 | } // namespace usvfs::log 109 | 110 | // prefer the short variant of the function name, without signature. 111 | // Fall back to the portable boost macro 112 | #ifdef __FUNCTION__ 113 | #define __MYFUNC__ __FUNCTION__ 114 | #else 115 | #define __MYFUNC__ BOOST_CURRENT_FUNCTION 116 | #endif 117 | 118 | #define LOG_CALL() usvfs::log::CallLogger(__MYFUNC__) 119 | 120 | #define PARAM(val) addParam(#val, val) 121 | #define PARAMHEX(val) \ 122 | addParam(#val, val, static_cast(usvfs::log::DisplayStyle::Hex)) 123 | #define PARAMWRAP(val) addParam(#val, usvfs::log::wrap(val)) 124 | -------------------------------------------------------------------------------- /src/shared/ntdll_declarations.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "ntdll_declarations.h" 22 | #include 23 | 24 | #define LOAD_EXT(mod, name) \ 25 | name = reinterpret_cast(::GetProcAddress(mod, #name)); \ 26 | assert(name != nullptr) 27 | 28 | NtQueryDirectoryFile_type NtQueryDirectoryFile; 29 | NtQueryDirectoryFileEx_type NtQueryDirectoryFileEx; 30 | NtQueryFullAttributesFile_type NtQueryFullAttributesFile; 31 | NtQueryAttributesFile_type NtQueryAttributesFile; 32 | NtQueryObject_type NtQueryObject; 33 | NtQueryInformationFile_type NtQueryInformationFile; 34 | NtQueryInformationByName_type NtQueryInformationByName; 35 | NtOpenFile_type NtOpenFile; 36 | NtCreateFile_type NtCreateFile; 37 | NtClose_type NtClose; 38 | RtlDoesFileExists_U_type RtlDoesFileExists_U; 39 | RtlDosPathNameToRelativeNtPathName_U_WithStatus_type 40 | RtlDosPathNameToRelativeNtPathName_U_WithStatus; 41 | RtlReleaseRelativeName_type RtlReleaseRelativeName; 42 | RtlGetVersion_type RtlGetVersion; 43 | NtTerminateProcess_type NtTerminateProcess; 44 | 45 | static bool ntdll_initialized; 46 | 47 | void ntdll_declarations_init() 48 | { 49 | if (!ntdll_initialized) { 50 | HMODULE ntDLLMod = GetModuleHandleW(L"ntdll.dll"); 51 | 52 | LOAD_EXT(ntDLLMod, NtQueryDirectoryFile); 53 | LOAD_EXT(ntDLLMod, NtQueryDirectoryFileEx); 54 | LOAD_EXT(ntDLLMod, NtQueryFullAttributesFile); 55 | LOAD_EXT(ntDLLMod, NtQueryAttributesFile); 56 | LOAD_EXT(ntDLLMod, NtQueryObject); 57 | LOAD_EXT(ntDLLMod, NtQueryInformationFile); 58 | LOAD_EXT(ntDLLMod, NtQueryInformationByName); 59 | LOAD_EXT(ntDLLMod, NtCreateFile); 60 | LOAD_EXT(ntDLLMod, NtOpenFile); 61 | LOAD_EXT(ntDLLMod, NtClose); 62 | LOAD_EXT(ntDLLMod, RtlDoesFileExists_U); 63 | LOAD_EXT(ntDLLMod, RtlDosPathNameToRelativeNtPathName_U_WithStatus); 64 | LOAD_EXT(ntDLLMod, RtlReleaseRelativeName); 65 | LOAD_EXT(ntDLLMod, RtlGetVersion); 66 | LOAD_EXT(ntDLLMod, NtTerminateProcess); 67 | 68 | ntdll_initialized = true; 69 | } 70 | } 71 | 72 | static struct __Initializer 73 | { 74 | __Initializer() { ntdll_declarations_init(); } 75 | } __initializer; 76 | -------------------------------------------------------------------------------- /src/shared/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /src/shared/pch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef NOMINMAX 33 | #define NOMINMAX 34 | #endif 35 | 36 | // clang-format off 37 | #define WIN32_LEAN_AND_MEAN 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | // clang-format on 47 | 48 | #define BOOST_INTERPROCESS_SEGMENT_MANAGER_ABI 1 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | 85 | #include 86 | 87 | namespace fs = boost::filesystem; 88 | -------------------------------------------------------------------------------- /src/shared/shared_memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | 22 | #pragma once 23 | 24 | namespace bi = boost::interprocess; 25 | namespace bc = boost::container; 26 | 27 | namespace usvfs::shared 28 | { 29 | 30 | template 31 | using OffsetPtrT = bi::offset_ptr; 32 | 33 | using VoidPointerT = OffsetPtrT; 34 | 35 | // important: the windows shared memory mechanism, unlike other implementations, 36 | // automatically removes the SHM object when there are no more "subscribers". 37 | // MO currently depends on that feature! 38 | 39 | // managed_windows_shared_memory apparently doesn't support sharing between 40 | // 64bit and 32bit processes 41 | using managed_windows_shared_memory = bi::basic_managed_windows_shared_memory< 42 | char, bi::rbtree_best_fit, bi::iset_index>; 43 | 44 | using SharedMemoryT = managed_windows_shared_memory; 45 | using SegmentManagerT = SharedMemoryT::segment_manager; 46 | 47 | using VoidAllocatorT = boost::container::scoped_allocator_adaptor< 48 | boost::interprocess::allocator>; 49 | using CharAllocatorT = VoidAllocatorT::rebind::other; 50 | 51 | using StringT = bc::basic_string, CharAllocatorT>; 52 | 53 | } // namespace usvfs::shared 54 | -------------------------------------------------------------------------------- /src/shared/shmlogger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | #include "logging.h" 27 | #include "shared_memory.h" 28 | #include "windows_sane.h" 29 | 30 | typedef boost::interprocess::message_queue_t 31 | message_queue_interop; 32 | 33 | namespace usvfs::sinks 34 | { 35 | class shm_sink : public spdlog::sinks::base_sink 36 | { 37 | public: 38 | shm_sink(const char* queueName); 39 | 40 | protected: 41 | void sink_it_(const spdlog::details::log_msg& msg) override; 42 | void output(spdlog::level::level_enum lev, const std::string& message); 43 | void flush_() override; 44 | 45 | private: 46 | message_queue_interop m_LogQueue; 47 | std::atomic m_DroppedMessages; 48 | }; 49 | 50 | } // namespace usvfs::sinks 51 | 52 | class SHMLogger 53 | { 54 | public: 55 | static const size_t MESSAGE_COUNT = 1024; 56 | static const size_t MESSAGE_SIZE = 512; 57 | 58 | static SHMLogger& create(const char* instanceName); 59 | static SHMLogger& open(const char* instanceName); 60 | static void free(); 61 | 62 | static bool isInstantiated() { return s_Instance != nullptr; } 63 | 64 | static inline SHMLogger& instance() 65 | { 66 | if (s_Instance == nullptr) { 67 | throw std::runtime_error("shm logger not instantiated"); 68 | } 69 | 70 | return *s_Instance; 71 | } 72 | 73 | void log(LogLevel logLevel, const std::string& message); 74 | bool tryGet(char* buffer, size_t bufferSize); 75 | void get(char* buffer, size_t bufferSize); 76 | 77 | private: 78 | struct owner_t 79 | {}; 80 | static owner_t owner; 81 | 82 | struct client_t 83 | {}; 84 | static client_t client; 85 | 86 | private: 87 | SHMLogger(owner_t, const std::string& instanceName); 88 | SHMLogger(client_t, const std::string& instanceName); 89 | 90 | SHMLogger(const SHMLogger&) = delete; 91 | SHMLogger& operator=(const SHMLogger&) = delete; 92 | 93 | ~SHMLogger(); 94 | 95 | private: 96 | static SHMLogger* s_Instance; 97 | 98 | message_queue_interop m_LogQueue; 99 | 100 | std::string m_SHMName; 101 | std::string m_LockName; 102 | std::string m_QueueName; 103 | 104 | std::atomic m_DroppedMessages; 105 | }; 106 | -------------------------------------------------------------------------------- /src/shared/stringcast.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "stringcast.h" 22 | 23 | namespace usvfs::shared 24 | { 25 | 26 | UINT windowsCP(CodePage codePage) 27 | { 28 | switch (codePage) { 29 | case CodePage::LOCAL: 30 | return CP_ACP; 31 | case CodePage::UTF8: 32 | return CP_UTF8; 33 | case CodePage::LATIN1: 34 | return 850; 35 | } 36 | // this should not be possible in practice 37 | throw std::runtime_error("unsupported codePage"); 38 | } 39 | 40 | } // namespace usvfs::shared 41 | -------------------------------------------------------------------------------- /src/shared/stringutils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "stringutils.h" 22 | #include "windows_sane.h" 23 | 24 | namespace usvfs::shared 25 | { 26 | 27 | void strncpy_sz(char* dest, const char* src, size_t destSize) 28 | { 29 | if (destSize > 0) { 30 | strncpy(dest, src, destSize - 1); 31 | dest[destSize - 1] = '\0'; 32 | } 33 | } 34 | 35 | void wcsncpy_sz(wchar_t* dest, const wchar_t* src, size_t destSize) 36 | { 37 | if ((destSize > 0) && (dest != nullptr)) { 38 | wcsncpy(dest, src, destSize - 1); 39 | dest[destSize - 1] = L'\0'; 40 | } 41 | } 42 | 43 | bool startswith(const wchar_t* string, const wchar_t* subString) 44 | { 45 | while ((*string != '\0') && (*subString != '\0')) { 46 | if (towlower(*string) != towlower(*subString)) { 47 | return false; 48 | } 49 | ++string; 50 | ++subString; 51 | } 52 | 53 | return *subString == '\0'; 54 | } 55 | 56 | static fs::path normalize(const fs::path& path) 57 | { 58 | fs::path result; 59 | 60 | boost::locale::generator gen; 61 | auto loc = gen("en_US.UTF-8"); 62 | for (fs::path::iterator iter = path.begin(); iter != path.end(); ++iter) { 63 | if (*iter == "..") { 64 | result = result.parent_path(); 65 | } else if (*iter != ".") { 66 | result /= boost::to_lower_copy(iter->string(), loc); 67 | } // single dot is ignored 68 | } 69 | return result; 70 | } 71 | 72 | fs::path make_relative(const fs::path& fromIn, const fs::path& toIn) 73 | { 74 | // converting path to lower case to make iterator comparison work correctly 75 | // on case-insenstive filesystems 76 | fs::path from(fs::absolute(fromIn)); 77 | fs::path to(fs::absolute(toIn)); 78 | 79 | // find common base 80 | fs::path::const_iterator fromIter(from.begin()); 81 | fs::path::const_iterator toIter(to.begin()); 82 | 83 | // TODO the following equivalent test is probably quite expensive as new 84 | // paths are created for each iteration but the case sensitivity depends on 85 | // the fs 86 | while ((fromIter != from.end()) && (toIter != to.end()) && 87 | (boost::iequals(fromIter->string(), toIter->string()))) { 88 | ++fromIter; 89 | ++toIter; 90 | } 91 | 92 | // Navigate backwards in directory to reach previously found base 93 | fs::path result; 94 | for (; fromIter != from.end(); ++fromIter) { 95 | if (*fromIter != ".") { 96 | result /= ".."; 97 | } 98 | } 99 | 100 | // Now navigate down the directory branch 101 | for (; toIter != to.end(); ++toIter) { 102 | result /= *toIter; 103 | } 104 | return result; 105 | } 106 | 107 | std::string to_hex(void* bufferIn, size_t bufferSize) 108 | { 109 | unsigned char* buffer = static_cast(bufferIn); 110 | std::ostringstream temp; 111 | temp << std::hex; 112 | for (size_t i = 0; i < bufferSize; ++i) { 113 | temp << std::setfill('0') << std::setw(2) << (unsigned int)buffer[i]; 114 | if ((i % 16) == 15) { 115 | temp << "\n"; 116 | } else { 117 | temp << " "; 118 | } 119 | } 120 | return temp.str(); 121 | } 122 | 123 | std::wstring to_upper(const std::wstring& input) 124 | { 125 | std::wstring result; 126 | result.resize(input.size()); 127 | ::LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, input.c_str(), 128 | static_cast(input.size()), &result[0], 129 | static_cast(result.size())); 130 | return result; 131 | } 132 | 133 | std::string byte_string(std::size_t n) 134 | { 135 | auto s = std::to_string(n); 136 | auto p = s.size(); 137 | 138 | while (p > 3) { 139 | p -= 3; 140 | s.insert(p, 1, ','); 141 | } 142 | 143 | return s + " B"; 144 | } 145 | 146 | } // namespace usvfs::shared 147 | -------------------------------------------------------------------------------- /src/shared/stringutils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | namespace usvfs::shared 24 | { 25 | 26 | void strncpy_sz(char* dest, const char* src, size_t destSize); 27 | void wcsncpy_sz(wchar_t* dest, const wchar_t* src, size_t destSize); 28 | 29 | bool startswith(const wchar_t* string, const wchar_t* subString); 30 | 31 | // Return path when appended to a_From will resolve to same as a_To 32 | fs::path make_relative(const fs::path& from, const fs::path& to); 33 | 34 | std::string to_hex(void* bufferIn, size_t bufferSize); 35 | 36 | // convert unicode string to upper-case (locale invariant) 37 | std::wstring to_upper(const std::wstring& input); 38 | 39 | // formats a number with thousand separators and B at the end 40 | // 41 | std::string byte_string(std::size_t n); 42 | 43 | class FormatGuard 44 | { 45 | std::ostream& m_Stream; 46 | std::ios::fmtflags m_Flags; 47 | 48 | public: 49 | FormatGuard(std::ostream& stream) : m_Stream(stream), m_Flags(stream.flags()) {} 50 | 51 | ~FormatGuard() { m_Stream.flags(m_Flags); } 52 | 53 | FormatGuard(const FormatGuard&) = delete; 54 | FormatGuard& operator=(FormatGuard&) = delete; 55 | }; 56 | 57 | } // namespace usvfs::shared 58 | -------------------------------------------------------------------------------- /src/shared/unicodestring.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "unicodestring.h" 22 | #include "logging.h" 23 | #include "stringcast.h" 24 | #include "stringutils.h" 25 | 26 | namespace usvfs 27 | { 28 | 29 | UnicodeString::UnicodeString(LPCWSTR string, size_t length) 30 | { 31 | if (length == std::string::npos) { 32 | length = wcslen(string); 33 | } 34 | m_Buffer.resize(length + 1); 35 | memcpy(m_Buffer.data(), string, length * sizeof(wchar_t)); 36 | update(); 37 | } 38 | 39 | UnicodeString::UnicodeString(const std::wstring& string) 40 | { 41 | m_Buffer.resize(string.length() + 1); 42 | memcpy(m_Buffer.data(), string.data(), string.length() * sizeof(wchar_t)); 43 | update(); 44 | } 45 | 46 | UnicodeString& UnicodeString::operator=(const std::wstring& string) 47 | { 48 | m_Buffer.resize(string.length() + 1); 49 | memcpy(m_Buffer.data(), string.data(), string.length() * sizeof(wchar_t)); 50 | update(); 51 | return *this; 52 | } 53 | 54 | UnicodeString& UnicodeString::appendPath(PUNICODE_STRING path) 55 | { 56 | if (path != nullptr && path->Buffer && path->Length) { 57 | auto appendAt = size(); 58 | if (appendAt) { 59 | m_Buffer.resize(m_Buffer.size() + path->Length / sizeof(WCHAR) + 1); 60 | m_Buffer[appendAt++] = L'\\'; 61 | } else 62 | m_Buffer.resize(path->Length / sizeof(WCHAR) + 1); 63 | memcpy(&m_Buffer[appendAt], path->Buffer, path->Length); 64 | update(); 65 | } 66 | 67 | return *this; 68 | } 69 | 70 | void UnicodeString::update() 71 | { 72 | m_Data.Length = static_cast(size() * sizeof(WCHAR)); 73 | m_Data.MaximumLength = static_cast((m_Buffer.capacity() - 1) * sizeof(WCHAR)); 74 | m_Data.Buffer = m_Buffer.data(); 75 | } 76 | 77 | } // namespace usvfs 78 | -------------------------------------------------------------------------------- /src/shared/unicodestring.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "ntdll_declarations.h" 24 | #include "windows_sane.h" 25 | 26 | #include "formatters.h" 27 | 28 | namespace usvfs 29 | { 30 | 31 | /** 32 | * @brief C++ wrapper for the windows UNICODE_STRING structure 33 | */ 34 | class UnicodeString 35 | { 36 | public: 37 | UnicodeString() : m_Buffer(1) { update(); } 38 | 39 | UnicodeString(const UnicodeString& other) : m_Buffer(other.m_Buffer) { update(); } 40 | 41 | UnicodeString(UnicodeString&& other) : m_Buffer(std::move(other.m_Buffer)) 42 | { 43 | update(); 44 | } 45 | 46 | UnicodeString(const std::wstring& string); 47 | UnicodeString(LPCWSTR string, size_t length = std::string::npos); 48 | 49 | UnicodeString& operator=(const std::wstring& string); 50 | 51 | UnicodeString& operator=(const UnicodeString& other) 52 | { 53 | m_Buffer = other.m_Buffer; 54 | update(); 55 | return *this; 56 | } 57 | 58 | UnicodeString& operator=(UnicodeString&& other) 59 | { 60 | m_Buffer = std::move(other.m_Buffer); 61 | update(); 62 | return *this; 63 | } 64 | 65 | /** 66 | * @brief convert to a WinNt Api-style unicode string. This is only valid as long 67 | * as the string isn't modified 68 | */ 69 | explicit operator PUNICODE_STRING() { return &m_Data; } 70 | 71 | /** 72 | * @brief convert to a Win32 Api-style unicode string. This is only valid as long 73 | * as the string isn't modified 74 | */ 75 | explicit operator LPCWSTR() const { return m_Data.Buffer; } 76 | 77 | /** 78 | * @return length of the string in 16-bit words (not including zero termination) 79 | */ 80 | size_t size() const { return m_Buffer.size() - 1; } 81 | 82 | wchar_t operator[](size_t pos) const { return m_Buffer[pos]; } 83 | 84 | UnicodeString& appendPath(PUNICODE_STRING path); 85 | 86 | private: 87 | friend struct ::std::formatter; 88 | 89 | void update(); 90 | 91 | UNICODE_STRING m_Data; 92 | std::vector m_Buffer; 93 | }; 94 | 95 | } // namespace usvfs 96 | 97 | template <> 98 | struct std::formatter 99 | : std::formatter 100 | { 101 | template 102 | FmtContext::iterator format(const usvfs::UnicodeString& v, FmtContext& ctx) const 103 | { 104 | if (v.size() == 0) { 105 | return std::format_to(ctx.out(), ""); 106 | } else { 107 | return std::formatter::format(&v.m_Data, ctx); 108 | } 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /src/shared/wildcard.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | /// Wildcard matching code 22 | /// by Martin Richter 23 | /// licensed under The Code Project Open License (CPOL) 24 | 25 | #pragma once 26 | 27 | #include "windows_sane.h" 28 | 29 | namespace usvfs::shared::wildcard 30 | { 31 | 32 | /** 33 | * @brief match string to wildcard windows-style 34 | * @param pszString Input string to match 35 | * @param pszMatch Match mask that may contain wildcards like ? and * 36 | * @note A ? sign matches any character, except an empty string. 37 | * @note A * sign matches any string inclusive an empty string. 38 | * @note Characters are compared caseless. 39 | * @return true if the string matches the pattern 40 | */ 41 | bool Match(LPCWSTR pszString, LPCWSTR pszMatch); 42 | 43 | /** 44 | * @brief match string to wildcard windows-style 45 | * @param pszString Input string to match 46 | * @param pszMatch Match mask that may contain wildcards like ? and * 47 | * @note A ? sign matches any character, except an empty string. 48 | * @note A * sign matches any string inclusive an empty string. 49 | * @note Characters are compared caseless. 50 | * @return true if the string matches the pattern 51 | */ 52 | bool Match(LPCSTR pszString, LPCSTR pszMatch); 53 | 54 | /** 55 | * @brief match string to wildcard windows-style 56 | * @param pszString Input string to match 57 | * @param pszMatch Match mask that may contain wildcards like ? and * 58 | * @note A ? sign matches any character, except an empty string. 59 | * @note A * sign matches any string inclusive an empty string. 60 | * @note Characters are compared caseless. 61 | * @return the "not-consumed" remainder of the pattern. If this points to a 62 | * zero terminator, this was a full match. 63 | * Returns nullptr if no match is possible 64 | */ 65 | LPCSTR PartialMatch(LPCSTR pszString, LPCSTR pszMatch); 66 | 67 | } // namespace usvfs::shared::wildcard 68 | -------------------------------------------------------------------------------- /src/shared/windows_sane.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #ifndef NOMINMAX 24 | #define NOMINMAX 25 | #endif 26 | 27 | #define WIN32_LEAN_AND_MEAN 28 | #include 29 | -------------------------------------------------------------------------------- /src/thooklib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(asmjit CONFIG REQUIRED) 4 | find_library(LIBUDIS86_LIBRARY libudis86) 5 | find_package(spdlog CONFIG REQUIRED) 6 | 7 | file(GLOB sources CONFIGURE_DEPENDS "*.cpp" "*.h") 8 | source_group("" FILES ${sources}) 9 | 10 | add_library(thooklib STATIC ${sources}) 11 | target_link_libraries(thooklib PRIVATE shared asmjit::asmjit ${LIBUDIS86_LIBRARY} spdlog::spdlog_header_only) 12 | target_include_directories(thooklib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 13 | set_target_properties(thooklib PROPERTIES FOLDER injection) 14 | target_precompile_headers(shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../shared/pch.h) 15 | -------------------------------------------------------------------------------- /src/thooklib/asmjit_sane.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #pragma warning(push) 24 | #pragma warning(disable : 4201) 25 | #pragma warning(disable : 4244) 26 | #pragma warning(disable : 4245) 27 | #include 28 | #include 29 | #pragma warning(pop) 30 | -------------------------------------------------------------------------------- /src/thooklib/hooklib.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #include 25 | 26 | namespace HookLib 27 | { 28 | 29 | enum HookError 30 | { 31 | ERR_NONE, 32 | ERR_INVALIDPARAMETERS, // parameters are invalid 33 | ERR_FUNCEND, // function is too short to be hooked 34 | ERR_JUMP, // function consists only of an unconditional jump. Maybe it has already 35 | // been hooked? 36 | ERR_RIP, // segment of the function to be overwritten contains a instruction-relative 37 | // operation 38 | ERR_RELJUMP // segment of the function to be overwritten contains a relative jump we 39 | // can't relocated 40 | }; 41 | 42 | typedef ULONG HOOKHANDLE; 43 | static const HOOKHANDLE INVALID_HOOK = (HOOKHANDLE)-1; 44 | 45 | /// 46 | /// \brief install a stub (function to be called before the target function) 47 | /// \param functionAddress address of the function to stub 48 | /// \param stubAddress address of the stub function. This function has to have the 49 | /// signature of void foobar(LPVOID address). 50 | /// address receives the address of the function. 51 | /// \param error (optional) if set, the referenced variable will receive an error code 52 | /// describing the problem (if any) 53 | /// \return a handle to reference the hook in later operations or INVALID_HOOK on error 54 | /// 55 | HOOKHANDLE InstallStub(LPVOID functionAddress, LPVOID stubAddress, 56 | HookError* error = nullptr); 57 | 58 | /// 59 | /// \brief install a stub (function to be called before the target function) 60 | /// \param module the module containing the function to hook 61 | /// \param functionName name of the function to stub (as exported by the library) 62 | /// \param stubAddress address of the stub function. This function has to have the 63 | /// signature of void foobar(LPVOID address). 64 | /// address receives the address of the function. 65 | /// \param error (optional) if set, the referenced variable will receive an error code 66 | /// describing the problem (if any) 67 | /// \return a handle to reference the hook in later operations or INVALID_HOOK on error 68 | /// 69 | HOOKHANDLE InstallStub(HMODULE module, LPCSTR functionName, LPVOID stubAddress, 70 | HookError* error = nullptr); 71 | 72 | /// 73 | /// \brief install a hook (function replacing the existing functionality of the 74 | /// function) 75 | /// \param functionAddress address of the function to hook 76 | /// \param hookAddress address of the replacement function. This function has to have 77 | /// the exact same signature as the replaced function 78 | /// \param error (optional) if set, the referenced variable will receive an error code 79 | /// describing the problem (if any) 80 | /// \return a handle to reference the hook in later operations or INVALID_HOOK on error 81 | /// 82 | HOOKHANDLE InstallHook(LPVOID functionAddress, LPVOID hookAddress, 83 | HookError* error = nullptr); 84 | 85 | /// 86 | /// \brief install a hook (function replacing the existing functionality of the 87 | /// function) 88 | /// \param functionName name of the function to hook (as exported by the library) 89 | /// \param hookAddress address of the replacement function. This function has to have 90 | /// the exact same signature as the replaced function 91 | /// \param error (optional) if set, the referenced variable will receive an error code 92 | /// describing the problem (if any) 93 | /// \return a handle to reference the hook in later operations or INVALID_HOOK on error 94 | /// 95 | HOOKHANDLE InstallHook(HMODULE module, LPCSTR functionName, LPVOID hookAddress, 96 | HookError* error = nullptr); 97 | 98 | /// 99 | /// \brief remove a hook 100 | /// \param handle handle returned in InstallStub or InstallHook 101 | /// 102 | void RemoveHook(HOOKHANDLE handle); 103 | 104 | /// 105 | /// \brief determine the type of a hook 106 | /// \param handle the handle to look up 107 | /// \return a string describing the used hooking mechanism 108 | /// 109 | const char* GetHookType(HOOKHANDLE handle); 110 | 111 | /// 112 | /// \brief retrieve the address that can be used to directly call a detour 113 | /// \param handle handle for the hook 114 | /// \return function address 115 | /// 116 | LPVOID GetDetour(HOOKHANDLE handle); 117 | 118 | /// 119 | /// \brief resolve an error code to a descriptive string 120 | /// \param err the error code to resolve 121 | /// \return the error string 122 | /// 123 | const char* GetErrorString(HookError err); 124 | 125 | } // namespace HookLib 126 | -------------------------------------------------------------------------------- /src/thooklib/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /src/thooklib/udis86wrapper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "udis86wrapper.h" 22 | #include "pch.h" 23 | #include 24 | #include 25 | #include 26 | 27 | namespace HookLib 28 | { 29 | 30 | UDis86Wrapper::UDis86Wrapper() 31 | { 32 | ud_init(&m_Obj); 33 | ud_set_syntax(&m_Obj, UD_SYN_INTEL); 34 | #if BOOST_ARCH_X86_64 35 | ud_set_mode(&m_Obj, 64); 36 | #else 37 | ud_set_mode(&m_Obj, 32); 38 | #endif 39 | } 40 | 41 | void UDis86Wrapper::setInputBuffer(const uint8_t* buffer, size_t size) 42 | { 43 | m_Buffer = buffer; 44 | ud_set_input_buffer(&m_Obj, buffer, size); 45 | ud_set_pc(&m_Obj, reinterpret_cast(m_Buffer)); 46 | } 47 | 48 | ud_t& UDis86Wrapper::obj() 49 | { 50 | return m_Obj; 51 | } 52 | 53 | bool UDis86Wrapper::isRelativeJump() 54 | { 55 | ud_mnemonic_code code = ud_insn_mnemonic(&m_Obj); 56 | // all conditional jumps and loops are relative, as are unconditional jumps with an 57 | // offset operand 58 | return (code == UD_Ijo) || (code == UD_Ijno) || (code == UD_Ijb) || 59 | (code == UD_Ijae) || (code == UD_Ijz) || (code == UD_Ijnz) || 60 | (code == UD_Ijbe) || (code == UD_Ija) || (code == UD_Ijs) || 61 | (code == UD_Ijns) || (code == UD_Ijp) || (code == UD_Ijnp) || 62 | (code == UD_Ijl) || (code == UD_Ijge) || (code == UD_Ijle) || 63 | (code == UD_Ijg) || (code == UD_Ijcxz) || (code == UD_Ijecxz) || 64 | (code == UD_Ijrcxz) || (code == UD_Iloop) || (code == UD_Iloope) || 65 | (code == UD_Iloopne) || 66 | ((code == UD_Icall) && (ud_insn_opr(&m_Obj, 0)->type == UD_OP_JIMM)) || 67 | ((code == UD_Ijmp) && (ud_insn_opr(&m_Obj, 0)->type == UD_OP_JIMM)); 68 | } 69 | 70 | intptr_t UDis86Wrapper::jumpOffset() 71 | { 72 | const ud_operand_t* op = ud_insn_opr(&m_Obj, 0); 73 | switch (op->size) { 74 | case 8: 75 | return static_cast(op->lval.sbyte); 76 | case 16: 77 | return static_cast(op->lval.sword); 78 | case 32: 79 | return static_cast(op->lval.sdword); 80 | case 64: 81 | return static_cast(op->lval.sqword); 82 | default: 83 | throw std::runtime_error("unsupported jump size"); 84 | } 85 | } 86 | 87 | uint64_t UDis86Wrapper::jumpTarget() 88 | { 89 | // TODO: assert we're actually on a jump 90 | 91 | uint64_t res = ud_insn_off(&m_Obj) + ud_insn_len(&m_Obj); 92 | 93 | res += jumpOffset(); 94 | 95 | if (ud_insn_opr(&m_Obj, 0)->base == UD_R_RIP) { 96 | res = *reinterpret_cast(res); 97 | } 98 | 99 | return res; 100 | } 101 | 102 | } // namespace HookLib 103 | -------------------------------------------------------------------------------- /src/thooklib/udis86wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | #undef inline // libudis86/types.h defines inline to __inline which is no longer legal 25 | // since vs2012 26 | 27 | namespace HookLib 28 | { 29 | 30 | class UDis86Wrapper 31 | { 32 | 33 | public: 34 | UDis86Wrapper(); 35 | 36 | void setInputBuffer(const uint8_t* buffer, size_t size); 37 | 38 | ud_t& obj(); 39 | 40 | operator ud_t*() { return &m_Obj; } 41 | 42 | bool isRelativeJump(); 43 | 44 | intptr_t jumpOffset(); 45 | 46 | /// 47 | /// determines the absolute jump target at the current instruction, taking into 48 | /// account relative instructions of all sizes and RIP-relative addressing. 49 | /// \return absolute address of the jump at the current disassembler instruction 50 | /// \note this works correctly ONLY if the input buffer has been set with 51 | /// setInputBuffer or 52 | /// if ud_set_pc has been called 53 | /// 54 | uint64_t jumpTarget(); 55 | 56 | private: 57 | private: 58 | ud_t m_Obj; 59 | const uint8_t* m_Buffer{nullptr}; 60 | }; 61 | 62 | } // namespace HookLib 63 | -------------------------------------------------------------------------------- /src/thooklib/utility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "utility.h" 22 | #include "exceptionex.h" 23 | #include 24 | 25 | namespace HookLib 26 | { 27 | 28 | FARPROC MyGetProcAddress(HMODULE module, LPCSTR functionName) 29 | { 30 | // determine position of the exports of the module 31 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)module; 32 | if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) { 33 | return nullptr; 34 | } 35 | 36 | PIMAGE_NT_HEADERS ntHeaders = 37 | (PIMAGE_NT_HEADERS)(((LPBYTE)dosHeader) + dosHeader->e_lfanew); 38 | if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) { 39 | return nullptr; 40 | } 41 | 42 | PIMAGE_OPTIONAL_HEADER optionalHeader = &ntHeaders->OptionalHeader; 43 | if (optionalHeader->NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) { 44 | return nullptr; 45 | } 46 | PIMAGE_DATA_DIRECTORY dataDirectory = 47 | &optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 48 | PIMAGE_EXPORT_DIRECTORY exportDirectory = 49 | (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)dosHeader + dataDirectory->VirtualAddress); 50 | 51 | ULONG* addressOfNames = (ULONG*)((LPBYTE)module + exportDirectory->AddressOfNames); 52 | ULONG* funcAddr = (ULONG*)((LPBYTE)module + exportDirectory->AddressOfFunctions); 53 | 54 | // search exports for the specified name 55 | for (DWORD i = 0; i < exportDirectory->NumberOfNames; ++i) { 56 | char* curFunctionName = (char*)((LPBYTE)module + addressOfNames[i]); 57 | USHORT* nameOrdinals = 58 | (USHORT*)((LPBYTE)module + exportDirectory->AddressOfNameOrdinals); 59 | if (strcmp(functionName, curFunctionName) == 0) { 60 | if (funcAddr[nameOrdinals[i]] >= dataDirectory->VirtualAddress && 61 | funcAddr[nameOrdinals[i]] < 62 | dataDirectory->VirtualAddress + dataDirectory->Size) { 63 | char* forwardLibName = _strdup((LPSTR)module + funcAddr[nameOrdinals[i]]); 64 | ON_BLOCK_EXIT([forwardLibName]() { 65 | free(forwardLibName); 66 | }); 67 | char* forwardFunctionName = strchr(forwardLibName, '.'); 68 | *forwardFunctionName = 0; 69 | ++forwardFunctionName; 70 | 71 | HMODULE forwardLib = LoadLibraryA(forwardLibName); 72 | FARPROC forward = nullptr; 73 | if (forwardLib != nullptr) { 74 | forward = MyGetProcAddress(forwardLib, forwardFunctionName); 75 | FreeLibrary(forwardLib); 76 | } 77 | 78 | return forward; 79 | } 80 | return (FARPROC)((LPBYTE)module + funcAddr[nameOrdinals[i]]); 81 | } 82 | } 83 | return nullptr; 84 | } 85 | 86 | } // namespace HookLib 87 | -------------------------------------------------------------------------------- /src/thooklib/utility.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | 25 | namespace HookLib 26 | { 27 | 28 | /// \brief reimplementation of GetProcAddress to circumvent foreign hooks of 29 | /// GetProcAddress like AcLayer 30 | /// \param module handle to the module that contains the function or variable 31 | /// \param functionName function to retrieve the address of 32 | /// \return address of the exported function 33 | FARPROC MyGetProcAddress(HMODULE module, LPCSTR functionName); 34 | 35 | } // namespace HookLib 36 | -------------------------------------------------------------------------------- /src/tinjectlib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(asmjit CONFIG REQUIRED) 4 | 5 | add_library(tinjectlib STATIC asmjit_sane.h injectlib.cpp injectlib.h) 6 | target_link_libraries(tinjectlib PRIVATE shared asmjit::asmjit) 7 | target_include_directories(tinjectlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 8 | set_target_properties(tinjectlib PROPERTIES FOLDER injection) 9 | -------------------------------------------------------------------------------- /src/tinjectlib/asmjit_sane.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #pragma warning(push, 3) 24 | #pragma warning(disable : 4201) 25 | #pragma warning(disable : 4244) 26 | #pragma warning(disable : 4245) 27 | // #ifndef ASMJIT_API 28 | // #define ASMJIT_API 29 | // #endif // ASMJIT_API 30 | #include 31 | #include 32 | #pragma warning(pop) 33 | -------------------------------------------------------------------------------- /src/tinjectlib/injectlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | 25 | namespace InjectLib 26 | { 27 | 28 | /** 29 | * @brief inject a dll into the target process 30 | * @param processHandle handle of the process to inject to 31 | * @param threadHandle handle of the main thread in the process 32 | * @param dllname name/path of the dll to inject. The path can't be longer than MAX_PATH 33 | * characters 34 | * @param initFunction name of the initialization function. Can't be longer than 20 35 | * characters. If this is null, no function is called. Important: the init function has 36 | * to exist, be exported, take the two userdata parameters and must be __cdecl calling 37 | * convention on 32bit windows! Failing any of these the target process will crash or 38 | * the dll isn't loaded Why __cdecl? Because otherwise we would need .def files to 39 | * export the init function with a GetProcAddress-able function name. 40 | * @param userData data passed to the init function 41 | * @param userDataSize size of the data to be passed 42 | * @param skipInit skip the call to the init function if the named function wasn't found 43 | * in the dll. If false, the target process will crash if the function isn't exported in 44 | * the dll 45 | */ 46 | void InjectDLL(HANDLE processHandle, HANDLE threadHandle, LPCWSTR dllName, 47 | LPCSTR initFunction = nullptr, LPCVOID userData = nullptr, 48 | size_t userDataSize = 0, bool skipInit = false); 49 | 50 | } // namespace InjectLib 51 | -------------------------------------------------------------------------------- /src/usvfs_dll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | include(GNUInstallDirs) 4 | 5 | find_package(Boost CONFIG REQUIRED COMPONENTS thread) 6 | find_package(spdlog CONFIG REQUIRED) 7 | 8 | file(GLOB sources "*.cpp" "*.h") 9 | source_group("" FILES ${sources}) 10 | 11 | file(GLOB hooks "hooks/*.cpp" "hooks/*.h") 12 | source_group("dlls" FILES ${hooks}) 13 | 14 | add_library(usvfs_dll SHARED) 15 | target_sources(usvfs_dll 16 | PRIVATE 17 | ${PROJECT_SOURCE_DIR}/include/usvfs/usvfsparametersprivate.h 18 | ${sources} 19 | ${hooks} 20 | version.rc 21 | PUBLIC 22 | FILE_SET HEADERS 23 | BASE_DIRS ${PROJECT_SOURCE_DIR}/include 24 | FILES 25 | ${PROJECT_SOURCE_DIR}/include/usvfs/dllimport.h 26 | ${PROJECT_SOURCE_DIR}/include/usvfs/logging.h 27 | ${PROJECT_SOURCE_DIR}/include/usvfs/sharedparameters.h 28 | ${PROJECT_SOURCE_DIR}/include/usvfs/usvfs_version.h 29 | ${PROJECT_SOURCE_DIR}/include/usvfs/usvfs.h 30 | ${PROJECT_SOURCE_DIR}/include/usvfs/usvfsparameters.h 31 | ) 32 | target_link_libraries(usvfs_dll 33 | PRIVATE shared thooklib usvfs_helper 34 | Boost::thread spdlog::spdlog_header_only 35 | Shlwapi) 36 | target_compile_definitions(usvfs_dll PRIVATE BUILDING_USVFS_DLL) 37 | set_target_properties(usvfs_dll 38 | PROPERTIES 39 | ARCHIVE_OUTPUT_NAME usvfs${ARCH_POSTFIX} 40 | ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR} 41 | ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR} 42 | LIBRARY_OUTPUT_NAME usvfs${ARCH_POSTFIX} 43 | LIBRARY_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR} 44 | LIBRARY_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR} 45 | PDB_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR} 46 | PDB_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR} 47 | RUNTIME_OUTPUT_NAME usvfs${ARCH_POSTFIX} 48 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_LIBDIR} 49 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_LIBDIR}) 50 | 51 | install(TARGETS usvfs_dll EXPORT usvfs${ARCH_POSTFIX}Targets FILE_SET HEADERS) 52 | install(FILES $ DESTINATION pdb OPTIONAL) 53 | install(EXPORT usvfs${ARCH_POSTFIX}Targets 54 | FILE usvfs${ARCH_POSTFIX}Targets.cmake 55 | NAMESPACE usvfs${ARCH_POSTFIX}:: 56 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/usvfs 57 | ) 58 | -------------------------------------------------------------------------------- /src/usvfs_dll/hookcallcontext.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "hookcallcontext.h" 22 | #include "hookcontext.h" 23 | #include 24 | 25 | namespace usvfs 26 | { 27 | 28 | class HookStack 29 | { 30 | public: 31 | static HookStack& instance() 32 | { 33 | PreserveGetLastError boostChangesGetLastError; 34 | if (s_Instance.get() == nullptr) { 35 | s_Instance.reset(new HookStack()); 36 | } 37 | return *s_Instance.get(); 38 | } 39 | 40 | bool setGroup(MutExHookGroup group) 41 | { 42 | if (m_ActiveGroups.test(static_cast(group)) || 43 | m_ActiveGroups.test(static_cast(MutExHookGroup::ALL_GROUPS))) { 44 | return false; 45 | } else { 46 | m_ActiveGroups.set(static_cast(group), true); 47 | return true; 48 | } 49 | } 50 | 51 | void unsetGroup(MutExHookGroup group) 52 | { 53 | m_ActiveGroups.set(static_cast(group), false); 54 | } 55 | 56 | private: 57 | HookStack() {} 58 | 59 | private: 60 | static boost::thread_specific_ptr s_Instance; 61 | std::bitset(MutExHookGroup::LAST)> m_ActiveGroups; 62 | }; 63 | 64 | boost::thread_specific_ptr HookStack::s_Instance; 65 | 66 | HookCallContext::HookCallContext() : m_Active(true), m_Group(MutExHookGroup::NO_GROUP) 67 | { 68 | updateLastError(); 69 | } 70 | 71 | HookCallContext::HookCallContext(MutExHookGroup group) 72 | : m_Active(HookStack::instance().setGroup(group)), m_Group(group) 73 | { 74 | updateLastError(); 75 | } 76 | 77 | HookCallContext::~HookCallContext() 78 | { 79 | if (m_Active && (m_Group != MutExHookGroup::NO_GROUP)) { 80 | HookStack::instance().unsetGroup(m_Group); 81 | } 82 | SetLastError(m_LastError); 83 | } 84 | 85 | void HookCallContext::restoreLastError() 86 | { 87 | SetLastError(m_LastError); 88 | } 89 | 90 | void HookCallContext::updateLastError(DWORD lastError) 91 | { 92 | m_LastError = lastError; 93 | } 94 | 95 | bool HookCallContext::active() const 96 | { 97 | return m_Active; 98 | } 99 | 100 | FunctionGroupLock::FunctionGroupLock(MutExHookGroup group) : m_Group(group) 101 | { 102 | m_Active = HookStack::instance().setGroup(m_Group); 103 | } 104 | 105 | FunctionGroupLock::~FunctionGroupLock() 106 | { 107 | if (m_Active) { 108 | HookStack::instance().unsetGroup(m_Group); 109 | } 110 | } 111 | 112 | } // namespace usvfs 113 | -------------------------------------------------------------------------------- /src/usvfs_dll/hookcallcontext.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | namespace usvfs 24 | { 25 | 26 | /** 27 | * @brief groups of hooks which may be used to implement each other, so only the first 28 | * call should be to one should be manipulated 29 | */ 30 | enum class MutExHookGroup : int 31 | { 32 | ALL_GROUPS = 0, // An ALL_GROUPS-hook prevents all other hooks from becoming active 33 | // BUT hooks from other groups don't prevent the ALL_GROUPS-hook from 34 | // becoming activated 35 | OPEN_FILE = 1, 36 | CREATE_PROCESS = 2, 37 | FILE_ATTRIBUTES = 3, 38 | FIND_FILES = 4, 39 | LOAD_LIBRARY = 5, 40 | FULL_PATHNAME = 6, 41 | SHELL_FILEOP = 7, 42 | DELETE_FILE = 8, 43 | GET_FILE_VERSION = 9, 44 | GET_MODULE_HANDLE = 10, 45 | SEARCH_FILES = 11, 46 | 47 | NO_GROUP = 12, 48 | LAST = NO_GROUP, 49 | }; 50 | 51 | class HookCallContext 52 | { 53 | 54 | public: 55 | HookCallContext(); 56 | HookCallContext(MutExHookGroup group); 57 | ~HookCallContext(); 58 | 59 | HookCallContext(const HookCallContext& reference) = delete; 60 | HookCallContext& operator=(const HookCallContext& reference) = delete; 61 | 62 | void restoreLastError(); 63 | 64 | void updateLastError(DWORD lastError = GetLastError()); 65 | 66 | DWORD lastError() const { return m_LastError; } 67 | 68 | bool active() const; 69 | 70 | private: 71 | DWORD m_LastError; 72 | bool m_Active; 73 | MutExHookGroup m_Group; 74 | }; 75 | 76 | class FunctionGroupLock 77 | { 78 | public: 79 | FunctionGroupLock(MutExHookGroup group); 80 | ~FunctionGroupLock(); 81 | 82 | private: 83 | MutExHookGroup m_Group; 84 | bool m_Active; 85 | }; 86 | 87 | } // namespace usvfs 88 | -------------------------------------------------------------------------------- /src/usvfs_dll/hookmanager.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "hookcontext.h" 24 | #include 25 | #include 26 | 27 | namespace usvfs 28 | { 29 | 30 | class HookManager 31 | { 32 | public: 33 | HookManager(const usvfsParameters& params, HMODULE module); 34 | ~HookManager(); 35 | 36 | HookManager(const HookManager& reference) = delete; 37 | 38 | HookManager& operator=(const HookManager& reference) = delete; 39 | 40 | static HookManager& instance(); 41 | 42 | HookContext* context() { return &m_Context; } 43 | 44 | /// 45 | /// \brief retrieve address of the detour of a function 46 | /// \param functionName name of the function to look up 47 | /// \return function address that can be used to directly execute the original code 48 | /// 49 | LPVOID detour(const char* functionName); 50 | 51 | /// 52 | /// \brief remove the hook on the specified function. 53 | /// \param functionName name of the function to unhook 54 | /// \note This function is only exposed to allow a workaround for ExitProcess and may 55 | /// be 56 | /// removed if a better solution is found there. If you have another legit use 57 | /// case, please let me know! 58 | /// 59 | void removeHook(const std::string& functionName); 60 | 61 | private: 62 | void logStubInt(LPVOID address); 63 | static void logStub(LPVOID address); 64 | 65 | void installHook(HMODULE module1, HMODULE module2, const std::string& functionName, 66 | LPVOID hook, LPVOID* fillFuncAddr); 67 | void installStub(HMODULE module1, HMODULE module2, const std::string& functionName); 68 | void initHooks(); 69 | void removeHooks(); 70 | 71 | private: 72 | static HookManager* s_Instance; 73 | 74 | std::map m_Hooks; 75 | 76 | std::map m_Stubs; 77 | 78 | HookContext m_Context; 79 | }; 80 | 81 | } // namespace usvfs 82 | -------------------------------------------------------------------------------- /src/usvfs_dll/hooks/ntdll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace usvfs 8 | { 9 | 10 | DLLEXPORT NTSTATUS WINAPI 11 | hook_NtQueryFullAttributesFile(POBJECT_ATTRIBUTES ObjectAttributes, 12 | PFILE_NETWORK_OPEN_INFORMATION FileInformation); 13 | 14 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryAttributesFile( 15 | POBJECT_ATTRIBUTES ObjectAttributes, PFILE_BASIC_INFORMATION FileInformation); 16 | 17 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFile( 18 | HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, 19 | PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, 20 | FILE_INFORMATION_CLASS FileInformationClass, BOOLEAN ReturnSingleEntry, 21 | PUNICODE_STRING FileName, BOOLEAN RestartScan); 22 | 23 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryDirectoryFileEx( 24 | HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, 25 | PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, 26 | FILE_INFORMATION_CLASS FileInformationClass, ULONG QueryFlags, 27 | PUNICODE_STRING FileName); 28 | 29 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryObject( 30 | HANDLE Handle, OBJECT_INFORMATION_CLASS ObjectInformationClass, 31 | PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength); 32 | 33 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationFile( 34 | HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, 35 | ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); 36 | 37 | DLLEXPORT NTSTATUS WINAPI hook_NtQueryInformationByName( 38 | POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, 39 | PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); 40 | 41 | DLLEXPORT NTSTATUS WINAPI hook_NtOpenFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess, 42 | POBJECT_ATTRIBUTES ObjectAttributes, 43 | PIO_STATUS_BLOCK IoStatusBlock, 44 | ULONG ShareAccess, ULONG OpenOptions); 45 | 46 | DLLEXPORT NTSTATUS WINAPI hook_NtCreateFile( 47 | PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, 48 | PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER AllocationSize, ULONG FileAttributes, 49 | ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, PVOID EaBuffer, 50 | ULONG EaLength); 51 | 52 | DLLEXPORT NTSTATUS WINAPI hook_NtClose(HANDLE Handle); 53 | 54 | DLLEXPORT NTSTATUS WINAPI hook_NtTerminateProcess(HANDLE ProcessHandle, 55 | NTSTATUS ExitStatus); 56 | 57 | } // namespace usvfs 58 | -------------------------------------------------------------------------------- /src/usvfs_dll/hooks/sharedids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../hookcontext.h" 4 | 5 | typedef std::map SearchHandleMap; 6 | 7 | // maps handles opened for searching to the original search path, which is 8 | // necessary if the handle creation was rerouted 9 | DATA_ID(SearchHandles); 10 | -------------------------------------------------------------------------------- /src/usvfs_dll/pch.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | -------------------------------------------------------------------------------- /src/usvfs_dll/redirectiontree.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include "redirectiontree.h" 22 | 23 | std::ostream& usvfs::operator<<(std::ostream& stream, const RedirectionData& data) 24 | { 25 | stream << data.linkTarget; 26 | return stream; 27 | } 28 | -------------------------------------------------------------------------------- /src/usvfs_dll/redirectiontree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include 24 | 25 | namespace usvfs 26 | { 27 | 28 | // typedef boost::interprocess::basic_string, 29 | // shared::CharAllocatorT> StringT; 30 | 31 | namespace shared 32 | { 33 | static const TreeFlags FLAG_CREATETARGET = FLAG_FIRSTUSERFLAG + 0x00; 34 | } 35 | 36 | struct RedirectionDataLocal 37 | { 38 | 39 | RedirectionDataLocal(const char* target) : linkTarget(target) {} 40 | 41 | RedirectionDataLocal(const std::string& target) : linkTarget(target) {} 42 | 43 | std::string linkTarget; 44 | }; 45 | 46 | struct RedirectionData 47 | { 48 | 49 | RedirectionData(const RedirectionData& reference, 50 | const shared::VoidAllocatorT& allocator) 51 | : linkTarget(reference.linkTarget.c_str(), allocator) 52 | {} 53 | 54 | RedirectionData(const RedirectionDataLocal& reference, 55 | const shared::VoidAllocatorT& allocator) 56 | : linkTarget(reference.linkTarget.c_str(), allocator) 57 | {} 58 | 59 | RedirectionData(const char* target, const shared::VoidAllocatorT& allocator) 60 | : linkTarget(target, allocator) 61 | {} 62 | 63 | shared::StringT linkTarget; 64 | }; 65 | 66 | std::ostream& operator<<(std::ostream& stream, const RedirectionData& data); 67 | 68 | template <> 69 | inline void shared::dataAssign(RedirectionData& destination, 70 | const RedirectionData& source) 71 | { 72 | destination.linkTarget.assign(source.linkTarget.c_str()); 73 | } 74 | 75 | template <> 76 | inline RedirectionData 77 | shared::createDataEmpty(const VoidAllocatorT& allocator) 78 | { 79 | return RedirectionData("", allocator); 80 | } 81 | 82 | template 83 | struct shared::SHMDataCreator 84 | { 85 | static RedirectionData create(T source, const VoidAllocatorT& allocator) 86 | { 87 | return RedirectionData(source, allocator); 88 | } 89 | }; 90 | 91 | template <> 92 | struct shared::SHMDataCreator 93 | { 94 | static RedirectionData create(const RedirectionData& source, 95 | const VoidAllocatorT& allocator) 96 | { 97 | return RedirectionData(source, allocator); 98 | } 99 | }; 100 | 101 | using RedirectionTree = shared::DirectoryTree; 102 | using RedirectionTreeContainer = shared::TreeContainer; 103 | 104 | } // namespace usvfs 105 | -------------------------------------------------------------------------------- /src/usvfs_dll/semaphore.cpp: -------------------------------------------------------------------------------- 1 | #include "semaphore.h" 2 | #include "exceptionex.h" 3 | 4 | RecursiveBenaphore::RecursiveBenaphore() : m_Counter(0), m_OwnerId(0UL), m_Recursion(0) 5 | { 6 | m_Semaphore = ::CreateSemaphore(nullptr, 1, 1, nullptr); 7 | } 8 | 9 | RecursiveBenaphore::~RecursiveBenaphore() 10 | { 11 | ::CloseHandle(m_Semaphore); 12 | } 13 | 14 | void RecursiveBenaphore::wait(DWORD timeout) 15 | { 16 | DWORD tid = ::GetCurrentThreadId(); 17 | 18 | if (::_InterlockedIncrement(&m_Counter) > 1) { 19 | if (tid != m_OwnerId) { 20 | int tries = 3; 21 | while (::WaitForSingleObject(m_Semaphore, timeout) != WAIT_OBJECT_0) { 22 | HANDLE owner = ::OpenThread(SYNCHRONIZE, FALSE, m_OwnerId); 23 | ON_BLOCK_EXIT([owner]() { 24 | ::CloseHandle(owner); 25 | }); 26 | if ((tries <= 0) || (::WaitForSingleObject(owner, 0) == WAIT_OBJECT_0)) { 27 | // owner has quit without releasing the semaphore! 28 | m_Recursion = 0; 29 | spdlog::get("usvfs")->error("thread {} never released the mutex", m_OwnerId); 30 | break; 31 | } else { 32 | --tries; 33 | } 34 | } 35 | } 36 | } 37 | m_OwnerId = tid; 38 | ++m_Recursion; 39 | } 40 | 41 | void RecursiveBenaphore::signal() 42 | { 43 | if (m_Recursion == 0) { 44 | return; 45 | } 46 | // no validation the signaling thread is the one owning the lock 47 | DWORD recursion = --m_Recursion; 48 | if (recursion == 0) { 49 | m_OwnerId = 0; 50 | } 51 | DWORD result = ::_InterlockedDecrement(&m_Counter); 52 | if (result > 0) { 53 | if (recursion == 0) { 54 | ::ReleaseSemaphore(m_Semaphore, 1, nullptr); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/usvfs_dll/semaphore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // based on code by Jeff Preshing 4 | 5 | // this is a synchronization class that prefers 6 | // undefined behaviour over deadlock. It's utterly broken 7 | // and needs to be replaced in time. 8 | 9 | class RecursiveBenaphore 10 | { 11 | 12 | public: 13 | RecursiveBenaphore(); 14 | ~RecursiveBenaphore(); 15 | 16 | // wait on the semaphore. after timeout this will check if the current owner 17 | // thread is still alive and steal the semaphore if it isn't. Otherwise this 18 | // will continue to wait. 19 | void wait(DWORD timeout = INFINITE); 20 | void signal(); 21 | 22 | private: 23 | LONG m_Counter; 24 | DWORD m_OwnerId; 25 | int m_Recursion; 26 | HANDLE m_Semaphore; 27 | }; 28 | -------------------------------------------------------------------------------- /src/usvfs_dll/stringcast_boost.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #include 22 | 23 | namespace usvfs 24 | { 25 | namespace shared 26 | { 27 | 28 | template 29 | class string_cast_impl> 30 | { 31 | public: 32 | static ToT 33 | cast(const boost::container::basic_string& source, 34 | CodePage codePage, size_t sourceLength) 35 | { 36 | return string_cast_impl::cast(source.c_str(), codePage, 37 | sourceLength); 38 | } 39 | }; 40 | 41 | } // namespace shared 42 | } // namespace usvfs 43 | -------------------------------------------------------------------------------- /src/usvfs_dll/usvfsparameters.cpp: -------------------------------------------------------------------------------- 1 | #include "usvfsparametersprivate.h" 2 | #include 3 | 4 | usvfsParameters::usvfsParameters() 5 | : debugMode(false), logLevel(LogLevel::Debug), crashDumpsType(CrashDumpsType::None), 6 | delayProcessMs(0) 7 | { 8 | std::fill(std::begin(instanceName), std::end(instanceName), 0); 9 | std::fill(std::begin(currentSHMName), std::end(currentSHMName), 0); 10 | std::fill(std::begin(currentInverseSHMName), std::end(currentInverseSHMName), 0); 11 | std::fill(std::begin(crashDumpsPath), std::end(crashDumpsPath), 0); 12 | } 13 | 14 | usvfsParameters::usvfsParameters(const char* instanceName, const char* currentSHMName, 15 | const char* currentInverseSHMName, bool debugMode, 16 | LogLevel logLevel, CrashDumpsType crashDumpsType, 17 | const char* crashDumpsPath, int delayProcessMs) 18 | : usvfsParameters() 19 | { 20 | strncpy_s(this->instanceName, instanceName, _TRUNCATE); 21 | strncpy_s(this->currentSHMName, currentSHMName, _TRUNCATE); 22 | strncpy_s(this->currentInverseSHMName, currentInverseSHMName, _TRUNCATE); 23 | this->debugMode = debugMode; 24 | this->logLevel = logLevel; 25 | this->crashDumpsType = crashDumpsType; 26 | strncpy_s(this->crashDumpsPath, crashDumpsPath, _TRUNCATE); 27 | this->delayProcessMs = delayProcessMs; 28 | } 29 | 30 | usvfsParameters::usvfsParameters(const USVFSParameters& oldParams) 31 | : usvfsParameters(oldParams.instanceName, oldParams.currentSHMName, 32 | oldParams.currentInverseSHMName, oldParams.debugMode, 33 | oldParams.logLevel, oldParams.crashDumpsType, 34 | oldParams.crashDumpsPath, 0) 35 | {} 36 | 37 | void usvfsParameters::setInstanceName(const char* name) 38 | { 39 | strncpy_s(instanceName, name, _TRUNCATE); 40 | strncpy_s(currentSHMName, 60, name, _TRUNCATE); 41 | memset(currentInverseSHMName, '\0', _countof(currentInverseSHMName)); 42 | _snprintf(currentInverseSHMName, 60, "inv_%s", name); 43 | } 44 | 45 | void usvfsParameters::setDebugMode(bool b) 46 | { 47 | debugMode = b; 48 | } 49 | 50 | void usvfsParameters::setLogLevel(LogLevel level) 51 | { 52 | logLevel = level; 53 | } 54 | 55 | void usvfsParameters::setCrashDumpType(CrashDumpsType type) 56 | { 57 | crashDumpsType = type; 58 | } 59 | 60 | void usvfsParameters::setCrashDumpPath(const char* path) 61 | { 62 | if (path && *path && strlen(path) < _countof(crashDumpsPath)) { 63 | memcpy(crashDumpsPath, path, strlen(path) + 1); 64 | } else { 65 | // crashDumpsPath invalid or overflow of USVFSParameters variable so disable 66 | // crash dumps: 67 | crashDumpsPath[0] = 0; 68 | crashDumpsType = CrashDumpsType::None; 69 | } 70 | } 71 | 72 | void usvfsParameters::setProcessDelay(int milliseconds) 73 | { 74 | delayProcessMs = milliseconds; 75 | } 76 | 77 | extern "C" 78 | { 79 | 80 | const char* usvfsLogLevelToString(LogLevel lv) 81 | { 82 | switch (lv) { 83 | case LogLevel::Debug: 84 | return "debug"; 85 | 86 | case LogLevel::Info: 87 | return "info"; 88 | 89 | case LogLevel::Warning: 90 | return "warning"; 91 | 92 | case LogLevel::Error: 93 | return "error"; 94 | 95 | default: 96 | return "unknown"; 97 | } 98 | } 99 | 100 | const char* usvfsCrashDumpTypeToString(CrashDumpsType t) 101 | { 102 | switch (t) { 103 | case CrashDumpsType::None: 104 | return "none"; 105 | 106 | case CrashDumpsType::Mini: 107 | return "mini"; 108 | 109 | case CrashDumpsType::Data: 110 | return "data"; 111 | 112 | case CrashDumpsType::Full: 113 | return "full"; 114 | 115 | default: 116 | return "unknown"; 117 | } 118 | } 119 | 120 | usvfsParameters* usvfsCreateParameters() 121 | { 122 | return new (std::nothrow) usvfsParameters; 123 | } 124 | 125 | usvfsParameters* usvfsDupeParameters(usvfsParameters* p) 126 | { 127 | if (!p) { 128 | return nullptr; 129 | } 130 | 131 | auto* dupe = usvfsCreateParameters(); 132 | if (!dupe) { 133 | return nullptr; 134 | } 135 | 136 | *dupe = *p; 137 | 138 | return dupe; 139 | } 140 | 141 | void usvfsCopyParameters(const usvfsParameters* source, usvfsParameters* dest) 142 | { 143 | *dest = *source; 144 | } 145 | 146 | void usvfsFreeParameters(usvfsParameters* p) 147 | { 148 | delete p; 149 | } 150 | 151 | void usvfsSetInstanceName(usvfsParameters* p, const char* name) 152 | { 153 | if (p) { 154 | p->setInstanceName(name); 155 | } 156 | } 157 | 158 | void usvfsSetDebugMode(usvfsParameters* p, BOOL debugMode) 159 | { 160 | if (p) { 161 | p->setDebugMode(debugMode); 162 | } 163 | } 164 | 165 | void usvfsSetLogLevel(usvfsParameters* p, LogLevel level) 166 | { 167 | if (p) { 168 | p->setLogLevel(level); 169 | } 170 | } 171 | 172 | void usvfsSetCrashDumpType(usvfsParameters* p, CrashDumpsType type) 173 | { 174 | if (p) { 175 | p->setCrashDumpType(type); 176 | } 177 | } 178 | 179 | void usvfsSetCrashDumpPath(usvfsParameters* p, const char* path) 180 | { 181 | if (p) { 182 | p->setCrashDumpPath(path); 183 | } 184 | } 185 | 186 | void usvfsSetProcessDelay(usvfsParameters* p, int milliseconds) 187 | { 188 | if (p) { 189 | p->setProcessDelay(milliseconds); 190 | } 191 | } 192 | 193 | } // extern "C" 194 | -------------------------------------------------------------------------------- /src/usvfs_dll/version.rc: -------------------------------------------------------------------------------- 1 | #include "Winver.h" 2 | #include "..\..\include\usvfs\usvfs_version.h" 3 | 4 | #define VER_FILEVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION 5 | #define VER_FILEVERSION_STR USVFS_VERSION_STRING 6 | 7 | #define VER_PRODUCTVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION 8 | #define VER_PRODUCTVERSION_STR USVFS_VERSION_STRING 9 | 10 | VS_VERSION_INFO VERSIONINFO 11 | FILEVERSION VER_FILEVERSION 12 | PRODUCTVERSION VER_PRODUCTVERSION 13 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 14 | FILEFLAGS (0) 15 | FILEOS VOS__WINDOWS32 16 | FILETYPE VFT_DLL 17 | FILESUBTYPE VFT2_UNKNOWN 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904B0" 22 | BEGIN 23 | VALUE "FileVersion", VER_FILEVERSION_STR 24 | VALUE "CompanyName", "Mod Organizer 2 Team\0" 25 | VALUE "FileDescription", "USVFS\0" 26 | #ifdef _WIN64 27 | VALUE "OriginalFilename", "usvfs_x64.dll\0" 28 | #else 29 | VALUE "OriginalFilename", "usvfs_x86.dll\0" 30 | #endif 31 | VALUE "ProductName", "USVFS\0" 32 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 33 | END 34 | END 35 | 36 | BLOCK "VarFileInfo" 37 | BEGIN 38 | VALUE "Translation", 0x0409L, 1200 39 | END 40 | END 41 | -------------------------------------------------------------------------------- /src/usvfs_helper/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | add_library(usvfs_helper STATIC inject.h inject.cpp) 4 | target_link_libraries(usvfs_helper PUBLIC shared PRIVATE tinjectlib) 5 | target_include_directories(usvfs_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 6 | set_target_properties(usvfs_helper PROPERTIES FOLDER injection) 7 | -------------------------------------------------------------------------------- /src/usvfs_helper/inject.h: -------------------------------------------------------------------------------- 1 | /* 2 | Userspace Virtual Filesystem 3 | 4 | Copyright (C) 2015 Sebastian Herbord. All rights reserved. 5 | 6 | This file is part of usvfs. 7 | 8 | usvfs is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | usvfs is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with usvfs. If not, see . 20 | */ 21 | #pragma once 22 | 23 | #include "usvfsparameters.h" 24 | #include 25 | #include 26 | 27 | namespace usvfs 28 | { 29 | 30 | /** 31 | * @brief inject usvfs to a process 32 | * @param applicationPath 33 | * @param parameters 34 | * @param processInfo 35 | */ 36 | void injectProcess(const std::wstring& applicationPath, 37 | const usvfsParameters& parameters, 38 | const PROCESS_INFORMATION& processInfo); 39 | 40 | /** 41 | * @brief inject usvfs to a process 42 | * @param applicationPath path to usvfs 43 | * @param parameters 44 | * @param process process handle to inject to 45 | * @param thread main thread inside that process. This can be set to 46 | * INVALID_HANDLE_VALUE in which case a new thread is created in the process 47 | */ 48 | void injectProcess(const std::wstring& applicationPath, 49 | const usvfsParameters& parameters, HANDLE process, HANDLE thread); 50 | 51 | } // namespace usvfs 52 | -------------------------------------------------------------------------------- /src/usvfs_proxy/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(Boost CONFIG REQUIRED COMPONENTS filesystem) 4 | 5 | add_executable(usvfs_proxy main.cpp version.rc) 6 | target_link_libraries(usvfs_proxy PRIVATE usvfs_dll shared usvfs_helper) 7 | set_target_properties(usvfs_proxy 8 | PROPERTIES 9 | RUNTIME_OUTPUT_NAME usvfs_proxy${ARCH_POSTFIX} 10 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_BINDIR} 11 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_BINDIR} 12 | ) 13 | 14 | install(TARGETS usvfs_proxy EXPORT usvfs${ARCH_POSTFIX}Targets) 15 | install(FILES $ DESTINATION pdb OPTIONAL) 16 | -------------------------------------------------------------------------------- /src/usvfs_proxy/version.rc: -------------------------------------------------------------------------------- 1 | #include "Winver.h" 2 | #include "..\..\include\usvfs\usvfs_version.h" 3 | 4 | #define VER_FILEVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION 5 | #define VER_FILEVERSION_STR USVFS_VERSION_STRING 6 | 7 | #define VER_PRODUCTVERSION USVFS_VERSION_MAJOR,USVFS_VERSION_MINOR,USVFS_VERSION_BUILD,USVFS_VERSION_REVISION 8 | #define VER_PRODUCTVERSION_STR USVFS_VERSION_STRING 9 | 10 | VS_VERSION_INFO VERSIONINFO 11 | FILEVERSION VER_FILEVERSION 12 | PRODUCTVERSION VER_PRODUCTVERSION 13 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 14 | FILEFLAGS (0) 15 | FILEOS VOS__WINDOWS32 16 | FILETYPE VFT_DLL 17 | FILESUBTYPE VFT2_UNKNOWN 18 | BEGIN 19 | BLOCK "StringFileInfo" 20 | BEGIN 21 | BLOCK "040904B0" 22 | BEGIN 23 | VALUE "FileVersion", VER_FILEVERSION_STR 24 | VALUE "CompanyName", "Mod Organizer 2 Team\0" 25 | VALUE "FileDescription", "USVFS Proxy\0" 26 | #ifdef _WIN64 27 | VALUE "OriginalFilename", "usvfs_proxy_x64.exe\0" 28 | #else 29 | VALUE "OriginalFilename", "usvfs_proxy_x86.exe\0" 30 | #endif 31 | VALUE "ProductName", "USVFS\0" 32 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 33 | END 34 | END 35 | 36 | BLOCK "VarFileInfo" 37 | BEGIN 38 | VALUE "Translation", 0x0409L, 1200 39 | END 40 | END 41 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | include(CMakeParseArguments) 4 | 5 | #! usvfs_set_test_properties 6 | # 7 | # this function sets the following properties on the given executable or shared 8 | # library test target: 9 | # - OUTPUT_NAME to add arch-specific prefix 10 | # - OUTPUT_DIRECTORY to put the test executable or shared library in the right location 11 | # - FOLDER to organize the VS solution layout 12 | # 13 | # \param:FOLDER if present, specifies the subfolder to use in the solution 14 | # 15 | function(usvfs_set_test_properties TARGET) 16 | cmake_parse_arguments(USVFS_TEST "" "FOLDER" "" ${ARGN}) 17 | if (NOT DEFINED USVFS_TEST_FOLDER) 18 | set(folder "tests") 19 | else() 20 | set(folder "tests/${USVFS_TEST_FOLDER}") 21 | endif() 22 | set_target_properties(${TARGET} 23 | PROPERTIES 24 | FOLDER ${folder} 25 | RUNTIME_OUTPUT_NAME ${TARGET}${ARCH_POSTFIX} 26 | RUNTIME_OUTPUT_DIRECTORY_DEBUG ${USVFS_TEST_BINDIR} 27 | RUNTIME_OUTPUT_DIRECTORY_RELEASE ${USVFS_TEST_BINDIR} 28 | RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${USVFS_TEST_BINDIR} 29 | ) 30 | endfunction() 31 | 32 | #! usvfs_target_link_usvfs 33 | # 34 | # add a target link between the given target and the usvfs shared library with delay 35 | # loading 36 | # 37 | function(usvfs_target_link_usvfs TARGET) 38 | target_link_libraries(${TARGET} PRIVATE usvfs_dll) 39 | target_link_options(${TARGET} PRIVATE "/DELAYLOAD:usvfs${ARCH_POSTFIX}.dll") 40 | endfunction() 41 | 42 | 43 | file(GLOB directories LIST_DIRECTORIES true "*") 44 | 45 | # this goes through all the directories and 46 | # 47 | # 1. add them if there is a CMakeLists.txt inside 48 | # 2. add correspondings tests (for CTest) for BOTH x86 and x64 when possible 49 | # 50 | foreach(directory ${directories}) 51 | if(NOT(IS_DIRECTORY ${directory})) 52 | continue() 53 | endif() 54 | 55 | if(NOT(EXISTS ${directory}/CMakeLists.txt)) 56 | continue() 57 | endif() 58 | 59 | add_subdirectory(${directory}) 60 | 61 | get_filename_component(dirname ${directory} NAME) 62 | if((dirname STREQUAL "test_utils") OR (dirname STREQUAL "gtest_utils")) 63 | continue() 64 | endif() 65 | 66 | add_test(NAME ${dirname}_x64 67 | COMMAND ${USVFS_TEST_BINDIR}/${dirname}_x64.exe 68 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 69 | ) 70 | add_test(NAME ${dirname}_x86 71 | COMMAND ${USVFS_TEST_BINDIR}/${dirname}_x86.exe 72 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} 73 | ) 74 | endforeach() 75 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/expected/data/file.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/empty/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/expected/mods/mod1/readme.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/source/data/file.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/empty/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/info.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BasicTest/source/mods/mod1/readme.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/data/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/subdocs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/expected/mods/mod1/docs/subdocs/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/source/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/data/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/subdocs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/BoostFilesystemTest/source/mods/mod1/docs/subdocs/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | std::filesystem::path storages_path; 5 | std::filesystem::path hudpainter_path; 6 | bool has_mo2; 7 | 8 | // See below main() for implementation. 9 | void request_directory(std::filesystem::path); 10 | std::filesystem::path restrict_path(std::filesystem::path); 11 | std::vector get_files(); 12 | std::string read_file(std::filesystem::path); 13 | void write_file(std::filesystem::path); 14 | 15 | // Expected path when not using MO2 16 | int main() 17 | { 18 | // RedFileSystem plugin (DLL) is loaded. 19 | has_mo2 = GetModuleHandle(TEXT("usvfs_x64.dll")) != nullptr; 20 | 21 | // Setup paths 22 | auto path = std::filesystem::absolute("."); // \bin\x64 23 | auto game_path = path.parent_path().parent_path(); // 24 | 25 | storages_path = game_path / "r6" / "storages"; // \r6\storages 26 | 27 | // Create storages directory if it is no present. 28 | request_directory(storages_path); // \r6\storages 29 | 30 | // Game is running... 31 | 32 | // HUD Painter request its storage endpoint. 33 | hudpainter_path = storages_path / "HUDPainter"; 34 | request_directory(hudpainter_path); // \r6\storages\HUDPainter 35 | 36 | // HUD Painter request a file (present in archive). 37 | auto default_path = 38 | restrict_path("DEFAULT.json"); // \r6\storages\HUDPainter\DEFAULT.json 39 | 40 | // HUD Painter read file in `default_path` with success. 41 | auto data = read_file(default_path); // \r6\storages\HUDPainter\DEFAULT.json 42 | 43 | // HUD Painter request another file. 44 | auto test_path = 45 | restrict_path("TEST.json"); // \r6\storages\HUDPainter\TEST.json 46 | 47 | // (Bug A) HUD Painter write file in `test_path`. 48 | write_file(test_path); // \r6\storages\HUDPainter\TEST.json 49 | // file is created with workaround, not created otherwise. 50 | 51 | // (Bug B) HUD Painter request list of files. 52 | // TEST.json is successfully created when using workaround. 53 | auto files = get_files(); // [0] \r6\storages\HUDPainter\DEFAULT.json 54 | 55 | // files.size() == 1 56 | 57 | // Expect: 58 | // files.size() == 2 59 | // 60 | // [0] \r6\storages\HUDPainter\DEFAULT.json 61 | // [1] \r6\storages\HUDPainter\TEST.json 62 | } 63 | 64 | // Create directory if it is no present 65 | void request_directory(std::filesystem::path path) 66 | { 67 | bool is_present = std::filesystem::exists(path); 68 | 69 | if (is_present) { 70 | return; 71 | } 72 | std::filesystem::create_directory(path); 73 | } 74 | 75 | // Resolve path for HUDPainter (security layer) 76 | std::filesystem::path restrict_path(std::filesystem::path path) 77 | { 78 | auto real_path = std::filesystem::weakly_canonical(hudpainter_path / path); 79 | 80 | // Workaround when using MO2 81 | if (has_mo2) { 82 | return real_path; 83 | } 84 | // Expected implementation without MO2 85 | if (real_path.string().find(hudpainter_path.string() + "\\") != 0) { 86 | // "throw" error 87 | } 88 | return real_path; 89 | } 90 | 91 | // List files in storage of HUDPainter. 92 | std::vector get_files() 93 | { 94 | std::vector files; 95 | auto entries = std::filesystem::directory_iterator(hudpainter_path); 96 | 97 | for (const auto& entry : entries) { 98 | if (entry.is_regular_file()) { 99 | auto file_name = entry.path().filename(); 100 | auto file_path = 101 | hudpainter_path / 102 | file_name; // Culprit of bug B. When using entry.path() directly, it works. 103 | 104 | files.emplace_back(file_path); 105 | } 106 | } 107 | return files; 108 | } 109 | 110 | std::string read_file(std::filesystem::path path) 111 | { 112 | std::ifstream stream; 113 | 114 | // With workaround: 115 | // std::filesystem::create_directories(path.parent_path()); 116 | stream.open(path); 117 | if (!stream.is_open()) { 118 | return ""; 119 | } 120 | std::stringstream data; 121 | 122 | data << stream.rdbuf(); 123 | stream.close(); 124 | return data.str(); 125 | } 126 | 127 | void write_file(std::filesystem::path path) 128 | { 129 | std::ofstream stream; 130 | 131 | // With workaround: 132 | // std::filesystem::create_directories(path.parent_path()); 133 | stream.open(path, std::ios_base::trunc); 134 | if (!stream.is_open()) { 135 | return; 136 | } 137 | stream << ""; 138 | stream.close(); 139 | } 140 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/RedFileSystemTest/expected/data/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/expected/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertiesDefault": [ 3 | { 4 | "name": "MainColors.Red", 5 | "red": 1.1761, 6 | "green": 0.3809, 7 | "blue": 0.3476, 8 | "alpha": 1 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/expected/overwrite/r6/storages/HUDPainter/TEST.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/RedFileSystemTest/source/data/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/RedFileSystemTest/source/mods/HUD Painter/r6/storages/HUDPainter/DEFAULT.json: -------------------------------------------------------------------------------- 1 | { 2 | "propertiesDefault": [ 3 | { 4 | "name": "MainColors.Red", 5 | "red": 1.1761, 6 | "green": 0.3809, 7 | "blue": 0.3476, 8 | "alpha": 1 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/docs/doc.skip: -------------------------------------------------------------------------------- 1 | doc.skip in data/docs 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/data/file.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.skip: -------------------------------------------------------------------------------- 1 | doc.skip in mod1/docs 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/empty/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.skip: -------------------------------------------------------------------------------- 1 | readme.skip in mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/expected/mods/mod1/readme.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/expected/overwrite/readme.skip: -------------------------------------------------------------------------------- 1 | readme.skip in overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/data/docs/doc.skip: -------------------------------------------------------------------------------- 1 | doc.skip in data/docs 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/source/data/file.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.skip: -------------------------------------------------------------------------------- 1 | doc.skip in mod1/docs 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/docs/doc.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/empty/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.skip: -------------------------------------------------------------------------------- 1 | readme.skip in mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_global_test/SkipFilesTest/source/mods/mod1/readme.txt -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rcopyme4.txt: -------------------------------------------------------------------------------- 1 | rfolder\rcopyme4.txt original contents 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfile0.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfile0.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfiledeletewrite.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/rfolder/rfilerewrite.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfilerewrite.txt rewrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/root0.txt: -------------------------------------------------------------------------------- 1 | root0 original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/root0w.txt: -------------------------------------------------------------------------------- 1 | root0w original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/root1.txt: -------------------------------------------------------------------------------- 1 | root1 original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount.postmortem/root1w.txt: -------------------------------------------------------------------------------- 1 | root1w original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rcopyme4.txt: -------------------------------------------------------------------------------- 1 | rfolder\rcopyme4.txt original contents 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rfile0.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfile0.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rfiledelete.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfiledelete.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfiledeletewrite.txt orignal file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rfileoldname.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfileoldname.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/rfolder/rfilerewrite.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfilerewrite.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/root0.txt: -------------------------------------------------------------------------------- 1 | root0 original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/root0w.txt: -------------------------------------------------------------------------------- 1 | root0w original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/root1.txt: -------------------------------------------------------------------------------- 1 | root1 original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/mount/root1w.txt: -------------------------------------------------------------------------------- 1 | root1w original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder1\mfile.txt by mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder1/mfilew.txt: -------------------------------------------------------------------------------- 1 | mfolder1\mfilew.txt by mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod1/mfolder3/mdeep3/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder3\mdeep3\mfile.txt by mod1 (to be hidden by mod3) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1.txt: -------------------------------------------------------------------------------- 1 | hello mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod1/mod1w.txt: -------------------------------------------------------------------------------- 1 | hello mod1w 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder2/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder2\mfile.txt by mod2 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletemove2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove2p.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfiledeletewrite2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite2p.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilemoveover.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfilemoveover.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfileoverwrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfileoverwrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mfolder4/mfilerewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfilerewrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod2/mod2.txt: -------------------------------------------------------------------------------- 1 | hello mod2 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder3/mdeep3/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder3\mdeep3\mfile.txt by mod3 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod3/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod3 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod3/mod3.txt: -------------------------------------------------------------------------------- 1 | hello mod3 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletemove.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfiledeletemove.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfiledeletewrite.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilemoveover.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfilemoveover.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfileoverwrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfileoverwrite.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/mod4/mfolder4/mfilerewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfilerewrite.txt rewrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_test/basic/source.postmortem/overwrite/.empty -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfile1.txt: -------------------------------------------------------------------------------- 1 | newfile1.txt nonrecursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder1/newfolder1/newfile1.txt: -------------------------------------------------------------------------------- 1 | newfile1.txt nonrecursive overwrite subfolder 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder2/newfile2.txt: -------------------------------------------------------------------------------- 1 | newfile2.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3.txt: -------------------------------------------------------------------------------- 1 | newfile3.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder3/newfolder3/newfile3e.txt: -------------------------------------------------------------------------------- 1 | newfile3e.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletemove2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfiledeletemove2p.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/mfiledeletewrite2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfiledeletewrite2p.txt overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4.txt: -------------------------------------------------------------------------------- 1 | newfile4.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4e.txt: -------------------------------------------------------------------------------- 1 | newfile4e.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/e/p/newfile4enr.txt: -------------------------------------------------------------------------------- 1 | newfile4enr.txt nonrecursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4.txt: -------------------------------------------------------------------------------- 1 | epnewfile4.txt nonrecursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4/d/e/epnewfile4r.txt: -------------------------------------------------------------------------------- 1 | epnewfile4r.txt recursive overwrite 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/newfolder4p/rcopyme4.txt: -------------------------------------------------------------------------------- 1 | rfolder\rcopyme4.txt original contents 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/mfolder4/rcopyme4.txt: -------------------------------------------------------------------------------- 1 | rfolder\rcopyme4.txt original contents 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/overwrite/rfolder/rfilenewname.txt: -------------------------------------------------------------------------------- 1 | rfolder\rfileoldname.txt original file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/root1.txt: -------------------------------------------------------------------------------- 1 | root1 source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/root1w.txt: -------------------------------------------------------------------------------- 1 | root1w source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/root2.txt: -------------------------------------------------------------------------------- 1 | root2 source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source.postmortem/root2w.txt: -------------------------------------------------------------------------------- 1 | root2w source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder1\mfile.txt by mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod1/mfolder1/mfilew.txt: -------------------------------------------------------------------------------- 1 | mfolder1\mfilew.txt by mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod1/mfolder3/mdeep3/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder3\mdeep3\mfile.txt by mod1 (to be hidden by mod3) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod1/mod1.txt: -------------------------------------------------------------------------------- 1 | hello mod1 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod1/mod1w.txt: -------------------------------------------------------------------------------- 1 | hello mod1w 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder2/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder2\mfile.txt by mod2 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletemove2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove2p.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfiledeletewrite2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite2p.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilemoveover.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfilemoveover.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfileoverwrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfileoverwrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mfolder4/mfilerewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfilerewrite.txt by mod2 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod2/mod2.txt: -------------------------------------------------------------------------------- 1 | hello mod2 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod3/mfolder3/mdeep3/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder3\mdeep3\mfile.txt by mod3 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod3/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod3 (to be hidden by mod4) 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod3/mod3.txt: -------------------------------------------------------------------------------- 1 | hello mod3 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfile.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfile.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletemove2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletemove2p.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfiledeletewrite2p.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfiledeletewrite2p.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilemoveover.txt: -------------------------------------------------------------------------------- 1 | mfolder4/mfilemoveover.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfileoverwrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfileoverwrite.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/mod4/mfolder4/mfilerewrite.txt: -------------------------------------------------------------------------------- 1 | mfolder4\mfilerewrite.txt by mod4 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/overwrite/.empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModOrganizer2/usvfs/a50d84c64c9244f80dc67e9fe7af209bfe514d5b/test/fixtures/usvfs_test/basic/source/overwrite/.empty -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/root1.txt: -------------------------------------------------------------------------------- 1 | root1 source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/root1w.txt: -------------------------------------------------------------------------------- 1 | root1w source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/root2.txt: -------------------------------------------------------------------------------- 1 | root2 source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/source/root2w.txt: -------------------------------------------------------------------------------- 1 | root2w source file 2 | -------------------------------------------------------------------------------- /test/fixtures/usvfs_test/basic/vfs_mappings.txt: -------------------------------------------------------------------------------- 1 | # mapdir 2 | # 3 | # ... 4 | mapdir 5 | mod1 6 | mod2 7 | mod3 8 | mod4 9 | 10 | # mapdircreate 11 | # 12 | # ... 13 | mapdircreate 14 | overwrite 15 | 16 | # mapfile 17 | # 18 | # ... 19 | mapfile root1.txt 20 | root1.txt 21 | mapfile root1w.txt 22 | root1w.txt 23 | mapfile root2.txt 24 | root2.txt 25 | mapfile root2w.txt 26 | root2w.txt 27 | -------------------------------------------------------------------------------- /test/gtest_utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | add_library(gtest_utils STATIC 6 | gtest_utils.cpp 7 | gtest_utils.h 8 | ) 9 | set_target_properties(gtest_utils PROPERTIES FOLDER tests) 10 | target_link_libraries(gtest_utils PUBLIC GTest::gtest) 11 | target_include_directories(gtest_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 | -------------------------------------------------------------------------------- /test/gtest_utils/gtest_utils.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest_utils.h" 2 | 3 | // this file is shared by both usvfs_global_test and usvfs_global_test_runner 4 | // 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | std::string trimmed(std::string content) 15 | { 16 | boost::algorithm::trim(content); 17 | return content; 18 | } 19 | 20 | // small utility to read files 21 | std::optional read_content(const std::filesystem::path& path, 22 | bool trim = true) 23 | { 24 | std::ifstream ifs(path, std::ios::binary | std::ios::ate); 25 | 26 | if (!ifs) { 27 | return {}; 28 | } 29 | 30 | const auto count = ifs.tellg(); 31 | 32 | std::string buffer(static_cast(count), '\0'); 33 | 34 | ifs.seekg(0, std::ios::beg); 35 | ifs.read(buffer.data(), count); 36 | 37 | if (trim) { 38 | boost::algorithm::trim(buffer); 39 | } 40 | 41 | return buffer; 42 | } 43 | 44 | ::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, 45 | const std::filesystem::path& actual, 46 | bool content) 47 | { 48 | std::vector failure_messages; 49 | std::vector in_both; 50 | 51 | // iterate left, check on right 52 | for (const auto& it : std::filesystem::recursive_directory_iterator{expected}) { 53 | const auto relpath = relative(it.path(), expected); 54 | if (!exists(actual / relpath)) { 55 | failure_messages.push_back( 56 | std::format("{} expected but not found", relpath.string())); 57 | } else { 58 | in_both.push_back(relpath); 59 | } 60 | } 61 | 62 | // iterate right, check on left 63 | for (const auto& it : std::filesystem::recursive_directory_iterator{actual}) { 64 | const auto relpath = relative(it.path(), actual); 65 | if (!exists(expected / relpath)) { 66 | failure_messages.push_back( 67 | std::format("{} found but not expected", relpath.string())); 68 | } 69 | } 70 | 71 | // check contents 72 | if (content) { 73 | for (const auto& relpath : in_both) { 74 | const auto expected_path = expected / relpath, actual_path = actual / relpath; 75 | 76 | if (is_directory(expected_path) != is_directory(actual_path)) { 77 | failure_messages.push_back( 78 | std::format("{} type mismatch, expected {} but found {}", relpath.string(), 79 | is_directory(expected_path) ? "directory" : "file", 80 | is_directory(expected_path) ? "file" : "directory")); 81 | continue; 82 | } 83 | 84 | if (is_directory(expected_path)) { 85 | continue; 86 | } 87 | 88 | if (read_content(expected_path) != read_content(actual_path)) { 89 | failure_messages.push_back( 90 | std::format("{} content mismatch", relpath.string())); 91 | } 92 | } 93 | } 94 | 95 | if (failure_messages.empty()) { 96 | return ::testing::AssertionSuccess(); 97 | } 98 | 99 | return ::testing::AssertionFailure() 100 | << "\n" 101 | << boost::algorithm::join(failure_messages, "\n") << "\n"; 102 | } 103 | 104 | ::testing::AssertionResult AssertContentEquals(std::string_view expected, 105 | const std::filesystem::path& path, 106 | bool trim) 107 | { 108 | const auto content = read_content(path, trim); 109 | 110 | if (!content) { 111 | return ::testing::AssertionFailure() 112 | << "failed to open path '" << path.string() << "'"; 113 | } 114 | 115 | if (*content != expected) { 116 | return ::testing::AssertionFailure() 117 | << "mismatch content for '" << path.string() << "', expected '" << expected 118 | << "', found '" << *content << "'"; 119 | } 120 | 121 | return ::testing::AssertionSuccess(); 122 | } 123 | -------------------------------------------------------------------------------- /test/gtest_utils/gtest_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // this file is shared by both usvfs_global_test and usvfs_global_test_runner 4 | // 5 | 6 | #include 7 | 8 | #include 9 | 10 | ::testing::AssertionResult AssertDirectoryEquals(const std::filesystem::path& expected, 11 | const std::filesystem::path& actual, 12 | bool content = true); 13 | 14 | ::testing::AssertionResult AssertContentEquals(std::string_view expected, 15 | const std::filesystem::path& path, 16 | bool trim = true); 17 | 18 | // macro to assert that the contents of two directories are identical - directories are 19 | // compared recursively and file contents are compared (excluding extra spaces or lines 20 | // in file) 21 | // 22 | #define ASSERT_DIRECTORY_EQ(Expected, Actual) \ 23 | ASSERT_TRUE(AssertDirectoryEquals(Expected, Actual)) 24 | 25 | // macro to assert that the contents of two files are identical (excluding extra space 26 | // or lines in file) 27 | // 28 | #define ASSERT_CONTENT_EQ(Expected, Path) \ 29 | ASSERT_TRUE(AssertContentEquals(Expected, Path)) 30 | -------------------------------------------------------------------------------- /test/shared_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | add_executable(shared_test main.cpp) 6 | usvfs_set_test_properties(shared_test) 7 | target_link_libraries(shared_test PRIVATE test_utils GTest::gtest GTest::gtest_main) 8 | -------------------------------------------------------------------------------- /test/test_utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | add_library(test_utils STATIC 6 | test_helpers.cpp 7 | test_helpers.h 8 | ) 9 | set_target_properties(test_utils PROPERTIES FOLDER tests) 10 | target_link_libraries(test_utils PUBLIC shared) 11 | target_include_directories(test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 | -------------------------------------------------------------------------------- /test/thooklib_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | find_package(Boost CONFIG REQUIRED COMPONENTS thread) 5 | 6 | # not sure why there is a test_hooks.cpp here? 7 | add_executable(thooklib_test main.cpp) 8 | usvfs_set_test_properties(thooklib_test) 9 | target_link_libraries(thooklib_test 10 | PRIVATE test_utils shared thooklib Boost::thread 11 | GTest::gtest GTest::gtest_main) 12 | -------------------------------------------------------------------------------- /test/thooklib_test/test_hooks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | HANDLE WINAPI THCreateFileA_1(LPCSTR lpFileName, DWORD dwDesiredAccess, 5 | DWORD dwShareMode, 6 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 7 | DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, 8 | HANDLE hTemplateFile) 9 | { 10 | if (strcmp(lpFileName, INVALID_FILENAME.c_str()) == 0) { 11 | return MARKERHANDLE; 12 | } else { 13 | HANDLE res = 14 | ::CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, 15 | dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); 16 | return res; 17 | } 18 | } 19 | 20 | HANDLE WINAPI THCreateFileW_1(LPCWSTR lpFileName, DWORD dwDesiredAccess, 21 | DWORD dwShareMode, 22 | LPSECURITY_ATTRIBUTES lpSecurityAttributes, 23 | DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, 24 | HANDLE hTemplateFile) 25 | { 26 | if (wcscmp(lpFileName, INVALID_FILENAME.w_str()) == 0) { 27 | EXPECT_EQ(0x42, dwDesiredAccess); 28 | EXPECT_EQ(0x43, dwShareMode); 29 | EXPECT_EQ(0x44, (int)lpSecurityAttributes); 30 | EXPECT_EQ(0x45, dwCreationDisposition); 31 | EXPECT_EQ(0x46, dwFlagsAndAttributes); 32 | EXPECT_EQ(0x47, (int)hTemplateFile); 33 | return MARKERHANDLE; 34 | } else { 35 | return ::CreateFileW(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, 36 | dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/tinjectlib_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | # binary and binary injected 6 | add_executable(testinject_bin testinject_bin/main.cpp) 7 | usvfs_set_test_properties(testinject_bin FOLDER tinjectlib) 8 | 9 | add_library(testinject_dll SHARED testinject_dll/main.cpp testinject_dll/main.h) 10 | usvfs_set_test_properties(testinject_dll FOLDER tinjectlib) 11 | 12 | # actual test executable 13 | add_executable(tinjectlib_test main.cpp) 14 | usvfs_set_test_properties(tinjectlib_test FOLDER tinjectlib) 15 | target_link_libraries(tinjectlib_test PRIVATE tinjectlib shared GTest::gtest GTest::gtest_main) 16 | add_dependencies(tinjectlib_test testinject_bin testinject_dll) 17 | -------------------------------------------------------------------------------- /test/tinjectlib_test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace usvfs::shared; 10 | using namespace InjectLib; 11 | 12 | #if BOOST_ARCH_X86_64 13 | static const wchar_t INJECT_BIN[] = L"testinject_bin_x64.exe"; 14 | #else 15 | static const wchar_t INJECT_BIN[] = L"testinject_bin_x86.exe"; 16 | #endif 17 | 18 | #if BOOST_ARCH_X86_64 19 | static const wchar_t INJECT_LIB[] = L"testinject_dll_x64.dll"; 20 | #else 21 | static const wchar_t INJECT_LIB[] = L"testinject_dll_x86.dll"; 22 | #endif 23 | 24 | static std::shared_ptr logger() 25 | { 26 | std::shared_ptr result = spdlog::get("test"); 27 | if (result.get() == nullptr) { 28 | result = spdlog::stdout_logger_mt("test"); 29 | } 30 | return result; 31 | } 32 | 33 | bool spawn(HANDLE& processHandle, HANDLE& threadHandle) 34 | { 35 | STARTUPINFO si; 36 | ::ZeroMemory(&si, sizeof(si)); 37 | si.cb = sizeof(si); 38 | 39 | PROCESS_INFORMATION pi; 40 | BOOL success = ::CreateProcess(INJECT_BIN, nullptr, nullptr, nullptr, FALSE, 41 | CREATE_SUSPENDED, nullptr, nullptr, &si, &pi); 42 | 43 | if (!success) { 44 | throw windows_error("failed to start process"); 45 | } 46 | 47 | processHandle = pi.hProcess; 48 | threadHandle = pi.hThread; 49 | 50 | return true; 51 | } 52 | 53 | TEST(InjectingTest, InjectionNoInit) 54 | { 55 | // Verify lib can inject without a init function 56 | 57 | HANDLE process, thread; 58 | spawn(process, thread); 59 | EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB)); 60 | ResumeThread(thread); 61 | 62 | DWORD res = WaitForSingleObject(process, INFINITE); 63 | DWORD exitCode = NO_ERROR; 64 | res = GetExitCodeProcess(process, &exitCode); 65 | EXPECT_EQ(NOERROR, exitCode); 66 | 67 | CloseHandle(process); 68 | CloseHandle(thread); 69 | } 70 | 71 | TEST(InjectingTest, InjectionSimpleInit) 72 | { 73 | // Verify lib can inject with a init function with null parameters 74 | 75 | HANDLE process, thread; 76 | spawn(process, thread); 77 | EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitNoParam")); 78 | ResumeThread(thread); 79 | 80 | DWORD res = WaitForSingleObject(process, INFINITE); 81 | DWORD exitCode = NO_ERROR; 82 | res = GetExitCodeProcess(process, &exitCode); 83 | EXPECT_EQ(10001, exitCode); // used init function exits process with this exit code 84 | 85 | CloseHandle(process); 86 | CloseHandle(thread); 87 | } 88 | 89 | TEST(InjectingTest, InjectionComplexInit) 90 | { 91 | // Verify lib can inject with a init function with null parameters 92 | 93 | static const WCHAR param[] = L"magic_parameter"; 94 | HANDLE process, thread; 95 | spawn(process, thread); 96 | EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitComplexParam", 97 | reinterpret_cast(param), 98 | wcslen(param) * sizeof(WCHAR))); 99 | 100 | ResumeThread(thread); 101 | 102 | DWORD res = WaitForSingleObject(process, INFINITE); 103 | DWORD exitCode = NO_ERROR; 104 | res = GetExitCodeProcess(process, &exitCode); 105 | EXPECT_EQ(10002, exitCode); // used init function exits process with this exit code 106 | 107 | CloseHandle(process); 108 | CloseHandle(thread); 109 | } 110 | 111 | TEST(InjectingTest, InjectionNoQuitInit) 112 | { 113 | // Verify lib can inject with a init function with null parameters 114 | 115 | HANDLE process, thread; 116 | spawn(process, thread); 117 | EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "InitNoQuit")); 118 | ResumeThread(thread); 119 | 120 | DWORD res = WaitForSingleObject(process, INFINITE); 121 | DWORD exitCode = NO_ERROR; 122 | res = GetExitCodeProcess(process, &exitCode); 123 | EXPECT_EQ(0, exitCode); // expect regular exit from process 124 | 125 | CloseHandle(process); 126 | CloseHandle(thread); 127 | } 128 | 129 | TEST(InjectingTest, InjectionSkipInit) 130 | { 131 | // verify the skip-on-missing mechanism for init function works 132 | 133 | HANDLE process, thread; 134 | spawn(process, thread); 135 | EXPECT_NO_THROW(InjectLib::InjectDLL(process, thread, INJECT_LIB, "__InitInvalid", 136 | nullptr, 0, true)); 137 | ResumeThread(thread); 138 | 139 | DWORD res = WaitForSingleObject(process, INFINITE); 140 | DWORD exitCode = NO_ERROR; 141 | res = GetExitCodeProcess(process, &exitCode); 142 | EXPECT_EQ(NOERROR, exitCode); 143 | 144 | CloseHandle(process); 145 | CloseHandle(thread); 146 | } 147 | 148 | int main(int argc, char** argv) 149 | { 150 | auto logger = spdlog::stdout_logger_mt("usvfs"); 151 | logger->set_level(spdlog::level::warn); 152 | 153 | boost::filesystem::path filePath(winapi::wide::getModuleFileName(nullptr)); 154 | SetCurrentDirectoryW(filePath.parent_path().wstring().c_str()); 155 | 156 | testing::InitGoogleTest(&argc, argv); 157 | return RUN_ALL_TESTS(); 158 | } 159 | -------------------------------------------------------------------------------- /test/tinjectlib_test/testinject_bin/main.cpp: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/tinjectlib_test/testinject_dll/main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | void __cdecl InitNoQuit(LPVOID, size_t) 4 | { 5 | // nop 6 | } 7 | 8 | void __cdecl InitNoParam(LPVOID, size_t) 9 | { 10 | ExitProcess(10001); 11 | } 12 | 13 | void __cdecl InitComplexParam(LPVOID userData, size_t) 14 | { 15 | LPCWSTR string = (LPCWSTR)userData; 16 | if (wcscmp(string, L"magic_parameter") == 0) { 17 | ExitProcess(10002); 18 | } else { 19 | ExitProcess(20003); 20 | } 21 | } 22 | 23 | BOOL APIENTRY DllMain(HMODULE, DWORD reasonForCall, LPVOID) 24 | { 25 | switch (reasonForCall) { 26 | case DLL_PROCESS_ATTACH: { 27 | } break; 28 | case DLL_PROCESS_DETACH: { 29 | } break; 30 | case DLL_THREAD_ATTACH: { 31 | } break; 32 | case DLL_THREAD_DETACH: { 33 | } break; 34 | } 35 | return TRUE; 36 | } 37 | -------------------------------------------------------------------------------- /test/tinjectlib_test/testinject_dll/main.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | 6 | extern "C" 7 | __declspec(dllexport) void __cdecl InitNoQuit(LPVOID userData, size_t userDataSize); 8 | extern "C" __declspec(dllexport) void __cdecl InitNoParam(LPVOID userData, 9 | size_t userDataSize); 10 | extern "C" __declspec(dllexport) void __cdecl InitComplexParam(LPVOID userData, 11 | size_t userDataSize); 12 | -------------------------------------------------------------------------------- /test/tvfs_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | add_executable(tvfs_test main.cpp) 6 | usvfs_set_test_properties(tvfs_test) 7 | target_link_libraries(tvfs_test PRIVATE test_utils usvfs_helper GTest::gtest GTest::gmock GTest::gtest_main) 8 | usvfs_target_link_usvfs(tvfs_test) 9 | 10 | # tvfs_test uses a private USVFS header so we need to include it manually 11 | get_target_property(USVFS_SOURCE_DIR usvfs_dll SOURCE_DIR) 12 | target_include_directories(tvfs_test PRIVATE ${USVFS_SOURCE_DIR}) 13 | -------------------------------------------------------------------------------- /test/usvfs_global_test_runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | find_package(Boost CONFIG REQUIRED COMPONENTS filesystem) 5 | 6 | # other executables 7 | add_executable(usvfs_global_test 8 | usvfs_global_test/usvfs_global_test.cpp 9 | ) 10 | usvfs_set_test_properties(usvfs_global_test FOLDER usvfs_global_test) 11 | target_link_libraries(usvfs_global_test PRIVATE gtest_utils GTest::gtest_main GTest::gmock Boost::filesystem) 12 | 13 | # actual test executable 14 | add_executable(usvfs_global_test_runner 15 | usvfs_global_test_fixture.cpp 16 | usvfs_global_test_fixture.h 17 | usvfs_global_test_runner.cpp 18 | ) 19 | usvfs_set_test_properties(usvfs_global_test_runner FOLDER usvfs_global_test) 20 | usvfs_target_link_usvfs(usvfs_global_test_runner) 21 | target_link_libraries(usvfs_global_test_runner 22 | PRIVATE test_utils gtest_utils GTest::gtest_main) 23 | add_dependencies(usvfs_global_test_runner usvfs_global_test) 24 | -------------------------------------------------------------------------------- /test/usvfs_global_test_runner/usvfs_global_test_fixture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | class UsvfsGlobalTest : public testing::Test 9 | { 10 | public: 11 | // enable log mode - this will generate USVFS log file for all tests regardless of 12 | // success or failure (default is to only generate for failure) 13 | // 14 | static void ForceUsvfsLogs(); 15 | 16 | public: 17 | UsvfsGlobalTest(); 18 | 19 | void SetUp() override { PrepareFileSystem(); } 20 | 21 | void TearDown() override { CleanUp(); } 22 | 23 | ~UsvfsGlobalTest(); 24 | 25 | // setup the overwrite folder 26 | // 27 | // if force is true, force creation of the overwrite folder even if not present, this 28 | // is useful when the overwrite is initially empty and thus cannot be committed to git 29 | // but need to contain files after the run 30 | // 31 | void SetUpOverwrite(bool force = true) const; 32 | 33 | // run the test, return the exit code of the google test process 34 | // 35 | int Run() const; 36 | 37 | // return the path to the folder containing the expected results 38 | // 39 | std::filesystem::path ActualFolder() const; 40 | 41 | // return the path to the folder containing the expected results 42 | // 43 | std::filesystem::path ExpectedFolder() const; 44 | 45 | private: 46 | class UsvfsGuard; 47 | 48 | // always generate usvfs logs 49 | static bool m_force_usvfs_logs; 50 | 51 | // prepare the filesystem by copying files and folders from the relevant fixtures 52 | // folder to the temporary folder 53 | // 54 | // after this operations, the temporary folder will contain 55 | // - a data folder 56 | // - [optional] a mods folder containing a set of folders that should be mounted 57 | // - [optional] an overwrite folder that should be mounted as overwrite 58 | // 59 | void PrepareFileSystem() const; 60 | 61 | // prepare mapping using the given set of paths 62 | // 63 | void PrepareMapping() const; 64 | 65 | // cleanup the temporary path 66 | // 67 | void CleanUp() const; 68 | 69 | // usvfs_guard 70 | std::unique_ptr m_usvfs; 71 | 72 | // name of GTest group (first argument of the TEST macro) to run 73 | std::wstring m_group; 74 | 75 | // path to the folder containing temporary files 76 | std::filesystem::path m_temporary_folder; 77 | 78 | // path to the subfolder inside the temporary folder 79 | std::filesystem::path m_data_folder = m_temporary_folder / "data"; 80 | std::filesystem::path m_mods_folder = m_temporary_folder / "mods"; 81 | std::filesystem::path m_overwrite_folder = m_temporary_folder / "overwrite"; 82 | }; 83 | -------------------------------------------------------------------------------- /test/usvfs_global_test_runner/usvfs_global_test_runner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "usvfs_global_test_fixture.h" 8 | 9 | TEST_F(UsvfsGlobalTest, BasicTest) 10 | { 11 | ASSERT_EQ(0, Run()); 12 | ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); 13 | } 14 | 15 | TEST_F(UsvfsGlobalTest, BoostFilesystemTest) 16 | { 17 | ASSERT_EQ(0, Run()); 18 | ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); 19 | } 20 | 21 | TEST_F(UsvfsGlobalTest, RedFileSystemTest) 22 | { 23 | SetUpOverwrite(true); 24 | ASSERT_EQ(0, Run()); 25 | ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); 26 | } 27 | 28 | TEST_F(UsvfsGlobalTest, SkipFilesTest) 29 | { 30 | SetUpOverwrite(true); 31 | 32 | usvfsAddSkipFileSuffix(L".skip"); 33 | 34 | ASSERT_EQ(0, Run()); 35 | ASSERT_DIRECTORY_EQ(ExpectedFolder(), ActualFolder()); 36 | } 37 | 38 | int main(int argc, char* argv[]) 39 | { 40 | // load the USVFS DLL 41 | // 42 | const auto usvfs_dll = 43 | test::path_of_usvfs_lib(test::platform_dependant_executable("usvfs", "dll")); 44 | test::ScopedLoadLibrary loadDll(usvfs_dll.c_str()); 45 | if (!loadDll) { 46 | std::wcerr << L"ERROR: failed to load usvfs dll: " << usvfs_dll.c_str() << L", " 47 | << GetLastError() << L"\n"; 48 | return 3; 49 | } 50 | 51 | testing::InitGoogleTest(&argc, argv); 52 | 53 | UsvfsGlobalTest::ForceUsvfsLogs(); 54 | 55 | usvfsInitLogging(false); 56 | 57 | return RUN_ALL_TESTS(); 58 | } 59 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | find_package(GTest CONFIG REQUIRED) 4 | 5 | # other executables 6 | add_executable(test_file_operations 7 | test_file_operations/test_file_operations.cpp 8 | test_file_operations/test_filesystem.cpp 9 | test_file_operations/test_filesystem.h 10 | test_file_operations/test_ntapi.cpp 11 | test_file_operations/test_ntapi.h 12 | test_file_operations/test_ntdll_declarations.h 13 | test_file_operations/test_w32api.cpp 14 | test_file_operations/test_w32api.h 15 | ) 16 | usvfs_set_test_properties(test_file_operations FOLDER usvfs_test_runner) 17 | target_link_libraries(test_file_operations PRIVATE test_utils ntdll) 18 | 19 | add_executable(usvfs_test 20 | usvfs_test/usvfs_basic_test.cpp 21 | usvfs_test/usvfs_basic_test.h 22 | usvfs_test/usvfs_test_base.cpp 23 | usvfs_test/usvfs_test_base.h 24 | usvfs_test/usvfs_test.cpp 25 | ) 26 | usvfs_set_test_properties(usvfs_test FOLDER usvfs_test_runner) 27 | usvfs_target_link_usvfs(usvfs_test) 28 | target_link_libraries(usvfs_test PRIVATE test_utils) 29 | 30 | # actual test executable 31 | add_executable(usvfs_test_runner usvfs_test_runner.cpp) 32 | usvfs_set_test_properties(usvfs_test_runner FOLDER usvfs_test_runner) 33 | target_link_libraries(usvfs_test_runner 34 | PRIVATE test_utils GTest::gtest GTest::gtest_main) 35 | add_dependencies(usvfs_test_runner usvfs_test test_file_operations) 36 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/test_file_operations/test_filesystem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class TestFileSystem 9 | { 10 | public: 11 | static constexpr auto FILE_CONTENTS_PRINT_PREFIX = "== "; 12 | 13 | typedef std::filesystem::path path; 14 | typedef std::FILE FILE; 15 | 16 | static path current_directory(); 17 | 18 | TestFileSystem(FILE* output); 19 | 20 | void set_output(FILE* output, bool clean) 21 | { 22 | m_output = output; 23 | m_cleanoutput = clean; 24 | } 25 | 26 | // base path used to trim outputs (which is important so we can compare tests ran at 27 | // different base paths) 28 | void set_basepath(const char* path) { m_basepath = real_path(path); } 29 | 30 | // returns the path relative to the base path 31 | path relative_path(path full_path); 32 | 33 | virtual const char* id() = 0; 34 | 35 | virtual path real_path(const char* abs_or_rel_path) = 0; 36 | 37 | struct FileInformation 38 | { 39 | std::wstring name; 40 | uint32_t attributes; 41 | uint64_t size; 42 | 43 | FileInformation(const std::wstring& iname, uint32_t iattributes, uint64_t isize) 44 | : name(iname), attributes(iattributes), size(isize) 45 | {} 46 | 47 | bool is_dir() const; 48 | bool is_file() const; 49 | }; 50 | typedef std::vector FileInfoList; 51 | 52 | virtual FileInfoList list_directory(const path& directory_path) = 0; 53 | 54 | virtual void create_path(const path& directory_path) = 0; 55 | 56 | virtual void read_file(const path& file_path) = 0; 57 | 58 | enum class write_mode 59 | { 60 | manual_truncate, 61 | truncate, 62 | create, 63 | overwrite, 64 | opencreate, 65 | append 66 | }; 67 | virtual void write_file(const path& file_path, const void* data, std::size_t size, 68 | bool add_new_line, write_mode mode, 69 | bool rw_access = false) = 0; 70 | 71 | virtual void touch_file(const path& file_path, bool full_write_access = false) = 0; 72 | 73 | virtual void delete_file(const path& file_path) = 0; 74 | 75 | virtual void copy_file(const path& source_path, const path& destination_path, 76 | bool replace_existing) = 0; 77 | 78 | virtual void rename_file(const path& source_path, const path& destination_path, 79 | bool replace_existing, bool allow_copy) = 0; 80 | 81 | protected: 82 | FILE* output() { return m_output; } 83 | bool cleanoutput() const { return m_cleanoutput; } 84 | static const char* write_operation_name(write_mode mode); 85 | static const char* rename_operation_name(bool replace_existing, bool allow_copy); 86 | 87 | public: // mainly for derived class (but also used by helper classes like SafeHandle so 88 | // public) 89 | void print_operation(const char* operation, const path& target); 90 | void print_operation(const char* operation, const path& source, const path& target); 91 | void print_result(const char* operation, uint32_t result, 92 | bool with_last_error = false, const char* opt_arg = nullptr, 93 | bool hide_result = false); 94 | void print_error(const char* operation, uint32_t result, bool with_last_error = false, 95 | const char* opt_arg = nullptr); 96 | void print_write_success(const void* data, std::size_t size, std::size_t written); 97 | 98 | uint32_t clean_attributes(uint32_t attr); 99 | 100 | private: 101 | FILE* m_output; 102 | bool m_cleanoutput = false; 103 | path m_basepath; 104 | }; 105 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/test_file_operations/test_ntapi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "test_filesystem.h" 4 | 5 | class TestNtApi : public TestFileSystem 6 | { 7 | public: 8 | TestNtApi(FILE* output) : TestFileSystem(output) {} 9 | 10 | path real_path(const char* abs_or_rel_path) override; 11 | 12 | FileInfoList list_directory(const path& directory_path) override; 13 | 14 | void create_path(const path& directory_path) override; 15 | 16 | void read_file(const path& file_path) override; 17 | 18 | void write_file(const path& file_path, const void* data, std::size_t size, 19 | bool add_new_line, write_mode mode, bool rw_access = false) override; 20 | 21 | void touch_file(const path& file_path, bool full_write_access = false) override; 22 | 23 | void delete_file(const path& file_path) override; 24 | 25 | void copy_file(const path& source_path, const path& destination_path, 26 | bool replace_existing) override; 27 | 28 | void rename_file(const path& source_path, const path& destination_path, 29 | bool replace_existing, bool allow_copy) override; 30 | 31 | const char* id() override; 32 | 33 | private: 34 | class SafeHandle; 35 | 36 | SafeHandle open_directory(const path& directory_path, bool create, 37 | bool allow_non_existence = false, long* pstatus = nullptr); 38 | }; 39 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/test_file_operations/test_w32api.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "test_filesystem.h" 4 | 5 | class TestW32Api : public TestFileSystem 6 | { 7 | public: 8 | TestW32Api(FILE* output) : TestFileSystem(output) {} 9 | 10 | path real_path(const char* abs_or_rel_path) override; 11 | 12 | FileInfoList list_directory(const path& directory_path) override; 13 | 14 | void create_path(const path& directory_path) override; 15 | 16 | void read_file(const path& file_path) override; 17 | 18 | void write_file(const path& file_path, const void* data, std::size_t size, 19 | bool add_new_line, write_mode mode, bool rw_access = false) override; 20 | 21 | void touch_file(const path& file_path, bool full_write_access = false) override; 22 | 23 | void delete_file(const path& file_path) override; 24 | 25 | void copy_file(const path& source_path, const path& destination_path, 26 | bool replace_existing) override; 27 | 28 | void rename_file(const path& source_path, const path& destination_path, 29 | bool replace_existing, bool allow_copy) override; 30 | 31 | const char* id() override; 32 | 33 | private: 34 | class SafeHandle; 35 | class SafeFindHandle; 36 | }; 37 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/usvfs_test/usvfs_basic_test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "usvfs_test_base.h" 4 | 5 | class usvfs_basic_test : public usvfs_test_base 6 | { 7 | public: 8 | static constexpr auto SCENARIO_NAME = "basic"; 9 | 10 | usvfs_basic_test(const usvfs_test_options& options) : usvfs_test_base(options) {} 11 | 12 | virtual const char* scenario_name(); 13 | virtual bool scenario_run(); 14 | }; 15 | -------------------------------------------------------------------------------- /test/usvfs_test_runner/usvfs_test_runner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static std::string usvfs_test_command(const char* scenario, const char* platform, 9 | const char* testflag = nullptr, 10 | const char* opsarg = nullptr) 11 | { 12 | using namespace test; 13 | std::string command = 14 | path_of_test_bin(platform_dependant_executable("usvfs_test", "exe", platform)) 15 | .string(); 16 | if (testflag) { 17 | command += " -"; 18 | command += testflag; 19 | } 20 | if (opsarg) { 21 | command += " -opsarg -"; 22 | command += opsarg; 23 | } 24 | command += " "; 25 | command += scenario; 26 | if (testflag || opsarg) { 27 | command += ":"; 28 | if (testflag) { 29 | command += testflag; 30 | command += "_"; 31 | } 32 | if (opsarg) { 33 | command += opsarg; 34 | command += "_"; 35 | } 36 | command += platform; 37 | } 38 | return command; 39 | } 40 | 41 | static DWORD spawn(std::string commandline) 42 | { 43 | STARTUPINFOA si{0}; 44 | si.cb = sizeof(si); 45 | PROCESS_INFORMATION pi{0}; 46 | 47 | std::cout << "Running: [" << commandline << "]" << std::endl; 48 | if (!CreateProcessA(NULL, commandline.data(), NULL, NULL, FALSE, 0, NULL, NULL, &si, 49 | &pi)) { 50 | DWORD gle = GetLastError(); 51 | std::cerr << "CreateProcess failed error=" << gle << std::endl; 52 | return 98; 53 | } 54 | 55 | WaitForSingleObject(pi.hProcess, INFINITE); 56 | 57 | DWORD exit = 99; 58 | if (!GetExitCodeProcess(pi.hProcess, &exit)) { 59 | DWORD gle = GetLastError(); 60 | std::cerr << "GetExitCodeProcess failed error=" << gle << std::endl; 61 | } 62 | 63 | CloseHandle(pi.hProcess); 64 | CloseHandle(pi.hThread); 65 | 66 | return exit; 67 | } 68 | 69 | TEST(UsvfsTest, basic_x64) 70 | { 71 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64"))); 72 | } 73 | 74 | TEST(UsvfsTest, basic_x86) 75 | { 76 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86"))); 77 | } 78 | 79 | TEST(UsvfsTest, basic_ops32_x64) 80 | { 81 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64", "ops32"))); 82 | } 83 | 84 | TEST(UsvfsTest, basic_ops64_x86) 85 | { 86 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86", "ops64"))); 87 | } 88 | 89 | /* 90 | TEST(UsvfsTest, basic_ntapi_x64) 91 | { 92 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x64", nullptr, "ntapi"))); 93 | } 94 | 95 | TEST(UsvfsTest, basic_ntapi_x86) 96 | { 97 | EXPECT_EQ(0, spawn(usvfs_test_command("basic", "x86", nullptr, "ntapi"))); 98 | } 99 | */ 100 | 101 | int main(int argc, char** argv) 102 | { 103 | testing::InitGoogleTest(&argc, argv); 104 | return RUN_ALL_TESTS(); 105 | } 106 | -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "repository": "https://github.com/Microsoft/vcpkg", 5 | "baseline": "294f76666c3000630d828703e675814c05a4fd43" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "git", 10 | "repository": "https://github.com/Microsoft/vcpkg", 11 | "baseline": "294f76666c3000630d828703e675814c05a4fd43", 12 | "packages": ["boost*", "boost-*"] 13 | }, 14 | { 15 | "kind": "git", 16 | "repository": "https://github.com/ModOrganizer2/vcpkg-registry", 17 | "baseline": "27d8adbfe9e4ce88a875be3a45fadab69869eb60", 18 | "packages": ["asmjit", "spdlog"] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "asmjit", 4 | "spdlog", 5 | "libudis86", 6 | "boost-algorithm", 7 | "boost-any", 8 | "boost-dll", 9 | "boost-filesystem", 10 | "boost-format", 11 | "boost-interprocess", 12 | "boost-headers", 13 | "boost-locale", 14 | "boost-multi-index", 15 | "boost-thread", 16 | "boost-predef" 17 | ], 18 | "features": { 19 | "testing": { 20 | "description": "Build USVFS tests.", 21 | "dependencies": ["gtest"] 22 | } 23 | } 24 | } 25 | --------------------------------------------------------------------------------