first implementation of vfs module

This commit is contained in:
2018-09-11 22:37:53 +07:00
parent f4afa64991
commit a598797212
33 changed files with 10225 additions and 204 deletions

View File

@@ -67,6 +67,9 @@ matrix:
- os: osx
osx_image: xcode9.4
compiler: clang
- os: osx
osx_image: xcode10
compiler: clang
before_install:
- eval "${MATRIX_EVAL}"
script:

View File

@@ -1,12 +1,6 @@
cmake_minimum_required(VERSION 3.9.2 FATAL_ERROR)
project(enduro2d)
#
# external packages
#
find_package(Threads REQUIRED)
#
# global defines
#
@@ -33,7 +27,12 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
add_compile_options(-Wall -Wextra -Wpedantic)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
add_compile_options(/W4)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
endif()
#
@@ -41,10 +40,23 @@ endif()
#
file(GLOB_RECURSE e2d_headers
headers/enduro2d/*.*)
headers/enduro2d/*.hpp
headers/enduro2d/*.inl)
file(GLOB_RECURSE e2d_sources
sources/enduro2d/*.*)
sources/enduro2d/*.cpp
sources/enduro2d/*.hpp
headers/enduro2d/*.inl)
if(APPLE)
file(GLOB_RECURSE e2d_sources_mm
sources/enduro2d/*.mm)
list(APPEND e2d_sources ${e2d_sources_mm})
endif()
#
# 3rd party
#
file(GLOB_RECURSE e2d_3rdparty
sources/3rdparty/*.*)
@@ -71,5 +83,12 @@ target_include_directories(${PROJECT_NAME} PRIVATE
# subdirectories
#
add_subdirectory(samples)
add_subdirectory(untests)
option(BUILD_SAMPLES "Build samples" ON)
if(BUILD_SAMPLES)
add_subdirectory(samples)
endif()
option(BUILD_UNTESTS "Build untests" ON)
if(BUILD_UNTESTS)
add_subdirectory(untests)
endif()

View File

@@ -8,6 +8,7 @@
#include <cmath>
#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdint>
#include <cstdlib>

View File

@@ -34,16 +34,13 @@
// E2D_PLATFORM
//
#define E2D_PLATFORM_ANDROID 1
#define E2D_PLATFORM_LINUX 2
#define E2D_PLATFORM_IOS 3
#define E2D_PLATFORM_MACOSX 4
#define E2D_PLATFORM_WINDOWS 5
#define E2D_PLATFORM_LINUX 1
#define E2D_PLATFORM_IOS 2
#define E2D_PLATFORM_MACOSX 3
#define E2D_PLATFORM_WINDOWS 4
#ifndef E2D_PLATFORM
# if defined(__ANDROID__) || defined(ANDROID)
# define E2D_PLATFORM E2D_PLATFORM_ANDROID
# elif defined(__linux__) || defined(__linux) || defined(linux)
# if defined(__linux__) || defined(__linux) || defined(linux)
# define E2D_PLATFORM E2D_PLATFORM_LINUX
# elif defined(macintosh) || defined(Macintosh) || defined(__APPLE__)
# include <TargetConditionals.h>

View File

@@ -9,3 +9,4 @@
#include "_core.hpp"
#include "debug.hpp"
#include "vfs.hpp"

View File

@@ -13,6 +13,7 @@
namespace e2d
{
class debug;
class vfs;
}
namespace e2d

View File

@@ -0,0 +1,86 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#pragma once
#include "_core.hpp"
namespace e2d
{
class bad_vfs_operation final : public exception {
public:
const char* what() const noexcept final {
return "bad vfs operation";
}
};
class vfs final : public module<vfs> {
public:
vfs();
~vfs() noexcept;
class file_source : private e2d::noncopyable {
public:
virtual ~file_source() noexcept = default;
virtual bool valid() const noexcept = 0;
virtual bool exists(str_view path) const = 0;
virtual input_stream_uptr open(str_view path) const = 0;
virtual std::pair<buffer,bool> load(str_view path) const = 0;
};
using file_source_uptr = std::unique_ptr<file_source>;
template < typename T, typename... Args >
bool register_scheme(str_view scheme, Args&&... args);
bool register_scheme(str_view scheme, file_source_uptr source);
bool unregister_scheme(str_view scheme) noexcept;
bool register_scheme_alias(str_view scheme, url alias);
bool unregister_scheme_alias(str_view scheme) noexcept;
bool exists(const url& url) const;
input_stream_uptr open(const url& url) const;
std::pair<buffer,bool> load(const url& url) const;
std::future<std::pair<buffer,bool>> load_async(const url& url) const;
url resolve_scheme_aliases(const url& url) const;
private:
class state;
std::unique_ptr<state> state_;
};
class archive_file_source final : public vfs::file_source {
public:
archive_file_source(input_stream_uptr stream);
~archive_file_source() noexcept final;
bool valid() const noexcept final;
bool exists(str_view path) const final;
input_stream_uptr open(str_view path) const final;
std::pair<buffer,bool> load(str_view path) const final;
private:
class state;
std::unique_ptr<state> state_;
};
class filesystem_file_source final : public vfs::file_source {
public:
filesystem_file_source();
~filesystem_file_source() noexcept final;
bool valid() const noexcept final;
bool exists(str_view path) const final;
input_stream_uptr open(str_view path) const final;
std::pair<buffer,bool> load(str_view path) const final;
};
}
namespace e2d
{
template < typename T, typename... Args >
bool vfs::register_scheme(str_view scheme, Args&&... args) {
return register_scheme(
scheme,
std::make_unique<T>(std::forward<Args>(args)...));
}
}

View File

@@ -47,4 +47,16 @@ namespace e2d { namespace filesystem
bool try_read_all(buffer& dst, str_view path) noexcept;
bool try_write_all(const buffer& src, str_view path, bool append) noexcept;
enum class predef_path {
home,
appdata,
desktop,
working,
documents,
resources,
executable
};
bool extract_predef_path(str& dst, predef_path path_type);
}}

View File

@@ -23,7 +23,7 @@ namespace e2d
virtual std::size_t read(void* dst, std::size_t size) = 0;
virtual std::size_t seek(std::ptrdiff_t offset, bool relative) = 0;
virtual std::size_t tell() const = 0;
virtual std::size_t length() const = 0;
virtual std::size_t length() const noexcept = 0;
};
using input_stream_uptr = std::unique_ptr<input_stream>;
@@ -52,6 +52,16 @@ namespace e2d
return exception_;
}
input_sequence& seek(std::ptrdiff_t offset, bool relative) noexcept {
try {
stream_.seek(offset, relative);
} catch (...) {
success_ = false;
exception_ = std::current_exception();
}
return *this;
}
template < typename T >
std::enable_if_t<
std::is_arithmetic<T>::value,
@@ -88,6 +98,16 @@ namespace e2d
return exception_;
}
output_sequence& seek(std::ptrdiff_t offset, bool relative) noexcept {
try {
stream_.seek(offset, relative);
} catch (...) {
success_ = false;
exception_ = std::current_exception();
}
return *this;
}
template < typename T >
std::enable_if_t<
std::is_arithmetic<T>::value,

View File

@@ -1,14 +1,60 @@
function(add_e2d_sample NAME)
set(SAMPLE_NAME sample_${NAME})
file(GLOB ${SAMPLE_NAME}_sources
sources/*.*
sources/${SAMPLE_NAME}/*.*)
set(SAMPLE_SOURCES ${${SAMPLE_NAME}_sources})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SAMPLE_SOURCES})
add_executable(${SAMPLE_NAME} ${SAMPLE_SOURCES})
target_include_directories(${SAMPLE_NAME} PRIVATE "../headers")
target_link_libraries(${SAMPLE_NAME} enduro2d)
target_link_libraries(${SAMPLE_NAME} "${CMAKE_THREAD_LIBS_INIT}")
set(SAMPLE_NAME sample_${NAME})
#
# external
#
find_package(Threads REQUIRED)
if(APPLE)
find_library(Foundation Foundation)
endif(APPLE)
#
# sources
#
file(GLOB ${SAMPLE_NAME}_sources
sources/*.*
sources/${SAMPLE_NAME}/*.*)
set(SAMPLE_SOURCES ${${SAMPLE_NAME}_sources})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SAMPLE_SOURCES})
#
# executable
#
add_executable(${SAMPLE_NAME}
${SAMPLE_SOURCES})
target_include_directories(${SAMPLE_NAME}
PRIVATE "../headers")
target_link_libraries(${SAMPLE_NAME}
enduro2d
"${CMAKE_THREAD_LIBS_INIT}")
if(APPLE)
target_link_libraries(${SAMPLE_NAME}
${Foundation})
endif(APPLE)
#
# resources
#
if(MSVC)
add_custom_command(TARGET ${SAMPLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/bin
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/bin)
else()
add_custom_command(TARGET ${SAMPLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/bin
${CMAKE_CURRENT_BINARY_DIR}/bin)
endif()
endfunction(add_e2d_sample)
add_e2d_sample(00)

0
samples/bin/.keep Normal file
View File

7563
sources/3rdparty/miniz/miniz.c vendored Normal file

File diff suppressed because it is too large Load Diff

1328
sources/3rdparty/miniz/miniz.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,291 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include <enduro2d/core/vfs.hpp>
#include <3rdparty/miniz/miniz.h>
namespace
{
using namespace e2d;
template < typename OwnedState >
class archive_stream final : public input_stream {
using iter_state_uptr = std::unique_ptr<
mz_zip_reader_extract_iter_state,
void(*)(mz_zip_reader_extract_iter_state*)>;
std::size_t iter_pos_ = 0;
OwnedState owned_state_;
iter_state_uptr iter_state_;
public:
archive_stream(const OwnedState& owned_state, mz_zip_archive* archive, const char* filename)
: owned_state_(owned_state)
, iter_state_(open_iter_state_(archive, filename))
{
if ( !iter_state_ ) {
throw bad_vfs_operation();
}
}
~archive_stream() noexcept final {
// reset iter state before owned state
iter_state_.reset();
}
std::size_t read(void* dst, std::size_t size) final {
E2D_ASSERT(iter_state_);
std::size_t read_bytes = mz_zip_reader_extract_iter_read(iter_state_.get(), dst, size);
iter_pos_ += read_bytes;
return read_bytes;
}
std::size_t seek(std::ptrdiff_t offset, bool relative) final {
E2D_UNUSED(offset, relative);
throw bad_vfs_operation();
}
std::size_t tell() const final {
return iter_pos_;
}
std::size_t length() const noexcept final {
return math::numeric_cast<std::size_t>(
iter_state_->file_stat.m_uncomp_size);
}
private:
static iter_state_uptr open_iter_state_(mz_zip_archive* archive, const char* filename) noexcept {
mz_zip_reader_extract_iter_state* iter_state = mz_zip_reader_extract_file_iter_new(
archive, filename, MZ_ZIP_FLAG_CASE_SENSITIVE);
return iter_state_uptr(iter_state, state_deleter_);
}
static void state_deleter_(mz_zip_reader_extract_iter_state* state) noexcept {
if ( state ) {
mz_zip_reader_extract_iter_free(state);
}
}
};
}
namespace e2d
{
//
// vfs
//
class vfs::state final : private e2d::noncopyable {
public:
std::mutex mutex;
jobber worker{1};
hash_map<str, url> aliases;
hash_map<str, file_source_uptr> schemes;
public:
url resolve_url(const url& url, u8 level = 0) const {
if ( level > 32 ) {
throw bad_vfs_operation();
}
const auto alias_iter = aliases.find(url.scheme());
return alias_iter != aliases.cend()
? resolve_url(alias_iter->second / url.path(), level + 1)
: url;
}
template < typename F, typename R >
R with_file_source(const url& url, F&& f, R&& fallback_result) const {
const auto resolved_url = resolve_url(url);
const auto scheme_iter = schemes.find(resolved_url.scheme());
return (scheme_iter != schemes.cend() && scheme_iter->second)
? stdex::invoke(std::forward<F>(f), scheme_iter->second, resolved_url.path())
: std::forward<R>(fallback_result);
}
};
vfs::vfs()
: state_(new state()){}
vfs::~vfs() noexcept = default;
bool vfs::register_scheme(str_view scheme, file_source_uptr source) {
std::lock_guard<std::mutex> guard(state_->mutex);
return (source && source->valid())
? state_->schemes.insert(
std::make_pair(scheme, std::move(source))).second
: false;
}
bool vfs::unregister_scheme(str_view scheme) noexcept {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->schemes.erase(scheme) > 0;
}
bool vfs::register_scheme_alias(str_view scheme, url alias) {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->aliases.insert(
std::make_pair(scheme, alias)).second;
}
bool vfs::unregister_scheme_alias(str_view scheme) noexcept {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->aliases.erase(scheme) > 0;
}
bool vfs::exists(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
[](const file_source_uptr& source, const str& path) {
return source->exists(path);
}, false);
}
input_stream_uptr vfs::open(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
[](const file_source_uptr& source, const str& path) {
return source->open(path);
}, input_stream_uptr());
}
std::pair<buffer,bool> vfs::load(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->with_file_source(url,
[](const file_source_uptr& source, const str& path) {
return source->load(path);
}, std::make_pair(buffer(), false));
}
std::future<std::pair<buffer,bool>> vfs::load_async(const url& url) const {
return state_->worker.async([](input_stream_uptr stream){
buffer buf;
return streams::try_read_tail(buf, stream)
? std::make_pair(std::move(buf), true)
: std::make_pair(buffer(), false);
}, open(url));
}
url vfs::resolve_scheme_aliases(const url& url) const {
std::lock_guard<std::mutex> guard(state_->mutex);
return state_->resolve_url(url);
}
//
// archive_file_source
//
class archive_file_source::state final : private e2d::noncopyable {
public:
using archive_ptr = std::shared_ptr<mz_zip_archive>;
using stream_ptr = std::shared_ptr<input_stream>;
archive_ptr archive;
stream_ptr stream;
jobber worker{1};
public:
state(input_stream_uptr stream)
: archive(open_archive_(stream))
, stream(std::move(stream)) {}
~state() noexcept = default;
private:
static archive_ptr open_archive_(const input_stream_uptr& stream) noexcept {
if ( stream ) {
mz_zip_archive* archive = static_cast<mz_zip_archive*>(
std::calloc(1, sizeof(mz_zip_archive)));
if ( archive ) {
archive->m_pRead = archive_reader_;
archive->m_pIO_opaque = stream.get();
if ( mz_zip_reader_init(archive, stream->length(), 0) ) {
return archive_ptr(archive, archive_deleter_);
}
std::free(archive);
}
}
return archive_ptr();
}
static void archive_deleter_(mz_zip_archive* archive) noexcept {
if ( archive ) {
mz_zip_reader_end(archive);
std::free(archive);
}
}
static size_t archive_reader_(void* opaque, mz_uint64 pos, void* dst, size_t size) noexcept {
input_stream* stream = static_cast<input_stream*>(opaque);
return input_sequence(*stream)
.seek(math::numeric_cast<std::ptrdiff_t>(pos), false)
.read(dst, size)
.success() ? size : 0;
}
};
archive_file_source::archive_file_source(input_stream_uptr stream)
: state_(new state(std::move(stream))) {}
archive_file_source::~archive_file_source() noexcept = default;
bool archive_file_source::valid() const noexcept {
return !!state_->archive;
}
bool archive_file_source::exists(str_view path) const {
return -1 != mz_zip_reader_locate_file(
state_->archive.get(),
make_utf8(path).c_str(),
nullptr,
MZ_ZIP_FLAG_CASE_SENSITIVE);
}
input_stream_uptr archive_file_source::open(str_view path) const {
try {
struct owned_state_t {
state::archive_ptr archive;
state::stream_ptr stream;
} owned_state{state_->archive, state_->stream};
return std::make_unique<archive_stream<owned_state_t>>(
std::move(owned_state),
state_->archive.get(),
make_utf8(path).c_str());
} catch (...) {
return nullptr;
}
}
std::pair<buffer,bool> archive_file_source::load(str_view path) const {
std::size_t mem_size = 0;
void* mem = mz_zip_reader_extract_file_to_heap(
state_->archive.get(),
make_utf8(path).c_str(),
&mem_size,
MZ_ZIP_FLAG_CASE_SENSITIVE);
std::unique_ptr<void, decltype(&mz_free)> mem_uptr(mem, mz_free);
return mem
? std::make_pair(buffer(mem, mem_size), true)
: std::make_pair(buffer(), false);
}
//
// filesystem_file_source
//
filesystem_file_source::filesystem_file_source() = default;
filesystem_file_source::~filesystem_file_source() noexcept = default;
bool filesystem_file_source::valid() const noexcept {
return true;
}
bool filesystem_file_source::exists(str_view path) const {
return filesystem::file_exists(path);
}
input_stream_uptr filesystem_file_source::open(str_view path) const {
return make_read_file(path);
}
std::pair<buffer,bool> filesystem_file_source::load(str_view path) const {
buffer buf;
return filesystem::try_read_all(buf, path)
? std::make_pair(std::move(buf), true)
: std::make_pair(buffer(), false);
}
}

View File

@@ -1,15 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include <enduro2d/base/_all.hpp>
#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_ANDROID
int main() {
return 0;
}
#endif

View File

@@ -4,7 +4,8 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem_impl/filesystem_impl.hpp"
#include "filesystem_impl/files.hpp"
#include "filesystem_impl/filesystem.hpp"
namespace e2d
{
@@ -73,4 +74,8 @@ namespace e2d { namespace filesystem
return streams::try_write_tail(
src, make_write_file(path, append));
}
bool extract_predef_path(str& dst, predef_path path_type) {
return impl::extract_predef_path(dst, path_type);
}
}}

View File

@@ -10,20 +10,18 @@
#include <enduro2d/utils/strings.hpp>
#include <enduro2d/utils/filesystem.hpp>
#define E2D_FILESYSTEM_MODE_POSIX 1
#define E2D_FILESYSTEM_MODE_WINAPI 2
#define E2D_FILES_MODE_POSIX 1
#define E2D_FILES_MODE_WINAPI 2
#ifndef E2D_FILESYSTEM_MODE
# if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_ANDROID
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
#ifndef E2D_FILES_MODE
# if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
# define E2D_FILES_MODE E2D_FILES_MODE_POSIX
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
# define E2D_FILES_MODE E2D_FILES_MODE_POSIX
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_POSIX
# define E2D_FILES_MODE E2D_FILES_MODE_POSIX
# elif defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS
# define E2D_FILESYSTEM_MODE E2D_FILESYSTEM_MODE_WINAPI
# define E2D_FILES_MODE E2D_FILES_MODE_WINAPI
# endif
#endif
@@ -32,14 +30,3 @@ namespace e2d { namespace impl
read_file_uptr make_read_file(str_view path) noexcept;
write_file_uptr make_write_file(str_view path, bool append) noexcept;
}}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path);
bool remove_directory(str_view path);
bool file_exists(str_view path);
bool directory_exists(str_view path);
bool create_directory(str_view path);
}}}

View File

@@ -4,9 +4,9 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem_impl.hpp"
#include "files.hpp"
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_POSIX
#if defined(E2D_FILES_MODE) && E2D_FILES_MODE == E2D_FILES_MODE_POSIX
#include <fcntl.h>
#include <unistd.h>
@@ -53,7 +53,7 @@ namespace
: throw bad_stream_operation();
}
std::size_t length() const final {
std::size_t length() const noexcept final {
return length_;
}

View File

@@ -4,9 +4,9 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem_impl.hpp"
#include "files.hpp"
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_WINAPI
#if defined(E2D_FILES_MODE) && E2D_FILES_MODE == E2D_FILES_MODE_WINAPI
#include <windows.h>
@@ -62,7 +62,7 @@ namespace
: throw bad_stream_operation();
}
std::size_t length() const final {
std::size_t length() const noexcept final {
return length_;
}

View File

@@ -0,0 +1,24 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#pragma once
#include <enduro2d/utils/path.hpp>
#include <enduro2d/utils/strings.hpp>
#include <enduro2d/utils/filesystem.hpp>
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path);
bool remove_directory(str_view path);
bool file_exists(str_view path);
bool directory_exists(str_view path);
bool create_directory(str_view path);
bool extract_predef_path(str& dst, predef_path path_type);
}}}

View File

@@ -0,0 +1,142 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem.hpp"
#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_IOS
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#import <Foundation/Foundation.h>
namespace
{
using namespace e2d;
const mode_t default_directory_mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
bool extract_home_directory(str& dst) {
NSString* path = NSHomeDirectory();
if ( path ) {
dst.assign([path UTF8String]);
return true;
}
return false;
}
bool extract_appdata_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_desktop_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSDesktopDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_working_directory(str& dst) {
NSString* filename = [[NSFileManager defaultManager] currentDirectoryPath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
bool extract_documents_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_resources_directory(str& dst) {
NSString* filename = [[NSBundle mainBundle] resourcePath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
bool extract_executable_path(str& dst) {
NSString* filename = [[NSBundle mainBundle] executablePath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
return 0 == ::unlink(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool remove_directory(str_view path) {
return 0 == ::rmdir(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool file_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISREG(st.st_mode);
}
bool directory_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISDIR(st.st_mode);
}
bool create_directory(str_view path) {
return 0 == ::mkdir(make_utf8(path).c_str(), default_directory_mode)
|| errno == EEXIST;
}
bool extract_predef_path(str& dst, predef_path path_type) {
switch ( path_type ) {
case predef_path::home:
return extract_home_directory(dst);
case predef_path::appdata:
return extract_appdata_directory(dst);
case predef_path::desktop:
return extract_desktop_directory(dst);
case predef_path::working:
return extract_working_directory(dst);
case predef_path::documents:
return extract_documents_directory(dst);
case predef_path::resources:
return extract_resources_directory(dst);
case predef_path::executable:
return extract_executable_path(dst);
default:
E2D_ASSERT_MSG(false, "unexpected predef path");
return false;
}
}
}}}
#endif

View File

@@ -0,0 +1,132 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem.hpp"
#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_LINUX
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace
{
using namespace e2d;
const mode_t default_directory_mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
bool extract_home_directory(str& dst) {
const char* const home_path = std::getenv("HOME");
if ( home_path ) {
dst.assign(home_path);
return true;
}
return false;
}
bool extract_appdata_directory(str& dst) {
return extract_home_directory(dst);
}
bool extract_desktop_directory(str& dst) {
str home_directory;
if ( extract_home_directory(home_directory) ) {
dst = path::combine(home_directory, "Desktop");
return true;
}
return false;
}
bool extract_documents_directory(str& dst) {
str home_directory;
if ( extract_home_directory(home_directory) ) {
dst = path::combine(home_directory, "Documents");
return true;
}
return false;
}
bool extract_working_directory(str& dst) {
char buf[PATH_MAX + 1] = {0};
if ( ::getcwd(buf, E2D_COUNTOF(buf) - 1) ) {
dst.assign(buf);
return true;
}
return false;
}
bool extract_executable_path(str& dst) {
char buf[PATH_MAX + 1] = {0};
if ( ::readlink("/proc/self/exe", buf, E2D_COUNTOF(buf) - 1) != -1 ) {
dst.assign(buf);
return true;
}
return false;
}
bool extract_resources_directory(str& dst) {
str executable_path;
if ( extract_executable_path(executable_path) ) {
dst = path::parent_path(executable_path);
return true;
}
return false;
}
}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
return 0 == ::unlink(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool remove_directory(str_view path) {
return 0 == ::rmdir(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool file_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISREG(st.st_mode);
}
bool directory_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISDIR(st.st_mode);
}
bool create_directory(str_view path) {
return 0 == ::mkdir(make_utf8(path).c_str(), default_directory_mode)
|| errno == EEXIST;
}
bool extract_predef_path(str& dst, predef_path path_type) {
switch ( path_type ) {
case predef_path::home:
return extract_home_directory(dst);
case predef_path::appdata:
return extract_appdata_directory(dst);
case predef_path::desktop:
return extract_desktop_directory(dst);
case predef_path::working:
return extract_working_directory(dst);
case predef_path::documents:
return extract_documents_directory(dst);
case predef_path::resources:
return extract_resources_directory(dst);
case predef_path::executable:
return extract_executable_path(dst);
default:
E2D_ASSERT_MSG(false, "unexpected predef path");
return false;
}
}
}}}
#endif

View File

@@ -0,0 +1,142 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem.hpp"
#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_MACOSX
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#import <Foundation/Foundation.h>
namespace
{
using namespace e2d;
const mode_t default_directory_mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
bool extract_home_directory(str& dst) {
NSString* path = NSHomeDirectory();
if ( path ) {
dst.assign([path UTF8String]);
return true;
}
return false;
}
bool extract_appdata_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_desktop_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSDesktopDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_working_directory(str& dst) {
NSString* filename = [[NSFileManager defaultManager] currentDirectoryPath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
bool extract_documents_directory(str& dst) {
NSArray<NSString*>* paths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
if ( paths && paths.count > 0 ) {
dst.assign([[paths objectAtIndex:0] UTF8String]);
return true;
}
return false;
}
bool extract_resources_directory(str& dst) {
NSString* filename = [[NSBundle mainBundle] resourcePath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
bool extract_executable_path(str& dst) {
NSString* filename = [[NSBundle mainBundle] executablePath];
if ( filename ) {
dst.assign([filename UTF8String]);
return true;
}
return false;
}
}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
return 0 == ::unlink(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool remove_directory(str_view path) {
return 0 == ::rmdir(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool file_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISREG(st.st_mode);
}
bool directory_exists(str_view path) {
struct stat st;
return 0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISDIR(st.st_mode);
}
bool create_directory(str_view path) {
return 0 == ::mkdir(make_utf8(path).c_str(), default_directory_mode)
|| errno == EEXIST;
}
bool extract_predef_path(str& dst, predef_path path_type) {
switch ( path_type ) {
case predef_path::home:
return extract_home_directory(dst);
case predef_path::appdata:
return extract_appdata_directory(dst);
case predef_path::desktop:
return extract_desktop_directory(dst);
case predef_path::working:
return extract_working_directory(dst);
case predef_path::documents:
return extract_documents_directory(dst);
case predef_path::resources:
return extract_resources_directory(dst);
case predef_path::executable:
return extract_executable_path(dst);
default:
E2D_ASSERT_MSG(false, "unexpected predef path");
return false;
}
}
}}}
#endif

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem_impl.hpp"
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_POSIX
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace
{
mode_t default_directory_mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
return
0 == ::unlink(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool remove_directory(str_view path) {
return
0 == ::rmdir(make_utf8(path).c_str())
|| errno == ENOENT;
}
bool file_exists(str_view path) {
struct stat st;
return
0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISREG(st.st_mode);
}
bool directory_exists(str_view path) {
struct stat st;
return
0 == ::stat(make_utf8(path).c_str(), &st)
&& S_ISDIR(st.st_mode);
}
bool create_directory(str_view path) {
return
0 == ::mkdir(make_utf8(path).c_str(), default_directory_mode)
|| errno == EEXIST;
}
}}}
#endif

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* This file is part of the "enduro2d"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem_impl.hpp"
#if defined(E2D_FILESYSTEM_MODE) && E2D_FILESYSTEM_MODE == E2D_FILESYSTEM_MODE_WINAPI
#include <windows.h>
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
const wstr wide_path = make_wide(path);
return
::DeleteFileW(wide_path.c_str())
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
}
bool remove_directory(str_view path) {
const wstr wide_path = make_wide(path);
return
::RemoveDirectoryW(wide_path.c_str())
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
}
bool file_exists(str_view path) {
const wstr wide_path = make_wide(path);
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
return
attributes != INVALID_FILE_ATTRIBUTES &&
!(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
bool directory_exists(str_view path) {
const wstr wide_path = make_wide(path);
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
return
attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
bool create_directory(str_view path) {
const wstr wide_path = make_wide(path);
return
::CreateDirectoryW(wide_path.c_str(), nullptr) ||
::GetLastError() == ERROR_ALREADY_EXISTS;
}
}}}
#endif

View File

@@ -0,0 +1,143 @@
/*******************************************************************************
* This file is part of the "enduro2d"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "filesystem.hpp"
#if defined(E2D_PLATFORM) && E2D_PLATFORM == E2D_PLATFORM_WINDOWS
#include <shlobj.h>
#include <windows.h>
namespace
{
using namespace e2d;
bool extract_home_directory(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
if ( SUCCEEDED(::SHGetFolderPathW(0, CSIDL_PROFILE | CSIDL_FLAG_CREATE, 0, 0, buf)) ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_appdata_directory(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
if ( SUCCEEDED(::SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, buf)) ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_desktop_directory(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
if ( SUCCEEDED(::SHGetFolderPathW(0, CSIDL_DESKTOP | CSIDL_FLAG_CREATE, 0, 0, buf)) ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_working_directory(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
const DWORD len = ::GetCurrentDirectoryW(E2D_COUNTOF(buf) - 1, buf);
if ( len > 0 && len <= MAX_PATH ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_documents_directory(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
if ( SUCCEEDED(::SHGetFolderPathW(0, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, 0, 0, buf)) ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_executable_path(str& dst) {
WCHAR buf[MAX_PATH + 1] = {0};
const DWORD len = ::GetModuleFileNameW(0, buf, E2D_COUNTOF(buf) - 1);
if ( len > 0 && len <= MAX_PATH ) {
dst = make_utf8(buf);
return true;
}
return false;
}
bool extract_resources_directory(str& dst) {
str executable_path;
if ( extract_executable_path(executable_path) ) {
dst = path::parent_path(executable_path);
return true;
}
return false;
}
}
namespace e2d { namespace filesystem { namespace impl
{
bool remove_file(str_view path) {
const wstr wide_path = make_wide(path);
return ::DeleteFileW(wide_path.c_str())
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
}
bool remove_directory(str_view path) {
const wstr wide_path = make_wide(path);
return ::RemoveDirectoryW(wide_path.c_str())
|| ::GetLastError() == ERROR_FILE_NOT_FOUND
|| ::GetLastError() == ERROR_PATH_NOT_FOUND;
}
bool file_exists(str_view path) {
const wstr wide_path = make_wide(path);
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES
&& !(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
bool directory_exists(str_view path) {
const wstr wide_path = make_wide(path);
DWORD attributes = ::GetFileAttributesW(wide_path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES
&& (attributes & FILE_ATTRIBUTE_DIRECTORY);
}
bool create_directory(str_view path) {
const wstr wide_path = make_wide(path);
return ::CreateDirectoryW(wide_path.c_str(), nullptr)
|| ::GetLastError() == ERROR_ALREADY_EXISTS;
}
bool extract_predef_path(str& dst, predef_path path_type) {
switch ( path_type ) {
case predef_path::home:
return extract_home_directory(dst);
case predef_path::appdata:
return extract_appdata_directory(dst);
case predef_path::desktop:
return extract_desktop_directory(dst);
case predef_path::working:
return extract_working_directory(dst);
case predef_path::documents:
return extract_documents_directory(dst);
case predef_path::resources:
return extract_resources_directory(dst);
case predef_path::executable:
return extract_executable_path(dst);
default:
E2D_ASSERT_MSG(false, "unexpected predef path");
return false;
}
}
}}}
#endif

View File

@@ -54,7 +54,7 @@ namespace
return pos_;
}
std::size_t length() const final {
std::size_t length() const noexcept final {
return data_.size();
}
private:

View File

@@ -1,15 +1,66 @@
function(add_e2d_tests NAME)
set(TESTS_NAME untests_${NAME})
file(GLOB ${TESTS_NAME}_sources
sources/*.*
sources/${TESTS_NAME}/*.*)
set(TESTS_SOURCES ${${TESTS_NAME}_sources})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TESTS_SOURCES})
add_executable(${TESTS_NAME} ${TESTS_SOURCES})
target_include_directories(${TESTS_NAME} PRIVATE "../headers")
target_link_libraries(${TESTS_NAME} enduro2d)
target_link_libraries(${TESTS_NAME} "${CMAKE_THREAD_LIBS_INIT}")
add_test(${TESTS_NAME} ${TESTS_NAME})
set(TESTS_NAME untests_${NAME})
#
# external
#
find_package(Threads REQUIRED)
if(APPLE)
find_library(Foundation Foundation)
endif(APPLE)
#
# sources
#
file(GLOB ${TESTS_NAME}_sources
sources/*.*
sources/${TESTS_NAME}/*.*)
set(TESTS_SOURCES ${${TESTS_NAME}_sources})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TESTS_SOURCES})
#
# executable
#
add_executable(${TESTS_NAME}
${TESTS_SOURCES})
target_include_directories(${TESTS_NAME}
PRIVATE "../headers")
target_link_libraries(${TESTS_NAME}
enduro2d
"${CMAKE_THREAD_LIBS_INIT}")
if(APPLE)
target_link_libraries(${TESTS_NAME}
${Foundation})
endif(APPLE)
#
# resources
#
if(MSVC)
add_custom_command(TARGET ${TESTS_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/bin
${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/bin)
else()
add_custom_command(TARGET ${TESTS_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/bin
${CMAKE_CURRENT_BINARY_DIR}/bin)
endif()
#
# tests
#
add_test(${TESTS_NAME} ${TESTS_NAME})
endfunction(add_e2d_tests)
add_e2d_tests(base)

0
untests/bin/.keep Normal file
View File

BIN
untests/bin/resources.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,134 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "_core.hpp"
using namespace e2d;
TEST_CASE("vfs"){
const str_view file_path = "vfs_file_name";
const str_view nofile_path = "vfs_file_name2";
{
REQUIRE(filesystem::remove_file(nofile_path));
REQUIRE(filesystem::create_file(file_path));
REQUIRE(filesystem::try_write_all({"hello", 5}, file_path, false));
}
{
vfs v;
REQUIRE(v.register_scheme<filesystem_file_source>("file"));
{
REQUIRE(v.exists({"file", file_path}));
REQUIRE_FALSE(v.exists({"file2", file_path}));
REQUIRE_FALSE(v.exists({"file", nofile_path}));
}
{
buffer b;
auto r = v.open({"file", file_path});
REQUIRE(r);
REQUIRE(streams::try_read_tail(b, r));
REQUIRE(b == buffer{"hello", 5});
REQUIRE(v.open({"file2", file_path}) == input_stream_uptr());
REQUIRE(v.open({"file", nofile_path}) == input_stream_uptr());
}
{
auto r = v.load({"file", file_path});
REQUIRE(r.second);
REQUIRE(r.first == buffer{"hello", 5});
auto r2 = v.load_async({"file", file_path}).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer{"hello", 5});
}
}
{
vfs v;
v.register_scheme_alias("home", url("file://~"));
v.register_scheme_alias("save", url("home://game/saves"));
REQUIRE(v.resolve_scheme_aliases({"home", "file.txt"}) == url("file://~/file.txt"));
REQUIRE(v.resolve_scheme_aliases({"save", "save.txt"}) == url("file://~/game/saves/save.txt"));
}
SECTION("archive"){
vfs v;
{
str resources;
REQUIRE(filesystem::extract_predef_path(resources, filesystem::predef_path::resources));
REQUIRE(v.register_scheme_alias("resources", {"file", resources}));
REQUIRE_FALSE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/noresources.zip"))));
REQUIRE(v.register_scheme<filesystem_file_source>("file"));
REQUIRE_FALSE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/noresources.zip"))));
REQUIRE(v.register_scheme<archive_file_source>(
"archive",
v.open(url("resources://bin/resources.zip"))));
REQUIRE(v.exists({"archive", "test.txt"}));
REQUIRE_FALSE(v.exists({"archive", "TEst.txt"}));
REQUIRE(v.exists({"archive", "folder/file.txt"}));
REQUIRE_FALSE(v.exists({"archive", "FOLder/file.txt"}));
REQUIRE_FALSE(v.exists({"archive", "test2.txt"}));
REQUIRE_FALSE(v.exists({"archive", "folder/file2.txt"}));
{
auto f = v.open(url("archive://test.txt"));
REQUIRE(f);
buffer b;
REQUIRE(streams::try_read_tail(b, f));
REQUIRE(b == buffer("hello", 5));
}
{
auto r = v.load(url("archive://test.txt"));
REQUIRE(r.second);
REQUIRE(r.first == buffer("hello", 5));
auto r2 = v.load_async(url("archive://test.txt")).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer("hello", 5));
}
{
auto f = v.open(url("archive://folder/file.txt"));
REQUIRE(f);
buffer b;
REQUIRE(streams::try_read_tail(b, f));
REQUIRE(b == buffer("world", 5));
}
{
auto r = v.load(url("archive://folder/file.txt"));
REQUIRE(r.second);
REQUIRE(r.first == buffer("world", 5));
auto r2 = v.load_async(url("archive://folder/file.txt")).get();
REQUIRE(r2.second);
REQUIRE(r2.first == buffer("world", 5));
}
{
REQUIRE(v.open(url("archive://TEst.txt")) == input_stream_uptr());
auto r = v.load(url("archive://TEst.txt"));
REQUIRE_FALSE(r.second);
REQUIRE(r.first == buffer());
auto r2 = v.load_async(url("archive://TEst.txt")).get();
REQUIRE_FALSE(r2.second);
REQUIRE(r2.first == buffer());
}
{
auto f = v.open(url("archive://test.txt"));
REQUIRE(f);
REQUIRE(v.unregister_scheme("archive"));
buffer b;
REQUIRE(streams::try_read_tail(b, f));
REQUIRE(b == buffer("hello", 5));
}
}
}
}

View File

@@ -49,6 +49,24 @@ TEST_CASE("filesystem") {
}
}
SECTION("filesystem") {
{
std::printf("-= filesystem::extract_predef_path tests\n");
str path;
std::printf("home: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::home) ? path.c_str() : "error");
std::printf("appdata: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::appdata) ? path.c_str() : "error");
std::printf("desktop: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::desktop) ? path.c_str() : "error");
std::printf("working: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::working) ? path.c_str() : "error");
std::printf("documents: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::documents) ? path.c_str() : "error");
std::printf("resources: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::resources) ? path.c_str() : "error");
std::printf("executable: %s\n",
filesystem::extract_predef_path(path, filesystem::predef_path::executable) ? path.c_str() : "error");
}
{
const str_view child_dir_name = "test_filesystem_file_name";
REQUIRE(filesystem::remove_file(child_dir_name));

View File

@@ -345,16 +345,16 @@ TEST_CASE("strings") {
str("0 2 1 4 3 6 7 5 8 9"));
}
{
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, 3)) == " -5");
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, 4)) == " -5");
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, 1)) == "21");
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, 2)) == "21");
REQUIRE(strings::rformat("%0", strings::make_format_arg(42, 3)) == " 42");
REQUIRE(strings::rformat("%0", strings::make_format_arg(42u, 3)) == " 42");
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, u8(3))) == " -5");
REQUIRE(strings::rformat("%0", strings::make_format_arg(-5, u8(4))) == " -5");
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, u8(1))) == "21");
REQUIRE(strings::rformat("%0", strings::make_format_arg(21, u8(2))) == "21");
REQUIRE(strings::rformat("%0", strings::make_format_arg(42, u8(3))) == " 42");
REQUIRE(strings::rformat("%0", strings::make_format_arg(42u, u8(3))) == " 42");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f)) == "1.230000");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,0)) == "1.230000");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,0,2)) == "1.23");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,5,2)) == " 1.23");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(0))) == "1.230000");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(0),u8(2))) == "1.23");
REQUIRE(strings::rformat("%0", strings::make_format_arg(1.23f,u8(5),u8(2))) == " 1.23");
REQUIRE(strings::rformat("%0", strings::make_format_arg(true)) == "true");
REQUIRE(strings::rformat("%0", strings::make_format_arg(false)) == "false");