mirror of https://github.com/PCSX2/pcsx2.git
Misc: Add StateWrapper
Eventually we'll move save states over to it..
This commit is contained in:
parent
ebeb88d425
commit
b1ff979171
|
@ -172,6 +172,7 @@ set(pcsx2Sources
|
|||
Sio.cpp
|
||||
SourceLog.cpp
|
||||
SPR.cpp
|
||||
StateWrapper.cpp
|
||||
System.cpp
|
||||
Vif0_Dma.cpp
|
||||
Vif1_Dma.cpp
|
||||
|
@ -242,6 +243,7 @@ set(pcsx2Headers
|
|||
SingleRegisterTypes.h
|
||||
Sio.h
|
||||
SPR.h
|
||||
StateWrapper.h
|
||||
SysForwardDefs.h
|
||||
System.h
|
||||
Vif_Dma.h
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "StateWrapper.h"
|
||||
#include "common/Console.h"
|
||||
#include <cinttypes>
|
||||
#include <cstring>
|
||||
|
||||
StateWrapper::StateWrapper(IStream* stream, Mode mode, u32 version)
|
||||
: m_stream(stream)
|
||||
, m_mode(mode)
|
||||
, m_version(version)
|
||||
{
|
||||
}
|
||||
|
||||
StateWrapper::~StateWrapper() = default;
|
||||
|
||||
void StateWrapper::DoBytes(void* data, size_t length)
|
||||
{
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
if (m_error || (m_error |= (m_stream->Read(data, static_cast<u32>(length)) != static_cast<u32>(length))) == true)
|
||||
std::memset(data, 0, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Write(data, static_cast<u32>(length)) != static_cast<u32>(length));
|
||||
}
|
||||
}
|
||||
|
||||
void StateWrapper::Do(bool* value_ptr)
|
||||
{
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
u8 value = 0;
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Read(&value, sizeof(value)) != sizeof(value));
|
||||
*value_ptr = (value != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 value = static_cast<u8>(*value_ptr);
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Write(&value, sizeof(value)) != sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
void StateWrapper::Do(std::string* value_ptr)
|
||||
{
|
||||
u32 length = static_cast<u32>(value_ptr->length());
|
||||
Do(&length);
|
||||
if (m_mode == Mode::Read)
|
||||
value_ptr->resize(length);
|
||||
DoBytes(&(*value_ptr)[0], length);
|
||||
value_ptr->resize(std::strlen(&(*value_ptr)[0]));
|
||||
}
|
||||
|
||||
bool StateWrapper::DoMarker(const char* marker)
|
||||
{
|
||||
std::string file_value(marker);
|
||||
Do(&file_value);
|
||||
if (m_error)
|
||||
return false;
|
||||
|
||||
if (m_mode == Mode::Write || file_value == marker)
|
||||
return true;
|
||||
|
||||
Console.WriteLn("Marker mismatch at offset %u: found '%s' expected '%s'", m_stream->GetPosition(),
|
||||
file_value.c_str(), marker);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
StateWrapper::ReadOnlyMemoryStream::ReadOnlyMemoryStream(const void* buf, u32 buf_length)
|
||||
: m_buf(static_cast<const u8*>(buf))
|
||||
, m_buf_length(buf_length)
|
||||
{
|
||||
}
|
||||
|
||||
u32 StateWrapper::ReadOnlyMemoryStream::Read(void* buf, u32 count)
|
||||
{
|
||||
count = std::min(m_buf_length - m_buf_position, count);
|
||||
if (count > 0)
|
||||
{
|
||||
std::memcpy(buf, &m_buf[m_buf_position], count);
|
||||
m_buf_position += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 StateWrapper::ReadOnlyMemoryStream::Write(const void* buf, u32 count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 StateWrapper::ReadOnlyMemoryStream::GetPosition()
|
||||
{
|
||||
return m_buf_position;
|
||||
}
|
||||
|
||||
bool StateWrapper::ReadOnlyMemoryStream::SeekAbsolute(u32 pos)
|
||||
{
|
||||
if (pos > m_buf_length)
|
||||
return false;
|
||||
|
||||
m_buf_position = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateWrapper::ReadOnlyMemoryStream::SeekRelative(s32 count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
if (static_cast<u32>(-count) > m_buf_position)
|
||||
return false;
|
||||
|
||||
m_buf_position -= static_cast<u32>(-count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_buf_position + static_cast<u32>(count)) > m_buf_length)
|
||||
return false;
|
||||
|
||||
m_buf_position += static_cast<u32>(count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
StateWrapper::MemoryStream::MemoryStream(void* buf, u32 buf_length)
|
||||
: m_buf(static_cast<u8*>(buf))
|
||||
, m_buf_length(buf_length)
|
||||
{
|
||||
}
|
||||
|
||||
u32 StateWrapper::MemoryStream::Read(void* buf, u32 count)
|
||||
{
|
||||
count = std::min(m_buf_length - m_buf_position, count);
|
||||
if (count > 0)
|
||||
{
|
||||
std::memcpy(buf, &m_buf[m_buf_position], count);
|
||||
m_buf_position += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 StateWrapper::MemoryStream::Write(const void* buf, u32 count)
|
||||
{
|
||||
count = std::min(m_buf_length - m_buf_position, count);
|
||||
if (count > 0)
|
||||
{
|
||||
std::memcpy(&m_buf[m_buf_position], buf, count);
|
||||
m_buf_position += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 StateWrapper::MemoryStream::GetPosition()
|
||||
{
|
||||
return m_buf_position;
|
||||
}
|
||||
|
||||
bool StateWrapper::MemoryStream::SeekAbsolute(u32 pos)
|
||||
{
|
||||
if (pos > m_buf_length)
|
||||
return false;
|
||||
|
||||
m_buf_position = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateWrapper::MemoryStream::SeekRelative(s32 count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
if (static_cast<u32>(-count) > m_buf_position)
|
||||
return false;
|
||||
|
||||
m_buf_position -= static_cast<u32>(-count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_buf_position + static_cast<u32>(count)) > m_buf_length)
|
||||
return false;
|
||||
|
||||
m_buf_position += static_cast<u32>(count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
StateWrapper::VectorMemoryStream::VectorMemoryStream() = default;
|
||||
|
||||
StateWrapper::VectorMemoryStream::VectorMemoryStream(u32 reserve)
|
||||
{
|
||||
m_buf.reserve(reserve);
|
||||
}
|
||||
|
||||
u32 StateWrapper::VectorMemoryStream::Read(void* buf, u32 count)
|
||||
{
|
||||
count = std::min(static_cast<u32>(m_buf.size() - m_buf_position), count);
|
||||
if (count > 0)
|
||||
{
|
||||
std::memcpy(buf, &m_buf[m_buf_position], count);
|
||||
m_buf_position += count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 StateWrapper::VectorMemoryStream::Write(const void* buf, u32 count)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
Expand(m_buf_position + count);
|
||||
std::memcpy(&m_buf[m_buf_position], buf, count);
|
||||
m_buf_position += count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 StateWrapper::VectorMemoryStream::GetPosition()
|
||||
{
|
||||
return m_buf_position;
|
||||
}
|
||||
|
||||
bool StateWrapper::VectorMemoryStream::SeekAbsolute(u32 pos)
|
||||
{
|
||||
if (pos > m_buf.size())
|
||||
return false;
|
||||
|
||||
m_buf_position = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StateWrapper::VectorMemoryStream::SeekRelative(s32 count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
if (static_cast<u32>(-count) > m_buf_position)
|
||||
return false;
|
||||
|
||||
m_buf_position -= static_cast<u32>(-count);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_buf_position + static_cast<u32>(count)) > m_buf.size())
|
||||
return false;
|
||||
|
||||
m_buf_position += static_cast<u32>(count);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void StateWrapper::VectorMemoryStream::Expand(u32 new_size)
|
||||
{
|
||||
if (m_buf.size() >= new_size)
|
||||
return;
|
||||
|
||||
// don't grow more than 4K at a time
|
||||
const u32 grow_size = std::min((m_buf.size() > 4096u) ? 4096u : static_cast<u32>(m_buf.size()), static_cast<u32>(m_buf.size() - new_size));
|
||||
m_buf.reserve(m_buf.size() + grow_size);
|
||||
|
||||
// should take care of growth, right?
|
||||
m_buf.resize(new_size);
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/* PCSX2 - PS2 Emulator for PCs
|
||||
* Copyright (C) 2002-2022 PCSX2 Dev Team
|
||||
*
|
||||
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU Lesser General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCSX2.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "common/HeapArray.h"
|
||||
#include "common/Pcsx2Defs.h"
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
class String;
|
||||
|
||||
class StateWrapper
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
Read,
|
||||
Write
|
||||
};
|
||||
|
||||
// Only supports up to 4GB. More than enough.
|
||||
class IStream
|
||||
{
|
||||
public:
|
||||
virtual u32 Read(void* buf, u32 count) = 0;
|
||||
virtual u32 Write(const void* buf, u32 count) = 0;
|
||||
virtual u32 GetPosition() = 0;
|
||||
virtual bool SeekAbsolute(u32 pos) = 0;
|
||||
virtual bool SeekRelative(s32 count) = 0;
|
||||
};
|
||||
|
||||
class ReadOnlyMemoryStream : public IStream
|
||||
{
|
||||
public:
|
||||
ReadOnlyMemoryStream(const void* buf, u32 buf_length);
|
||||
|
||||
u32 Read(void* buf, u32 count) override;
|
||||
u32 Write(const void* buf, u32 count) override;
|
||||
u32 GetPosition() override;
|
||||
bool SeekAbsolute(u32 pos) override;
|
||||
bool SeekRelative(s32 count) override;
|
||||
|
||||
private:
|
||||
const u8* m_buf;
|
||||
u32 m_buf_length;
|
||||
u32 m_buf_position = 0;
|
||||
};
|
||||
|
||||
class MemoryStream : public IStream
|
||||
{
|
||||
public:
|
||||
MemoryStream(void* buf, u32 buf_length);
|
||||
|
||||
u32 Read(void* buf, u32 count) override;
|
||||
u32 Write(const void* buf, u32 count) override;
|
||||
u32 GetPosition() override;
|
||||
bool SeekAbsolute(u32 pos) override;
|
||||
bool SeekRelative(s32 count) override;
|
||||
|
||||
private:
|
||||
u8* m_buf;
|
||||
u32 m_buf_length;
|
||||
u32 m_buf_position = 0;
|
||||
};
|
||||
|
||||
class VectorMemoryStream : public IStream
|
||||
{
|
||||
public:
|
||||
VectorMemoryStream();
|
||||
VectorMemoryStream(u32 reserve);
|
||||
|
||||
u32 Read(void* buf, u32 count) override;
|
||||
u32 Write(const void* buf, u32 count) override;
|
||||
u32 GetPosition() override;
|
||||
bool SeekAbsolute(u32 pos) override;
|
||||
bool SeekRelative(s32 count) override;
|
||||
|
||||
private:
|
||||
void Expand(u32 new_size);
|
||||
|
||||
std::vector<u8> m_buf;
|
||||
u32 m_buf_position = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
StateWrapper(IStream* stream, Mode mode, u32 version);
|
||||
StateWrapper(const StateWrapper&) = delete;
|
||||
~StateWrapper();
|
||||
|
||||
IStream* GetStream() const { return m_stream; }
|
||||
bool HasError() const { return m_error; }
|
||||
bool IsReading() const { return (m_mode == Mode::Read); }
|
||||
bool IsWriting() const { return (m_mode == Mode::Write); }
|
||||
Mode GetMode() const { return m_mode; }
|
||||
void SetMode(Mode mode) { m_mode = mode; }
|
||||
u32 GetVersion() const { return m_version; }
|
||||
|
||||
/// Overload for integral or floating-point types. Writes bytes as-is.
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, int> = 0>
|
||||
void Do(T* value_ptr)
|
||||
{
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
if (m_error || (m_error |= (m_stream->Read(value_ptr, sizeof(T)) != sizeof(T))) == true)
|
||||
*value_ptr = static_cast<T>(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Write(value_ptr, sizeof(T)) != sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
/// Overload for enum types. Uses the underlying type.
|
||||
template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
|
||||
void Do(T* value_ptr)
|
||||
{
|
||||
using TType = std::underlying_type_t<T>;
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
TType temp;
|
||||
if (m_error || (m_error |= (m_stream->Read(&temp, sizeof(TType)) != sizeof(T))) == true)
|
||||
temp = static_cast<TType>(0);
|
||||
|
||||
*value_ptr = static_cast<T>(temp);
|
||||
}
|
||||
else
|
||||
{
|
||||
TType temp;
|
||||
std::memcpy(&temp, value_ptr, sizeof(TType));
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Write(&temp, sizeof(TType)) != sizeof(TType));
|
||||
}
|
||||
}
|
||||
|
||||
/// Overload for POD types, such as structs.
|
||||
template <typename T, std::enable_if_t<std::is_pod_v<T>, int> = 0>
|
||||
void DoPOD(T* value_ptr)
|
||||
{
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
if (m_error || (m_error |= (m_stream->Read(value_ptr, sizeof(T)) != sizeof(T))) == true)
|
||||
std::memset(value_ptr, 0, sizeof(*value_ptr));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_error)
|
||||
m_error |= (m_stream->Write(value_ptr, sizeof(T)) != sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DoArray(T* values, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
Do(&values[i]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DoPODArray(T* values, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
DoPOD(&values[i]);
|
||||
}
|
||||
|
||||
void DoBytes(void* data, size_t length);
|
||||
|
||||
void Do(bool* value_ptr);
|
||||
void Do(std::string* value_ptr);
|
||||
void Do(String* value_ptr);
|
||||
|
||||
template <typename T, size_t N>
|
||||
void Do(std::array<T, N>* data)
|
||||
{
|
||||
DoArray(data->data(), data->size());
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
void Do(HeapArray<T, N>* data)
|
||||
{
|
||||
DoArray(data->data(), data->size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::vector<T>* data)
|
||||
{
|
||||
u32 length = static_cast<u32>(data->size());
|
||||
Do(&length);
|
||||
if (m_mode == Mode::Read)
|
||||
data->resize(length);
|
||||
DoArray(data->data(), data->size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Do(std::deque<T>* data)
|
||||
{
|
||||
u32 length = static_cast<u32>(data->size());
|
||||
Do(&length);
|
||||
if (m_mode == Mode::Read)
|
||||
{
|
||||
data->clear();
|
||||
for (u32 i = 0; i < length; i++)
|
||||
{
|
||||
T value;
|
||||
Do(&value);
|
||||
data->push_back(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < length; i++)
|
||||
Do(&data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T DoBitfield(T data)
|
||||
{
|
||||
Do(&data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool DoMarker(const char* marker);
|
||||
|
||||
template <typename T>
|
||||
void DoEx(T* data, u32 version_introduced, T default_value)
|
||||
{
|
||||
if (m_version < version_introduced)
|
||||
{
|
||||
*data = std::move(default_value);
|
||||
return;
|
||||
}
|
||||
|
||||
Do(data);
|
||||
}
|
||||
|
||||
void SkipBytes(u32 count)
|
||||
{
|
||||
if (m_mode != Mode::Read)
|
||||
{
|
||||
m_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_error)
|
||||
m_error = !m_stream->SeekRelative(static_cast<s32>(count));
|
||||
}
|
||||
|
||||
private:
|
||||
IStream* m_stream;
|
||||
Mode m_mode;
|
||||
u32 m_version;
|
||||
bool m_error = false;
|
||||
};
|
|
@ -335,6 +335,7 @@
|
|||
<ClCompile Include="SPU2\Windows\SndOut_XAudio2.cpp" />
|
||||
<ClCompile Include="USB\USBNull.cpp" />
|
||||
<ClCompile Include="Dump.cpp" />
|
||||
<ClCompile Include="StateWrapper.cpp" />
|
||||
<ClCompile Include="VMManager.cpp" />
|
||||
<ClCompile Include="windows\Optimus.cpp" />
|
||||
<ClCompile Include="Pcsx2Config.cpp" />
|
||||
|
@ -664,6 +665,7 @@
|
|||
<ClInclude Include="PCSX2Base.h" />
|
||||
<ClInclude Include="PrecompiledHeader.h" />
|
||||
<ClInclude Include="ps2\pgif.h" />
|
||||
<ClInclude Include="StateWrapper.h" />
|
||||
<ClInclude Include="USB\USB.h" />
|
||||
<ClInclude Include="Utilities\AsciiFile.h" />
|
||||
<ClInclude Include="Elfheader.h" />
|
||||
|
|
|
@ -1317,6 +1317,7 @@
|
|||
<ClCompile Include="MultitapProtocol.cpp">
|
||||
<Filter>System\Ps2\Iop</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StateWrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Patch.h">
|
||||
|
@ -2192,6 +2193,7 @@
|
|||
<ClInclude Include="MultitapProtocol.h">
|
||||
<Filter>System\Ps2\Iop</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StateWrapper.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuildStep Include="rdebug\deci2.h">
|
||||
|
@ -2216,4 +2218,4 @@
|
|||
<Filter>System\Ps2\Debug\rdebug</Filter>
|
||||
</CustomBuildStep>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue