2020-04-14 00:07:37 +00:00
|
|
|
/*
|
2021-10-04 19:59:10 +00:00
|
|
|
* Copyright (c) Atmosphère-NX
|
2020-04-14 00:07:37 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <stratosphere.hpp>
|
|
|
|
#include "erpt_srv_allocator.hpp"
|
|
|
|
#include "erpt_srv_stream.hpp"
|
|
|
|
|
|
|
|
namespace ams::erpt::srv {
|
|
|
|
|
2021-04-30 11:21:03 +00:00
|
|
|
constinit bool Stream::s_can_access_fs = true;
|
|
|
|
constinit os::SdkMutex Stream::s_fs_commit_mutex;
|
2020-04-14 00:07:37 +00:00
|
|
|
|
|
|
|
void Stream::EnableFsAccess(bool en) {
|
|
|
|
s_can_access_fs = en;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::DeleteStream(const char *path) {
|
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
|
|
|
return fs::DeleteFile(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::CommitStream() {
|
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
2021-04-30 11:21:03 +00:00
|
|
|
|
|
|
|
std::scoped_lock lk(s_fs_commit_mutex);
|
|
|
|
|
2020-04-14 00:07:37 +00:00
|
|
|
fs::CommitSaveData(ReportStoragePath);
|
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::GetStreamSize(s64 *out, const char *path) {
|
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
|
|
|
|
|
|
|
fs::FileHandle file;
|
|
|
|
R_TRY(fs::OpenFile(std::addressof(file), path, fs::OpenMode_Read));
|
|
|
|
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
|
|
|
|
|
|
|
return fs::GetFileSize(out, file);
|
|
|
|
}
|
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
Stream::Stream() : m_buffer_size(0), m_file_position(0), m_buffer_count(0), m_buffer(nullptr), m_stream_mode(StreamMode_Invalid), m_initialized(false) {
|
2020-04-14 00:07:37 +00:00
|
|
|
/* ... */
|
|
|
|
}
|
|
|
|
|
|
|
|
Stream::~Stream() {
|
|
|
|
this->CloseStream();
|
2021-04-30 11:21:03 +00:00
|
|
|
AMS_ASSERT(!s_fs_commit_mutex.IsLockedByCurrentThread());
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::OpenStream(const char *path, StreamMode mode, u32 buffer_size) {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
|
|
|
R_UNLESS(!m_initialized, erpt::ResultAlreadyInitialized());
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-04-30 11:21:03 +00:00
|
|
|
auto lock_guard = SCOPE_GUARD {
|
|
|
|
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
|
|
|
s_fs_commit_mutex.Unlock();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-14 00:07:37 +00:00
|
|
|
if (mode == StreamMode_Write) {
|
2021-04-30 11:21:03 +00:00
|
|
|
s_fs_commit_mutex.Lock();
|
|
|
|
|
2020-04-14 00:07:37 +00:00
|
|
|
while (true) {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_TRY_CATCH(fs::OpenFile(std::addressof(m_file_handle), path, fs::OpenMode_Write | fs::OpenMode_AllowAppend)) {
|
2020-04-14 00:07:37 +00:00
|
|
|
R_CATCH(fs::ResultPathNotFound) {
|
|
|
|
R_TRY(fs::CreateFile(path, 0));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} R_END_TRY_CATCH;
|
|
|
|
break;
|
|
|
|
}
|
2021-10-10 07:14:06 +00:00
|
|
|
fs::SetFileSize(m_file_handle, 0);
|
2020-04-14 00:07:37 +00:00
|
|
|
} else {
|
|
|
|
R_UNLESS(mode == StreamMode_Read, erpt::ResultInvalidArgument());
|
2021-04-30 11:21:03 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
R_TRY(fs::OpenFile(std::addressof(m_file_handle), path, fs::OpenMode_Read));
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
2021-10-10 07:14:06 +00:00
|
|
|
auto file_guard = SCOPE_GUARD { fs::CloseFile(m_file_handle); };
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
std::strncpy(m_file_name, path, sizeof(m_file_name));
|
|
|
|
m_file_name[sizeof(m_file_name) - 1] = '\x00';
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-04-30 11:21:03 +00:00
|
|
|
if (buffer_size > 0) {
|
2021-10-10 07:14:06 +00:00
|
|
|
m_buffer = reinterpret_cast<u8 *>(Allocate(buffer_size));
|
|
|
|
AMS_ASSERT(m_buffer != nullptr);
|
2021-04-30 11:21:03 +00:00
|
|
|
} else {
|
2021-10-10 07:14:06 +00:00
|
|
|
m_buffer = nullptr;
|
2021-04-30 11:21:03 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
m_buffer_size = m_buffer != nullptr ? buffer_size : 0;
|
|
|
|
m_buffer_count = 0;
|
|
|
|
m_buffer_position = 0;
|
|
|
|
m_file_position = 0;
|
|
|
|
m_stream_mode = mode;
|
|
|
|
m_initialized = true;
|
2020-04-14 00:07:37 +00:00
|
|
|
|
|
|
|
file_guard.Cancel();
|
2021-04-30 11:21:03 +00:00
|
|
|
lock_guard.Cancel();
|
2020-04-14 00:07:37 +00:00
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::ReadStream(u32 *out, u8 *dst, u32 dst_size) {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
|
|
|
R_UNLESS(m_initialized, erpt::ResultNotInitialized());
|
|
|
|
R_UNLESS(m_stream_mode == StreamMode_Read, erpt::ResultNotInitialized());
|
|
|
|
R_UNLESS(out != nullptr, erpt::ResultInvalidArgument());
|
|
|
|
R_UNLESS(dst != nullptr, erpt::ResultInvalidArgument());
|
2020-04-14 00:07:37 +00:00
|
|
|
|
|
|
|
size_t fs_read_size;
|
|
|
|
u32 read_count = 0;
|
|
|
|
|
|
|
|
ON_SCOPE_EXIT {
|
|
|
|
*out = read_count;
|
|
|
|
};
|
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_buffer != nullptr) {
|
2021-04-30 11:21:03 +00:00
|
|
|
while (dst_size > 0) {
|
2021-10-10 07:14:06 +00:00
|
|
|
if (u32 cur = std::min<u32>(m_buffer_count - m_buffer_position, dst_size); cur > 0) {
|
|
|
|
std::memcpy(dst, m_buffer + m_buffer_position, cur);
|
|
|
|
m_buffer_position += cur;
|
|
|
|
dst += cur;
|
|
|
|
dst_size -= cur;
|
|
|
|
read_count += cur;
|
2021-04-30 11:21:03 +00:00
|
|
|
} else {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_TRY(fs::ReadFile(std::addressof(fs_read_size), m_file_handle, m_file_position, m_buffer, m_buffer_size));
|
2021-04-30 11:21:03 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
m_buffer_position = 0;
|
|
|
|
m_file_position += static_cast<u32>(fs_read_size);
|
|
|
|
m_buffer_count = static_cast<u32>(fs_read_size);
|
2021-04-30 11:21:03 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_buffer_count == 0) {
|
2021-04-30 11:21:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-30 11:21:03 +00:00
|
|
|
} else {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_TRY(fs::ReadFile(std::addressof(fs_read_size), m_file_handle, m_file_position, dst, dst_size));
|
2021-04-30 11:21:03 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
m_file_position += static_cast<u32>(fs_read_size);
|
2021-04-30 11:21:03 +00:00
|
|
|
read_count = static_cast<u32>(fs_read_size);
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::WriteStream(const u8 *src, u32 src_size) {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_UNLESS(s_can_access_fs, erpt::ResultInvalidPowerState());
|
|
|
|
R_UNLESS(m_initialized, erpt::ResultNotInitialized());
|
|
|
|
R_UNLESS(m_stream_mode == StreamMode_Write, erpt::ResultNotInitialized());
|
|
|
|
R_UNLESS(src != nullptr || src_size == 0, erpt::ResultInvalidArgument());
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_buffer != nullptr) {
|
2021-04-30 11:21:03 +00:00
|
|
|
while (src_size > 0) {
|
2021-10-10 07:14:06 +00:00
|
|
|
if (u32 cur = std::min<u32>(m_buffer_size - m_buffer_count, src_size); cur > 0) {
|
|
|
|
std::memcpy(m_buffer + m_buffer_count, src, cur);
|
|
|
|
m_buffer_count += cur;
|
|
|
|
src += cur;
|
|
|
|
src_size -= cur;
|
2021-04-30 11:21:03 +00:00
|
|
|
}
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_buffer_count == m_buffer_size) {
|
2021-04-30 11:21:03 +00:00
|
|
|
R_TRY(this->Flush());
|
|
|
|
}
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
2021-04-30 11:21:03 +00:00
|
|
|
} else {
|
2021-10-10 07:14:06 +00:00
|
|
|
R_TRY(fs::WriteFile(m_file_handle, m_file_position, src, src_size, fs::WriteOption::None));
|
|
|
|
m_file_position += src_size;
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Stream::CloseStream() {
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_initialized) {
|
2021-04-30 11:21:03 +00:00
|
|
|
if (s_can_access_fs) {
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_stream_mode == StreamMode_Write) {
|
2021-04-30 11:21:03 +00:00
|
|
|
this->Flush();
|
2021-10-10 07:14:06 +00:00
|
|
|
fs::FlushFile(m_file_handle);
|
2021-04-30 11:21:03 +00:00
|
|
|
}
|
2021-10-10 07:14:06 +00:00
|
|
|
fs::CloseFile(m_file_handle);
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
2021-04-30 11:21:03 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
if (m_buffer != nullptr) {
|
|
|
|
Deallocate(m_buffer);
|
2021-04-30 11:21:03 +00:00
|
|
|
}
|
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
m_initialized = false;
|
2021-04-30 11:21:03 +00:00
|
|
|
|
|
|
|
if (s_fs_commit_mutex.IsLockedByCurrentThread()) {
|
|
|
|
s_fs_commit_mutex.Unlock();
|
|
|
|
}
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-03 19:13:35 +00:00
|
|
|
Result Stream::GetStreamSize(s64 *out) const {
|
2021-10-10 07:14:06 +00:00
|
|
|
return GetStreamSize(out, m_file_name);
|
2020-04-14 00:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result Stream::Flush() {
|
2021-04-30 11:21:03 +00:00
|
|
|
AMS_ASSERT(s_fs_commit_mutex.IsLockedByCurrentThread());
|
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
R_SUCCEED_IF(m_buffer_count == 0);
|
|
|
|
R_TRY(fs::WriteFile(m_file_handle, m_file_position, m_buffer, m_buffer_count, fs::WriteOption::None));
|
2020-04-14 00:07:37 +00:00
|
|
|
|
2021-10-10 07:14:06 +00:00
|
|
|
m_file_position += m_buffer_count;
|
|
|
|
m_buffer_count = 0;
|
2020-04-14 00:07:37 +00:00
|
|
|
return ResultSuccess();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|