mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-27 10:54:30 +01:00
ps3: add iso device
This commit is contained in:
parent
06dcf32e1b
commit
b36d65ed30
|
|
@ -10,6 +10,10 @@ include(CheckFunctionExists)
|
|||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/MT)
|
||||
endif()
|
||||
|
||||
set(ADDITIONAL_LIBS "")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
#on some Linux distros shm_unlink and similar functions are in librt only
|
||||
|
|
@ -77,6 +81,8 @@ if (NOT ANDROID)
|
|||
stb_image.cpp
|
||||
stdafx.cpp
|
||||
|
||||
dev/iso.cpp
|
||||
|
||||
Input/basic_keyboard_handler.cpp
|
||||
Input/basic_mouse_handler.cpp
|
||||
Input/ds3_pad_handler.cpp
|
||||
|
|
|
|||
81
rpcs3/rpcs3/dev/block_dev.hpp
Normal file
81
rpcs3/rpcs3/dev/block_dev.hpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/File.h"
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
class block_dev {
|
||||
std::size_t m_block_size = 0;
|
||||
std::size_t m_block_count = 0;
|
||||
|
||||
public:
|
||||
virtual ~block_dev() = default;
|
||||
|
||||
std::size_t block_size() const { return m_block_size; }
|
||||
std::size_t block_count() const { return m_block_count; }
|
||||
std::size_t size() const { return block_size() * block_count(); }
|
||||
|
||||
virtual std::size_t read(std::size_t blockIndex, void *data,
|
||||
std::size_t blockCount) = 0;
|
||||
|
||||
virtual std::size_t write(std::size_t blockIndex, const void *data,
|
||||
std::size_t blockCount) = 0;
|
||||
|
||||
protected:
|
||||
void set_block_info(std::size_t size, std::size_t count) {
|
||||
m_block_size = size;
|
||||
m_block_count = count;
|
||||
}
|
||||
};
|
||||
|
||||
class file_block_dev final : public block_dev {
|
||||
fs::file m_file;
|
||||
|
||||
public:
|
||||
explicit file_block_dev(fs::file file, std::size_t blockSize = 2048)
|
||||
: m_file(std::move(file)) {
|
||||
set_block_info(blockSize, m_file.size() / blockSize);
|
||||
}
|
||||
|
||||
std::size_t read(std::size_t blockIndex, void *data,
|
||||
std::size_t blockCount) override {
|
||||
auto result = m_file.read_at(block_size() * blockIndex, data,
|
||||
blockCount * block_size());
|
||||
return result / block_size();
|
||||
}
|
||||
|
||||
std::size_t write(std::size_t blockIndex, const void *data,
|
||||
std::size_t blockCount) override {
|
||||
auto result = m_file.write_at(block_size() * blockIndex, data,
|
||||
blockCount * block_size());
|
||||
return result / block_size();
|
||||
}
|
||||
|
||||
fs::file &file() { return m_file; }
|
||||
fs::file release() { return std::exchange(m_file, {}); }
|
||||
};
|
||||
|
||||
class file_view_block_dev final : public block_dev {
|
||||
const fs::file *m_file;
|
||||
|
||||
public:
|
||||
explicit file_view_block_dev(const fs::file &file,
|
||||
std::size_t blockSize = 2048)
|
||||
: m_file(&file) {
|
||||
set_block_info(blockSize, m_file->size() / blockSize);
|
||||
}
|
||||
|
||||
std::size_t read(std::size_t blockIndex, void *data,
|
||||
std::size_t blockCount) override {
|
||||
auto result = m_file->read_at(block_size() * blockIndex, data,
|
||||
blockCount * block_size());
|
||||
return result / block_size();
|
||||
}
|
||||
|
||||
std::size_t write(std::size_t blockIndex, const void *data,
|
||||
std::size_t blockCount) override {
|
||||
auto result = m_file->write_at(block_size() * blockIndex, data,
|
||||
blockCount * block_size());
|
||||
return result / block_size();
|
||||
}
|
||||
};
|
||||
397
rpcs3/rpcs3/dev/iso.cpp
Normal file
397
rpcs3/rpcs3/dev/iso.cpp
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
|
||||
#include "iso.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include "util/types.hpp"
|
||||
#include <bit>
|
||||
#include <cctype>
|
||||
#include <codecvt>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
static std::string u16_ne_to_string(const char16_t* bytes, std::size_t count)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
return convert.to_bytes(bytes, bytes + count);
|
||||
}
|
||||
|
||||
static std::string u16_se_to_string(const char16_t* bytes, std::size_t count)
|
||||
{
|
||||
auto seBytes = reinterpret_cast<const se_t<char16_t, true, alignof(char16_t)>*>(bytes);
|
||||
|
||||
std::vector<char16_t> neBytes(count);
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
neBytes[i] = seBytes[i];
|
||||
}
|
||||
|
||||
return u16_ne_to_string(neBytes.data(), neBytes.size());
|
||||
}
|
||||
|
||||
static std::string u16_be_to_string(const char16_t* bytes, std::size_t count)
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::big)
|
||||
{
|
||||
return u16_ne_to_string(bytes, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
return u16_se_to_string(bytes, count);
|
||||
}
|
||||
}
|
||||
|
||||
static std::string decodeString(std::string_view data,
|
||||
iso::StringEncoding encoding)
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case iso::StringEncoding::ascii:
|
||||
break;
|
||||
|
||||
case iso::StringEncoding::utf16_be:
|
||||
return u16_be_to_string(reinterpret_cast<const char16_t*>(data.data()), data.size() / 2);
|
||||
}
|
||||
|
||||
return std::string(data);
|
||||
}
|
||||
|
||||
bool iso_dev::initialize()
|
||||
{
|
||||
constexpr std::size_t primaryVolumeDescOffset = 16;
|
||||
ensure(m_dev->block_size() >= sizeof(iso::VolumeHeader));
|
||||
std::vector<std::byte> block(m_dev->block_size());
|
||||
|
||||
std::optional<iso::PrimaryVolumeDescriptor> primaryVolume;
|
||||
std::optional<iso::PrimaryVolumeDescriptor> supplementaryVolume;
|
||||
|
||||
for (std::size_t i = 0; i < 256; ++i)
|
||||
{
|
||||
if (m_dev->read(primaryVolumeDescOffset + i, block.data(), 1) != 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
auto header = reinterpret_cast<iso::VolumeHeader*>(block.data());
|
||||
|
||||
if (header->type == 255)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::memcmp(header->standard_id, "CD001", 5) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header->type == 1)
|
||||
{
|
||||
primaryVolume =
|
||||
*reinterpret_cast<iso::PrimaryVolumeDescriptor*>(block.data());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header->type == 2)
|
||||
{
|
||||
std::printf("found supplementary volume\n");
|
||||
supplementaryVolume =
|
||||
*reinterpret_cast<iso::PrimaryVolumeDescriptor*>(block.data());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!primaryVolume)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& pvd = supplementaryVolume ? *supplementaryVolume : *primaryVolume;
|
||||
m_encoding = supplementaryVolume ? iso::StringEncoding::utf16_be : iso::StringEncoding::ascii;
|
||||
m_root_dir = pvd.root;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool iso_dev::stat(const std::string& path, fs::stat_t& info)
|
||||
{
|
||||
auto optEntry = open_entry(path);
|
||||
if (!optEntry)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
info = optEntry->to_fs_stat();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool iso_dev::statfs(const std::string& path, fs::device_stat& info)
|
||||
{
|
||||
auto optEntry = open_entry(path);
|
||||
if (!optEntry)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
info = {
|
||||
.block_size = m_dev->block_size(),
|
||||
.total_size = m_dev->size(),
|
||||
.total_free = 0,
|
||||
.avail_free = 0,
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<fs::file_base> iso_dev::open(const std::string& path, bs_t<fs::open_mode> mode)
|
||||
{
|
||||
if (mode != fs::open_mode::read)
|
||||
{
|
||||
fs::g_tls_error = fs::error::acces;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto optEntry = open_entry(path);
|
||||
if (!optEntry)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return {};
|
||||
}
|
||||
|
||||
if ((optEntry->flags & iso::DirEntryFlags::Directory) == iso::DirEntryFlags::Directory)
|
||||
{
|
||||
fs::g_tls_error = fs::error::isdir;
|
||||
return {};
|
||||
}
|
||||
|
||||
return read_file(*optEntry).release();
|
||||
}
|
||||
|
||||
std::unique_ptr<fs::dir_base> iso_dev::open_dir(const std::string& path)
|
||||
{
|
||||
auto optEntry = open_entry(path);
|
||||
if (!optEntry)
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return {};
|
||||
}
|
||||
|
||||
if ((optEntry->flags & iso::DirEntryFlags::Directory) != iso::DirEntryFlags::Directory)
|
||||
{
|
||||
fs::g_tls_error = fs::error::exist;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto items = read_dir(*optEntry);
|
||||
std::vector<fs::dir_entry> result_items(items.first.size());
|
||||
|
||||
for (std::size_t i = 0; i < result_items.size(); ++i)
|
||||
{
|
||||
result_items[i] = items.first[i].to_fs_entry(items.second[i]);
|
||||
}
|
||||
|
||||
return std::make_unique<fs::virtual_dir>(std::move(result_items));
|
||||
}
|
||||
|
||||
std::optional<iso::DirEntry>
|
||||
iso_dev::open_entry(const std::filesystem::path& path)
|
||||
{
|
||||
auto pathString = std::filesystem::weakly_canonical(path).string();
|
||||
|
||||
if (pathString == "/" || pathString == "\\" || pathString.empty())
|
||||
{
|
||||
return m_root_dir;
|
||||
}
|
||||
|
||||
auto item = m_root_dir;
|
||||
auto pathView = std::string_view(pathString);
|
||||
|
||||
auto isStringEqNoCase = [](std::string_view lhs, std::string_view rhs)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < lhs.size(); ++i)
|
||||
{
|
||||
if (std::tolower(lhs[i]) != std::tolower(rhs[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
while (!pathView.empty())
|
||||
{
|
||||
auto sepPos = pathView.find_first_of("/\\");
|
||||
if (sepPos == 0)
|
||||
{
|
||||
pathView.remove_prefix(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((item.flags & iso::DirEntryFlags::Directory) !=
|
||||
iso::DirEntryFlags::Directory)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto dirName = pathView.substr(0, sepPos);
|
||||
if (sepPos == std::string_view::npos)
|
||||
{
|
||||
pathView = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
pathView.remove_prefix(sepPos);
|
||||
}
|
||||
|
||||
auto items = read_dir(item);
|
||||
|
||||
bool found = false;
|
||||
for (std::size_t i = 0; i < items.first.size(); ++i)
|
||||
{
|
||||
if (!isStringEqNoCase(items.second[i], dirName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
item = items.first[i];
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
std::pair<std::vector<iso::DirEntry>, std::vector<std::string>>
|
||||
iso_dev::read_dir(const iso::DirEntry& entry)
|
||||
{
|
||||
if ((entry.flags & iso::DirEntryFlags::Directory) !=
|
||||
iso::DirEntryFlags::Directory)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto block_size = m_dev->block_size();
|
||||
auto total_block_count = (entry.length.value() + block_size - 1) / block_size;
|
||||
auto total_buffer_block_count = std::min<std::size_t>(total_block_count, 10);
|
||||
|
||||
std::vector<std::byte> buffer(total_buffer_block_count * block_size);
|
||||
auto first_block = entry.lba.value();
|
||||
|
||||
std::vector<iso::DirEntry> isoEntries;
|
||||
std::vector<std::string> names;
|
||||
|
||||
for (std::size_t block = first_block, end = first_block + total_block_count;
|
||||
block < end;)
|
||||
{
|
||||
auto block_count =
|
||||
m_dev->read(block, buffer.data(), total_buffer_block_count);
|
||||
block += block_count;
|
||||
|
||||
std::size_t buffer_offset = 0;
|
||||
std::size_t buffer_size = block_count * block_size;
|
||||
|
||||
while (buffer_offset < buffer_size)
|
||||
{
|
||||
auto item = reinterpret_cast<const iso::DirEntry*>(buffer.data() +
|
||||
buffer_offset);
|
||||
if (item->entry_length == 0)
|
||||
{
|
||||
buffer_offset += block_size;
|
||||
buffer_offset &= ~(block_size - 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto filename_end =
|
||||
sizeof(iso::DirEntry) +
|
||||
((item->filename_length + 1) & ~static_cast<std::size_t>(1));
|
||||
|
||||
if (item->entry_length < filename_end)
|
||||
{
|
||||
buffer_offset += item->entry_length;
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer_offset += item->entry_length;
|
||||
|
||||
if (item->filename_length == 0 ||
|
||||
item->filename_length + sizeof(iso::DirEntry) > item->entry_length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto filename =
|
||||
item->filename_length > 1 ? decodeString({reinterpret_cast<const char*>(item + 1),
|
||||
item->filename_length},
|
||||
m_encoding) :
|
||||
std::string{};
|
||||
|
||||
if (item->filename_length == 1)
|
||||
{
|
||||
char c = *reinterpret_cast<const char*>(item + 1);
|
||||
// can be special name
|
||||
if (c == 0)
|
||||
{
|
||||
filename = ".";
|
||||
}
|
||||
else if (c == 1)
|
||||
{
|
||||
filename = "..";
|
||||
}
|
||||
}
|
||||
|
||||
filename = filename.substr(0, filename.find(';'));
|
||||
if (filename.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
isoEntries.push_back(*item);
|
||||
names.emplace_back(filename);
|
||||
}
|
||||
}
|
||||
|
||||
return {std::move(isoEntries), std::move(names)};
|
||||
}
|
||||
|
||||
fs::file iso_dev::read_file(const iso::DirEntry& entry)
|
||||
{
|
||||
if ((entry.flags & iso::DirEntryFlags::Directory) ==
|
||||
iso::DirEntryFlags::Directory)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (entry.length.value() == 0)
|
||||
{
|
||||
return fs::make_stream<std::vector<std::uint8_t>>();
|
||||
}
|
||||
|
||||
auto block_size = m_dev->block_size();
|
||||
auto block_count = (entry.length.value() + block_size - 1) / block_size;
|
||||
|
||||
std::vector<std::uint8_t> data;
|
||||
data.resize(block_count * block_size);
|
||||
if (!m_dev->read(entry.lba.value(), data.data(), block_count))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
data.resize(entry.length.value());
|
||||
return fs::make_stream(std::move(data), entry.to_fs_stat());
|
||||
}
|
||||
233
rpcs3/rpcs3/dev/iso.hpp
Normal file
233
rpcs3/rpcs3/dev/iso.hpp
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
#pragma once
|
||||
|
||||
#include "Utilities/File.h"
|
||||
#include "block_dev.hpp"
|
||||
#include "util/endian.hpp"
|
||||
#include "util/types.hpp"
|
||||
#include <bit>
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
namespace iso
|
||||
{
|
||||
#pragma pack(push, 1)
|
||||
template <typename T>
|
||||
struct le_be_pair
|
||||
{
|
||||
le_t<T> le;
|
||||
be_t<T> be;
|
||||
|
||||
T value() const
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::little)
|
||||
{
|
||||
return le;
|
||||
}
|
||||
else
|
||||
{
|
||||
return be;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PrimaryVolumeDescriptorDateTime
|
||||
{
|
||||
char year[4];
|
||||
char month[2];
|
||||
char day[2];
|
||||
char hour[2];
|
||||
char minute[2];
|
||||
char second[2];
|
||||
char milliseconds[2];
|
||||
s8 gmt_offset;
|
||||
};
|
||||
|
||||
struct VolumeHeader
|
||||
{
|
||||
u8 type;
|
||||
char standard_id[5];
|
||||
u8 version;
|
||||
};
|
||||
|
||||
struct DirDateTime
|
||||
{
|
||||
u8 year; // + 1900
|
||||
u8 month;
|
||||
u8 day;
|
||||
u8 hour;
|
||||
u8 minute;
|
||||
u8 second;
|
||||
s8 gmt_offset;
|
||||
|
||||
struct tm to_tm() const
|
||||
{
|
||||
struct tm time{};
|
||||
time.tm_year = year;
|
||||
time.tm_mon = month;
|
||||
time.tm_mday = day;
|
||||
time.tm_hour = hour;
|
||||
time.tm_min = minute;
|
||||
time.tm_sec = second;
|
||||
time.tm_gmtoff = gmt_offset;
|
||||
return time;
|
||||
}
|
||||
|
||||
time_t to_time_t() const
|
||||
{
|
||||
auto tm = to_tm();
|
||||
return mktime(&tm);
|
||||
}
|
||||
};
|
||||
|
||||
enum class DirEntryFlags : u8
|
||||
{
|
||||
None = 0,
|
||||
Hidden = 1 << 0,
|
||||
Directory = 1 << 1,
|
||||
File = 1 << 2,
|
||||
ExtAttr = 1 << 3,
|
||||
Permissions = 1 << 4,
|
||||
};
|
||||
|
||||
constexpr DirEntryFlags operator&(DirEntryFlags lhs, DirEntryFlags rhs)
|
||||
{
|
||||
return static_cast<DirEntryFlags>(static_cast<unsigned>(lhs) &
|
||||
static_cast<unsigned>(rhs));
|
||||
}
|
||||
constexpr DirEntryFlags operator|(DirEntryFlags lhs, DirEntryFlags rhs)
|
||||
{
|
||||
return static_cast<DirEntryFlags>(static_cast<unsigned>(lhs) |
|
||||
static_cast<unsigned>(rhs));
|
||||
}
|
||||
|
||||
struct DirEntry
|
||||
{
|
||||
u8 entry_length;
|
||||
u8 ext_attr_length;
|
||||
le_be_pair<u32> lba;
|
||||
le_be_pair<u32> length;
|
||||
DirDateTime create_time;
|
||||
DirEntryFlags flags;
|
||||
u8 interleave_unit_size;
|
||||
u8 interleave_gap_size;
|
||||
le_be_pair<u16> sequence;
|
||||
u8 filename_length;
|
||||
|
||||
fs::stat_t to_fs_stat() const
|
||||
{
|
||||
fs::stat_t result{};
|
||||
result.is_directory =
|
||||
(flags & iso::DirEntryFlags::Directory) != iso::DirEntryFlags::None;
|
||||
result.size = length.value();
|
||||
result.ctime = create_time.to_time_t();
|
||||
result.mtime = result.ctime;
|
||||
result.atime = result.ctime;
|
||||
return result;
|
||||
}
|
||||
fs::dir_entry to_fs_entry(std::string name) const
|
||||
{
|
||||
fs::dir_entry entry = {};
|
||||
static_cast<fs::stat_t&>(entry) = to_fs_stat();
|
||||
entry.name = std::move(name);
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
|
||||
struct PrimaryVolumeDescriptor
|
||||
{
|
||||
VolumeHeader header;
|
||||
uint8_t pad0;
|
||||
char system_id[32];
|
||||
char volume_id[32];
|
||||
char pad1[8];
|
||||
le_be_pair<u32> block_count;
|
||||
char pad2[32];
|
||||
le_be_pair<u16> volume_set_size;
|
||||
le_be_pair<u16> vol_seq_num;
|
||||
le_be_pair<u16> block_size;
|
||||
le_be_pair<u32> path_table_size;
|
||||
le_t<u32> path_table_block_le;
|
||||
le_t<u32> ext_path_table_block_le;
|
||||
be_t<u32> path_table_block_be;
|
||||
be_t<u32> ext_path_table_block_be;
|
||||
DirEntry root;
|
||||
u8 pad3;
|
||||
uint8_t volume_set_id[128];
|
||||
uint8_t publisher_id[128];
|
||||
uint8_t data_preparer_id[128];
|
||||
uint8_t application_id[128];
|
||||
uint8_t copyright_file_id[37];
|
||||
uint8_t abstract_file_id[37];
|
||||
uint8_t bibliographical_file_id[37];
|
||||
PrimaryVolumeDescriptorDateTime vol_creation_time;
|
||||
PrimaryVolumeDescriptorDateTime vol_modification_time;
|
||||
PrimaryVolumeDescriptorDateTime vol_expire_time;
|
||||
PrimaryVolumeDescriptorDateTime vol_effective_time;
|
||||
uint8_t version;
|
||||
uint8_t pad4;
|
||||
uint8_t app_used[512];
|
||||
|
||||
u32 path_table_block() const
|
||||
{
|
||||
if constexpr (std::endian::native == std::endian::little)
|
||||
{
|
||||
return path_table_block_le;
|
||||
}
|
||||
else
|
||||
{
|
||||
return path_table_block_be;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PathTableEntryHeader
|
||||
{
|
||||
u8 name_length;
|
||||
u8 ext_attr_length;
|
||||
le_t<u32> location;
|
||||
le_t<u16> parent_id;
|
||||
};
|
||||
|
||||
enum class StringEncoding
|
||||
{
|
||||
ascii,
|
||||
utf16_be,
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
} // namespace iso
|
||||
|
||||
class iso_dev final : public fs::device_base
|
||||
{
|
||||
std::unique_ptr<block_dev> m_dev;
|
||||
iso::DirEntry m_root_dir;
|
||||
iso::StringEncoding m_encoding = iso::StringEncoding::ascii;
|
||||
|
||||
public:
|
||||
iso_dev() = default;
|
||||
|
||||
static std::optional<iso_dev> open(std::unique_ptr<block_dev> device)
|
||||
{
|
||||
iso_dev result;
|
||||
result.m_dev = std::move(device);
|
||||
|
||||
if (!result.initialize())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool stat(const std::string& path, fs::stat_t& info) override;
|
||||
bool statfs(const std::string& path, fs::device_stat& info) override;
|
||||
std::unique_ptr<fs::file_base> open(const std::string& path, bs_t<fs::open_mode> mode) override;
|
||||
std::unique_ptr<fs::dir_base> open_dir(const std::string& path) override;
|
||||
|
||||
private:
|
||||
bool initialize();
|
||||
|
||||
std::optional<iso::DirEntry> open_entry(const std::filesystem::path& path);
|
||||
std::pair<std::vector<iso::DirEntry>, std::vector<std::string>> read_dir(const iso::DirEntry& entry);
|
||||
fs::file read_file(const iso::DirEntry& entry);
|
||||
};
|
||||
Loading…
Reference in a new issue