diff --git a/Makefile b/Makefile
index c8f2e846..26e4c3b3 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
-SOURCES := source source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm
+SOURCES := source source/spl source/spl/smc source/updater source/patcher source/map source/rnd source/util source/sm source/cfg source/pm source/hid source/ldr
DATA := data
INCLUDES := include
diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp
index 5e2da836..4227326d 100644
--- a/include/stratosphere.hpp
+++ b/include/stratosphere.hpp
@@ -45,5 +45,8 @@
#include "stratosphere/on_crash.hpp"
+#include "stratosphere/cfg.hpp"
+#include "stratosphere/hid.hpp"
+#include "stratosphere/pm.hpp"
#include "stratosphere/rnd.hpp"
#include "stratosphere/util.hpp"
\ No newline at end of file
diff --git a/include/stratosphere/cfg.hpp b/include/stratosphere/cfg.hpp
new file mode 100644
index 00000000..e78dd8a1
--- /dev/null
+++ b/include/stratosphere/cfg.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "cfg/cfg_api.hpp"
diff --git a/include/stratosphere/cfg/cfg_api.hpp b/include/stratosphere/cfg/cfg_api.hpp
new file mode 100644
index 00000000..62431da2
--- /dev/null
+++ b/include/stratosphere/cfg/cfg_api.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "../ncm/ncm_types.hpp"
+
+namespace sts::cfg {
+
+ /* Is the current proces privileged? */
+ bool IsInitialProcess();
+
+ /* SD card configuration. */
+ bool IsSdCardInitialized();
+ void WaitSdCardInitialized();
+
+ /* Override key utilities. */
+ bool IsTitleOverrideKeyHeld(ncm::TitleId title_id);
+ bool IsHblOverrideKeyHeld(ncm::TitleId title_id);
+ void GetOverrideKeyHeldStatus(bool *out_hbl, bool *out_title, ncm::TitleId title_id);
+ bool IsCheatEnableKeyHeld(ncm::TitleId title_id);
+
+ /* Flag utilities. */
+ bool HasFlag(ncm::TitleId title_id, const char *flag);
+ bool HasTitleSpecificFlag(ncm::TitleId title_id, const char *flag);
+ bool HasGlobalFlag(const char *flag);
+
+ /* HBL Configuration utilities. */
+ bool IsHblTitleId(ncm::TitleId title_id);
+ bool HasHblFlag(const char *flag);
+ const char *GetHblPath();
+
+}
diff --git a/include/stratosphere/hid.hpp b/include/stratosphere/hid.hpp
new file mode 100644
index 00000000..bea31e2c
--- /dev/null
+++ b/include/stratosphere/hid.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "hid/hid_api.hpp"
\ No newline at end of file
diff --git a/include/stratosphere/hid/hid_api.hpp b/include/stratosphere/hid/hid_api.hpp
new file mode 100644
index 00000000..b1b97c63
--- /dev/null
+++ b/include/stratosphere/hid/hid_api.hpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+namespace sts::hid {
+
+ /* Key API. */
+ Result GetKeysHeld(u64 *out);
+
+}
diff --git a/include/stratosphere/ipc/ipc_special.hpp b/include/stratosphere/ipc/ipc_special.hpp
index 0588631b..044a7f49 100644
--- a/include/stratosphere/ipc/ipc_special.hpp
+++ b/include/stratosphere/ipc/ipc_special.hpp
@@ -52,6 +52,10 @@ struct MovedHandle : public IpcHandle {
MovedHandle(Handle h) {
this->handle = h;
}
+
+ Handle GetValue() const {
+ return this->handle;
+ }
};
/* Represents a copied handle. */
@@ -67,6 +71,10 @@ struct CopiedHandle : public IpcHandle {
CopiedHandle(Handle h) {
this->handle = h;
}
+
+ Handle GetValue() const {
+ return this->handle;
+ }
};
template <>
diff --git a/include/stratosphere/ldr.hpp b/include/stratosphere/ldr.hpp
new file mode 100644
index 00000000..fc56cb76
--- /dev/null
+++ b/include/stratosphere/ldr.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "ldr/ldr_types.hpp"
diff --git a/include/stratosphere/ldr/ldr_pm_api.hpp b/include/stratosphere/ldr/ldr_pm_api.hpp
new file mode 100644
index 00000000..823c34b1
--- /dev/null
+++ b/include/stratosphere/ldr/ldr_pm_api.hpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "ldr_types.hpp"
+
+namespace sts::ldr::pm {
+
+ /* Process Manager API. */
+ Result CreateProcess(Handle *out, PinId pin_id, u32 flags, Handle reslimit);
+ Result GetProgramInfo(ProgramInfo *out, const ncm::TitleLocation &loc);
+ Result PinTitle(PinId *out, const ncm::TitleLocation &loc);
+ Result UnpinTitle(PinId pin_id);
+ Result HasLaunchedTitle(bool *out, ncm::TitleId title_id);
+
+}
diff --git a/include/stratosphere/ldr/ldr_types.hpp b/include/stratosphere/ldr/ldr_types.hpp
new file mode 100644
index 00000000..3efc5726
--- /dev/null
+++ b/include/stratosphere/ldr/ldr_types.hpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+#include "../ncm/ncm_types.hpp"
+
+namespace sts::ldr {
+
+ /* General types. */
+ struct ProgramInfo {
+ u8 main_thread_priority;
+ u8 default_cpu_id;
+ u16 flags;
+ u32 main_thread_stack_size;
+ ncm::TitleId title_id;
+ u32 acid_sac_size;
+ u32 aci_sac_size;
+ u32 acid_fac_size;
+ u32 aci_fah_size;
+ u8 ac_buffer[0x3E0];
+ };
+ static_assert(sizeof(ProgramInfo) == 0x400, "ProgramInfo definition!");
+
+ enum ProgramInfoFlag {
+ ProgramInfoFlag_SystemModule = (0 << 0),
+ ProgramInfoFlag_Application = (1 << 0),
+ ProgramInfoFlag_Applet = (2 << 0),
+ ProgramInfoFlag_InvalidType = (3 << 0),
+ ProgramInfoFlag_ApplicationTypeMask = (3 << 0),
+
+ ProgramInfoFlag_AllowDebug = (1 << 2),
+ };
+
+ enum CreateProcessFlag {
+ CreateProcessFlag_EnableDebug = (1 << 0),
+ CreateProcessFlag_DisableAslr = (1 << 1),
+ };
+
+ struct ProgramArguments {
+ u32 allocated_size;
+ u32 arguments_size;
+ u8 reserved[0x18];
+ u8 arguments[];
+ };
+ static_assert(sizeof(ProgramArguments) == 0x20, "ProgramArguments definition!");
+
+ struct PinId {
+ u64 value;
+ };
+
+ inline bool operator==(const PinId &lhs, const PinId &rhs) {
+ return lhs.value == rhs.value;
+ }
+
+ inline bool operator!=(const PinId &lhs, const PinId &rhs) {
+ return lhs.value != rhs.value;
+ }
+ static_assert(sizeof(PinId) == sizeof(u64) && std::is_pod::value, "PinId definition!");
+
+ /* Import ModuleInfo from libnx. */
+ using ModuleInfo = ::LoaderModuleInfo;
+
+ /* NSO types. */
+ struct NsoHeader {
+ static constexpr u32 Magic = 0x304F534E;
+ enum Segment : size_t {
+ Segment_Text = 0,
+ Segment_Ro = 1,
+ Segment_Rw = 2,
+ Segment_Count,
+ };
+
+ enum Flag : u32 {
+ Flag_CompressedText = (1 << 0),
+ Flag_CompressedRo = (1 << 1),
+ Flag_CompressedRw = (1 << 2),
+ Flag_CheckHashText = (1 << 3),
+ Flag_CheckHashRo = (1 << 4),
+ Flag_CheckHashRw = (1 << 5),
+ };
+
+ struct SegmentInfo {
+ u32 file_offset;
+ u32 dst_offset;
+ u32 size;
+ u32 reserved;
+ };
+
+ u32 magic;
+ u32 version;
+ u32 reserved_08;
+ u32 flags;
+ union {
+ struct {
+ u32 text_file_offset;
+ u32 text_dst_offset;
+ u32 text_size;
+ u32 unk_file_offset;
+ u32 ro_file_offset;
+ u32 ro_dst_offset;
+ u32 ro_size;
+ u32 unk_size;
+ u32 rw_file_offset;
+ u32 rw_dst_offset;
+ u32 rw_size;
+ u32 bss_size;
+ };
+ SegmentInfo segments[Segment_Count];
+ };
+ u8 build_id[sizeof(ModuleInfo::build_id)];
+ union {
+ u32 compressed_sizes[Segment_Count];
+ struct {
+ u32 text_compressed_size;
+ u32 ro_compressed_size;
+ u32 rw_compressed_size;
+ };
+ };
+ u8 reserved_6C[0x34];
+ union {
+ u8 segment_hashes[Segment_Count][SHA256_HASH_SIZE];
+ struct {
+ u8 text_hash[SHA256_HASH_SIZE];
+ u8 ro_hash[SHA256_HASH_SIZE];
+ u8 rw_hash[SHA256_HASH_SIZE];
+ };
+ };
+ };
+ static_assert(sizeof(NsoHeader) == 0x100 && std::is_pod::value, "NsoHeader definition!");
+
+ /* NPDM types. */
+ struct Aci {
+ static constexpr u32 Magic = 0x30494341;
+
+ u32 magic;
+ u8 reserved_04[0xC];
+ ncm::TitleId title_id;
+ u8 reserved_18[0x8];
+ u32 fah_offset;
+ u32 fah_size;
+ u32 sac_offset;
+ u32 sac_size;
+ u32 kac_offset;
+ u32 kac_size;
+ u8 reserved_38[0x8];
+ };
+ static_assert(sizeof(Aci) == 0x40 && std::is_pod::value, "Aci definition!");
+
+ struct Acid {
+ static constexpr u32 Magic = 0x44494341;
+
+ enum AcidFlag {
+ AcidFlag_Production = (1 << 0),
+ AcidFlag_UnqualifiedApproval = (1 << 1),
+
+ AcidFlag_DeprecatedUseSecureMemory = (1 << 2),
+
+ AcidFlag_PoolPartitionShift = 2,
+ AcidFlag_PoolPartitionMask = (3 << AcidFlag_PoolPartitionShift),
+ };
+
+ enum PoolPartition {
+ PoolPartition_Application = 0,
+ PoolPartition_Applet = 1,
+ PoolPartition_System = 2,
+ PoolPartition_SystemNonSecure = 3,
+ };
+
+ u8 signature[0x100];
+ u8 modulus[0x100];
+ u32 magic;
+ u32 size;
+ u8 version;
+ u8 reserved_209[3];
+ u32 flags;
+ ncm::TitleId title_id_min;
+ ncm::TitleId title_id_max;
+ u32 fac_offset;
+ u32 fac_size;
+ u32 sac_offset;
+ u32 sac_size;
+ u32 kac_offset;
+ u32 kac_size;
+ u8 reserved_238[0x8];
+ };
+ static_assert(sizeof(Acid) == 0x240 && std::is_pod::value, "Acid definition!");
+
+ struct Npdm {
+ static constexpr u32 Magic = 0x4154454D;
+
+ enum MetaFlag {
+ MetaFlag_Is64Bit = (1 << 0),
+
+ MetaFlag_AddressSpaceTypeShift = 1,
+ MetaFlag_AddressSpaceTypeMask = (7 << MetaFlag_AddressSpaceTypeShift),
+
+ MetaFlag_OptimizeMemoryAllocation = (1 << 4),
+ };
+
+ enum AddressSpaceType {
+ AddressSpaceType_32Bit = 0,
+ AddressSpaceType_64BitDeprecated = 1,
+ AddressSpaceType_32BitWithoutAlias = 2,
+ AddressSpaceType_64Bit = 3,
+ };
+
+ u32 magic;
+ u8 reserved_04[8];
+ u8 flags;
+ u8 reserved_0D;
+ u8 main_thread_priority;
+ u8 default_cpu_id;
+ u8 reserved_10[4];
+ u32 system_resource_size;
+ u32 version;
+ u32 main_thread_stack_size;
+ char title_name[0x10];
+ char product_code[0x10];
+ u8 reserved_40[0x30];
+ u32 aci_offset;
+ u32 aci_size;
+ u32 acid_offset;
+ u32 acid_size;
+ };
+ static_assert(sizeof(Npdm) == 0x80 && std::is_pod::value, "Npdm definition!");
+
+}
diff --git a/include/stratosphere/map/map_types.hpp b/include/stratosphere/map/map_types.hpp
index f874696b..28a0b8d1 100644
--- a/include/stratosphere/map/map_types.hpp
+++ b/include/stratosphere/map/map_types.hpp
@@ -33,6 +33,13 @@ namespace sts::map {
uintptr_t aslr_end;
};
+ static constexpr uintptr_t AslrBase32Bit = 0x0000200000ul;
+ static constexpr size_t AslrSize32Bit = 0x003FE00000ul;
+ static constexpr uintptr_t AslrBase64BitDeprecated = 0x0008000000ul;
+ static constexpr size_t AslrSize64BitDeprecated = 0x0078000000ul;
+ static constexpr uintptr_t AslrBase64Bit = 0x0008000000ul;
+ static constexpr size_t AslrSize64Bit = 0x7FF8000000ul;
+
class AutoCloseMap {
private:
Handle process_handle;
diff --git a/include/stratosphere/ncm.hpp b/include/stratosphere/ncm.hpp
new file mode 100644
index 00000000..ec0830de
--- /dev/null
+++ b/include/stratosphere/ncm.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "ncm/ncm_types.hpp"
diff --git a/include/stratosphere/ncm/ncm_types.hpp b/include/stratosphere/ncm/ncm_types.hpp
new file mode 100644
index 00000000..e86295a2
--- /dev/null
+++ b/include/stratosphere/ncm/ncm_types.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+
+namespace sts::ncm {
+
+ /* Storage IDs. */
+ enum class StorageId : u8 {
+ None = 0,
+ Host = 1,
+ GameCard = 2,
+ NandSystem = 3,
+ NandUser = 4,
+ SdCard = 5,
+ };
+
+ /* Title IDs. */
+ struct TitleId {
+ u64 value;
+
+ inline explicit operator u64() const {
+ return this->value;
+ }
+ };
+ static constexpr TitleId InvalidTitleId = {};
+
+ inline bool operator==(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value == rhs.value;
+ }
+
+ inline bool operator!=(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value != rhs.value;
+ }
+
+ inline bool operator<(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value < rhs.value;
+ }
+
+ inline bool operator<=(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value <= rhs.value;
+ }
+
+ inline bool operator>(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value > rhs.value;
+ }
+
+ inline bool operator>=(const TitleId &lhs, const TitleId &rhs) {
+ return lhs.value >= rhs.value;
+ }
+
+ static_assert(sizeof(TitleId) == sizeof(u64) && std::is_pod::value, "TitleId definition!");
+
+ /* Title Location. */
+ struct TitleLocation {
+ TitleId title_id;
+ u8 storage_id;
+ };
+
+ constexpr TitleLocation MakeTitleLocation(TitleId title_id, StorageId storage_id) {
+ TitleLocation loc = { .title_id = title_id, .storage_id = static_cast(storage_id) };
+ return loc;
+ }
+
+ static_assert(sizeof(TitleLocation) == 0x10 && std::is_pod::value, "TitleLocation definition!");
+
+}
diff --git a/include/stratosphere/pm.hpp b/include/stratosphere/pm.hpp
new file mode 100644
index 00000000..877cdc1b
--- /dev/null
+++ b/include/stratosphere/pm.hpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "pm/pm_info_api.hpp"
\ No newline at end of file
diff --git a/include/stratosphere/pm/pm_info_api.hpp b/include/stratosphere/pm/pm_info_api.hpp
new file mode 100644
index 00000000..29f8dc89
--- /dev/null
+++ b/include/stratosphere/pm/pm_info_api.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+namespace sts::pm::info {
+
+ /* Information API. */
+ Result GetTitleId(u64 *out_title_id, u64 process_id);
+ Result GetProcessId(u64 *out_process_id, u64 title_id);
+ Result HasLaunchedTitle(bool *out, u64 title_id);
+
+ /* Information convenience API. */
+ bool HasLaunchedTitle(u64 title_id);
+
+}
diff --git a/include/stratosphere/results/loader_results.hpp b/include/stratosphere/results/loader_results.hpp
index 3bc5e435..7803a68c 100644
--- a/include/stratosphere/results/loader_results.hpp
+++ b/include/stratosphere/results/loader_results.hpp
@@ -26,7 +26,7 @@ static constexpr Result ResultLoaderInvalidMeta = MAKERESULT(Module_Lo
static constexpr Result ResultLoaderInvalidNso = MAKERESULT(Module_Loader, 5);
static constexpr Result ResultLoaderInvalidPath = MAKERESULT(Module_Loader, 6);
static constexpr Result ResultLoaderTooManyProcesses = MAKERESULT(Module_Loader, 7);
-static constexpr Result ResultLoaderProcessNotRegistered = MAKERESULT(Module_Loader, 8);
+static constexpr Result ResultLoaderNotPinned = MAKERESULT(Module_Loader, 8);
static constexpr Result ResultLoaderInvalidProgramId = MAKERESULT(Module_Loader, 9);
static constexpr Result ResultLoaderInvalidVersion = MAKERESULT(Module_Loader, 10);
diff --git a/include/stratosphere/results/utilities.h b/include/stratosphere/results/utilities.h
index 2638dc09..9a3d6c9a 100644
--- a/include/stratosphere/results/utilities.h
+++ b/include/stratosphere/results/utilities.h
@@ -8,6 +8,7 @@
#include
#ifdef __cplusplus
+#include
extern "C" {
#endif
diff --git a/include/stratosphere/title_ids.hpp b/include/stratosphere/title_ids.hpp
index 86dcfc9e..52531d5d 100644
--- a/include/stratosphere/title_ids.hpp
+++ b/include/stratosphere/title_ids.hpp
@@ -187,13 +187,13 @@ static inline bool TitleIdIsSystem(const u64 title_id) {
}
static inline bool TitleIdIsArchive(const u64 title_id) {
- return TitleId_ArchiveStart <= title_id && title_id <= TitleId_ArchiveEnd;
+ return TitleId_ArchiveStart <= title_id && title_id <= TitleId_ArchiveEnd;
}
static inline bool TitleIdIsApplet(const u64 title_id) {
- return TitleId_AppletStart <= title_id && title_id <= TitleId_AppletEnd;
+ return TitleId_AppletStart <= title_id && title_id <= TitleId_AppletEnd;
}
static inline bool TitleIdIsApplication(const u64 title_id) {
- return TitleId_ApplicationStart <= title_id && title_id <= TitleId_ApplicationEnd;
+ return TitleId_ApplicationStart <= title_id && title_id <= TitleId_ApplicationEnd;
}
\ No newline at end of file
diff --git a/source/cfg/cfg_flags.cpp b/source/cfg/cfg_flags.cpp
new file mode 100644
index 00000000..48b8ef6b
--- /dev/null
+++ b/source/cfg/cfg_flags.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+
+namespace sts::cfg {
+
+ namespace {
+
+ /* Helper. */
+ bool HasFlagFile(const char *flag_path) {
+ /* All flags are not present until the SD card is. */
+ if (!IsSdCardInitialized()) {
+ return false;
+ }
+
+ /* Mount the SD card. */
+ FsFileSystem sd_fs = {};
+ if (R_FAILED(fsMountSdcard(&sd_fs))) {
+ return false;
+ }
+ ON_SCOPE_EXIT { serviceClose(&sd_fs.s); };
+
+ /* Open the file. */
+ FsFile flag_file;
+ if (R_FAILED(fsFsOpenFile(&sd_fs, flag_path, FS_OPEN_READ, &flag_file))) {
+ return false;
+ }
+ fsFileClose(&flag_file);
+
+ return true;
+ }
+
+ }
+
+ /* Flag utilities. */
+ bool HasFlag(ncm::TitleId title_id, const char *flag) {
+ return HasTitleSpecificFlag(title_id, flag) || (IsHblTitleId(title_id) && HasHblFlag(flag));
+ }
+
+ bool HasTitleSpecificFlag(ncm::TitleId title_id, const char *flag) {
+ char title_flag[FS_MAX_PATH];
+ std::snprintf(title_flag, sizeof(title_flag) - 1, "/atmosphere/titles/%016lx/flags/%s.flag", static_cast(title_id), flag);
+ return HasFlagFile(title_flag);
+ }
+
+ bool HasGlobalFlag(const char *flag) {
+ char title_flag[FS_MAX_PATH];
+ std::snprintf(title_flag, sizeof(title_flag) - 1, "/atmosphere/flags/%s.flag", flag);
+ return HasFlagFile(title_flag);
+ }
+
+ bool HasHblFlag(const char *flag) {
+ char hbl_flag[0x100];
+ std::snprintf(hbl_flag, sizeof(hbl_flag) - 1, "hbl_%s", flag);
+ return HasGlobalFlag(hbl_flag);
+ }
+
+}
diff --git a/source/cfg/cfg_override.cpp b/source/cfg/cfg_override.cpp
new file mode 100644
index 00000000..e6f1814a
--- /dev/null
+++ b/source/cfg/cfg_override.cpp
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace sts::cfg {
+
+ namespace {
+
+ /* Types. */
+ struct OverrideKey {
+ u64 key_combination;
+ bool override_by_default;
+ };
+
+ struct HblOverrideConfig {
+ OverrideKey override_key;
+ ncm::TitleId title_id;
+ bool override_any_app;
+ };
+
+ struct TitleSpecificOverrideConfig {
+ OverrideKey override_key;
+ OverrideKey cheat_enable_key;
+ };
+
+ /* Override globals. */
+ OverrideKey g_default_override_key = {
+ .key_combination = KEY_L,
+ .override_by_default = true,
+ };
+
+ OverrideKey g_default_cheat_enable_key = {
+ .key_combination = KEY_L,
+ .override_by_default = true,
+ };
+
+ HblOverrideConfig g_hbl_override_config = {
+ .override_key = {
+ .key_combination = KEY_R,
+ .override_by_default = false,
+ },
+ .title_id = {TitleId_AppletPhotoViewer},
+ .override_any_app = true,
+ };
+
+ char g_hbl_sd_path[0x100] = "/atmosphere/hbl.nsp";
+
+ /* Helpers. */
+ OverrideKey ParseOverrideKey(const char *value) {
+ OverrideKey cfg = {};
+
+ /* Parse on by default. */
+ if (value[0] == '!') {
+ cfg.override_by_default = true;
+ value++;
+ }
+
+ /* Parse key combination. */
+ if (strcasecmp(value, "A") == 0) {
+ cfg.key_combination = KEY_A;
+ } else if (strcasecmp(value, "B") == 0) {
+ cfg.key_combination = KEY_B;
+ } else if (strcasecmp(value, "X") == 0) {
+ cfg.key_combination = KEY_X;
+ } else if (strcasecmp(value, "Y") == 0) {
+ cfg.key_combination = KEY_Y;
+ } else if (strcasecmp(value, "LS") == 0) {
+ cfg.key_combination = KEY_LSTICK;
+ } else if (strcasecmp(value, "RS") == 0) {
+ cfg.key_combination = KEY_RSTICK;
+ } else if (strcasecmp(value, "L") == 0) {
+ cfg.key_combination = KEY_L;
+ } else if (strcasecmp(value, "R") == 0) {
+ cfg.key_combination = KEY_R;
+ } else if (strcasecmp(value, "ZL") == 0) {
+ cfg.key_combination = KEY_ZL;
+ } else if (strcasecmp(value, "ZR") == 0) {
+ cfg.key_combination = KEY_ZR;
+ } else if (strcasecmp(value, "PLUS") == 0) {
+ cfg.key_combination = KEY_PLUS;
+ } else if (strcasecmp(value, "MINUS") == 0) {
+ cfg.key_combination = KEY_MINUS;
+ } else if (strcasecmp(value, "DLEFT") == 0) {
+ cfg.key_combination = KEY_DLEFT;
+ } else if (strcasecmp(value, "DUP") == 0) {
+ cfg.key_combination = KEY_DUP;
+ } else if (strcasecmp(value, "DRIGHT") == 0) {
+ cfg.key_combination = KEY_DRIGHT;
+ } else if (strcasecmp(value, "DDOWN") == 0) {
+ cfg.key_combination = KEY_DDOWN;
+ } else if (strcasecmp(value, "SL") == 0) {
+ cfg.key_combination = KEY_SL;
+ } else if (strcasecmp(value, "SR") == 0) {
+ cfg.key_combination = KEY_SR;
+ }
+
+ return cfg;
+ }
+
+ int LoaderIniHandler(void *user, const char *section, const char *name, const char *value) {
+ /* Taken and modified, with love, from Rajkosto's implementation. */
+ if (strcasecmp(section, "hbl_config") == 0) {
+ if (strcasecmp(name, "title_id") == 0) {
+ u64 override_tid = strtoul(value, NULL, 16);
+ if (override_tid != 0) {
+ g_hbl_override_config.title_id = {override_tid};
+ }
+ } else if (strcasecmp(name, "path") == 0) {
+ while (*value == '/' || *value == '\\') {
+ value++;
+ }
+ std::snprintf(g_hbl_sd_path, sizeof(g_hbl_sd_path) - 1, "/%s", value);
+ g_hbl_sd_path[sizeof(g_hbl_sd_path) - 1] = '\0';
+ } else if (strcasecmp(name, "override_key") == 0) {
+ g_hbl_override_config.override_key = ParseOverrideKey(value);
+ } else if (strcasecmp(name, "override_any_app") == 0) {
+ if (strcasecmp(value, "true") == 0 || strcasecmp(value, "1") == 0) {
+ g_hbl_override_config.override_any_app = true;
+ } else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "0") == 0) {
+ g_hbl_override_config.override_any_app = false;
+ } else {
+ /* I guess we default to not changing the value? */
+ }
+ }
+ } else if (strcasecmp(section, "default_config") == 0) {
+ if (strcasecmp(name, "override_key") == 0) {
+ g_default_override_key = ParseOverrideKey(value);
+ } else if (strcasecmp(name, "cheat_enable_key") == 0) {
+ g_default_cheat_enable_key = ParseOverrideKey(value);
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+ }
+
+ int TitleSpecificIniHandler(void *user, const char *section, const char *name, const char *value) {
+ TitleSpecificOverrideConfig *config = reinterpret_cast(user);
+
+ if (strcasecmp(section, "override_config") == 0) {
+ if (strcasecmp(name, "override_key") == 0) {
+ config->override_key = ParseOverrideKey(value);
+ } else if (strcasecmp(name, "cheat_enable_key") == 0) {
+ config->cheat_enable_key = ParseOverrideKey(value);
+ }
+ } else {
+ return 0;
+ }
+ return 1;
+ }
+
+ bool IsOverrideKeyHeld(OverrideKey *cfg) {
+ u64 kHeld = 0;
+ bool keys_triggered = (R_SUCCEEDED(hid::GetKeysHeld(&kHeld)) && ((kHeld & cfg->key_combination) != 0));
+ return IsSdCardInitialized() && (cfg->override_by_default ^ keys_triggered);
+ }
+
+ void ParseIniFile(util::ini::Handler handler, const char *path, void *user_ctx) {
+ /* Mount the SD card. */
+ FsFileSystem sd_fs = {};
+ if (R_FAILED(fsMountSdcard(&sd_fs))) {
+ return;
+ }
+ ON_SCOPE_EXIT { serviceClose(&sd_fs.s); };
+
+ /* Open the file. */
+ FsFile config_file;
+ if (R_FAILED(fsFsOpenFile(&sd_fs, path, FS_OPEN_READ, &config_file))) {
+ return;
+ }
+ ON_SCOPE_EXIT { fsFileClose(&config_file); };
+
+ /* Parse the config. */
+ util::ini::ParseFile(&config_file, user_ctx, handler);
+ }
+
+ void RefreshLoaderConfiguration() {
+ ParseIniFile(LoaderIniHandler, "/atmosphere/loader.ini", nullptr);
+ }
+
+ TitleSpecificOverrideConfig GetTitleOverrideConfig(ncm::TitleId title_id) {
+ char path[FS_MAX_PATH];
+ std::snprintf(path, sizeof(path) - 1, "/atmosphere/titles/%016lx/config.ini", static_cast(title_id));
+
+ TitleSpecificOverrideConfig config = {
+ .override_key = g_default_override_key,
+ .cheat_enable_key = g_default_cheat_enable_key,
+ };
+ ParseIniFile(TitleSpecificIniHandler, path, &config);
+ return config;
+ }
+
+ }
+
+ bool IsHblOverrideKeyHeld(ncm::TitleId title_id) {
+ /* If the SD card isn't initialized, we can't override. */
+ if (!IsSdCardInitialized()) {
+ return false;
+ }
+
+ /* For system modules and anything launched before the home menu, always override. */
+ if (static_cast(title_id) < TitleId_AppletStart || !pm::info::HasLaunchedTitle(TitleId_AppletQlaunch)) {
+ return true;
+ }
+
+ /* Unconditionally refresh loader.ini contents. */
+ RefreshLoaderConfiguration();
+
+ /* Check HBL config. */
+ return IsHblTitleId(title_id) && IsOverrideKeyHeld(&g_hbl_override_config.override_key);
+ }
+
+ bool IsTitleOverrideKeyHeld(ncm::TitleId title_id) {
+ /* If the SD card isn't initialized, we can't override. */
+ if (!IsSdCardInitialized()) {
+ return false;
+ }
+
+ /* For system modules and anything launched before the home menu, always override. */
+ if (static_cast(title_id) < TitleId_AppletStart || !pm::info::HasLaunchedTitle(TitleId_AppletQlaunch)) {
+ return true;
+ }
+
+ /* Unconditionally refresh loader.ini contents. */
+ RefreshLoaderConfiguration();
+
+ TitleSpecificOverrideConfig title_cfg = GetTitleOverrideConfig(title_id);
+ return IsOverrideKeyHeld(&title_cfg.override_key);
+ }
+
+ void GetOverrideKeyHeldStatus(bool *out_hbl, bool *out_title, ncm::TitleId title_id) {
+
+ /* If the SD card isn't initialized, we can't override. */
+ if (!IsSdCardInitialized()) {
+ *out_hbl = false;
+ *out_title = false;
+ return;
+ }
+
+ /* For system modules and anything launched before the home menu, always override. */
+ if (static_cast(title_id) < TitleId_AppletStart || !pm::info::HasLaunchedTitle(TitleId_AppletQlaunch)) {
+ *out_hbl = false;
+ *out_title = true;
+ return;
+ }
+
+ /* Unconditionally refresh loader.ini contents. */
+ RefreshLoaderConfiguration();
+
+ /* Set HBL output. */
+ *out_hbl = IsHblTitleId(title_id) && IsOverrideKeyHeld(&g_hbl_override_config.override_key);
+
+ /* Set title specific output. */
+ TitleSpecificOverrideConfig title_cfg = GetTitleOverrideConfig(title_id);
+ *out_title = IsOverrideKeyHeld(&title_cfg.override_key);
+ }
+
+ bool IsCheatEnableKeyHeld(ncm::TitleId title_id) {
+ /* If the SD card isn't initialized, don't apply cheats. */
+ if (!IsSdCardInitialized()) {
+ return false;
+ }
+
+ /* Don't apply cheats to HBL. */
+ if (IsHblOverrideKeyHeld(title_id)) {
+ return false;
+ }
+
+ TitleSpecificOverrideConfig title_cfg = GetTitleOverrideConfig(title_id);
+ return IsOverrideKeyHeld(&title_cfg.cheat_enable_key);
+ }
+
+ /* HBL Configuration utilities. */
+ bool IsHblTitleId(ncm::TitleId title_id) {
+ return (g_hbl_override_config.override_any_app && TitleIdIsApplication(static_cast(title_id))) || (title_id == g_hbl_override_config.title_id);
+ }
+
+ const char *GetHblPath() {
+ return g_hbl_sd_path;
+ }
+
+
+}
diff --git a/source/cfg/cfg_privileged_process.cpp b/source/cfg/cfg_privileged_process.cpp
new file mode 100644
index 00000000..e33af937
--- /dev/null
+++ b/source/cfg/cfg_privileged_process.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+
+namespace sts::cfg {
+
+ namespace {
+
+ /* Convenience definitions. */
+ constexpr u64 InitialProcessIdMinDeprecated = 0x00;
+ constexpr u64 InitialProcessIdMaxDeprecated = 0x50;
+
+ /* Privileged process globals. */
+ HosMutex g_lock;
+ bool g_detected_privileged_process = false;
+ bool g_is_privileged_process = false;
+
+ /* SD card helpers. */
+ void GetPrivilegedProcessIdRange(u64 *out_min, u64 *out_max) {
+ u64 min = 0, max = 0;
+ if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) {
+ /* On 5.0.0+, we can get precise limits from svcGetSystemInfo. */
+ R_ASSERT(svcGetSystemInfo(&min, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
+ R_ASSERT(svcGetSystemInfo(&max, SystemInfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
+ } else if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) {
+ /* On 4.0.0-4.1.0, we can get the precise limits from normal svcGetInfo. */
+ R_ASSERT(svcGetInfo(&min, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Minimum));
+ R_ASSERT(svcGetInfo(&max, InfoType_InitialProcessIdRange, INVALID_HANDLE, InitialProcessIdRangeInfo_Maximum));
+ } else {
+ /* On < 4.0.0, we just use hardcoded extents. */
+ min = InitialProcessIdMinDeprecated;
+ max = InitialProcessIdMaxDeprecated;
+ }
+
+ *out_min = min;
+ *out_max = max;
+ }
+
+ u64 GetCurrentProcessId() {
+ u64 process_id = 0;
+ R_ASSERT(svcGetProcessId(&process_id, CUR_PROCESS_HANDLE));
+ return process_id;
+ }
+
+ void DetectIsPrivilegedProcess() {
+ u64 min = 0, max = 0, cur = 0;
+ GetPrivilegedProcessIdRange(&min, &max);
+ cur = GetCurrentProcessId();
+ g_is_privileged_process = min <= cur && cur <= max;
+ g_detected_privileged_process = true;
+ }
+
+
+ }
+
+ /* SD card utilities. */
+ bool IsPrivilegedProcess() {
+ std::scoped_lock lk(g_lock);
+
+ /* If we've already detected, return cached result. */
+ if (!g_detected_privileged_process) {
+ DetectIsPrivilegedProcess();
+ }
+
+ /* Determine if we're privileged, and return. */
+ return g_is_privileged_process;
+ }
+
+}
diff --git a/source/cfg/cfg_sd_card.cpp b/source/cfg/cfg_sd_card.cpp
new file mode 100644
index 00000000..a1c1ed7c
--- /dev/null
+++ b/source/cfg/cfg_sd_card.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+
+namespace sts::cfg {
+
+ namespace {
+
+ /* Convenience definitions. */
+ constexpr sm::ServiceName RequiredServicesForSdCardAccess[] = {
+ sm::ServiceName::Encode("pcv"),
+ sm::ServiceName::Encode("gpio"),
+ sm::ServiceName::Encode("pinmux"),
+ sm::ServiceName::Encode("psc:c")
+ };
+ constexpr size_t NumRequiredServicesForSdCardAccess = sizeof(RequiredServicesForSdCardAccess) / sizeof(RequiredServicesForSdCardAccess[0]);
+
+ /* SD card globals. */
+ HosMutex g_sd_card_lock;
+ bool g_sd_card_initialized = false;
+ FsFileSystem g_sd_card_filesystem = {};
+
+ /* SD card helpers. */
+ Result TryInitializeSdCard() {
+ for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
+ bool service_present = false;
+ R_TRY(sm::HasService(&service_present, RequiredServicesForSdCardAccess[i]));
+ if (!service_present) {
+ return ResultFsSdCardNotPresent;
+ }
+ }
+ R_ASSERT(fsMountSdcard(&g_sd_card_filesystem));
+ g_sd_card_initialized = true;
+ return ResultSuccess;
+ }
+
+ void InitializeSdCard() {
+ for (size_t i = 0; i < NumRequiredServicesForSdCardAccess; i++) {
+ Service tmp;
+ R_ASSERT(sm::GetService(&tmp, RequiredServicesForSdCardAccess[i]));
+ serviceClose(&tmp);
+ }
+ R_ASSERT(fsMountSdcard(&g_sd_card_filesystem));
+ g_sd_card_initialized = true;
+ }
+
+ }
+
+ /* SD card utilities. */
+ bool IsSdCardInitialized() {
+ std::scoped_lock lk(g_sd_card_lock);
+
+ if (!g_sd_card_initialized) {
+ if (R_SUCCEEDED(TryInitializeSdCard())) {
+ g_sd_card_initialized = true;
+ }
+ }
+ return g_sd_card_initialized;
+ }
+
+ void WaitSdCardInitialized() {
+ std::scoped_lock lk(g_sd_card_lock);
+
+ InitializeSdCard();
+ }
+
+}
diff --git a/source/hid/hid_api.cpp b/source/hid/hid_api.cpp
new file mode 100644
index 00000000..cee7b577
--- /dev/null
+++ b/source/hid/hid_api.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+#include
+#include
+
+namespace sts::hid {
+
+ namespace {
+
+ /* Global lock. */
+ HosMutex g_hid_lock;
+ bool g_initialized_hid = false;
+
+ /* Helper. */
+ void InitializeHid() {
+ R_ASSERT(smInitialize());
+ ON_SCOPE_EXIT { smExit(); };
+ {
+ R_ASSERT(hidInitialize());
+ }
+ }
+
+ Result EnsureHidInitialized() {
+ if (!g_initialized_hid) {
+ if (!serviceIsActive(hidGetSessionService())) {
+ if (!pm::info::HasLaunchedTitle(TitleId_Hid)) {
+ return MAKERESULT(Module_Libnx, LibnxError_InitFail_HID);
+ }
+ InitializeHid();
+ }
+ g_initialized_hid = true;
+ }
+
+ return ResultSuccess;
+ }
+
+ }
+
+ Result GetKeysHeld(u64 *out) {
+ std::scoped_lock lk(g_hid_lock);
+
+ R_TRY(EnsureHidInitialized());
+
+ hidScanInput();
+ *out = 0;
+
+ for (size_t controller = 0; controller < 10; controller++) {
+ *out |= hidKeysHeld(static_cast(controller));
+ }
+
+ return ResultSuccess;
+ }
+
+}
diff --git a/source/ldr/ldr_ams.c b/source/ldr/ldr_ams.c
new file mode 100644
index 00000000..94c042e1
--- /dev/null
+++ b/source/ldr/ldr_ams.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include "ldr_ams.h"
+
+static Result _ldrAtmosphereHasLaunchedTitle(Service *srv, bool *out, u64 tid) {
+ IpcCommand c;
+ ipcInitialize(&c);
+
+ struct {
+ u64 magic;
+ u64 cmd_id;
+ u64 title_id;
+ } *raw;
+
+ raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw));
+ raw->magic = SFCI_MAGIC;
+ raw->cmd_id = 65000;
+ raw->title_id = tid;
+
+ Result rc = serviceIpcDispatch(srv);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+ struct {
+ u64 magic;
+ u64 result;
+ u8 has_launched_title;
+ } *resp;
+
+ serviceIpcParse(srv, &r, sizeof(*resp));
+ resp = r.Raw;
+
+ rc = resp->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *out = resp->has_launched_title != 0;
+ } else {
+ rc = 0x666;
+ }
+ } else {
+ rc = 0x555;
+ }
+
+ return rc;
+}
+
+Result ldrDmntAtmosphereHasLaunchedTitle(bool *out, u64 tid) {
+ return _ldrAtmosphereHasLaunchedTitle(ldrDmntGetServiceSession(), out, tid);
+}
+
+Result ldrPmAtmosphereHasLaunchedTitle(bool *out, u64 tid) {
+ return _ldrAtmosphereHasLaunchedTitle(ldrPmGetServiceSession(), out, tid);
+}
diff --git a/source/ldr/ldr_ams.h b/source/ldr/ldr_ams.h
new file mode 100644
index 00000000..9ca154a2
--- /dev/null
+++ b/source/ldr/ldr_ams.h
@@ -0,0 +1,19 @@
+/**
+ * @file ldr_ams.h
+ * @brief Loader (ldr:*) IPC wrapper for Atmosphere extensions.
+ * @author SciresM
+ * @copyright libnx Authors
+ */
+#pragma once
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Result ldrPmAtmosphereHasLaunchedTitle(bool *out, u64 tid);
+Result ldrDmntAtmosphereHasLaunchedTitle(bool *out, u64 tid);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/source/ldr/ldr_pm_api.cpp b/source/ldr/ldr_pm_api.cpp
new file mode 100644
index 00000000..4f4e5e0f
--- /dev/null
+++ b/source/ldr/ldr_pm_api.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+#include
+#include
+
+#include "ldr_ams.h"
+
+namespace sts::ldr::pm {
+
+ /* Information API. */
+ Result CreateProcess(Handle *out, PinId pin_id, u32 flags, Handle reslimit) {
+ return ldrPmCreateProcess(flags, pin_id.value, reslimit, out);
+ }
+
+ Result GetProgramInfo(ProgramInfo *out, const ncm::TitleLocation &loc) {
+ return ldrPmGetProgramInfo(static_cast(loc.title_id), static_cast(loc.storage_id), reinterpret_cast(out));
+ }
+
+ Result PinTitle(PinId *out, const ncm::TitleLocation &loc) {
+ static_assert(sizeof(*out) == sizeof(u64), "PinId definition!");
+ return ldrPmRegisterTitle(static_cast(loc.title_id), static_cast(loc.storage_id), reinterpret_cast(out));
+ }
+
+ Result UnpinTitle(PinId pin_id) {
+ return ldrPmUnregisterTitle(pin_id.value);
+ }
+
+ Result HasLaunchedTitle(bool *out, ncm::TitleId title_id) {
+ return ldrPmAtmosphereHasLaunchedTitle(out, static_cast(title_id));
+ }
+
+}
diff --git a/source/map/map_api.cpp b/source/map/map_api.cpp
index 60fdccd0..f6d70442 100644
--- a/source/map/map_api.cpp
+++ b/source/map/map_api.cpp
@@ -23,11 +23,6 @@ namespace sts::map {
namespace {
/* Convenience defines. */
- constexpr uintptr_t Deprecated64BitAslrBase = 0x08000000ul;
- constexpr size_t Deprecated64BitAslrSize = 0x78000000ul;
- constexpr uintptr_t Deprecated32BitAslrBase = 0x00200000ul;
- constexpr size_t Deprecated32BitAslrSize = 0x3FE00000ul;
-
constexpr size_t GuardRegionSize = 0x4000;
constexpr size_t LocateRetryCount = 0x200;
@@ -194,12 +189,12 @@ namespace sts::map {
R_TRY(svcGetInfo(&out->aslr_size, InfoType_AslrRegionSize, process_h, 0));
} else {
/* Auto-detect 32-bit vs 64-bit. */
- if (out->heap_base < Deprecated64BitAslrBase || out->alias_base < Deprecated64BitAslrBase) {
- out->aslr_base = Deprecated32BitAslrBase;
- out->aslr_size = Deprecated32BitAslrSize;
+ if (out->heap_base < AslrBase64BitDeprecated || out->alias_base < AslrBase64BitDeprecated) {
+ out->aslr_base = AslrBase32Bit;
+ out->aslr_size = AslrSize32Bit;
} else {
- out->aslr_base = Deprecated64BitAslrBase;
- out->aslr_size = Deprecated64BitAslrSize;
+ out->aslr_base = AslrBase64BitDeprecated;
+ out->aslr_size = AslrSize64BitDeprecated;
}
}
diff --git a/source/pm/pm_ams.c b/source/pm/pm_ams.c
new file mode 100644
index 00000000..46882eb0
--- /dev/null
+++ b/source/pm/pm_ams.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include "pm_ams.h"
+
+Result pminfoAtmosphereGetProcessId(u64 *out_pid, u64 tid) {
+ IpcCommand c;
+ ipcInitialize(&c);
+ Service *srv = pminfoGetServiceSession();
+
+ struct {
+ u64 magic;
+ u64 cmd_id;
+ u64 title_id;
+ } *raw;
+
+ raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw));
+ raw->magic = SFCI_MAGIC;
+ raw->cmd_id = 65000;
+ raw->title_id = tid;
+
+ Result rc = serviceIpcDispatch(srv);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+ struct {
+ u64 magic;
+ u64 result;
+ u64 pid;
+ } *resp;
+
+ serviceIpcParse(srv, &r, sizeof(*resp));
+ resp = r.Raw;
+
+ rc = resp->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *out_pid = resp->pid;
+ }
+ }
+
+ return rc;
+}
+
+Result pminfoAtmosphereHasLaunchedTitle(bool *out, u64 tid) {
+ IpcCommand c;
+ ipcInitialize(&c);
+ Service *srv = pminfoGetServiceSession();
+
+ struct {
+ u64 magic;
+ u64 cmd_id;
+ u64 title_id;
+ } *raw;
+
+ raw = serviceIpcPrepareHeader(srv, &c, sizeof(*raw));
+ raw->magic = SFCI_MAGIC;
+ raw->cmd_id = 65001;
+ raw->title_id = tid;
+
+ Result rc = serviceIpcDispatch(srv);
+
+ if (R_SUCCEEDED(rc)) {
+ IpcParsedCommand r;
+ struct {
+ u64 magic;
+ u64 result;
+ u8 has_launched_title;
+ } *resp;
+
+ serviceIpcParse(srv, &r, sizeof(*resp));
+ resp = r.Raw;
+
+ rc = resp->result;
+
+ if (R_SUCCEEDED(rc)) {
+ *out = resp->has_launched_title != 0;
+ }
+ }
+
+ return rc;
+}
diff --git a/source/pm/pm_ams.h b/source/pm/pm_ams.h
new file mode 100644
index 00000000..78bc9ce9
--- /dev/null
+++ b/source/pm/pm_ams.h
@@ -0,0 +1,19 @@
+/**
+ * @file pm_ams.h
+ * @brief Process Manager (pm:*) IPC wrapper for Atmosphere extensions.
+ * @author SciresM
+ * @copyright libnx Authors
+ */
+#pragma once
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Result pminfoAtmosphereGetProcessId(u64 *out_pid, u64 tid);
+Result pminfoAtmosphereHasLaunchedTitle(bool *out, u64 tid);
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/source/pm/pm_info_api.cpp b/source/pm/pm_info_api.cpp
new file mode 100644
index 00000000..3b62c341
--- /dev/null
+++ b/source/pm/pm_info_api.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+#include
+#include
+#include
+#include
+
+#include "pm_ams.h"
+
+namespace sts::pm::info {
+
+ namespace {
+
+ /* Global lock. */
+ HosMutex g_info_lock;
+ std::set g_cached_launched_titles;
+
+ }
+
+ /* Information API. */
+ Result GetTitleId(u64 *out_title_id, u64 process_id) {
+ std::scoped_lock lk(g_info_lock);
+
+ return pminfoGetTitleId(out_title_id, process_id);
+ }
+
+ Result GetProcessId(u64 *out_process_id, u64 title_id) {
+ std::scoped_lock lk(g_info_lock);
+
+ return pminfoAtmosphereGetProcessId(out_process_id, title_id);
+ }
+
+ Result __attribute__((weak)) HasLaunchedTitle(bool *out, u64 title_id) {
+ std::scoped_lock lk(g_info_lock);
+
+ if (g_cached_launched_titles.find(title_id) != g_cached_launched_titles.end()) {
+ *out = true;
+ return ResultSuccess;
+ }
+
+ bool has_launched = false;
+ R_TRY(pminfoAtmosphereHasLaunchedTitle(&has_launched, title_id));
+ if (has_launched) {
+ g_cached_launched_titles.insert(title_id);
+ }
+
+ *out = has_launched;
+ return ResultSuccess;
+ }
+
+ bool HasLaunchedTitle(u64 title_id) {
+ bool has_launched = false;
+ R_ASSERT(HasLaunchedTitle(&has_launched, title_id));
+ return has_launched;
+ }
+
+}