carb/extras/WindowsPath.h

File members: carb/extras/WindowsPath.h

// Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../Defines.h"

#if CARB_PLATFORM_WINDOWS || defined(DOXYGEN_BUILD)

#    include "../CarbWindows.h"
#    include "../Error.h"
#    include "Unicode.h"

#    include <algorithm>
#    include <string>

namespace carb
{
namespace extras
{

std::wstring convertCarboniteToWindowsPath(const std::string& path);

std::string convertWindowsToCarbonitePath(const std::wstring& pathW);

std::wstring fixWindowsPathPrefix(const std::wstring& pathW);

std::wstring getWindowsCanonicalPath(const std::wstring& pathW);

std::wstring getWindowsFullPath(const std::wstring& pathW);

void adjustWindowsDllSearchPaths();

//
// Implementations
//

inline std::wstring convertCarboniteToWindowsPath(const std::string& path)
{
    std::wstring pathW = convertUtf8ToWide(path);
    if (pathW == kUnicodeToWideFailure)
    {
        ErrorApi::instance().setError(kResultFail);
        return L"";
    }
    std::replace(pathW.begin(), pathW.end(), L'/', L'\\');
    return fixWindowsPathPrefix(pathW);
}

inline std::string convertWindowsToCarbonitePath(const std::wstring& pathW)
{
    bool hasPrefix = (pathW.compare(0, 4, L"\\\\?\\") == 0);
    std::string path = convertWideToUtf8(pathW.c_str() + (hasPrefix ? 4 : 0));
    if (path == kUnicodeToUtf8Failure)
    {
        ErrorApi::instance().setError(kResultFail);
        return "";
    }
    std::replace(path.begin(), path.end(), '\\', '/');
    return path;
}

inline std::wstring fixWindowsPathPrefix(const std::wstring& pathW)
{
    bool hasPrefix = (pathW.compare(0, 4, L"\\\\?\\") == 0);

    if (pathW.size() >= CARBWIN_MAX_PATH && !hasPrefix)
    {
        return L"\\\\?\\" + pathW;
    }
    if (pathW.size() < CARBWIN_MAX_PATH && hasPrefix)
    {
        return pathW.substr(4, pathW.size() - 4);
    }

    return pathW;
}

inline std::wstring getWindowsCanonicalPath(const std::wstring& pathW)
{
    wchar_t* canonical = nullptr;
    auto hr = PathAllocCanonicalize(pathW.c_str(), CARBWIN_PATHCCH_ALLOW_LONG_PATHS, &canonical);
    if (CARBWIN_SUCCEEDED(hr))
    {
        std::wstring result = canonical;
        LocalFree(canonical);
        return result;
    }

    ErrorApi::instance().setError(
        omni::core::kResultFail, omni::string{ omni::formatted, "PathAllocCanonicalize failed with HRESULT 0x%08x", hr });
    return pathW;
}

inline std::wstring getWindowsFullPath(const std::wstring& pathW)
{
    // Retrieve the size
    DWORD size = GetFullPathNameW(pathW.c_str(), 0, nullptr, nullptr);
    if (size != 0)
    {
        std::wstring fullPathName(size - 1, '\0');
        size = GetFullPathNameW(pathW.c_str(), size, &fullPathName[0], nullptr);
        if (size)
        {
            // Assert if the Win32 API lied to us. Note that sometimes it does use less than asked for.
            CARB_ASSERT(size <= fullPathName.size());
            fullPathName.resize(size);
            return fullPathName;
        }
    }

    ErrorApi::setFromWinApiErrorCode();
    return pathW;
}

inline void adjustWindowsDllSearchPaths()
{
    // MSDN:
    // https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories
    // LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
    // This value is a combination of LOAD_LIBRARY_SEARCH_APPLICATION_DIR, LOAD_LIBRARY_SEARCH_SYSTEM32, and
    // LOAD_LIBRARY_SEARCH_USER_DIRS.
    SetDefaultDllDirectories(CARBWIN_LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
}

} // namespace extras
} // namespace carb

#endif