mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 18:14:20 +01:00
Adapt C++ backend to changes
This commit adapts the C++ backend to the Kotlin frontend by moving to usage of file descriptors and, provides an interface to access frontend code via JNI which is used to check the state of the activity and catch events such as surface destruction. In addition, this commit fixes some minor linting errors and changes the CMake version to 3.10.2+.
This commit is contained in:
parent
e4dc602f4d
commit
b3e811d488
@ -39,7 +39,7 @@ android {
|
|||||||
}
|
}
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
version "3.8.0+"
|
version "3.10.2+"
|
||||||
path "CMakeLists.txt"
|
path "CMakeLists.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,115 +1,51 @@
|
|||||||
#include "skyline/common.h"
|
#include "skyline/common.h"
|
||||||
#include "skyline/os.h"
|
#include "skyline/os.h"
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <jni.h>
|
|
||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
#include <android/native_activity.h>
|
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
|
|
||||||
bool Halt{};
|
bool Halt{};
|
||||||
uint faultCount{};
|
uint FaultCount{};
|
||||||
ANativeActivity *Activity{};
|
|
||||||
ANativeWindow *Window{};
|
|
||||||
AInputQueue *Queue{};
|
|
||||||
std::thread *uiThread{};
|
|
||||||
|
|
||||||
void GameThread(const std::string &prefPath, const std::string &logPath, const std::string &romPath) {
|
|
||||||
while (!Window)
|
|
||||||
sched_yield();
|
|
||||||
setpriority(PRIO_PROCESS, static_cast<id_t>(getpid()), skyline::constant::PriorityAn.second);
|
|
||||||
auto settings = std::make_shared<skyline::Settings>(prefPath);
|
|
||||||
auto logger = std::make_shared<skyline::Logger>(logPath, static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))));
|
|
||||||
//settings->List(logger); // (Uncomment when you want to print out all settings strings)
|
|
||||||
auto start = std::chrono::steady_clock::now();
|
|
||||||
try {
|
|
||||||
skyline::kernel::OS os(logger, settings, Window);
|
|
||||||
logger->Info("Launching ROM {}", romPath);
|
|
||||||
os.Execute(romPath);
|
|
||||||
logger->Info("Emulation has ended");
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
logger->Error(e.what());
|
|
||||||
} catch (...) {
|
|
||||||
logger->Error("An unknown exception has occurred");
|
|
||||||
}
|
|
||||||
auto end = std::chrono::steady_clock::now();
|
|
||||||
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
|
||||||
Window = nullptr;
|
|
||||||
Halt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UIThread(const std::string &prefPath, const std::string &logPath, const std::string &romPath) {
|
|
||||||
while (!Queue)
|
|
||||||
sched_yield();
|
|
||||||
std::thread gameThread(GameThread, std::string(prefPath), std::string(logPath), std::string(romPath));
|
|
||||||
AInputEvent *event{};
|
|
||||||
while (!Halt) {
|
|
||||||
if (AInputQueue_getEvent(Queue, &event) >= -1) {
|
|
||||||
if (AKeyEvent_getKeyCode(event) == AKEYCODE_BACK)
|
|
||||||
Halt = true;
|
|
||||||
AInputQueue_finishEvent(Queue, event, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Queue = nullptr;
|
|
||||||
gameThread.join();
|
|
||||||
Halt = false;
|
|
||||||
ANativeActivity_finish(Activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNativeWindowCreated(ANativeActivity *activity, ANativeWindow *window) {
|
|
||||||
Window = window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onInputQueueCreated(ANativeActivity *activity, AInputQueue *queue) {
|
|
||||||
Queue = queue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNativeWindowDestroyed(ANativeActivity *activity, ANativeWindow *window) {
|
|
||||||
Halt = true;
|
|
||||||
while (Window)
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void onInputQueueDestroyed(ANativeActivity *activity, AInputQueue *queue) {
|
|
||||||
Halt = true;
|
|
||||||
while (Queue)
|
|
||||||
sched_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
void signalHandler(int signal) {
|
void signalHandler(int signal) {
|
||||||
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
|
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
|
||||||
if (faultCount > 2)
|
if (FaultCount > 2)
|
||||||
pthread_kill(uiThread->native_handle(), SIGKILL);
|
exit(SIGKILL);
|
||||||
else
|
else
|
||||||
ANativeActivity_finish(Activity);
|
Halt = true;
|
||||||
faultCount++;
|
FaultCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void ANativeActivity_onCreate(ANativeActivity *activity, void *savedState, size_t savedStateSize) {
|
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, jobject instance, jstring romJstring, jint romType, jint romFd, jint preferenceFd, jint logFd) {
|
||||||
Activity = activity;
|
|
||||||
Halt = false;
|
|
||||||
faultCount = 0;
|
|
||||||
JNIEnv *env = activity->env;
|
|
||||||
jobject intent = env->CallObjectMethod(activity->clazz, env->GetMethodID(env->GetObjectClass(activity->clazz), "getIntent", "()Landroid/content/Intent;"));
|
|
||||||
jclass icl = env->GetObjectClass(intent);
|
|
||||||
jmethodID gse = env->GetMethodID(icl, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;");
|
|
||||||
auto jsRom = reinterpret_cast<jstring>(env->CallObjectMethod(intent, gse, env->NewStringUTF("rom")));
|
|
||||||
auto jsPrefs = reinterpret_cast<jstring>(env->CallObjectMethod(intent, gse, env->NewStringUTF("prefs")));
|
|
||||||
auto jsLog = reinterpret_cast<jstring>(env->CallObjectMethod(intent, gse, env->NewStringUTF("log")));
|
|
||||||
const char *romPath = env->GetStringUTFChars(jsRom, nullptr);
|
|
||||||
const char *prefPath = env->GetStringUTFChars(jsPrefs, nullptr);
|
|
||||||
const char *logPath = env->GetStringUTFChars(jsLog, nullptr);
|
|
||||||
std::signal(SIGTERM, signalHandler);
|
std::signal(SIGTERM, signalHandler);
|
||||||
std::signal(SIGSEGV, signalHandler);
|
std::signal(SIGSEGV, signalHandler);
|
||||||
std::signal(SIGINT, signalHandler);
|
std::signal(SIGINT, signalHandler);
|
||||||
std::signal(SIGILL, signalHandler);
|
std::signal(SIGILL, signalHandler);
|
||||||
std::signal(SIGABRT, signalHandler);
|
std::signal(SIGABRT, signalHandler);
|
||||||
std::signal(SIGFPE, signalHandler);
|
std::signal(SIGFPE, signalHandler);
|
||||||
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
|
|
||||||
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
|
setpriority(PRIO_PROCESS, static_cast<id_t>(getpid()), skyline::constant::PriorityAn.second);
|
||||||
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
|
|
||||||
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
|
auto jvmManager = std::make_shared<skyline::JvmManager>(env, instance);
|
||||||
uiThread = new std::thread(UIThread, std::string(prefPath), std::string(logPath), std::string(romPath));
|
auto settings = std::make_shared<skyline::Settings>(preferenceFd);
|
||||||
env->ReleaseStringUTFChars(jsRom, romPath);
|
auto logger = std::make_shared<skyline::Logger>(logFd, static_cast<skyline::Logger::LogLevel>(std::stoi(settings->GetString("log_level"))));
|
||||||
env->ReleaseStringUTFChars(jsPrefs, prefPath);
|
//settings->List(logger); // (Uncomment when you want to print out all settings strings)
|
||||||
env->ReleaseStringUTFChars(jsLog, logPath);
|
|
||||||
|
auto start = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
try {
|
||||||
|
skyline::kernel::OS os(jvmManager, logger, settings);
|
||||||
|
const char *romString = env->GetStringUTFChars(romJstring, nullptr);
|
||||||
|
logger->Info("Launching ROM {}", romString);
|
||||||
|
env->ReleaseStringUTFChars(romJstring, romString);
|
||||||
|
os.Execute(romFd, static_cast<skyline::TitleFormat>(romType));
|
||||||
|
logger->Info("Emulation has ended");
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
logger->Error(e.what());
|
||||||
|
} catch (...) {
|
||||||
|
logger->Error("An unknown exception has occurred");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end = std::chrono::steady_clock::now();
|
||||||
|
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
#include <tinyxml2.h>
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
Settings::Settings(const std::string &prefXml) {
|
Settings::Settings(const int preferenceFd) {
|
||||||
tinyxml2::XMLDocument pref;
|
tinyxml2::XMLDocument pref;
|
||||||
if (pref.LoadFile(prefXml.c_str()))
|
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
||||||
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
|
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
|
||||||
tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement();
|
tinyxml2::XMLElement *elem = pref.LastChild()->FirstChild()->ToElement();
|
||||||
while (elem) {
|
while (elem) {
|
||||||
@ -51,8 +51,8 @@ namespace skyline {
|
|||||||
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
|
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Logger(const std::string &logPath, LogLevel configLevel) : configLevel(configLevel) {
|
Logger::Logger(const int logFd, LogLevel configLevel) : configLevel(configLevel) {
|
||||||
logFile.open(logPath, std::ios::app);
|
logFile.__open(logFd, std::ios::app);
|
||||||
WriteHeader("Logging started");
|
WriteHeader("Logging started");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,9 +75,20 @@ namespace skyline {
|
|||||||
logFile.flush();
|
logFile.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, ANativeWindow *window, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger) : os(os), settings(std::move(settings)), logger(std::move(logger)), thisProcess(thisProcess), thisThread(thisThread) {
|
JvmManager::JvmManager(JNIEnv *env, jobject instance) : env(env), instance(instance), instanceClass(env->GetObjectClass(instance)) {}
|
||||||
// We assign these later as they may use the state in their constructor and we don't want null pointers
|
|
||||||
|
jobject JvmManager::GetField(const char *key, const char *signature) {
|
||||||
|
return env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JvmManager::CheckNull(const char *key, const char *signature) {
|
||||||
|
return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger)
|
||||||
|
: os(os), jvmManager(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)), thisProcess(thisProcess), thisThread(thisThread) {
|
||||||
|
// We assign these later as they use the state in their constructor and we don't want null pointers
|
||||||
nce = std::move(std::make_shared<NCE>(*this));
|
nce = std::move(std::make_shared<NCE>(*this));
|
||||||
gpu = std::move(std::make_shared<gpu::GPU>(*this, window));
|
gpu = std::move(std::make_shared<gpu::GPU>(*this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
using u128 = __uint128_t; //!< Unsigned 128-bit integer
|
using u128 = __uint128_t; //!< Unsigned 128-bit integer
|
||||||
@ -94,6 +95,16 @@ namespace skyline {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This enumerates the types of the ROM
|
||||||
|
* @note This needs to be synchronized with emu.skyline.loader.BaseLoader.TitleFormat
|
||||||
|
*/
|
||||||
|
enum class TitleFormat {
|
||||||
|
NRO, //!< The NRO format: https://switchbrew.org/wiki/NRO
|
||||||
|
XCI, //!< The XCI format: https://switchbrew.org/wiki/XCI
|
||||||
|
NSP, //!< The NSP format from "nspwn" exploit: https://switchbrew.org/wiki/Switch_System_Flaws
|
||||||
|
};
|
||||||
|
|
||||||
namespace instr {
|
namespace instr {
|
||||||
/**
|
/**
|
||||||
* @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
|
* @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
|
||||||
@ -182,7 +193,7 @@ namespace skyline {
|
|||||||
enum class Sreg { Sp, Pc, PState };
|
enum class Sreg { Sp, Pc, PState };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The Logger class is to generate a log of the program
|
* @brief The Logger class is to write log output
|
||||||
*/
|
*/
|
||||||
class Logger {
|
class Logger {
|
||||||
private:
|
private:
|
||||||
@ -195,13 +206,13 @@ namespace skyline {
|
|||||||
LogLevel configLevel; //!< The level of logs to write
|
LogLevel configLevel; //!< The level of logs to write
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param logPath The path to the log file
|
* @param logFd A FD to the log file
|
||||||
* @param configLevel The minimum level of logs to write
|
* @param configLevel The minimum level of logs to write
|
||||||
*/
|
*/
|
||||||
Logger(const std::string &logPath, LogLevel configLevel);
|
Logger(const int logFd, LogLevel configLevel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes "Logging ended" as a header
|
* @brief Writes the termination message to the log file
|
||||||
*/
|
*/
|
||||||
~Logger();
|
~Logger();
|
||||||
|
|
||||||
@ -278,9 +289,9 @@ namespace skyline {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @param prefXml The path to the preference XML file
|
* @param preferenceFd An FD to the preference XML file
|
||||||
*/
|
*/
|
||||||
Settings(const std::string &prefXml);
|
Settings(const int preferenceFd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Retrieves a particular setting as a string
|
* @brief Retrieves a particular setting as a string
|
||||||
@ -322,6 +333,64 @@ namespace skyline {
|
|||||||
inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, args...)) {}
|
inline exception(const S &formatStr, Args &&... args) : runtime_error(fmt::format(formatStr, args...)) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The JvmManager class is used to simplify transactions with the Java component
|
||||||
|
*/
|
||||||
|
class JvmManager {
|
||||||
|
public:
|
||||||
|
JNIEnv *env; //!< A pointer to the JNI environment
|
||||||
|
jobject instance; //!< A reference to the activity
|
||||||
|
jclass instanceClass; //!< The class of the activity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param env A pointer to the JNI environment
|
||||||
|
* @param instance A reference to the activity
|
||||||
|
*/
|
||||||
|
JvmManager(JNIEnv *env, jobject instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves a specific field of the given type from the activity
|
||||||
|
* @tparam objectType The type of the object in the field
|
||||||
|
* @param key The name of the field in the activity class
|
||||||
|
* @return The contents of the field as objectType
|
||||||
|
*/
|
||||||
|
template<typename objectType>
|
||||||
|
objectType GetField(const char *key) {
|
||||||
|
if constexpr(std::is_same<objectType, jboolean>())
|
||||||
|
return env->GetBooleanField(instance, env->GetFieldID(instanceClass, key, "Z"));
|
||||||
|
else if constexpr(std::is_same<objectType, jbyte>())
|
||||||
|
return env->GetByteField(instance, env->GetFieldID(instanceClass, key, "B"));
|
||||||
|
else if constexpr(std::is_same<objectType, jchar>())
|
||||||
|
return env->GetCharField(instance, env->GetFieldID(instanceClass, key, "C"));
|
||||||
|
else if constexpr(std::is_same<objectType, jshort>())
|
||||||
|
return env->GetShortField(instance, env->GetFieldID(instanceClass, key, "S"));
|
||||||
|
else if constexpr(std::is_same<objectType, jint>())
|
||||||
|
return env->GetIntField(instance, env->GetFieldID(instanceClass, key, "I"));
|
||||||
|
else if constexpr(std::is_same<objectType, jlong>())
|
||||||
|
return env->GetLongField(instance, env->GetFieldID(instanceClass, key, "J"));
|
||||||
|
else if constexpr(std::is_same<objectType, jfloat>())
|
||||||
|
return env->GetFloatField(instance, env->GetFieldID(instanceClass, key, "F"));
|
||||||
|
else if constexpr(std::is_same<objectType, jdouble>())
|
||||||
|
return env->GetDoubleField(instance, env->GetFieldID(instanceClass, key, "D"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieves a specific field from the activity as a jobject
|
||||||
|
* @param key The name of the field in the activity class
|
||||||
|
* @param signature The signature of the field
|
||||||
|
* @return A jobject of the contents of the field
|
||||||
|
*/
|
||||||
|
jobject GetField(const char *key, const char *signature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if a specific field from the activity is null or not
|
||||||
|
* @param key The name of the field in the activity class
|
||||||
|
* @param signature The signature of the field
|
||||||
|
* @return If the field is null or not
|
||||||
|
*/
|
||||||
|
bool CheckNull(const char *key, const char *signature);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Returns the current time in nanoseconds
|
* @brief Returns the current time in nanoseconds
|
||||||
* @return The current time in nanoseconds
|
* @return The current time in nanoseconds
|
||||||
@ -346,13 +415,14 @@ namespace skyline {
|
|||||||
* @brief This struct is used to hold the state of a device
|
* @brief This struct is used to hold the state of a device
|
||||||
*/
|
*/
|
||||||
struct DeviceState {
|
struct DeviceState {
|
||||||
DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, ANativeWindow *window, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
|
DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &thisProcess, std::shared_ptr<kernel::type::KThread> &thisThread, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
|
||||||
|
|
||||||
kernel::OS *os; //!< This holds a reference to the OS class
|
kernel::OS *os; //!< This holds a reference to the OS class
|
||||||
std::shared_ptr<kernel::type::KProcess> &thisProcess; //!< This holds a reference to the current process object
|
std::shared_ptr<kernel::type::KProcess> &thisProcess; //!< This holds a reference to the current process object
|
||||||
std::shared_ptr<kernel::type::KThread> &thisThread; //!< This holds a reference to the current thread object
|
std::shared_ptr<kernel::type::KThread> &thisThread; //!< This holds a reference to the current thread object
|
||||||
std::shared_ptr<NCE> nce; //!< This holds a reference to the NCE class
|
std::shared_ptr<NCE> nce; //!< This holds a reference to the NCE class
|
||||||
std::shared_ptr<gpu::GPU> gpu; //!< This holds a reference to the GPU class
|
std::shared_ptr<gpu::GPU> gpu; //!< This holds a reference to the GPU class
|
||||||
|
std::shared_ptr<JvmManager> jvmManager; //!< This holds a reference to the JvmManager class
|
||||||
std::shared_ptr<Settings> settings; //!< This holds a reference to the Settings class
|
std::shared_ptr<Settings> settings; //!< This holds a reference to the Settings class
|
||||||
std::shared_ptr<Logger> logger; //!< This holds a reference to the Logger class
|
std::shared_ptr<Logger> logger; //!< This holds a reference to the Logger class
|
||||||
};
|
};
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
#include "gpu/devices/nvhost_channel.h"
|
#include "gpu/devices/nvhost_channel.h"
|
||||||
#include "gpu/devices/nvhost_as_gpu.h"
|
#include "gpu/devices/nvhost_as_gpu.h"
|
||||||
#include <kernel/types/KProcess.h>
|
#include <kernel/types/KProcess.h>
|
||||||
|
#include <android/native_window_jni.h>
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
GPU::GPU(const DeviceState &state, ANativeWindow *window) : state(state), window(window), bufferQueue(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"))), bufferQueue(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
||||||
ANativeWindow_acquire(window);
|
ANativeWindow_acquire(window);
|
||||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||||
@ -20,6 +21,18 @@ namespace skyline::gpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GPU::Loop() {
|
void GPU::Loop() {
|
||||||
|
if(state.jvmManager->CheckNull("surface", "Landroid/view/Surface;")) {
|
||||||
|
while (state.jvmManager->CheckNull("surface", "Landroid/view/Surface;")) {
|
||||||
|
if(state.jvmManager->GetField<jboolean>("halt"))
|
||||||
|
return;
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
window = ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"));
|
||||||
|
ANativeWindow_acquire(window);
|
||||||
|
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||||
|
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||||
|
format = ANativeWindow_getFormat(window);
|
||||||
|
}
|
||||||
if (!bufferQueue.displayQueue.empty()) {
|
if (!bufferQueue.displayQueue.empty()) {
|
||||||
auto &buffer = bufferQueue.displayQueue.front();
|
auto &buffer = bufferQueue.displayQueue.front();
|
||||||
bufferQueue.displayQueue.pop();
|
bufferQueue.displayQueue.pop();
|
||||||
|
@ -31,7 +31,7 @@ namespace skyline::gpu {
|
|||||||
/**
|
/**
|
||||||
* @param window The ANativeWindow to render to
|
* @param window The ANativeWindow to render to
|
||||||
*/
|
*/
|
||||||
GPU(const DeviceState &state, ANativeWindow *window);
|
GPU(const DeviceState &state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The destructor for the GPU class
|
* @brief The destructor for the GPU class
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
#include <os.h>
|
#include <os.h>
|
||||||
#include <kernel/types/KProcess.h>
|
#include <kernel/types/KProcess.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace skyline::loader {
|
namespace skyline::loader {
|
||||||
class Loader {
|
class Loader {
|
||||||
protected:
|
protected:
|
||||||
std::string filePath; //!< The path to the ROM file
|
const int romFd; //!< An FD to the ROM file
|
||||||
std::ifstream file; //!< An input stream from the file
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read the file at a particular offset
|
* @brief Read the file at a particular offset
|
||||||
@ -17,9 +17,8 @@ namespace skyline::loader {
|
|||||||
* @param size The amount to read in bytes
|
* @param size The amount to read in bytes
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void ReadOffset(T *output, u32 offset, size_t size) {
|
void ReadOffset(T *output, u64 offset, size_t size) {
|
||||||
file.seekg(offset, std::ios_base::beg);
|
pread64(romFd, output, size, offset);
|
||||||
file.read(reinterpret_cast<char *>(output), size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +51,7 @@ namespace skyline::loader {
|
|||||||
/**
|
/**
|
||||||
* @param filePath The path to the ROM file
|
* @param filePath The path to the ROM file
|
||||||
*/
|
*/
|
||||||
Loader(std::string &filePath) : filePath(filePath), file(filePath, std::ios::binary | std::ios::beg) {}
|
Loader(const int romFd) : romFd(romFd) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This loads in the data of the main process
|
* This loads in the data of the main process
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "nro.h"
|
#include "nro.h"
|
||||||
|
|
||||||
namespace skyline::loader {
|
namespace skyline::loader {
|
||||||
NroLoader::NroLoader(std::string filePath) : Loader(filePath) {
|
NroLoader::NroLoader(const int romFd) : Loader(romFd) {
|
||||||
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
|
ReadOffset((u32 *) &header, 0x0, sizeof(NroHeader));
|
||||||
if (header.magic != constant::NroMagic)
|
if (header.magic != constant::NroMagic)
|
||||||
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
|
throw exception("Invalid NRO magic! 0x{0:X}", header.magic);
|
||||||
|
@ -45,7 +45,7 @@ namespace skyline::loader {
|
|||||||
/**
|
/**
|
||||||
* @param filePath The path to the ROM file
|
* @param filePath The path to the ROM file
|
||||||
*/
|
*/
|
||||||
NroLoader(std::string filePath);
|
NroLoader(const int romFd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This loads in the data of the main process
|
* This loads in the data of the main process
|
||||||
|
@ -32,7 +32,7 @@ namespace skyline {
|
|||||||
void NCE::Execute() {
|
void NCE::Execute() {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
while (!Halt && !state.os->processMap.empty()) {
|
while (!Halt && !state.os->processMap.empty()) {
|
||||||
for (const auto &process : state.os->processMap) { // NOLINT(performance-for-range-copy)
|
for (const auto &process : state.os->processMap) {
|
||||||
state.os->thisProcess = process.second;
|
state.os->thisProcess = process.second;
|
||||||
state.os->thisThread = process.second->threadMap.at(process.first);
|
state.os->thisThread = process.second->threadMap.at(process.first);
|
||||||
currPid = process.first;
|
currPid = process.first;
|
||||||
@ -87,6 +87,7 @@ namespace skyline {
|
|||||||
}
|
}
|
||||||
state.os->serviceManager.Loop();
|
state.os->serviceManager.Loop();
|
||||||
state.gpu->Loop();
|
state.gpu->Loop();
|
||||||
|
Halt = state.jvmManager->GetField<jboolean>("halt");
|
||||||
}
|
}
|
||||||
for (const auto &process : state.os->processMap) {
|
for (const auto &process : state.os->processMap) {
|
||||||
state.os->KillThread(process.first);
|
state.os->KillThread(process.first);
|
||||||
|
@ -3,14 +3,12 @@
|
|||||||
#include "loader/nro.h"
|
#include "loader/nro.h"
|
||||||
|
|
||||||
namespace skyline::kernel {
|
namespace skyline::kernel {
|
||||||
OS::OS(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, ANativeWindow *window) : state(this, thisProcess, thisThread, window, settings, logger), serviceManager(state) {}
|
OS::OS(std::shared_ptr<JvmManager>& jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : state(this, thisProcess, thisThread, jvmManager, settings, logger), serviceManager(state) {}
|
||||||
|
|
||||||
void OS::Execute(const std::string &romFile) {
|
void OS::Execute(const int romFd, const TitleFormat romType) {
|
||||||
std::string romExt = romFile.substr(romFile.find_last_of('.') + 1);
|
|
||||||
std::transform(romExt.begin(), romExt.end(), romExt.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
||||||
auto process = CreateProcess(constant::BaseAddr, constant::DefStackSize);
|
auto process = CreateProcess(constant::BaseAddr, constant::DefStackSize);
|
||||||
if (romExt == "nro") {
|
if (romType == TitleFormat::NRO) {
|
||||||
loader::NroLoader loader(romFile);
|
loader::NroLoader loader(romFd);
|
||||||
loader.LoadProcessData(process, state);
|
loader.LoadProcessData(process, state);
|
||||||
} else
|
} else
|
||||||
throw exception("Unsupported ROM extension.");
|
throw exception("Unsupported ROM extension.");
|
||||||
|
@ -30,13 +30,14 @@ namespace skyline::kernel {
|
|||||||
* @param settings An instance of the Settings class
|
* @param settings An instance of the Settings class
|
||||||
* @param window The ANativeWindow object to draw the screen to
|
* @param window The ANativeWindow object to draw the screen to
|
||||||
*/
|
*/
|
||||||
OS(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, ANativeWindow *window);
|
OS(std::shared_ptr<JvmManager>& jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution.
|
* @brief Execute a particular ROM file. This launches the main process and calls the NCE class to handle execution.
|
||||||
* @param romFile The path to the ROM file to execute
|
* @param romFd A FD to the ROM file to execute
|
||||||
|
* @param romType The type of the ROM file
|
||||||
*/
|
*/
|
||||||
void Execute(const std::string &romFile);
|
void Execute(const int romFd, const TitleFormat romType);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Creates a new process
|
* @brief Creates a new process
|
||||||
|
@ -38,7 +38,7 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
|
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
|
||||||
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
|
||||||
val log = File("${applicationInfo.dataDir}/skyline.log")
|
val log = File("${applicationInfo.dataDir}/skyline.log")
|
||||||
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_READ_WRITE)
|
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE)
|
||||||
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||||
game_view.holder.addCallback(this)
|
game_view.holder.addCallback(this)
|
||||||
//window.takeInputQueue(this)
|
//window.takeInputQueue(this)
|
||||||
|
@ -73,7 +73,7 @@ internal class LogAdapter internal constructor(val context: Context, val compact
|
|||||||
viewHolder.txtTitle!!.text = item.title
|
viewHolder.txtTitle!!.text = item.title
|
||||||
}
|
}
|
||||||
viewHolder.position = position
|
viewHolder.position = position
|
||||||
return view!!
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ViewHolder {
|
private class ViewHolder {
|
||||||
|
@ -19,7 +19,7 @@ enum class TitleFormat {
|
|||||||
|
|
||||||
internal class TitleEntry(var name: String, var author: String, var romType: TitleFormat, var valid: Boolean, @Transient var uri: Uri, @Transient var icon: Bitmap) : Serializable {
|
internal class TitleEntry(var name: String, var author: String, var romType: TitleFormat, var valid: Boolean, @Transient var uri: Uri, @Transient var icon: Bitmap) : Serializable {
|
||||||
constructor(context: Context, author: String, romType: TitleFormat, valid: Boolean, uri: Uri) : this("", author, romType, valid, uri, context.resources.getDrawable(R.drawable.ic_missing_icon, context.theme).toBitmap(256, 256)) {
|
constructor(context: Context, author: String, romType: TitleFormat, valid: Boolean, uri: Uri) : this("", author, romType, valid, uri, context.resources.getDrawable(R.drawable.ic_missing_icon, context.theme).toBitmap(256, 256)) {
|
||||||
context.contentResolver.query(uri, null, null, null, null)!!.use { cursor ->
|
context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
|
||||||
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||||
cursor.moveToFirst()
|
cursor.moveToFirst()
|
||||||
name = cursor.getString(nameIndex)
|
name = cursor.getString(nameIndex)
|
||||||
|
@ -12,12 +12,4 @@
|
|||||||
<item>2</item>
|
<item>2</item>
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="language_names">
|
|
||||||
<item>System Language</item>
|
|
||||||
<item>English</item>
|
|
||||||
</string-array>
|
|
||||||
<string-array name="language_values">
|
|
||||||
<item>sys</item>
|
|
||||||
<item>en</item>
|
|
||||||
</string-array>
|
|
||||||
</resources>
|
</resources>
|
@ -27,8 +27,6 @@
|
|||||||
<string name="log_compact">Compact Logs</string>
|
<string name="log_compact">Compact Logs</string>
|
||||||
<string name="log_compact_desc_on">Logs will be displayed in a compact form factor</string>
|
<string name="log_compact_desc_on">Logs will be displayed in a compact form factor</string>
|
||||||
<string name="log_compact_desc_off">The logs will be displayed in a verbose form factor</string>
|
<string name="log_compact_desc_off">The logs will be displayed in a verbose form factor</string>
|
||||||
<string name="localization">Localization</string>
|
|
||||||
<string name="localization_language">Language</string>
|
|
||||||
<string name="system">System</string>
|
<string name="system">System</string>
|
||||||
<string name="use_docked">Use Docked Mode</string>
|
<string name="use_docked">Use Docked Mode</string>
|
||||||
<string name="handheld_enabled">The system will emulate being in handheld mode</string>
|
<string name="handheld_enabled">The system will emulate being in handheld mode</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user