Compare commits

..

23 Commits

Author SHA1 Message Date
Michael Scire
f7bf379cfe git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "80bf6aeee"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "80bf6aeee"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-10-27 16:22:45 -07:00
Michael Scire
9f26419b1a ams: bump version to 1.6.2, add changelog 2023-10-27 16:21:57 -07:00
Michael Scire
1b057d48c6 sm: fix compat with new service macros 2023-10-26 14:44:45 -07:00
Michael Scire
0c3afff4d3 pm: update to reflect 17.0.0 internal design changes 2023-10-26 14:44:32 -07:00
Michael Scire
274f6b63f2 erpt: add remaining SubmitFsInfo helpers 2023-10-25 16:08:12 -07:00
Michael Scire
2ed8450446 erpt: SubmitFileSystemProxyErrorInfo 2023-10-25 14:21:27 -07:00
Michael Scire
60974a5f4e erpt: GetMmcErrorInfo, GetSdCard*Info 2023-10-25 12:41:18 -07:00
Michael Scire
fa384fd920 erpt: begin SubmitFsinfo (SubmitMmcDetailInfo) 2023-10-25 04:45:41 -07:00
Michael Scire
3f19db0d96 jpegdec: fix abort check on output width 2023-10-18 02:33:59 -07:00
Michael Scire
a84f725e21 jpegdec: update to reflect 17.0.0 changes 2023-10-18 02:31:26 -07:00
Michael Scire
7f61dfdb8d pm: since 15.0.0, WaitApplicationMemoryAvailable is more lenient 2023-10-17 11:25:35 -07:00
Michael Scire
c44da84869 pm: adjust resource limit function names 2023-10-17 11:10:09 -07:00
Liam
7f4450f930 haze: pretend to be able to write MTP server code 2023-10-16 17:31:09 -04:00
Michael Scire
edb4e2ea56 git subrepo push libraries
subrepo:
  subdir:   "libraries"
  merged:   "965e05b3c"
upstream:
  origin:   "https://github.com/Atmosphere-NX/Atmosphere-libs"
  branch:   "master"
  commit:   "965e05b3c"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
2023-10-16 12:31:15 -07:00
Michael Scire
183f3e0d7e ams: bump version to 1.6.1 2023-10-16 12:30:35 -07:00
Michael Scire
7650c5eb96 docs: add changelog for 1.6.1 2023-10-16 12:30:18 -07:00
Liam
0ff197b300 haze: fix bMaxPacketSize0 2023-10-16 08:36:33 -07:00
Liam
d9fff85bc4 haze: use gpu console for rendering 2023-10-16 08:36:10 -07:00
Liam
c866c15856 haze: implement GetObjectPropList 2023-10-16 08:36:10 -07:00
Michael Scire
e8ac23e2ee ncm: fix two comments 2023-10-16 08:24:07 -07:00
Michael Scire
3a8cffef57 ncm: better detect + fix 17 brick after-the-fact
This adds detection for missing-save or empty-save, and rebuilds in either case.
2023-10-16 02:38:30 -07:00
Liam
13411902c9 fs: add missing stub for GetProgramId 2023-10-14 07:52:47 -07:00
Michael Scire
693fb423cb kern: fix minor sin 2023-10-12 14:25:17 -07:00
71 changed files with 2974 additions and 392 deletions

View File

@@ -84,7 +84,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000034
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000036
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000037
#mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000042
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000420
mkdir -p $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240
@@ -98,7 +98,7 @@ dist-no-debug: package3 $(CURRENT_DIRECTORY)/$(ATMOSPHERE_OUT_DIR)
cp stratosphere/fatal/$(ATMOSPHERE_OUT_DIR)/fatal.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000034/exefs.nsp
cp stratosphere/creport/$(ATMOSPHERE_OUT_DIR)/creport.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000036/exefs.nsp
cp stratosphere/ro/$(ATMOSPHERE_OUT_DIR)/ro.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000037/exefs.nsp
#cp stratosphere/jpegdec/$(ATMOSPHERE_OUT_DIR)/jpegdec.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c/exefs.nsp
cp stratosphere/jpegdec/$(ATMOSPHERE_OUT_DIR)/jpegdec.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000003c/exefs.nsp
cp stratosphere/pgl/$(ATMOSPHERE_OUT_DIR)/pgl.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000042/exefs.nsp
cp stratosphere/LogManager/$(ATMOSPHERE_OUT_DIR)/LogManager.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/0100000000000420/exefs.nsp
cp stratosphere/htc/$(ATMOSPHERE_OUT_DIR)/htc.nsp $(DIST_DIR)/stratosphere_romfs/atmosphere/contents/010000000000b240/exefs.nsp

View File

@@ -1,4 +1,19 @@
# Changelog
## 1.6.2
+ Support was finished for 17.0.0.
+ `erpt` was updated to support the latest official behavior.
+ `jpegdec` was updated to support the latest official behavior.
+ `pm` was updated to support the latest official behavior.
+ General system stability improvements to enhance the user's experience.
## 1.6.1
+ An improved solution to [the problem that would cause consoles which had previously re-built their SYSTEM partition to brick on update-to-17.0.0](https://gist.github.com/SciresM/2ddb708c812ed585c4d99f54e25205ff) was added.
+ In particular, booting atmosphère will now automatically detect the problem and unbrick any consoles which have fallen into this state.
+ Some improvements were made to `haze`, including:
+ Performance was greatly improved:
+ Support was added for GetObjectPropList, which decreases the amount of requests made by ~8x.
+ Haze now performs rendering on the GPU, freeing up the CPU to respond to requests in a more timely manner.
+ An issue was fixed with how `haze` configures `bMaxPacketSize0` which improves support for USB3.
+ General system stability improvements to enhance the user's experience.
## 1.6.0
+ Basic support was added for 17.0.0.
+ The console should boot and atmosphère should be fully functional. However, not all modules have been fully updated to reflect the latest changes.

View File

@@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/Atmosphere-NX/Atmosphere-libs
branch = master
commit = 132558c33865f6a21f06caa31bdfc6b1f92bd9b2
parent = d389ef639ed2988325523f1d95f90030a3370541
commit = 80bf6aeeed03df0871329e1ec32da073a62bf102
parent = 9f26419b1a669762d995ebf21ff83b6e1cf56153
method = merge
cmdver = 0.4.1

View File

@@ -66,7 +66,7 @@ endif
ifeq ($(ATMOSPHERE_BOARD),nx-hac-001)
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $*.map)
export LDFLAGS = -specs=$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/stratosphere.specs -specs=$(DEVKITPRO)/libnx/switch.specs $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now
else ifeq ($(ATMOSPHERE_OS_NAME),macos)
export LDFLAGS = $(CXXFLAGS) $(CXXWRAPS) $(CXXREQUIRED) -Wl,-map,$(notdir $@.map)
else

View File

@@ -583,7 +583,7 @@ namespace ams::kern {
/* Check memory state. */
const KProcessAddress last_addr = addr + size - 1;
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr))
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr, state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
/* If the start address isn't aligned, we need a block. */
if (out_blocks_needed != nullptr && util::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fat/fat_file_system.hpp>

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
namespace ams::fat {
constexpr inline size_t FatErrorNameMaxLength = 0x10;
struct FatError {
int error;
int extra_error;
int drive_id;
char name[FatErrorNameMaxLength];
u8 reserved[4];
};
static_assert(sizeof(FatError) == 0x20);
static_assert(util::is_pod<FatError>::value);
struct FatReportInfo1 {
u16 open_file_peak_count;
u16 open_directory_peak_count;
};
static_assert(sizeof(FatReportInfo1) == 4);
static_assert(util::is_pod<FatReportInfo1>::value);
struct FatReportInfo2 {
u16 open_unique_file_entry_peak_count;
u16 open_unique_directory_entry_peak_count;
};
static_assert(sizeof(FatReportInfo2) == 4);
static_assert(util::is_pod<FatReportInfo2>::value);
struct FatSafeInfo {
u32 result;
u32 error_number;
u32 safe_error_number;
};
static_assert(sizeof(FatSafeInfo) == 12);
static_assert(util::is_pod<FatSafeInfo>::value);
}

View File

@@ -51,9 +51,11 @@
#include <stratosphere/fs/fs_code.hpp>
#include <stratosphere/fs/fs_content.hpp>
#include <stratosphere/fs/fs_content_storage.hpp>
#include <stratosphere/fs/fs_error_info.hpp>
#include <stratosphere/fs/fs_game_card.hpp>
#include <stratosphere/fs/fs_host.hpp>
#include <stratosphere/fs/fs_image_directory.hpp>
#include <stratosphere/fs/fs_mmc.hpp>
#include <stratosphere/fs/fs_save_data_types.hpp>
#include <stratosphere/fs/fs_save_data_management.hpp>
#include <stratosphere/fs/fs_save_data_transaction.hpp>

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
#include <stratosphere/fat/fat_file_system.hpp>
namespace ams::fs {
struct StorageErrorInfo {
u32 num_activation_failures;
u32 num_activation_error_corrections;
u32 num_read_write_failures;
u32 num_read_write_error_corrections;
};
static_assert(sizeof(StorageErrorInfo) == 0x10);
static_assert(util::is_pod<StorageErrorInfo>::value);
struct FileSystemProxyErrorInfo {
u32 rom_fs_remount_for_data_corruption_count;
u32 rom_fs_unrecoverable_data_corruption_by_remount_count;
fat::FatError fat_fs_error;
u32 rom_fs_recovered_by_invalidate_cache_count;
u32 save_data_index_count;
fat::FatReportInfo1 bis_system_fat_report_info_1;
fat::FatReportInfo1 bis_user_fat_report_info_1;
fat::FatReportInfo1 sd_card_fat_report_info_1;
fat::FatReportInfo2 bis_system_fat_report_info_2;
fat::FatReportInfo2 bis_user_fat_report_info_2;
fat::FatReportInfo2 sd_card_fat_report_info_2;
u32 rom_fs_deep_retry_start_count;
u32 rom_fs_unrecoverable_by_game_card_access_failed_count;
fat::FatSafeInfo bis_system_fat_safe_info;
fat::FatSafeInfo bis_user_fat_safe_info;
u8 reserved[0x18];
};
static_assert(sizeof(FileSystemProxyErrorInfo) == 0x80);
static_assert(util::is_pod<FileSystemProxyErrorInfo>::value);
Result GetAndClearMmcErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size);
Result GetAndClearSdCardErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size);
Result GetAndClearFileSystemProxyErrorInfo(FileSystemProxyErrorInfo *out);
}

View File

