diff --git a/.gitignore b/.gitignore index 28e373e5..58a3f235 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,4 @@ captures/ .vscode/ # Discord plugin for IntelliJ IDEA -.idea\discord.xml +.idea/discord.xml diff --git a/.idea/scopes/SkylineCPP.xml b/.idea/scopes/SkylineCPP.xml index 179a6bef..3b743f30 100644 --- a/.idea/scopes/SkylineCPP.xml +++ b/.idea/scopes/SkylineCPP.xml @@ -1,3 +1,3 @@ - - \ No newline at end of file + + diff --git a/README.md b/README.md index d1b05753..0bba4be4 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,18 @@

- Skyline is an experimental emulator that runs on ARMv8 Android™ devices and emulates the functionality of a Nintendo Switch™ system. Skyline currently does not run any games, nor Homebrew. It's licensed under GPLv3, refer to the license file for more information.

+ Skyline is an experimental emulator that runs on ARMv8 Android™ devices and emulates the functionality of a Nintendo Switch™ system. It's licensed under GPLv3, refer to the license file for more information.

### Contact > You can contact the core developers of Skyline at our [Discord](https://discord.gg/XnbXNQM). If you have any questions, feel free to ask. +### Credit +[](https://ryujinx.org/) +[**Ryujinx**](https://ryujinx.org/)
+We've used Ryujinx throughout the project for reference, the amount of accuracy of their HLE kernel implementation is what makes them such an amazing reference. +
+
### Disclaimer * Nintendo Switch is a trademark of Nintendo Co., Ltd. * Android is a trademark of Google LLC. diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 43655bb9..51267723 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,7 +49,8 @@ + android:screenOrientation="landscape" + android:launchMode="singleInstance"> diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 917cc99a..c7db4637 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -44,12 +44,12 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env, logger->Info("Launching ROM {}", romString); env->ReleaseStringUTFChars(romJstring, romString); os.Execute(romFd, static_cast(romType)); - logger->Info("Emulation has ended"); } catch (std::exception &e) { logger->Error(e.what()); } catch (...) { logger->Error("An unknown exception has occurred"); } + logger->Info("Emulation has ended"); auto end = std::chrono::steady_clock::now(); logger->Info("Done in: {} ms", (std::chrono::duration_cast(end - start).count())); diff --git a/app/src/main/cpp/skyline/common.cpp b/app/src/main/cpp/skyline/common.cpp index 9adb342b..d8a68ae0 100644 --- a/app/src/main/cpp/skyline/common.cpp +++ b/app/src/main/cpp/skyline/common.cpp @@ -18,23 +18,37 @@ namespace skyline { void GroupMutex::lock(Group group) { auto none = Group::None; - constexpr u64 timeout = 1000; // The timeout in ns - auto start = utils::GetTimeNs(); - while (next != group && !next.compare_exchange_weak(none, group)) { - if (flag == group && ((utils::GetTimeNs() - start) > timeout)) { - num++; - return; - } + constexpr u64 timeout = 100; // The timeout in ns + auto end = utils::GetTimeNs() + timeout; + while (true) { + if (next == group) { + if (flag == group) { + std::lock_guard lock(mtx); + if (flag == group) { + auto groupT = group; + next.compare_exchange_strong(groupT, Group::None); + num++; + return; + } + } else + flag.compare_exchange_weak(none, group); + } else if (flag == group && (next == Group::None || utils::GetTimeNs() >= end)) { + std::lock_guard lock(mtx); + if (flag == group) { + num++; + return; + } + } else + next.compare_exchange_weak(none, group); + none = Group::None; asm volatile("yield"); } - while (flag != group && !flag.compare_exchange_weak(none, group)) - asm volatile("yield"); - num++; } void GroupMutex::unlock() { + std::lock_guard lock(mtx); if (!--num) - flag.exchange(Group::None); + flag.exchange(next); } Settings::Settings(const int preferenceFd) { @@ -118,6 +132,6 @@ namespace skyline { gpu = std::move(std::make_shared(*this)); } - thread_local std::shared_ptr DeviceState::thread = 0; - thread_local ThreadContext *DeviceState::ctx = 0; + thread_local std::shared_ptr DeviceState::thread = nullptr; + thread_local ThreadContext *DeviceState::ctx = nullptr; } diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index f7a6d9df..4ce173db 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -36,7 +36,7 @@ namespace skyline { constexpr u8 NumRegs = 30; //!< The amount of registers that ARMv8 has constexpr u32 TpidrroEl0 = 0x5E83; //!< ID of TPIDRRO_EL0 in MRS constexpr u32 CntfrqEl0 = 0x5F00; //!< ID of CNTFRQ_EL0 in MRS - constexpr u32 TegraX1Freq = 0x124F800; //!< The clock frequency of the Tegra X1 (19.2 MHz) + constexpr u32 TegraX1Freq = 19200000; //!< The clock frequency of the Tegra X1 (19.2 MHz) constexpr u32 CntpctEl0 = 0x5F01; //!< ID of CNTPCT_EL0 in MRS constexpr u32 CntvctEl0 = 0x5F02; //!< ID of CNTVCT_EL0 in MRS // Kernel @@ -101,18 +101,17 @@ namespace skyline { * @return The current time in nanoseconds */ inline u64 GetTimeNs() { - static u64 frequencyMs{}; - if (!frequencyMs) { - asm("MRS %0, CNTFRQ_EL0" : "=r"(frequencyMs)); - frequencyMs *= 1000000000; - } + constexpr uint64_t NsInSecond = 1000000000; + static u64 frequency{}; + if (!frequency) + asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); u64 ticks; asm("MRS %0, CNTVCT_EL0" : "=r"(ticks)); - return ticks / frequencyMs; + return ((ticks / frequency) * NsInSecond) + (((ticks % frequency) * NsInSecond + (frequency / 2)) / frequency); } /** - * @brief Aligns up a value to a multiple of twoB + * @brief Aligns up a value to a multiple of two * @tparam Type The type of the values * @param value The value to round up * @param multiple The multiple to round up to (Should be a multiple of 2) @@ -216,6 +215,7 @@ namespace skyline { std::atomic flag = Group::None; //!< An atomic flag to hold which group holds the mutex std::atomic next = Group::None; //!< An atomic flag to hold which group will hold the mutex next std::atomic num = 0; //!< An atomic u8 keeping track of how many users are holding the mutex + Mutex mtx; //!< A mutex to lock before changing of num and flag }; /** diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index cc2bc600..d6984297 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -31,9 +31,11 @@ namespace skyline::gpu { resolution.width = static_cast(ANativeWindow_getWidth(window)); resolution.height = static_cast(ANativeWindow_getHeight(window)); format = ANativeWindow_getFormat(window); + surfaceUpdate = false; + } else if (Surface == nullptr) { surfaceUpdate = true; - } else - surfaceUpdate = (Surface == nullptr); + return; + } if (!bufferQueue.displayQueue.empty()) { auto &buffer = bufferQueue.displayQueue.front(); bufferQueue.displayQueue.pop(); diff --git a/app/src/main/cpp/skyline/gpu/display.cpp b/app/src/main/cpp/skyline/gpu/display.cpp index 9f5e96bf..49c7b614 100644 --- a/app/src/main/cpp/skyline/gpu/display.cpp +++ b/app/src/main/cpp/skyline/gpu/display.cpp @@ -62,7 +62,7 @@ namespace skyline::gpu { break; } } - sched_yield(); + asm("yield"); } struct { u32 slot; diff --git a/app/src/main/cpp/skyline/nce.cpp b/app/src/main/cpp/skyline/nce.cpp index 8e23e364..369a766e 100644 --- a/app/src/main/cpp/skyline/nce.cpp +++ b/app/src/main/cpp/skyline/nce.cpp @@ -8,6 +8,7 @@ #include "nce.h" extern bool Halt; +extern jobject Surface; extern skyline::GroupMutex jniMtx; namespace skyline { @@ -16,10 +17,17 @@ namespace skyline { state.thread = state.process->threads.at(thread); state.ctx = reinterpret_cast(state.thread->ctxMemory->kernel.address); while (true) { - std::lock_guard jniGd(jniMtx); + asm("yield"); if (Halt) break; + if (!Surface) + continue; if (state.ctx->state == ThreadState::WaitKernel) { + std::lock_guard jniGd(jniMtx); + if (Halt) + break; + if (!Surface) + continue; const u16 svc = static_cast(state.ctx->commandId); try { if (kernel::svc::SvcTable[svc]) { @@ -43,11 +51,14 @@ namespace skyline { } catch (...) { state.logger->Error("An unknown exception has occurred"); } - if (thread == state.process->pid) { - jniMtx.lock(GroupMutex::Group::Group2); - state.os->KillThread(thread); - Halt = true; - jniMtx.unlock(); + if (!Halt) { + if (thread == state.process->pid) { + jniMtx.lock(GroupMutex::Group::Group2); + state.os->KillThread(thread); + Halt = true; + jniMtx.unlock(); + } else + state.os->KillThread(thread); } } @@ -60,15 +71,17 @@ namespace skyline { void NCE::Execute() { while (true) { - std::lock_guard jniGd(jniMtx); + std::lock_guard guard(jniMtx); if (Halt) break; state.os->serviceManager.Loop(); state.gpu->Loop(); } - jniMtx.lock(GroupMutex::Group::Group2); - Halt = true; - jniMtx.unlock(); + if (!Halt) { + jniMtx.lock(GroupMutex::Group::Group2); + Halt = true; + jniMtx.unlock(); + } } /** @@ -93,7 +106,7 @@ namespace skyline { } void NCE::ExecuteFunction(ThreadCall call, Registers &funcRegs) { - if(state.process->status == kernel::type::KProcess::Status::Exiting) + if (state.process->status == kernel::type::KProcess::Status::Exiting) throw exception("Executing function on Exiting process"); auto thread = state.thread ? state.thread : state.process->threads.at(state.process->pid); ExecuteFunctionCtx(call, funcRegs, reinterpret_cast(thread->ctxMemory->kernel.address)); @@ -151,67 +164,23 @@ namespace skyline { state.logger->Debug("CPU Context:{}", regStr); } - const std::array cntpctEl0X0 = { - 0xA9BF0BE1, // STP X1, X2, [SP, #-16]! - 0x3C9F0FE0, // STR Q0, [SP, #-16]! - 0x3C9F0FE1, // STR Q1, [SP, #-16]! - 0x3C9F0FE2, // STR Q2, [SP, #-16]! + const std::array CntpctEl0 = { + 0xD10083FF, // SUB SP, SP, #32 + 0xA90107E0, // STP X0, X1, [SP, #16] + 0xD28F0860, // MOV X0, #30787 + 0xF2AE3680, // MOVK X0, #29108, LSL #16 0xD53BE001, // MRS X1, CNTFRQ_EL0 - 0xD53BE042, // MRS X2, CNTVCT_EL0 - 0x9E630020, // UCVTF D0, X0 - 0xD2C9F001, // MOV X1, 87411174408192 - 0xF2E82E41, // MOVK X1, 0x4172, LSL 48 - 0x9E670022, // FMOV D2, X1 - 0x9E630041, // UCVTF D1, X1 - 0x1E621800, // FDIV D0, D0, D2 - 0x1E610800, // FMUL D0, D0, D1 - 0x9E790000, // FCVTZU X0, D0 - 0x3CC107E2, // LDR Q2, [SP], #16 - 0x3CC107E1, // LDR Q1, [SP], #16 - 0x3CC107E0, // LDR Q0, [SP], #16 - 0xA8C10BE1, // LDP X1, X2, [SP], #16 - }; - - const std::array cntpctEl0X1 = { - 0xA9BF0BE0, // STP X0, X2, [SP, #-16]! - 0x3C9F0FE0, // STR Q0, [SP, #-16]! - 0x3C9F0FE1, // STR Q1, [SP, #-16]! - 0x3C9F0FE2, // STR Q2, [SP, #-16]! - 0xD53BE000, // MRS X0, CNTFRQ_EL0 - 0xD53BE042, // MRS X2, CNTVCT_EL0 - 0x9E630020, // UCVTF D0, X0 - 0xD2C9F000, // MOV X0, 87411174408192 - 0xF2E82E40, // MOVK X0, 0x4172, LSL 48 - 0x9E670002, // FMOV D2, X0 - 0x9E630041, // UCVTF D1, X2 - 0x1E621800, // FDIV D0, D0, D2 - 0x1E610800, // FMUL D0, D0, D1 - 0x9E790001, // FCVTZU X0, D0 - 0x3CC107E2, // LDR Q2, [SP], #16 - 0x3CC107E1, // LDR Q1, [SP], #16 - 0x3CC107E0, // LDR Q0, [SP], #16 - 0xA8C10BE0, // LDP X0, X2, [SP], #16 - }; - - std::array cntpctEl0Xn = { - 0xA9BF07E0, // STP X0, X1, [SP, #-16]! - 0x3C9F0FE0, // STR Q0, [SP, #-16]! - 0x3C9F0FE1, // STR Q1, [SP, #-16]! - 0x3C9F0FE2, // STR Q2, [SP, #-16]! - 0xD53BE000, // MRS X0, CNTFRQ_EL0 - 0xD53BE041, // MRS X1, CNTVCT_EL0 - 0x9E630000, // UCVTF D0, X0 - 0xD2C9F000, // MOV X0, 87411174408192 - 0xF2E82E40, // MOVK X0, 0x4172, LSL 48 - 0x9E670002, // FMOV D2, X0 - 0x9E630021, // UCVTF D1, X1 - 0x1E621800, // FDIV D0, D0, D2 - 0x1E610800, // FMUL D0, D0, D1 - 0x00000000, // FCVTZU Xn, D0 (Set at runtime) - 0x3CC107E2, // LDR Q2, [SP], #16 - 0x3CC107E1, // LDR Q1, [SP], #16 - 0x3CC107E0, // LDR Q0, [SP], #16 - 0xA8C107E0, // LDP X0, X1, [SP], #16 + 0xF2CB5880, // MOVK X0, #23236, LSL #32 + 0xD345FC21, // LSR X1, X1, #5 + 0xF2E14F80, // MOVK X0, #2684, LSL #48 + 0x9BC07C21, // UMULH X1, X1, X0 + 0xD347FC21, // LSR X1, X1, #7 + 0xD53BE040, // MRS X0, CNTVCT_EL0 + 0x9AC10801, // UDIV X1, X0, X1 + 0x8B010421, // ADD X1, X1, X1, LSL #1 + 0xD37AE420, // LSL X0, X1, #6 + 0xF90003E0, // STR X0, [SP, #0] + 0xA94107E0, // LDP X0, X1, [SP, #16] }; std::vector NCE::PatchCode(std::vector &code, u64 baseAddress, i64 offset) { @@ -232,6 +201,10 @@ namespace skyline { reinterpret_cast(&guest::svcHandler), guest::svcHandlerSize); offset += guest::svcHandlerSize; + static u64 frequency{}; + if (!frequency) + asm("MRS %0, CNTFRQ_EL0" : "=r"(frequency)); + for (u32 *address = start; address < end; address++) { auto instrSvc = reinterpret_cast(address); auto instrMrs = reinterpret_cast(address); @@ -275,9 +248,9 @@ namespace skyline { strX0 = 0xF81F0FE0; // STR X0, [SP, #-16]! offset += sizeof(strX0); } - u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0 + const u32 mrsX0 = 0xD53BD040; // MRS X0, TPIDR_EL0 offset += sizeof(mrsX0); - u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256] + const u32 ldrTls = 0xF9408000; // LDR X0, [X0, #256] offset += sizeof(ldrTls); u32 movXn{}; u32 ldrX0{}; @@ -300,41 +273,41 @@ namespace skyline { if (ldrX0) patch.push_back(ldrX0); patch.push_back(bret.raw); - } else if (instrMrs->srcReg == constant::CntpctEl0) { - instr::B bjunc(offset); - if (instrMrs->destReg == 0) - offset += cntpctEl0X0.size() * sizeof(u32); - else if (instrMrs->destReg == 1) - offset += cntpctEl0X1.size() * sizeof(u32); - else - offset += cntpctEl0Xn.size() * sizeof(u32); - instr::B bret(-offset + sizeof(u32)); - offset += sizeof(bret); + } else if (frequency != constant::TegraX1Freq) { + if (instrMrs->srcReg == constant::CntpctEl0) { + instr::B bjunc(offset); + offset += CntpctEl0.size() * sizeof(u32); + instr::Ldr ldr(0xF94003E0); // LDR XOUT, [SP] + ldr.destReg = instrMrs->destReg; + offset += sizeof(ldr); + const u32 addSp = 0x910083FF; // ADD SP, SP, #32 + offset += sizeof(addSp); + instr::B bret(-offset + sizeof(u32)); + offset += sizeof(bret); - *address = bjunc.raw; - if (instrMrs->destReg == 0) - for (auto &instr : cntpctEl0X0) + *address = bjunc.raw; + for (const auto &instr : CntpctEl0) patch.push_back(instr); - else if (instrMrs->destReg == 1) - for (auto &instr : cntpctEl0X1) - patch.push_back(instr); - else { - cntpctEl0Xn[13] = instr::Fcvtzu(regs::X(instrMrs->destReg), 0).raw; - for (auto &instr : cntpctEl0Xn) + patch.push_back(ldr.raw); + patch.push_back(addSp); + patch.push_back(bret.raw); + } else if (instrMrs->srcReg == constant::CntfrqEl0) { + instr::B bjunc(offset); + auto movFreq = instr::MoveU32Reg(static_cast(instrMrs->destReg), constant::TegraX1Freq); + offset += sizeof(u32) * movFreq.size(); + instr::B bret(-offset + sizeof(u32)); + offset += sizeof(bret); + + *address = bjunc.raw; + for (auto &instr : movFreq) patch.push_back(instr); + patch.push_back(bret.raw); + } + } else { + if (instrMrs->srcReg == constant::CntpctEl0) { + instr::Mrs mrs(constant::CntvctEl0, regs::X(instrMrs->destReg)); + *address = mrs.raw; } - patch.push_back(bret.raw); - } else if (instrMrs->srcReg == constant::CntfrqEl0) { - instr::B bjunc(offset); - auto movFreq = instr::MoveU32Reg(static_cast(instrMrs->destReg), constant::TegraX1Freq); - offset += sizeof(u32) * movFreq.size(); - instr::B bret(-offset + sizeof(u32)); - offset += sizeof(bret); - - *address = bjunc.raw; - for (auto &instr : movFreq) - patch.push_back(instr); - patch.push_back(bret.raw); } } offset -= sizeof(u32); diff --git a/app/src/main/cpp/skyline/nce/guest.cpp b/app/src/main/cpp/skyline/nce/guest.cpp index e9a0c670..63226960 100644 --- a/app/src/main/cpp/skyline/nce/guest.cpp +++ b/app/src/main/cpp/skyline/nce/guest.cpp @@ -278,10 +278,8 @@ namespace skyline::guest { .sa_sigaction = reinterpret_cast(reinterpret_cast(signalHandler)), .sa_flags = SA_SIGINFO, }; - /* for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}) sigaction(signal, &sigact, nullptr); - */ ctx->state = ThreadState::Running; asm("MOV LR, %0\n\t" "MOV X0, %1\n\t" diff --git a/app/src/main/cpp/skyline/nce/instr.h b/app/src/main/cpp/skyline/nce/instr.h index 4460ba8b..baf170ff 100644 --- a/app/src/main/cpp/skyline/nce/instr.h +++ b/app/src/main/cpp/skyline/nce/instr.h @@ -302,30 +302,33 @@ namespace skyline { }; static_assert(sizeof(Movk) == sizeof(u32)); - const std::array MoveU64Reg(regs::X destReg, u64 value) { + const std::vector MoveU64Reg(regs::X destReg, u64 value) { union { u64 val; struct { u16 v0; u16 v16; u16 v32; - u16 v64; + u16 v48; }; } val; val.val = value; - std::array instr; + std::vector instr; instr::Movz mov0(destReg, val.v0, 0); - instr[0] = mov0.raw; + instr.push_back(mov0.raw); instr::Movk mov16(destReg, val.v16, 16); - instr[1] = mov16.raw; + if (val.v16) + instr.push_back(mov16.raw); instr::Movk mov32(destReg, val.v32, 32); - instr[2] = mov32.raw; - instr::Movk mov64(destReg, val.v64, 48); - instr[3] = mov64.raw; + if (val.v32) + instr.push_back(mov32.raw); + instr::Movk mov48(destReg, val.v48, 48); + if (val.v48) + instr.push_back(mov48.raw); return instr; } - const std::array MoveU32Reg(regs::X destReg, u32 value) { + const std::vector MoveU32Reg(regs::X destReg, u32 value) { union { u32 val; struct { @@ -334,11 +337,12 @@ namespace skyline { }; } val; val.val = value; - std::array instr; + std::vector instr; instr::Movz mov0(destReg, val.v0, 0); - instr[0] = mov0.raw; + instr.push_back(mov0.raw); instr::Movk mov16(destReg, val.v16, 16); - instr[1] = mov16.raw; + if (val.v16) + instr.push_back(mov16.raw); return instr; } @@ -398,44 +402,37 @@ namespace skyline { static_assert(sizeof(Mov) == sizeof(u32)); /** - * @brief A bit-field struct that encapsulates a FCVTZU (Scalar, Integer) instruction. See https://developer.arm.com/docs/ddi0602/d/simd-and-floating-point-instructions-alphabetic-order/fcvtzu-scalar-integer-floating-point-convert-to-unsigned-integer-rounding-toward-zero-scalar. + * @brief A bit-field struct that encapsulates a LDR (immediate) instruction. See https://developer.arm.com/docs/ddi0596/e/base-instructions-alphabetic-order/ldr-immediate-load-register-immediate. */ - struct Fcvtzu { + struct Ldr { public: /** - * @brief Creates a FCVTZU (Scalar, Integer) instruction - * @param destReg The destination Xn register to store the value in - * @param srcReg The source Dn register to retrieve the value from + * @brief Creates a LDR (immediate) instruction + * @param raw The raw value of the whole instruction */ - Fcvtzu(regs::X destReg, u8 srcReg) { - this->destReg = static_cast(destReg); - this->srcReg = static_cast(srcReg); - sig0 = 0xE40; - ftype = 1; - sig1 = 0x1E; - sf = 1; - } + Ldr(u32 raw) : raw(raw) {} /** * @brief Returns if the opcode is valid or not * @return If the opcode represents a valid FCVTZU instruction */ inline bool Verify() { - return (sig0 == 0xE40 && sig1 == 0x1E); + return (sig0 == 0x0 && sig1 == 0x1CA && sig2 == 0x1); } union { struct __attribute__((packed)) { u8 destReg : 5; u8 srcReg : 5; - u32 sig0 : 12; - u8 ftype : 2; - u8 sig1 : 7; - u8 sf : 1; + u8 sig0 : 2; + u16 imm : 9; + u16 sig1 : 9; + u8 x : 1; + u8 sig2 : 1; }; u32 raw{}; }; }; - static_assert(sizeof(Fcvtzu) == sizeof(u32)); + static_assert(sizeof(Ldr) == sizeof(u32)); } } diff --git a/app/src/main/java/emu/skyline/GameActivity.kt b/app/src/main/java/emu/skyline/GameActivity.kt index 188b0ed3..24fc6434 100644 --- a/app/src/main/java/emu/skyline/GameActivity.kt +++ b/app/src/main/java/emu/skyline/GameActivity.kt @@ -1,5 +1,6 @@ package emu.skyline +import android.content.Intent import android.net.Uri import android.os.Bundle import android.os.ParcelFileDescriptor @@ -19,47 +20,61 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal System.loadLibrary("skyline") // libskyline.so } - private lateinit var rom: Uri private lateinit var romFd: ParcelFileDescriptor private lateinit var preferenceFd: ParcelFileDescriptor private lateinit var logFd: ParcelFileDescriptor private var surface: Surface? = null private var inputQueue: Long = 0L + private var shouldFinish: Boolean = true private lateinit var gameThread: Thread private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int) private external fun setHalt(halt: Boolean) private external fun setSurface(surface: Surface?) + fun executeRom(rom : Uri) { + val romType = getTitleFormat(rom, contentResolver).ordinal + romFd = contentResolver.openFileDescriptor(rom, "r")!! + gameThread = Thread { + while ((surface == null)) + Thread.yield() + executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd) + if (shouldFinish) + runOnUiThread { finish() } + } + gameThread.start() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.game_activity) - rom = intent.data!! - val romType = getTitleFormat(rom, contentResolver).ordinal - romFd = contentResolver.openFileDescriptor(rom, "r")!! val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml") preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE) val log = File("${applicationInfo.dataDir}/skyline.log") logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or ParcelFileDescriptor.MODE_READ_WRITE) window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN) game_view.holder.addCallback(this) - //window.takeInputQueue(this) - gameThread = Thread { - while ((surface == null)) - Thread.yield() - executeRom(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, logFd.fd) - runOnUiThread { finish() } - } - gameThread.start() + executeRom(intent.data!!) + } + + override fun onNewIntent(intent: Intent?) { + shouldFinish = false + setHalt(true) + gameThread.join() + shouldFinish = true + romFd.close() + executeRom(intent?.data!!) + super.onNewIntent(intent) } override fun onDestroy() { - super.onDestroy() + shouldFinish = false setHalt(true) gameThread.join() romFd.close() preferenceFd.close() logFd.close() + super.onDestroy() } override fun surfaceCreated(holder: SurfaceHolder?) {