metaforce/Runtime/CDvdFile.cpp

306 lines
7.4 KiB
C++
Raw Normal View History

#include "Runtime/CDvdFile.hpp"
2021-06-28 15:10:54 -07:00
#include <optick.h>
#include "Runtime/CDvdRequest.hpp"
#include "Runtime/CStopwatch.hpp"
2016-03-06 19:12:32 -08:00
2021-04-10 01:42:06 -07:00
namespace metaforce {
2016-03-06 19:12:32 -08:00
2022-01-31 16:06:54 -08:00
std::unique_ptr<nod::DiscBase> CDvdFile::m_DvdRoot;
// std::unordered_map<std::string, std::string> CDvdFile::m_caseInsensitiveMap;
2016-03-06 19:12:32 -08:00
2018-12-07 21:30:43 -08:00
class CFileDvdRequest : public IDvdRequest {
2022-01-31 16:06:54 -08:00
std::shared_ptr<nod::IPartReadStream> m_reader;
uint64_t m_begin;
uint64_t m_size;
2018-12-07 21:30:43 -08:00
void* m_buf;
u32 m_len;
ESeekOrigin m_whence;
int m_offset;
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
std::atomic_bool m_cancel = {false};
std::atomic_bool m_complete = {false};
#else
bool m_cancel = false;
bool m_complete = false;
#endif
2018-12-07 21:30:43 -08:00
std::function<void(u32)> m_callback;
2016-03-06 19:12:32 -08:00
public:
2020-04-15 06:42:44 -07:00
~CFileDvdRequest() override { CFileDvdRequest::PostCancelRequest(); }
2016-03-06 19:12:32 -08:00
void WaitUntilComplete() override {
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
while (!m_complete.load() && !m_cancel.load()) {
std::unique_lock lk{CDvdFile::m_WaitMutex};
2016-03-06 19:12:32 -08:00
}
#else
if (!m_complete && !m_cancel) {
CDvdFile::DoWork();
}
#endif
}
bool IsComplete() override {
#ifdef HAS_DVD_THREAD
return m_complete.load();
#else
if (!m_complete) {
CDvdFile::DoWork();
}
return m_complete;
#endif
2018-12-07 21:30:43 -08:00
}
void PostCancelRequest() override {
#ifdef HAS_DVD_THREAD
if (m_complete.load() || m_cancel.load()) {
return;
}
std::unique_lock waitlk{CDvdFile::m_WaitMutex};
2018-12-07 21:30:43 -08:00
m_cancel.store(true);
#else
m_cancel = true;
#endif
2018-12-07 21:30:43 -08:00
}
2016-03-06 19:12:32 -08:00
2020-04-15 06:42:44 -07:00
[[nodiscard]] EMediaType GetMediaType() const override { return EMediaType::File; }
2016-03-06 19:12:32 -08:00
2018-12-07 21:30:43 -08:00
CFileDvdRequest(CDvdFile& file, void* buf, u32 len, ESeekOrigin whence, int off, std::function<void(u32)>&& cb)
2022-01-31 16:06:54 -08:00
: m_reader(file.m_reader)
, m_begin(file.m_begin)
, m_size(file.m_size)
, m_buf(buf)
, m_len(len)
, m_whence(whence)
, m_offset(off)
, m_callback(std::move(cb)) {}
2016-03-06 19:12:32 -08:00
2018-12-07 21:30:43 -08:00
void DoRequest() {
#ifdef HAS_DVD_THREAD
2020-04-15 06:42:44 -07:00
if (m_cancel.load()) {
2018-12-07 21:30:43 -08:00
return;
2020-04-15 06:42:44 -07:00
}
#else
if (m_cancel) {
return;
}
#endif
2022-01-31 16:06:54 -08:00
u32 readLen = 0;
2018-12-07 21:30:43 -08:00
if (m_whence == ESeekOrigin::Cur && m_offset == 0) {
2022-01-31 16:06:54 -08:00
readLen = m_reader->read(m_buf, m_len);
2018-12-07 21:30:43 -08:00
} else {
2022-01-31 16:06:54 -08:00
int seek = 0;
int64_t offset = m_offset;
switch (m_whence) {
case ESeekOrigin::Begin: {
seek = SEEK_SET;
offset += int64_t(m_begin);
break;
}
case ESeekOrigin::End: {
seek = SEEK_SET;
offset += int64_t(m_begin) + int64_t(m_size);
break;
}
case ESeekOrigin::Cur: {
seek = SEEK_CUR;
break;
}
};
m_reader->seek(offset, seek);
readLen = m_reader->read(m_buf, m_len);
2016-03-06 19:12:32 -08:00
}
2020-04-15 06:42:44 -07:00
if (m_callback) {
2018-12-07 21:30:43 -08:00
m_callback(readLen);
2020-04-15 06:42:44 -07:00
}
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
m_complete.store(true);
#else
m_complete = true;
#endif
2018-12-07 21:30:43 -08:00
}
2016-03-06 19:12:32 -08:00
};
#ifdef HAS_DVD_THREAD
2016-03-06 19:12:32 -08:00
std::thread CDvdFile::m_WorkerThread;
std::mutex CDvdFile::m_WorkerMutex;
std::condition_variable CDvdFile::m_WorkerCV;
std::mutex CDvdFile::m_WaitMutex;
2018-06-01 17:03:31 -07:00
std::atomic_bool CDvdFile::m_WorkerRun = {false};
#endif
2016-03-06 19:12:32 -08:00
std::vector<std::shared_ptr<IDvdRequest>> CDvdFile::m_RequestQueue;
std::string CDvdFile::m_rootDirectory;
std::unique_ptr<u8[]> CDvdFile::m_dolBuf;
2022-01-31 16:06:54 -08:00
CDvdFile::CDvdFile(std::string_view path) : x18_path(path) {
auto* node = ResolvePath(path);
if (node != nullptr && node->getKind() == nod::Node::Kind::File) {
m_reader = node->beginReadStream();
m_begin = m_reader->position();
m_size = node->size();
}
}
// single-threaded hack
void CDvdFile::DoWork() {
for (std::shared_ptr<IDvdRequest>& req : m_RequestQueue) {
auto& concreteReq = static_cast<CFileDvdRequest&>(*req);
concreteReq.DoRequest();
}
m_RequestQueue.clear();
}
2018-12-07 21:30:43 -08:00
void CDvdFile::WorkerProc() {
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
logvisor::RegisterThreadName("CDvdFile");
2021-06-28 15:10:54 -07:00
OPTICK_THREAD("CDvdFile");
2018-12-07 21:30:43 -08:00
while (m_WorkerRun.load()) {
std::unique_lock lk{m_WorkerMutex};
while (!m_RequestQueue.empty()) {
2018-12-07 21:30:43 -08:00
std::vector<std::shared_ptr<IDvdRequest>> swapQueue;
swapQueue.swap(m_RequestQueue);
2018-12-07 21:30:43 -08:00
lk.unlock();
std::unique_lock waitlk{m_WaitMutex};
2018-12-07 21:30:43 -08:00
for (std::shared_ptr<IDvdRequest>& req : swapQueue) {
2020-04-15 06:42:44 -07:00
auto& concreteReq = static_cast<CFileDvdRequest&>(*req);
2018-12-07 21:30:43 -08:00
concreteReq.DoRequest();
}
waitlk.unlock();
swapQueue.clear();
lk.lock();
2016-03-06 19:12:32 -08:00
}
2020-04-15 06:42:44 -07:00
if (!m_WorkerRun.load()) {
2018-12-07 21:30:43 -08:00
break;
2020-04-15 06:42:44 -07:00
}
2018-12-07 21:30:43 -08:00
m_WorkerCV.wait(lk);
}
#endif
2016-03-06 19:12:32 -08:00
}
2018-12-07 21:30:43 -08:00
std::shared_ptr<IDvdRequest> CDvdFile::AsyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int off,
std::function<void(u32)>&& cb) {
std::shared_ptr<IDvdRequest> ret = std::make_shared<CFileDvdRequest>(*this, buf, len, whence, off, std::move(cb));
#ifdef HAS_DVD_THREAD
std::unique_lock lk{m_WorkerMutex};
#endif
2018-12-07 21:30:43 -08:00
m_RequestQueue.emplace_back(ret);
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
lk.unlock();
m_WorkerCV.notify_one();
#endif
2018-12-07 21:30:43 -08:00
return ret;
2016-03-06 19:12:32 -08:00
}
2022-01-31 16:06:54 -08:00
u32 CDvdFile::SyncSeekRead(void* buf, u32 len, ESeekOrigin whence, int offset) {
int seek = 0;
switch (whence) {
case ESeekOrigin::Begin: {
seek = SEEK_SET;
offset += int64_t(m_begin);
break;
2020-04-15 06:42:44 -07:00
}
2022-01-31 16:06:54 -08:00
case ESeekOrigin::End: {
seek = SEEK_SET;
offset += int64_t(m_begin) + int64_t(m_size);
break;
2020-04-15 06:42:44 -07:00
}
2022-01-31 16:06:54 -08:00
case ESeekOrigin::Cur: {
seek = SEEK_CUR;
break;
}
};
m_reader->seek(offset, seek);
return m_reader->read(buf, len);
2019-03-09 00:58:27 -08:00
}
2022-01-31 16:06:54 -08:00
nod::Node* CDvdFile::ResolvePath(std::string_view path) {
if (!m_DvdRoot) {
return nullptr;
}
if (path.starts_with('/')) {
path.remove_prefix(1);
}
std::string prefixedPath;
if (!m_rootDirectory.empty()) {
prefixedPath = m_rootDirectory;
prefixedPath += '/';
prefixedPath += path;
path = prefixedPath;
}
2022-01-31 16:06:54 -08:00
auto* node = &m_DvdRoot->getDataPartition()->getFSTRoot();
while (node != nullptr && !path.empty()) {
std::string component;
auto end = path.find('/');
if (end != std::string_view::npos) {
component = path.substr(0, end);
path.remove_prefix(component.size() + 1);
2019-03-09 00:58:27 -08:00
} else {
2022-01-31 16:06:54 -08:00
component = path;
path.remove_prefix(component.size());
}
std::transform(component.begin(), component.end(), component.begin(), ::tolower);
auto* tmpNode = node;
node = nullptr;
for (auto& item : *tmpNode) {
const auto name = item.getName();
if (std::equal(component.begin(), component.end(), name.begin(), name.end(),
[](char a, char b) { return a == tolower(b); })) {
node = &item;
break;
}
2019-03-09 00:58:27 -08:00
}
}
2022-01-31 16:06:54 -08:00
return node;
2019-03-09 00:58:27 -08:00
}
2022-01-31 16:06:54 -08:00
bool CDvdFile::Initialize(const std::string_view& path) {
#ifdef HAS_DVD_THREAD
2020-04-15 06:42:44 -07:00
if (m_WorkerRun.load()) {
2022-01-31 16:06:54 -08:00
return true;
}
#endif
2022-01-31 16:06:54 -08:00
m_DvdRoot = nod::OpenDiscFromImage(path);
if (!m_DvdRoot) {
return false;
2020-04-15 06:42:44 -07:00
}
m_dolBuf = m_DvdRoot->getDataPartition()->getDOLBuf();
#ifdef HAS_DVD_THREAD
2018-12-07 21:30:43 -08:00
m_WorkerRun.store(true);
m_WorkerThread = std::thread(WorkerProc);
#endif
2022-01-31 16:06:54 -08:00
return true;
2016-03-06 19:12:32 -08:00
}
2018-12-07 21:30:43 -08:00
void CDvdFile::Shutdown() {
#ifdef HAS_DVD_THREAD
2020-04-15 06:42:44 -07:00
if (!m_WorkerRun.load()) {
2018-12-07 21:30:43 -08:00
return;
2020-04-15 06:42:44 -07:00
}
2018-12-07 21:30:43 -08:00
m_WorkerRun.store(false);
m_WorkerCV.notify_one();
2020-04-15 06:42:44 -07:00
if (m_WorkerThread.joinable()) {
2018-12-07 21:30:43 -08:00
m_WorkerThread.join();
2020-04-15 06:42:44 -07:00
}
#endif
2018-12-07 21:30:43 -08:00
m_RequestQueue.clear();
2016-03-06 19:12:32 -08:00
}
SDiscInfo CDvdFile::DiscInfo() {
SDiscInfo out{};
if (!m_DvdRoot) {
return out;
}
const auto& header = m_DvdRoot->getHeader();
std::memcpy(out.gameId.data(), header.m_gameID, sizeof(header.m_gameID));
out.version = header.m_discVersion;
out.gameTitle = header.m_gameTitle;
return out;
}
void CDvdFile::SetRootDirectory(const std::string_view& rootDir) { m_rootDirectory = rootDir; }
2021-04-10 01:42:06 -07:00
} // namespace metaforce