arapelle / arba-rsce

MIT License
0 stars 0 forks source link

resource_manager handles resource packages. (new project `rscpack`) #10

Open arapelle opened 11 months ago

arapelle commented 11 months ago
rscm.register_pack(strn::string64 pack_name, std::filesystem::path);
arapelle commented 11 months ago
#include <span>
#include <algorithm>
#include <numeric>
#include <ranges>
#include <vector>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <unordered_map>

void check_input_args(int argc)
{
    if (argc < 3)
    {
        std::cerr << "ERROR: arguments are missing. Please provide : output_file input_file ..." << std::endl;
        exit(EXIT_FAILURE);
    }
}

void check_input_file(const std::filesystem::path& input_file_path)
{
    if (!std::filesystem::exists(input_file_path))
    {
        std::cerr << "ERROR: Input file does not exist : " << input_file_path << "." << std::endl;
        exit(EXIT_FAILURE);
    }
}

void check_input_file(std::ifstream& input_file)
{
    if (!input_file)
    {
        std::cerr << "ERROR: Error while reading input file." << std::endl;
        exit(EXIT_FAILURE);
    }
}

void check_output_file(std::ofstream& output_file)
{
    if (!output_file)
    {
        std::cerr << "ERROR: Error while writing output file." << std::endl;
        exit(EXIT_FAILURE);
    }
}

class Input_file
{
public:
    Input_file(std::filesystem::path path)
        : path_(path), fstream_(path, std::ios::binary|std::ios::ate), size_(fstream_.tellg())
    {}

    const std::filesystem::path& path() const { return path_; }
    std::size_t size() const { return size_; }

    void write_to_ofstream(std::ofstream& ofs) const
    {
        fstream_.seekg(0, std::ios::beg);

        constexpr std::size_t buffer_size = 1024;
        std::array<char, buffer_size> bytes;

        for (std::size_t file_size = size(); file_size > 0;)
        {
            std::size_t byte_count = std::min<std::size_t>(bytes.size(), file_size);
            fstream_.read(bytes.data(), byte_count);
            check_input_file(fstream_);
            ofs.write(bytes.data(), byte_count);
            check_output_file(ofs);
            file_size -= byte_count;
        }

        fstream_.seekg(0, std::ios::beg);
    }

private:
    std::filesystem::path path_;
    mutable std::ifstream fstream_;
    std::size_t size_;
};

template <class RequestTypeTag, class Type, class ResponseType>
struct define_requested_type
{
    using type = ResponseType;
};

template <class RequestTag, class Type>
struct requested_type;

template <class RequestTag, class Type>
using requested_type_t = typename requested_type<RequestTag, Type>::type;

template <class RequestTag, class Type, class OrType>
struct requested_type_or { using type = OrType; };

template <class RequestTag, class Type, class OrType>
requires requires
{
    typename requested_type_t<RequestTag, Type>;
}
struct requested_type_or<RequestTag, Type, OrType> { using type = requested_type_t<RequestTag, Type>; };

template <class RequestTag, class Type, class OrType>
using requested_type_or_t = typename requested_type_or<RequestTag, Type, OrType>::type;

// ---

class resource_ifstream_tag { ~resource_ifstream_tag() = delete; };

template <>
struct requested_type<resource_ifstream_tag, int>
    : public define_requested_type<resource_ifstream_tag, int, void> {};

// #define ARBA_CORE_DEFINE_REQUESTED_TYPE(tag, type, vtype) !
// e.g. ARBA_CORE_DEFINE_REQUESTED_TYPE(resource_ifstream_tag, sf::Texture, sf::InputFileStream)

class resource_pack
{
public:
    struct resource_info
    {
        uint64_t index;
        uint64_t size;

        void read_binary(std::istream& stream)
        {
//            seri::read_binary(stream, index);
//            seri::read_binary(stream, size);
        }
    };

public:
    resource_pack(const std::filesystem::path& rscp_fpath)
        : rscp_fpath_(rscp_fpath)
    {
        load_index_();
    }

    inline const std::filesystem::path& path() const { return rscp_fpath_; }

    const resource_info* resource_info_ptr(std::string_view rsc_path) const
    {
        if (auto iter = index_.find(std::string(rsc_path)); iter != index_.end()) [[likely]]
            return &iter->second;
        return nullptr;
    }

    bool open_input_file_stream(std::string_view rsc_path, std::ifstream& input_fstream, uint64_t& rsc_size) const
    {
        if (const resource_info* rsc_info_ptr = resource_info_ptr(rsc_path); rsc_info_ptr)
        {
            rsc_size = rsc_info_ptr->size;
            input_fstream.open(rscp_fpath_);
            input_fstream.seekg(rsc_info_ptr->index);
            return static_cast<bool>(input_fstream);
        }
        return false;
    }

    inline bool open_input_file_stream(std::string_view rsc_path, std::ifstream& input_fstream) const
    {
        std::size_t unused;
        return open_input_file_stream(rsc_path, input_fstream, unused);
    }

    template <class Resource>
    std::shared_ptr<Resource> get_rsc(std::string_view rsc_path) const
    {
        if (const resource_info* rsc_info_ptr = resource_info_ptr(rsc_path); rsc_info_ptr)
        {
//            using input_fstream_t = typename resource_types<Resource>::input_file_stream;
            using input_fstream_t = requested_type_or_t<resource_ifstream_tag, Resource, std::ifstream>;
            input_fstream_t rscp_fstream(rscp_fpath_);
            rscp_fstream.seekg(rsc_info_ptr->index);
            Resource rsc;
            //        if (seri::read_binary(rscp_fstream, rsc))
            return std::make_shared(std::move(rsc));
        }
        return nullptr;
    }

private:
    void load_index_()
    {
        std::ifstream rscp_fstream(rscp_fpath_);
        uint64_t offset = 0;
//        seri::read_binary(rscp_fstream, offset);
        rscp_fstream.seekg(offset, std::ifstream::cur);
//        seri::write_binary(rscp_fstream, index_);
    }

private:
    std::filesystem::path rscp_fpath_;
    std::unordered_map<std::string, resource_info> index_;
};

void make_rscpack(int argc, char** argv)
{
    check_input_args(argc);

    std::filesystem::path output_fpath(argv[1]);
    std::ofstream output_fstream(output_fpath.string(), std::ios::binary);
    check_output_file(output_fstream);

    std::vector<Input_file> input_files;
    input_files.reserve(argc - 2);

    for (std::filesystem::path input_fpath : std::ranges::subrange(argv + 2, argv + argc))
    {
        check_input_file(input_fpath);
        input_files.emplace_back(input_fpath);
        std::cout << input_fpath << std::endl;
    }

    uint64_t input_files_size = 0;
    std::ranges::for_each(input_files, [&](const auto& arg){ input_files_size += arg.size(); });
    output_fstream << input_files_size;

    std::unordered_map<std::filesystem::path, uint64_t> resource_index;
    resource_index.reserve(input_files.size());
    for (const Input_file& input_file : input_files)
    {
        resource_index.emplace(input_file.path(), output_fstream.tellp());
        input_file.write_to_ofstream(output_fstream);
    }
    output_fstream << resource_index.size();
    for (const auto& entry : resource_index)
        output_fstream << entry.first.generic_string() << entry.second;
}

int main(int argc, char** argv)
{
    std::cout << "EXIT SUCCESS" << std::endl;
    return EXIT_SUCCESS;
}