mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-27 16:15:30 +03:00
Update Logger to use NDK Logger APIs + Improve Backing API + Fix FDSAN issues
This commit is contained in:
parent
4070686897
commit
97ac45d83b
@ -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) {
|
||||
|
@ -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')
|
||||
|
@ -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:
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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++;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 &) {
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
@ -18,4 +18,4 @@ namespace skyline::vfs {
|
||||
applicationPublisher = std::string(entry.applicationPublisher.data(), entry.applicationPublisher.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
@ -152,4 +152,4 @@ namespace skyline::vfs {
|
||||
return keyArea(keyStore->areaKeySystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -22,10 +22,10 @@ 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user