IOS/FS: Allow IPC interface to be used internally from IOS HLE
This makes it more convenient to emulate timings for IPC commands that perform internal IOS <-> IOS IPC, for example ES relying on FS for filesystem access.
This commit is contained in:
parent
1073463d35
commit
f214df5d2c
|
@ -22,10 +22,16 @@ namespace IOS::HLE
|
||||||
{
|
{
|
||||||
using namespace IOS::HLE::FS;
|
using namespace IOS::HLE::FS;
|
||||||
|
|
||||||
static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0)
|
static constexpr u64 GetIPCOverheadTicks()
|
||||||
{
|
{
|
||||||
// According to hardware tests, FS takes at least 2700 TB ticks to reply to commands.
|
// According to hardware tests, FS takes at least 2700 TB ticks to reply to commands.
|
||||||
return IPCReply{return_value, (2700 + extra_tb_ticks) * SystemTimers::TIMER_RATIO};
|
return 2700;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IPCReply GetFSReply(s32 return_value, u64 extra_tb_ticks = 0)
|
||||||
|
{
|
||||||
|
return IPCReply{return_value,
|
||||||
|
(GetIPCOverheadTicks() + extra_tb_ticks) * SystemTimers::TIMER_RATIO};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duration of a superblock write (in timebase ticks).
|
/// Duration of a superblock write (in timebase ticks).
|
||||||
|
@ -95,10 +101,11 @@ FSDevice::FSDevice(Kernel& ios, const std::string& device_name) : Device(ios, de
|
||||||
|
|
||||||
void FSDevice::DoState(PointerWrap& p)
|
void FSDevice::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(m_fd_map);
|
|
||||||
p.Do(m_cache_fd);
|
|
||||||
p.Do(m_cache_chain_index);
|
|
||||||
p.Do(m_dirty_cache);
|
p.Do(m_dirty_cache);
|
||||||
|
p.Do(m_cache_chain_index);
|
||||||
|
p.Do(m_cache_fd);
|
||||||
|
p.Do(m_next_fd);
|
||||||
|
p.Do(m_fd_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
|
@ -153,59 +160,78 @@ static IPCReply GetReplyForSuperblockOperation(int ios_version, ResultCode resul
|
||||||
|
|
||||||
std::optional<IPCReply> FSDevice::Open(const OpenRequest& request)
|
std::optional<IPCReply> FSDevice::Open(const OpenRequest& request)
|
||||||
{
|
{
|
||||||
if (m_fd_map.size() >= 16)
|
return MakeIPCReply([&](Ticks t) {
|
||||||
return GetFSReply(ConvertResult(ResultCode::NoFreeHandle));
|
return Open(request.uid, request.gid, request.path, static_cast<Mode>(request.flags & 3),
|
||||||
|
request.fd, t);
|
||||||
if (request.path.size() >= 64)
|
});
|
||||||
return GetFSReply(ConvertResult(ResultCode::Invalid));
|
|
||||||
|
|
||||||
if (request.path == "/dev/fs")
|
|
||||||
{
|
|
||||||
m_fd_map[request.fd] = {request.gid, request.uid, INVALID_FD};
|
|
||||||
return GetFSReply(IPC_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 ticks = EstimateFileLookupTicks(request.path, FileLookupMode::Normal);
|
s64 FSDevice::Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode,
|
||||||
|
std::optional<u32> ipc_fd, Ticks ticks)
|
||||||
|
{
|
||||||
|
ticks.AddTimeBaseTicks(GetIPCOverheadTicks());
|
||||||
|
|
||||||
auto backend_fd = m_ios.GetFS()->OpenFile(request.uid, request.gid, request.path,
|
if (m_fd_map.size() >= 16)
|
||||||
static_cast<Mode>(request.flags & 3));
|
return ConvertResult(ResultCode::NoFreeHandle);
|
||||||
LogResult(backend_fd, "OpenFile({})", request.path);
|
|
||||||
|
if (path.size() >= 64)
|
||||||
|
return ConvertResult(ResultCode::Invalid);
|
||||||
|
|
||||||
|
const u64 fd = ipc_fd.has_value() ? u64(*ipc_fd) : m_next_fd++;
|
||||||
|
|
||||||
|
if (path == "/dev/fs")
|
||||||
|
{
|
||||||
|
m_fd_map[fd] = {gid, uid, INVALID_FD};
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticks.AddTimeBaseTicks(EstimateFileLookupTicks(path, FileLookupMode::Normal));
|
||||||
|
|
||||||
|
auto backend_fd = m_ios.GetFS()->OpenFile(uid, gid, path, mode);
|
||||||
|
LogResult(backend_fd, "OpenFile({})", path);
|
||||||
if (!backend_fd)
|
if (!backend_fd)
|
||||||
return GetFSReply(ConvertResult(backend_fd.Error()), ticks);
|
return ConvertResult(backend_fd.Error());
|
||||||
|
|
||||||
m_fd_map[request.fd] = {request.gid, request.uid, backend_fd->Release()};
|
auto& handle = m_fd_map[fd] = {gid, uid, backend_fd->Release()};
|
||||||
std::strncpy(m_fd_map[request.fd].name.data(), request.path.c_str(), 64);
|
std::strncpy(handle.name.data(), path.c_str(), handle.name.size());
|
||||||
return GetFSReply(IPC_SUCCESS, ticks);
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IPCReply> FSDevice::Close(u32 fd)
|
std::optional<IPCReply> FSDevice::Close(u32 fd)
|
||||||
{
|
{
|
||||||
u64 ticks = 0;
|
return MakeIPCReply([&](Ticks t) { return Close(static_cast<u64>(fd), t); });
|
||||||
if (m_fd_map[fd].fs_fd != INVALID_FD)
|
}
|
||||||
|
|
||||||
|
s32 FSDevice::Close(u64 fd, Ticks ticks)
|
||||||
|
{
|
||||||
|
ticks.AddTimeBaseTicks(GetIPCOverheadTicks());
|
||||||
|
|
||||||
|
const auto& handle = m_fd_map[fd];
|
||||||
|
if (handle.fs_fd != INVALID_FD)
|
||||||
{
|
{
|
||||||
if (fd == m_cache_fd)
|
if (fd == m_cache_fd)
|
||||||
{
|
{
|
||||||
ticks += SimulateFlushFileCache();
|
ticks.AddTimeBaseTicks(SimulateFlushFileCache());
|
||||||
m_cache_fd = INVALID_FD;
|
m_cache_fd.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fd_map[fd].superblock_flush_needed)
|
if (handle.superblock_flush_needed)
|
||||||
ticks += GetSuperblockWriteTbTicks(m_ios.GetVersion());
|
ticks.AddTimeBaseTicks(GetSuperblockWriteTbTicks(m_ios.GetVersion()));
|
||||||
|
|
||||||
const ResultCode result = m_ios.GetFS()->Close(m_fd_map[fd].fs_fd);
|
const ResultCode result = m_ios.GetFS()->Close(handle.fs_fd);
|
||||||
LogResult(result, "Close({})", m_fd_map[fd].name.data());
|
LogResult(result, "Close({})", handle.name.data());
|
||||||
m_fd_map.erase(fd);
|
m_fd_map.erase(fd);
|
||||||
if (result != ResultCode::Success)
|
if (result != ResultCode::Success)
|
||||||
return GetFSReply(ConvertResult(result));
|
return ConvertResult(result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_fd_map.erase(fd);
|
m_fd_map.erase(fd);
|
||||||
}
|
}
|
||||||
return GetFSReply(IPC_SUCCESS, ticks);
|
return IPC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 FSDevice::SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size)
|
u64 FSDevice::SimulatePopulateFileCache(u64 fd, u32 offset, u32 file_size)
|
||||||
{
|
{
|
||||||
if (HasCacheForFile(fd, offset))
|
if (HasCacheForFile(fd, offset))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -221,22 +247,23 @@ u64 FSDevice::SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size)
|
||||||
|
|
||||||
u64 FSDevice::SimulateFlushFileCache()
|
u64 FSDevice::SimulateFlushFileCache()
|
||||||
{
|
{
|
||||||
if (m_cache_fd == INVALID_FD || !m_dirty_cache)
|
if (!m_cache_fd.has_value() || !m_dirty_cache)
|
||||||
return 0;
|
return 0;
|
||||||
m_dirty_cache = false;
|
m_dirty_cache = false;
|
||||||
m_fd_map[m_cache_fd].superblock_flush_needed = true;
|
m_fd_map[*m_cache_fd].superblock_flush_needed = true;
|
||||||
return GetClusterWriteTbTicks(m_ios.GetVersion());
|
return GetClusterWriteTbTicks(m_ios.GetVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulate parts of the FS read/write logic to estimate ticks for file operations correctly.
|
// Simulate parts of the FS read/write logic to estimate ticks for file operations correctly.
|
||||||
u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteRequest& request)
|
u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, u64 fd, IPCCommandType command,
|
||||||
|
u32 size)
|
||||||
{
|
{
|
||||||
u64 ticks = 0;
|
u64 ticks = 0;
|
||||||
|
|
||||||
const bool is_write = request.command == IPC_CMD_WRITE;
|
const bool is_write = command == IPC_CMD_WRITE;
|
||||||
const Result<FileStatus> status = m_ios.GetFS()->GetFileStatus(handle.fs_fd);
|
const Result<FileStatus> status = m_ios.GetFS()->GetFileStatus(handle.fs_fd);
|
||||||
u32 offset = status->offset;
|
u32 offset = status->offset;
|
||||||
u32 count = request.size;
|
u32 count = size;
|
||||||
if (!is_write && count + offset > status->size)
|
if (!is_write && count + offset > status->size)
|
||||||
count = status->size - offset;
|
count = status->size - offset;
|
||||||
|
|
||||||
|
@ -244,17 +271,17 @@ u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteReq
|
||||||
{
|
{
|
||||||
u32 copy_length;
|
u32 copy_length;
|
||||||
// Fast path (if not cached): FS copies an entire cluster directly from/to the request.
|
// Fast path (if not cached): FS copies an entire cluster directly from/to the request.
|
||||||
if (!HasCacheForFile(request.fd, offset) && count >= CLUSTER_DATA_SIZE &&
|
if (!HasCacheForFile(fd, offset) && count >= CLUSTER_DATA_SIZE &&
|
||||||
offset % CLUSTER_DATA_SIZE == 0)
|
offset % CLUSTER_DATA_SIZE == 0)
|
||||||
{
|
{
|
||||||
ticks += (is_write ? GetClusterWriteTbTicks : GetClusterReadTbTicks)(m_ios.GetVersion());
|
ticks += (is_write ? GetClusterWriteTbTicks : GetClusterReadTbTicks)(m_ios.GetVersion());
|
||||||
copy_length = CLUSTER_DATA_SIZE;
|
copy_length = CLUSTER_DATA_SIZE;
|
||||||
if (is_write)
|
if (is_write)
|
||||||
m_fd_map[request.fd].superblock_flush_needed = true;
|
m_fd_map[fd].superblock_flush_needed = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ticks += SimulatePopulateFileCache(request.fd, offset, status->size);
|
ticks += SimulatePopulateFileCache(fd, offset, status->size);
|
||||||
|
|
||||||
const u32 start = offset - m_cache_chain_index * CLUSTER_DATA_SIZE;
|
const u32 start = offset - m_cache_chain_index * CLUSTER_DATA_SIZE;
|
||||||
copy_length = std::min<u32>(CLUSTER_DATA_SIZE - start, count);
|
copy_length = std::min<u32>(CLUSTER_DATA_SIZE - start, count);
|
||||||
|
@ -276,7 +303,7 @@ u64 FSDevice::EstimateTicksForReadWrite(const Handle& handle, const ReadWriteReq
|
||||||
return ticks;
|
return ticks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FSDevice::HasCacheForFile(u32 fd, u32 offset) const
|
bool FSDevice::HasCacheForFile(u64 fd, u32 offset) const
|
||||||
{
|
{
|
||||||
const u16 chain_index = static_cast<u16>(offset / CLUSTER_DATA_SIZE);
|
const u16 chain_index = static_cast<u16>(offset / CLUSTER_DATA_SIZE);
|
||||||
return m_cache_fd == fd && m_cache_chain_index == chain_index;
|
return m_cache_fd == fd && m_cache_chain_index == chain_index;
|
||||||
|
@ -284,52 +311,81 @@ bool FSDevice::HasCacheForFile(u32 fd, u32 offset) const
|
||||||
|
|
||||||
std::optional<IPCReply> FSDevice::Read(const ReadWriteRequest& request)
|
std::optional<IPCReply> FSDevice::Read(const ReadWriteRequest& request)
|
||||||
{
|
{
|
||||||
const Handle& handle = m_fd_map[request.fd];
|
return MakeIPCReply([&](Ticks t) {
|
||||||
|
return Read(request.fd, Memory::GetPointer(request.buffer), request.size, request.buffer, t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 FSDevice::Read(u64 fd, u8* data, u32 size, std::optional<u32> ipc_buffer_addr, Ticks ticks)
|
||||||
|
{
|
||||||
|
ticks.AddTimeBaseTicks(GetIPCOverheadTicks());
|
||||||
|
|
||||||
|
const Handle& handle = m_fd_map[fd];
|
||||||
if (handle.fs_fd == INVALID_FD)
|
if (handle.fs_fd == INVALID_FD)
|
||||||
return GetFSReply(ConvertResult(ResultCode::Invalid));
|
return ConvertResult(ResultCode::Invalid);
|
||||||
|
|
||||||
// Simulate the FS read logic to estimate ticks. Note: this must be done before reading.
|
// Simulate the FS read logic to estimate ticks. Note: this must be done before reading.
|
||||||
const u64 ticks = EstimateTicksForReadWrite(handle, request);
|
ticks.AddTimeBaseTicks(EstimateTicksForReadWrite(handle, fd, IPC_CMD_READ, size));
|
||||||
|
|
||||||
|
const Result<u32> result = m_ios.GetFS()->ReadBytesFromFile(handle.fs_fd, data, size);
|
||||||
|
if (ipc_buffer_addr)
|
||||||
|
LogResult(result, "Read({}, 0x{:08x}, {})", handle.name.data(), *ipc_buffer_addr, size);
|
||||||
|
|
||||||
const Result<u32> result = m_ios.GetFS()->ReadBytesFromFile(
|
|
||||||
handle.fs_fd, Memory::GetPointer(request.buffer), request.size);
|
|
||||||
LogResult(result, "Read({}, 0x{:08x}, {})", handle.name.data(), request.buffer, request.size);
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return GetFSReply(ConvertResult(result.Error()));
|
return ConvertResult(result.Error());
|
||||||
|
|
||||||
return GetFSReply(*result, ticks);
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IPCReply> FSDevice::Write(const ReadWriteRequest& request)
|
std::optional<IPCReply> FSDevice::Write(const ReadWriteRequest& request)
|
||||||
{
|
{
|
||||||
const Handle& handle = m_fd_map[request.fd];
|
return MakeIPCReply([&](Ticks t) {
|
||||||
|
return Write(request.fd, Memory::GetPointer(request.buffer), request.size, request.buffer, t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
s32 FSDevice::Write(u64 fd, const u8* data, u32 size, std::optional<u32> ipc_buffer_addr,
|
||||||
|
Ticks ticks)
|
||||||
|
{
|
||||||
|
ticks.AddTimeBaseTicks(GetIPCOverheadTicks());
|
||||||
|
|
||||||
|
const Handle& handle = m_fd_map[fd];
|
||||||
if (handle.fs_fd == INVALID_FD)
|
if (handle.fs_fd == INVALID_FD)
|
||||||
return GetFSReply(ConvertResult(ResultCode::Invalid));
|
return ConvertResult(ResultCode::Invalid);
|
||||||
|
|
||||||
// Simulate the FS write logic to estimate ticks. Must be done before writing.
|
// Simulate the FS write logic to estimate ticks. Must be done before writing.
|
||||||
const u64 ticks = EstimateTicksForReadWrite(handle, request);
|
ticks.AddTimeBaseTicks(EstimateTicksForReadWrite(handle, fd, IPC_CMD_WRITE, size));
|
||||||
|
|
||||||
|
const Result<u32> result = m_ios.GetFS()->WriteBytesToFile(handle.fs_fd, data, size);
|
||||||
|
if (ipc_buffer_addr)
|
||||||
|
LogResult(result, "Write({}, 0x{:08x}, {})", handle.name.data(), *ipc_buffer_addr, size);
|
||||||
|
|
||||||
const Result<u32> result = m_ios.GetFS()->WriteBytesToFile(
|
|
||||||
handle.fs_fd, Memory::GetPointer(request.buffer), request.size);
|
|
||||||
LogResult(result, "Write({}, 0x{:08x}, {})", handle.name.data(), request.buffer, request.size);
|
|
||||||
if (!result)
|
if (!result)
|
||||||
return GetFSReply(ConvertResult(result.Error()));
|
return ConvertResult(result.Error());
|
||||||
|
|
||||||
return GetFSReply(*result, ticks);
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<IPCReply> FSDevice::Seek(const SeekRequest& request)
|
std::optional<IPCReply> FSDevice::Seek(const SeekRequest& request)
|
||||||
{
|
{
|
||||||
const Handle& handle = m_fd_map[request.fd];
|
return MakeIPCReply([&](Ticks t) {
|
||||||
if (handle.fs_fd == INVALID_FD)
|
return Seek(request.fd, request.offset, HLE::FS::SeekMode(request.mode), t);
|
||||||
return GetFSReply(ConvertResult(ResultCode::Invalid));
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const Result<u32> result =
|
s32 FSDevice::Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks)
|
||||||
m_ios.GetFS()->SeekFile(handle.fs_fd, request.offset, FS::SeekMode(request.mode));
|
{
|
||||||
LogResult(result, "Seek({}, 0x{:08x}, {})", handle.name.data(), request.offset, request.mode);
|
ticks.AddTimeBaseTicks(GetIPCOverheadTicks());
|
||||||
|
|
||||||
|
const Handle& handle = m_fd_map[fd];
|
||||||
|
if (handle.fs_fd == INVALID_FD)
|
||||||
|
return ConvertResult(ResultCode::Invalid);
|
||||||
|
|
||||||
|
const Result<u32> result = m_ios.GetFS()->SeekFile(handle.fs_fd, offset, mode);
|
||||||
|
LogResult(result, "Seek({}, 0x{:08x}, {})", handle.name.data(), offset, mode);
|
||||||
if (!result)
|
if (!result)
|
||||||
return GetFSReply(ConvertResult(result.Error()));
|
return ConvertResult(result.Error());
|
||||||
return GetFSReply(*result);
|
return *result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Core/IOS/Device.h"
|
#include "Core/IOS/Device.h"
|
||||||
|
@ -24,6 +26,16 @@ class FSDevice : public Device
|
||||||
public:
|
public:
|
||||||
FSDevice(Kernel& ios, const std::string& device_name);
|
FSDevice(Kernel& ios, const std::string& device_name);
|
||||||
|
|
||||||
|
// These are the equivalent of the IPC command handlers so IPC overhead is included
|
||||||
|
// in timing calculations.
|
||||||
|
s64 Open(FS::Uid uid, FS::Gid gid, const std::string& path, FS::Mode mode,
|
||||||
|
std::optional<u32> ipc_fd = {}, Ticks ticks = {});
|
||||||
|
s32 Close(u64 fd, Ticks ticks = {});
|
||||||
|
s32 Read(u64 fd, u8* data, u32 size, std::optional<u32> ipc_buffer_addr = {}, Ticks ticks = {});
|
||||||
|
s32 Write(u64 fd, const u8* data, u32 size, std::optional<u32> ipc_buffer_addr = {},
|
||||||
|
Ticks ticks = {});
|
||||||
|
s32 Seek(u64 fd, u32 offset, FS::SeekMode mode, Ticks ticks = {});
|
||||||
|
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
||||||
std::optional<IPCReply> Open(const OpenRequest& request) override;
|
std::optional<IPCReply> Open(const OpenRequest& request) override;
|
||||||
|
@ -76,14 +88,16 @@ private:
|
||||||
IPCReply GetUsage(const Handle& handle, const IOCtlVRequest& request);
|
IPCReply GetUsage(const Handle& handle, const IOCtlVRequest& request);
|
||||||
IPCReply Shutdown(const Handle& handle, const IOCtlRequest& request);
|
IPCReply Shutdown(const Handle& handle, const IOCtlRequest& request);
|
||||||
|
|
||||||
u64 EstimateTicksForReadWrite(const Handle& handle, const ReadWriteRequest& request);
|
u64 EstimateTicksForReadWrite(const Handle& handle, u64 fd, IPCCommandType command, u32 size);
|
||||||
u64 SimulatePopulateFileCache(u32 fd, u32 offset, u32 file_size);
|
u64 SimulatePopulateFileCache(u64 fd, u32 offset, u32 file_size);
|
||||||
u64 SimulateFlushFileCache();
|
u64 SimulateFlushFileCache();
|
||||||
bool HasCacheForFile(u32 fd, u32 offset) const;
|
bool HasCacheForFile(u64 fd, u32 offset) const;
|
||||||
|
|
||||||
std::map<u32, Handle> m_fd_map;
|
|
||||||
u32 m_cache_fd = INVALID_FD;
|
|
||||||
u16 m_cache_chain_index = 0;
|
|
||||||
bool m_dirty_cache = false;
|
bool m_dirty_cache = false;
|
||||||
|
u16 m_cache_chain_index = 0;
|
||||||
|
std::optional<u64> m_cache_fd;
|
||||||
|
// The first 0x18 IDs are reserved for the PPC.
|
||||||
|
u64 m_next_fd = 0x18;
|
||||||
|
std::map<u64, Handle> m_fd_map;
|
||||||
};
|
};
|
||||||
} // namespace IOS::HLE
|
} // namespace IOS::HLE
|
||||||
|
|
|
@ -45,6 +45,38 @@ struct IPCReply
|
||||||
u64 reply_delay_ticks;
|
u64 reply_delay_ticks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used to make it more convenient for functions to return timing information
|
||||||
|
// without having to explicitly keep track of ticks in callers.
|
||||||
|
class Ticks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ticks(u64* ticks = nullptr) : m_ticks(ticks) {}
|
||||||
|
|
||||||
|
void Add(u64 ticks)
|
||||||
|
{
|
||||||
|
if (m_ticks != nullptr)
|
||||||
|
*m_ticks += ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddTimeBaseTicks(u64 tb_ticks) { Add(tb_ticks * SystemTimers::TIMER_RATIO); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
u64* m_ticks = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ResultProducer>
|
||||||
|
IPCReply MakeIPCReply(u64 ticks, const ResultProducer& fn)
|
||||||
|
{
|
||||||
|
const s32 result_value = fn(Ticks{&ticks});
|
||||||
|
return IPCReply{result_value, ticks};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ResultProducer>
|
||||||
|
IPCReply MakeIPCReply(const ResultProducer& fn)
|
||||||
|
{
|
||||||
|
return MakeIPCReply(0, fn);
|
||||||
|
}
|
||||||
|
|
||||||
enum IPCCommandType : u32
|
enum IPCCommandType : u32
|
||||||
{
|
{
|
||||||
IPC_CMD_OPEN = 1,
|
IPC_CMD_OPEN = 1,
|
||||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
constexpr u32 STATE_VERSION = 128; // Last changed in PR 9366
|
constexpr u32 STATE_VERSION = 129; // Last changed in PR 9511
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
Loading…
Reference in New Issue