Update Logger to use NDK Logger APIs + Improve Backing API + Fix FDSAN issues

This commit is contained in:
◱ PixelyIon 2020-09-29 18:16:17 +05:30 committed by ◱ PixelyIon
parent 4070686897
commit 97ac45d83b
26 changed files with 116 additions and 140 deletions

View File

@ -4,6 +4,7 @@
#include <csignal>
#include <unistd.h>
#include <sys/resource.h>
#include <android/log.h>
#include "skyline/loader/loader.h"
#include "skyline/common.h"
#include "skyline/os.h"
@ -12,24 +13,18 @@
bool Halt;
jobject Surface;
uint FaultCount;
skyline::GroupMutex JniMtx;
skyline::u16 fps;
skyline::u32 frametime;
std::weak_ptr<skyline::input::Input> inputWeak;
void signalHandler(int signal) {
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
if (FaultCount > 2)
exit(SIGKILL);
else
Halt = true;
FaultCount++;
__android_log_print(ANDROID_LOG_FATAL, "emu-cpp", "Halting program due to signal: %s", strsignal(signal));
exit(signal);
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) {
Halt = false;
FaultCount = 0;
fps = 0;
frametime = 0;
@ -44,6 +39,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};
auto settings{std::make_shared<skyline::Settings>(preferenceFd)};
close(preferenceFd);
auto appFilesPath{env->GetStringUTFChars(appFilesPathJstring, nullptr)};
auto logger{std::make_shared<skyline::Logger>(std::string(appFilesPath) + "skyline.log", static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))))};
@ -74,6 +70,8 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto end{std::chrono::steady_clock::now()};
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
close(romFd);
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setHalt(JNIEnv *, jobject, jboolean halt) {

View File

@ -2,6 +2,7 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <tinyxml2.h>
#include <android/log.h>
#include "common.h"
#include "nce.h"
#include "gpu.h"
@ -68,11 +69,12 @@ namespace skyline {
Settings::Settings(int fd) {
tinyxml2::XMLDocument pref;
if (pref.LoadFile(fdopen(fd, "r")))
auto fileDeleter = [](FILE *file) { fclose(file); };
std::unique_ptr<FILE, decltype(fileDeleter)> file{fdopen(fd, "r"), fileDeleter};
if (pref.LoadFile(file.get()))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
while (elem) {
switch (elem->Value()[0]) {
case 's':
@ -88,7 +90,7 @@ namespace skyline {
break;
default:
syslog(LOG_ALERT, "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
__android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
break;
};
@ -122,7 +124,7 @@ namespace skyline {
}
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
logFile.open(path, std::ios::app);
logFile.open(path, std::ios::trunc);
WriteHeader("Logging started");
}
@ -132,14 +134,17 @@ namespace skyline {
}
void Logger::WriteHeader(const std::string &str) {
syslog(LOG_ALERT, "%s", str.c_str());
__android_log_write(ANDROID_LOG_INFO, "emu-cpp", str.c_str());
std::lock_guard guard(mtx);
logFile << "0|" << str << "\n";
}
void Logger::Write(LogLevel level, std::string str) {
syslog(levelSyslog[static_cast<u8>(level)], "%s", str.c_str());
constexpr std::array<char, 4> levelCharacter{'0', '1', '2', '3'}; // The LogLevel as written out to a file
constexpr std::array<int, 4> levelAlog{ANDROID_LOG_ERROR, ANDROID_LOG_WARN, ANDROID_LOG_INFO, ANDROID_LOG_DEBUG}; // This corresponds to LogLevel and provides it's equivalent for NDK Logging
__android_log_write(levelAlog[static_cast<u8>(level)], "emu-cpp", str.c_str());
for (auto &character : str)
if (character == '\n')

View File

@ -17,7 +17,6 @@
#include <string>
#include <sstream>
#include <memory>
#include <syslog.h>
#include <sys/mman.h>
#include <fmt/format.h>
#include <frozen/unordered_map.h>
@ -366,8 +365,6 @@ namespace skyline {
class Logger {
private:
std::ofstream logFile; //!< An output stream to the log file
std::array<char, 4> levelCharacter{'0', '1', '2', '3'}; //!< The LogLevel as written out to a file
static constexpr std::array<int, 4> levelSyslog{LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG}; //!< This corresponds to LogLevel and provides it's equivalent for syslog
Mutex mtx; //!< A mutex to lock before logging anything
public:
@ -377,6 +374,7 @@ namespace skyline {
Info,
Debug,
};
LogLevel configLevel; //!< The minimum level of logs to write
/**
@ -457,9 +455,9 @@ namespace skyline {
*/
class Settings {
private:
std::map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value
std::map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value
std::map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value
std::unordered_map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value
std::unordered_map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value
std::unordered_map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value
public:
/**

View File

@ -15,7 +15,7 @@ namespace skyline::crypto {
void KeyStore::ReadPairs(const std::shared_ptr<vfs::Backing> &backing, ReadPairsCallback callback) {
std::vector<char> fileContent(backing->size);
backing->Read(fileContent.data(), 0, fileContent.size());
backing->Read(span(fileContent).cast<u8>());
auto lineStart{fileContent.begin()};
std::vector<char>::iterator lineEnd;

View File

@ -28,14 +28,14 @@ namespace skyline::crypto {
std::map<Key128, Key128> titleKeys;
std::unordered_map<std::string_view, std::optional<Key256> &> key256Names{
{"header_key", headerKey}
{"header_key", headerKey},
};
std::unordered_map<std::string_view, IndexedKeys128 &> indexedKey128Names{
{"titlekek_", titleKek},
{"key_area_key_application_", areaKeyApplication},
{"key_area_key_ocean_", areaKeyOcean},
{"key_area_key_system_", areaKeySystem}
{"key_area_key_system_", areaKeySystem},
};
using ReadPairsCallback = void (skyline::crypto::KeyStore::*)(std::string_view, std::string_view);

View File

@ -16,8 +16,8 @@ namespace skyline::kernel::type {
class KSession : public KSyncObject {
public:
std::shared_ptr<service::BaseService> serviceObject;
std::unordered_map<KHandle, std::shared_ptr<service::BaseService>> domainTable; //!< A map from a virtual handle to it's service
KHandle handleIndex{0x1}; //!< The currently allocated handle index
std::vector<std::shared_ptr<service::BaseService>> domains; //!< A vector of services that correspond to virtual handles
KHandle handleIndex{}; //!< The currently allocated handle index
bool isOpen{true}; //!< If the session is open or not
bool isDomain{}; //!< If this is a domain session or not
@ -33,7 +33,7 @@ namespace skyline::kernel::type {
*/
KHandle ConvertDomain() {
isDomain = true;
domainTable[handleIndex] = serviceObject;
domains.push_back(serviceObject);
return handleIndex++;
}
};

View File

@ -10,14 +10,14 @@
namespace skyline::loader {
NroLoader::NroLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) {
backing->Read(&header);
header = backing->Read<NroHeader>();
if (header.magic != util::MakeMagic<u32>("NRO0"))
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
// The homebrew asset section is appended to the end of an NRO file
if (backing->size > header.size) {
backing->Read(&assetHeader, header.size);
assetHeader = backing->Read<NroAssetHeader>(header.size);
if (assetHeader.magic != util::MakeMagic<u32>("ASET"))
throw exception("Invalid ASET magic! 0x{0:X}", assetHeader.magic);
@ -34,14 +34,14 @@ namespace skyline::loader {
NroAssetSection &segmentHeader{assetHeader.icon};
std::vector<u8> buffer(segmentHeader.size);
backing->Read(buffer.data(), header.size + segmentHeader.offset, segmentHeader.size);
backing->Read(buffer, header.size + segmentHeader.offset);
return buffer;
}
std::vector<u8> NroLoader::GetSegment(const NroSegmentHeader &segment) {
std::vector<u8> buffer(segment.size);
backing->Read(buffer.data(), segment.offset, segment.size);
backing->Read(buffer, segment.offset);
return buffer;
}

View File

@ -9,8 +9,7 @@
namespace skyline::loader {
NsoLoader::NsoLoader(const std::shared_ptr<vfs::Backing> &backing) : backing(backing) {
u32 magic{};
backing->Read(&magic);
u32 magic{backing->Read<u32>()};
if (magic != util::MakeMagic<u32>("NSO0"))
throw exception("Invalid NSO magic! 0x{0:X}", magic);
@ -21,19 +20,18 @@ namespace skyline::loader {
if (compressedSize) {
std::vector<u8> compressedBuffer(compressedSize);
backing->Read(compressedBuffer.data(), segment.fileOffset, compressedSize);
backing->Read(compressedBuffer, segment.fileOffset);
LZ4_decompress_safe(reinterpret_cast<char *>(compressedBuffer.data()), reinterpret_cast<char *>(outputBuffer.data()), compressedSize, segment.decompressedSize);
} else {
backing->Read(outputBuffer.data(), segment.fileOffset, segment.decompressedSize);
backing->Read(outputBuffer, segment.fileOffset);
}
return outputBuffer;
}
Loader::ExecutableLoadInfo NsoLoader::LoadNso(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state, size_t offset) {
NsoHeader header{};
backing->Read(&header);
auto header{backing->Read<NsoHeader>()};
if (header.magic != util::MakeMagic<u32>("NSO0"))
throw exception("Invalid NSO magic! 0x{0:X}", header.magic);

View File

@ -57,8 +57,7 @@ namespace skyline::loader {
return std::vector<u8>();
std::vector<u8> buffer(icon->size);
icon->Read(buffer.data(), 0, icon->size);
icon->Read(buffer);
return buffer;
}
}

View File

@ -23,7 +23,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize;
}
response.Push<u32>(static_cast<u32>(backing->Read(request.outputBuf.at(0).data(), offset, size)));
response.Push<u32>(static_cast<u32>(backing->Read(request.outputBuf.at(0), offset)));
return {};
}
@ -48,7 +48,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize;
}
if (backing->Write(request.inputBuf.at(0).data(), offset, request.inputBuf.at(0).size()) != size) {
if (backing->Write(request.inputBuf.at(0), offset) != size) {
state.logger->Warn("Failed to write all data to the backing");
return result::UnexpectedFailure;
}

View File

@ -21,7 +21,7 @@ namespace skyline::service::fssrv {
return result::InvalidSize;
}
backing->Read(request.outputBuf.at(0).data(), offset, size);
backing->Read(request.outputBuf.at(0), offset);
return {};
}

View File

@ -10,7 +10,9 @@ namespace skyline::service::nvdrv {
constexpr u32 VBlank0SyncpointId{26};
constexpr u32 VBlank1SyncpointId{27};
// Reserve both vblank syncpoints as client managed since the userspace driver has direct access to them
// Reserve both vblank syncpoints as client managed as they use Continuous Mode
// Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
// https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
ReserveSyncpoint(VBlank0SyncpointId, true);
ReserveSyncpoint(VBlank1SyncpointId, true);
}
@ -20,7 +22,7 @@ namespace skyline::service::nvdrv {
throw exception("Requested syncpoint is in use");
syncpoints.at(id).reserved = true;
syncpoints.at(id).clientManaged = clientManaged;
syncpoints.at(id).interfaceManaged = clientManaged;
return id;
}
@ -44,7 +46,8 @@ namespace skyline::service::nvdrv {
if (!syncpoint.reserved)
throw exception("Cannot check the expiry status of an unreserved syncpoint!");
if (syncpoint.clientManaged)
// If the interface manages counters then we don't keep track of the maximum value as it handles sanity checking the values then
if (syncpoint.interfaceManaged)
return static_cast<i32>(syncpoint.counterMin - threshold) >= 0;
else
return (syncpoint.counterMax - threshold) >= (syncpoint.counterMin - threshold);

View File

@ -7,16 +7,17 @@
namespace skyline::service::nvdrv {
/**
* @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints
* @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints, these are cached versions of the HW syncpoints which are intermittently synced
* @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them
* @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
* @url https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
*/
class NvHostSyncpoint {
private:
struct SyncpointInfo {
std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with the GPU for host managed syncpoints)
std::atomic<u32> counterMin; //!< The least value the syncpoint can be (The value it was when it was last synchronized with host1x)
std::atomic<u32> counterMax; //!< The maximum value the syncpoint can reach according to the current usage
bool clientManaged; //!< If the syncpoint is managed by the client (CPU) or host (GPU), it determines if the value is absolute or not
bool interfaceManaged; //!< If the syncpoint is managed by a host1x client interface, a client interface is a HW block that can handle host1x transactions on behalf of a host1x client (Which would otherwise need to be manually synced using PIO which is synchronous and requires direct cooperation of the CPU)
bool reserved;
};

View File

@ -89,9 +89,9 @@ namespace skyline::service {
auto serviceObject{CreateService(name)};
KHandle handle{};
if (session.isDomain) {
session.domainTable[++session.handleIndex] = serviceObject;
session.domains.push_back(serviceObject);
response.domainObjects.push_back(session.handleIndex);
handle = session.handleIndex;
handle = session.handleIndex++;
} else {
handle = state.process->NewHandle<type::KSession>(serviceObject).handle;
response.moveHandles.push_back(handle);
@ -105,7 +105,7 @@ namespace skyline::service {
KHandle handle{};
if (session.isDomain) {
session.domainTable[session.handleIndex] = serviceObject;
session.domains.push_back(serviceObject);
response.domainObjects.push_back(session.handleIndex);
handle = session.handleIndex++;
} else {
@ -121,9 +121,9 @@ namespace skyline::service {
auto session{state.process->GetHandle<type::KSession>(handle)};
if (session->isOpen) {
if (session->isDomain) {
for (const auto &domainEntry : session->domainTable)
std::erase_if(serviceMap, [domainEntry](const auto &entry) {
return entry.second == domainEntry.second;
for (const auto &domainService : session->domains)
std::erase_if(serviceMap, [domainService](const auto &entry) {
return entry.second == domainService;
});
} else {
std::erase_if(serviceMap, [session](const auto &entry) {
@ -148,7 +148,9 @@ namespace skyline::service {
case ipc::CommandType::RequestWithContext:
if (session->isDomain) {
try {
auto service{session->domainTable.at(request.domain->objectId)};
auto service{session->domains.at(request.domain->objectId)};
if (service == nullptr)
throw exception("Domain request used an expired handle");
switch (request.domain->command) {
case ipc::DomainCommand::SendMessage:
response.errorCode = service->HandleRequest(*session, request, response);
@ -158,7 +160,7 @@ namespace skyline::service {
std::erase_if(serviceMap, [service](const auto &entry) {
return entry.second == service;
});
session->domainTable.erase(request.domain->objectId);
session->domains.at(request.domain->objectId).reset();
break;
}
} catch (std::out_of_range &) {

View File

@ -11,9 +11,6 @@ namespace skyline::vfs {
*/
class Backing {
public:
/**
* @brief The capabilities of the Backing
*/
union Mode {
struct {
bool read : 1; //!< The backing is readable
@ -41,47 +38,44 @@ namespace skyline::vfs {
/**
* @brief Read bytes from the backing at a particular offset to a buffer
* @param output The object to write to
* @param output The object to write the data read to
* @param offset The offset to start reading from
* @param size The amount to read in bytes
* @return The amount of bytes read
*/
virtual size_t Read(u8 *output, size_t offset, size_t size) = 0;
virtual size_t Read(span<u8> output, size_t offset = 0) = 0;
/**
* @brief Read bytes from the backing at a particular offset to a buffer (template version)
* @param output The object to write to
* @brief Read bytes from the backing at a particular offset into an object
* @param offset The offset to start reading from
* @param size The amount to read in bytes
* @return The amount of bytes read
* @return The object that was read
*/
template<typename T>
inline size_t Read(T *output, size_t offset = 0, size_t size = 0) {
return Read(reinterpret_cast<u8 *>(output), offset, size ? size : sizeof(T));
inline T Read(size_t offset = 0) {
T object;
Read(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset);
return object;
}
/**
* @brief Writes from a buffer to a particular offset in the backing
* @param input The object to write to the backing
* @param input The data to write to the backing
* @param offset The offset where the input buffer should be written
* @param size The amount to write
* @return The amount of bytes written
*/
virtual size_t Write(u8 *input, size_t offset, size_t size) {
virtual size_t Write(span<u8> input, size_t offset = 0) {
throw exception("This backing does not support being written to");
}
/**
* @brief Writes from a buffer to a particular offset in the backing (template version)
* @tparam T The type of object to write
* @param input The object to write to the backing
* @param offset The offset where the input buffer should be written
* @param size The amount to write
* @return The amount of bytes written
* @brief Writes from an object into a particular offset in the backing
* @param object The object to write to the backing
* @param offset The offset where the input should be written
*/
template<typename T>
inline size_t Write(T *output, size_t offset = 0, size_t size = 0) {
return Write(reinterpret_cast<u8 *>(output), offset, size ? size : sizeof(T));
inline void WriteObject(const T& object, size_t offset = 0) {
size_t size;
if((size = Write(span(reinterpret_cast<u8 *>(&object), sizeof(T)), offset)) != sizeof(T))
throw exception("Object wasn't written fully into output backing: {}/{}", size, sizeof(T));
}
/**

View File

@ -15,34 +15,34 @@ namespace skyline::vfs {
cipher.SetIV(ctr);
}
size_t CtrEncryptedBacking::Read(u8 *output, size_t offset, size_t size) {
size_t CtrEncryptedBacking::Read(span<u8> output, size_t offset) {
if (size == 0)
return 0;
size_t sectorOffset{offset % SectorSize};
if (sectorOffset == 0) {
UpdateCtr(baseOffset + offset);
size_t read{backing->Read(output, offset, size)};
size_t read{backing->Read(output, offset)};
if (read != size)
return 0;
cipher.Decrypt({output, size});
cipher.Decrypt(output);
return size;
}
size_t sectorStart{offset - sectorOffset};
std::vector<u8> blockBuf(SectorSize);
size_t read{backing->Read(blockBuf.data(), sectorStart, SectorSize)};
size_t read{backing->Read(blockBuf, sectorStart)};
if (read != SectorSize)
return 0;
UpdateCtr(baseOffset + sectorStart);
cipher.Decrypt(blockBuf);
if (size + sectorOffset < SectorSize) {
std::memcpy(output, blockBuf.data() + sectorOffset, size);
std::memcpy(output.data(), blockBuf.data() + sectorOffset, size);
return size;
}
size_t readInBlock{SectorSize - sectorOffset};
std::memcpy(output, blockBuf.data() + sectorOffset, readInBlock);
return readInBlock + Read(output + readInBlock, offset + readInBlock, size - readInBlock);
std::memcpy(output.data(), blockBuf.data() + sectorOffset, readInBlock);
return readInBlock + Read(output.subspan(readInBlock), offset + readInBlock);
}
}

View File

@ -26,6 +26,6 @@ namespace skyline::vfs {
public:
CtrEncryptedBacking(crypto::KeyStore::Key128 &ctr, crypto::KeyStore::Key128 &key, const std::shared_ptr<Backing> &backing, size_t baseOffset);
size_t Read(u8 *output, size_t offset, size_t size) override;
size_t Read(span<u8> output, size_t offset = 0) override;
};
}

View File

@ -5,7 +5,7 @@
namespace skyline::vfs {
NACP::NACP(const std::shared_ptr<vfs::Backing> &backing) {
backing->Read(&nacpContents);
nacpContents = backing->Read<NacpData>();
// TODO: Select based on language settings, complete struct, yada yada

View File

@ -14,7 +14,7 @@ namespace skyline::vfs {
using namespace loader;
NCA::NCA(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<crypto::KeyStore> &keyStore) : backing(backing), keyStore(keyStore) {
backing->Read(&header);
header = backing->Read<NcaHeader>();
if (header.magic != util::MakeMagic<u32>("NCA3")) {
if (!keyStore->headerKey)

View File

@ -20,22 +20,22 @@ namespace skyline::vfs {
close(fd);
}
size_t OsBacking::Read(u8 *output, size_t offset, size_t size) {
size_t OsBacking::Read(span<u8> output, size_t offset) {
if (!mode.read)
throw exception("Attempting to read a backing that is not readable");
auto ret{pread64(fd, output, size, offset)};
auto ret{pread64(fd, output.data(), output.size(), offset)};
if (ret < 0)
throw exception("Failed to read from fd: {}", strerror(errno));
return static_cast<size_t>(ret);
}
size_t OsBacking::Write(u8 *output, size_t offset, size_t size) {
size_t OsBacking::Write(span<u8> input, size_t offset) {
if (!mode.write)
throw exception("Attempting to write to a backing that is not writable");
auto ret{pwrite64(fd, output, size, offset)};
auto ret{pwrite64(fd, input.data(), input.size(), offset)};
if (ret < 0)
throw exception("Failed to write to fd: {}", strerror(errno));

View File

@ -22,9 +22,9 @@ namespace skyline::vfs {
~OsBacking();
size_t Read(u8 *output, size_t offset, size_t size);
size_t Read(span<u8> output, size_t offset = 0);
size_t Write(u8 *output, size_t offset, size_t size);
size_t Write(span<u8> input, size_t offset = 0);
void Resize(size_t size);
};

View File

@ -6,7 +6,7 @@
namespace skyline::vfs {
PartitionFileSystem::PartitionFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) {
backing->Read(&header);
header = backing->Read<FsHeader>();
if (header.magic == util::MakeMagic<u32>("PFS0"))
hashed = false;
@ -20,12 +20,11 @@ namespace skyline::vfs {
fileDataOffset = stringTableOffset + header.stringTableSize;
std::vector<char> stringTable(header.stringTableSize + 1);
backing->Read(stringTable.data(), stringTableOffset, header.stringTableSize);
backing->Read(span(stringTable).first(header.stringTableSize).cast<u8>(), stringTableOffset);
stringTable[header.stringTableSize] = 0;
for (u32 entryOffset{sizeof(FsHeader)}; entryOffset < header.numFiles * entrySize; entryOffset += entrySize) {
PartitionFileEntry entry;
backing->Read(&entry, entryOffset);
auto entry{backing->Read<PartitionFileEntry>(entryOffset)};
std::string name(&stringTable[entry.stringTableOffset]);
fileMap.emplace(name, std::move(entry));

View File

@ -16,7 +16,7 @@ namespace skyline::vfs {
u32 numFiles; //!< The number of files in the filesystem
u32 stringTableSize; //!< The size of the filesystem's string table
u32 _pad_;
} header{};
} header;
static_assert(sizeof(FsHeader) == 0x10);
struct PartitionFileEntry {

View File

@ -22,13 +22,12 @@ namespace skyline::vfs {
*/
RegionBacking(const std::shared_ptr<vfs::Backing> &backing, size_t offset, size_t size, Mode mode = {true, false, false}) : Backing(mode, size), backing(backing), baseOffset(offset) {};
inline size_t Read(u8 *output, size_t offset, size_t size) {
virtual size_t Read(span<u8> output, size_t offset = 0) {
if (!mode.read)
throw exception("Attempting to read a backing that is not readable");
size = std::min(offset + size, this->size) - offset;
return backing->Read(output, baseOffset + offset, size);
if (size - offset < output.size())
throw exception("Trying to read past the end of a region backing: 0x{:X}/0x{:X} (Offset: 0x{:X})", output.size(), size, offset);
return backing->Read(output, baseOffset + offset);
}
};
}

View File

@ -6,20 +6,18 @@
namespace skyline::vfs {
RomFileSystem::RomFileSystem(std::shared_ptr<Backing> backing) : FileSystem(), backing(backing) {
backing->Read(&header);
header = backing->Read<RomFsHeader>();
TraverseDirectory(0, "");
}
void RomFileSystem::TraverseFiles(u32 offset, const std::string &path) {
RomFsFileEntry entry{};
RomFsFileEntry entry;
do {
backing->Read(&entry, header.fileMetaTableOffset + offset);
entry = backing->Read<RomFsFileEntry>(header.fileMetaTableOffset + offset);
if (entry.nameSize) {
std::vector<char> name(entry.nameSize);
backing->Read(name.data(), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry), entry.nameSize);
backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFsFileEntry));
std::string fullPath{path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize)};
fileMap.emplace(fullPath, entry);
@ -30,13 +28,12 @@ namespace skyline::vfs {
}
void RomFileSystem::TraverseDirectory(u32 offset, const std::string &path) {
RomFsDirectoryEntry entry{};
backing->Read(&entry, header.dirMetaTableOffset + offset);
auto entry{backing->Read<RomFsDirectoryEntry>(header.dirMetaTableOffset + offset)};
std::string childPath{path};
if (entry.nameSize) {
std::vector<char> name(entry.nameSize);
backing->Read(name.data(), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry), entry.nameSize);
backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFsDirectoryEntry));
childPath = path + (path.empty() ? "" : "/") + std::string(name.data(), entry.nameSize);
}
@ -89,11 +86,11 @@ namespace skyline::vfs {
u32 offset{ownEntry.fileOffset};
do {
backing->Read(&romFsFileEntry, header.fileMetaTableOffset + offset);
romFsFileEntry = backing->Read<RomFileSystem::RomFsFileEntry>(header.fileMetaTableOffset + offset);
if (romFsFileEntry.nameSize) {
std::vector<char> name(romFsFileEntry.nameSize);
backing->Read(name.data(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry), romFsFileEntry.nameSize);
backing->Read(span(name).cast<u8>(), header.fileMetaTableOffset + offset + sizeof(RomFileSystem::RomFsFileEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsFileEntry.nameSize), EntryType::File});
}
@ -107,11 +104,11 @@ namespace skyline::vfs {
u32 offset{ownEntry.childOffset};
do {
backing->Read(&romFsDirectoryEntry, header.dirMetaTableOffset + offset);
romFsDirectoryEntry = backing->Read<RomFileSystem::RomFsDirectoryEntry>(header.dirMetaTableOffset + offset);
if (romFsDirectoryEntry.nameSize) {
std::vector<char> name(romFsDirectoryEntry.nameSize);
backing->Read(name.data(), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry), romFsDirectoryEntry.nameSize);
backing->Read(span(name).cast<u8>(), header.dirMetaTableOffset + offset + sizeof(RomFileSystem::RomFsDirectoryEntry));
contents.emplace_back(Entry{std::string(name.data(), romFsDirectoryEntry.nameSize), EntryType::Directory});
}

View File

@ -29,16 +29,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
System.loadLibrary("skyline") // libskyline.so
}
/**
* The file descriptor of the ROM
*/
private lateinit var romFd : ParcelFileDescriptor
/**
* The file descriptor of the application Preference XML
*/
private lateinit var preferenceFd : ParcelFileDescriptor
/**
* The [InputManager] class handles loading/saving the input data
*/
@ -187,12 +177,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
*/
private fun executeApplication(rom : Uri) {
val romType = getRomFormat(rom, contentResolver).ordinal
romFd = contentResolver.openFileDescriptor(rom, "r")!!
val romFd = contentResolver.openFileDescriptor(rom, "r")!!
val preferenceFd = ParcelFileDescriptor.open(File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml"), ParcelFileDescriptor.MODE_READ_WRITE)
emulationThread = Thread {
surfaceReady.block()
executeApplication(rom.toString(), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
executeApplication(rom.toString(), romType, romFd.detachFd(), preferenceFd.detachFd(), applicationContext.filesDir.canonicalPath + "/")
if (shouldFinish)
runOnUiThread { finish() }
@ -225,9 +216,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
input = InputManager(this)
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
game_view.holder.addCallback(this)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
@ -262,8 +250,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
shouldFinish = true
romFd.close()
executeApplication(intent?.data!!)
super.onNewIntent(intent)
@ -281,9 +267,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
vibrators.forEach { (_, vibrator) -> vibrator.cancel() }
vibrators.clear()
romFd.close()
preferenceFd.close()
super.onDestroy()
}