@@ -19,6 +19,9 @@
namespace ams::fs {
/* ACCURATE_TO_VERSION: Unknown */
constexpr inline size_t GameCardCidSize = 0x10;
constexpr inline size_t GameCardDeviceIdSize = 0x10;
enum class GameCardPartition {
Update = 0,
Normal = 1,
@@ -47,9 +50,44 @@ namespace ams::fs {
Terra = 1,
};
struct GameCardErrorReportInfo {
u16 game_card_crc_error_num;
u16 reserved1;
u16 asic_crc_error_num;
u16 reserved2;
u16 refresh_num;
u16 reserved3;
u16 retry_limit_out_num;
u16 timeout_retry_num;
u16 asic_reinitialize_failure_detail;
u16 insertion_count;
u16 removal_count;
u16 asic_reinitialize_num;
u32 initialize_count;
u16 asic_reinitialize_failure_num;
u16 awaken_failure_num;
u16 reserved4;
u16 refresh_succeeded_count;
u32 last_read_error_page_address;
u32 last_read_error_page_count;
u32 awaken_count;
u32 read_count_from_insert;
u32 read_count_from_awaken;
u8 reserved5[8];
};
static_assert(util::is_pod<GameCardErrorReportInfo>::value);
static_assert(sizeof(GameCardErrorReportInfo) == 0x40);
using GameCardHandle = u32;
Result GetGameCardHandle(GameCardHandle *out);
Result MountGameCardPartition(const char *name, GameCardHandle handle, GameCardPartition partition);
Result GetGameCardCid(void *dst, size_t size);
Result GetGameCardDeviceId(void *dst, size_t size);
Result GetGameCardErrorReportInfo(GameCardErrorReportInfo *out);
bool IsGameCardInserted();
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
namespace ams::fs {
struct MemoryReportInfo {
u64 pooled_buffer_peak_free_size;
u64 pooled_buffer_retried_count;
u64 pooled_buffer_reduce_allocation_count;
u64 buffer_manager_peak_free_size;
u64 buffer_manager_retried_count;
u64 exp_heap_peak_free_size;
u64 buffer_pool_peak_free_size;
u64 patrol_read_allocate_buffer_success_count;
u64 patrol_read_allocate_buffer_failure_count;
u64 buffer_manager_peak_total_allocatable_size;
u64 buffer_pool_max_allocate_size;
u64 pooled_buffer_failed_ideal_allocation_count_on_async_access;
u8 reserved[0x20];
};
static_assert(sizeof(MemoryReportInfo) == 0x80);
static_assert(util::is_pod<MemoryReportInfo>::value);
Result GetAndClearMemoryReportInfo(MemoryReportInfo *out);
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere/fs/fs_common.hpp>
namespace ams::fs {
constexpr inline size_t MmcCidSize = 0x10;
constexpr inline size_t MmcExtendedCsdSize = 0x200;
constexpr inline int MmcExtendedCsdOffsetReEolInfo = 267;
constexpr inline int MmcExtendedCsdOffsetDeviceLifeTimeEstTypA = 268;
constexpr inline int MmcExtendedCsdOffsetDeviceLifeTimeEstTypB = 269;
enum MmcSpeedMode {
MmcSpeedMode_Identification = 0,
MmcSpeedMode_LegacySpeed = 1,
MmcSpeedMode_HighSpeed = 2,
MmcSpeedMode_Hs200 = 3,
MmcSpeedMode_Hs400 = 4,
MmcSpeedMode_Unknown = 5,
};
enum class MmcPartition {
UserData = 0,
BootPartition1 = 1,
BootPartition2 = 2,
};
Result GetMmcCid(void *dst, size_t size);
inline void ClearMmcCidSerialNumber(u8 *cid) {
/* Clear the serial number from the cid. */
std::memset(cid + 1, 0, 4);
}
Result GetMmcSpeedMode(MmcSpeedMode *out);
Result GetMmcPatrolCount(u32 *out);
Result GetMmcExtendedCsd(void *dst, size_t size);
}

View File

@@ -21,12 +21,38 @@ namespace ams::fs {
/* ACCURATE_TO_VERSION: Unknown */
class IEventNotifier;
constexpr inline size_t SdCardCidSize = 0x10;
enum SdCardSpeedMode {
SdCardSpeedMode_Identification = 0,
SdCardSpeedMode_DefaultSpeed = 1,
SdCardSpeedMode_HighSpeed = 2,
SdCardSpeedMode_Sdr12 = 3,
SdCardSpeedMode_Sdr25 = 4,
SdCardSpeedMode_Sdr50 = 5,
SdCardSpeedMode_Sdr104 = 6,
SdCardSpeedMode_Ddr50 = 7,
SdCardSpeedMode_Unknown = 8,
};
struct EncryptionSeed {
char value[0x10];
};
static_assert(util::is_pod<EncryptionSeed>::value);
static_assert(sizeof(EncryptionSeed) == 0x10);
Result GetSdCardCid(void *dst, size_t size);
inline void ClearSdCardCidSerialNumber(u8 *cid) {
/* Clear the serial number from the cid. */
std::memset(cid + 2, 0, 4);
}
Result GetSdCardUserAreaSize(s64 *out);
Result GetSdCardProtectedAreaSize(s64 *out);
Result GetSdCardSpeedMode(SdCardSpeedMode *out);
Result MountSdCard(const char *name);
Result MountSdCardErrorReportDirectoryForAtmosphere(const char *name);

View File

@@ -121,6 +121,7 @@ namespace ams::fssrv {
Result IsSignedSystemPartitionOnSdCardValid(ams::sf::Out<bool> out);
Result OpenAccessFailureDetectionEventNotifier();
/* ... */
Result GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out);
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count);
Result SetBisRootForHost(u32 id, const fssrv::sf::FspPath &path);
Result SetSaveDataSize(s64 size, s64 journal_size);
@@ -131,6 +132,7 @@ namespace ams::fssrv {
Result OutputAccessLogToSdCard(const ams::sf::InBuffer &buf);
Result RegisterUpdatePartition();
Result OpenRegisteredUpdatePartition(ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out);
Result GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out);
/* ... */
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count);
Result GetFsStackUsage(ams::sf::Out<u32> out, u32 type);

View File

@@ -16,12 +16,27 @@
#pragma once
#include <vapours.hpp>
#include <stratosphere/sf.hpp>
#include <stratosphere/fs/fs_error_info.hpp>
#include <stratosphere/fs/fs_game_card.hpp>
/* TODO */
/* ACCURATE_TO_VERSION: 13.4.0.0 */
#define AMS_FSSRV_I_DEVICE_OPERATOR_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, IsSdCardInserted, (ams::sf::Out<bool> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 200, Result, IsGameCardInserted, (ams::sf::Out<bool> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 202, Result, GetGameCardHandle, (ams::sf::Out<u32> out), (out))
AMS_SF_METHOD_INFO(C, H, 0, Result, IsSdCardInserted, (ams::sf::Out<bool> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, GetSdCardSpeedMode, (ams::sf::Out<s64> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetSdCardCid, (ams::sf::OutBuffer out, s64 size), (out, size)) \
AMS_SF_METHOD_INFO(C, H, 3, Result, GetSdCardUserAreaSize, (ams::sf::Out<s64> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 4, Result, GetSdCardProtectedAreaSize, (ams::sf::Out<s64> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, GetAndClearSdCardErrorInfo, (ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size), (out_sei, out_size, out_buf, size)) \
AMS_SF_METHOD_INFO(C, H, 100, Result, GetMmcCid, (ams::sf::OutBuffer out, s64 size), (out, size)) \
AMS_SF_METHOD_INFO(C, H, 101, Result, GetMmcSpeedMode, (ams::sf::Out<s64> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 112, Result, GetMmcPatrolCount, (ams::sf::Out<u32> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 113, Result, GetAndClearMmcErrorInfo, (ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size), (out_sei, out_size, out_buf, size)) \
AMS_SF_METHOD_INFO(C, H, 114, Result, GetMmcExtendedCsd, (ams::sf::OutBuffer out, s64 size), (out, size)) \
AMS_SF_METHOD_INFO(C, H, 200, Result, IsGameCardInserted, (ams::sf::Out<bool> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 202, Result, GetGameCardHandle, (ams::sf::Out<u32> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 208, Result, GetGameCardIdSet, (ams::sf::OutBuffer out, s64 size), (out, size)) \
AMS_SF_METHOD_INFO(C, H, 217, Result, GetGameCardErrorReportInfo, (ams::sf::Out<fs::GameCardErrorReportInfo> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 218, Result, GetGameCardDeviceId, (ams::sf::OutBuffer out, s64 size), (out, size))
AMS_SF_DEFINE_INTERFACE(ams::fssrv::sf, IDeviceOperator, AMS_FSSRV_I_DEVICE_OPERATOR_INTERFACE_INFO, 0x1484E21C)

View File

@@ -20,6 +20,8 @@
#include <stratosphere/fssrv/sf/fssrv_sf_istorage.hpp>
#include <stratosphere/fssrv/sf/fssrv_sf_i_device_operator.hpp>
#include <stratosphere/fssrv/sf/fssrv_sf_i_event_notifier.hpp>
#include <stratosphere/fs/fs_error_info.hpp>
#include <stratosphere/fs/fs_memory_report_info.hpp>
/* ACCURATE_TO_VERSION: 13.4.0.0 */
#define AMS_FSSRV_I_FILE_SYSTEM_PROXY_INTERFACE_INFO(C, H) \
@@ -125,7 +127,7 @@
/* AMS_SF_METHOD_INFO(C, H, 702, Result, IsAccessFailureDetected, (), (), hos::Version_5_0_0) */ \
/* AMS_SF_METHOD_INFO(C, H, 710, Result, ResolveAccessFailure, (), (), hos::Version_5_0_0) */ \
/* AMS_SF_METHOD_INFO(C, H, 720, Result, AbandonAccessFailure, (), (), hos::Version_5_0_0) */ \
/* AMS_SF_METHOD_INFO(C, H, 800, Result, GetAndClearErrorInfo, (), (), hos::Version_2_0_0) */ \
AMS_SF_METHOD_INFO(C, H, 800, Result, GetAndClearErrorInfo, (ams::sf::Out<fs::FileSystemProxyErrorInfo> out), (out), hos::Version_2_0_0) \
AMS_SF_METHOD_INFO(C, H, 810, Result, RegisterProgramIndexMapInfo, (const ams::sf::InBuffer &buffer, s32 count), (buffer, count), hos::Version_7_0_0) \
AMS_SF_METHOD_INFO(C, H, 1000, Result, SetBisRootForHost, (u32 id, const fssrv::sf::FspPath &path), (id, path), hos::Version_Min, hos::Version_9_2_0) \
AMS_SF_METHOD_INFO(C, H, 1001, Result, SetSaveDataSize, (s64 size, s64 journal_size), (size, journal_size)) \
@@ -136,7 +138,7 @@
AMS_SF_METHOD_INFO(C, H, 1006, Result, OutputAccessLogToSdCard, (const ams::sf::InBuffer &buf), (buf)) \
AMS_SF_METHOD_INFO(C, H, 1007, Result, RegisterUpdatePartition, (), (), hos::Version_4_0_0) \
AMS_SF_METHOD_INFO(C, H, 1008, Result, OpenRegisteredUpdatePartition, (ams::sf::Out<ams::sf::SharedPointer<fssrv::sf::IFileSystem>> out), (out), hos::Version_4_0_0) \
/* AMS_SF_METHOD_INFO(C, H, 1009, Result, GetAndClearMemoryReportInfo, (ams::sf::Out<fs::MemoryReportInfo> out), (out), hos::Version_4_0_0) */ \
AMS_SF_METHOD_INFO(C, H, 1009, Result, GetAndClearMemoryReportInfo, (ams::sf::Out<fs::MemoryReportInfo> out), (out), hos::Version_4_0_0) \
/* AMS_SF_METHOD_INFO(C, H, 1010, Result, SetDataStorageRedirectTarget, (), (), hos::Version_5_1_0, hos::Version_6_2_0) */ \
AMS_SF_METHOD_INFO(C, H, 1011, Result, GetProgramIndexForAccessLog, (ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count), (out_idx, out_count), hos::Version_7_0_0) \
AMS_SF_METHOD_INFO(C, H, 1012, Result, GetFsStackUsage, (ams::sf::Out<u32> out, u32 type), (out, type), hos::Version_9_0_0) \

View File

@@ -18,3 +18,4 @@
#include <stratosphere/gc/impl/gc_types.hpp>
#include <stratosphere/gc/impl/gc_gc_crypto.hpp>
#include <stratosphere/gc/impl/gc_embedded_data_holder.hpp>
#include <stratosphere/gc/gc.hpp>

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/gc/impl/gc_types.hpp>
namespace ams::gc {
struct GameCardIdSet {
gc::impl::CardId1 id1;
gc::impl::CardId2 id2;
gc::impl::CardId3 id3;
};
static_assert(util::is_pod<GameCardIdSet>::value);
static_assert(sizeof(GameCardIdSet) == 0xC);
}

View File

@@ -76,6 +76,11 @@ namespace ams::gc::impl {
static_assert(util::is_pod<CardHeaderEncryptedData>::value);
static_assert(sizeof(CardHeaderEncryptedData) == 0x70);
enum MakerCodeForCardId1 : u8 {
MakerCodeForCardId1_MegaChips = 0xC2,
MakerCodeForCardId1_Lapis = 0xAE,
};
enum MemoryCapacity : u8 {
MemoryCapacity_1GB = 0xFA,
MemoryCapacity_2GB = 0xF8,
@@ -85,6 +90,33 @@ namespace ams::gc::impl {
MemoryCapacity_32GB = 0xE2,
};
enum MemoryType : u8 {
MemoryType_T1RomFast = 0x01,
MemoryType_T2RomFast = 0x02,
MemoryType_T1NandFast = 0x09,
MemoryType_T2NandFast = 0x0A,
MemoryType_T1RomLate = 0x21,
MemoryType_T2RomLate = 0x22,
MemoryType_T1NandLate = 0x29,
MemoryType_T2NandLate = 0x2A,
};
enum CardSecurityNumber : u8 {
CardSecurityNumber_0 = 0x00,
CardSecurityNumber_1 = 0x01,
CardSecurityNumber_2 = 0x02,
CardSecurityNumber_3 = 0x03,
CardSecurityNumber_4 = 0x04,
};
enum CardType : u8 {
CardType_Rom = 0x00,
CardType_Writable_Dev_T1 = 0x01,
CardType_Writable_Prod_T1 = 0x02,
CardType_Writable_Dev_T2 = 0x03,
CardType_Writable_Prod_T2 = 0x04,
};
enum AccessControl1ClockRate : u32 {
AccessControl1ClockRate_25MHz = 0x00A10011,
AccessControl1ClockRate_50MHz = 0x00A10010,
@@ -95,6 +127,23 @@ namespace ams::gc::impl {
SelSec_T2 = 2,
};
struct CardId1 {
MakerCodeForCardId1 maker_code;
MemoryCapacity memory_capacity;
u8 reserved;
MemoryType memory_type;
};
struct CardId2 {
CardSecurityNumber card_security_number;
CardType card_type;
u8 reserved[2];
};
struct CardId3 {
u8 reserved[4];
};
struct CardHeader {
static constexpr u32 Magic = util::FourCC<'H','E','A','D'>::Code;

View File

@@ -239,8 +239,12 @@ namespace ams::ncm {
Result InitializeIntegratedContentMetaDatabaseRoot(IntegratedContentMetaDatabaseRoot *out, const IntegratedContentStorageConfig *config, size_t root_idx, size_t root_count);
Result BuildContentMetaDatabase(StorageId storage_id);
Result BuildContentMetaDatabaseImpl(StorageId storage_id);
Result ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition);
Result ImportContentMetaDatabaseImpl(ContentMetaDatabaseRoot *root, const char *import_mount_name);
private:
/* Helpers for unofficial functionality. */
bool IsNeedRebuildSystemContentMetaDatabase();
public:
/* Actual commands. */
Result CreateContentStorage(StorageId storage_id);

View File

@@ -27,7 +27,7 @@
AMS_SF_METHOD_INFO(C, H, 4, Result, GetApplicationProcessId, (sf::Out<os::ProcessId> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 5, Result, HookToCreateApplicationProcess, (sf::OutCopyHandle out_hook), (out_hook)) \
AMS_SF_METHOD_INFO(C, H, 6, Result, ClearHook, (u32 which), (which), hos::Version_6_0_0) \
AMS_SF_METHOD_INFO(C, H, 7, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
AMS_SF_METHOD_INFO(C, H, 7, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetProcessInfo, (sf::OutCopyHandle out_process_handle, sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id), (out_process_handle, out_loc, out_status, process_id)) \
AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereGetCurrentLimitInfo, (sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource), (out_cur_val, out_lim_val, group, resource))

View File

@@ -21,8 +21,8 @@
#define AMS_PM_I_INFORMATION_INTERFACE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 0, Result, GetProgramId, (sf::Out<ncm::ProgramId> out, os::ProcessId process_id), (out, process_id)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, GetAppletCurrentResourceLimitValues, (sf::Out<pm::ResourceLimitValues> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetAppletPeakResourceLimitValues, (sf::Out<pm::ResourceLimitValues> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 1, Result, GetAppletResourceLimitCurrentValue, (sf::Out<pm::ResourceLimitValue> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 2, Result, GetAppletResourceLimitPeakValue, (sf::Out<pm::ResourceLimitValue> out), (out)) \
AMS_SF_METHOD_INFO(C, H, 65000, Result, AtmosphereGetProcessId, (sf::Out<os::ProcessId> out, ncm::ProgramId program_id), (out, program_id)) \
AMS_SF_METHOD_INFO(C, H, 65001, Result, AtmosphereHasLaunchedBootProgram, (sf::Out<bool> out, ncm::ProgramId program_id), (out, program_id)) \
AMS_SF_METHOD_INFO(C, H, 65002, Result, AtmosphereGetProcessInfo, (sf::Out<ncm::ProgramLocation> out_loc, sf::Out<cfg::OverrideStatus> out_status, os::ProcessId process_id), (out_loc, out_status, process_id))

View File

@@ -29,8 +29,8 @@ namespace ams::pm::info {
Result GetProcessId(os::ProcessId *out_process_id, ncm::ProgramId program_id);
Result HasLaunchedBootProgram(bool *out, ncm::ProgramId program_id);
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out);
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out);
Result GetProcessInfo(ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id);

View File

@@ -52,7 +52,7 @@ namespace ams::pm {
LaunchFlagsDeprecated_SignalOnStart = (1 << 5),
};
struct ResourceLimitValues {
struct ResourceLimitValue {
u64 physical_memory;
u32 thread_count;
u32 event_count;

View File

@@ -16,11 +16,16 @@
#include <stratosphere.hpp>
#include "decodersrv_decoder_server_object.hpp"
#include "../jpeg/decodersrv_software_jpeg_decoder.hpp"
#include "../jpeg/decodersrv_software_jpeg_shrinker.hpp"
namespace ams::capsrv::server {
namespace {
constexpr const int JpegShrinkQualities[] = {
98, 95, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0
};
Result DecodeJpegImpl(void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
/* Clear the work memory. */
std::memset(work, 0, work_size);
@@ -67,6 +72,61 @@ namespace ams::capsrv::server {
R_SUCCEED();
}
Result ShrinkJpegImpl(u64 *out_size, void *dst, size_t dst_size, const void *src_jpeg, size_t src_jpeg_size, u32 width, u32 height, const ScreenShotDecodeOption &option, void *work, size_t work_size) {
/* Validate parameters. */
R_UNLESS(util::IsAligned(width, 0x10), capsrv::ResultAlbumOutOfRange());
R_UNLESS(util::IsAligned(height, 0x4), capsrv::ResultAlbumOutOfRange());
R_UNLESS(dst != nullptr, capsrv::ResultInternalJpegOutBufferShortage());
R_UNLESS(dst_size != 0, capsrv::ResultAlbumReadBufferShortage());
R_UNLESS(src_jpeg != nullptr, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(src_jpeg_size != 0, capsrv::ResultAlbumInvalidFileData());
/* Create the input. */
const jpeg::SoftwareJpegShrinkerInput shrink_input = {
.jpeg = src_jpeg,
.jpeg_size = src_jpeg_size,
.width = width,
.height = height,
.fancy_upsampling = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableFancyUpsampling),
.block_smoothing = option.HasJpegDecoderFlag(ScreenShotDecoderFlag_EnableBlockSmoothing),
};
/* Create the output. */
u64 shrunk_size = 0;
s32 shrunk_width = 0, shrunk_height = 0;
jpeg::SoftwareJpegShrinkerOutput shrink_output = {
.out_size = std::addressof(shrunk_size),
.out_width = std::addressof(shrunk_width),
.out_height = std::addressof(shrunk_height),
.dst = dst,
.dst_size = dst_size,
};
/* Try to shrink the jpeg at various quality levels. */
for (auto quality : JpegShrinkQualities) {
/* Shrink at the current quality. */
R_TRY_CATCH(jpeg::SoftwareJpegShrinker::ShrinkRgba8(shrink_output, shrink_input, quality, work, work_size)) {
/* If the output buffer isn't large enough to fit the output, we should try at a lower quality. */
R_CATCH(capsrv::ResultInternalJpegOutBufferShortage) {
continue;
}
/* Nintendo doesn't catch this result, but our lack of work buffer use makes me think this may be necessary. */
R_CATCH(capsrv::ResultInternalJpegWorkMemoryShortage) {
continue;
}
} R_END_TRY_CATCH;
/* Write the output size. */
*out_size = shrunk_size;
R_SUCCEED();
}
/* Nintendo aborts if no quality succeeds. */
AMS_ABORT("ShrinkJpeg should succeed before this point\n");
}
}
Result DecoderControlService::DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option) {
@@ -78,4 +138,13 @@ namespace ams::capsrv::server {
R_RETURN(DecodeJpegImpl(out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
}
Result DecoderControlService::ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option) {
/* Get the work buffer. */
void *work = g_work_memory.jpeg_decoder_memory;
size_t work_size = sizeof(g_work_memory.jpeg_decoder_memory);
/* Call the shrink implementation. */
R_RETURN(ShrinkJpegImpl(out_size.GetPointer(), out.GetPointer(), out.GetSize(), in.GetPointer(), in.GetSize(), width, height, option, work, work_size));
}
}

View File

@@ -17,7 +17,8 @@
#include <stratosphere.hpp>
#define AMS_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO(C, H) \
AMS_SF_METHOD_INFO(C, H, 3001, Result, DecodeJpeg, (const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out, in, width, height, option))
AMS_SF_METHOD_INFO(C, H, 3001, Result, DecodeJpeg, (const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out, in, width, height, option)) \
AMS_SF_METHOD_INFO(C, H, 4001, Result, ShrinkJpeg, (ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option), (out_size, out, in, width, height, option))
AMS_SF_DEFINE_INTERFACE(ams::capsrv::sf, IDecoderControlService, AMS_CAPSRV_DECODER_CONTROL_SERVICE_INTERFACE_INFO, 0xD168E90B)
@@ -26,6 +27,7 @@ namespace ams::capsrv::server {
class DecoderControlService final {
public:
Result DecodeJpeg(const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const ScreenShotDecodeOption &option);
Result ShrinkJpeg(ams::sf::Out<u64> out_size, const ams::sf::OutNonSecureBuffer &out, const ams::sf::InBuffer &in, u32 width, u32 height, const capsrv::ScreenShotDecodeOption &option);
};
static_assert(capsrv::sf::IsIDecoderControlService<DecoderControlService>);

View File

@@ -0,0 +1,236 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "decodersrv_software_jpeg_shrinker.hpp"
#include "capsrv_server_jpeg_library_types.hpp"
#include "capsrv_server_jpeg_error_handler.hpp"
namespace ams::capsrv::server::jpeg {
#define CAPSRV_ABORT_UNLESS(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
AMS_ABORT_UNLESS(__capsrv_assert_res); \
} while (0)
#define CAPSRV_ASSERT(expr) do { \
const bool __capsrv_assert_res = (expr); \
AMS_ASSERT(__capsrv_assert_res); \
R_UNLESS(__capsrv_assert_res, capsrv::ResultAlbumError()); \
} while (0)
namespace {
constexpr s32 ImageSizeHorizonalUnit = 0x10;
constexpr s32 ImageSizeVerticalUnitDecode = 0x4;
constexpr s32 ImageSizeVerticalUnitEncode = 0x8;
constexpr s32 RgbColorComponentCount = 3;
constexpr s32 RgbaColorComponentCount = 4;
Result GetRgbBufferSize(size_t *out_size, size_t *out_stride, s32 width, size_t work_size) {
/* Calculate the space we need and verify we have enough. */
const size_t rgb_width = util::AlignUp(static_cast<size_t>(width), ImageSizeHorizonalUnit);
const size_t rgb_stride = rgb_width * RgbColorComponentCount;
const size_t rgb_size = rgb_stride * ImageSizeVerticalUnitEncode;
R_UNLESS(work_size >= rgb_size, capsrv::ResultInternalJpegWorkMemoryShortage());
/* Return the output to the caller. */
*out_size = rgb_size;
*out_stride = rgb_stride;
R_SUCCEED();
}
void SetupEncodingParameter(JpegLibraryType::jpeg_compress_struct *cinfo, const SoftwareJpegShrinkerInput &input, int quality) {
/* Set parameters. */
cinfo->image_width = static_cast<JpegLibraryType::JDIMENSION>(input.width / 2);
cinfo->image_height = static_cast<JpegLibraryType::JDIMENSION>(input.height / 2);
cinfo->input_components = RgbColorComponentCount;
cinfo->in_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
/* Set defaults/color space. */
jpeg_set_defaults(cinfo);
jpeg_set_colorspace(cinfo, JpegLibraryType::J_COLOR_SPACE::JCS_YCbCr);
/* Configure sampling. */
/* libjpeg-turbo doesn't actually have this field, as of now. */
/* cinfo->do_fancy_downsampling = false; */
cinfo->comp_info[0].h_samp_factor = 2;
cinfo->comp_info[0].v_samp_factor = 1;
cinfo->comp_info[1].h_samp_factor = 1;
cinfo->comp_info[1].v_samp_factor = 1;
cinfo->comp_info[2].h_samp_factor = 1;
cinfo->comp_info[2].v_samp_factor = 1;
/* Set the quality. */
jpeg_set_quality(cinfo, quality, true);
/* Configure remaining parameters. */
cinfo->dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
cinfo->optimize_coding = false;
cinfo->write_JFIF_header = true;
}
}
Result SoftwareJpegShrinker::ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size) {
CAPSRV_ABORT_UNLESS(util::IsAligned(input.width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(util::IsAligned(input.height, ImageSizeVerticalUnitDecode));
const u32 shrunk_width = input.width / 2;
const u32 shrunk_height = input.height / 2;
CAPSRV_ABORT_UNLESS(util::IsAligned(shrunk_width, ImageSizeHorizonalUnit));
CAPSRV_ABORT_UNLESS(output.dst != nullptr);
CAPSRV_ABORT_UNLESS(output.out_width != nullptr);
CAPSRV_ABORT_UNLESS(output.out_height != nullptr);
/* Determine work buffer extents. */
char *work_start = static_cast<char *>(work);
char *work_end = work_start + work_size;
/* Determine the buffer extents for our linebuffers. */
u8 *rgb_buffer = static_cast<u8 *>(static_cast<void *>(work_start));
size_t rgb_buffer_size;
size_t rgb_buffer_stride;
R_TRY(GetRgbBufferSize(std::addressof(rgb_buffer_size), std::addressof(rgb_buffer_stride), input.width, work_size));
/* The start of the workbuffer is reserved for linebuffer space. */
work_start += rgb_buffer_size;
/* Create our compression structures. */
JpegLibraryType::jpeg_decompress_struct dcinfo = {};
JpegLibraryType::jpeg_compress_struct ecinfo = {};
/* Here nintendo creates a work buffer structure containing work_start + work_size. */
/* This seems to be a custom patch for/to libjpeg-turbo. */
/* It would be desirable for us to mimic this, because it gives Nintendo strong */
/* fixed memory usage guarantees. */
/* TODO: Determine if it is feasible for us to recreate this ourselves, */
/* Either by adding support to the devkitPro libjpeg-turbo portlib or otherwise. */
AMS_UNUSED(work_end);
/* Create our error managers. */
JpegErrorHandler jerr_dc = { .result = ResultSuccess(), };
JpegErrorHandler jerr_ec = { .result = ResultSuccess(), };
jerr_dc.error_exit = JpegErrorHandler::HandleError,
jerr_ec.error_exit = JpegErrorHandler::HandleError,
/* Link our error managers to our compression structures. */
dcinfo.err = jpeg_std_error(std::addressof(jerr_dc));
ecinfo.err = jpeg_std_error(std::addressof(jerr_ec));
/* Use setjmp, so that on error our handler will longjmp to return an error result. */
if (setjmp(jerr_ec.jmp_buf) == 0) {
if (setjmp(jerr_dc.jmp_buf) == 0) {
/* Create our decompressor. */
jpeg_create_decompress(std::addressof(dcinfo));
ON_SCOPE_EXIT { jpeg_destroy_decompress(std::addressof(dcinfo)); };
/* Setup our memory reader, ensure the header is correct. */
jpeg_mem_src(std::addressof(dcinfo), const_cast<unsigned char *>(static_cast<const unsigned char *>(input.jpeg)), input.jpeg_size);
R_UNLESS(jpeg_read_header(std::addressof(dcinfo), true) == JPEG_HEADER_OK, capsrv::ResultAlbumInvalidFileData());
/* Ensure width and height are correct. */
R_UNLESS(dcinfo.image_width == input.width, capsrv::ResultAlbumInvalidFileData());
R_UNLESS(dcinfo.image_height == input.height, capsrv::ResultAlbumInvalidFileData());
/* Set output parameters. */
dcinfo.out_color_space = JpegLibraryType::J_COLOR_SPACE::JCS_RGB;
dcinfo.dct_method = JpegLibraryType::J_DCT_METHOD::JDCT_ISLOW;
dcinfo.do_fancy_upsampling = input.fancy_upsampling;
dcinfo.do_block_smoothing = input.block_smoothing;
dcinfo.scale_num = 1;
dcinfo.scale_denom = 2;
/* Start decompression. */
R_UNLESS(jpeg_start_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Check the parameters. */
CAPSRV_ASSERT(dcinfo.output_width == shrunk_width);
CAPSRV_ASSERT(dcinfo.output_height == shrunk_height);
CAPSRV_ASSERT(dcinfo.out_color_components == RgbColorComponentCount);
CAPSRV_ASSERT(dcinfo.output_components == RgbColorComponentCount);
/* Create our compressor. */
jpeg_create_compress(std::addressof(ecinfo));
ON_SCOPE_EXIT { jpeg_destroy_compress(std::addressof(ecinfo)); };
/* Setup our memory writer. */
unsigned long out_size = static_cast<unsigned long>(output.dst_size);
jpeg_mem_dest(std::addressof(ecinfo), reinterpret_cast<unsigned char **>(std::addressof(output.dst)), std::addressof(out_size));
/* Setup the encoding parameters. */
SetupEncodingParameter(std::addressof(ecinfo), input, quality);
/* Start compression. */
jpeg_start_compress(std::addressof(ecinfo), true);
/* Parse the scanlines. */
{
/* Create our linebuffer structure. */
JpegLibraryType::JSAMPROW linebuffers[ImageSizeVerticalUnitEncode] = {};
for (int i = 0; i < ImageSizeVerticalUnitEncode; i++) {
linebuffers[i] = rgb_buffer + rgb_buffer_stride * i;
}
/* While we still have scanlines, parse! */
while (dcinfo.output_scanline < shrunk_height) {
/* Determine remaining scanlines. */
const auto remaining_scanlines = shrunk_height - dcinfo.output_scanline;
const auto cur_max_scanlines = std::min<s32>(remaining_scanlines, ImageSizeVerticalUnitEncode);
/* If we have scanlines to decode, try to do so. */
auto writable_scanlines = 0;
while (writable_scanlines < cur_max_scanlines) {
const auto decoded = jpeg_read_scanlines(std::addressof(dcinfo), linebuffers + writable_scanlines, ImageSizeVerticalUnitDecode);
CAPSRV_ASSERT(decoded <= ImageSizeVerticalUnitDecode / 2);
writable_scanlines += decoded;
}
/* If we have scanlines to write, try to do so. */
jpeg_write_scanlines(std::addressof(ecinfo), linebuffers, writable_scanlines);
}
}
/* Finish the decompression. */
R_UNLESS(jpeg_finish_decompress(std::addressof(dcinfo)) == TRUE, capsrv::ResultAlbumInvalidFileData());
/* Finish the compression. */
jpeg_finish_compress(std::addressof(ecinfo));
/* Set the output size. */
*output.out_size = out_size;
} else {
/* Some unknown error was caught by our handler. */
R_THROW(capsrv::ResultAlbumInvalidFileData());
}
} else {
/* Return the encoding result. */
R_THROW(jerr_ec.result);
}
/* Write the size we decoded to output. */
*output.out_width = static_cast<s32>(dcinfo.output_width);
*output.out_width = static_cast<s32>(dcinfo.output_height);
R_SUCCEED();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::capsrv::server::jpeg {
struct SoftwareJpegShrinkerInput {
const void *jpeg;
size_t jpeg_size;
u32 width;
u32 height;
bool fancy_upsampling;
bool block_smoothing;
};
struct SoftwareJpegShrinkerOutput {
u64 *out_size;
s32 *out_width;
s32 *out_height;
void *dst;
size_t dst_size;
};
class SoftwareJpegShrinker {
public:
static Result ShrinkRgba8(SoftwareJpegShrinkerOutput &output, const SoftwareJpegShrinkerInput &input, int quality, void *work, size_t work_size);
};
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::erpt::srv {
Result SubmitFsInfo();
}

View File

@@ -0,0 +1,499 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "erpt_srv_fs_info.hpp"
#include "erpt_srv_context_record.hpp"
#include "erpt_srv_context.hpp"
namespace ams::erpt::srv {
namespace {
Result SubmitMmcDetailInfo() {
/* Submit the mmc cid. */
{
u8 mmc_cid[fs::MmcCidSize] = {};
if (R_SUCCEEDED(fs::GetMmcCid(mmc_cid, sizeof(mmc_cid)))) {
/* Clear the serial number from the cid. */
fs::ClearMmcCidSerialNumber(mmc_cid);
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDTypeInfo, sizeof(mmc_cid));
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add the cid. */
R_ABORT_UNLESS(record->Add(FieldId_NANDType, mmc_cid, sizeof(mmc_cid)));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
/* Submit the mmc speed mode. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDSpeedModeInfo, 0x20);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Get the speed mode. */
fs::MmcSpeedMode speed_mode{};
const auto res = fs::GetMmcSpeedMode(std::addressof(speed_mode));
if (R_SUCCEEDED(res)) {
const char *speed_mode_name = "None";
switch (speed_mode) {
case fs::MmcSpeedMode_Identification:
speed_mode_name = "Identification";
break;
case fs::MmcSpeedMode_LegacySpeed:
speed_mode_name = "LegacySpeed";
break;
case fs::MmcSpeedMode_HighSpeed:
speed_mode_name = "HighSpeed";
break;
case fs::MmcSpeedMode_Hs200:
speed_mode_name = "Hs200";
break;
case fs::MmcSpeedMode_Hs400:
speed_mode_name = "Hs400";
break;
case fs::MmcSpeedMode_Unknown:
speed_mode_name = "Unknown";
break;
default:
speed_mode_name = "UnDefined";
break;
}
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
} else {
/* Getting speed mode failed, so add the result. */
char res_str[0x20];
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
R_ABORT_UNLESS(record->Add(FieldId_NANDSpeedMode, res_str, std::strlen(res_str)));
}
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
/* Submit the mmc extended csd. */
{
u8 mmc_csd[fs::MmcExtendedCsdSize] = {};
if (R_SUCCEEDED(fs::GetMmcExtendedCsd(mmc_csd, sizeof(mmc_csd)))) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDExtendedCsd, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields from the csd. */
R_ABORT_UNLESS(record->Add(FieldId_NANDPreEolInfo, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetReEolInfo])));
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypA, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypA])));
R_ABORT_UNLESS(record->Add(FieldId_NANDDeviceLifeTimeEstTypB, static_cast<u32>(mmc_csd[fs::MmcExtendedCsdOffsetDeviceLifeTimeEstTypB])));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
/* Submit the mmc patrol count. */
{
u32 count = 0;
if (R_SUCCEEDED(fs::GetMmcPatrolCount(std::addressof(count)))) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDPatrolInfo, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add the count. */
R_ABORT_UNLESS(record->Add(FieldId_NANDPatrolCount, count));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
R_SUCCEED();
}
Result SubmitMmcErrorInfo() {
/* Get the mmc error info. */
fs::StorageErrorInfo sei = {};
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
size_t log_size = 0;
if (R_SUCCEEDED(fs::GetAndClearMmcErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
/* Submit the error info. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDErrorInfo, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationFailures, sei.num_activation_failures));
R_ABORT_UNLESS(record->Add(FieldId_NANDNumActivationErrorCorrections, sei.num_activation_error_corrections));
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteFailures, sei.num_read_write_failures));
R_ABORT_UNLESS(record->Add(FieldId_NANDNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
/* If we have a log, submit it. */
if (log_size > 0) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_NANDDriverLog, log_size);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_NANDErrorLog, log_buffer, log_size));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
R_SUCCEED();
}
Result SubmitSdCardDetailInfo() {
/* Submit the sd card cid. */
{
u8 sd_cid[fs::SdCardCidSize] = {};
if (R_SUCCEEDED(fs::GetSdCardCid(sd_cid, sizeof(sd_cid)))) {
/* Clear the serial number from the cid. */
fs::ClearSdCardCidSerialNumber(sd_cid);
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDTypeInfo, sizeof(sd_cid));
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add the cid. */
R_ABORT_UNLESS(record->Add(FieldId_MicroSDType, sd_cid, sizeof(sd_cid)));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
/* Submit the sd card speed mode. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_MicroSDSpeedModeInfo, 0x20);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Get the speed mode. */
fs::SdCardSpeedMode speed_mode{};
const auto res = fs::GetSdCardSpeedMode(std::addressof(speed_mode));
if (R_SUCCEEDED(res)) {
const char *speed_mode_name = "None";
switch (speed_mode) {
case fs::SdCardSpeedMode_Identification:
speed_mode_name = "Identification";
break;
case fs::SdCardSpeedMode_DefaultSpeed:
speed_mode_name = "DefaultSpeed";
break;
case fs::SdCardSpeedMode_HighSpeed:
speed_mode_name = "HighSpeed";
break;
case fs::SdCardSpeedMode_Sdr12:
speed_mode_name = "Sdr12";
break;
case fs::SdCardSpeedMode_Sdr25:
speed_mode_name = "Sdr25";
break;
case fs::SdCardSpeedMode_Sdr50:
speed_mode_name = "Sdr50";
break;
case fs::SdCardSpeedMode_Sdr104:
speed_mode_name = "Sdr104";
break;
case fs::SdCardSpeedMode_Ddr50:
speed_mode_name = "Ddr50";
break;
case fs::SdCardSpeedMode_Unknown:
speed_mode_name = "Unknown";
break;
default:
speed_mode_name = "UnDefined";
break;
}
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, speed_mode_name, std::strlen(speed_mode_name)));
} else {
/* Getting speed mode failed, so add the result. */
char res_str[0x20];
util::SNPrintf(res_str, sizeof(res_str), "0x%08X", res.GetValue());
R_ABORT_UNLESS(record->Add(FieldId_MicroSDSpeedMode, res_str, std::strlen(res_str)));
}
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
/* Submit the area sizes. */
{
s64 user_area_size = 0;
s64 prot_area_size = 0;
const Result res_user = fs::GetSdCardUserAreaSize(std::addressof(user_area_size));
const Result res_prot = fs::GetSdCardProtectedAreaSize(std::addressof(prot_area_size));
if (R_SUCCEEDED(res_user) || R_SUCCEEDED(res_prot)) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardSizeSpec, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add sizes. */
if (R_SUCCEEDED(res_user)) {
R_ABORT_UNLESS(record->Add(FieldId_SdCardUserAreaSize, user_area_size));
}
if (R_SUCCEEDED(res_prot)) {
R_ABORT_UNLESS(record->Add(FieldId_SdCardProtectedAreaSize, prot_area_size));
}
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
R_SUCCEED();
}
Result SubmitSdCardErrorInfo() {
/* Get the sd card error info. */
fs::StorageErrorInfo sei = {};
char log_buffer[erpt::ArrayBufferSizeDefault] = {};
size_t log_size = 0;
if (R_SUCCEEDED(fs::GetAndClearSdCardErrorInfo(std::addressof(sei), std::addressof(log_size), log_buffer, sizeof(log_buffer)))) {
/* Submit the error info. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardErrorInfo, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationFailures, sei.num_activation_failures));
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumActivationErrorCorrections, sei.num_activation_error_corrections));
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteFailures, sei.num_read_write_failures));
R_ABORT_UNLESS(record->Add(FieldId_SdCardNumReadWriteErrorCorrections, sei.num_read_write_error_corrections));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
/* If we have a log, submit it. */
if (log_size > 0) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_SdCardDriverLog, log_size);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_SdCardErrorLog, log_buffer, log_size));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
R_SUCCEED();
}
Result SubmitGameCardDetailInfo() {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardCIDInfo, fs::GameCardCidSize + fs::GameCardDeviceIdSize);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add the game card cid. */
{
u8 gc_cid[fs::GameCardCidSize] = {};
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardCid(gc_cid, sizeof(gc_cid)))) {
/* Add the cid. */
R_ABORT_UNLESS(record->Add(FieldId_GameCardCID, gc_cid, sizeof(gc_cid)));
}
}
/* Add the game card device id. */
{
u8 gc_device_id[fs::GameCardDeviceIdSize] = {};
if (fs::IsGameCardInserted() && R_SUCCEEDED(fs::GetGameCardDeviceId(gc_device_id, sizeof(gc_device_id)))) {
/* Add the cid. */
R_ABORT_UNLESS(record->Add(FieldId_GameCardDeviceId, gc_device_id, sizeof(gc_device_id)));
}
}
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
R_SUCCEED();
}
Result SubmitGameCardErrorInfo() {
/* Get the game card error info. */
fs::GameCardErrorReportInfo ei = {};
if (R_SUCCEEDED(fs::GetGameCardErrorReportInfo(std::addressof(ei)))) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_GameCardErrorInfo, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_GameCardCrcErrorCount, static_cast<u32>(ei.game_card_crc_error_num)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicCrcErrorCount, static_cast<u32>(ei.asic_crc_error_num)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshCount, static_cast<u32>(ei.refresh_num)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadRetryCount, static_cast<u32>(ei.retry_limit_out_num)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardTimeoutRetryErrorCount, static_cast<u32>(ei.timeout_retry_num)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardInsertionCount, static_cast<u32>(ei.insertion_count)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardRemovalCount, static_cast<u32>(ei.removal_count)));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicInitializeCount, ei.initialize_count));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeCount, ei.asic_reinitialize_num));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureCount, ei.asic_reinitialize_failure_num));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAsicReinitializeFailureDetail, ei.asic_reinitialize_failure_detail));
R_ABORT_UNLESS(record->Add(FieldId_GameCardRefreshSuccessCount, ei.refresh_succeeded_count));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenCount, ei.awaken_count));
R_ABORT_UNLESS(record->Add(FieldId_GameCardAwakenFailureCount, ei.awaken_failure_num));
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromInsert, ei.read_count_from_insert));
R_ABORT_UNLESS(record->Add(FieldId_GameCardReadCountFromAwaken, ei.read_count_from_awaken));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageAddress, ei.last_read_error_page_address));
R_ABORT_UNLESS(record->Add(FieldId_GameCardLastReadErrorPageCount, ei.last_read_error_page_count));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
R_SUCCEED();
}
Result SubmitFileSystemErrorInfo() {
/* Get the fsp error info. */
fs::FileSystemProxyErrorInfo ei = {};
if (R_SUCCEEDED(fs::GetAndClearFileSystemProxyErrorInfo(std::addressof(ei)))) {
/* Submit FsProxyErrorInfo. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo, fat::FatErrorNameMaxLength);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptCount, ei.rom_fs_remount_for_data_corruption_count));
R_ABORT_UNLESS(record->Add(FieldId_FsRemountForDataCorruptRetryOutCount, ei.rom_fs_unrecoverable_data_corruption_by_remount_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsError, ei.fat_fs_error.error));
R_ABORT_UNLESS(record->Add(FieldId_FatFsExtraError, ei.fat_fs_error.extra_error));
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorDrive, ei.fat_fs_error.drive_id));
R_ABORT_UNLESS(record->Add(FieldId_FatFsErrorName, ei.fat_fs_error.name, fat::FatErrorNameMaxLength));
R_ABORT_UNLESS(record->Add(FieldId_FsRecoveredByInvalidateCacheCount, ei.rom_fs_recovered_by_invalidate_cache_count));
R_ABORT_UNLESS(record->Add(FieldId_FsSaveDataIndexCount, ei.save_data_index_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFilePeakOpenCount, ei.bis_system_fat_report_info_1.open_file_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemDirectoryPeakOpenCount, ei.bis_system_fat_report_info_1.open_directory_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFilePeakOpenCount, ei.bis_user_fat_report_info_1.open_file_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserDirectoryPeakOpenCount, ei.bis_user_fat_report_info_1.open_directory_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardFilePeakOpenCount, ei.sd_card_fat_report_info_1.open_file_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardDirectoryPeakOpenCount, ei.sd_card_fat_report_info_1.open_directory_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueFileEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_file_entry_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemUniqueDirectoryEntryPeakOpenCount, ei.bis_system_fat_report_info_2.open_unique_directory_entry_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueFileEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_file_entry_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserUniqueDirectoryEntryPeakOpenCount, ei.bis_user_fat_report_info_2.open_unique_directory_entry_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueFileEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_file_entry_peak_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsSdCardUniqueDirectoryEntryPeakOpenCount, ei.sd_card_fat_report_info_2.open_unique_directory_entry_peak_count));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
/* Submit FsProxyErrorInfo2. */
{
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_FsProxyErrorInfo2, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_FsDeepRetryStartCount, ei.rom_fs_deep_retry_start_count));
R_ABORT_UNLESS(record->Add(FieldId_FsUnrecoverableByGameCardAccessFailedCount, ei.rom_fs_unrecoverable_by_game_card_access_failed_count));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeControlResult, static_cast<u8>(ei.bis_system_fat_safe_info.result)));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatErrorNumber, ei.bis_system_fat_safe_info.error_number));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisSystemFatSafeErrorNumber, ei.bis_system_fat_safe_info.safe_error_number));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeControlResult, static_cast<u8>(ei.bis_user_fat_safe_info.result)));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatErrorNumber, ei.bis_user_fat_safe_info.error_number));
R_ABORT_UNLESS(record->Add(FieldId_FatFsBisUserFatSafeErrorNumber, ei.bis_user_fat_safe_info.safe_error_number));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
}
R_SUCCEED();
}
Result SubmitMemoryReportInfo() {
/* Get the memory report info. */
fs::MemoryReportInfo mri = {};
if (R_SUCCEEDED(fs::GetAndClearMemoryReportInfo(std::addressof(mri)))) {
/* Create a record. */
auto record = std::make_unique<ContextRecord>(CategoryId_FsMemoryInfo, 0);
R_UNLESS(record != nullptr, erpt::ResultOutOfMemory());
/* Add fields. */
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferPeakFreeSize, mri.pooled_buffer_peak_free_size));
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferRetriedCount, mri.pooled_buffer_retried_count));
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferReduceAllocationCount, mri.pooled_buffer_reduce_allocation_count));
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakFreeSize, mri.buffer_manager_peak_free_size));
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerRetriedCount, mri.buffer_manager_retried_count));
R_ABORT_UNLESS(record->Add(FieldId_FsExpHeapPeakFreeSize, mri.exp_heap_peak_free_size));
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolPeakFreeSize, mri.buffer_pool_peak_free_size));
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferSuccessCount, mri.patrol_read_allocate_buffer_success_count));
R_ABORT_UNLESS(record->Add(FieldId_FsPatrolReadAllocateBufferFailureCount, mri.patrol_read_allocate_buffer_failure_count));
R_ABORT_UNLESS(record->Add(FieldId_FsBufferManagerPeakTotalAllocatableSize, mri.buffer_manager_peak_total_allocatable_size));
R_ABORT_UNLESS(record->Add(FieldId_FsBufferPoolMaxAllocateSize, mri.buffer_pool_max_allocate_size));
R_ABORT_UNLESS(record->Add(FieldId_FsPooledBufferFailedIdealAllocationCountOnAsyncAccess, mri.pooled_buffer_failed_ideal_allocation_count_on_async_access));
/* Submit the record. */
R_ABORT_UNLESS(Context::SubmitContextRecord(std::move(record)));
}
R_SUCCEED();
}
}
Result SubmitFsInfo() {
/* Temporarily disable auto-abort. */
fs::ScopedAutoAbortDisabler aad;
/* Submit various FS info. */
R_TRY(SubmitMmcDetailInfo());
R_TRY(SubmitMmcErrorInfo());
R_TRY(SubmitSdCardDetailInfo());
R_TRY(SubmitSdCardErrorInfo());
R_TRY(SubmitGameCardDetailInfo());
R_TRY(SubmitGameCardErrorInfo());
R_TRY(SubmitFileSystemErrorInfo());
R_TRY(SubmitMemoryReportInfo());
R_SUCCEED();
}
}

