mirror of
https://github.com/enduro2d/enduro2d.git
synced 2026-02-04 15:06:57 +07:00
first implementation of vfs module
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
#include "_core.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
#include "vfs.hpp"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
namespace e2d
|
||||
{
|
||||
class debug;
|
||||
class vfs;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
|
||||
86
headers/enduro2d/core/vfs.hpp
Normal file
86
headers/enduro2d/core/vfs.hpp
Normal 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)...));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
0
samples/bin/.keep
Normal file
7563
sources/3rdparty/miniz/miniz.c
vendored
Normal 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
1328
sources/3rdparty/miniz/miniz.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
291
sources/enduro2d/core/vfs.cpp
Normal file
291
sources/enduro2d/core/vfs.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -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);
|
||||
}}}
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
24
sources/enduro2d/utils/filesystem_impl/filesystem.hpp
Normal file
24
sources/enduro2d/utils/filesystem_impl/filesystem.hpp
Normal 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);
|
||||
}}}
|
||||
142
sources/enduro2d/utils/filesystem_impl/filesystem_ios.mm
Normal file
142
sources/enduro2d/utils/filesystem_impl/filesystem_ios.mm
Normal 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
|
||||
132
sources/enduro2d/utils/filesystem_impl/filesystem_linux.cpp
Normal file
132
sources/enduro2d/utils/filesystem_impl/filesystem_linux.cpp
Normal 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
|
||||
142
sources/enduro2d/utils/filesystem_impl/filesystem_macosx.mm
Normal file
142
sources/enduro2d/utils/filesystem_impl/filesystem_macosx.mm
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
143
sources/enduro2d/utils/filesystem_impl/filesystem_windows.cpp
Normal file
143
sources/enduro2d/utils/filesystem_impl/filesystem_windows.cpp
Normal 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
|
||||
@@ -54,7 +54,7 @@ namespace
|
||||
return pos_;
|
||||
}
|
||||
|
||||
std::size_t length() const final {
|
||||
std::size_t length() const noexcept final {
|
||||
return data_.size();
|
||||
}
|
||||
private:
|
||||
|
||||
@@ -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
0
untests/bin/.keep
Normal file
BIN
untests/bin/resources.zip
Normal file
BIN
untests/bin/resources.zip
Normal file
Binary file not shown.
134
untests/sources/untests_core/vfs.cpp
Normal file
134
untests/sources/untests_core/vfs.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user