neptools

Modding tools to Neptunia games
git clone https://git.neptards.moe/neptards/neptools.git
Log | Files | Refs | Submodules | README | LICENSE

source.hpp (9398B)


      1 #ifndef UUID_11A3E8B0_C5C5_4C4E_A22E_56F6E5346CEC
      2 #define UUID_11A3E8B0_C5C5_4C4E_A22E_56F6E5346CEC
      3 #pragma once
      4 
      5 #include "dumpable.hpp"
      6 #include "endian.hpp"
      7 
      8 #include <libshit/check.hpp>
      9 #include <libshit/low_io.hpp>
     10 #include <libshit/lua/value_object.hpp>
     11 #include <libshit/meta_utils.hpp>
     12 #include <libshit/nonowning_string.hpp>
     13 #include <libshit/not_null.hpp>
     14 #include <libshit/shared_ptr.hpp>
     15 
     16 #include <array>
     17 #include <cstdint>
     18 #include <boost/filesystem/path.hpp>
     19 
     20 namespace Neptools
     21 {
     22 
     23   LIBSHIT_GEN_EXCEPTION_TYPE(SourceOverflow, std::logic_error);
     24 
     25   /// A fixed size, read-only, seekable data source (or something that emulates
     26   /// it)
     27   class LIBSHIT_LUAGEN(const: false) Source final
     28     : public Libshit::Lua::ValueObject
     29   {
     30     LIBSHIT_LUA_CLASS;
     31   public:
     32     struct BufEntry
     33     {
     34       const Byte* ptr = nullptr;
     35       FilePosition offset = static_cast<FilePosition>(-1);
     36       FileMemSize size = 0;
     37     };
     38 
     39     Source(Source s, FilePosition offset, FilePosition size) noexcept
     40       : Source{std::move(s)} { Slice(offset, size); get = 0; }
     41 
     42     static Source FromFile(const boost::filesystem::path& fname);
     43     LIBSHIT_NOLUA
     44     static Source FromFd(
     45       boost::filesystem::path fname, Libshit::LowIo::FdType fd, bool owning);
     46     static Source FromMemory(std::string data)
     47     { return FromMemory("", std::move(data)); }
     48     static Source FromMemory(boost::filesystem::path fname, std::string data);
     49     LIBSHIT_NOLUA
     50     static Source FromMemory(boost::filesystem::path fname,
     51                              std::unique_ptr<char[]> data, std::size_t len);
     52 
     53     template <typename Checker = Libshit::Check::Assert>
     54     void Slice(FilePosition offset, FilePosition size) noexcept
     55     {
     56       LIBSHIT_CHECK(SourceOverflow, offset <= this->size &&
     57                     offset + size <= this->size, "Slice: invalid sizes");
     58       this->offset += offset;
     59       this->get -= offset;
     60       this->size = size;
     61     }
     62 
     63     FilePosition GetOffset() const noexcept { return offset; }
     64     FilePosition GetOrigSize() const noexcept { return p->size; }
     65     const boost::filesystem::path& GetFileName() const noexcept
     66     { return p->file_name; }
     67 
     68     FilePosition GetSize() const noexcept { return size; }
     69 
     70     template <typename Checker = Libshit::Check::Assert>
     71     void Seek(FilePosition pos) noexcept
     72     {
     73       LIBSHIT_CHECK(SourceOverflow, pos <= size, "Seek past end of source");
     74       get = pos;
     75     }
     76     FilePosition Tell() const noexcept { return get; }
     77     FilePosition GetRemainingSize() const noexcept { return size - get; }
     78     bool Eof() const noexcept { return get == size; }
     79 
     80     void CheckSize(FilePosition size) const
     81     {
     82       if (p->size < size)
     83         LIBSHIT_THROW(Libshit::DecodeError, "Premature end of data",
     84                       "Used source", *this);
     85     }
     86     void CheckRemainingSize(FilePosition size) const { CheckSize(get + size); }
     87 
     88     template <typename Checker = Libshit::Check::Assert, typename T>
     89     LIBSHIT_NOLUA void ReadGen(T& x)
     90     { Read<Checker>(reinterpret_cast<Byte*>(&x), Libshit::EmptySizeof<T>); }
     91 
     92     template <typename T, typename Checker = Libshit::Check::Assert>
     93     LIBSHIT_NOLUA T ReadGen() { T ret; ReadGen<Checker>(ret); return ret; }
     94 
     95 
     96     template <typename Checker = Libshit::Check::Assert, typename T>
     97     LIBSHIT_NOLUA void PreadGen(FilePosition offs, T& x) const
     98     { Pread<Checker>(offs, reinterpret_cast<Byte*>(&x), Libshit::EmptySizeof<T>); }
     99 
    100     template <typename T, typename Checker = Libshit::Check::Assert>
    101     LIBSHIT_NOLUA T PreadGen(FilePosition offs) const
    102     { T ret; PreadGen<Checker>(offs, ret); return ret; }
    103 
    104 
    105     template <typename Checker = Libshit::Check::Assert>
    106     LIBSHIT_NOLUA void Read(Byte* buf, FileMemSize len)
    107     { Pread<Checker>(get, buf, len); get += len; }
    108     template <typename Checker = Libshit::Check::Assert>
    109     LIBSHIT_NOLUA void Read(char* buf, FileMemSize len)
    110     { Pread<Checker>(get, buf, len); get += len; }
    111 
    112     template <typename Checker = Libshit::Check::Assert>
    113     LIBSHIT_NOLUA void Pread(
    114       FilePosition offs, Byte* buf, FileMemSize len) const
    115     {
    116       LIBSHIT_ADD_INFOS(
    117         LIBSHIT_CHECK(SourceOverflow, offs <= size && offs+len <= size,
    118                       "Source overflow");
    119         Pread_(offs, buf, len),
    120         "Used source", *this, "Read offset", offs, "Read size", len);
    121     }
    122 
    123     template <typename Checker = Libshit::Check::Assert>
    124     LIBSHIT_NOLUA
    125     void Pread(FilePosition offs, char* buf, FileMemSize len) const
    126     { Pread<Checker>(offs, reinterpret_cast<Byte*>(buf), len); }
    127 
    128     // helper
    129 #define NEPTOOLS_GEN_HLP2(bits, Camel, snake)                             \
    130     template <typename Checker = Libshit::Check::Assert>                  \
    131     std::uint##bits##_t Read##Camel##Uint##bits()                         \
    132     {                                                                     \
    133       return boost::endian::snake##_to_native(                            \
    134         ReadGen<std::uint##bits##_t, Checker>());                         \
    135     }                                                                     \
    136     template <typename Checker = Libshit::Check::Assert>                  \
    137     std::uint##bits##_t Pread##Camel##Uint##bits(FilePosition offs) const \
    138     {                                                                     \
    139       return boost::endian::snake##_to_native(                            \
    140         PreadGen<std::uint##bits##_t, Checker>(offs));                    \
    141     }
    142 #define NEPTOOLS_GEN_HLP(bits)                                           \
    143     template <typename Checker = Libshit::Check::Assert>                 \
    144     std::uint##bits##_t ReadUint##bits(Endian e)                         \
    145     {                                                                    \
    146       return ToNativeCopy(ReadGen<std::uint##bits##_t, Checker>(), e);   \
    147     }                                                                    \
    148     template <typename Checker = Libshit::Check::Assert>                 \
    149     std::uint##bits##_t PreadUint##bits(FilePosition offs, Endian e)     \
    150     {                                                                    \
    151       return ToNativeCopy(                                               \
    152         PreadGen<std::uint##bits##_t, Checker>(offs), e);                \
    153     }                                                                    \
    154     NEPTOOLS_GEN_HLP2(bits, Little, little)                              \
    155     NEPTOOLS_GEN_HLP2(bits, Big, big)
    156 
    157     // 8-bit values have no endian, but have these functions for consistency
    158     NEPTOOLS_GEN_HLP(8)
    159     NEPTOOLS_GEN_HLP(16)
    160     NEPTOOLS_GEN_HLP(32)
    161     NEPTOOLS_GEN_HLP(64)
    162 #undef NEPTOOLS_GEN_HLP
    163 
    164       std::string ReadCString()
    165     {
    166       auto ret = PreadCString(Tell());
    167       Seek(Tell() + ret.size() + 1);
    168       return ret;
    169     }
    170     std::string PreadCString(FilePosition offs) const;
    171 
    172     struct Provider : public Libshit::RefCounted
    173     {
    174       Provider(boost::filesystem::path file_name, FilePosition size)
    175         : file_name{std::move(file_name)}, size{size} {}
    176       Provider(const Provider&) = delete;
    177       void operator=(const Provider&) = delete;
    178       virtual ~Provider() = default;
    179 
    180       virtual void Pread(FilePosition offs, Byte* buf, FileMemSize len) = 0;
    181 
    182       void LruPush(const Byte* ptr, FilePosition offset, FileMemSize size);
    183       bool LruGet(FilePosition offs);
    184 
    185       std::array<BufEntry, 4> lru;
    186       boost::filesystem::path file_name;
    187       FilePosition size;
    188     };
    189     LIBSHIT_NOLUA Source(Libshit::NotNullSmartPtr<Provider> p)
    190       : size{p->size}, p{Libshit::Move(p)} {}
    191 
    192     void Dump(Sink& sink) const;
    193     LIBSHIT_NOLUA void Dump(Sink&& sink) const { Dump(sink); }
    194     LIBSHIT_NOLUA void Inspect(std::ostream& os) const;
    195     LIBSHIT_NOLUA void Inspect(std::ostream&& os) const { Inspect(os); }
    196     std::string Inspect() const;
    197 
    198     LIBSHIT_NOLUA Libshit::StringView GetChunk(FilePosition offs) const;
    199 
    200   private:
    201     // offset: in original file!
    202     BufEntry GetTemporaryEntry(FilePosition offs) const;
    203 
    204     void Pread_(FilePosition offs, Byte* buf, FileMemSize len) const;
    205     static Source FromFile_(const boost::filesystem::path& fname);
    206 
    207     FilePosition offset = 0, size, get = 0;
    208 
    209     Libshit::NotNull<Libshit::SmartPtr<Provider>> p;
    210   };
    211 
    212   inline std::ostream& operator<<(std::ostream& os, const Source s)
    213   { s.Inspect(os); return os; }
    214 
    215   std::string to_string(const Source& s);
    216 
    217   class DumpableSource final : public Dumpable
    218   {
    219     LIBSHIT_DYNAMIC_OBJECT;
    220   public:
    221     LIBSHIT_NOLUA
    222     DumpableSource(Source&& s) noexcept : src{std::move(s)} {}
    223     DumpableSource(const Source& s, FilePosition offset, FilePosition size) noexcept
    224       : src{s, offset, size} {}
    225     DumpableSource(const Source& s) noexcept : src{s} {} // NOLINT
    226 
    227     void Fixup() override {}
    228 
    229     FilePosition GetSize() const override { return src.GetSize(); }
    230     Source GetSource() const noexcept { return src; }
    231   private:
    232     Source src;
    233     void Dump_(Sink& sink) const override { src.Dump(sink); }
    234     void Inspect_(std::ostream& os, unsigned) const override;
    235   };
    236 
    237 #define ADD_SOURCE(expr, ...) \
    238   LIBSHIT_ADD_INFOS(expr, "Used source", __VA_ARGS__)
    239 
    240   struct QuotedSource { Source src; };
    241   inline std::ostream& operator<<(std::ostream& os, QuotedSource q)
    242   { DumpBytes(os, q.src); return os; }
    243   inline QuotedSource Quoted(Source src) { return {src}; }
    244 
    245 }
    246 #endif