Fix GroupMutex and Clock Rescaling

This commit mainly fixes GroupMutex and clock rescaling. In addition, clock rescaling is no longer performed if the CNTFRQ_EL0 of the host device is same as that of the Switch (19.2MHz) which is fairly common on higher end devices.
This commit is contained in:
◱ PixelyIon 2020-02-11 12:04:22 +05:30 committed by ◱ PixelyIon
parent 003e9c5a01
commit 66d20a9429
13 changed files with 186 additions and 180 deletions

2
.gitignore vendored
View File

@ -95,4 +95,4 @@ captures/
.vscode/
# Discord plugin for IntelliJ IDEA
.idea\discord.xml
.idea/discord.xml

View File

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&amp;&amp;!file[Lightswitch]:*&amp;&amp;!file[app]:*" />
<scope name="SkylineCPP" pattern="file[app]:src/main/cpp//*&amp;&amp;!file[Skyline]:*&amp;&amp;!file[app]:*" />
</component>

View File

@ -10,12 +10,18 @@
</h1>
<p align="center">
<i>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 <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE">license file</a> for more information.</i><br/><br>
<i>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 <a href="https://github.com/skyline-emu/skyline/blob/master/LICENSE">license file</a> for more information.</i><br/><br>
</p>
### 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
[<img align="left" height="10%" width="10%" src="https://i.imgur.com/aOADoDM.png"/>](https://ryujinx.org/)
[**Ryujinx**](https://ryujinx.org/)<br>
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.
<br>
<br>
### Disclaimer
* Nintendo Switch is a trademark of Nintendo Co., Ltd.
* Android is a trademark of Google LLC.

View File

@ -49,7 +49,8 @@
<activity
android:name="emu.skyline.GameActivity"
android:configChanges="orientation|screenSize"
android:screenOrientation="landscape">
android:screenOrientation="landscape"
android:launchMode="singleInstance">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />

View File

@ -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<skyline::TitleFormat>(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<std::chrono::milliseconds>(end - start).count()));

View File

@ -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<gpu::GPU>(*this));
}
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = 0;
thread_local ThreadContext *DeviceState::ctx = 0;
thread_local std::shared_ptr<kernel::type::KThread> DeviceState::thread = nullptr;
thread_local ThreadContext *DeviceState::ctx = nullptr;
}

View File

@ -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<Group> flag = Group::None; //!< An atomic flag to hold which group holds the mutex
std::atomic<Group> next = Group::None; //!< An atomic flag to hold which group will hold the mutex next
std::atomic<u8> 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
};
/**

View File

@ -31,9 +31,11 @@ namespace skyline::gpu {
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
resolution.height = static_cast<u32>(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();

View File

@ -62,7 +62,7 @@ namespace skyline::gpu {
break;
}
}
sched_yield();
asm("yield");
}
struct {
u32 slot;

View File

@ -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<ThreadContext *>(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<const u16>(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<ThreadContext *>(thread->ctxMemory->kernel.address));
@ -151,67 +164,23 @@ namespace skyline {
state.logger->Debug("CPU Context:{}", regStr);
}
const std::array<u32, 18> 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<u32, 16> 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<u32, 18> 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<u32, 18> 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<u32> NCE::PatchCode(std::vector<u8> &code, u64 baseAddress, i64 offset) {
@ -232,6 +201,10 @@ namespace skyline {
reinterpret_cast<void *>(&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<instr::Svc *>(address);
auto instrMrs = reinterpret_cast<instr::Mrs *>(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<regs::X>(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<regs::X>(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);

View File

@ -278,10 +278,8 @@ namespace skyline::guest {
.sa_sigaction = reinterpret_cast<void (*)(int, struct siginfo *, void *)>(reinterpret_cast<void *>(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"

View File

@ -302,30 +302,33 @@ namespace skyline {
};
static_assert(sizeof(Movk) == sizeof(u32));
const std::array<u32, 4> MoveU64Reg(regs::X destReg, u64 value) {
const std::vector<u32> 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<u32, 4> instr;
std::vector<u32> 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<u32, 2> MoveU32Reg(regs::X destReg, u32 value) {
const std::vector<u32> MoveU32Reg(regs::X destReg, u32 value) {
union {
u32 val;
struct {
@ -334,11 +337,12 @@ namespace skyline {
};
} val;
val.val = value;
std::array<u32, 2> instr;
std::vector<u32> 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<u8>(destReg);
this->srcReg = static_cast<u8>(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));
}
}

View File

@ -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?) {