C
Qt Quick Ultralite fileloading Example
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
#include "posixfilesystem.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <cstring>
#include <string>
#include <filesystem>
#include <platforminterface/allocator.h>
#if defined(QUL_OS_LINUX)
#include <unistd.h>
#endif
#ifdef QUL_OS_MACOS
#include <unistd.h>
#define fstat64 fstat
#define stat64 stat
#endif
#ifdef QUL_OS_WIN
#include <io.h>
#include <windows.h>
#ifdef QUL_CC_MSVC
#define fstat64 _fstat64
#define stat64 __stat64
typedef uintptr_t ssize_t;
#endif
#endif
static std::string appFilePath()
{
#if defined(QUL_OS_LINUX) || defined(QUL_OS_MACOS)
std::filesystem::path executable(std::string("/proc/") + std::to_string(::getpid()) + std::string("/exe"));
if (!std::filesystem::exists(executable) || !std::filesystem::is_symlink(executable)) {
return std::string();
}
executable = std::filesystem::canonical(executable);
if (!std::filesystem::exists(executable))
return std::string();
return executable.remove_filename().string();
#elif defined(QUL_OS_WIN)
wchar_t buffer[MAX_PATH + 2];
DWORD v = GetModuleFileNameW(NULL, buffer, MAX_PATH + 1);
buffer[MAX_PATH + 1] = 0;
std::filesystem::path executable;
if (v == 0)
return NULL;
else if (v <= MAX_PATH) {
std::wstring ws(buffer);
executable = std::filesystem::path(std::string(ws.begin(), ws.end()));
return executable.remove_filename().string();
}
// MAX_PATH sized buffer wasn't large enough to contain the full path, use heap
wchar_t *b = 0;
int i = 1;
size_t size;
do {
++i;
size = MAX_PATH * i;
b = reinterpret_cast<wchar_t *>(std::realloc(b, (size + 1) * sizeof(wchar_t)));
if (b)
v = GetModuleFileNameW(NULL, b, size);
} while (b && v == size);
if (b)
*(b + size) = 0;
std::wstring res(b);
std::free(b);
executable = std::filesystem::path(std::string(res.begin(), res.end()));
return executable.remove_filename().string();
#else
#error Not implemented
#endif
}
PosixFile::PosixFile(int fileHandle)
{
m_fileHandle = fileHandle;
}
PosixFile::~PosixFile()
{
if (m_fileHandle >= 0)
close();
}
uint64_t PosixFile::size()
{
struct stat64 stat_buf;
int rc = ::fstat64(m_fileHandle, &stat_buf);
return rc == 0 ? stat_buf.st_size : 0;
}
int PosixFile::read(unsigned char *outputBuffer, uint64_t startOffset, unsigned int readSize)
{
off_t offt = ::lseek(m_fileHandle, startOffset, SEEK_SET);
if (offt == -1) {
Qul::PlatformInterface::log("Failed to seek: %s\n", strerror(errno));
return -1;
}
ssize_t actualSize = ::read(m_fileHandle, outputBuffer, readSize);
if (actualSize != readSize) {
Qul::PlatformInterface::log("Unable to read enough data: %s\n", strerror(errno));
}
return actualSize;
}
int PosixFile::close()
{
if (::close(m_fileHandle) != 0) {
Qul::PlatformInterface::log("Unable to close file: %s\n", strerror(errno));
return -1;
}
m_fileHandle = -1;
return 0;
}
Qul::PlatformInterface::File *PosixFilesystem::open(const char *fileName, Qul::PlatformInterface::File::Mode mode)
{
if (strlen(fileName) == 0) {
Qul::PlatformInterface::log("File name is empty.\n");
return nullptr;
}
std::filesystem::path path(fileName);
if (path.is_relative()) {
// In case the path is relative, it is treated relative to the application binary.
path = std::filesystem::path(appFilePath()) / path;
}
#ifndef NDEBUG
Qul::PlatformInterface::log("Loading file %s\n", path.string().c_str());
#endif
#ifdef QUL_OS_LINUX
int flags = O_CLOEXEC;
#elif defined(QUL_OS_MACOS)
int flags = O_CLOEXEC;
#elif defined(QUL_OS_WIN)
int flags = O_BINARY;
#endif
if (mode == Qul::PlatformInterface::File::Mode::ReadOnly)
flags |= O_RDONLY;
else {
Qul::PlatformInterface::log("Only Read-Only is supported");
return nullptr;
}
if (std::filesystem::is_directory(path)) {
Qul::PlatformInterface::log("Could not open file '%s' because it is a directory.\n",
path.make_preferred().string().c_str());
return nullptr;
}
int handle = ::open(path.make_preferred().string().c_str(), flags);
if (handle < 0) {
Qul::PlatformInterface::log("Could not open file '%s': %s\n",
path.make_preferred().string().c_str(),
strerror(errno));
return nullptr;
}
return Qul::PlatformInterface::qul_new<PosixFile>(handle);
}