View File

@@ -19,6 +19,7 @@
#include "erpt_srv_journal.hpp"
#include "erpt_srv_context_record.hpp"
#include "erpt_srv_context.hpp"
#include "erpt_srv_fs_info.hpp"
namespace ams::erpt::srv {
@@ -530,9 +531,14 @@ namespace ams::erpt::srv {
SubmitResourceLimitContexts();
#endif
if (flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
/* TODO: 17.0.0 SubmitFsInfo() */
/* If we should, submit fs info. */
#if defined(ATMOSPHERE_OS_HORIZON)
if (hos::GetVersion() >= hos::Version_17_0_0 && flags.Test<CreateReportOptionFlag::SubmitFsInfo>()) {
/* NOTE: Nintendo ignores the result of this call. */
SubmitFsInfo();
}
#endif
R_SUCCEED();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
Result GetAndClearFileSystemProxyErrorInfo(FileSystemProxyErrorInfo *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Get the error info. */
AMS_FS_R_TRY(fsp->GetAndClearErrorInfo(out));
R_SUCCEED();
}
}

View File

@@ -100,4 +100,65 @@ namespace ams::fs {
R_SUCCEED();
}
bool IsGameCardInserted() {
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_ABORT_UNLESS(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get insertion status. */
bool inserted;
AMS_FS_R_ABORT_UNLESS(device_operator->IsGameCardInserted(std::addressof(inserted)));
return inserted;
}
Result GetGameCardCid(void *dst, size_t size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(size >= sizeof(gc::GameCardIdSet), fs::ResultInvalidSize());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the id set. */
gc::GameCardIdSet gc_id_set;
AMS_FS_R_TRY(device_operator->GetGameCardIdSet(sf::OutBuffer(std::addressof(gc_id_set), sizeof(gc_id_set)), static_cast<s64>(sizeof(gc_id_set))));
/* Copy the id set to output. */
std::memcpy(dst, std::addressof(gc_id_set), sizeof(gc_id_set));
R_SUCCEED();
}
Result GetGameCardDeviceId(void *dst, size_t size) {
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the cid. */
AMS_FS_R_TRY(device_operator->GetGameCardDeviceId(sf::OutBuffer(dst, size), static_cast<s64>(size)));
R_SUCCEED();
}
Result GetGameCardErrorReportInfo(GameCardErrorReportInfo *out) {
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the error report info. */
AMS_FS_R_TRY(device_operator->GetGameCardErrorReportInfo(out));
R_SUCCEED();
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
Result GetAndClearMemoryReportInfo(MemoryReportInfo *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Get the memory report info. */
AMS_FS_R_TRY(fsp->GetAndClearMemoryReportInfo(out));
R_SUCCEED();
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "fsa/fs_mount_utils.hpp"
#include "impl/fs_file_system_proxy_service_object.hpp"
#include "impl/fs_file_system_service_object_adapter.hpp"
namespace ams::fs {
Result GetMmcCid(void *dst, size_t size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the cid. */
AMS_FS_R_TRY(device_operator->GetMmcCid(sf::OutBuffer(dst, size), static_cast<s64>(size)));
R_SUCCEED();
}
Result GetMmcSpeedMode(MmcSpeedMode *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the speed mode. */
s64 speed_mode = 0;
AMS_FS_R_TRY(device_operator->GetMmcSpeedMode(std::addressof(speed_mode)));
*out = static_cast<MmcSpeedMode>(speed_mode);
R_SUCCEED();
}
Result GetMmcPatrolCount(u32 *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the patrol count. */
AMS_FS_R_TRY(device_operator->GetMmcPatrolCount(out));
R_SUCCEED();
}
Result GetAndClearMmcErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out_sei != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out_log_size != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out_log_buffer != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the error info. */
s64 log_size = 0;
AMS_FS_R_TRY(device_operator->GetAndClearMmcErrorInfo(out_sei, std::addressof(log_size), sf::OutBuffer(out_log_buffer, log_buffer_size), static_cast<s64>(log_buffer_size)));
*out_log_size = static_cast<size_t>(log_size);
R_SUCCEED();
}
Result GetMmcExtendedCsd(void *dst, size_t size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the csd. */
AMS_FS_R_TRY(device_operator->GetMmcExtendedCsd(sf::OutBuffer(dst, size), static_cast<s64>(size)));
R_SUCCEED();
}
}

View File

@@ -389,6 +389,11 @@ namespace ams::fs {
/* ... */
Result GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out) {
static_assert(sizeof(fs::FileSystemProxyErrorInfo) == sizeof(::FsFileSystemProxyErrorInfo));
R_RETURN(::fsGetAndClearErrorInfo(reinterpret_cast<::FsFileSystemProxyErrorInfo *>(out.GetPointer())));
}
Result RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
AMS_ABORT("TODO");
}
@@ -429,6 +434,11 @@ namespace ams::fs {
AMS_ABORT("TODO");
}
Result GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out) {
static_assert(sizeof(fs::MemoryReportInfo) == sizeof(::FsMemoryReportInfo));
R_RETURN(::fsGetAndClearMemoryReportInfo(reinterpret_cast<::FsMemoryReportInfo *>(out.GetPointer())));
}
/* ... */
Result GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {

View File

@@ -124,4 +124,90 @@ namespace ams::fs {
return inserted;
}
Result GetSdCardSpeedMode(SdCardSpeedMode *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the speed mode. */
s64 speed_mode = 0;
AMS_FS_R_TRY(device_operator->GetSdCardSpeedMode(std::addressof(speed_mode)));
*out = static_cast<SdCardSpeedMode>(speed_mode);
R_SUCCEED();
}
Result GetSdCardCid(void *dst, size_t size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(dst != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the cid. */
AMS_FS_R_TRY(device_operator->GetSdCardCid(sf::OutBuffer(dst, size), static_cast<s64>(size)));
R_SUCCEED();
}
Result GetSdCardUserAreaSize(s64 *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the size. */
AMS_FS_R_TRY(device_operator->GetSdCardUserAreaSize(out));
R_SUCCEED();
}
Result GetSdCardProtectedAreaSize(s64 *out) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the size. */
AMS_FS_R_TRY(device_operator->GetSdCardProtectedAreaSize(out));
R_SUCCEED();
}
Result GetAndClearSdCardErrorInfo(StorageErrorInfo *out_sei, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
/* Check pre-conditions. */
AMS_FS_R_UNLESS(out_sei != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out_log_size != nullptr, fs::ResultNullptrArgument());
AMS_FS_R_UNLESS(out_log_buffer != nullptr, fs::ResultNullptrArgument());
auto fsp = impl::GetFileSystemProxyServiceObject();
/* Open a device operator. */
sf::SharedPointer<fssrv::sf::IDeviceOperator> device_operator;
AMS_FS_R_TRY(fsp->OpenDeviceOperator(std::addressof(device_operator)));
/* Get the error info. */
s64 log_size = 0;
AMS_FS_R_TRY(device_operator->GetAndClearSdCardErrorInfo(out_sei, std::addressof(log_size), sf::OutBuffer(out_log_buffer, log_buffer_size), static_cast<s64>(log_buffer_size)));
*out_log_size = static_cast<size_t>(log_size);
R_SUCCEED();
}
}

View File

@@ -33,6 +33,48 @@ namespace ams::fs::impl {
R_RETURN(fsDeviceOperatorIsSdCardInserted(std::addressof(m_operator), out.GetPointer()));
}
Result GetSdCardSpeedMode(ams::sf::Out<s64> out) {
R_RETURN(fsDeviceOperatorGetSdCardSpeedMode(std::addressof(m_operator), out.GetPointer()));
}
Result GetSdCardCid(ams::sf::OutBuffer out, s64 size) {
R_RETURN(fsDeviceOperatorGetSdCardCid(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
}
Result GetSdCardUserAreaSize(ams::sf::Out<s64> out) {
R_RETURN(fsDeviceOperatorGetSdCardUserAreaSize(std::addressof(m_operator), out.GetPointer()));
}
Result GetSdCardProtectedAreaSize(ams::sf::Out<s64> out) {
R_RETURN(fsDeviceOperatorGetSdCardProtectedAreaSize(std::addressof(m_operator), out.GetPointer()));
}
Result GetAndClearSdCardErrorInfo(ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size) {
static_assert(sizeof(::FsStorageErrorInfo) == sizeof(fs::StorageErrorInfo));
R_RETURN(fsDeviceOperatorGetAndClearSdCardErrorInfo(std::addressof(m_operator), reinterpret_cast<::FsStorageErrorInfo *>(out_sei.GetPointer()), out_size.GetPointer(), out_buf.GetPointer(), out_buf.GetSize(), size));
}
Result GetMmcCid(ams::sf::OutBuffer out, s64 size) {
R_RETURN(fsDeviceOperatorGetMmcCid(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
}
Result GetMmcSpeedMode(ams::sf::Out<s64> out) {
R_RETURN(fsDeviceOperatorGetMmcSpeedMode(std::addressof(m_operator), out.GetPointer()));
}
Result GetMmcPatrolCount(ams::sf::Out<u32> out) {
R_RETURN(fsDeviceOperatorGetMmcPatrolCount(std::addressof(m_operator), out.GetPointer()));
}
Result GetAndClearMmcErrorInfo(ams::sf::Out<fs::StorageErrorInfo> out_sei, ams::sf::Out<s64> out_size, ams::sf::OutBuffer out_buf, s64 size) {
static_assert(sizeof(::FsStorageErrorInfo) == sizeof(fs::StorageErrorInfo));
R_RETURN(fsDeviceOperatorGetAndClearMmcErrorInfo(std::addressof(m_operator), reinterpret_cast<::FsStorageErrorInfo *>(out_sei.GetPointer()), out_size.GetPointer(), out_buf.GetPointer(), out_buf.GetSize(), size));
}
Result GetMmcExtendedCsd(ams::sf::OutBuffer out, s64 size) {
R_RETURN(fsDeviceOperatorGetMmcExtendedCsd(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
}
Result IsGameCardInserted(ams::sf::Out<bool> out) {
R_RETURN(fsDeviceOperatorIsGameCardInserted(std::addressof(m_operator), out.GetPointer()));
}
@@ -41,6 +83,19 @@ namespace ams::fs::impl {
static_assert(sizeof(::FsGameCardHandle) == sizeof(u32));
R_RETURN(fsDeviceOperatorGetGameCardHandle(std::addressof(m_operator), reinterpret_cast<::FsGameCardHandle *>(out.GetPointer())));
}
Result GetGameCardIdSet(ams::sf::OutBuffer out, s64 size) {
R_RETURN(fsDeviceOperatorGetGameCardIdSet(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
}
Result GetGameCardErrorReportInfo(ams::sf::Out<fs::GameCardErrorReportInfo> out) {
static_assert(sizeof(::FsGameCardErrorReportInfo) == sizeof(fs::GameCardErrorReportInfo));
R_RETURN(fsDeviceOperatorGetGameCardErrorReportInfo(std::addressof(m_operator), reinterpret_cast<::FsGameCardErrorReportInfo *>(out.GetPointer())));
}
Result GetGameCardDeviceId(ams::sf::OutBuffer out, s64 size) {
R_RETURN(fsDeviceOperatorGetGameCardDeviceId(std::addressof(m_operator), out.GetPointer(), out.GetSize(), size));
}
};
static_assert(fssrv::sf::IsIDeviceOperator<RemoteDeviceOperator>);
#endif

View File

@@ -307,6 +307,10 @@ namespace ams::fssrv {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetProgramId(ams::sf::Out<ncm::ProgramId> out_program_id, const fssrv::sf::FspPath &path, fs::ContentAttributes attr) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetRightsIdByPath(ams::sf::Out<fs::RightsId> out, const fssrv::sf::FspPath &path) {
AMS_ABORT("TODO");
}
@@ -369,6 +373,10 @@ namespace ams::fssrv {
/* ... */
Result FileSystemProxyImpl::GetAndClearErrorInfo(ams::sf::Out<fs::FileSystemProxyErrorInfo> out) {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::RegisterProgramIndexMapInfo(const ams::sf::InBuffer &buffer, s32 count) {
AMS_ABORT("TODO");
}
@@ -409,6 +417,10 @@ namespace ams::fssrv {
AMS_ABORT("TODO");
}
Result FileSystemProxyImpl::GetAndClearMemoryReportInfo(ams::sf::Out<fs::MemoryReportInfo> out) {
AMS_ABORT("TODO");
}
/* ... */
Result FileSystemProxyImpl::GetProgramIndexForAccessLog(ams::sf::Out<u32> out_idx, ams::sf::Out<u32> out_count) {

View File

@@ -784,27 +784,31 @@ namespace ams::ncm {
}
Result ContentManagerImpl::BuildContentMetaDatabase(StorageId storage_id) {
/* NOTE: we build on 17.0.0+, to work around a change in Nintendo save handling behavior. */
if (hos::GetVersion() < hos::Version_5_0_0 || hos::GetVersion() >= hos::Version_17_0_0) {
/* Temporarily activate the database. */
R_TRY(this->ActivateContentMetaDatabase(storage_id));
ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); };
/* Open the content meta database and storage. */
ContentMetaDatabase meta_db;
ContentStorage storage;
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id));
R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id));
/* Create a builder, and build. */
ContentMetaDatabaseBuilder builder(std::addressof(meta_db));
R_RETURN(builder.BuildFromStorage(std::addressof(storage)));
if (hos::GetVersion() < hos::Version_5_0_0) {
/* On < 5.0.0, perform an actual build of the database. */
R_RETURN(this->BuildContentMetaDatabaseImpl(storage_id));
} else {
/* On 5.0.0+, building just performs an import. */
R_RETURN(this->ImportContentMetaDatabase(storage_id, false));
}
}
Result ContentManagerImpl::BuildContentMetaDatabaseImpl(StorageId storage_id) {
/* Temporarily activate the database. */
R_TRY(this->ActivateContentMetaDatabase(storage_id));
ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(storage_id); };
/* Open the content meta database and storage. */
ContentMetaDatabase meta_db;
ContentStorage storage;
R_TRY(ncm::OpenContentMetaDatabase(std::addressof(meta_db), storage_id));
R_TRY(ncm::OpenContentStorage(std::addressof(storage), storage_id));
/* Create a builder, and build. */
ContentMetaDatabaseBuilder builder(std::addressof(meta_db));
R_RETURN(builder.BuildFromStorage(std::addressof(storage)));
}
Result ContentManagerImpl::ImportContentMetaDatabase(StorageId storage_id, bool from_signed_partition) {
/* Only support importing BuiltInSystem. */
AMS_ABORT_UNLESS(storage_id == StorageId::BuiltInSystem);
@@ -832,6 +836,31 @@ namespace ams::ncm {
R_SUCCEED();
}
bool ContentManagerImpl::IsNeedRebuildSystemContentMetaDatabase() {
/* TODO: Should hos::GetVersion() >= hos::Version_17_0_0 be checked? */
/* If we do not actually have a content meta db, we should re-build. */
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
return true;
}
/* We have a content meta db. Temporarily, activate it. */
if (R_FAILED(this->ActivateContentMetaDatabase(StorageId::BuiltInSystem))) {
return true;
}
ON_SCOPE_EXIT { this->InactivateContentMetaDatabase(StorageId::BuiltInSystem); };
/* Open the content meta db. */
ContentMetaDatabase meta_db;
R_ABORT_UNLESS(ncm::OpenContentMetaDatabase(std::addressof(meta_db), StorageId::BuiltInSystem));
/* List the meta db's contents. */
const auto list_count = meta_db.ListContentMeta(nullptr, 0);
/* We need to rebuild if the db has zero entries. */
return list_count.total == 0;
}
Result ContentManagerImpl::Initialize(const ContentManagerConfig &config) {
/* Initialize based on whether integrated content is enabled. */
if (config.IsIntegratedSystemContentEnabled()) {
@@ -942,13 +971,26 @@ namespace ams::ncm {
}
R_TRY(this->ActivateContentStorage(StorageId::BuiltInSystem));
/* NOTE: This logic is unofficial. */
/* Beginning with 17.0.0+, save management behavior changed. The primary symptom of this is either verify fail */
/* or an empty kvs, both of which we can fix by performing a rebuild. */
if (this->IsNeedRebuildSystemContentMetaDatabase()) {
/* Clean up the system content meta database, to ensure creation can succeed. */
this->CleanupContentMetaDatabase(StorageId::BuiltInSystem);
/* Create the content meta database. */
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
/* Rebuild the content meta database. */
R_TRY(this->BuildContentMetaDatabaseImpl(StorageId::BuiltInSystem));
}
/* Setup the content meta database for system. */
if (R_FAILED(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem))) {
R_TRY(this->CreateContentMetaDatabase(StorageId::BuiltInSystem));
/* Try to build or import a database, depending on our configuration. */
/* NOTE: To work around a change in save management behavior in 17.0.0+, we build the database if needed. */
if (manager_config.ShouldBuildDatabase() || hos::GetVersion() >= hos::Version_17_0_0) {
if (manager_config.ShouldBuildDatabase()) {
/* If we should build the database, do so. */
R_TRY(this->BuildContentMetaDatabase(StorageId::BuiltInSystem));
R_TRY(this->VerifyContentMetaDatabase(StorageId::BuiltInSystem));

View File

@@ -28,13 +28,13 @@ namespace ams::pm::info {
R_RETURN(pminfoAtmosphereGetProcessId(reinterpret_cast<u64 *>(out_process_id), static_cast<u64>(program_id)));
}
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out) {
static_assert(sizeof(pm::ResourceLimitValues) == sizeof(::PmResourceLimitValues));
Result GetAppletResourceLimitCurrentValue(pm::ResourceLimitValue *out) {
static_assert(sizeof(pm::ResourceLimitValue) == sizeof(::PmResourceLimitValues));
R_RETURN(pminfoGetAppletCurrentResourceLimitValues(reinterpret_cast<PmResourceLimitValues *>(out)));
}
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out) {
static_assert(sizeof(pm::ResourceLimitValues) == sizeof(::PmResourceLimitValues));
Result GetAppletResourceLimitPeakValue(pm::ResourceLimitValue *out) {
static_assert(sizeof(pm::ResourceLimitValue) == sizeof(::PmResourceLimitValues));
R_RETURN(pminfoGetAppletPeakResourceLimitValues(reinterpret_cast<PmResourceLimitValues *>(out)));
}

View File

@@ -17,7 +17,7 @@
#define ATMOSPHERE_RELEASE_VERSION_MAJOR 1
#define ATMOSPHERE_RELEASE_VERSION_MINOR 6
#define ATMOSPHERE_RELEASE_VERSION_MICRO 0
#define ATMOSPHERE_RELEASE_VERSION_MICRO 2
#define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO

View File

@@ -54,6 +54,7 @@ namespace ams::capsrv {
R_DEFINE_ERROR_RANGE(InternalError, 1024, 2047);
R_DEFINE_ERROR_RESULT(InternalJpegEncoderError, 1210);
R_DEFINE_ERROR_RESULT(InternalJpegOutBufferShortage, 1211);
R_DEFINE_ERROR_RESULT(InternalJpegWorkMemoryShortage, 1212);
R_DEFINE_ERROR_RANGE(InternalFileDataVerificationError, 1300, 1399);

View File

@@ -1,7 +1,7 @@
{
"name": "ProcessManager",
"title_id": "0x0100000000000003",
"main_thread_stack_size": "0x00002000",
"main_thread_stack_size": "0x00003000",
"main_thread_priority": 49,
"default_cpu_id": 3,
"process_category": 1,

View File

@@ -18,6 +18,64 @@
namespace ams::pm::impl {
namespace {
template<size_t MaxProcessInfos>
class ProcessInfoAllocator {
NON_COPYABLE(ProcessInfoAllocator);
NON_MOVEABLE(ProcessInfoAllocator);
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
private:
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
bool m_process_info_allocated[MaxProcessInfos]{};
os::SdkMutex m_lock{};
private:
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
return process_info - GetPointer(m_process_info_storages[0]);
}
public:
constexpr ProcessInfoAllocator() = default;
template<typename... Args>
ProcessInfo *AllocateProcessInfo(Args &&... args) {
std::scoped_lock lk(m_lock);
for (size_t i = 0; i < MaxProcessInfos; i++) {
if (!m_process_info_allocated[i]) {
m_process_info_allocated[i] = true;
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
}
}
return nullptr;
}
void FreeProcessInfo(ProcessInfo *process_info) {
std::scoped_lock lk(m_lock);
const size_t index = this->GetProcessInfoIndex(process_info);
AMS_ABORT_UNLESS(index < MaxProcessInfos);
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
util::DestroyAt(m_process_info_storages[index]);
m_process_info_allocated[index] = false;
}
};
/* Process lists. */
constinit ProcessList g_process_list;
constinit ProcessList g_exit_list;
/* Process Info Allocation. */
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
constexpr size_t MaxProcessCount = 0x60;
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
}
ProcessInfo::ProcessInfo(os::NativeHandle h, os::ProcessId pid, ldr::PinId pin, const ncm::ProgramLocation &l, const cfg::OverrideStatus &s) : m_process_id(pid), m_pin_id(pin), m_loc(l), m_status(s), m_handle(h), m_state(svc::ProcessState_Created), m_flags(0) {
os::InitializeMultiWaitHolder(std::addressof(m_multi_wait_holder), m_handle);
os::SetMultiWaitHolderUserData(std::addressof(m_multi_wait_holder), reinterpret_cast<uintptr_t>(this));
@@ -37,10 +95,27 @@ namespace ams::pm::impl {
/* Close the process's handle. */
os::CloseNativeHandle(m_handle);
m_handle = os::InvalidNativeHandle;
/* Unlink the process from its multi wait. */
os::UnlinkMultiWaitHolder(std::addressof(m_multi_wait_holder));
}
}
ProcessListAccessor GetProcessList() {
return ProcessListAccessor(g_process_list);
}
ProcessListAccessor GetExitList() {
return ProcessListAccessor(g_exit_list);
}
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status) {
return g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
}
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
/* Remove the process from the list. */
list->Remove(process_info);
/* Delete the process. */
g_process_info_allocator.FreeProcessInfo(process_info);
}
}

View File

@@ -64,8 +64,8 @@ namespace ams::pm::impl {
~ProcessInfo();
void Cleanup();
void LinkToMultiWait(os::MultiWaitType &multi_wait) {
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(m_multi_wait_holder));
os::MultiWaitHolderType *GetMultiWaitHolder() {
return std::addressof(m_multi_wait_holder);
}
os::NativeHandle GetHandle() const {
@@ -232,4 +232,10 @@ namespace ams::pm::impl {
}
};
ProcessListAccessor GetProcessList();
ProcessListAccessor GetExitList();
ProcessInfo *AllocateProcessInfo(svc::Handle process_handle, os::ProcessId process_id, ldr::PinId pin_id, const ncm::ProgramLocation &location, const cfg::OverrideStatus &override_status);
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info);
}

View File

@@ -15,9 +15,9 @@
*/
#include <stratosphere.hpp>
#include "pm_process_manager.hpp"
#include "pm_resource_manager.hpp"
#include "pm_process_tracker.hpp"
#include "pm_process_info.hpp"
#include "pm_spec.hpp"
namespace ams::pm::impl {
@@ -29,13 +29,7 @@ namespace ams::pm::impl {
HookType_Application = (1 << 1),
};
struct LaunchProcessArgs {
os::ProcessId *out_process_id;
ncm::ProgramLocation location;
u32 flags;
};
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
#define GET_FLAG_MASK(flag) (hos_version >= hos::Version_5_0_0 ? static_cast<u32>(LaunchFlags_##flag) : static_cast<u32>(LaunchFlagsDeprecated_##flag))
inline bool ShouldSignalOnExit(u32 launch_flags) {
const auto hos_version = hos::GetVersion();
@@ -70,115 +64,26 @@ namespace ams::pm::impl {
return launch_flags & GET_FLAG_MASK(DisableAslr);
}
#undef GET_FLAG_MASK
template<size_t MaxProcessInfos>
class ProcessInfoAllocator {
NON_COPYABLE(ProcessInfoAllocator);
NON_MOVEABLE(ProcessInfoAllocator);
static_assert(MaxProcessInfos >= 0x40, "MaxProcessInfos is too small.");
private:
util::TypedStorage<ProcessInfo> m_process_info_storages[MaxProcessInfos]{};
bool m_process_info_allocated[MaxProcessInfos]{};
os::SdkMutex m_lock{};
private:
constexpr inline size_t GetProcessInfoIndex(ProcessInfo *process_info) const {
return process_info - GetPointer(m_process_info_storages[0]);
}
public:
constexpr ProcessInfoAllocator() = default;
template<typename... Args>
ProcessInfo *AllocateProcessInfo(Args &&... args) {
std::scoped_lock lk(m_lock);
for (size_t i = 0; i < MaxProcessInfos; i++) {
if (!m_process_info_allocated[i]) {
m_process_info_allocated[i] = true;
std::memset(m_process_info_storages + i, 0, sizeof(m_process_info_storages[i]));
return util::ConstructAt(m_process_info_storages[i], std::forward<Args>(args)...);
}
}
return nullptr;
}
void FreeProcessInfo(ProcessInfo *process_info) {
std::scoped_lock lk(m_lock);
const size_t index = this->GetProcessInfoIndex(process_info);
AMS_ABORT_UNLESS(index < MaxProcessInfos);
AMS_ABORT_UNLESS(m_process_info_allocated[index]);
util::DestroyAt(m_process_info_storages[index]);
m_process_info_allocated[index] = false;
}
};
#undef GET_FLAG_MASK
/* Process Tracking globals. */
void ProcessTrackingMain(void *);
constinit os::ThreadType g_process_track_thread;
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[16_KB];
/* Process lists. */
constinit ProcessList g_process_list;
constinit ProcessList g_dead_process_list;
/* Process Info Allocation. */
/* Note: The kernel slabheap is size 0x50 -- we allow slightly larger to account for the dead process list. */
constexpr size_t MaxProcessCount = 0x60;
constinit ProcessInfoAllocator<MaxProcessCount> g_process_info_allocator;
constinit ProcessTracker g_process_tracker;
alignas(os::ThreadStackAlignment) constinit u8 g_process_track_thread_stack[8_KB];
/* Global events. */
constinit os::SystemEventType g_process_event;
constinit os::SystemEventType g_hook_to_create_process_event;
constinit os::SystemEventType g_hook_to_create_application_process_event;
constinit os::SystemEventType g_boot_finished_event;
/* Process Launch synchronization globals. */
constinit os::SdkMutex g_launch_program_lock;
os::Event g_process_launch_start_event(os::EventClearMode_AutoClear);
os::Event g_process_launch_finish_event(os::EventClearMode_AutoClear);
constinit Result g_process_launch_result = ResultSuccess();
constinit LaunchProcessArgs g_process_launch_args = {};
/* Hook globals. */
constinit std::atomic<ncm::ProgramId> g_program_id_hook;
constinit std::atomic<bool> g_application_hook;
/* Forward declarations. */
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args);
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info);
/* Helpers. */
void ProcessTrackingMain(void *) {
/* This is the main loop of the process tracking thread. */
/* Setup multi wait/holders. */
os::MultiWaitType process_multi_wait;
os::MultiWaitHolderType start_event_holder;
os::InitializeMultiWait(std::addressof(process_multi_wait));
os::InitializeMultiWaitHolder(std::addressof(start_event_holder), g_process_launch_start_event.GetBase());
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(start_event_holder));
while (true) {
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
if (signaled_holder == std::addressof(start_event_holder)) {
/* Launch start event signaled. */
/* TryWait will clear signaled, preventing duplicate notifications. */
if (g_process_launch_start_event.TryWait()) {
g_process_launch_result = LaunchProcess(process_multi_wait, g_process_launch_args);
g_process_launch_finish_event.Signal();
}
} else {
/* Some process was signaled. */
ProcessListAccessor list(g_process_list);
OnProcessSignaled(list, reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
}
}
void CreateDebuggerEvent() {
/* Create debugger hook events. */
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
}
inline u32 GetLoaderCreateProcessFlags(u32 launch_flags) {
@@ -195,7 +100,7 @@ namespace ams::pm::impl {
}
bool HasApplicationProcess() {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
if (process.IsApplication()) {
@@ -212,19 +117,14 @@ namespace ams::pm::impl {
R_SUCCEED();
}
void CleanupProcessInfo(ProcessListAccessor &list, ProcessInfo *process_info) {
/* Remove the process from the list. */
list->Remove(process_info);
Result LaunchProgramImpl(ProcessInfo **out_process_info, os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
/* Set the output to nullptr, if we fail. */
*out_process_info = nullptr;
/* Delete the process. */
g_process_info_allocator.FreeProcessInfo(process_info);
}
Result LaunchProcess(os::MultiWaitType &multi_wait, const LaunchProcessArgs &args) {
/* Get Program Info. */
ldr::ProgramInfo program_info;
cfg::OverrideStatus override_status;
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), args.location));
R_TRY(ldr::pm::AtmosphereGetProgramInfo(std::addressof(program_info), std::addressof(override_status), loc));
const bool is_application = (program_info.flags & ldr::ProgramInfoFlag_ApplicationTypeMask) == ldr::ProgramInfoFlag_Application;
const bool allow_debug = (program_info.flags & ldr::ProgramInfoFlag_AllowDebug) || hos::GetVersion() < hos::Version_2_0_0;
@@ -232,14 +132,14 @@ namespace ams::pm::impl {
R_UNLESS(!is_application || !HasApplicationProcess(), pm::ResultApplicationRunning());
/* Fix the program location to use the right program id. */
const ncm::ProgramLocation location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(args.location.storage_id));
const ncm::ProgramLocation fixed_location = ncm::ProgramLocation::Make(program_info.program_id, static_cast<ncm::StorageId>(loc.storage_id));
/* Pin and create the process. */
os::NativeHandle process_handle;
ldr::PinId pin_id;
{
/* Pin the program with loader. */
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), location, override_status));
R_TRY(ldr::pm::AtmospherePinProgram(std::addressof(pin_id), fixed_location, override_status));
/* If we fail after now, unpin. */
ON_RESULT_FAILURE { ldr::pm::UnpinProgram(pin_id); };
@@ -263,29 +163,28 @@ namespace ams::pm::impl {
ON_RESULT_FAILURE_2 { if (mitm_boost_size > 0 || is_application) { R_ABORT_UNLESS(BoostSystemMemoryResourceLimitForMitm(0)); } };
/* Ensure resources are available. */
resource::WaitResourceAvailable(std::addressof(program_info));
WaitResourceAvailable(std::addressof(program_info));
/* Actually create the process. */
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(args.flags), resource::GetResourceLimitHandle(std::addressof(program_info))));
R_TRY(ldr::pm::CreateProcess(std::addressof(process_handle), pin_id, GetLoaderCreateProcessFlags(flags), GetResourceLimitHandle(std::addressof(program_info))));
}
/* Get the process id. */
os::ProcessId process_id = os::GetProcessId(process_handle);
/* Make new process info. */
ProcessInfo *process_info = g_process_info_allocator.AllocateProcessInfo(process_handle, process_id, pin_id, location, override_status);
ProcessInfo *process_info = AllocateProcessInfo(process_handle, process_id, pin_id, fixed_location, override_status);
AMS_ABORT_UNLESS(process_info != nullptr);
/* Link new process info. */
/* Add the new process info to the process list. */
{
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
list->push_back(*process_info);
process_info->LinkToMultiWait(multi_wait);
}
/* Prevent resource leakage if register fails. */
ON_RESULT_FAILURE {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
process_info->Cleanup();
CleanupProcessInfo(list, process_info);
};
@@ -296,166 +195,73 @@ namespace ams::pm::impl {
const u8 *aci_fah = acid_fac + program_info.acid_fac_size;
/* Register with FS and SM. */
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(location.program_id), static_cast<NcmStorageId>(location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
R_TRY(sm::manager::RegisterProcess(process_id, location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
R_TRY(fsprRegisterProgram(static_cast<u64>(process_id), static_cast<u64>(fixed_location.program_id), static_cast<NcmStorageId>(fixed_location.storage_id), aci_fah, program_info.aci_fah_size, acid_fac, program_info.acid_fac_size));
R_TRY(sm::manager::RegisterProcess(process_id, fixed_location.program_id, override_status, acid_sac, program_info.acid_sac_size, aci_sac, program_info.aci_sac_size));
/* Set flags. */
if (is_application) {
process_info->SetApplication();
}
if (ShouldSignalOnStart(args.flags) && allow_debug) {
if (ShouldSignalOnStart(flags) && allow_debug) {
process_info->SetSignalOnStart();
}
if (ShouldSignalOnExit(args.flags)) {
if (ShouldSignalOnExit(flags)) {
process_info->SetSignalOnExit();
}
if (ShouldSignalOnDebugEvent(args.flags) && allow_debug) {
if (ShouldSignalOnDebugEvent(flags) && allow_debug) {
process_info->SetSignalOnDebugEvent();
}
/* Process hooks/signaling. */
if (location.program_id == g_program_id_hook) {
if (fixed_location.program_id == g_program_id_hook) {
os::SignalSystemEvent(std::addressof(g_hook_to_create_process_event));
g_program_id_hook = ncm::InvalidProgramId;
} else if (is_application && g_application_hook) {
os::SignalSystemEvent(std::addressof(g_hook_to_create_application_process_event));
g_application_hook = false;
} else if (!ShouldStartSuspended(args.flags)) {
} else if (!ShouldStartSuspended(flags)) {
R_TRY(StartProcess(process_info, std::addressof(program_info)));
}
*args.out_process_id = process_id;
*out_process_id = process_id;
*out_process_info = process_info;
R_SUCCEED();
}
void OnProcessSignaled(ProcessListAccessor &list, ProcessInfo *process_info) {
/* Reset the process's signal. */
svc::ResetSignal(process_info->GetHandle());
/* Update the process's state. */
const svc::ProcessState old_state = process_info->GetState();
{
s64 tmp = 0;
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
process_info->SetState(static_cast<svc::ProcessState>(tmp));
}
const svc::ProcessState new_state = process_info->GetState();
/* If we're transitioning away from crashed, clear waiting attached. */
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
process_info->ClearExceptionWaitingAttach();
}
switch (new_state) {
case svc::ProcessState_Created:
case svc::ProcessState_CreatedAttached:
case svc::ProcessState_Terminating:
break;
case svc::ProcessState_Running:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
process_info->SetStartedStateChanged();
process_info->ClearSignalOnStart();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->ClearUnhandledException();
break;
case svc::ProcessState_Crashed:
if (!process_info->HasUnhandledException()) {
process_info->SetExceptionOccurred();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->SetExceptionWaitingAttach();
break;
case svc::ProcessState_RunningAttached:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->ClearUnhandledException();
break;
case svc::ProcessState_Terminated:
/* Free process resources, unlink from multi wait. */
process_info->Cleanup();
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
os::SignalSystemEvent(std::addressof(g_process_event));
} else {
/* Handle the case where we need to keep the process alive some time longer. */
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
/* Remove from the living list. */
list->Remove(process_info);
/* Add the process to the list of dead processes. */
{
ProcessListAccessor dead_list(g_dead_process_list);
dead_list->push_back(*process_info);
}
/* Signal. */
os::SignalSystemEvent(std::addressof(g_process_event));
} else {
/* Actually delete process. */
CleanupProcessInfo(list, process_info);
}
}
break;
case svc::ProcessState_DebugBreak:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->SetSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
}
break;
}
}
}
/* Initialization. */
Result InitializeProcessManager() {
/* Create events. */
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_process_event), os::EventClearMode_AutoClear, true));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_hook_to_create_application_process_event), os::EventClearMode_AutoClear, true));
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
CreateProcessEvent();
CreateDebuggerEvent();
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_boot_finished_event), os::EventClearMode_AutoClear, true));
/* Initialize resource limits. */
R_TRY(resource::InitializeResourceManager());
R_TRY(InitializeSpec());
/* Create thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_process_track_thread), ProcessTrackingMain, nullptr, g_process_track_thread_stack, sizeof(g_process_track_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
os::SetThreadNamePointer(std::addressof(g_process_track_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
/* Initialize the process tracker. */
g_process_tracker.Initialize(g_process_track_thread_stack, sizeof(g_process_track_thread_stack));
/* Start thread. */
os::StartThread(std::addressof(g_process_track_thread));
/* Start the process tracker thread. */
g_process_tracker.StartThread();
R_SUCCEED();
}
/* Process Management. */
Result LaunchProgram(os::ProcessId *out_process_id, const ncm::ProgramLocation &loc, u32 flags) {
/* Ensure we only try to launch one program at a time. */
std::scoped_lock lk(g_launch_program_lock);
/* Launch the program. */
ProcessInfo *process_info = nullptr;
R_TRY(LaunchProgramImpl(std::addressof(process_info), out_process_id, loc, flags));
/* Set global arguments, signal, wait. */
g_process_launch_args = {
.out_process_id = out_process_id,
.location = loc,
.flags = flags,
};
g_process_launch_start_event.Signal();
g_process_launch_finish_event.Wait();
R_RETURN(g_process_launch_result);
/* Register the process info with the tracker. */
g_process_tracker.QueueEntry(process_info);
R_SUCCEED();
}
Result StartProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -467,7 +273,7 @@ namespace ams::pm::impl {
}
Result TerminateProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -476,7 +282,7 @@ namespace ams::pm::impl {
}
Result TerminateProgram(ncm::ProgramId program_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -484,15 +290,10 @@ namespace ams::pm::impl {
R_RETURN(svc::TerminateProcess(process_info->GetHandle()));
}
Result GetProcessEventHandle(os::NativeHandle *out) {
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
R_SUCCEED();
}
Result GetProcessEventInfo(ProcessEventInfo *out) {
/* Check for event from current process. */
{
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
@@ -528,14 +329,14 @@ namespace ams::pm::impl {
/* Check for event from exited process. */
if (hos::GetVersion() >= hos::Version_5_0_0) {
ProcessListAccessor dead_list(g_dead_process_list);
auto exit_list = GetExitList();
if (!dead_list->empty()) {
auto &process_info = dead_list->front();
out->event = GetProcessEventValue(ProcessEvent::Exited);
if (!exit_list->empty()) {
auto &process_info = exit_list->front();
out->event = GetProcessEventValue(ProcessEvent::Exited);
out->process_id = process_info.GetProcessId();
CleanupProcessInfo(dead_list, std::addressof(process_info));
CleanupProcessInfo(exit_list, std::addressof(process_info));
R_SUCCEED();
}
}
@@ -546,7 +347,7 @@ namespace ams::pm::impl {
}
Result CleanupProcess(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -557,7 +358,7 @@ namespace ams::pm::impl {
}
Result ClearExceptionOccurred(os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -575,7 +376,7 @@ namespace ams::pm::impl {
}
Result GetExceptionProcessIdList(u32 *out_count, os::ProcessId *out_process_ids, size_t max_out_count) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
size_t count = 0;
@@ -596,7 +397,7 @@ namespace ams::pm::impl {
}
Result GetProcessId(os::ProcessId *out, ncm::ProgramId program_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(program_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -606,7 +407,7 @@ namespace ams::pm::impl {
}
Result GetProgramId(ncm::ProgramId *out, os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -616,7 +417,7 @@ namespace ams::pm::impl {
}
Result GetApplicationProcessId(os::ProcessId *out_process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
for (auto &process : *list) {
if (process.IsApplication()) {
@@ -629,7 +430,7 @@ namespace ams::pm::impl {
}
Result AtmosphereGetProcessInfo(os::NativeHandle *out_process_handle, ncm::ProgramLocation *out_loc, cfg::OverrideStatus *out_status, os::ProcessId process_id) {
ProcessListAccessor list(g_process_list);
auto list = GetProcessList();
auto process_info = list->Find(process_id);
R_UNLESS(process_info != nullptr, pm::ResultProcessNotFound());
@@ -706,33 +507,4 @@ namespace ams::pm::impl {
R_SUCCEED();
}
/* Resource Limit API. */
Result BoostSystemMemoryResourceLimit(u64 boost_size) {
R_RETURN(resource::BoostSystemMemoryResourceLimit(boost_size));
}
Result BoostApplicationThreadResourceLimit() {
R_RETURN(resource::BoostApplicationThreadResourceLimit());
}
Result BoostSystemThreadResourceLimit() {
R_RETURN(resource::BoostSystemThreadResourceLimit());
}
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out) {
R_RETURN(resource::GetCurrentResourceLimitValues(ResourceLimitGroup_Applet, out));
}
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out) {
R_RETURN(resource::GetPeakResourceLimitValues(ResourceLimitGroup_Applet, out));
}
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource) {
R_RETURN(resource::GetResourceLimitValues(out_cur_val, out_lim_val, static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
}
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size) {
R_RETURN(resource::BoostSystemMemoryResourceLimitForMitm(boost_size));
}
}

View File

@@ -48,13 +48,4 @@ namespace ams::pm::impl {
Result NotifyBootFinished();
Result GetBootFinishedEventHandle(os::NativeHandle *out);
/* Resource Limit API. */
Result BoostSystemMemoryResourceLimit(u64 boost_size);
Result BoostApplicationThreadResourceLimit();
Result BoostSystemThreadResourceLimit();
Result GetAppletCurrentResourceLimitValues(pm::ResourceLimitValues *out);
Result GetAppletPeakResourceLimitValues(pm::ResourceLimitValues *out);
Result AtmosphereGetCurrentLimitInfo(s64 *out_cur_val, s64 *out_lim_val, u32 group, u32 resource);
Result BoostSystemMemoryResourceLimitForMitm(u64 boost_size);
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_process_tracker.hpp"
#include "pm_process_info.hpp"
#include "pm_spec.hpp"
namespace ams::pm::impl {
namespace {
/* Global process event. */
constinit os::SystemEventType g_process_event;
}
void ProcessTracker::Initialize(void *stack, size_t stack_size) {
/* Initialize our events. */
os::InitializeEvent(std::addressof(m_request_event), false, os::EventClearMode_AutoClear);
os::InitializeEvent(std::addressof(m_reply_event), false, os::EventClearMode_AutoClear);
/* Our process count should initially be zero. */
m_process_count = 0;
/* Create the process tracking thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_thread), ProcessTracker::ThreadFunction, this, stack, stack_size, AMS_GET_SYSTEM_THREAD_PRIORITY(pm, ProcessTrack)));
os::SetThreadNamePointer(std::addressof(m_thread), AMS_GET_SYSTEM_THREAD_NAME(pm, ProcessTrack));
}
void ProcessTracker::StartThread() {
/* Start thread. */
os::StartThread(std::addressof(m_thread));
}
void ProcessTracker::ThreadBody() {
/* This is the main loop of the process tracking thread. */
/* Setup multi wait/holders. */
os::MultiWaitType process_multi_wait;
os::MultiWaitHolderType enqueue_event_holder;
os::InitializeMultiWait(std::addressof(process_multi_wait));
os::InitializeMultiWaitHolder(std::addressof(enqueue_event_holder), std::addressof(m_request_event));
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), std::addressof(enqueue_event_holder));
while (true) {
auto signaled_holder = os::WaitAny(std::addressof(process_multi_wait));
if (signaled_holder == std::addressof(enqueue_event_holder)) {
/* TryWait will clear signaled, preventing duplicate notifications. */
if (os::TryWaitEvent(std::addressof(m_request_event))) {
/* Link the process to our multi-wait. */
os::LinkMultiWaitHolder(std::addressof(process_multi_wait), m_queued_process_info->GetMultiWaitHolder());
m_queued_process_info = nullptr;
/* Increment our process count. */
++m_process_count;
/* Reply. */
os::SignalEvent(std::addressof(m_reply_event));
}
} else {
/* Some process was signaled. */
this->OnProcessSignaled(reinterpret_cast<ProcessInfo *>(os::GetMultiWaitHolderUserData(signaled_holder)));
}
}
}
void ProcessTracker::QueueEntry(ProcessInfo *process_info) {
/* Lock ourselves. */
std::scoped_lock lk(m_mutex);
/* Request to enqueue the process info. */
m_queued_process_info = process_info;
os::SignalEvent(std::addressof(m_request_event));
/* Wait for acknowledgement. */
os::WaitEvent(std::addressof(m_reply_event));
}
void ProcessTracker::OnProcessSignaled(ProcessInfo *process_info) {
/* Get the process list. */
auto list = GetProcessList();
/* Reset the process's signal. */
svc::ResetSignal(process_info->GetHandle());
/* Update the process's state. */
const svc::ProcessState old_state = process_info->GetState();
{
s64 tmp = 0;
R_ABORT_UNLESS(svc::GetProcessInfo(std::addressof(tmp), process_info->GetHandle(), svc::ProcessInfoType_ProcessState));
process_info->SetState(static_cast<svc::ProcessState>(tmp));
}
const svc::ProcessState new_state = process_info->GetState();
/* If we're transitioning away from crashed, clear waiting attached. */
if (old_state == svc::ProcessState_Crashed && new_state != svc::ProcessState_Crashed) {
process_info->ClearExceptionWaitingAttach();
}
switch (new_state) {
case svc::ProcessState_Created:
case svc::ProcessState_CreatedAttached:
case svc::ProcessState_Terminating:
break;
case svc::ProcessState_Running:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
} else if (hos::GetVersion() >= hos::Version_2_0_0 && process_info->ShouldSignalOnStart()) {
process_info->SetStartedStateChanged();
process_info->ClearSignalOnStart();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->ClearUnhandledException();
break;
case svc::ProcessState_Crashed:
if (!process_info->HasUnhandledException()) {
process_info->SetExceptionOccurred();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->SetExceptionWaitingAttach();
break;
case svc::ProcessState_RunningAttached:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->ClearSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
}
process_info->ClearUnhandledException();
break;
case svc::ProcessState_Terminated:
/* Unlink from multi wait. */
os::UnlinkMultiWaitHolder(process_info->GetMultiWaitHolder());
/* Free process resources. */
process_info->Cleanup();
if (hos::GetVersion() < hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
os::SignalSystemEvent(std::addressof(g_process_event));
} else {
/* Handle the case where we need to keep the process alive some time longer. */
if (hos::GetVersion() >= hos::Version_5_0_0 && process_info->ShouldSignalOnExit()) {
/* Remove from the living list. */
list->Remove(process_info);
/* Add the process to the list of dead processes. */
{
GetExitList()->push_back(*process_info);
}
/* Signal. */
os::SignalSystemEvent(std::addressof(g_process_event));
} else {
/* Actually delete process. */
CleanupProcessInfo(list, process_info);
}
}
break;
case svc::ProcessState_DebugBreak:
if (process_info->ShouldSignalOnDebugEvent()) {
process_info->SetSuspended();
process_info->SetSuspendedStateChanged();
os::SignalSystemEvent(std::addressof(g_process_event));
}
break;
}
}
void CreateProcessEvent() {
/* Create process event. */
R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_process_event), os::EventClearMode_AutoClear, true));
}
Result GetProcessEventHandle(os::NativeHandle *out) {
*out = os::GetReadableHandleOfSystemEvent(std::addressof(g_process_event));
R_SUCCEED();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "pm_process_info.hpp"
namespace ams::pm::impl {
class ProcessTracker {
NON_COPYABLE(ProcessTracker);
NON_MOVEABLE(ProcessTracker);
private:
os::ThreadType m_thread;
os::EventType m_request_event;
os::EventType m_reply_event;
os::SdkMutex m_mutex;
ProcessInfo *m_queued_process_info;
util::Atomic<u32> m_process_count;
public:
constexpr ProcessTracker() : m_thread(), m_request_event(), m_reply_event(), m_mutex(), m_queued_process_info(nullptr), m_process_count(0) {
/* ... */
}
void Initialize(void *stack, size_t stack_size);
void StartThread();
void QueueEntry(ProcessInfo *process_info);
u32 GetProcessCount() const {
return m_process_count;
}
private:
void OnProcessSignaled(ProcessInfo *process_info);
private:
static void ThreadFunction(void *_this) {
static_cast<ProcessTracker *>(_this)->ThreadBody();
}
void ThreadBody();
};
void CreateProcessEvent();
}

View File

@@ -14,9 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "pm_resource_manager.hpp"
#include "pm_spec.hpp"
namespace ams::pm::resource {
namespace ams::pm::impl {
namespace {
@@ -170,10 +170,18 @@ namespace ams::pm::resource {
}
void WaitApplicationMemoryAvailable() {
/* Get firmware version. */
const auto fw_ver = hos::GetVersion();
/* On 15.0.0+, pm considers application memory to be available if there is exactly 96 MB outstanding. */
/* This is probably because this corresponds to the gameplay-recording memory. */
constexpr u64 AllowedUsedApplicationMemory = 96_MB;
/* Wait for memory to be available. */
u64 value = 0;
while (true) {
R_ABORT_UNLESS(svc::GetSystemInfo(&value, svc::SystemInfoType_UsedPhysicalMemorySize, svc::InvalidHandle, svc::PhysicalMemorySystemInfo_Application));
if (value == 0) {
if (value == 0 || (fw_ver >= hos::Version_15_0_0 && value == AllowedUsedApplicationMemory)) {
break;
}
os::SleepThread(TimeSpan::FromMilliSeconds(1));
@@ -201,8 +209,8 @@ namespace ams::pm::resource {
R_SUCCEED();
}
template<auto GetResourceLimitValueImpl>
ALWAYS_INLINE Result GetResourceLimitValuesImpl(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
template<auto ImplFunction>
ALWAYS_INLINE Result GetResourceLimitValueImpl(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
/* Sanity check group. */
AMS_ABORT_UNLESS(group < ResourceLimitGroup_Count);
@@ -211,11 +219,11 @@ namespace ams::pm::resource {
/* Get values. */
int64_t values[svc::LimitableResource_Count];
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
R_ABORT_UNLESS(GetResourceLimitValueImpl(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_PhysicalMemoryMax]), handle, svc::LimitableResource_PhysicalMemoryMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_ThreadCountMax]), handle, svc::LimitableResource_ThreadCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_EventCountMax]), handle, svc::LimitableResource_EventCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_TransferMemoryCountMax]), handle, svc::LimitableResource_TransferMemoryCountMax));
R_ABORT_UNLESS(ImplFunction(std::addressof(values[svc::LimitableResource_SessionCountMax]), handle, svc::LimitableResource_SessionCountMax));
/* Set to output. */
out->physical_memory = values[svc::LimitableResource_PhysicalMemoryMax];
@@ -270,8 +278,7 @@ namespace ams::pm::resource {
}
/* Resource API. */
Result InitializeResourceManager() {
Result InitializeSpec() {
/* Create resource limit handles. */
for (size_t i = 0; i < ResourceLimitGroup_Count; i++) {
if (i == ResourceLimitGroup_System) {
@@ -451,16 +458,16 @@ namespace ams::pm::resource {
}
}
Result GetCurrentResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitCurrentValue>(group, out));
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitCurrentValue>(out, group));
}
Result GetPeakResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitPeakValue>(group, out));
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitPeakValue>(out, group));
}
Result GetLimitResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out) {
R_RETURN(GetResourceLimitValuesImpl<::ams::svc::GetResourceLimitLimitValue>(group, out));
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group) {
R_RETURN(GetResourceLimitValueImpl<::ams::svc::GetResourceLimitLimitValue>(out, group));
}
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource) {

View File

@@ -16,10 +16,10 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::pm::resource {
namespace ams::pm::impl {
Result InitializeSpec();
/* Resource API. */
Result InitializeResourceManager();
Result BoostSystemMemoryResourceLimit(u64 boost_size);
Result BoostApplicationThreadResourceLimit();
Result BoostSystemThreadResourceLimit();
@@ -31,9 +31,9 @@ namespace ams::pm::resource {
void WaitResourceAvailable(const ldr::ProgramInfo *info);
Result GetCurrentResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
Result GetPeakResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
Result GetLimitResourceLimitValues(ResourceLimitGroup group, pm::ResourceLimitValues *out);
Result GetResourceLimitCurrentValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
Result GetResourceLimitPeakValue(pm::ResourceLimitValue *outm, ResourceLimitGroup group);
Result GetResourceLimitLimitValue(pm::ResourceLimitValue *out, ResourceLimitGroup group);
Result GetResourceLimitValues(s64 *out_cur, s64 *out_lim, ResourceLimitGroup group, svc::LimitableResource resource);

View File

@@ -21,7 +21,7 @@ namespace ams::pm {
namespace {
/* Global bootmode. */
BootMode g_boot_mode = BootMode::Normal;
constinit BootMode g_boot_mode = BootMode::Normal;
}

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "pm_debug_monitor_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {
@@ -76,7 +77,7 @@ namespace ams::pm {
}
Result DebugMonitorService::AtmosphereGetCurrentLimitInfo(sf::Out<s64> out_cur_val, sf::Out<s64> out_lim_val, u32 group, u32 resource) {
R_RETURN(impl::AtmosphereGetCurrentLimitInfo(out_cur_val.GetPointer(), out_lim_val.GetPointer(), group, resource));
R_RETURN(impl::GetResourceLimitValues(out_cur_val.GetPointer(), out_lim_val.GetPointer(), static_cast<ResourceLimitGroup>(group), static_cast<svc::LimitableResource>(resource)));
}
}

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "pm_info_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {
@@ -33,12 +34,12 @@ namespace ams::pm {
R_RETURN(impl::GetProgramId(out.GetPointer(), process_id));
}
Result InformationService::GetAppletCurrentResourceLimitValues(sf::Out<pm::ResourceLimitValues> out) {
R_RETURN(impl::GetAppletCurrentResourceLimitValues(out.GetPointer()));
Result InformationService::GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out) {
R_RETURN(impl::GetResourceLimitCurrentValue(out.GetPointer(), ResourceLimitGroup_Applet));
}
Result InformationService::GetAppletPeakResourceLimitValues(sf::Out<pm::ResourceLimitValues> out) {
R_RETURN(impl::GetAppletPeakResourceLimitValues(out.GetPointer()));
Result InformationService::GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out) {
R_RETURN(impl::GetResourceLimitPeakValue(out.GetPointer(), ResourceLimitGroup_Applet));
}
/* Atmosphere extension commands. */

View File

@@ -22,8 +22,8 @@ namespace ams::pm {
public:
/* Actual command implementations. */
Result GetProgramId(sf::Out<ncm::ProgramId> out, os::ProcessId process_id);
Result GetAppletCurrentResourceLimitValues(sf::Out<pm::ResourceLimitValues> out);
Result GetAppletPeakResourceLimitValues(sf::Out<pm::ResourceLimitValues> out);
Result GetAppletResourceLimitCurrentValue(sf::Out<pm::ResourceLimitValue> out);
Result GetAppletResourceLimitPeakValue(sf::Out<pm::ResourceLimitValue> out);
/* Atmosphere extension commands. */
Result AtmosphereGetProcessId(sf::Out<os::ProcessId> out, ncm::ProgramId program_id);

View File

@@ -16,6 +16,7 @@
#include <stratosphere.hpp>
#include "pm_shell_service.hpp"
#include "impl/pm_process_manager.hpp"
#include "impl/pm_spec.hpp"
namespace ams::pm {

View File

@@ -437,7 +437,7 @@ namespace ams::sm::impl {
{
/* TODO: Convert mitm internal messaging to use tipc? */
::Service srv { .session = mitm_info->query_h };
R_ABORT_UNLESS(::serviceDispatchInOut(std::addressof(srv), 65000, client_info, should_mitm));
R_ABORT_UNLESS((serviceDispatchInOut(std::addressof(srv), 65000, client_info, should_mitm)));
}
/* If we shouldn't mitm, give normal session. */

View File

@@ -42,7 +42,10 @@ BUILD := build
SOURCES := source
DATA := data
INCLUDES := include ../../libraries/libvapours/include
#ROMFS := romfs
ROMFS := romfs
# Output folders for autogenerated files in romfs
OUT_SHADERS := shaders
APP_TITLE := USB File Transfer
APP_AUTHOR := Atmosphere-NX
@@ -63,7 +66,7 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
LIBS := -ldeko3d -lnx -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
@@ -90,6 +93,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
@@ -155,19 +159,61 @@ ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
ifneq ($(strip $(ROMFS)),)
ROMFS_TARGETS :=
ROMFS_FOLDERS :=
ifneq ($(strip $(OUT_SHADERS)),)
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
ROMFS_FOLDERS += $(ROMFS_SHADERS)
endif
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
all: $(ROMFS_TARGETS) | $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
ifneq ($(strip $(ROMFS_TARGETS)),)
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
$(ROMFS_FOLDERS):
@mkdir -p $@
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
@echo {vert} $(notdir $<)
@uam -s vert -o $@ $<
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
@echo {tess_ctrl} $(notdir $<)
@uam -s tess_ctrl -o $@ $<
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
@echo {tess_eval} $(notdir $<)
@uam -s tess_eval -o $@ $<
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
@echo {geom} $(notdir $<)
@uam -s geom -o $@ $<
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
@echo {frag} $(notdir $<)
@uam -s frag -o $@ $<
$(ROMFS_SHADERS)/%.dksh: %.glsl
@echo {comp} $(notdir $<)
@uam -s comp -o $@ $<
endif
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@@ -192,9 +238,9 @@ ifeq ($(strip $(APP_JSON)),)
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
else
$(OUTPUT).nro : $(OUTPUT).elf
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
endif
else

View File

@@ -143,7 +143,7 @@ namespace haze {
if (m_is_applet_mode) {
/* Print "Applet Mode" in red text. */
printf("\n" CONSOLE_ESC(38;5;196m) "Applet Mode" CONSOLE_ESC(0m) "\n");
printf("\n" CONSOLE_ESC(31;1m) "Applet Mode" CONSOLE_ESC(0m) "\n");
}
consoleUpdate(nullptr);

View File

@@ -111,6 +111,14 @@ namespace haze {
}
constexpr void Deallocate(void *p, size_t n) {
/* Check for overflow in alignment. */
if (!util::CanAddWithoutOverflow(n, alignof(u64) - 1)) {
return;
}
/* Align the amount to satisfy allocation for u64. */
n = util::AlignUp(n, alignof(u64));
/* If the pointer was the last allocation, return the memory to the heap. */
if (static_cast<u8 *>(p) + n == this->GetNextAddress()) {
m_next_address = this->GetNextAddress() - n;

View File

@@ -83,6 +83,7 @@ namespace haze {
Result GetObjectPropDesc(PtpDataParser &dp);
Result GetObjectPropValue(PtpDataParser &dp);
Result SetObjectPropValue(PtpDataParser &dp);
Result GetObjectPropList(PtpDataParser &dp);
};
}

View File

@@ -57,6 +57,7 @@ namespace haze {
PtpOperationCode_MtpGetObjectPropDesc,
PtpOperationCode_MtpGetObjectPropValue,
PtpOperationCode_MtpSetObjectPropValue,
PtpOperationCode_MtpGetObjPropList,
PtpOperationCode_AndroidGetPartialObject64,
PtpOperationCode_AndroidSendPartialObject,
PtpOperationCode_AndroidTruncateObject,

View File

@@ -38,5 +38,7 @@ namespace haze {
R_DEFINE_ERROR_RESULT(UnknownPropertyCode, 14);
R_DEFINE_ERROR_RESULT(InvalidPropertyValue, 15);
R_DEFINE_ERROR_RESULT(InvalidArgument, 16);
R_DEFINE_ERROR_RESULT(GroupSpecified, 17);
R_DEFINE_ERROR_RESULT(DepthSpecified, 18);
}

View File

@@ -0,0 +1,15 @@
#version 460
layout (location = 0) noperspective in vec3 inTexCoord;
layout (location = 1) flat in vec4 inFrontPal;
layout (location = 2) flat in vec4 inBackPal;
layout (location = 0) out vec4 outColor;
layout (binding = 0) uniform sampler2DArray tileset;
void main()
{
float value = texture(tileset, inTexCoord).r;
outColor = mix(inBackPal, inFrontPal, value);
}

View File

@@ -0,0 +1,35 @@
#version 460
layout (location = 0) in float inTileId;
layout (location = 1) in uvec2 inColorId;
layout (location = 0) out vec3 outTexCoord;
layout (location = 1) out vec4 outFrontPal;
layout (location = 2) out vec4 outBackPal;
layout (std140, binding = 0) uniform Config
{
vec4 dimensions;
vec4 vertices[3];
vec4 palettes[24];
} u;
void main()
{
float id = float(gl_InstanceID);
float tileRow = floor(id / u.dimensions.z);
float tileCol = id - tileRow * u.dimensions.z;
vec2 basePos;
basePos.x = 2.0 * (tileCol + 0.5) / u.dimensions.z - 1.0;
basePos.y = 2.0 * (1.0 - (tileRow + 0.5) / u.dimensions.w) - 1.0;
vec2 vtxData = u.vertices[gl_VertexID].xy;
vec2 scale = vec2(1.0) / u.dimensions.zw;
gl_Position.xy = vtxData * scale + basePos;
gl_Position.zw = vec2(0.5, 1.0);
outTexCoord = vec3(u.vertices[gl_VertexID].zw, inTileId);
outFrontPal = u.palettes[inColorId.x];
outBackPal = u.palettes[inColorId.y];
}

View File

@@ -0,0 +1,487 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/iosupport.h>
#include <switch.h>
#include <deko3d.h>
// Define the desired number of framebuffers
#define FB_NUM 2
// Define the size of the memory block that will hold code
#define CODEMEMSIZE (64*1024)
// Define the size of the memory block that will hold command lists
#define CMDMEMSIZE (64*1024)
#define NUM_IMAGE_SLOTS 1
#define NUM_SAMPLER_SLOTS 1
typedef struct {
float pos[2];
float tex[2];
} VertexDef;
typedef struct {
float red;
float green;
float blue;
float alpha;
} PaletteColor;
typedef struct {
float dimensions[4];
VertexDef vertices[3];
PaletteColor palettes[24];
} ConsoleConfig;
static const VertexDef g_vertexData[3] = {
{ { 0.0f, +1.0f }, { 0.5f, 0.0f, } },
{ { -1.0f, -1.0f }, { 0.0f, 1.0f, } },
{ { +1.0f, -1.0f }, { 1.0f, 1.0f, } },
};
static const PaletteColor g_paletteData[24] = {
{ 0.0f, 0.0f, 0.0f, 0.0f }, // black
{ 0.5f, 0.0f, 0.0f, 1.0f }, // red
{ 0.0f, 0.5f, 0.0f, 1.0f }, // green
{ 0.5f, 0.5f, 0.0f, 1.0f }, // yellow
{ 0.0f, 0.0f, 0.5f, 1.0f }, // blue
{ 0.5f, 0.0f, 0.5f, 1.0f }, // magenta
{ 0.0f, 0.5f, 0.5f, 1.0f }, // cyan
{ 0.75f, 0.75f, 0.75f, 1.0f }, // white
{ 0.5f, 0.5f, 0.5f, 1.0f }, // bright black
{ 1.0f, 0.0f, 0.0f, 1.0f }, // bright red
{ 0.0f, 1.0f, 0.0f, 1.0f }, // bright green
{ 1.0f, 1.0f, 0.0f, 1.0f }, // bright yellow
{ 0.0f, 0.0f, 1.0f, 1.0f }, // bright blue
{ 1.0f, 0.0f, 1.0f, 1.0f }, // bright magenta
{ 0.0f, 1.0f, 1.0f, 1.0f }, // bright cyan
{ 1.0f, 1.0f, 1.0f, 1.0f }, // bright white
{ 0.0f, 0.0f, 0.0f, 0.0f }, // faint black
{ 0.25f, 0.0f, 0.0f, 1.0f }, // faint red
{ 0.0f, 0.25f, 0.0f, 1.0f }, // faint green
{ 0.25f, 0.25f, 0.0f, 1.0f }, // faint yellow
{ 0.0f, 0.0f, 0.25f, 1.0f }, // faint blue
{ 0.25f, 0.0f, 0.25f, 1.0f }, // faint magenta
{ 0.0f, 0.25f, 0.25f, 1.0f }, // faint cyan
{ 0.375f, 0.375f, 0.375f, 1.0f }, // faint white
};
typedef struct {
uint16_t tileId;
uint8_t frontPal;
uint8_t backPal;
} ConsoleChar;
static const DkVtxAttribState g_attribState[] = {
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,tileId), .size=DkVtxAttribSize_1x16, .type=DkVtxAttribType_Uscaled, .isBgra=0 },
{ .bufferId=0, .isFixed=0, .offset=offsetof(ConsoleChar,frontPal), .size=DkVtxAttribSize_2x8, .type=DkVtxAttribType_Uint, .isBgra=0 },
};
static const DkVtxBufferState g_vtxbufState[] = {
{ .stride=sizeof(ConsoleChar), .divisor=1 },
};
struct GpuRenderer {
ConsoleRenderer base;
bool initialized;
DkDevice device;
DkQueue queue;
DkMemBlock imageMemBlock;
DkMemBlock codeMemBlock;
DkMemBlock dataMemBlock;
DkSwapchain swapchain;
DkImage framebuffers[FB_NUM];
DkImage tileset;
ConsoleChar* charBuf;
uint32_t codeMemOffset;
DkShader vertexShader;
DkShader fragmentShader;
DkCmdBuf cmdbuf;
DkCmdList cmdsBindFramebuffer[FB_NUM];
DkCmdList cmdsRender;
DkFence lastRenderFence;
};
static struct GpuRenderer* GpuRenderer(PrintConsole* con)
{
return (struct GpuRenderer*)con->renderer;
}
static void GpuRenderer_destroy(struct GpuRenderer* r)
{
// Make sure the queue is idle before destroying anything
dkQueueWaitIdle(r->queue);
// Destroy all the resources we've created
dkQueueDestroy(r->queue);
dkCmdBufDestroy(r->cmdbuf);
dkSwapchainDestroy(r->swapchain);
dkMemBlockDestroy(r->dataMemBlock);
dkMemBlockDestroy(r->codeMemBlock);
dkMemBlockDestroy(r->imageMemBlock);
dkDeviceDestroy(r->device);
// Clear out all state
memset(&r->initialized, 0, sizeof(*r) - offsetof(struct GpuRenderer, initialized));
}
// Simple function for loading a shader from the filesystem
static void GpuRenderer_loadShader(struct GpuRenderer* r, DkShader* pShader, const char* path)
{
// Open the file, and retrieve its size
FILE* f = fopen(path, "rb");
fseek(f, 0, SEEK_END);
uint32_t size = ftell(f);
rewind(f);
// Look for a spot in the code memory block for loading this shader. Note that
// we are just using a simple incremental offset; this isn't a general purpose
// allocation algorithm.
uint32_t codeOffset = r->codeMemOffset;
r->codeMemOffset += (size + DK_SHADER_CODE_ALIGNMENT - 1) &~ (DK_SHADER_CODE_ALIGNMENT - 1);
// Read the file into memory, and close the file
fread((uint8_t*)dkMemBlockGetCpuAddr(r->codeMemBlock) + codeOffset, size, 1, f);
fclose(f);
// Initialize the user provided shader object with the code we've just loaded
DkShaderMaker shaderMaker;
dkShaderMakerDefaults(&shaderMaker, r->codeMemBlock, codeOffset);
dkShaderInitialize(pShader, &shaderMaker);
}
static bool GpuRenderer_init(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
if (r->initialized) {
// We're already initialized
return true;
}
// Create the deko3d device, which is the root object
DkDeviceMaker deviceMaker;
dkDeviceMakerDefaults(&deviceMaker);
r->device = dkDeviceCreate(&deviceMaker);
// Create the queue
DkQueueMaker queueMaker;
dkQueueMakerDefaults(&queueMaker, r->device);
queueMaker.flags = DkQueueFlags_Graphics;
r->queue = dkQueueCreate(&queueMaker);
// Calculate required width/height for the framebuffers
u32 width = con->font.tileWidth * con->consoleWidth;
u32 height = con->font.tileHeight * con->consoleHeight;
u32 totalConSize = con->consoleWidth * con->consoleHeight;
// Calculate layout for the framebuffers
DkImageLayoutMaker imageLayoutMaker;
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
imageLayoutMaker.flags = DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression;
imageLayoutMaker.format = DkImageFormat_RGBA8_Unorm;
imageLayoutMaker.dimensions[0] = width;
imageLayoutMaker.dimensions[1] = height;
// Calculate layout for the framebuffers
DkImageLayout framebufferLayout;
dkImageLayoutInitialize(&framebufferLayout, &imageLayoutMaker);
// Calculate layout for the tileset
dkImageLayoutMakerDefaults(&imageLayoutMaker, r->device);
imageLayoutMaker.type = DkImageType_2DArray;
imageLayoutMaker.format = DkImageFormat_R32_Float;
imageLayoutMaker.dimensions[0] = con->font.tileWidth;
imageLayoutMaker.dimensions[1] = con->font.tileHeight;
imageLayoutMaker.dimensions[2] = con->font.numChars;
// Calculate layout for the tileset
DkImageLayout tilesetLayout;
dkImageLayoutInitialize(&tilesetLayout, &imageLayoutMaker);
// Retrieve necessary size and alignment for the framebuffers
uint32_t framebufferSize = dkImageLayoutGetSize(&framebufferLayout);
uint32_t framebufferAlign = dkImageLayoutGetAlignment(&framebufferLayout);
framebufferSize = (framebufferSize + framebufferAlign - 1) &~ (framebufferAlign - 1);
// Retrieve necessary size and alignment for the tileset
uint32_t tilesetSize = dkImageLayoutGetSize(&tilesetLayout);
uint32_t tilesetAlign = dkImageLayoutGetAlignment(&tilesetLayout);
tilesetSize = (tilesetSize + tilesetAlign - 1) &~ (tilesetAlign - 1);
// Create a memory block that will host the framebuffers and the tileset
DkMemBlockMaker memBlockMaker;
dkMemBlockMakerDefaults(&memBlockMaker, r->device, FB_NUM*framebufferSize + tilesetSize);
memBlockMaker.flags = DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image;
r->imageMemBlock = dkMemBlockCreate(&memBlockMaker);
// Initialize the framebuffers with the layout and backing memory we've just created
DkImage const* swapchainImages[FB_NUM];
for (unsigned i = 0; i < FB_NUM; i ++) {
swapchainImages[i] = &r->framebuffers[i];
dkImageInitialize(&r->framebuffers[i], &framebufferLayout, r->imageMemBlock, i*framebufferSize);
}
// Create a swapchain out of the framebuffers we've just initialized
DkSwapchainMaker swapchainMaker;
dkSwapchainMakerDefaults(&swapchainMaker, r->device, nwindowGetDefault(), swapchainImages, FB_NUM);
r->swapchain = dkSwapchainCreate(&swapchainMaker);
// Initialize the tileset
dkImageInitialize(&r->tileset, &tilesetLayout, r->imageMemBlock, FB_NUM*framebufferSize);
// Create a memory block onto which we will load shader code
dkMemBlockMakerDefaults(&memBlockMaker, r->device, CODEMEMSIZE);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code;
r->codeMemBlock = dkMemBlockCreate(&memBlockMaker);
r->codeMemOffset = 0;
// Load our shaders (both vertex and fragment)
romfsInit();
GpuRenderer_loadShader(r, &r->vertexShader, "romfs:/shaders/console_vsh.dksh");
GpuRenderer_loadShader(r, &r->fragmentShader, "romfs:/shaders/console_fsh.dksh");
// Generate the descriptors
struct {
DkImageDescriptor images[NUM_IMAGE_SLOTS];
DkSamplerDescriptor samplers[NUM_SAMPLER_SLOTS];
} descriptors;
// Generate a image descriptor for the tileset
DkImageView tilesetView;
dkImageViewDefaults(&tilesetView, &r->tileset);
dkImageDescriptorInitialize(&descriptors.images[0], &tilesetView, false, false);
// Generate a sampler descriptor for the tileset
DkSampler sampler;
dkSamplerDefaults(&sampler);
sampler.wrapMode[0] = DkWrapMode_ClampToEdge;
sampler.wrapMode[1] = DkWrapMode_ClampToEdge;
sampler.minFilter = DkFilter_Nearest;
sampler.magFilter = DkFilter_Nearest;
dkSamplerDescriptorInitialize(&descriptors.samplers[0], &sampler);
uint32_t descriptorsOffset = CMDMEMSIZE;
uint32_t configOffset = (descriptorsOffset + sizeof(descriptors) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
uint32_t configSize = (sizeof(ConsoleConfig) + DK_UNIFORM_BUF_ALIGNMENT - 1) &~ (DK_UNIFORM_BUF_ALIGNMENT - 1);
uint32_t charBufOffset = configOffset + configSize;
uint32_t charBufSize = totalConSize * sizeof(ConsoleChar);
// Create a memory block which will be used for recording command lists using a command buffer
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
(charBufOffset + charBufSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
r->dataMemBlock = dkMemBlockCreate(&memBlockMaker);
// Create a command buffer object
DkCmdBufMaker cmdbufMaker;
dkCmdBufMakerDefaults(&cmdbufMaker, r->device);
r->cmdbuf = dkCmdBufCreate(&cmdbufMaker);
// Feed our memory to the command buffer so that we can start recording commands
dkCmdBufAddMemory(r->cmdbuf, r->dataMemBlock, 0, CMDMEMSIZE);
// Create a temporary buffer that will hold the tileset
dkMemBlockMakerDefaults(&memBlockMaker, r->device,
(sizeof(float)*con->font.tileWidth*con->font.tileHeight*con->font.numChars + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1)
);
memBlockMaker.flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached;
DkMemBlock scratchMemBlock = dkMemBlockCreate(&memBlockMaker);
float* scratchMem = (float*)dkMemBlockGetCpuAddr(scratchMemBlock);
// Unpack 1bpp tileset into a texture image the GPU can read
unsigned packedTileWidth = (con->font.tileWidth+7)/8;
for (unsigned tile = 0; tile < con->font.numChars; tile ++) {
const uint8_t* data = (const uint8_t*)con->font.gfx + con->font.tileHeight*packedTileWidth*tile;
for (unsigned y = 0; y < con->font.tileHeight; y ++) {
const uint8_t* row = &data[packedTileWidth*(y+1)];
uint8_t c = 0;
for (unsigned x = 0; x < con->font.tileWidth; x ++) {
if (!(x & 7))
c = *--row;
*scratchMem++ = (c & 0x80) ? 1.0f : 0.0f;
c <<= 1;
}
}
}
// Set up configuration
DkGpuAddr configAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + configOffset;
ConsoleConfig consoleConfig = {};
consoleConfig.dimensions[0] = width;
consoleConfig.dimensions[1] = height;
consoleConfig.dimensions[2] = con->consoleWidth;
consoleConfig.dimensions[3] = con->consoleHeight;
memcpy(consoleConfig.vertices, g_vertexData, sizeof(g_vertexData));
memcpy(consoleConfig.palettes, g_paletteData, sizeof(g_paletteData));
// Generate a temporary command list for uploading stuff and run it
DkGpuAddr descriptorSet = dkMemBlockGetGpuAddr(r->dataMemBlock) + descriptorsOffset;
DkCopyBuf copySrc = { dkMemBlockGetGpuAddr(scratchMemBlock), 0, 0 };
DkImageRect copyDst = { 0, 0, 0, con->font.tileWidth, con->font.tileHeight, con->font.numChars };
dkCmdBufPushData(r->cmdbuf, descriptorSet, &descriptors, sizeof(descriptors));
dkCmdBufPushConstants(r->cmdbuf, configAddr, configSize, 0, sizeof(consoleConfig), &consoleConfig);
dkCmdBufBindImageDescriptorSet(r->cmdbuf, descriptorSet, NUM_IMAGE_SLOTS);
dkCmdBufBindSamplerDescriptorSet(r->cmdbuf, descriptorSet + NUM_IMAGE_SLOTS*sizeof(DkImageDescriptor), NUM_SAMPLER_SLOTS);
dkCmdBufCopyBufferToImage(r->cmdbuf, &copySrc, &tilesetView, &copyDst, 0);
dkQueueSubmitCommands(r->queue, dkCmdBufFinishList(r->cmdbuf));
dkQueueFlush(r->queue);
dkQueueWaitIdle(r->queue);
dkCmdBufClear(r->cmdbuf);
// Destroy the scratch memory block since we don't need it anymore
dkMemBlockDestroy(scratchMemBlock);
// Retrieve the address of the character buffer
DkGpuAddr charBufAddr = dkMemBlockGetGpuAddr(r->dataMemBlock) + charBufOffset;
r->charBuf = (ConsoleChar*)((uint8_t*)dkMemBlockGetCpuAddr(r->dataMemBlock) + charBufOffset);
memset(r->charBuf, 0, charBufSize);
// Generate a command list for each framebuffer, which will bind each of them as a render target
for (unsigned i = 0; i < FB_NUM; i ++) {
DkImageView imageView;
dkImageViewDefaults(&imageView, &r->framebuffers[i]);
dkCmdBufBindRenderTarget(r->cmdbuf, &imageView, NULL);
r->cmdsBindFramebuffer[i] = dkCmdBufFinishList(r->cmdbuf);
}
// Declare structs that will be used for binding state
DkViewport viewport = { 0.0f, 0.0f, (float)width, (float)height, 0.0f, 1.0f };
DkScissor scissor = { 0, 0, width, height };
DkShader const* shaders[] = { &r->vertexShader, &r->fragmentShader };
DkRasterizerState rasterizerState;
DkColorState colorState;
DkColorWriteState colorWriteState;
// Initialize state structs with the deko3d defaults
dkRasterizerStateDefaults(&rasterizerState);
dkColorStateDefaults(&colorState);
dkColorWriteStateDefaults(&colorWriteState);
rasterizerState.fillRectangleEnable = true;
colorState.alphaCompareOp = DkCompareOp_Greater;
// Generate the main rendering command list
dkCmdBufSetViewports(r->cmdbuf, 0, &viewport, 1);
dkCmdBufSetScissors(r->cmdbuf, 0, &scissor, 1);
//dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.125f, 0.294f, 0.478f, 0.0f);
dkCmdBufClearColorFloat(r->cmdbuf, 0, DkColorMask_RGBA, 0.0f, 0.0f, 0.0f, 0.0f);
dkCmdBufBindShaders(r->cmdbuf, DkStageFlag_GraphicsMask, shaders, sizeof(shaders)/sizeof(shaders[0]));
dkCmdBufBindRasterizerState(r->cmdbuf, &rasterizerState);
dkCmdBufBindColorState(r->cmdbuf, &colorState);
dkCmdBufBindColorWriteState(r->cmdbuf, &colorWriteState);
dkCmdBufBindUniformBuffer(r->cmdbuf, DkStage_Vertex, 0, configAddr, configSize);
dkCmdBufBindTexture(r->cmdbuf, DkStage_Fragment, 0, dkMakeTextureHandle(0, 0));
dkCmdBufBindVtxAttribState(r->cmdbuf, g_attribState, sizeof(g_attribState)/sizeof(g_attribState[0]));
dkCmdBufBindVtxBufferState(r->cmdbuf, g_vtxbufState, sizeof(g_vtxbufState)/sizeof(g_vtxbufState[0]));
dkCmdBufBindVtxBuffer(r->cmdbuf, 0, charBufAddr, charBufSize);
dkCmdBufSetAlphaRef(r->cmdbuf, 0.0f);
dkCmdBufDraw(r->cmdbuf, DkPrimitive_Triangles, 3, totalConSize, 0, 0);
r->cmdsRender = dkCmdBufFinishList(r->cmdbuf);
r->initialized = true;
return true;
}
static void GpuRenderer_deinit(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
if (r->initialized) {
GpuRenderer_destroy(r);
}
}
static void GpuRenderer_drawChar(PrintConsole* con, int x, int y, int c)
{
struct GpuRenderer* r = GpuRenderer(con);
int writingColor = con->fg;
int screenColor = con->bg;
if (con->flags & CONSOLE_COLOR_BOLD) {
writingColor += 8;
} else if (con->flags & CONSOLE_COLOR_FAINT) {
writingColor += 16;
}
if (con->flags & CONSOLE_COLOR_REVERSE) {
int tmp = writingColor;
writingColor = screenColor;
screenColor = tmp;
}
// Wait for the fence
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
ConsoleChar* pos = &r->charBuf[y*con->consoleWidth+x];
pos->tileId = c;
pos->frontPal = writingColor;
pos->backPal = screenColor;
}
static void GpuRenderer_scrollWindow(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
// Wait for the fence
dkFenceWait(&r->lastRenderFence, UINT64_MAX);
// Perform the scrolling
for (int y = 0; y < con->windowHeight-1; y ++) {
memcpy(
&r->charBuf[(con->windowY+y+0)*con->consoleWidth + con->windowX],
&r->charBuf[(con->windowY+y+1)*con->consoleWidth + con->windowX],
sizeof(ConsoleChar)*con->windowWidth);
}
}
static void GpuRenderer_flushAndSwap(PrintConsole* con)
{
struct GpuRenderer* r = GpuRenderer(con);
// Acquire a framebuffer from the swapchain (and wait for it to be available)
int slot = dkQueueAcquireImage(r->queue, r->swapchain);
// Run the command list that binds said framebuffer as a render target
dkQueueSubmitCommands(r->queue, r->cmdsBindFramebuffer[slot]);
// Run the main rendering command list
dkQueueSubmitCommands(r->queue, r->cmdsRender);
// Signal the fence
dkQueueSignalFence(r->queue, &r->lastRenderFence, false);
// Now that we are done rendering, present it to the screen
dkQueuePresentImage(r->queue, r->swapchain, slot);
}
static struct GpuRenderer s_gpuRenderer =
{
{
GpuRenderer_init,
GpuRenderer_deinit,
GpuRenderer_drawChar,
GpuRenderer_scrollWindow,
GpuRenderer_flushAndSwap,
}
};
ConsoleRenderer* getDefaultConsoleRenderer(void)
{
return &s_gpuRenderer.base;
}

View File

@@ -19,8 +19,8 @@ namespace haze {
namespace {
/* Allow 20MiB for use by libnx. */
static constexpr size_t LibnxReservedMemorySize = 20_MB;
/* Allow 30MiB for use by libnx. */
static constexpr size_t LibnxReservedMemorySize = 30_MB;
}

View File

@@ -91,6 +91,12 @@ namespace haze {
R_CATCH(haze::ResultInvalidPropertyValue) {
R_TRY(this->WriteResponse(PtpResponseCode_MtpInvalidObjectPropValue));
}
R_CATCH(haze::ResultGroupSpecified) {
R_TRY(this->WriteResponse(PtpResponseCode_MtpSpecificationByGroupUnsupported));
}
R_CATCH(haze::ResultDepthSpecified) {
R_TRY(this->WriteResponse(PtpResponseCode_MtpSpecificationByDepthUnsupported));
}
R_CATCH(haze::ResultInvalidArgument) {
R_TRY(this->WriteResponse(PtpResponseCode_GeneralError));
}
@@ -134,6 +140,7 @@ namespace haze {
case PtpOperationCode_MtpGetObjectPropDesc: R_RETURN(this->GetObjectPropDesc(dp)); break;
case PtpOperationCode_MtpGetObjectPropValue: R_RETURN(this->GetObjectPropValue(dp)); break;
case PtpOperationCode_MtpSetObjectPropValue: R_RETURN(this->SetObjectPropValue(dp)); break;
case PtpOperationCode_MtpGetObjPropList: R_RETURN(this->GetObjectPropList(dp)); break;
case PtpOperationCode_AndroidGetPartialObject64: R_RETURN(this->GetPartialObject64(dp)); break;
case PtpOperationCode_AndroidSendPartialObject: R_RETURN(this->SendPartialObject(dp)); break;
case PtpOperationCode_AndroidTruncateObject: R_RETURN(this->TruncateObject(dp)); break;

View File

@@ -113,9 +113,6 @@ namespace haze {
R_TRY(dp.Read(std::addressof(property_code)));
R_TRY(dp.Finalize());
/* Disallow renaming the storage root. */
R_UNLESS(object_id != StorageId_SdmcFs, haze::ResultInvalidObjectId());
/* Ensure we have a valid property code before continuing. */
R_UNLESS(IsSupportedObjectPropertyCode(property_code), haze::ResultUnknownPropertyCode());
@@ -198,6 +195,147 @@ namespace haze {
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
}
Result PtpResponder::GetObjectPropList(PtpDataParser &dp) {
u32 object_id;
u32 object_format;
s32 property_code;
s32 group_code;
s32 depth;
R_TRY(dp.Read(std::addressof(object_id)));
R_TRY(dp.Read(std::addressof(object_format)));
R_TRY(dp.Read(std::addressof(property_code)));
R_TRY(dp.Read(std::addressof(group_code)));
R_TRY(dp.Read(std::addressof(depth)));
R_TRY(dp.Finalize());
/* Ensure format is unspecified. */
R_UNLESS(object_format == 0, haze::ResultInvalidArgument());
/* Ensure we have a valid property code. */
R_UNLESS(property_code == -1 || IsSupportedObjectPropertyCode(PtpObjectPropertyCode(property_code)), haze::ResultUnknownPropertyCode());
/* Ensure group code is the default. */
R_UNLESS(group_code == PtpPropertyGroupCode_Default, haze::ResultGroupSpecified());
/* Ensure depth is 0. */
R_UNLESS(depth == 0, haze::ResultDepthSpecified());
/* Check if we know about the object. If we don't, it's an error. */
auto * const obj = m_object_database.GetObjectById(object_id);
R_UNLESS(obj != nullptr, haze::ResultInvalidObjectId());
/* Define helper for getting the object type. */
const auto GetObjectType = [&] (FsDirEntryType *out_entry_type) {
R_RETURN(m_fs.GetEntryType(obj->GetName(), out_entry_type));
};
/* Define helper for getting the object size. */
const auto GetObjectSize = [&] (s64 *out_size) {
*out_size = 0;
/* Check if this is a directory. */
FsDirEntryType entry_type;
R_TRY(GetObjectType(std::addressof(entry_type)));
/* If it is, we're done. */
R_SUCCEED_IF(entry_type == FsDirEntryType_Dir);
/* Otherwise, open as a file. */
FsFile file;
R_TRY(m_fs.OpenFile(obj->GetName(), FsOpenMode_Read, std::addressof(file)));
/* Ensure we maintain a clean state on exit. */
ON_SCOPE_EXIT { m_fs.CloseFile(std::addressof(file)); };
R_RETURN(m_fs.GetFileSize(std::addressof(file), out_size));
};
/* Define helper for determining if the property should be included. */
const auto ShouldIncludeProperty = [&] (PtpObjectPropertyCode code) {
/* If all properties were requested, or it was the requested property, we should include the property. */
return property_code == -1 || code == property_code;
};
/* Determine how many output elements we will report. */
u32 num_output_elements = 0;
for (const auto obj_property : SupportedObjectProperties) {
if (ShouldIncludeProperty(obj_property)) {
num_output_elements++;
}
}
/* Begin writing the requested object properties. */
PtpDataBuilder db(m_buffers->usb_bulk_write_buffer, std::addressof(m_usb_server));
R_TRY(db.WriteVariableLengthData(m_request_header, [&] {
/* Report the number of elements. */
R_TRY(db.Add(num_output_elements));
for (const auto obj_property : SupportedObjectProperties) {
if (!ShouldIncludeProperty(obj_property)) {
continue;
}
/* Write the object handle. */
R_TRY(db.Add<u32>(object_id));
/* Write the property code. */
R_TRY(db.Add<u16>(obj_property));
/* Write the property value. */
switch (obj_property) {
case PtpObjectPropertyCode_PersistentUniqueObjectIdentifier:
{
R_TRY(db.Add(PtpDataTypeCode_U128));
R_TRY(db.Add<u128>(object_id));
}
break;
case PtpObjectPropertyCode_ObjectSize:
{
s64 size;
R_TRY(GetObjectSize(std::addressof(size)));
R_TRY(db.Add(PtpDataTypeCode_U64));
R_TRY(db.Add<u64>(size));
}
break;
case PtpObjectPropertyCode_StorageId:
{
R_TRY(db.Add(PtpDataTypeCode_U32));
R_TRY(db.Add(StorageId_SdmcFs));
}
break;
case PtpObjectPropertyCode_ParentObject:
{
R_TRY(db.Add(PtpDataTypeCode_U32));
R_TRY(db.Add(obj->GetParentId()));
}
break;
case PtpObjectPropertyCode_ObjectFormat:
{
FsDirEntryType entry_type;
R_TRY(GetObjectType(std::addressof(entry_type)));
R_TRY(db.Add(PtpDataTypeCode_U16));
R_TRY(db.Add(entry_type == FsDirEntryType_File ? PtpObjectFormatCode_Undefined : PtpObjectFormatCode_Association));
}
break;
case PtpObjectPropertyCode_ObjectFileName:
{
R_TRY(db.Add(PtpDataTypeCode_String));
R_TRY(db.AddString(std::strrchr(obj->GetName(), '/') + 1));
}
break;
HAZE_UNREACHABLE_DEFAULT_CASE();
}
}
R_SUCCEED();
}));
/* Write the success response. */
R_RETURN(this->WriteResponse(PtpResponseCode_Ok));
}
Result PtpResponder::SetObjectPropValue(PtpDataParser &rdp) {
u32 object_id;
PtpObjectPropertyCode property_code;

View File

@@ -191,6 +191,7 @@ namespace haze {
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, std::addressof(device_descriptor)));
device_descriptor.bcdUSB = 0x0300;
device_descriptor.bMaxPacketSize0 = 0x09;
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, std::addressof(device_descriptor)));
/* Binary Object Store */