duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

state_wrapper.h (4869B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #pragma once
      5 
      6 #include "common/fifo_queue.h"
      7 #include "common/heap_array.h"
      8 #include "common/types.h"
      9 
     10 #include <cstring>
     11 #include <deque>
     12 #include <string>
     13 #include <span>
     14 #include <type_traits>
     15 #include <vector>
     16 
     17 class SmallStringBase;
     18 
     19 class StateWrapper
     20 {
     21 public:
     22   enum class Mode
     23   {
     24     Read,
     25     Write
     26   };
     27 
     28   StateWrapper(std::span<u8> data, Mode mode, u32 version);
     29   StateWrapper(std::span<const u8> data, Mode mode, u32 version);
     30   StateWrapper(const StateWrapper&) = delete;
     31   ~StateWrapper();
     32 
     33   ALWAYS_INLINE bool HasError() const { return m_error; }
     34   ALWAYS_INLINE bool IsReading() const { return (m_mode == Mode::Read); }
     35   ALWAYS_INLINE bool IsWriting() const { return (m_mode == Mode::Write); }
     36   ALWAYS_INLINE u32 GetVersion() const { return m_version; }
     37   ALWAYS_INLINE size_t GetPosition() const { return m_pos; }
     38 
     39   /// Overload for integral or floating-point types. Writes bytes as-is.
     40   template<typename T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, int> = 0>
     41   void Do(T* value_ptr)
     42   {
     43     if (m_mode == Mode::Read)
     44     {
     45       if (!ReadData(value_ptr, sizeof(T))) [[unlikely]]
     46         *value_ptr = static_cast<T>(0);
     47     }
     48     else
     49     {
     50       WriteData(value_ptr, sizeof(T));
     51     }
     52   }
     53 
     54   /// Overload for enum types. Uses the underlying type.
     55   template<typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
     56   void Do(T* value_ptr)
     57   {
     58     using TType = std::underlying_type_t<T>;
     59     if (m_mode == Mode::Read)
     60     {
     61       TType temp;
     62       if (!ReadData(&temp, sizeof(temp))) [[unlikely]]
     63         temp = static_cast<TType>(0);
     64 
     65       *value_ptr = static_cast<T>(temp);
     66     }
     67     else
     68     {
     69       const TType temp = static_cast<TType>(*value_ptr);
     70       WriteData(&temp, sizeof(temp));
     71     }
     72   }
     73 
     74   /// Overload for POD types, such as structs.
     75   template<typename T, std::enable_if_t<std::is_standard_layout_v<T> && std::is_trivial_v<T>, int> = 0>
     76   void DoPOD(T* value_ptr)
     77   {
     78     if (m_mode == Mode::Read)
     79     {
     80       if (!ReadData(value_ptr, sizeof(T))) [[unlikely]]
     81         std::memset(value_ptr, 0, sizeof(*value_ptr));
     82     }
     83     else
     84     {
     85       WriteData(value_ptr, sizeof(T));
     86     }
     87   }
     88 
     89   template<typename T>
     90   void DoArray(T* values, size_t count)
     91   {
     92     for (size_t i = 0; i < count; i++)
     93       Do(&values[i]);
     94   }
     95 
     96   template<typename T>
     97   void DoPODArray(T* values, size_t count)
     98   {
     99     for (size_t i = 0; i < count; i++)
    100       DoPOD(&values[i]);
    101   }
    102 
    103   void DoBytes(void* data, size_t length);
    104   void DoBytesEx(void* data, size_t length, u32 version_introduced, const void* default_value);
    105 
    106   void Do(bool* value_ptr);
    107   void Do(std::string* value_ptr);
    108   void Do(std::string_view* value_ptr);
    109   void Do(SmallStringBase* value_ptr);
    110 
    111   template<typename T, size_t N>
    112   void Do(std::array<T, N>* data)
    113   {
    114     DoArray(data->data(), data->size());
    115   }
    116 
    117   template<typename T, size_t N>
    118   void Do(FixedHeapArray<T, N>* data)
    119   {
    120     DoArray(data->data(), data->size());
    121   }
    122 
    123   template<typename T>
    124   void Do(std::vector<T>* data)
    125   {
    126     u32 length = static_cast<u32>(data->size());
    127     Do(&length);
    128     if (m_mode == Mode::Read)
    129       data->resize(length);
    130     DoArray(data->data(), data->size());
    131   }
    132 
    133   template<typename T>
    134   void Do(std::deque<T>* data)
    135   {
    136     u32 length = static_cast<u32>(data->size());
    137     Do(&length);
    138     if (m_mode == Mode::Read)
    139     {
    140       data->clear();
    141       for (u32 i = 0; i < length; i++)
    142       {
    143         T value;
    144         Do(&value);
    145         data->push_back(value);
    146       }
    147     }
    148     else
    149     {
    150       for (u32 i = 0; i < length; i++)
    151         Do(&data[i]);
    152     }
    153   }
    154 
    155   template<typename T, u32 CAPACITY>
    156   void Do(FIFOQueue<T, CAPACITY>* data)
    157   {
    158     u32 size = data->GetSize();
    159     Do(&size);
    160 
    161     if (m_mode == Mode::Read)
    162     {
    163       T* temp = new T[size];
    164       DoArray(temp, size);
    165       data->Clear();
    166       data->PushRange(temp, size);
    167       delete[] temp;
    168     }
    169     else
    170     {
    171       for (u32 i = 0; i < size; i++)
    172       {
    173         T temp(data->Peek(i));
    174         Do(&temp);
    175       }
    176     }
    177   }
    178 
    179   bool DoMarker(const char* marker);
    180 
    181   template<typename T>
    182   void DoEx(T* data, u32 version_introduced, T default_value)
    183   {
    184     if (m_mode == Mode::Read && m_version < version_introduced) [[unlikely]]
    185     {
    186       *data = std::move(default_value);
    187       return;
    188     }
    189 
    190     Do(data);
    191   }
    192 
    193   void SkipBytes(size_t count)
    194   {
    195     if (m_mode != Mode::Read)
    196     {
    197       m_error = true;
    198       return;
    199     }
    200 
    201     m_error = (m_error || (m_pos + count) > m_size);
    202     if (!m_error) [[likely]]
    203       m_pos += count;
    204   }
    205 
    206 private:
    207   bool ReadData(void* buf, size_t size);
    208   bool WriteData(const void* buf, size_t size);
    209 
    210   u8* m_data;
    211   size_t m_size;
    212   size_t m_pos = 0;
    213   Mode m_mode;
    214   u32 m_version;
    215   bool m_error = false;
    216 };