189 lines
4.9 KiB
C
189 lines
4.9 KiB
C
/*
|
|
* Copyright 2022 Hans-Kristian Arntzen for Valve Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
|
|
|
|
#include "vkd3d_file_utils.h"
|
|
#include "vkd3d_debug.h"
|
|
|
|
/* For disk cache. */
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
|
|
bool vkd3d_file_rename_overwrite(const char *from_path, const char *to_path)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD code = ERROR_SUCCESS;
|
|
|
|
if (!MoveFileA(from_path, to_path))
|
|
{
|
|
code = GetLastError();
|
|
if (code == ERROR_ALREADY_EXISTS)
|
|
{
|
|
code = ERROR_SUCCESS;
|
|
if (!ReplaceFileA(to_path, from_path, NULL, 0, NULL, NULL))
|
|
code = GetLastError();
|
|
}
|
|
}
|
|
|
|
return code == ERROR_SUCCESS;
|
|
#else
|
|
return rename(from_path, to_path) == 0;
|
|
#endif
|
|
}
|
|
|
|
bool vkd3d_file_rename_no_replace(const char *from_path, const char *to_path)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD code = ERROR_SUCCESS;
|
|
if (!MoveFileA(from_path, to_path))
|
|
code = GetLastError();
|
|
return code == ERROR_SUCCESS;
|
|
#else
|
|
return renameat2(AT_FDCWD, from_path, AT_FDCWD, to_path, RENAME_NOREPLACE) == 0;
|
|
#endif
|
|
}
|
|
|
|
bool vkd3d_file_delete(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD code = ERROR_SUCCESS;
|
|
if (!DeleteFileA(path))
|
|
code = GetLastError();
|
|
return code == ERROR_SUCCESS;
|
|
#else
|
|
return unlink(path) == 0;
|
|
#endif
|
|
}
|
|
|
|
FILE *vkd3d_file_open_exclusive_write(const char *path)
|
|
{
|
|
#ifdef _WIN32
|
|
/* From Fossilize. AFAIK, there is no direct way to make this work with FILE interface, so have to roundtrip
|
|
* through jank POSIX layer.
|
|
* wbx kinda works, but Wine warns about it, despite it working anyways.
|
|
* Older MSVC runtimes do not support wbx. */
|
|
FILE *file = NULL;
|
|
int fd;
|
|
fd = _open(path, _O_BINARY | _O_WRONLY | _O_CREAT | _O_EXCL | _O_TRUNC | _O_SEQUENTIAL,
|
|
_S_IWRITE | _S_IREAD);
|
|
if (fd >= 0)
|
|
{
|
|
file = _fdopen(fd, "wb");
|
|
/* _fdopen takes ownership. */
|
|
if (!file)
|
|
_close(fd);
|
|
}
|
|
return file;
|
|
#else
|
|
return fopen(path, "wbx");
|
|
#endif
|
|
}
|
|
|
|
void vkd3d_file_unmap(struct vkd3d_memory_mapped_file *file)
|
|
{
|
|
if (file->mapped)
|
|
{
|
|
#ifdef _WIN32
|
|
UnmapViewOfFile(file->mapped);
|
|
#else
|
|
munmap(file->mapped, file->mapped_size);
|
|
#endif
|
|
}
|
|
memset(file, 0, sizeof(*file));
|
|
}
|
|
|
|
bool vkd3d_file_map_read_only(const char *path, struct vkd3d_memory_mapped_file *file)
|
|
{
|
|
#ifdef _WIN32
|
|
DWORD size_hi, size_lo;
|
|
HANDLE file_mapping;
|
|
HANDLE handle;
|
|
#else
|
|
struct stat stat_buf;
|
|
int fd;
|
|
#endif
|
|
|
|
file->mapped = NULL;
|
|
file->mapped_size = 0;
|
|
|
|
#ifdef _WIN32
|
|
handle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
|
|
INVALID_HANDLE_VALUE);
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
goto out;
|
|
|
|
size_lo = GetFileSize(handle, &size_hi);
|
|
file->mapped_size = size_lo | (((uint64_t)size_hi) << 32);
|
|
|
|
file_mapping = CreateFileMappingA(handle, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (file_mapping == INVALID_HANDLE_VALUE)
|
|
goto out;
|
|
|
|
file->mapped = MapViewOfFile(file_mapping, FILE_MAP_READ, 0, 0, file->mapped_size);
|
|
CloseHandle(file_mapping);
|
|
file_mapping = INVALID_HANDLE_VALUE;
|
|
if (!file->mapped)
|
|
{
|
|
ERR("Failed to MapViewOfFile for %s.\n", path);
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
CloseHandle(handle);
|
|
#else
|
|
fd = open(path, O_RDONLY);
|
|
if (fd < 0)
|
|
goto out;
|
|
|
|
if (fstat(fd, &stat_buf) < 0)
|
|
{
|
|
ERR("Failed to fstat pipeline cache.\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Map private to make sure we get CoW behavior in case someone clobbers
|
|
* the cache while in flight. We need to read data directly out of the cache. */
|
|
file->mapped = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (file->mapped != MAP_FAILED)
|
|
file->mapped_size = stat_buf.st_size;
|
|
else
|
|
goto out;
|
|
|
|
out:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
#endif
|
|
|
|
if (!file->mapped)
|
|
file->mapped_size = 0;
|
|
return file->mapped != NULL;
|
|
}
|