mirror of
https://github.com/Force67/ps4delta.git
synced 2025-04-02 11:01:45 -04:00
207 lines
No EOL
5.4 KiB
C++
207 lines
No EOL
5.4 KiB
C++
#pragma once
|
|
|
|
/*
|
|
* UTL : The universal utility library
|
|
*
|
|
* Copyright 2019-2020 Force67.
|
|
* For information regarding licensing see LICENSE
|
|
* in the root of the source tree.
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace utl {
|
|
#ifdef _WIN32
|
|
using native_handle = void *;
|
|
#else
|
|
using native_handle = int;
|
|
#endif
|
|
|
|
enum class fileMode { read, write, append, create, trunc };
|
|
|
|
enum class seekMode : uint32_t {
|
|
seek_set,
|
|
seek_cur,
|
|
seek_end,
|
|
};
|
|
|
|
class fileBase {
|
|
public:
|
|
virtual ~fileBase() = default;
|
|
|
|
virtual void Close(){};
|
|
virtual bool IsOpen() { return true; }
|
|
virtual uint64_t Read(void *, size_t) = 0;
|
|
virtual uint64_t Write(const void *, size_t) = 0;
|
|
virtual uint64_t Seek(int64_t, seekMode) = 0;
|
|
virtual uint64_t Tell() = 0;
|
|
virtual uint64_t GetSize() = 0;
|
|
virtual native_handle GetNativeHandle() = 0;
|
|
};
|
|
|
|
class File {
|
|
std::unique_ptr<fileBase> file{};
|
|
|
|
public:
|
|
File() = default;
|
|
File(const std::string &, fileMode mode = fileMode::read);
|
|
File(const void *, size_t);
|
|
File(std::unique_ptr<fileBase> &&);
|
|
~File();
|
|
|
|
// move
|
|
File(File &rhs) : file(rhs.GetBase()) {}
|
|
|
|
void Close() {
|
|
if (file)
|
|
file.reset();
|
|
}
|
|
|
|
void Reset(std::unique_ptr<fileBase> &&ptr) { file = std::move(ptr); }
|
|
|
|
inline std::unique_ptr<fileBase> GetBase() { return std::move(file); }
|
|
|
|
inline uint64_t Read(void *ptr, size_t size) { return file->Read(ptr, size); }
|
|
inline uint64_t Write(const void *ptr, size_t size) {
|
|
return file->Write(ptr, size);
|
|
}
|
|
inline uint64_t Seek(uint64_t ofs, seekMode mods) {
|
|
return file->Seek(ofs, mods);
|
|
}
|
|
inline uint64_t GetSize() { return file->GetSize(); }
|
|
inline uint64_t Tell() { return file->Tell(); }
|
|
inline native_handle GetNativeHandle() { return file->GetNativeHandle(); }
|
|
inline bool IsOpen() { return file->IsOpen(); }
|
|
inline bool Exists() { return file.get(); }
|
|
|
|
// POD to std::vector
|
|
template <typename T>
|
|
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool>
|
|
Read(std::vector<T> &vec, std::size_t size) {
|
|
vec.resize(size);
|
|
return this->Read(vec.data(), sizeof(T) * size) == sizeof(T) * size;
|
|
}
|
|
|
|
// Read POD std::vector, size must be set by resize() method
|
|
template <typename T>
|
|
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool>
|
|
Read(std::vector<T> &vec) {
|
|
return this->Read(vec.data(), sizeof(T) * vec.size()) ==
|
|
sizeof(T) * vec.size();
|
|
}
|
|
|
|
// Read POD, sizeof(T) is used
|
|
template <typename T>
|
|
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool>
|
|
Read(T &data) {
|
|
return Read(&data, sizeof(T)) == sizeof(T);
|
|
}
|
|
|
|
// Write POD unconditionally
|
|
template <typename T>
|
|
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value,
|
|
const File &>
|
|
Write(const T &data) {
|
|
Write(std::addressof(data), sizeof(T));
|
|
return *this;
|
|
}
|
|
|
|
// Write POD std::vector unconditionally
|
|
template <typename T>
|
|
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value,
|
|
const File &>
|
|
Write(const std::vector<T> &vec) {
|
|
/*if (*/ Write(vec.data(),
|
|
vec.size() *
|
|
sizeof(T)); // != vec.size() * sizeof(T);//) //xfail();
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
using FileHandle = std::shared_ptr<File>;
|
|
|
|
template <typename T> struct ContainerStream final : fileBase {
|
|
// T can be a reference, but this is not recommended
|
|
using value_type = typename std::remove_reference_t<T>::value_type;
|
|
|
|
T obj;
|
|
uint64_t pos;
|
|
|
|
ContainerStream(T &&obj) : obj(std::forward<T>(obj)), pos(0) {}
|
|
|
|
~ContainerStream() override {}
|
|
|
|
uint64_t Read(void *buffer, uint64_t size) override {
|
|
const uint64_t end = obj.size();
|
|
|
|
if (pos < end) {
|
|
// Get readable size
|
|
if (const uint64_t max = std::min<uint64_t>(size, end - pos)) {
|
|
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max,
|
|
static_cast<value_type *>(buffer));
|
|
pos = pos + max;
|
|
return max;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t Write(const void *buffer, uint64_t size) override {
|
|
const uint64_t old_size = obj.size();
|
|
|
|
if (old_size + size < old_size) {
|
|
// fmt::raw_error("fs::container_stream<>::write(): overflow");
|
|
}
|
|
|
|
if (pos > old_size) {
|
|
// Fill gap if necessary (default-initialized)
|
|
obj.resize(pos);
|
|
}
|
|
|
|
const auto src = static_cast<const value_type *>(buffer);
|
|
|
|
// Overwrite existing part
|
|
const uint64_t overlap = std::min<uint64_t>(obj.size() - pos, size);
|
|
std::copy(src, src + overlap, obj.begin() + pos);
|
|
|
|
// Append new data
|
|
obj.insert(obj.end(), src + overlap, src + size);
|
|
pos += size;
|
|
|
|
return size;
|
|
}
|
|
|
|
uint64_t Seek(int64_t offset, seekMode whence) override {
|
|
const int64_t new_pos =
|
|
whence == seekMode::seek_set
|
|
? offset
|
|
: whence == seekMode::seek_cur
|
|
? offset + pos
|
|
: whence == seekMode::seek_end ? offset + GetSize() : (0);
|
|
|
|
if (new_pos < 0) {
|
|
// fs::g_tls_error = fs::error::inval;
|
|
return -1;
|
|
}
|
|
|
|
pos = new_pos;
|
|
return pos;
|
|
}
|
|
|
|
uint64_t GetSize() override { return obj.size(); }
|
|
|
|
native_handle GetNativeHandle() override { return nullptr; }
|
|
|
|
uint64_t Tell() override { return pos; }
|
|
};
|
|
|
|
template <typename T> File make_stream(T &&container = T{}) {
|
|
File result(std::make_unique<ContainerStream<T>>(std::forward<T>(container)));
|
|
return result;
|
|
}
|
|
} |